Files
ProjectFGame/Architecture_Projet.md
T
2026-09-19 18:13:06 +02:00

881 lines
186 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# đŸ—ïž Architecture du Projet — ProjectF
> **Ce document est la référence d'architecture du projet, destinée en priorité aux modÚles d'IA (Claude, etc.) qui vont lire, modifier ou créer du contenu dans ce projet Unreal Engine.**
> Il doit permettre de comprendre **oĂč se trouve chaque systĂšme, quel fichier est responsable de quoi, et oĂč ajouter du nouveau contenu** — sans avoir Ă  explorer tout le projet Ă  chaque session.
>
> ⚠ **Ce document doit rester Ă  jour.** Voir la section [10. Maintenir ce document Ă  jour](#10-maintenir-ce-document-Ă -jour) et le skill `maintain-architecture-doc`.
---
## 1. Vue d'ensemble du projet
### 1.1 Nature du projet
- **Moteur** : Unreal Engine **5.8**.
- **Aucun code C++** : il n'y a pas de dossier `Source/`. Tout le gameplay est écrit en **Blueprint visuel**, dans `Content/`.
- **Genre** : jeu de survie / extraction en vue 3e personne avec un **inventaire en grille façon "Escape from Tarkov"** (armure, sac à dos, armes, munitions, craft sur feu de camp/fonderie, générateurs, lecteurs de carte magnétique, coffres, PNJ pillables, véhicules).
- **Multijoueur** : le systĂšme d'inventaire est rĂ©pliquĂ© (RPC client/serveur visibles partout dans `Inventory_Player`, ex. `Request Switch Weapon SERVER`, `Disarm Request SERVER`). Le projet doit donc ĂȘtre pensĂ© serveur-autoritaire pour tout ce qui touche Ă  l'inventaire, aux stats et au combat.
### 1.2 Les grandes couches du projet
Le projet est construit par empilement de **trois grands blocs** de contenu, plus une couche d'outillage IA :
1. **Framework de personnage / locomotion ("Sandbox Character")** — `Content/Systems/Blueprints/`
Basé sur le sample officiel Epic "Motion Matching" (plugins `Mover`, `MotionWarping`, `MotionTrajectory`, `Chooser`, `AnimationLocomotionLibrary`, `AnimationWarping`). GÚre le mouvement, la caméra, le Motion Matching, le traversal (vault/mantle/climb), les modes de mouvement personnalisés. **Ce framework est en grande partie template/tiers** : n'y toucher qu'en connaissance de cause.
2. **Systùme d'inventaire AGIS** ("Advanced Grid Inventory System", asset marketplace) — `Content/Core/Coreinventory/`, `Content/Systems/Inventory/`, `Content/UI/Inventory_UI/`, `Content/Data/Inventory_Data/`.
C'est le **cƓur du gameplay** : inventaire en grille, Ă©quipement, craft, loot, sauvegarde. La quasi-totalitĂ© des systĂšmes de jeu ajoutĂ©s par l'Ă©quipe (stats, mort, sĂ©lection rapide d'Ă©quipement, interactables) se branchent sur ce systĂšme.
3. **SystĂšmes de jeu spĂ©cifiques au projet**, ajoutĂ©s par-dessus AGIS — `Content/Systems/Stats/`, `Content/Systems/Scent/`, `Content/Systems/Movable_Object/`, `Content/Systems/Door/`, `Content/PCG/`, `Content/Items/`, `Content/UI/Widgets/` (HUD, pause menu). C'est ici que la majoritĂ© des Ă©volutions de gameplay futures doivent ĂȘtre ajoutĂ©es.
4. **Outillage IA** — plugins `VibeUE`, `EditorToolset`, `ModelContextProtocol`, `Terminal` (dĂ©clarĂ©s dans `ProjectFv1.uproject`) + `.mcp.json` Ă  la racine. Ils exposent l'Ă©diteur Unreal (Blueprints, Widgets, Input, PCG, Landscape, Niagara, etc.) Ă  un serveur MCP local (`http://127.0.0.1:8000/mcp`) pilotĂ© par des agents Claude. **C'est le mĂ©canisme par lequel un modĂšle d'IA lit/modifie ce projet** — voir la section [9. Notes pour les IA](#9-notes-pour-les-ia-piĂšges-connus-de-loutillage) pour les piĂšges connus.
En plus de cela, deux couches d'assets tiers volumineuses sont présentes mais **ne contiennent pas de logique de gameplay propre au projet** : le pack météo **UltraDynamicSky** (`Content/Systems/Weather/UltraDynamicSky/`) et diverses bibliothÚques d'assets 3D Fab/Megascans/Quixel (`Content/Fab/`, `Content/Megaplant_Library/`, une bonne partie de `Content/Animations/` et `Content/Characters/`).
### 1.3 Dépendances principales entre systÚmes
```
Character (Pawn, géré par le framework "Sandbox Character")
│
├─ Composant Inventory_Player (Content/Core/Coreinventory/Inventory_Player)
│ └─ hĂ©rite de Inventory_Equipper (Ă©quipement d'armes/objets)
│ └─ hĂ©rite de Inventory__Main (cƓur gĂ©nĂ©rique AGIS : slots, containers, rĂ©seau)
│
│ ⚠ Pour un item (`_BP_ItemBase` et enfants) qui a besoin de chercher/consommer un item dans
│ l'inventaire de son porteur (ex. l'arc qui cherche des flĂšches), utiliser sa propriĂ©tĂ© hĂ©ritĂ©e
│ `Owner Inventory` (fonction `Variables|Runtime|GetOwnerInventory`, self implicite) plutît que de
│ reconstruire `GetOwnerCharacter → Cast SandboxCharacter_CMC → GetInventoryPlayer → GetListeningInventory` :
│ cette derniĂšre chaĂźne se rĂ©sout Ă  `None` tant que le joueur n'a pas ouvert son Ă©cran d'inventaire au
│ moins une fois (`Listening Inventory` est un Ă©tat d'UI, pas l'inventaire persistant). `Owner Inventory`
│ est typĂ©e `Inventory_Equipper` mais accepte directement les pins `Inventory__Main` (ex. `BOTH|FindItem`)
│ grĂące Ă  cette hiĂ©rarchie d'hĂ©ritage — aucun cast nĂ©cessaire. DĂ©couvert et corrigĂ© dans
│ `BP_Item_Bow::CountArrows/ConsumeArrow` (voir §7).
│
│ Inventory_Player pilote/possùde :
│ ├─ WB_InventoryScreen (UI d'inventaire complĂšte, créée Ă  la demande)
│ │ └─ WB_InventoryPart1 / WB_InventoryPart2
│ │ └─ WB_EquipSlot → WB_Container → WB_InventorySlot → WB_Item
│ ├─ WB_QuickSelect (roue de sĂ©lection rapide d'Ă©quipement, 4 slots)
│ └─ Les Input Actions IA_*AGIS via l'IMC_AGIS_Character / IMC_AGIS_InventoryTab
│
├─ Composant AC_Stats (Content/Systems/Stats/AC_Stats)
│ └─ Vie, mort (OnDeath), Revive → dĂ©clenche WB_YouDied et le respawn du Character.
│ TakeDamage(BodyPart, DMGValue) est le point d'entrĂ©e UNIQUE de toute cause de mort :
│ utilisĂ© Ă  la fois par la mort par chute et par la touche de debug K (SandboxCharacter_CMC/EventGraph).
│
├─ Armes (Content/Systems/Weapons/, voir §4.3)
│ └─ AC_Weapon, ActorComponent ajoutĂ© Ă  chaque Blueprint d'arme (BP_Item_Bow, BP_Item_AdvancedRifle) :
│ s'auto-abonne (EventBeginPlay, cast Owner → BP_Item_HoldableBase) au dispatcher
│ `BP_Item_HoldableBase::OnHeldStateChanged(bIsHeld)` — PAS à `_BP_ItemBase::OnOwnerChanged`
│ (2026-09 : bug corrigĂ© — OnOwnerChanged se dĂ©clenche pour TOUT changement de propriĂ©taire,
│ y compris le simple ramassage/rangement dans un slot d'inventaire non tenu en main, ce qui
│ causait "2 armes Ă©quipĂ©es en mĂȘme temps" dĂšs que 2 armes Ă©taient prĂ©sentes dans l'inventaire.
│ OnHeldStateChanged ne se dĂ©clenche que sur le VRAI changement d'Ă©tat main/pas-en-main, via
│ `BPI_HoldableItem::On Item Attached(ShouldHold)` dĂ©jĂ  implĂ©mentĂ© par BP_Item_HoldableBase).
│ HandleHeldStateChanged(bIsHeld) → Equip(Owner)/Unequip() selon bIsHeld, qui pilotent `bIsEquipped`.
│ Expose CanUse()/TryUsePrimary()/StopPrimary() (cooldown gĂ©nĂ©rique) que chaque item interroge dans ses
│ overrides `Custom|PrimaryUse`/`StopPrimaryUsing` existants (voir §7). BFL_WeaponDamage::ApplyPointDamage
│ route les dĂ©gĂąts d'un projectile vers `AC_Stats::TakeDamage` (mapping nom d'os → E_BodyPart).
│ **SĂ©lection d'animation (2026-09-06)** : `Equip`/`Unequip` pilotent aussi `SandboxCharacter_CMC::Weapon`
│ (enum `Weapon_Equip` : None/Bow/OneArmPistol/TwoArmPistol/Riffle/Bat — lu par la Pose Search Database
│ `CHT_PoseSearchDatabases_Dense` pour sĂ©lectionner le pool d'animations armĂ©es/dĂ©sarmĂ©es de Motion
│ Matching) et `isArmed` (bool) — ces deux variables existaient dĂ©jĂ  sur le personnage mais n'Ă©taient
│ JAMAIS renseignĂ©es avant ce fix (dette du template de base), donc la locomotion restait toujours
│ "à mains nues" quelle que soit l'arme tenue. `AC_Weapon` porte une variable `WeaponTypeByte` (byte,
│ PAS l'enum directement — UserDefinedEnum non utilisable comme type de variable membre via les outils,
│ voir §9) configurĂ©e par arme sur le composant (1=Bow sur `BP_Item_Bow`, 4=Riffle sur
│ `BP_Item_AdvancedRifle`), convertie via `Utilities|Enum|BytetoEnumWeapon_Equip` avant d'ĂȘtre assignĂ©e.
│ **Support du personnage Mover (2026-09-19)** : `Equip`/`Unequip` ne faisaient qu'un `Cast To
│ SandboxCharacter_CMC`. Sur `SandboxCharacter_Mover` (parent `Pawn`, aucune classe commune avec le CMC)
│ le cast Ă©chouait silencieusement → `Weapon`/`isArmed` jamais Ă©crits, donc arme en main mais locomotion
│ toujours dĂ©sarmĂ©e. Les deux fonctions ont dĂ©sormais une branche `Cast Failed` → `Cast To
│ SandboxCharacter_Mover` → `SetEquippedWeapon(WeaponTypeByte)` (nouvelle fonction du pion Mover, voir §4.2.3).
│ **Bras armĂ© qui reste bloquĂ© en pose par dĂ©faut Ă  l'Ă©quipement (2026-09-06, CORRIGÉ)** : deux causes
│ distinctes cumulĂ©es, toutes deux dans `SandboxCharacter_CMC_ABP` :
│ (1) `Get_MMInterruptMode` (utilisĂ©e par `Update_MotionMatching` pour choisir le `PoseSearchInterruptMode`
│ passĂ© Ă  `MotionMatchingAnimNodeLibrary.SetDatabasesToSearch`) ne testait QUE MovementMode/MovementState/
│ Gait — jamais `Weapon_Equip` (variable ABP, distincte de `SandboxCharacter_CMC::Weapon`, alimentĂ©e depuis
│ celle-ci via PropertyAccess dans `Update_States`, qui a bien SON PROPRE tracker `Weapon_Equip Last Frame`
│ dĂ©jĂ  prĂ©sent mais jamais branchĂ©). RĂ©sultat : passer armĂ©/dĂ©sarmĂ© EN RESTANT IMMOBILE ne changeait ni
│ MovementMode/State/Gait, donc `Get_MMInterruptMode` retournait toujours `DoNotInterrupt` et Motion
│ Matching continuait indĂ©finiment l'ancienne pose (`bIsContinuingPoseSearch=True`) sans jamais essayer
│ la nouvelle base de donnĂ©es pourtant correctement sĂ©lectionnĂ©e dans `ValidDatabases`/`DatabasesToSearch`.
│ Fix : condition supplĂ©mentaire `Weapon_Equip != Weapon_Equip Last Frame` ajoutĂ©e au `OR` racine de
│ `Get_MMInterruptMode` (3ᔉ pin ajoutĂ©e au nƓud `ORBoolean` existant via `add_node_pin`, PAS une réécriture
│ DSL complĂšte — voir §9 pour le piĂšge du wildcard enum). (2) Une fois l'interruption forcĂ©e, la recherche
│ Ă©chouait quand mĂȘme : `PSD_Dense_Stand_Bow_Idles`/`_Bow_Aim_Idles`/`PSD_Walk_Bow`/`PSD_Crouch_Bow`
│ (toutes sous `/Game/Animations/humain/Armed_mov/Bow/`) avaient leur `PoseSearchMode` sur `PCAKD_TREE`
│ et leur build d'index Ă©chouait dĂšs le chargement (`LogPoseSearch: Error: ... BuildIndex Failed because
│ of 'No Convergence' while calculating PCA data`) — passĂ©es en `BRUTE_FORCE` (pas besoin de PCA, dataset
│ de toute façon minuscule), ce qui a dĂ©bloquĂ© la recherche. (3) **Lignes armĂ©es manquantes dans le
│ chooser (CORRIGÉ 2026-09-06)** : la colonne `Is Armed` de `CHT_PoseSearchDatabases_Dense` (seule des 4
│ tables LOD à en avoir une) n'avait que DEUX lignes `MatchTrue` — "Stand Idles Bow" (ligne 1) et "Stand
│ Walks Armed" (ligne 3). Toutes les autres (courses 4-5, sprint 6, InAir 7, accroupi 8-9, prone 10-11,
│ nage 12-17) exigeaient `MatchFalse`, donc AUCUNE ligne ne correspondait dùs qu'on courait/sautait/
│ s'accroupissait armĂ© → chooser vide → Motion Matching gardait la derniĂšre pose (symptĂŽme : "mĂȘme
│ animation en marchant, courant et accroupi"). Fix : ces lignes passĂ©es de `MatchFalse` Ă  `MatchAny`
│ (repli propre sur l'animation non-armĂ©e au lieu du gel ; les lignes 0 et 2 DOIVENT rester `MatchFalse`
│ sinon elles masqueraient les lignes arc 1 et 3, l'Ă©valuation Ă©tant descendante premier-match-gagne).
│ Aucun sous-chooser concernĂ© n'a de colonne d'arme, le repli est donc sans effet de bord.
│ ⚠ **RÉGRESSION 2026-09-13 (voir §8.2)** : `CHT_PoseSearchDatabases_Dense` a Ă©tĂ© ÉCRASÉ par la migration depuis
│ `Project_F` — les lignes armĂ©es Bow (`PSD_Dense_Stand_Bow_Idles`, `_Bow_Aim_Idles`, `PSD_Walk_Bow`) et le passage
│ `MatchFalse`→`MatchAny` dĂ©crits ci-dessus avaient disparu. **RESTAURÉ le 2026-09-14** : la version du commit `d29748b`
│ a Ă©tĂ© remise (lignes Bow, Prone, nage, cover accroupi et Push revenues, contexte `Systems/Blueprints/SandboxCharacter_CMC_ABP`)
│ puis le fix MatchAny a Ă©tĂ© refait par script : colonne `Is Armed` : lignes 0 et 2 en `MatchFalse`, 1 et 3 en `MatchTrue`, lignes 4 Ă  17 passĂ©es de `MatchFalse` Ă  `MatchAny` (la ligne 18 l'Ă©tait dĂ©jĂ ), vĂ©rifiĂ© aprĂšs rechargement.
│ **Reste un problĂšme de contenu NON corrigĂ©** (voir §9) : `PSD_Dense_Stand_Bow_Idles` — et ELLE SEULE
│ (`_Bow_Aim_Idles`/`PSD_Walk_Bow`/`PSD_Crouch_Bow` sont sains) — a ses 4 entrĂ©es
│ `DatabaseAnimationAssets[i].SamplingRange` rĂ©glĂ©es sur `(longueur_du_clip, longueur_du_clip)`, une
│ fenĂȘtre de largeur nulle placĂ©e Ă  la toute FIN du clip. Or Pose Search ne traite un asset comme
│ bouclable que si sa plage vaut `(0,0)` (= « tout le clip ») : d'oĂč `bLoopAnimation=False` + fenĂȘtre de
│ lecture nulle = une seule frame figĂ©e, alors mĂȘme que les AnimSequences sources sont bien `Loop=True`.
│ Correction = remettre chaque `Sampling Range` Ă  `0 / 0` dans l'Ă©diteur Pose Search Database — Ă  faire Ă 
│ la main : la propriĂ©tĂ© n'est exposĂ©e par AUCUNE API (absente de `ObjectTools.list_properties`, refusĂ©e
│ par `get/set_editor_property` ET `ObjectTools`, et le `SET` console ne cible qu'une classe entiùre donc
│ toutes les databases chargĂ©es). C'est aussi ce dataset dĂ©gĂ©nĂ©rĂ© (4 poses uniques) qui faisait Ă©chouer
│ le PCA du point (2).
│ `PSD_Crouch_Bow` existe mais n'est rĂ©fĂ©rencĂ©e par AUCUN chooser (contenu orphelin) : une fois les
│ SamplingRange corrigĂ©es, on pourra lui dĂ©dier une vraie ligne armĂ©e accroupie plutĂŽt que le repli.
│
├─ Composants de mouvement (Content/Systems/Movement/, Systems/Blueprints/MovementModes/)
│ └─ AC_ClimbingComponent, AC_Cover, AC_TraversalLogic, AC_PreCMCTick, AC_VisualOverrideManager

│
├─ Composant AIPerceptionStimuliSourceComponent (2026-09-05)
│ └─ AjoutĂ© sur SandboxCharacter_CMC pour que le joueur soit dĂ©tectable par la vue de l'IA
│ (AISense_Sight, auto_register_as_source=true). Sans lui, AUCUN AIPerceptionComponent
│ ne peut jamais dĂ©tecter le joueur, quels que soient rayon/FOV/LOS configurĂ©s cĂŽtĂ© IA.
│
└─ Autres composants gameplay (BP_ScentPoint, etc. — voir Systems/Scent/ §4.3 et Systems/Wildlife/ §4.4)
Interactables (Content/Systems/Inventory/Interactables/*)
└─ ImplĂ©mentent BPI_Interactable, exposent un inventaire via un composant
Inventory_Storage / Inventory_Vendor / Inventory_Crafting (mĂȘmes bases AGIS que le joueur)
→ consultĂ©s par Inventory_Player lors d'une interaction (touche IA_InteractAGIS)
Données d'items (Content/Data/Inventory_Data/, Content/Items/)
└─ DT_Items (DataTable), ItemDefaults / ItemInfo (structs), DD_Item (DataAsset par item)
alimentent Ă  la fois l'UI d'inventaire ET les Blueprints d'objets sous Content/Items/BP_Item_*
Faune sauvage (Content/Systems/Wildlife/, voir §4.4)
└─ BP_AnimalBase (Character) rĂ©utilise TEL QUEL AC_Stats (vie/dĂ©gĂąts/mort, §1.3 ci-dessus) et
BP_ScentPoint (Systems/Scent/, §4.3) — aucun systĂšme de vie ni d'odeur parallĂšle n'a Ă©tĂ© créé.
BFL_WeaponDamage::ApplyPointDamage fonctionne automatiquement sur un animal (résout AC_Stats
par GetComponentByClass) : aucune adaptation nécessaire cÎté systÚme d'armes.
```
### 1.4 Conventions de nommage des assets
| Préfixe | Signification | Exemple |
|---|---|---|
| `BP_` | Blueprint d'acteur | `BP_Campfire`, `BP_Item_Helmet` |
| `WB_` / `WBP_` | Widget Blueprint (UMG) | `WB_InventoryScreen`, `WBP_PauseMenu` |
| `AC_` | ActorComponent | `AC_Stats`, `AC_ClimbingComponent` |
| `IA_` | Input Action (Enhanced Input) | `IA_InteractAGIS`, `IA_QuickSelectAGIS` |
| `IMC_` | Input Mapping Context | `IMC_AGIS_Character` |
| `E_` / `EM_` | Enum | `E_MovementMode`, `EM_Road_Type` |
| `S_` / `ST_` | Struct | `S_TraversalCheckInputs`, `ST_Weather_Zone_forecasts` |
| `M_` | Material | `M_Road`, `M_Campfire` |
| `MI_` | Material Instance | `MI_Black`, `MI_Bottle` |
| `SM_` | Static Mesh | `SM_Campfire`, `SM_RifleAdvanced` |
| `SKM_` | Skeletal Mesh | `SKM_BodyArmor` |
| `PA_` | Physics Asset | `PA_BodyArmor` |
| `T_` | Texture | `T_Helmet` |
| `RT_` | Render Target | `RT_InspectRenderer` |
| `DT_` | Data Table | `DT_Items` |
| `DD_` | Data Definition (DataAsset) | `DD_Item` |
| `ML_` | Macro Library (Blueprint) | `ML_InventoryMacros` |
| `SG_` | SaveGame | `SG_AGIS_UID`, `SG_AGIS_World` |
| `GM_` / `PC_` | GameMode / PlayerController | `GM_Sandbox`, `PC_Sandbox` |
| `CR_` | Control Rig | `CR_ClimbingGraphV2` |
| `ABP_` | Animation Blueprint | `ABP_GenericRetarget` |
| `AM_` | Anim Modifier / Anim Montage | `AM_ReloadHip` |
| `NS_` | Niagara System | `NS_Tornado` |
| `BPI_` | Blueprint Interface | `BPI_Interactable` |
Le suffixe `AGIS` sur beaucoup d'assets (`IA_InteractAGIS`, `WB_...AGIS`, fonctions "Target is Inventory Player") signale qu'ils appartiennent au **socle marketplace AGIS**, par opposition aux ajouts spécifiques au projet.
### 1.5 Conventions importantes Ă  respecter
- **Ne jamais renommer/dĂ©placer** les assets du socle AGIS (`Content/Core/Coreinventory/`, une bonne partie de `Content/Systems/Inventory/`, `Content/UI/Inventory_UI/Widgets/WB_Container|WB_EquipSlot|WB_InventorySlot|WB_Item`) sans nĂ©cessitĂ© — de nombreuses rĂ©fĂ©rences internes au plugin marketplace en dĂ©pendent.
- **Nouveau systĂšme de gameplay** → crĂ©er un dossier dĂ©diĂ© sous `Content/Systems/<NomDuSysteme>/`, Ă  l'image de `Systems/Stats/`, `Systems/Scent/`, `Systems/Movable_Object/`.
- **Nouvel objet d'inventaire (item)** → suivre le patron existant dans `Content/Items/` : un Blueprint `BP_Item_<Nom>` (hĂ©ritant de `_BP_ItemBase` ou d'une base spĂ©cialisĂ©e dans `Items/BaseBlueprints/`), ses assets (mesh/matĂ©riau/texture) dans un sous-dossier `Content/Items/<Nom>/` ou `Content/Items/Resources/<Nom>/`, et une entrĂ©e dans `DT_Items` / les DataAssets sous `Content/Data/Inventory_Data/DataAssets/`.
- **Nouvel Input Action liĂ© Ă  l'inventaire** → `Content/Systems/Inventory/Other/Inputs/Actions/`, mappĂ© dans `IMC_AGIS_Character` ou `IMC_AGIS_InventoryTab`. **Input Action de dĂ©placement/camĂ©ra gĂ©nĂ©rique** → `Content/Core/PlayerController/Input/`, mappĂ© dans `IMC_Sandbox`.
- **Nouveau widget d'inventaire** → `Content/UI/Inventory_UI/Widgets/`. **Nouveau widget hors inventaire** (HUD, menus) → `Content/UI/Widgets/`.
- Le clavier cible est **AZERTY** ; privilégier des touches identiques physiquement sur QWERTY/AZERTY pour les nouveaux raccourcis (ex. `V`, `B`).
- Réplication réseau : toute action qui modifie l'état du jeu cÎté inventaire/stats doit suivre le patron `Request X SERVER` (Custom Event `...SERVER`, `Replicated From Client, Executes On Server`) déjà présent dans `Inventory_Player`.
---
## 2. Vue d'ensemble de l'arborescence racine
```text
ProjectF/
├── .gitattributes → Rùgles Git (LFS, line-endings)
├── .gitignore → Exclut Binaries/, Intermediate/, Saved/, DerivedDataCache/, .vscode/

├── .mcp.json → DĂ©clare le serveur MCP "unreal-mcp" (http://127.0.0.1:8000/mcp) utilisĂ© par les agents IA
├── ProjectFv1.uproject → Fichier projet UE5.8 : liste des plugins activĂ©s (voir §1.2/§8)
├── README.md → Description minimale du projet
├── Architecture_Projet.md → CE DOCUMENT
│
├── .claude/ → Skills et configuration Claude Code pour ce projet
│ └── skills/
│ └── maintain-architecture-doc/SKILL.md → Skill de maintenance de ce document (voir §10)
│
├── .codex/, .cursor/, .gemini/, .vscode/ → Configuration d'IDE / d'agents IA tiers (mcp.json, config.toml
)
│
├── Config/ → Configuration moteur & jeu (voir dĂ©tail §7)
├── Content/ → Tous les assets du jeu (voir dĂ©tail §3 Ă  §6)
│
├── Binaries/, Intermediate/, Saved/, DerivedDataCache/ → GĂ©nĂ©rĂ©s par l'Ă©diteur, exclus de Git, jamais Ă  Ă©diter/documenter
```
### 2.1 ⚠ Fichiers centraux autrefois isolĂ©s Ă  la racine de `Content/` (rangĂ©s le 2026-09-13)
Quelques fichiers importants vivent directement sous `Content/`, hors de toute organisation par dossier — Ă  connaĂźtre avant de chercher "oĂč est le personnage" ou "oĂč est le GameInstance" :
```text
Content/
├── Core/Character/SandboxCharacter_CMC.uasset → (dĂ©placĂ© depuis la racine le 2026-09-13) ⭐ Le Blueprint du PERSONNAGE JOUEUR lui-mĂȘme (Character/Pawn). RĂ©fĂ©rencĂ© par GM_Dev, GM_Sandbox, PC_Sandbox, Gamemode_AGIS, WB_InventoryScreen, WBP_PauseMenu, BP_Moveble_Object, BP_Simply_Door, AC_PreCMCTick, AC_ClimbingComponent et son propre Anim BP (Systems/Blueprints/SandboxCharacter_CMC_ABP). C'est le point d'entrĂ©e central de tout le gameplay. Son EventGraph contient aussi : la dĂ©tection de mort par chute (suivi tick de `IsFalling`/`FallStartZ`, calcul de hauteur Ă  `OnLanded`, dĂ©gĂąts scalĂ©s via `AC_Stats.TakeDamage` au-delĂ  de 300 unitĂ©s, lĂ©tal au-delĂ  de 800) et une touche de debug K (InputDebugKey) qui force la mort via ce mĂȘme `TakeDamage` — ajoutĂ©s le 2026-09-04.
├── Core/Character/SandboxCharacter_Mover.uasset + _Mover_Ragdoll.uasset → Personnage alternatif basĂ© sur le plugin **Mover** (pion par dĂ©faut de `GM_Dev` ; en PIE c'est la variante `_Ragdoll` qui est possĂ©dĂ©e). DĂ©crits avec le reste du framework Mover en §4.2.1, et leurs systĂšmes cover/prone/nage en §4.2.2.
├── Core/BP_GameInstance.uasset → (dĂ©placĂ© depuis la racine le 2026-09-13 ; `Config/DefaultEngine.ini` → `GameInstanceClass=/Game/Core/BP_GameInstance.BP_GameInstance_C`) GameInstance rĂ©ellement utilisĂ© par le projet (Ă  ne pas confondre avec `Core/Coreinventory/GameInstance_AGIS`, la base du plugin — vĂ©rifier lequel des deux est configurĂ© comme GameInstance par dĂ©faut avant de supposer que c'est l'un ou l'autre).
└── Data/DT_DemoTags.uasset → (dĂ©placĂ© depuis la racine le 2026-09-13 ; l'export texte `DT_DemoTags.T3D` a Ă©tĂ© supprimĂ©) ⚠ **À NE PAS SUPPRIMER** : DataTable de gameplay tags du socle AGIS, dĂ©clarĂ©e dans `Config/DefaultGameplayTags.ini` (`+GameplayTagTableList=/Game/Data/DT_DemoTags`). Elle dĂ©finit `General Inventory Slot` (indispensable Ă  l'inventaire), `Human.*`, `Attachment.*` (singulier), `Flashlight.Battery`, `Other.*`, `Vehicle.*`. (CorrigĂ© le 2026-09-13 : l'ancienne mention « aucune rĂ©fĂ©rence trouvĂ©e » Ă©tait fausse — la rĂ©fĂ©rence passe par la config, invisible pour l'Asset Registry.) Dette connue : `DT_Items`, `Inventory_Player` et `AttachmentData_AdvancedRifle` utilisent `Attachments.*` (pluriel, tags inexistants → avertissements « Invalid GameplayTag »), antĂ©rieur au commit du 2026-09-04.
```
---
## 3. `Content/Core/` — Socle inventaire (AGIS) + Player Controller + Tick
Le cƓur technique de l'inventaire et des entrĂ©es gĂ©nĂ©riques du joueur.
```text
Content/Core/
├── Coreinventory/ → CƓur du plugin AGIS (composants, donnĂ©es, sauvegarde)
│ ├── Inventory__Main.uasset → ActorComponent BASE : logique gĂ©nĂ©rique de containers/slots, rĂ©plication rĂ©seau. Racine de la hiĂ©rarchie d'inventaire.
│ ├── Inventory_Equipper.uasset → hĂ©rite de Inventory__Main : ajoute la logique d'Ă©quipement (armes/objets tenus en main)
│ ├── Inventory_Player.uasset → hĂ©rite de Inventory_Equipper : composant d'inventaire du JOUEUR. Contient toute la logique spĂ©cifique projet ajoutĂ©e cette session (SelectQuickSlot, ToggleQuickEquip, GetQuickSlotItemInfo, graphe INPUTS avec tous les bindings Enhanced Input du joueur). (2026-09-05) `ToggleQuickEquip`/`GetQuickSlotItemInfo` (touches V/B) Ă©taient des stubs de prototype (PrintString uniquement, aucun appel d'Ă©quipement rĂ©el, plus un bool `bQuickSlotArmed` inversĂ©) — corrigĂ©s pour appeler rĂ©ellement `Inventory_Equipper::SwitchWeaponByUID`/`EmptyHands` (hĂ©ritĂ©s). `GetQuickSlotItemInfo` retourne dĂ©sormais aussi le `ContainerUID` du slot (WEAPON/WEAPON2/Speed1/Speed2) via `WBContainer::GetContainerUID`, nĂ©cessaire pour cibler `SwitchWeaponByUID`. B Ă©quipe/dĂ©sĂ©quipe donc rĂ©ellement l'item du slot prĂ©sĂ©lectionnĂ© par V (le slot doit dĂ©jĂ  contenir un item via le drag&drop du paperdoll — la roue V/B ne fait que choisir PARMI les 4 emplacements dĂ©jĂ  Ă©quipĂ©s, elle ne pioche pas dans l'inventaire gĂ©nĂ©ral). Nouvelle fonction `CycleQuickSlot()` (Weapon→Weapon2→Speed1→Speed2→Weapon→
) ajoutĂ©e pour fiabiliser la sĂ©lection : `WB_QuickSelect::EventConstruct` (Content/UI/Inventory_UI/Widgets/Other/WB_QuickSelect.uasset) l'appelle dĂ©sormais automatiquement Ă  chaque pression de V (donc Ă  chaque crĂ©ation de la roue), sans dĂ©pendre du clic sur un bouton de la roue (`BtnWeapon1` etc. — dont le fonctionnement en jeu n'a pas pu ĂȘtre confirmĂ©). Les boutons de la roue restent cĂąblĂ©s Ă  `SelectQuickSlot` en plus, au cas oĂč le clic fonctionne rĂ©ellement.
│ ├── Inventory_Storage.uasset → Inventaire des containers de stockage (coffres, trunk, etc.)
│ ├── Inventory_Vendor.uasset → Inventaire des PNJ vendeurs
│ ├── Inventory_Crafting.uasset → Inventaire des stations de craft (campfire, smelter, gĂ©nĂ©rateur)
│ ├── BP_AttachmentHandler.uasset → ActorComponent gĂ©rant les attachements sur les armes (silencieux, lampe, chargeur
)
│ ├── CombatComponent.uasset → ActorComponent : logique de combat (tir, dĂ©gĂąts) liĂ©e aux armes AGIS
│ ├── BP_SM_DroppedMesh.uasset → Acteur reprĂ©sentant un objet lĂąchĂ© au sol (mesh statique + interaction de ramassage)
│ ├── ItemMesh_STATIC.uasset / ItemMesh_Skeletal.uasset → Acteurs gĂ©nĂ©rant le rendu 3D d'un item tenu/lĂąchĂ© (mesh statique ou squelettal)
│ ├── DD_Item.uasset → DataAsset dĂ©crivant un item (rĂ©fĂ©rencĂ© par ItemDefaults)
│ ├── FL_AGIS.uasset → Function Library : fonctions statiques utilitaires du plugin AGIS
│ ├── ML_AGISDefaults.uasset → Macro Library : macros par dĂ©faut du plugin
│ ├── ML_AGIS_UMG.uasset → Macro Library : macros dĂ©diĂ©es Ă  l'UI (UMG)
│ ├── ML_InventoryMacros.uasset → Macro Library : macros gĂ©nĂ©riques d'inventaire
│ ├── GameInstance_AGIS.uasset → GameInstance de base du plugin (Ă©tat persistant entre les maps)
│ ├── Gamemode_AGIS.uasset → GameMode de base du plugin (indĂ©pendant de GM_Sandbox, voir §4)
│ ├── PlayerController_AGIS.uasset → PlayerController de base du plugin
│ └── SG_AGIS_UID.uasset / SG_AGIS_World.uasset → SaveGame : compteur d'UID uniques / Ă©tat persistant du monde (sauvegarde)
│
├── PlayerController/ → Input gĂ©nĂ©rique du joueur (mouvement, camĂ©ra), indĂ©pendant de l'inventaire
│ ├── IA_InitiateClimb / IA_LeapFromClimb / IA_MovingClimb.uasset → Input Actions liĂ©es Ă  l'escalade
│ └── Input/
│ ├── IMC_Sandbox.uasset → Input Mapping Context PRINCIPAL, ajoutĂ© dans `SetupInput` par `SandboxCharacter_CMC` ET (depuis le 2026-09-15) par `SandboxCharacter_Mover` (mouvement, camĂ©ra, saut, + actions AGIS : `IA_ToggleInventory` Tab, `IA_InteractAGIS`, `IA_QuickEquip/Select/GiveAGIS`, `IA_UseItemPrimary/SecondaryAGIS`). Mappings `IA_TriggerRagdoll` (R, X manette) et `IA_Takedown` (clic gauche, RT) ajoutĂ©s le 2026-09-15 pour le Mover ragdoll
│ └── IA_Move, IA_Look, IA_Jump, IA_Sprint, IA_Run, IA_Walk, IA_Crouch, IA_Prone, IA_Strafe,
│ IA_Aim, IA_Interact, IA_ESC, IA_NextPawn, IA_Traverse, IA_TeleportToTarget,
│ IA_SwimmingDeep, IA_TwinStick_AimDirection, IA_Move_WorldSpace, IA_Look_Gamepad,
│ IA_NextVisualOverride, IA_Takedown, IA_TriggerRagdoll, IA_rool.uasset → Input Actions individuelles mappĂ©es dans IMC_Sandbox. Aussi Ă©coutĂ©es par la chaĂźne Mover (`SandboxCharacter_Mover`/`_Ragdoll`, `Systems/Mover/PC_Sandbox`, `Systems/Mover/SmartObjects/AC_SmartObjectAnimation`) ; le sprint du Mover Ă©coute `IA_Run` (Started/Completed). `IA_rool` et `IA_Traverse` ne sont Ă©coutĂ©s par aucun Blueprint
│
└── Tick/ → SystĂšme de gestion centralisĂ©e du Tick (perf)
├── BPI_TickManager.uasset → Interface implĂ©mentĂ©e par les acteurs qui veulent ĂȘtre "tickĂ©s" par le manager
├── BP_TickManager.uasset → Acteur central qui distribue les ticks (probablement Ă  frĂ©quence configurable)
└── E_TickFrequency.uasset → Enum des frĂ©quences de tick disponibles
```
---
## 4. `Content/Systems/` — Systùmes de gameplay
### 4.1 `Systems/Inventory/` — Extension du socle AGIS pour ce projet
```text
Content/Systems/Inventory/
├── BP_Spawner.uasset → Acteur de spawn (probablement spawn d'items/loot en dĂ©but de partie)
├── DT_Items.uasset → DataTable listant tous les items du jeu (clĂ© pour crĂ©er/reconnaĂźtre un item)
│
├── Interactables/ → Tout ce que le joueur peut "interagir" avec (touche IA_InteractAGIS)
│ ├── BP_EnemyPuppet.uasset → PNJ/mannequin pillable
│ ├── BP_Vendor.uasset → PNJ vendeur (utilise Inventory_Vendor)
│ ├── CraftingInteractables/
│ │ ├── Campfire/BP_Campfire.uasset → Station de cuisson/craft (feu de camp) + ses assets (mesh, matĂ©riaux)
│ │ └── Smelter/BP_Smelter.uasset → Station de fonte de minerai + ses assets
│ ├── FuelGenerator/BP_Generator.uasset → GĂ©nĂ©rateur Ă©lectrique (consomme du carburant)
│ ├── KeycardReader/BP_KeycardReader.uasset → Lecteur de carte magnĂ©tique (dĂ©verrouillage)
│ ├── Other/BP_SimpleDoor.uasset → Porte interactable simple
│ └── Storages/
│ ├── BP_StorageBox.uasset, BP_Trunk.uasset, BP_SphereStorage.uasset, _BP_StorageBase.uasset → Coffres/contenants de stockage (hĂ©ritent de _BP_StorageBase)
│ └── Deadbody/BP_Deadman.uasset → Cadavre pillable
│
├── Interfaces/ → Interfaces Blueprint (contrats partagĂ©s)
│ ├── BPI_Interactable.uasset → Contrat pour tout objet interactible (implĂ©mentĂ© par tous les Interactables ci-dessus)
│ ├── BPI_SlotWidgets.uasset → Contrat pour les widgets de slot d'inventaire
│ ├── BPI_HoldableItem.uasset → Contrat pour un item tenable en main
│ ├── BPI_WeaponAGIS.uasset → Contrat pour les armes
│ ├── BPI_DamageFeedback.uasset → Contrat de retour visuel/sonore lors de dĂ©gĂąts
│ └── BPI_GameInstance_AGIS.uasset → Contrat exposĂ© par le GameInstance cĂŽtĂ© AGIS
│
├── Other/ → Assets d'appoint de l'inventaire
│ ├── Animations/ → Animations liĂ©es aux armes/mains nues (Jog, Rifle, TurnInPlace, WALK) — clips Motion Matching (MM_*) et montages (AM_*)
│ ├── Fonts/ → Polices utilisĂ©es par l'UI d'inventaire
│ ├── Inputs/
│ │ ├── Actions/ → **Toutes** les Input Actions liĂ©es Ă  l'inventaire (IA_*AGIS), y compris IA_QuickSelectAGIS et IA_QuickEquipAGIS (sĂ©lection rapide, ajoutĂ©es pour ce projet)
│ │ ├── IMC_AGIS_Character.uasset → Mapping Context actif en jeu (personnage) : touches 1/2/3/4, F, V, B, etc.
│ │ └── IMC_AGIS_InventoryTab.uasset → Mapping Context actif quand l'inventaire plein Ă©cran est ouvert
│ ├── InspectCapture/ → Rendu d'inspection d'objet (RT_InspectRenderer + BP_InspectRenderer + M_Inspect)
│ └── Materials/ → MatĂ©riaux gĂ©nĂ©riques rĂ©utilisĂ©s par les items (MI_Black, MI_Red, M_Metal, M_Outline
)
│
└── Utilities/ → Outils d'Ă©diteur (Editor Utility Widgets) pour la crĂ©ation de containers/inventaires
├── EUW_ContainersCreator.uasset, EUW_WidgetToInventoryData.uasset
└── Others/WB_ContainerSpecsUtility.uasset, WB_CreatorContainerUtility.uasset
```
### 4.2 `Systems/Blueprints/` — Framework de personnage / locomotion ("Sandbox Character")
Contenu trĂšs largement issu du sample Epic **Motion Matching** (plugins `Mover`, `MotionTrajectory`, `Chooser`, `AnimationLocomotionLibrary`). À traiter comme un framework tiers : prĂ©fĂ©rer l'Ă©tendre plutĂŽt que le réécrire.
```text
Content/Systems/Blueprints/
├── BPI_SandboxCharacter_Pawn.uasset → Interface du Pawn joueur (point d'entrĂ©e du framework de locomotion). (2026-09-14) + fonctions du sample GASP pour le Mover : `AddTarget`, `RemoveTarget`, `Set_IsPlayingRootMotion`, `Set_PhysicsProfile`, sorties typĂ©es de `Get_MMIResult` / `Get_PropertiesForRagdoll`
├── BPI_SandboxCharacter_ABP.uasset → Interface de l'Anim Blueprint du personnage. (2026-09-14) + `Get_Trajectory`, `Update_MMIConstraints`
├── AC_TraversalLogic.uasset → ActorComponent : logique de traversal (vault/mantle/climb contextuel)
├── AC_PreCMCTick.uasset → ActorComponent : logique exĂ©cutĂ©e avant le tick du mouvement (Mover/CMC)
├── AC_VisualOverrideManager.uasset → ActorComponent : gestion des overrides visuels du personnage
├── GM_Sandbox.uasset / PC_Sandbox.uasset → GameMode / PlayerController du framework de locomotion (indĂ©pendants de Gamemode_AGIS/PlayerController_AGIS, voir §3)
│
├── Cameras/ → SystĂšme de camĂ©ra (Camera Rigs modulaires)
│ ├── CameraAsset_SandboxCharacter, CameraDirector_SandboxCharacter, CHT_CameraRig
│ ├── E_CameraMode.uasset, E_CameraStyle.uasset
│ └── Rigs/CameraRig_* (Close/Medium/Far × Aim/Freecam/Strafe, CrouchOffset, CollisionOffset, TwinStick
)
│
├── MovementModes/ → Modes de mouvement personnalisĂ©s (plugin Mover)
│ ├── BP_MovementMode_Walking / _Falling / _Slide
│ ├── BP_MovementTransition_ToSlide / _FromSlide
│ └── BP_ClimbingMode.uasset
│
├── ControlRigs/CR_Biped_FootPlacement.uasset → Control Rig : placement des pieds (foot IK)
├── RetargetedCharacters/ABP_GenericRetarget.uasset → Anim BP gĂ©nĂ©rique pour le retargeting
│
├── AnimModifiers/ → Anim Modifiers (post-traitement de clips d'animation à l'import)
├── AnimNotifies/ → Anim Notifies personnalisĂ©es (BP_AnimNotify_FoleyEvent*, BP_NotifyState_EarlyTransition/MontageBlendOut) + enums associĂ©s
│
├── Data/ → Structs/Enums/Curves partagĂ©s par tout le framework de locomotion
│ ├── E_Gait, E_Stance, E_MovementMode, E_MovementState, E_MovementDirection(Bias), E_RotationMode,
│ │ E_AnalogStickBehavior, E_ExperimentalStateMachineState, E_TraversalActionType
│ │ (2026-09-14, convergence §8.2) valeurs du sample ajoutĂ©es EN FIN d'enum : E_MovementMode += Ragdoll, Flying ;
│ │ E_ExperimentalStateMachineState += Debug Flying Loop. S_CharacterPropertiesForAnimation += BasedMovementDelta,
│ │ Weapon_Type (`Data/Enums/E_WaponType`) ; S_BlendStackInputs += Blend Curve. Ne jamais rĂ©ordonner ces enums (valeurs stockĂ©es par nom interne).
│ ├── S_CharacterPropertiesForAnimation / _ForCamera / _ForTraversal, S_BlendStackInputs,
│ │ S_MoverCustomInputs, S_PlayerInputState, S_TraversalCheckInputs/Result,
│ │ S_TraversalChooserInputs/Outputs (contexte des choosers `CHT_TraversalMontages_CMC/_Mover`), S_ChooserOutputs
│ │ (contexte de `CHT_CMCCharacterAnimations`), S_MovementDirectionThresholds,
│ │ S_RotationOffsetCurveChooser_Inputs, S_DebugGraphLineProperties
│ ├── Curve_RotationOffset_* , Curve_StrafeSpeedMap, CHT_RotationOffsetCurve → Curve Tables pilotant les offsets d'animation
│ └── BFL_HelpfulFunctions.uasset, BPI_InteractionTransform.uasset
│
├── SmartObjects/ → IntĂ©gration du plugin SmartObject (points d'ancrage d'animation contextuelle)
│ ├── CHPA_SmartObject, AC_SmartObjectAnimation
│ └── SmartObjectAnimationPayload, SmartObjectSelectionInputs/Outputs
│
├── SandboxCharacter_CMC_ABP.uasset → Anim Blueprint principal du personnage joueur (pilotĂ© par `/Game/Core/Character/SandboxCharacter_CMC`). Anciennement Ă©garĂ© dans un dossier `d/` mal nommĂ© — dĂ©placĂ© ici (nettoyage du 2026-09-04).
│
├── (SUPPRIMÉS de ce dossier) SandboxCharacter_Mover / _ABP / _Ragdoll → **Depuis le 2026-09-13 ces trois assets n'existent plus dans `Systems/Blueprints/`** : les versions actives (personnage Mover conservĂ© Ă  la demande de l'utilisateur) sont dans `Content/Systems/Mover/` (voir §4.2.1). `SandboxCharacter_CMC` (`Content/Core/Character/`) reste le personnage RÉEL du jeu. VĂ©rifiĂ© le 2026-09-14 : `GM_Dev` (GameMode de `MapDev1`), `Systems/Blueprints/GM_Sandbox` et `Core/Coreinventory/Gamemode_AGIS` ont `DefaultPawnClass = /Game/Core/Character/SandboxCharacter_CMC` ; `GM_Sandbox_Ragdoll` et `GM_MoverTest` (contenu de test du sample) restent Ă  `None` ; `Systems/Mover/GM_Sandbox` spawne `SandboxCharacter_Mover`.
│
├── GM_Sandbox_Ragdoll.uasset → Variante de `GM_Sandbox` avec `SandboxCharacter_Mover_Ragdoll` comme pawn par dĂ©faut (contenu du sample, non utilisĂ©e par le jeu rĂ©el).
│
├── AI/StateTree/ → **(2026-09-08)** StateTree + tĂąches pour l'interaction Smart Object du personnage (`ST_Player_SandboxCharacter_SmartObject`, `STT_CharacterIgnoreCollisionsWithOtherActor`, `STT_FindSlotTransforms`, `S_SmartObjectPayload`). Contenu du sample, AI/SmartObject non branchĂ© sur le gameplay AGIS.
│
└── (2026-09-08) Contenu supplĂ©mentaire fusionnĂ© depuis un import du sample officiel Epic **Game Animation Sample** (voir note globale en fin de §8) : `RetargetedCharacters/` (BP_Echo, BP_Manny, BP_Quinn, BP_Twinblast, BP_UE4_Mannequin — **supprimĂ©s le 2026-09-13** avec les personnages de dĂ©mo ; seul `ABP_GenericRetarget` subsiste), des `Cameras/Rigs/` et `MovementModes/` supplĂ©mentaires (`CameraRig_Far_Ragdoll`, `CameraRigPrefab_ThirdPerson`, `BP_MovementMode_Ragdoll`, `B_MovementMode_Flying`), des `Data/` supplĂ©mentaires (`S_RagdollProperties`, `S_MoverCustomInputs_Ragdoll`, `E_RagdollInjuryState`, `Ragdoll_Fall_to_Ground_BlendCurve`, variantes `S_CHT_*_IN/OUT` pour les choosers de traversal/interaction/get-up), des `AnimNotifies/` supplĂ©mentaires (`BP_NotifyState_MMI_IK`, `_MMI_MotionWarping`, `_OverrideCapsuleCollision`, `_OverrideMovementMode`, enums `E_EarlyTransition_*`/`E_FoleyEventSide`/`E_TraversalBlendOutCondition`), et des `SmartObjects/` supplĂ©mentaires (`Bench/`, `DistanceToSmartObject`, `PSS_SmartObject`, `TasksAndConditions/STT_ClaimSlot|ClearHandle|PlayAnimFromBestCost|PlayAnimMontage`).
```
### 4.2.1 `Systems/Mover/` et `Systems/Aiming/` — personnage Mover et framework du sample (isolĂ©s le 2026-09-14)
Copie du framework "Sandbox Character" du sample Epic **Game Animation Sample**, arrivĂ©e avec la migration `Project_F` (§8.2) et conservĂ©e pour le personnage alternatif basĂ© sur le plugin **Mover** et pour le systĂšme de visĂ©e. DĂ©placĂ©e SANS fusion depuis `Content/Blueprints/` et `Content/Input/`. Beaucoup d'assets ont un homonyme dans `Systems/Blueprints/` (`AC_TraversalLogic`, `BPI_*`, `GM_Sandbox`, `PC_Sandbox`, structs `S_*`, camĂ©ras
) : ce sont des doublons DIVERGENTS, Ă  ne pas confondre ni fusionner aveuglĂ©ment (voir §9, piĂšge `consolidate_assets`). Les enums, eux, sont partagĂ©s avec `Systems/Blueprints/Data/` (fusionnĂ©s le 2026-09-14).
```text
Content/Systems/Mover/
⚠ (2026-09-18) `SandboxCharacter_Mover`, `_Ragdoll` (et l'ABP du CMC) vivent en rĂ©alitĂ© dans `Content/Core/Character/` — les deux lignes ci-dessous y correspondent, elles restent dĂ©crites ici pour garder le framework Mover d'un seul tenant.
├── SandboxCharacter_Mover.uasset → (dans `Content/Core/Character/`) Personnage basĂ© sur le plugin Mover. (VĂ©rifiĂ© le 2026-09-15) `GM_Dev` (GameMode de `MapDev1`) a `DefaultPawnClass = SandboxCharacter_Mover` ; en PIE sur `MapDev1` le pion obtenu est `SandboxCharacter_Mover_Ragdoll`. `SetupInput` ajoute `Core/PlayerController/Input/IMC_Sandbox` (mĂȘme IMC que le CMC, inputs AGIS/inventaire compris) ; il porte aussi `Inventory_Player`, dont la propriĂ©tĂ© `Input Mapping Context` DOIT valoir ce mĂȘme `IMC_Sandbox` (corrigĂ© le 2026-09-15, elle valait `IMC_AGIS_Character`) : Ă  la fermeture de l'inventaire, `WB_InventoryScreen.SwitchInput` fait `ClearAllMappings` puis ne rĂ©-ajoute QUE cette variable — une mauvaise valeur coupe tous les contrĂŽles (ZQSD, camĂ©ra) aprĂšs fermeture. MĂȘme piĂšge pour tout futur pion Ă©quipĂ© d'`Inventory_Player`. ImplĂ©mente `BPI_Interactable` depuis le 2026-09-17 (`GetTracePoint` = position/forward du composant `Camera`, comme `SandboxCharacter_CMC`) : sans cette interface, le trace d'interaction d'`Inventory_Player` (`Client Trace`, `Verify Trace`) part de (0,0,0) avec une direction nulle et aucun objet ne peut ĂȘtre ramassĂ©. Tout futur pion Ă©quipĂ© d'`Inventory_Player` doit aussi implĂ©menter `BPI_Interactable::GetTracePoint`
├── SandboxCharacter_Mover_Ragdoll.uasset → Variante ragdoll du personnage Mover. ⚠ C'est LUI le pion rĂ©ellement possĂ©dĂ© en PIE ; il surcharge le composant `CharacterMover` (ses propres `MovementModes`, dont `Ragdoll`) et `ProduceInput`/`On_MovementModeChanged_PostFinalize`. Toute modification du parent qui touche Ă  ces Ă©lĂ©ments doit ĂȘtre vĂ©rifiĂ©e ici.
├── SandboxCharacter_Mover_ABP.uasset → Anim Blueprint du Mover (utilise `Systems/Aiming/ABP_AimingMaster`). (2026-09-18) Deux corrections majeures : (1) les nƓuds `Evaluate Chooser` de `Update_MotionMatching` n'avaient AUCUN objet de contexte branchĂ© (au CMC il est reliĂ© Ă  `self`) → le chooser ne pouvait rĂ©soudre aucune de ses colonnes, `ValidDatabases` restait vide et la branche Motion Matching de l'`AnimGraph` Ă©tait morte ; `self` est dĂ©sormais branchĂ© sur les deux nƓuds. (2) Nouvelle variable `AnimBranchIndex` (affectĂ©e en fin d'`Update_Logic`) branchĂ©e sur l'`ActiveChildIndex` du *Blend Poses by int* de l'`AnimGraph`. Elle a d'abord pilotĂ© un hybride (Motion Matching en prone/nage, state machine GASP sinon) ; **depuis le 2026-09-18 elle vaut la constante `0`** : l'ABP du Mover est **100 % Motion Matching** (chooser `CHT_PoseSearchDatabases_Mover`), la state machine « State Controller » n'est plus jamais Ă©valuĂ©e. Le nƓud `Set AnimBranchIndex` et la variable sont conservĂ©s pour pouvoir rebasculer sur la voie 1 en changeant une seule valeur. Variables `Is Armed` et `IS Moving Object` ajoutĂ©es pour les colonnes du chooser copiĂ©.
├── GM_Sandbox.uasset / PC_Sandbox.uasset → GameMode (pawn = SandboxCharacter_Mover) et PlayerController du Mover ; crĂ©ation du widget de dĂ©mo retirĂ©e le 2026-09-14
├── AC_TraversalLogic.uasset → Traversal du personnage Mover (vault/mantle/hurdle/climb). (2026-09-15) La dĂ©tection de rebords du sample (limitĂ©e aux blocs de dĂ©mo `LevelBlock_Traversable`, retirĂ©e le 2026-09-14) est remplacĂ©e par celle de `Systems/Blueprints/AC_TraversalLogic` (CMC), portĂ©e Ă  l'identique : variables `Min Ledge Width`, `Min Front Ledge Depth`, `Max Ledge Sideways Depth`, `Trace Complex`, `Show Trace`, `Persistent Show Trace`, `RespectPlayersFacingDir`, `Banned Tags` (mĂȘmes valeurs par dĂ©faut) et fonctions `ComputeLedgeData`, `TraceAlongHitPlane`, `TraceCorners`, `TraceWidth`, `NudgeTraceTowardsObjectOrigin`, `Set Trace Hit Point`, `Tag Compare`. Dans `TryTraversalAction` : capsule trace sur `TraceTypeQuery1` (Visibility, au lieu du canal `Traversable`), `Hit` → tags acteur+composant → `ComputeLedgeData` → rebords avant/arriĂšre Ă©crits dans `TraversalCheckResult` (Ă©quivalent inlinĂ© de `TryAndCalculateLedges`, non recopiĂ©e car elle prend le struct Systems par rĂ©fĂ©rence), puis `Tag Compare` (tags bannis) avant le chooser `CHT_TraversalMontages_Mover`. Le dĂ©clenchement (`IA_Jump` → `CanDoTraversal` → `TryTraversalAction`) et l'exĂ©cution Mover (`PerformTraversalAction_Mover`, mode `Traversing`) sont inchangĂ©s. Les deux composants restent des doublons divergents (structs/BPI `Systems/Mover/` vs `Systems/Blueprints/`).
├── AC_PreCMCTick, AC_PostABPTick, AC_VisualOverrideManager → Composants du sample (versions GASP, divergentes de celles de `Systems/Blueprints/`)
├── BPI_SandboxCharacter_Pawn / _ABP.uasset → Interfaces GASP implĂ©mentĂ©es par la chaĂźne Mover
├── AI/StateTree/ → `ST_Player_SandboxCharacter_SmartObject` + tñches (copie GASP)
├── AnimModifiers/ → `AM_BakePhaseCurveFromFootstepNotifies`, `AM_FootSteps_Run` (rĂ©fĂ©rencĂ©s par quelques anims `UEFN_Mannequin`)
├── Cameras/ → `CameraAsset_SandboxCharacter`, `CameraDirector_SandboxCharacter`, `CHT_CameraRig`, `CameraShake_Basic`, `Rigs/CameraRig_*` (16)
├── ControlRigs/CR_Biped_FootPlacement.uasset
├── Data/ → Structs GASP (`S_CharacterPropertiesFor*`, `S_Traversal*`, `S_CHT_*` des choosers Mover/GetUp/Interaction, `S_Ragdoll*`, `S_MoverCustomInputs*`
), `Curve_RotationOffset_*`, `CHT_RotationOffsetCurve`, `BFL_HelpfulFunctions`, `BPI_InteractionTransform`
├── MovementModes/ → `BP_MovementMode_Walking/Falling/Slide/Ragdoll`, `B_MovementMode_Flying`, `BP_MovementTransition_To/FromSlide`, **`BP_MovementMode_Swimming`** (2026-09-18). `BP_MovementMode_Walking` : la `Select` d'allure n'alimentait QUE Walk/Run/Sprint — les allures ajoutĂ©es (Prone, Swinning, SwinningSprint) donnaient vitesse ET accĂ©lĂ©ration = 0 (personnage figĂ©) ; ajout de la variable `ProneSpeed` (125) et cĂąblage des entrĂ©es manquantes (prone → `ProneSpeed`/`WalkAcceleration`, nage → Walk/Run). `BP_MovementMode_Swimming` : enfant de `SimpleFlyingMode` (comme le mode vol), tag `Mover.IsSwimming`, `MaxSpeedOverride = 1` (l'entrĂ©e devient un pur vecteur d'intention) ; sa fonction `ComputeSwimTarget` lit le personnage propriĂ©taire (cast `SandboxCharacter_Mover`) et produit la vitesse cible : en surface, Z = flottaison vers `WaterSurfaceZ - SurfaceOffset` (60) bornĂ©e par `MaxBobSpeed`, en plongĂ©e Z = intention camĂ©ra + `SwimVerticalInput` × `VerticalSpeed`, horizontal = intention × `SwimSpeed` (250) ou `SwimSprintSpeed` (450). ⚠ Le `SwimmingMode` natif du plugin Mover (UE 5.8) n'est PAS utilisable : son `SimulationTick` est vide et rien n'alimente ses donnĂ©es d'eau (`CommonBlackboard::LastWaterResult`).
└── SmartObjects/ → `AC_SmartObjectAnimation` (version GASP, aussi utilisĂ©e par des STT de `Systems/Blueprints/`), `CHPA_SmartObject`, `PSS_SmartObject`, `Bench/`, `TasksAndConditions/STT_*`
Content/Systems/Aiming/ → SystĂšme de visĂ©e dĂ©veloppĂ© dans Project_F : `ABP_AimingMaster`, `ABP_AimingMaster_UEFN` (aim offset), choosers `CH_AO`, `CH_Base_AO_Animechooser` (enum `Data/Enums/E_WaponType`)
Content/Blueprints/AnimNotifies/ → SEUL reste de `Content/Blueprints/` : 23 notifies du sample (`BP_AnimNotify_FoleyEvent*`, `BP_NotifyState_EarlyTransition`, `_MontageBlendOut`, `_MMI_*`, `BP_AnimNotify_TriggerRagdoll`, `_OverrideMovementMode`
) liĂ©s Ă  1278 animations de `Characters/UEFN_Mannequin`. LaissĂ©s en place volontairement : les dĂ©placer rĂ©enregistrerait 1,78 Go d'animations. Pour la plupart doublons de `Systems/Blueprints/AnimNotifies/`.
```
#### 4.2.2 Cover / prone / nage portés du CMC vers le personnage Mover (2026-09-18)
Ces trois fonctionnalités n'existaient que sur `SandboxCharacter_CMC` (via le `CharacterMovementComponent`). Elles ont été reportées sur `Core/Character/SandboxCharacter_Mover`, en passant par les mécanismes du plugin Mover et non par des écritures directes sur la capsule.
- **Composants ajoutĂ©s au Mover** : `Systems/Movement/Cover/AC_Cover` (le composant du CMC, gĂ©nĂ©rique : il travaille via `GetOwner`) et `Arrow1` (ArrowComponent, `(0,0,90)` / yaw 90). ⚠ `Arrow1` DOIT ĂȘtre attachĂ© au **SkeletalMesh** (comme au CMC) et non Ă  la capsule : le mesh porte un yaw −90, donc l'arrow attachĂ© au mesh se retrouve alignĂ© sur l'acteur (avant = avant de l'acteur, droite = droite de l'acteur), ce qu'attend le sphere trace de `MoveAlongWall` (il part de 55 uu sur le cĂŽtĂ© puis trace 60 uu vers l'avant, dans le mur). AttachĂ© Ă  la capsule, ses axes sont tournĂ©s de 90° : le trace part dans le vide, `MoveAlongWall` ne produit aucun `AddMovementInput` et on ne peut pas se dĂ©placer en couverture (bug corrigĂ© le 2026-09-18).
- **Variables ajoutĂ©es au Mover** — catĂ©gorie `Cover` : `In Cover`, `Cover` (`E_CoverD`), `Edge L`, `Edge R` ; catĂ©gorie `Prone` : `IsProne`, `ProneHalfHeight` (40), `CrouchHalfHeight` (55) ; catĂ©gorie `Swimming` : `CurrentWaterBodyComp`, `IsSwimming`, `IsUnderwater`, `SwimSprint`, `WaterSurfaceZ`, `WaterDepth`, `SwimVerticalInput`, `SwimStartDepth` (95), `SwimStopDepth` (80), `SwimmingModeClass`.
- **Prone** : `IA_Prone` → `ToggleProne` (bascule `IsProne` + `WantsToCrouch`/`WantsToStrafe` dans `PlayerInputState`), refusĂ© en nage ou en cover. La hauteur de capsule N'est PAS Ă©crite directement (contrairement au CMC qui fait `SetCapsuleSize`) : `Apply_CrouchOrProne`, appelĂ©e depuis `On_PreSimulateTick`, rĂšgle `StanceSettings.CrouchHalfHeight` (40 en prone, 55 accroupi) et force un cycle `UnCrouch`→`Crouch` quand la capsule courante ne correspond pas — c'est le `FStanceModifier` du plugin qui redimensionne la capsule, replace l'acteur et dĂ©cale le mesh. `IA_Crouch` en prone appelle `ExitProneToCrouch` (retour accroupi).
- **Nage** : les overlaps de la capsule (`OnComponentBegin/EndOverlap` → cast `WaterBody`) renseignent `CurrentWaterBodyComp` ; `Update_Swimming` (appelĂ©e depuis `On_PreSimulateTick`, voir l'encadrĂ© « tick » plus bas) lit `GetWaterSurfaceInfoAtLocation` (surface + profondeur), met Ă  jour `SwimSprint` (depuis `PlayerInputState.WantsToSprint`) et `SwimVerticalInput` (valeurs brutes de `IA_Jump` − `IA_Crouch`), puis `StartSwimming` (profondeur ≄ `SwimStartDepth` → `QueueNextMovementMode("Swimming")`) / `StopSwimming` (< `SwimStopDepth` → `Falling`). `IA_SwimmingDeep` bascule `IsUnderwater` (plongĂ©e). Le mode « Swimming » est enregistrĂ© **au BeginPlay** par `AddMovementModeFromClass` (variable `SwimmingModeClass`) et non dans la map `MovementModes` du composant : une instance créée par script dans le template du parent est privĂ©e et empĂȘchait la sauvegarde de `SandboxCharacter_Mover_Ragdoll` (« Illegal reference to private object »).
- **Cover** : l'Ă©vĂ©nement `OnEntreredCover` d'`AC_Cover` recopie `InCover`/`cover`/`EdgeL`/`EdgeR` sur le personnage puis `ApplyCoverCrouch` (accroupi **et `WantsToStrafe` = en couverture** tant qu'on y est) ; `IA_Move` appelle `AC_Cover.MoveAlongWall` quand `In Cover`. Comme le Mover ignore `AddMovementInput`, `Get_MoveInput` renvoie, en cover, `ConsumeMovementInputVector` (le vecteur accumulĂ© par le composant). `Update_Cover` (`On_PreSimulateTick`) met `CharacterMover.bAcceptExternalMovement` Ă  la valeur d'`AC_Cover.InCover`, sinon le `MoveComponentTo` de collage au mur serait annulĂ© par la simulation Mover. ⚠ Le mode **strafe est indispensable** en couverture : sans lui le Mover rĂ©oriente le personnage dans la direction du glissement dĂšs le premier pas, l'`Arrow1` tourne avec, le trace de `MoveAlongWall` rate le mur et le dĂ©placement se bloque aprĂšs quelques centimĂštres (bug corrigĂ© le 2026-09-18). L'entrĂ©e en couverture reste sur la touche **P** : depuis le 2026-09-18 le corps de cet `InputKey` est dĂ©portĂ© dans l'Ă©vĂ©nement `ToggleCover` d'`AC_Cover` (la touche P l'appelle), ce qui permet de dĂ©clencher la couverture par script/test ou de la remapper plus tard sur une Input Action.
- **Alimentation de l'animation** : `Get_PropertiesForAnimation` renvoie désormais `Stance` = Swinning / SwinningDeep / Prone / Crouch / Stand, et `Get_Gait` renvoie Prone en prone, Swinning/SwinningSprint en nage. `Get_MoveInput` utilise la rotation caméra complÚte (input 3D) en plongée comme en vol. Le chooser `Characters/UEFN_Mannequin/Animations/MotionMatchingData/CHT_PoseSearchDatabases_Mover` est une copie du `_Dense` du CMC dont le `ContextData` pointe sur `SandboxCharacter_Mover_ABP` ; ses deux colonnes booléennes (`Is Armed`, `IS Moving Object`) sont désactivées et la ligne « push » restreinte à `Stance = Stand`.
- **Les deux moteurs d'animation de l'ABP (Motion Matching vs state machine)** : l'`AnimGraph` (CMC comme Mover) contient un `Blend Poses by int` dont l'entrĂ©e **0 est le nƓud Motion Matching** et l'entrĂ©e **1 la state machine « State Controller » + Blend Stack** (la voie « expĂ©rimentale » du sample GASP). Voie 0 : `Update_MotionMatching` (fonction de nƓud) Ă©value un chooser qui renvoie une LISTE de `PoseSearchDatabase`, `SetDatabasesToSearch` les donne au nƓud, qui recherche chaque frame la meilleure pose d'aprĂšs la trajectoire (`Update_Trajectory`) — variables `ValidDatabases`, `CurrentSelectedDatabase/Anim`, `MM Search Cost`, rĂ©glages `Get_MMBlendTime`/`Get_MMInterruptMode`. Voie 1 : Ă  chaque entrĂ©e d'Ă©tat, `SetBlendStackAnimFromChooser` Ă©value `CHT_MoverCharacterAnimations_PoseMatch` (chooser **par clip**, une ligne = une animation) qui renvoie UNE anim + ses rĂ©glages de blend, poussĂ©e dans le `Blend Stack` — variables `StateName`, `BlendStackInputs`, `Search Cost`, `NoValidAnim`. Dans les deux ABP l'index valait la constante `0` (Motion Matching en permanence) ; sur le Mover il passe par la variable `AnimBranchIndex`, qui vaut elle aussi `0` en permanence depuis le 2026-09-18 (l'hybride state machine / Motion Matching a Ă©tĂ© abandonnĂ© Ă  la demande de l'utilisateur, voir §4.2.1). Les CVars GASP `DDCvar.UseExperimentalStateMachine` / `DDCvar.ExperimentalStateMachine` n'existent pas dans ce projet (avertissement « Failed to find console variable » en PIE) : elles ne commandent rien.
- **Correction de `MovementModeMap`** : « Ragdoll » pointait sur *Climbing* et « Flying » sur *SwimmingOnSurface* (dĂ©calage laissĂ© par la fusion des enums GASP → projet, §8.2). Remis sur `Ragdoll`/`Flying` + ajout de `Swimming` → `SwimmingOnSurface`. Effet de bord bĂ©nĂ©fique : le ragdoll du pion refonctionne (son Tick et `EnteredRagdollSuccessfullyCheck` comparent le mode courant Ă  `Ragdoll`).
- **⚠ Tick du personnage Mover (corrigĂ© le 2026-09-18)** : `SandboxCharacter_Mover` et `_Ragdoll` avaient `PrimaryActorTick.TickInterval = 30 s` (le CMC est Ă  0). Tout leur `Event Tick` — `CacheInputsFromMover`, `Update_FloorValues`, `Update_ControlRotationRate`, `Update_SlidingAudio`, `Update_TargetedActor`, `Update_TwinStickMode`, `Update_SmoothedAnalogInputAmount`, `DebugDraws` — ne tournait donc qu'une fois toutes les 30 secondes (c'est ce qui empĂȘchait la nage de se dĂ©clencher : `Update_Swimming` y Ă©tait branchĂ©e). Intervalle remis Ă  **0** sur les deux pions, et par sĂ©curitĂ© `Update_Swimming` + `Update_Cover` ont Ă©tĂ© dĂ©placĂ©es du `Event Tick` vers `On_PreSimulateTick` (dĂ©lĂ©guĂ© du `CharacterMover`, exĂ©cutĂ© Ă  chaque pas de simulation). Si un jour le tick de ces pions est de nouveau throttlĂ© (politique `BP_TickManager`), la nage et la couverture continueront de fonctionner, mais les `Update_*` du framework GASP, eux, en souffriront.
- **⚠ Bases Pose Search de nage vides → T-pose (contournĂ© le 2026-09-18)** : sur les 6 bases de nage, **une seule est rĂ©ellement remplie**, `Animations/humain/SwimmingAnimation/Swimming_animation/Breastok/surface/PSD_SwimmingMoving` (5 clips de brasse en surface). `PSD_SwimmingIdle`, `PSD_SwimmingMovingSprint`, `PSD_SwimmingDeepIdle`, `PSD_SwimmingDeepMoving`, `PSD_SwimmingDeepMovingSprint` ont des lignes dont l'`AnimAsset` est **None** (log : `OnGetComplete - Database None entry N has no associated animation asset`), et la table imbriquĂ©e `SwimmingInDeepIdle` du chooser n'a **aucun rĂ©sultat**. L'ABP Ă©tant dĂ©sormais 100 % Motion Matching, une base vide = aucune pose = **T-pose**, exactement le bug remontĂ©. Contournement appliquĂ© dans `CHT_PoseSearchDatabases_Mover` : les lignes qui pointent sur une base vide (12, 14, 15, 17) sont rendues inatteignables (`Stance = Prone` alors que `Movement Mode = Swimming`, combinaison impossible par construction) et les deux lignes qui pointent sur `PSD_SwimmingMoving` (13 pour Swinning, 16 pour SwinningDeep) passent `Movement State` et `Gait` en **MatchAny** pour couvrir tous les cas de nage. VĂ©rifiĂ© en PIE : nage de surface immobile, nage de surface en mouvement et nage profonde sĂ©lectionnent bien `RM_Breastroke_overwater_01_*`.
- **Vraie correction Ă  faire Ă  la main** (impossible par script : la propriĂ©tĂ© `DatabaseAnimationAssets` d'un `PoseSearchDatabase` et le `ResultsStructs` d'un `ChooserTable` ne sont exposĂ©s ni au Python d'UE ni Ă  `ObjectTools.set_properties` — seules `columnsStructs`/`contextData` le sont) : ouvrir chaque base vide et y glisser les clips qui existent dĂ©jĂ  sous `Animations/humain/SwimmingAnimation/` — `IDLE/IP_idle_underwater_01_Retargeted` + `IDLE/RM_Idle_turn_*` pour `PSD_SwimmingIdle`, `Swimming_animation/Crawl/RM_Crawl_*` pour `PSD_SwimmingMovingSprint`, `Swimming_animation/Breastok/RM_Breastroke_underwater_*` pour `PSD_SwimmingDeepIdle/Moving/MovingSprint` — puis remettre les lignes 12/14/15/17 du chooser sur leur `Stance` d'origine (3, 3, 4, 4) et donner un rĂ©sultat Ă  la table imbriquĂ©e `SwimmingInDeepIdle`.
- **Restes connus** : le cover du Mover joue les anims accroupies GASP, pas les bases de couverture du CMC (`PSD_Dense_Crouch_Idles_CoverLeft/Right`) ni le nƓud `Mirror` de l'AnimGraph du CMC ; en couverture le personnage conserve un Ă©cart d'orientation d'une dizaine de degrĂ©s par rapport au mur (orientation strafe qui converge lentement).
#### 4.2.3 SystÚme d'armes porté sur le personnage Mover (2026-09-19)
Ce qui marchait dĂ©jĂ  sur le Mover avant cette session (vĂ©rifiĂ© en PIE) : le ramassage (`IA_InteractAGIS`), le rangement au dos (socket `SpineRifle`), le passage en main par la roue V/B (`IA_QuickSelectAGIS` / `IA_QuickEquipAGIS` → socket `Rifle`), l'appel de `AC_Weapon::Equip`/`Unequip` via `HandleHeldStateChanged`, et le tir (`IA_UseItemPrimaryAGIS` → `Inventory_Player::UseHeldPrimarySERVER` → `BP_Item_AdvancedRifle::EventPrimaryUse`). `Inventory_Equipper` est gĂ©nĂ©rique : son `OwnerMesh` se rĂ©sout bien sur le composant `SkeletalMesh` du Mover.
Ce qui ne marchait PAS et a été corrigé :
- **`AC_Weapon` ne parlait qu'au CMC** : `Equip`/`Unequip` faisaient un `Cast To SandboxCharacter_CMC`, qui Ă©choue sur le Mover (`Pawn` vs `Character`, aucune classe commune). RĂ©sultat : arme bien en main, mais `Weapon` et `isArmed` jamais Ă©crits → locomotion dĂ©sarmĂ©e en permanence. Une branche `Cast Failed` → `Cast To SandboxCharacter_Mover` a Ă©tĂ© ajoutĂ©e dans les deux fonctions ; le CMC est inchangĂ©.
- **Nouvelle fonction `SandboxCharacter_Mover::SetEquippedWeapon(WeaponTypeByte)`** (appelĂ©e par `AC_Weapon`, `0` = dĂ©sĂ©quipement) : Ă©crit `Weapon_Equip` (nouvelle variable, enum `Weapon_Equip`), `isArmed` (= byte ≠ 0), `WantToshot` (remis Ă  false au dĂ©sĂ©quipement) et `Weapon`. ⚠ **Trois enums d'arme coexistent dans le projet** : `Weapon_Equip` (None/Bow/OneArmPistol/TwoArmPistol/Riffle/Bat — c'est l'espace de `AC_Weapon::WeaponTypeByte` et des colonnes du chooser de locomotion), `E_WaponType` (None/Riffle/Pistoll/Bow — c'est le type de `SandboxCharacter_Mover::Weapon` et du champ `S_CharacterPropertiesForAnimation.Weapon_Type`, lu par les choosers d'aim offset de `Systems/Aiming/`) et `E_WeaponType` (inutilisĂ© ici). `SetEquippedWeapon` fait donc la conversion 1(Bow)→3, 2/3(Pistol)→2, 4(Riffle)→1, reste→0.
- **IA_Aim renseigne `WantToshot`** (nouvelle variable du pion) : `WantToshot = ActionValue AND isArmed` sur `Triggered`, remis à `false` sur `Completed`. C'est ce bool que lit la colonne « Want Toshot » du sous-chooser `Stand Idles Bow` pour passer de l'idle arc à l'idle arc en visée.
- **`SandboxCharacter_Mover_ABP` reçoit l'Ă©tat d'arme** : nouvelles variables `Weapon_Equip`, `Weapon_Equip Last Frame`, `Want Toshot` (+ `Is Armed` et `IS Moving Object` dĂ©jĂ  prĂ©sentes), toutes alimentĂ©es en fin de `Update_PropertiesFromCharacter` (**thread principal**, comme le CMC le fait dans son `EventBlueprintUpdateAnimation` — surtout pas dans `Update_Logic`, qui peut tourner en thread-safe update).
- **`Is Armed` de l'ABP = `isArmed` ET `Weapon_Equip == Bow`** — *stopgap assumĂ©* : l'arc est la SEULE arme qui possĂšde des bases Motion Matching armĂ©es (`PSD_Dense_Stand_Bow_Idles`, `_Bow_Aim_Idles`, `PSD_Walk_Bow`). Sans cette condition, un fusil mettait `Is Armed = true`, les lignes 0/2 du chooser (`MatchFalse`) Ă©taient rejetĂ©es, les lignes arc 1/3 Ă©chouaient sur leur colonne `Weapon_Equip`, et on retombait sur la ligne 18 « Push » (ou sur rien du tout). Avec la condition, fusil/pistolet jouent proprement la locomotion dĂ©sarmĂ©e, arme en main. À retirer le jour oĂč des bases fusil existeront.
- **`Update_MotionMatching`** : le chooser `CHT_PoseSearchDatabases_Mover` n'Ă©tait consultĂ© que si `Stance` ∉ {Stand, Crouch} (prone/nage). La condition devient `(Stance ≠ Stand ET ≠ Crouch) OU Is Armed`, sinon les lignes arc — qui sont des lignes *debout* — n'Ă©taient jamais atteintes. La sortie `False` du Branch est en plus reliĂ©e Ă  `SetDatabasesToSearch` pour que les deux chemins poussent bien leur liste dans le nƓud MM.
- **`Get_MMInterruptMode`** : ajout du terme `Weapon_Equip ≠ Weapon_Equip Last Frame` sur le `OR` racine. Sans lui, changer d'arme ne forcçait aucune interruption : Motion Matching gardait la pose continue (anim dĂ©sarmĂ©e) et ne basculait jamais sur la base arc, mĂȘme une fois celle-ci dans la liste (coĂ»t de continuitĂ© toujours plus bas). C'est le mĂȘme piĂšge que celui dĂ©crit en §1.3 pour le CMC.
- **`CHT_PoseSearchDatabases_Mover`** : colonne `Is Armed` **rĂ©activĂ©e** (elle avait Ă©tĂ© dĂ©sactivĂ©e le 2026-09-18 faute de propriĂ©tĂ© correspondante sur l'ABP Mover), colonne `IS Moving Object` rĂ©activĂ©e aussi (sinon la ligne 18 « Push » devient un attrape-tout qui remporte tous les cas debout non couverts), et **ligne 1 (« Stand Idles Bow ») : colonne `Gait` passĂ©e de `MatchEqual Walk` Ă  `MatchAny`**. C'Ă©tait le dernier blocage : l'allure par dĂ©faut du personnage est `Run`, donc la ligne arc ne pouvait jamais matcher Ă  l'arrĂȘt. (Le mĂȘme dĂ©faut existe dans `CHT_PoseSearchDatabases_Dense` cĂŽtĂ© CMC, non corrigĂ© ici.)
VĂ©rifiĂ© en PIE, dans cet ordre : ramassage du fusil → dos ; V+B → main + `Weapon = Riffle`, `Weapon_Equip = Riffle`, `isArmed = true` cĂŽtĂ© pion ET cĂŽtĂ© ABP ; locomotion dĂ©sarmĂ©e propre avec le fusil en main (pas de T-pose) ; arc → `PSD_Dense_Stand_Bow_Idles` → `StandingIdle01_UE_Anim3` ; arc + clic droit → `Want Toshot = true` → `PSD_Dense_Stand_Bow_Aim_Idles` ; arc en course → repli propre sur `PSD_Dense_Stand_Run_Loops` ; clic gauche → `EventPrimaryUse` du fusil (qui refuse de tirer faute de chargeur Ă©quipĂ© — comportement normal, non liĂ© au Mover) ; non-rĂ©gression prone (`PSD_Dense_Prone_Idles`) et nage (`PSD_SwimmingMoving`).
Restant Ă  faire :
- Aucune base Motion Matching armée pour le fusil / le pistolet (seul l'arc en a) ; `PSD_Crouch_Bow` existe mais n'est référencée par aucune ligne de chooser.
- `SandboxCharacter_Mover` n'a **pas** de composant `AC_Stats` (le CMC en a un) : `BFL_WeaponDamage::ApplyPointDamage` résout `AC_Stats` sur la cible, donc le pion Mover ne peut pas encaisser de dégùts. Il lui manque aussi les capsules de hitbox et le `PerceptionStimuliSource` du CMC.
- La visée n'a pas d'équivalent de `SandboxCharacter_CMC::ISaiming` sur le Mover (seuls `PlayerInputState.WantsToAim` et `WantToshot` existent).
#### 4.2.4 MetaHuman en « visual override » du personnage Mover (2026-09-19)
Approche retenue (celle du sample GASP, cf. la vidĂ©o de rĂ©fĂ©rence) : **le mannequin UEFN reste le mesh du pion et porte toute l'animation ; le MetaHuman est un acteur enfant qui le recopie par retarget IK.** Le mesh du pion est simplement rendu invisible. Avantage : rien ne change cĂŽtĂ© Motion Matching, sockets, prone/nage, ragdoll — et on peut changer de « peau » Ă  chaud.
Le pipeline existait déjà dans le projet mais était **incomplet** : `SandboxCharacter_Mover` a son `ChildActorComponent` `VisualOverride` (tagué `VisualOverride`) et son `AC_VisualOverrideManager`, mais tous les acteurs de skin du sample (`BP_Manny`, `BP_Quinn`, `BP_Echo`, `BP_Twinblast`, `BP_UE4_Mannequin`) **et l'AnimBP qui les pilote** avaient été supprimés au nettoyage de la démo GASP.
Assets créés / restaurés, tous dans `Content/Systems/Mover/VisualOverrides/` :
- **`ABP_GenericRetarget`** — rĂ©cupĂ©rĂ© du commit initial (`538ebd2a`). C'est un **Animation Blueprint Template** (`bIsTemplate`, donc sans squelette cible : utilisable sur n'importe quel mesh). Son AnimGraph tient un seul nƓud `Retarget Pose From Mesh` ; son `EventBlueprintUpdateAnimation` choisit l'`IKRetargeter` dans la map `IKRetargeter_Map` **en utilisant le premier Component Tag du composant qui l'hĂ©berge** comme clĂ©. La map garde ses entrĂ©es d'origine (toutes Ă  `None`, les retargeters du sample ayant Ă©tĂ© supprimĂ©s) plus la nĂŽtre.
- **`IK_MetaHuman_test`** — IK Rig sur `/Game/test/Body/SKM_test_BodyMesh`, gĂ©nĂ©rĂ© par `IKRigController.apply_auto_generated_retarget_definition()` (l'auto-caractĂ©risation reconnaĂźt le squelette MetaHuman : bassin = `pelvis`, 29 chaĂźnes standard).
- **`RTG_UEFN_to_MetaHuman_test`** — source `IK_UEFN_Mannequin`, cible `IK_MetaHuman_test`. `auto_map_chains(EXACT)` mappe tout sauf deux chaĂźnes, l'auto-caractĂ©risation MetaHuman nommant `LeftFoot`/`RightFoot` ce que le mannequin appelle `LeftToe`/`RightToe` — mappĂ©es Ă  la main.
⚠ **`add_default_ops()` ne crĂ©e PAS l'op des chaĂźnes IK.** La pile par dĂ©faut est Pelvis Motion / FK Chains / Run IK Rig / Root Motion / Remap Curves ; or `ABP_GenericRetarget::UpdateRetargetProfile` va chercher l'op **nommĂ©e « Retarget IK Goals »** (`GetOpControllerFromRetargetProfile` → cast vers `IKRetarget_IKChains_Controller`) pour moduler l'alpha d'IK par chaĂźne. Sans elle, le cast renvoie None et `SetSettings` spamme un *Blueprint Runtime Error* Ă  chaque frame. Correctif : `add_retarget_op("/Script/IKRig.IKRetargetIKChainsOp")` — le **chemin de classe complet est obligatoire**, le nom court est refusĂ© (retourne -1). L'op arrive dĂ©jĂ  nommĂ©e « Retarget IK Goals » et se place juste avant « Run IK Rig » ; il faut ensuite refaire `assign_ik_rig_to_all_ops` + `auto_map_chains` (les mappings de chaĂźnes sont **par op**). RĂ©sultat : 4 chaĂźnes IK (LeftLeg, LeftArm, RightLeg, RightArm), plus aucune erreur en PIE.
- **`BP_VisualOverride_MetaHuman`** — Actor : `Body` (racine, `SKM_test_BodyMesh`, `AnimClass = ABP_GenericRetarget_C`, **Component Tag `RTG_UEFN_to_MetaHuman_test`**) → `Face` (`SKM_test_FaceMesh`, `ABP_Face_C`) ; plus un `MetaHumanComponentUE` (`BodyComponentName = "Body"`, `FaceComponentName = "Face"`, post-process `ABP_Clothing_PostProcess`). Pas de grooms : ce MetaHuman n'en a aucun d'assignĂ©.
Cùblage de la sélection :
- `GM_Sandbox` (`Systems/Mover/`) reçoit `BP_VisualOverride_MetaHuman` dans `VisualOverrides_Soft`.
- **`GM_Dev` est reparentĂ© sur `GM_Sandbox`.** C'Ă©tait un `GameModeBase` totalement vide (aucune variable, aucun nƓud) qui ne servait qu'Ă  fixer `DefaultPawnClass` ; or `AC_VisualOverrideManager::FindAndApplyVisualOverride` fait un `Cast To GM_Sandbox` — sans ce reparentage, la DevMap n'appliquait **jamais** d'override (pire : le cast ratĂ© le remettait activement Ă  zĂ©ro). `DefaultPawnClass`, `PawnClasses_Soft` et `VisualOverrides_Soft` sont renseignĂ©s sur `GM_Dev`.
- La sélection se fait par le CVar **`DDCvar.VisualOverride`** (index dans la liste, `-1` = pas d'override). Son défaut est passé de `-1` à **`0`** dans `Config/DefaultEngine.ini` pour que le MetaHuman soit affiché d'entrée. Repasser à `-1` (ou taper `DDCvar.VisualOverride -1` en console) rend le mannequin.
VĂ©rifiĂ© en PIE : le mannequin passe invisible, `BP_VisualOverride_MetaHuman` est spawnĂ© dans le `ChildActorComponent`, le MetaHuman (corps + tĂȘte + matĂ©riaux) suit l'animation Ă  l'arrĂȘt comme en course, le Motion Matching continue de tourner normalement sur le mannequin cachĂ© (`PSD_Relaxed_Stand_Idles` → `PSD_Relaxed_Stand_Run_F_Loops`).
⚠ **Limite structurelle de cette approche** : les objets Ă©quipĂ©s s'accrochent aux sockets du **mesh pilote cachĂ©**, pas du MetaHuman (`Inventory_Equipper` attache sur `OwnerMesh`, qui est le composant `SkeletalMesh` du pion). MesurĂ© en PIE : ~**12 cm** d'Ă©cart entre `hand_r` du mannequin et `hand_r` du MetaHuman — une arme en main flottera d'autant. Correctif Ă  prĂ©voir : pointer `OwnerMesh` sur le composant `Body` de l'override quand un override est actif, ou n'utiliser l'override que pour des skins aux proportions du mannequin.
Autres points :
- Le ragdoll n'a pas été testé avec l'override actif (il simule sur le mesh caché ; le MetaHuman devrait suivre par retarget).
- Les 13 sockets recopiées sur `SKM_test_BodyMesh` lors de la tentative précédente ont été retirées : elles ne servent plus, les attaches restant sur le mannequin.
- Pour ajouter une autre peau : créer `IK_X` + `RTG_UEFN_to_X`, ajouter l'entrée dans `ABP_GenericRetarget.IKRetargeter_Map`, dupliquer `BP_VisualOverride_MetaHuman` en changeant meshes et Component Tag, et l'ajouter à `VisualOverrides_Soft`.
### 4.3 Autres systÚmes de gameplay (ajouts spécifiques au projet)
```text
Content/Systems/
├── Stats/
│ └── AC_Stats.uasset → ActorComponent ACTIF : gĂšre la vie du personnage (Health), la mort (bIsDead, dispatcher OnDeath), Heal, Revive (voir §1.3). AjoutĂ© cette session pour le systĂšme de mort/respawn. (2026-09-05) Extension additive pour la faune : nouveau dispatcher `OnDamaged(BodyPart, DMGValue)`, diffusĂ© au point de convergence partagĂ© de `TakeDamage` (aprĂšs le switch par bodypart, avant le calcul de mort) — permet Ă  un consommateur (ex. BP_AnimalBase) de rĂ©agir Ă  CHAQUE coup reçu, pas seulement Ă  la mort, sans dupliquer la logique de dĂ©gĂąts.
│
├── Weapons/ (2026-09-05) → Architecture d'armes gĂ©nĂ©rique, rĂ©utilisable par toute arme (arc, arme Ă  feu, futur mĂȘlĂ©e). Voir §1.3 pour le mĂ©canisme d'Ă©quipement.
│ ├── AC_Weapon.uasset → ActorComponent commun Ă  TOUTE arme (ajoutĂ© Ă  BP_Item_Bow et BP_Item_AdvancedRifle). Ne contient AUCUNE logique spĂ©cifique Ă  un type d'arme. Variables : bIsEquipped, bIsBusy, CooldownDuration, LastUseTime, OwnerCharacter. Dispatchers : OnEquipped/OnUnequipped/OnPrimaryUse(Stopped)/OnSecondaryUse(Stopped). Fonctions : CanUse(), Equip/Unequip, TryUsePrimary/StopPrimary, TryUseSecondary/StopSecondary. Son EventGraph s'auto-abonne Ă  `OnOwnerChanged` de son propre acteur (pattern `Default|AssignOnOwnerChanged`) pour appeler Equip/Unequip automatiquement — zĂ©ro couplage avec Inventory_Player/Equipper.
│ ├── BFL_WeaponDamage.uasset → Blueprint Function Library. `ApplyPointDamage(Target, Instigator, HitBoneName, Damage)` : rĂ©sout le composant `AC_Stats` de la cible (`GetComponentByClass` + cast, no-op silencieux si absent) et route vers `AC_Stats::TakeDamage`, en mappant le nom d'os touchĂ© (bones du squelette UEFN_Mannequin : head/neck_*→Head, thigh|calf|foot|ball_L/R→LeftLeg/RightLeg, clavicle|upperarm|lowerarm|hand_L/R→LeftArm/RightArm, tout le reste dont spine_*/pelvis/root→Torso par dĂ©faut). RĂ©utilisable par toute arme/projectile/piĂšge futur.
│ ├── BP_Projectile_Base.uasset → Actor projectile gĂ©nĂ©rique : StaticMeshComponent racine `ArrowMesh` (mesh + collision, PAS de mesh par dĂ©faut — Ă  fournir par les enfants) + `ProjectileMovement`. `OnComponentHit` : ignore les hits sur son propre `Owner` (Ă©vite l'auto-hit au tir), appelle `BFL_WeaponDamage::ApplyPointDamage` puis `HandleImpact(Hit)` — fonction overridable (dĂ©faut : `DestroyActor`). Variable `BaseDamage` (instance-editable).
│ └── BP_Projectile_Arrow.uasset → HĂ©rite de BP_Projectile_Base. Override du mesh `ArrowMesh` (mesh Fab `cc0_wooden_arrow`, collision `BlockAllDynamic`) via override de composant hĂ©ritĂ© (PAS le template du parent — voir piĂšge §9). Override `HandleImpact` : au lieu de se dĂ©truire, se fige (dĂ©sactive la simulation + la collision) et s'attache (KeepWorld) au composant touchĂ© pour rester plantĂ© visuellement dans le dĂ©cor/la cible.
│
├── Wildlife/ (2026-09-05) → Premiers animaux sauvages (cerf mĂąle + biche). Architecture gĂ©nĂ©rique : toute la logique commune vit dans `BP_AnimalBase` + un unique StateTree partagĂ© ; une espĂšce (Content/Systems/Wildlife/BP_Deer_Stag, BP_Deer_Doe) ne fait qu'assigner mesh/anims/tunables — ajouter un sanglier ou un loup plus tard consiste Ă  crĂ©er UN NOUVEAU Character enfant de `BP_AnimalBase`, sans toucher au StateTree ni aux tĂąches. RĂ©utilise tel quel `AC_Stats` (vie/dĂ©gĂąts/mort) et `BP_ScentPoint` (odeurs) — voir §1.3. NĂ©cessite le plugin **GameplayStateTree** (ajoutĂ© Ă  `ProjectFv1.uproject` cette session, `StateTreeAIComponentSchema`/`StateTreeComponentSchema` indisponibles sans lui — seul le plugin `StateTree` de base Ă©tait actif auparavant) ; premier usage de StateTree dans ce projet (aucune Behavior Tree/Blackboard/AIController n'existait avant).
│ ├── E_AnimalState.uasset → Enum (UserDefinedEnum, créé via `unreal.EnumStructService`) : Idle/Wander/Graze/Rest/Alert/Flee/Dead. Sert d'Ă©tiquette de debug + de sĂ©lecteur d'anim d'arrĂȘt (voir `BP_AnimalBase::ApplyIdleForState`) ; le StateTree stocke en interne l'Ă©tat courant sous forme de `byte` (les outils Blueprint disponibles ne permettent pas d'ajouter une variable membre ou un paramĂštre de fonction/dispatcher directement typĂ© sur un enum utilisateur — limitation dĂ©jĂ  documentĂ©e §9 pour AC_Stats, confirmĂ©e Ă  nouveau ici).
│ ├── AC_AnimalSenses.uasset → ActorComponent gĂ©nĂ©rique (vue + odorat), ajoutĂ© Ă  `BP_AnimalBase`. Vue : ne fait AUCUNE dĂ©tection elle-mĂȘme — se contente de variables (`bCanSeePlayer`, `DistanceToPlayer`, `LastKnownPlayerLocation`) mises Ă  jour par `BP_AnimalAIController` via `ReportPlayerSighted`/`ReportPlayerLostSight`. Odorat : self-contained, `CheckScent` tourne sur un timer (`ScentCheckInterval`, 0.5s par dĂ©faut) dĂ©marrĂ© au `BeginPlay`, interroge `GetAllActorsOfClass(BP_ScentPoint)` et retient le plus proche (`bScentDetected`/`ScentSourceLocation` si distance ≀ `ScentDetectionRadius`, 800 par dĂ©faut). `DistanceToPlayer` est recalculĂ©e Ă  CHAQUE tick de `CheckScent` (pas seulement quand le joueur est vu) — nĂ©cessaire pour que le StateTree puisse dĂ©tecter le retour au calme (`DistanceToPlayer >= SafeDistance`) mĂȘme aprĂšs que l'animal ait fui hors de vue (bug rencontrĂ© et corrigĂ© en test PIE, voir §9). (2026-09-14) `CheckScent` réécrit : distance calculĂ©e par point d'odeur dans la boucle (l'ancienne version comparait une valeur hors boucle), pawn joueur relu Ă  chaque passe via `GetPlayerPawn(0)` (le cache du `BeginPlay` devenait invalide aprĂšs mort/respawn → fuite infinie), `LastKnownPlayerLocation` rafraĂźchie tant que le joueur est vu (la perception ne notifie qu'au gain/perte de vue), et `bCanSeePlayer=false` si aucun pawn.
│ ├── BP_AnimalAIController.uasset → AIController, `AIPerceptionComponent` natif configurĂ© en Python (`AISenseConfig_Sight`, rayon 2500/3000, FOV 90°). `OnTargetPerceptionUpdated` relaie vers `AC_AnimalSenses` du pion possĂ©dĂ© (`ReportPlayerSighted`/`ReportPlayerLostSight` selon `Stimulus.WasSuccessfullySensed()`) — ne fonctionne QUE parce que le joueur porte un `AIPerceptionStimuliSourceComponent` (voir §1.3). (2026-09-14) Filtre ajoutĂ© : l'Ă©vĂ©nement ne relaie QUE si `Actor == GetPlayerPawn(0)` — sans lui, les autres animaux (pawns) perçus faisaient passer `bCanSeePlayer` Ă  vrai/faux et dĂ©clenchaient alertes/fuites entre cerfs. (2026-09-14) Le template `AIPerception` avait perdu son `AISenseConfig_Sight` (`SensesConfig = [None]` → aucune dĂ©tection) : recréé par Python (`unreal.new_object(AISenseConfig_Sight, outer=<template AIPerception_GEN_VARIABLE>)` via `SubobjectDataSubsystem`), rayon 2500/3000, angle pĂ©riphĂ©rique 90°, MaxAge 5, toutes affiliations. Le pawn joueur actuel (`SandboxCharacter_Mover`, pawn par dĂ©faut de `GM_Dev`) n'a pas de `AIPerceptionStimuliSourceComponent` mais est dĂ©tectĂ© via `AISense_Sight.bAutoRegisterAllPawnsAsSources` (vrai par dĂ©faut) ; en revanche il n'Ă©met PAS d'odeurs (`ScentFunct` n'existe que sur `SandboxCharacter_CMC`).
│ ├── BP_AnimalBase.uasset → Character gĂ©nĂ©rique, base de TOUTE la faune. Composants : `Stats` (AC_Stats), `AnimalSenses` (AC_AnimalSenses), `StateTree` (StateTreeComponent, schema `StateTreeComponentSchema`, contexte = l'acteur lui-mĂȘme — PAS `StateTreeAIComponentSchema`, pour rester dĂ©tachĂ© de l'AIController et garder le StateTree simple). `AIControllerClass` = `BP_AnimalAIController`, `AutoPossessAI` = Placed in World or Spawned. Tunables instance-editable : WalkSpeed/RunSpeed, WanderRadius, IdleTime/GrazeTime/RestTime(Min/Max), AlertTime, FleeDistance (400-600 : distance de panique immĂ©diate si vu de prĂšs), SafeDistance (doit ĂȘtre largement > FleeDistance : distance Ă  atteindre pour se recalmer), HerdID (Name — animaux partageant le mĂȘme HerdID se relaient une alerte), DebugEnabled, Anim_Idle/Wander/Graze/Rest/Alert/Flee/Dead/Hit + Anim_RestEnter/Anim_RestExit (AnimSequence, un jeu Ă  fournir par espĂšce ; RestEnter/Exit optionnelles). Mouvement (2026-09-14) : `bOrientRotationToMovement=true`, `bUseControllerRotationYaw=false`, RotationRate 220, accĂ©lĂ©ration sur les chemins (`NavMovementProperties.bUseAccelerationForPaths`) ; mesh tournĂ© de Yaw −90 (les squelettes DeerStag/DeerDoe regardent +Y). Fonctions clĂ©s : `MoveTo`/`StopMovementAnimal` (wrappers autour de l'AIController cachĂ©, utilisĂ©s par les tĂąches StateTree), `SetCurrentState` (met Ă  jour l'Ă©tat, `bIsFleeing` (=Flee), `bInDeadState` (=Dead) et consomme `bHerdAlertReceived` Ă  l'entrĂ©e d'Alert/Flee — ne joue plus d'anim), animation (2026-09-14) : toujours **Single Node** (`SkeletalMeshComponent::PlayAnimation`, pas d'AnimBlueprint) mais pilotĂ©e par la VITESSE Ă  chaque tick via `DebugTick→UpdateAnimation` : course (`Anim_Flee`, play rate = vitesse/RunSpeed) au-delĂ  de (Walk+Run)/2, marche (`Anim_Wander`, play rate = vitesse/WalkSpeed) au-delĂ  de ~30, sinon `UpdateIdleAnimation` (Rest avec transitions RestEnter/RestExit verrouillĂ©es par `AnimLockUntil`, sinon `ApplyIdleForState` : Graze/Alert/Idle). `ApplyAnim(Anim, Loop, Rate)` ne relance l'anim QUE si elle change (`CurrentAnim`) — l'ancienne version relançait l'anim Ă  chaque changement d'Ă©tat. `PlayStateAnimation` n'est plus appelĂ©e (conservĂ©e, inutilisĂ©e), `GetThreatLocation` (position du joueur si vu, sinon source d'odeur, sinon position du pawn joueur, sinon position de l'animal), `BroadcastAlertToHerd`/`ReceiveHerdAlert` (alerte les autres membres du mĂȘme HerdID sans jamais lire une variable d'une AUTRE instance directement — le HerdID appelant est passĂ© en paramĂštre de fonction, voir piĂšge §9), `HandleDeath` (2026-09-14 : plus de ragdoll — `DisableMovement`, arrĂȘt du polling de vie, capsule en `NoCollision`, `Anim_Dead` jouĂ© une fois et figĂ© sur sa derniĂšre frame ; liĂ© Ă  `AC_Stats::OnDeath` via un Custom Event `OnAnimalDeathEvt` — PAS un binding direct sur une fonction classique, voir piĂšge §9), `CheckHealthChanged` (polling toutes les 0.2s de `AC_Stats::GetHealth` : une baisse de vie hors mort → `HandleDamaged` : `bWasHitRecently=true`, remis Ă  false par le timer `ClearHitFlag` 6s aprĂšs le dernier coup (auparavant JAMAIS remis Ă  false → fuite relancĂ©e Ă  chaque tick), anim `Anim_Hit` verrouillĂ©e 0.4s ; prĂ©fĂ©rĂ© Ă  un binding direct sur le dispatcher `AC_Stats::OnDamaged`, qui provoque une erreur interne du compilateur Blueprint dans ce projet, voir piĂšge §9).
│ ├── ST_AnimalBase.uasset → StateTree partagĂ© par toute la faune (schema `StateTreeComponentSchema`, contexte = `BP_AnimalBase`). États : `Normal` (groupe) {Idle→Wander→Graze→Rest→Idle, cycle via transitions `OnStateCompleted`/`NextState`}, `Alert` (hĂ©site `AlertTime` secondes puis retourne Ă  Idle si la menace n'est pas confirmĂ©e — deny-timeout), `Flee` (fuite), `Dead` (terminal). Transitions clĂ©s, TOUTES en conditions liĂ©es par binding direct au contexte (`bind_transition_condition_property_to_context`, propriĂ©tĂ©s imbriquĂ©es type `AnimalSenses.bCanSeePlayer` — inclure le prĂ©fixe `b` des boolĂ©ens, voir piĂšge §9), aucune n'exige de Blueprint condition custom : `Normal→Alert` (vue OU odorat OU alerte de horde, combinĂ©es en OR via `set_transition_condition_operand`), `Normal→Flee` et `Alert→Flee` (prioritĂ© High : vu ET distance ≀ FleeDistance — "trop prĂšs, panique immĂ©diate"), `Flee→Normal/Idle` (distance ≄ SafeDistance ET plus vu ET `bWasHitRecently` faux — fuite minimale de 6s aprĂšs un coup, mĂȘme tirĂ© de loin), transitions globales posĂ©es sur `Root` (s'appliquent depuis N'IMPORTE quel Ă©tat actif) : `→Flee` (prioritĂ© High, `bWasHitRecently` ET pas mort ET `bIsFleeing` faux — sans cette derniĂšre condition, la transition rĂ©-entrait dans Flee Ă  chaque tick) et `→Dead` (prioritĂ© Critical, `bIsDead` ET `bInDeadState` faux — mĂȘme raison que `bIsFleeing` : sans elle, Dead Ă©tait rĂ©-entrĂ© Ă  chaque tick).
│ └── Tasks/ → StateTree Tasks en Blueprint (parent natif `StateTreeTaskBlueprintBase`), chacune rĂ©utilisĂ©e par PLUSIEURS Ă©tats via ses propriĂ©tĂ©s instance-editable liĂ©es au contexte (`bind_task_property_to_context`) :
│ ├── STT_SetAnimalState.uasset → Appelle `BP_AnimalBase::SetCurrentState(NewState)` et ne se termine JAMAIS (2026-09-14 : l'ancien `FinishTask` immĂ©diat, avec `TasksCompletion=Any`, terminait chaque Ă©tat dans la frame mĂȘme de son entrĂ©e → Idle→Wander→Graze→Rest bouclaient Ă  chaque frame, animaux immobiles et anims relancĂ©es en permanence ; `set_task_considered_for_completion(false)` n'a PAS suffi, voir §9). C'est la tĂąche Wait/Move de l'Ă©tat qui le termine. Un exemplaire par Ă©tat-feuille, seul `NewState` diffĂšre.
│ ├── STT_Wait.uasset → Attend une durĂ©e alĂ©atoire entre `MinTime`/`MaxTime` (liĂ©s au contexte : IdleTimeMin/Max, GrazeTimeMin/Max, RestTimeMin/Max, ou AlertTime/AlertTime pour une durĂ©e fixe). NĂ©cessite `should_call_tick_only_on_events = true` sur la classe (sinon `ReceiveLatentTick` n'est jamais appelĂ© — propriĂ©tĂ© peu intuitive, voir piĂšge §9).
│ ├── STT_MoveToRandomPoint.uasset → UtilisĂ© par `Wander`. Tire un point via `NavigationSystem::GetRandomReachablePointInRadius` autour de `HomeLocation` (capturĂ©e une fois au spawn), appelle `BP_AnimalBase::MoveTo`, termine en arrivant (≀120 en XY), si l'animal est bloquĂ© (vitesse < 5 aprĂšs 1.5s) ou aprĂšs 25s ; `ExitState` arrĂȘte le mouvement (une alerte interrompt proprement la marche).
│ └── STT_MoveAwayFromThreat.uasset → UtilisĂ© par `Flee`. À l'entrĂ©e : diffuse l'alerte Ă  la horde puis `IssueFleeMove` : point Ă  1500 dans la direction opposĂ©e Ă  `GetThreatLocation` (XY), projetĂ© sur le NavMesh (extent 300/300/1000), sinon point atteignable alĂ©atoire dans 1500 autour de l'animal. RĂ©-Ă©mis tant que l'Ă©tat Flee est actif dĂšs que l'animal arrive (≀200) ou est bloquĂ© (vitesse < 20 aprĂšs 1s) — c'est la transition du StateTree qui termine la fuite. `ExitState` arrĂȘte le mouvement.
│
│ EspĂšces (enfants de BP_AnimalBase, aucun nouvel asset graphique créé — rĂ©utilisent les meshes/anims
│ dĂ©jĂ  prĂ©sents sous Content/Characters/Animal/DeerStagAndDoe/ et Content/Animations/DeerAndDoe/) :
│ ├── BP_Deer_Stag.uasset → SK_DeerStag + jeu d'anims ANIM_DeerStag_* (RestEnter/Exit = GoToRest/RestToGoBackUp), HerdID "DeerHerd_A".
│ └── BP_Deer_Doe.uasset → SK_DeerDoe + jeu d'anims ANIM_DeerDoe_* (`Anim_Rest`=IdleRest, manquante avant le 2026-09-14), HerdID "DeerHerd_A" (mĂȘme horde que le cerf → rĂ©activitĂ© croisĂ©e via BroadcastAlertToHerd/ReceiveHerdAlert).
│
│ Une instance de chaque a Ă©tĂ© placĂ©e dans `Content/Maps/DevMap/MapDev1` avec un `NavMeshBoundsVolume`
│ (`NavMeshBounds_Wildlife`, RuntimeGeneration = Dynamic — le mode par dĂ©faut `Static` ne (re)gĂ©nĂšre
│ JAMAIS le NavMesh automatiquement, voir piùge §9). Étend le NavMesh existant du niveau (aucun n'existait
│ avant cette session).
│
├── Door/
│ ├── Garage_Door/BP_Garage_door.uasset → Porte de garage interactable
│ └── Simply_Door/BP_Simply_Door.uasset → Porte simple interactable
│
├── Movement/
│ ├── Climbing/AC_ClimbingComponent.uasset, CR_ClimbingGraphV2.uasset (Control Rig), CR_Mannequin_BasicFootIK.uasset, ClimbingMaterial.uasset
│ └── Cover/AC_Cover.uasset → ActorComponent : systĂšme de couverture (cover system). GĂ©nĂ©rique (tout passe par `GetOwner`) : portĂ© par `SandboxCharacter_CMC` ET, depuis le 2026-09-18, par `SandboxCharacter_Mover` (voir §4.2.2). EntrĂ©e/sortie de couverture sur la touche **P** gĂ©rĂ©e dans le composant lui-mĂȘme ; `MoveAlongWall` fait un sphere trace le long du mur, `AddMovementInput` sur le pion et diffuse `OnEntreredCover` (InCover, direction `E_CoverD`, EdgeL/EdgeR).
│
├── Scent/BP_ScentPoint.uasset → Point Ă©metteur d'odeur, spawnĂ© par `SandboxCharacter_CMC::ScentFunct` (toutes les 2s pendant que le joueur se dĂ©place) et consommĂ© par `AC_AnimalSenses::CheckScent` (Systems/Wildlife/, §4.4). (2026-09-05) Deux bugs corrigĂ©s Ă  la rĂ©utilisation par la faune : (a) `ScentFunct` ne crĂ©ait le point QUE via `SpawnActorFromClass`, sans jamais appeler son Ă©vĂ©nement `Custom|Create(life)` — la durĂ©e de vie (`TimeToLife`) restait donc Ă  0 ; ajout d'un appel explicite `Create(life=20.0)` juste aprĂšs le spawn. (b) Le dĂ©compte de durĂ©e de vie (`Custom|CustomEvent(DeltaTime)`) existait dĂ©jĂ  mais n'Ă©tait appelĂ© par PERSONNE (`BeginPlay` enregistrait l'acteur auprĂšs de `BP_TickManager` mais rien n'invoquait rĂ©ellement l'event) — le point ne s'auto-dĂ©truisait donc jamais ; ajout d'un `EventTick` natif qui appelle directement ce Custom Event (Ă©vite de toucher au brancher `BP_TickManager` partagĂ©, hors-scope). Comparaison de fin de vie durcie de `== 0` Ă  `<= 0` (float exact-equality fragile). Aucune nouvelle logique d'odeur créée — uniquement rĂ©paration du mĂ©canisme existant.
├── Movable_Object/BP_Moveble_Object.uasset → Objet physique dĂ©plaçable par le joueur
├── Interact_Interface.uasset → Interface d'interaction gĂ©nĂ©rique (racine de Systems/, Ă  distinguer de BPI_Interactable dans Inventory/Interfaces/)
│
└── Weather/
├── Tornado/BP_Tornado.uasset, NS_Tornado.uasset → Tornade dynamique (acteur + systĂšme Niagara), spĂ©cifique au projet (`NS_Tornado.uasset`, supprimĂ© du disque avant le 2026-09-13 sans commit, restaurĂ© depuis git le 2026-09-14 Ă  la demande de l'utilisateur)
└── UltraDynamicSky/ → đŸ§© **Asset marketplace tiers complet** (~789 fichiers : Blueprints, matĂ©riaux, sons, textures de ciel/mĂ©tĂ©o dynamique). Ne pas modifier directement ; utiliser ses points d'extension documentĂ©s par le plugin (Weather_Presets, Climate_Presets, Sky_Modifiers). Structure interne : Blueprints/ (Enum, Functions, Occlusion, Sky_Modifiers, Sound, System, Tools, Utilities, Weather_Effects, Widgets), Materials/, Meshes/, Particles/, Sound/, Textures/, Maps/ (map de dĂ©mo du plugin). Les systĂšmes Niagara `Particles/` (Snow, Dust, Wind_Debris, Radial_Storm, Puddle_FluidGrid, DLWE_Interaction_System, VolumetricCloud_LightRays, Actor_Exposure_Test, Standalone/Dripping_*) rĂ©fĂ©rençaient encore l'ancien chemin `/Game/UltraDynamicSky/Particles/UDW_Niagara_Parameters` / `UDS_VolumetricClouds_Niagara_Properties` : `[CoreRedirects]` ajoutĂ© dans `Config/DefaultEngine.ini` + re-sauvegarde des 10 systĂšmes (2026-09-14).
⚠ **Exception — code mĂ©tĂ©o PROPRE AU PROJET rangĂ© dans ce dossier tiers** (Ă  ne pas confondre avec le plugin) :
├── Blueprints/WB_WeatherControl.uasset → Acteur contrĂŽleur mĂ©tĂ©o (1 par map, porte UDS/UDW en ChildActor). Tick via `BP_TickManager` : fait avancer la saison (`DT_Season`, durĂ©e en minutes via `SeasonDurationScale`=60), puis si le joueur est dans une zone (`ActiveZone`) pousse la mĂ©tĂ©o de cette zone vers UDW, sinon `GetNearZone` choisit les 2 zones les plus proches (poids inversement proportionnels Ă  la distance) et `setWeatherbetweenZone` mĂ©lange leurs Ă©tats dans UDW.
└── Blueprints/WB_WeatherZone.uasset → Zone mĂ©tĂ©o (Box). `WeatherData` (ST_Weather_ZoneData : temp/humiditĂ©/vent/sable) = climat de base. `MakeForcast` gĂ©nĂšre 16 prĂ©visions (`ST_Weather_Zone_forecasts`, pas de 60–240 s via `ForecastStepMin/Max`) avec `makestate` (nuages `SetNuage`, tempĂ©rature `MakeTemp` tirĂ©e vers la moyenne de saison, neige/pluie/orage/vent/brouillard/poussiĂšre) ; `StepForecast(Delta)` interpole l'Ă©tat courant et rĂ©gĂ©nĂšre en fin de sĂ©quence ; `ApplyToUDW` pousse l'Ă©tat dans UDW ; `GetInflueZone` liste les zones voisines dans `InfluenceRadius` (15000 uu) non masquĂ©es (lissage entre zones via `GetZoneState`). Overlap filtrĂ© sur le pawn contrĂŽlĂ© par un joueur. Fonction `Trie zone` restĂ©e dans le BP mais plus appelĂ©e (2026-09-14).
```
---
## 5. `Content/Data/` — Structures de donnĂ©es globales
```text
Content/Data/
├── DataTable/DT_Season.uasset → DataTable des saisons (mĂ©tĂ©o)
│
├── Enums/
│ ├── E_CoverD.uasset, E_WeaponType.uasset
│ ├── E_WaponType.uasset → (2026-09-14, venu de `Characters/UEFN_Mannequin/Animations/Props/`) type d'arme lu par les choosers d'aim offset de `Systems/Aiming/` et par `S_CharacterPropertiesForAnimation.Weapon_Type` ; volontairement NON fusionnĂ© avec `E_WeaponType`
│ ├── Enums/E_BodyPart.uasset
│ └── Movement_enum/Weapon_Equip.uasset
│ (⚠ Ă  ne pas confondre avec Data/Inventory_Data/Enumerations/ ci-dessous — deux emplacements d'enums coexistent)
│
├── Structure/Weather/ → Structs mĂ©tĂ©o (ST_Season, ST_WeatherZoneState, ST_Weather_Zone_CurentWeather/forecasts/_Data, ST_Zone_Influance)
│
└── Inventory_Data/ → DonnĂ©es du systĂšme d'inventaire AGIS
├── DataAssets/
│ ├── AttachmentData/ → AttachmentData (base) + AttachmentData_AdvancedRifle, AttachmentData_Flashlight : configuration des attachements d'armes
│ ├── ContainerGroupData/ → Un DataAsset par "groupe de containers" (Backpack BIG/SMALL, Box/Chest Storage, Vest_1/2, ExampleStorage
) : dĂ©finit la mise en page des slots d'un contenant
│ └── InventoryData/ → Un DataAsset par inventaire prĂ©dĂ©fini (PlayerInventory, PlayerInventoryPart1/2, CardReader_Inventory, Generator_Inventory, Vehicle_Inventory). `PlayerInventory` (2026-09-16) = inventaire du JOUEUR : widget `WB_InventoryPlayer`, containers = les 12 de PlayerInventoryPart1 puis les 3 de PlayerInventoryPart2 (mĂȘme ordre que `WB_InventoryPlayer::GetAllContainerWidgets`) ; le container 12 (poches) est en 4×3 / 12 slots depuis le 2026-09-17 pour correspondre Ă  `PocketContainer` (il Ă©tait en 2×2 → seule la 1re ligne Ă©tait utilisable ; `PlayerInventoryPart2`, utilisĂ© par le pillage, a toujours ce dĂ©faut). `PlayerInventoryPart1/2` restent utilisĂ©s par les PNJ/cadavres fouillables (`BP_Deadman`, `BP_EnemyPuppet` → `WB_LootingHuman`)
├── Enumerations/ → Enums spĂ©cifiques AGIS : EAnimOverlayState, EBallDirections, EFourDirections/ETwoDirections, EInteractionType, EItemActions, ENestingRules, EPrintType, EReloadType, EServerVerificationType, ESlotAppearance, EVendorBuyOption
└── Structures/ → Structs spĂ©cifiques AGIS
├── ItemDefaults.uasset → Struct CENTRALE dĂ©crivant un item (Name, etc.) — lue par WB_Item et par la logique de sĂ©lection rapide (Inventory_Player)
├── ItemInfo, SlotInfo, SlotAddress, SlotAppearance, SubItemInfo, RandomSubItem
├── ContainerInfo, ContainerAppearance
├── AttachmentInfo, EquipSlot_KeySettings/_OwnContainersSettings/_TextureSettings/_TitleSettings
├── ClientWidgetData, CombinedTexturesInfo, TextureInfo
├── CraftingSlotRowInfo, DragSounds, DebugPrint, FindResult, InspectCameraInfo, InteractCosmetics, ItemActorInfo, MenuActions
└── SaveStructures/SaveData_Player, SaveData_Storages, Savedata_ItemActor → Structures de sauvegarde (non encore branchĂ©es Ă  un vrai systĂšme de save, voir §1.3 du systĂšme de mort)
```
---
## 6. `Content/UI/` — Interfaces utilisateur
```text
Content/UI/
├── Inventory_UI/ → TOUTE l'UI de l'inventaire (socle AGIS + ajouts projet)
│ ├── MaterialResources/ → MatĂ©riaux/Material Instances de fond des slots (MI_Background_*, M_RuntimeItemMaterial
)
│ └── Widgets/
│ ├── WB_Container.uasset → Widget reprĂ©sentant un container (grille de slots) — contient SlotWidgets[]. `CreateSlots` construit la grille d'aprĂšs la `Container Size` DU WIDGET (les SlotInfo du DataAsset doivent avoir la mĂȘme taille, sinon seules les premiĂšres cases sont utilisables). `GetParentEquipSlot` (corrigĂ© le 2026-09-17) ne renvoie l'EquipSlot englobant (Outer.Outer) que si ce container EST son `Container` principal : les sous-containers d'un sac/gilet affichĂ©s ailleurs (sous les poches) gardent leur taille et leurs cases de 75 px au lieu d'ĂȘtre rĂ©duits Ă  1×1 en taille d'EquipSlot
│ ├── WB_EquipSlot.uasset → Widget d'un slot d'Ă©quipement (arme/speed) — contient Container. Var `OwnContainersHost` (2026-09-04, typĂ©e `WB_InventoryPlayer` depuis 2026-09-16) : si dĂ©finie, `OnContainerUpdate` ajoute les sous-containers de l'item Ă©quipĂ© (ex. grille du sac Ă  dos) dans ce widget via `AddContainerBelowPockets` au lieu de son propre `ContainersCanvas` interne. CĂąblĂ©e uniquement pour `EquipSlotBACKPACK` par `WB_InventoryScreen::InitializeWidgets`.
│ ├── WB_InventorySlot.uasset → Widget d'un slot individuel dans une grille — expose "ItemWidget On Slot"
│ ├── WB_Item.uasset → Widget reprĂ©sentant un item posĂ© dans un slot — expose "Item Defaults" (struct ItemDefaults)
│ │
│ ├── InventoryMenu/
│ │ ├── WB_InventoryScreen.uasset → Widget racine de l'inventaire plein Ă©cran. Depuis 2026-09-16 contient un seul panneau joueur `Part2Box → OutlinePart2 → BackgroundPart2 → InventoryPlayer` (WB_InventoryPlayer : Ă©quipements Ă  gauche, poches + sac Ă  dos dĂ©filants Ă  droite ; plus de ScrollBox extĂ©rieur depuis 2026-09-17, sinon la colonne de droite n'a pas de hauteur bornĂ©e) ; `InitializeWidgets` initialise `InventoryPlayer` et cĂąble `EquipSlotBACKPACK.OwnContainersHost = InventoryPlayer`. `Inventory_Player::GetQuickSlotItemInfo` lit les slots WEAPON/WEAPON2/Speed1/Speed2 via `InventoryScreen.InventoryPlayer`
│ │ ├── WB_InventoryPlayer.uasset → (2026-09-16) Fusion de WB_InventoryPart1 + WB_InventoryPart2 pour l'inventaire du joueur, dans un seul cadre en deux colonnes (2026-09-17) : `EquipsVertical` → `ColumnsBox` (HorizontalBox, Fill) → [`EquipSlotsBox` (SizeBox 760×866) → `SlotsCanvas` avec les 12 EquipSlots] + [`PocketsScroll` (ScrollBox, barre visible) → `VerticalBox_0` (titre Pockets + `PocketContainer`, puis les containers du sac Ă  dos ajoutĂ©s dessous par `Add Container Below Pockets`)]. `EquipSlotBACKPACK` et `EquipSlotVEST` ont `Item Containers Settings.ShowInventory = True` et `OwnContainersHost = InventoryPlayer` (cĂąblĂ© dans `WB_InventoryScreen::InitializeWidgets`) (dĂ©faut de WB_EquipSlot = False : sans ça, `OnContainerUpdate` ne crĂ©e jamais les grilles du sac). `GetAllEquipSlots` = mĂȘme ordre que Part1 ; `GetAllContainerWidgets` = containers des Ă©quipements puis PocketContainer. DonnĂ©es : DataAsset `PlayerInventory`
│ │ ├── WB_InventoryPart1.uasset → (utilisĂ© seulement par WB_LootingHuman depuis 2026-09-16) Contient les 4 slots d'Ă©quipement (EquipSlotWEAPON, EquipSlotWEAPON2, EquipSlotSpeed1, EquipSlotSpeed2) + autres slots d'Ă©quipement (casque, gilet, gants, sac Ă  dos
)
│ │ └── WB_InventoryPart2.uasset → (utilisĂ© seulement par WB_LootingHuman depuis 2026-09-16 ; sa fonction `Add Container Below Pockets` n'est plus appelĂ©e, WB_EquipSlot pointe dĂ©sormais WB_InventoryPlayer) Grille(s) d'inventaire principale(s) (PocketContainer + titre, dans `VerticalBox_0`). Fonction `Add Container Below Pockets(Container)` (2026-09-04) : insĂšre un WB_Container juste aprĂšs le bloc Pockets (`PocketContainer.GetParent().GetParent()`) — utilisĂ©e par `WB_EquipSlot` (sac Ă  dos) pour afficher ses slots supplĂ©mentaires sous les slots Pocket plutĂŽt que dans son propre canvas.
│ │
│ ├── ItemActionsMenu/WB_ItemMenu.uasset, WB_GeneralButton.uasset → Menu contextuel d'actions sur un item
│ │
│ ├── LootWidgets/ → FenĂȘtres de pillage, une par type de source (Human, Storage, Vendor, Vehicle, Generator, KeycardReader) hĂ©ritant de _WB_LootWidgetBASE ; LootingCrafting/ pour les stations de craft
│ │
│ ├── MovableWindows/ → SystĂšme de fenĂȘtres dĂ©plaçables (_WB_Window_Base) : ItemInspect/ (inspection 3D d'un item), ItemInventory/ (fenĂȘtre d'inventaire flottante), WB_TitleBar_Window (barre de titre commune)
│ │
│ └── Other/
│ ├── WB_QuickSelect.uasset → 🆕 Roue de sĂ©lection rapide (4 slots : Arme1/Arme2/Speed1/Speed2 + "Aucun"). RĂ©sout seule son Inventory_Player propriĂ©taire au Construct (GetOwningPlayerPawn → GetComponentByClass). Ouverte/fermĂ©e depuis Inventory_Player (touche V maintenue). Le graphe `INPUTS` d'Inventory_Player bascule aussi Input Mode/curseur/Ignore Look Input Ă  l'ouverture/fermeture (mĂȘme pattern que `ToggleInventory`) — fix du 2026-09-04. **Cause rĂ©elle du bug (2026-09-04, deuxiĂšme fix)** : IA_QuickSelectAGIS/IA_QuickEquipAGIS (touches V/B) n'Ă©taient mappĂ©es QUE dans `IMC_AGIS_Character` (Systems/Inventory/Other/Inputs/), un Input Mapping Context jamais ajoutĂ© au joueur nulle part dans le projet (seule une variable `Input Mapping Context` orpheline sur Inventory_Player y faisait rĂ©fĂ©rence, sans AddMappingContext associĂ©) — donc l'input V n'atteignait mĂȘme pas l'Enhanced Input, avant tout problĂšme de widget. Fix : V et B ajoutĂ©s directement dans `IMC_Sandbox` (Core/PlayerController/Input/), l'unique IMC rĂ©ellement ajoutĂ© au joueur (via SandboxCharacter_CMC/SetupInput). `IMC_AGIS_Character` reste inutilisĂ©e — Ă  nettoyer/fusionner un jour si confirmĂ© obsolĂšte.
│ ├── WB_QuickGive.uasset → 🆕 (2026-09-05) Menu debug "Quick Give" (touche F9, mappĂ©e dans `IMC_Sandbox` via `IA_QuickGiveAGIS`). Champ Item ID + Amount + bouton "Give" gĂ©nĂ©rique, plus des boutons raccourcis "Give Bow"/"Give 20 Arrows". Fonction `GiveItem(ItemID, Amount)` : rĂ©sout l'`Inventory_Player` du joueur possĂ©dant (`GetOwningPlayerPawn`+`GetComponentByClass`), construit un item via `FL_AGIS::Create Item Structure`, trouve un slot libre via `Inventory__Main::Find Space For Item`, puis le pose via `Inventory__Main::Set Item On Container` — mĂȘme chaĂźne AGIS bas niveau que celle utilisĂ©e en interne par `Reload Graph` de `BP_Item_AdvancedRifle`. Toggle (crĂ©er/`AddToViewport`+InputMode GameAndUI vs `RemoveFromParent`+InputMode GameOnly) cĂąblĂ© dans `SandboxCharacter_CMC::EventGraph` sur `IA_QuickGiveAGIS` (Started), variable `QuickGiveWidget` sur le Character. Outil de test, pas une fonctionnalitĂ© shippable.
│ ├── WB_YouDied.uasset → Écran "Vous ĂȘtes mort" (systĂšme de mort/respawn)
│ ├── WB_ActionScreen, WB_InteractScreen, WB_InteractBall, WB_PickupDisplay → Feedback d'interaction/pickup au sol
│ ├── WB_CombatHUD, WB_DisplayHUD, WB_WeaponAmmoInfo → HUD de combat/munitions
│ ├── WB_InventoryBackground, WB_ItemInfo, WB_SlotTitle → ÉlĂ©ments visuels d'appoint
│ └── WidgetTextures/ → IcĂŽnes des slots d'Ă©quipement (T_EquipSlot_*) et textures d'UI diverses
│
├── TextureUI/M_CharacterUI.uasset, RT_CharacterView.uasset → Rendu de la vue personnage dans l'UI (ex. Ă©cran d'inspection du personnage)
│
└── Widgets/ → UI HORS inventaire
├── HUD/WB_HUD.uasset, WB_New_HUD.uasset, WB_New_InterfacePlayer.uasset
├── PauseMenu/WBP_PauseMenu.uasset, WBP_Options.uasset
└── interaction/WB_Interact.uasset
```
---
## 7. `Content/Items/`, `Content/PCG/`, `Content/Maps/`, `Content/VFX/`, `Content/Prop/`
```text
Content/Items/ → Objets ramassables/Ă©quipables du jeu
├── BaseBlueprints/
│ ├── _BP_ItemBase.uasset → Blueprint BASE de tout item (racine de hiĂ©rarchie). Event `Custom|UpdateCollisionandPhysics` (appelĂ© Ă  l'attache/au drop) bascule aussi la rĂ©ponse au canal `Visibility` (Ignore quand Ă©quipĂ© / Block quand lĂąchĂ©) du mesh principal ET de tous les `WidgetComponent` de l'item (ex. `AmmoUI`) — fix du 2026-09-04 : un item Ă©quipĂ© bloquait les BoxTrace (canal Visibility) du systĂšme de grimpe/parkour (`AC_ClimbingComponent`), empĂȘchant de grimper une fois une arme en main.
│ ├── BP_Item_HoldableBase.uasset → Base des items tenables en main. ImplĂ©mente `BPI_HoldableItem::On Item Attached(ShouldHold)` (seul signal fiable du VRAI Ă©tat main/pas-en-main) et diffuse ce boolĂ©en via son propre dispatcher `OnHeldStateChanged(bIsHeld)` (2026-09) — c'est CE dispatcher (et non `_BP_ItemBase::OnOwnerChanged`, qui se dĂ©clenche aussi pour un simple rangement en inventaire) qu'`AC_Weapon` Ă©coute pour piloter Equip/Unequip, voir §4.3.
│ ├── BP_Item_Armor_Base.uasset → Base des items d'armure/Ă©quipement portĂ©
│ └── BP_Item_Skeletal_Base.uasset → Base des items avec mesh squelettal
│
├── BP_Item_AdvancedRifle / _Backpack_Camper / _Backpack_Sport / _BodyArmor / _Flashlight /
│ _Gloves / _Helmet / _KevlarVest / _LightVest / _Siren / _Bow.uasset → Blueprints d'items "portĂ©s" (racine d'Items/). `BP_Item_Bow` (hĂ©rite `BP_Item_HoldableBase`) porte un composant `ItemMesh_STATIC` (mesh Fab `survival_kit_bow_fixed` — voir §8 pour l'incident de corruption/rĂ©-import du 2026-09-05 —, socket `ArrowSocket` ajoutĂ© pour l'encoche — position approximative, Ă  ajuster visuellement), Ă©quipĂ© sur le socket `weapon_l` (Spine `SpineRifle`, rĂ©utilisĂ© faute de socket dĂ©diĂ©). `BP_Item_AdvancedRifle` a reçu le mĂȘme composant `AC_Weapon` (2026-09-05, retrofit de validation) pour l'Ă©quipement/dĂ©sĂ©quipement gĂ©nĂ©rique ; sa cadence de tir reste pilotĂ©e par sa propre macro interne `Weapon|FiringGate` (non branchĂ©e sur `AC_Weapon.CanUse()` — dette technique documentĂ©e, voir §9).
│
│ **Gameplay de l'arc** (`BP_Item_Bow`, 2026-09-05/06) — override des hooks existants `Custom|PrimaryUse`/`StopPrimaryUsing` de `_BP_ItemBase`, sans nouveau systĂšme d'input : `PrimaryUse` vĂ©rifie `AC_Weapon->CanUse()` ET `CountArrows() >= 1` (fonction propre, voir §1.3 pour `Owner Inventory`) avant de dĂ©marrer la tension (horodatage `DrawStartTime`, pas de Timeline), puis joue `Draw Montage` (`AM_Bow_Draw`, voir ci-dessous) sur le mesh du personnage. `StopPrimaryUsing` calcule la puissance (`Clamp((Now-DrawStartTime)/MaxDrawTime, MinDrawPower, 1.0)`), joue `Fire Montage` (`AM_Bow_Fire`), appelle `ConsumeArrow()` et, si rĂ©ussi, spawn `BP_Projectile_Arrow` au socket `ArrowSocket` avec vitesse/dĂ©gĂąts scalĂ©s par la puissance, puis appelle `AC_Weapon->TryUsePrimary()`.
│ **`ConsumeArrow()` — fix du 2026-09-06** : retrouve le stack "Arrow" via `BOTH|FindItem`, puis rĂ©duit son montant via `Inventory__Main::AddItemAmount(ItemAddress, -1)` — une opĂ©ration purement sur les DONNÉES de l'inventaire, par adresse, ne nĂ©cessitant aucun acteur monde. L'implĂ©mentation d'origine appelait `Utilities|GetItemActor` (macro `ML_InventoryMacros::Get Item Actor`) puis `SERVER|ReduceAmount` sur cet acteur — ce qui Ă©choue TOUJOURS silencieusement pour un item empilĂ© passif comme "Arrow" qui, tant qu'il n'est pas Ă©quipĂ©/lĂąchĂ© dans le monde, n'a jamais d'acteur spawnĂ© (`GetItemActor` retourne systĂ©matiquement invalide). SymptĂŽme observĂ© : le tir semblait dĂ©marrer (logs "TIR EN COURS") mais aucune flĂšche ne partait et aucune munition n'Ă©tait consommĂ©e. Voir aussi §9 pour la mĂȘme leçon appliquĂ©e Ă  toute consommation de stock passif (ne jamais utiliser `GetItemActor`+fonction-sur-acteur pour un item qui n'est pas garanti d'ĂȘtre spawnĂ© dans le monde — utiliser `Inventory__Main::AddItemAmount` par adresse Ă  la place).
│ **Animations de l'arc** (2026-09-06) — `AM_Bow_Draw`/`AM_Bow_Fire.uasset` (`Content/Items/`) sont des `AnimMontage` Ă  piste unique gĂ©nĂ©rĂ©s depuis `standingdrawarrow_UE`/`standingaimrecoil_UE` (`Content/Animations/humain/Armed_mov/Bow/`), assignĂ©s au **slot `UpperBody`** (voir `SandboxCharacter_CMC_ABP` ci-dessous — PAS `DefaultSlot`). Variables `Draw Montage`/`Fire Montage` sur `BP_Item_Bow`, jouĂ©es via un nƓud natif `Animation|Montage|PlayMontage` sur le mesh du personnage (`Cast Owner Character → Character → GetMesh`). Appel direct (pas de variante Multicast `...CLIENTS` comme pour `PlayReloadMontageCLIENTS` du fusil) — suffisant en PIE solo ; Ă  revoir si une rĂ©plication multijoueur de l'anim est nĂ©cessaire plus tard.
│ **IntĂ©gration Ă  la locomotion (2026-09-06)** — un montage plein-corps sur `DefaultSlot` remplaçait TOUT le squelette (jambes comprises) par la pose figĂ©e de l'animation source, ce qui semblait correct Ă  l'arrĂȘt (la pose "debout" du montage coĂŻncide avec Motion Matching Ă  l'arrĂȘt) mais cassait la marche/course/crouch (jambes figĂ©es dans la pose d'arc "debout" enregistrĂ©e). Fix : ajout d'un **`Slot 'UpperBody'`** + nƓud **`Layered Blend Per Bone`** dans l'`AnimGraph` de `SandboxCharacter_CMC_ABP`, avec un filtre de branche sur l'os `spine_01` (BlendDepth=-1 → toute la branche colonne+bras+tĂȘte, jambes/bassin exclus) — voir §9 pour le dĂ©tail technique (le skeleton `SK_UEFN_Mannequin` avait dĂ©jĂ  un Blend Profile "UpperBody" masquant exactement les mĂȘmes os, confirmant que `spine_01` est le bon point de coupure, mais ce Blend Profile n'est pas directement utilisable par ce nƓud en 5.8 — filtre de branche manuel utilisĂ© Ă  la place). Le `DefaultSlot` existant (plein corps) n'est pas touchĂ©, rĂ©utilisable tel quel pour de futures actions plein-corps. Motion Matching, Pose Search, Aim Offset (`Enable_AO`/`Get_AOValue`), Foot/Leg IK et Orientation Warping — tous dĂ©jĂ  prĂ©sents dans cet AnimBP — continuent de piloter le bas du corps et la visĂ©e sans changement.
├── Attachments/BP_Item_MagazineBase, BP_Item_RifleFlashlight, BP_Item_Suppressor.uasset → Attachements d'armes
├── ItemObjects/BPI_MergeableItemObject, ItemObject__BASE, ItemObject_ConsumableBASE, ItemObject_Magazine.uasset → Objets "monde" associĂ©s aux items (empilables, consommables, munitions)
│
├── Ammo/ (Arrow), Weapon/ (Base_Bow, Base_knife), Tool/ (saucepan), Water/ (water_bottle),
│ Food/ (Red_Apple, meat, cooked_Meat, Burnt_Meat), Resource/ (Stick, Stone)
│ → Chaque sous-dossier contient les assets (mesh/matĂ©riau/texture/Thumbnail) d'un item ; les Blueprints correspondants sont soit Ă  la racine d'Items/, soit dans Resources/. `Weapon/Base_Bow/Thumbnail` et `Ammo/Arrow/Thumbnail` ne contenaient que des icĂŽnes prĂ©parĂ©es Ă  l'avance (2026-09-04) — item "Bow" et "Arrow" ajoutĂ©s dans DT_Items pour les utiliser (voir §5).
│
├── Resources/ → Assets 3D (mesh/matĂ©riau/texture) groupĂ©s par Ă©quipement portĂ©, + cas particuliers
│ ├── Backpack_Camper/, Backpack_Sport/, BodyArmor/, Gloves/, Helmet/, KevlarVest/, LightVest/, Rifle/
│ ├── _Others/ → Assets des petits objets (Keycard, Log, Meat, AmmoBox, Battery, Flashlight, FuelCanister, GoldIngot/GoldOre, Headphone, MoneyBag, WaterBottle)
│ └── ABP_SkeletalEquippables.uasset → Anim Blueprint partagĂ© par les items squelettaux Ă©quipĂ©s
│
└── ItemTextures/ → Textures d'icînes/inventaire communes à tous les items (T_*)
Content/PCG/ → GĂ©nĂ©ration procĂ©durale de contenu (plugin PCG)
├── Road/
│ ├── PCG_Road.uasset → Graphe PCG gĂ©nĂ©rant la route procĂ©durale (matĂ©riaux vieillis/mousse/humiditĂ©, potholes via GeometryScript)
│ ├── BP_AbandonedRoad.uasset → Acteur portant le graphe PCG
│ ├── M_Road.uasset, ST_Road_data.uasset, EM_Road_Type.uasset → MatĂ©riau, struct de donnĂ©es et enum de types de route
├── Vegetation/TreeTest.uasset → Graphe PCG de placement de vĂ©gĂ©tation (arbres, gĂ©nĂ©rĂ©s via PVE pour la route)
└── test/NewPCGGraph1.uasset → Graphe de test/brouillon
Content/Maps/DevMap/
├── MapDev1.umap → đŸ—ș Map de dĂ©veloppement PRINCIPALE (GameDefaultMap / EditorStartupMap, voir Config/DefaultEngine.ini)
├── GM_Dev.uasset → GameMode utilisĂ© sur MapDev1 (hĂ©rite directement de GameModeBase, indĂ©pendant de GM_Sandbox/Gamemode_AGIS)
├── pcg_Test_Dev.umap → Map de test PCG
└── PCG_Test_Dev_HLODLayer_*, Untitled_HLOD0_Instancing.uasset → Assets HLOD gĂ©nĂ©rĂ©s pour les maps de test
Content/VFX/
├── Firetrail/BP_FireTrail.uasset, Effects/P_BulletTrail.uasset, Materials/M_BulletTrail.uasset → TraĂźnĂ©e de balle
└── MI_MuzzleFlash, M_MuzzleFlash, MuzzleFlash, P_MuzzleFlash → Effet de tir (bouche de canon)
├── M_Blood.uasset, T_Blood.uasset → Effet de sang
└── crosshair.uasset, hitmarker.uasset → ÉlĂ©ments de HUD de tir
Content/Prop/Wooden_Door/ → Prop de porte en bois (mesh + matĂ©riaux), utilisĂ© par les BP de porte interactable
```
---
## 8. Assets tiers / bibliothĂšques (peu ou pas de logique de gameplay)
Ces dossiers contiennent des assets achetĂ©s/importĂ©s (Marketplace, Fab/Quixel Megascans, Megaplant, packs d'animation mocap). Ils sont volumineux (des centaines de fichiers) et **ne sont volontairement pas dĂ©taillĂ©s fichier par fichier** ici — voir §10 pour la rĂšgle sur quand les documenter.
```text
Content/Animations/ → Clips de mocap tiers : DeerAndDoe/, Fox/, Wolf/, humain/ (Armed_mov, Avoid, Carrying, Cover,
Effect_Animation, INteraction, Prone, Push, SwimmingAnimation
)
Content/Characters/ → Meshes/matĂ©riaux/textures des personnages et animaux (Animal/DeerStagAndDoe|Fox|Wolf,
UEFN_Mannequin/ avec ses données de Motion Matching : AimOffset, Crouch, Idle, Jump,
MotionMatchingData/ [Channels, Databases, Normalization_Sets, Schemas], Poses, Run, Slide,
Sprint, Traversal, Walk, Rigs, Materials, Meshes, Textures)
Content/Fab/ → Assets Fab/Quixel Megascans : MaterialFunctions/, MaterialParameterCollection/, Materials/
(Standard/VT), Megascans/Plants/ (Common_Hemp_Nettle, Wild_Grass ×2), Megascans/Surfaces/
(Damaged_Asphalt, Dirt_Ground, Forest_Floor), Textures/ (Standard/VT)
Survival_Kit_-_Bow/ et CC0_-_Wooden_Arrow/ (StaticMesh + Textures + MI_SurvivalBow/MI_WoodenArrow,
utilisĂ©s par les items Bow/Arrow, voir §4.4) — ⚠ ces deux imports glTF avaient leurs slots de
matériau pointant vers un `MaterialInstanceDynamic` transitoire (jamais sauvegardable, cause
d'échecs d'auto-save « Illegal reference to private object »). Corrigé le 2026-09-04 en créant de
vraies `MaterialInstanceConstant` (parent `/InterchangeAssets/gltf/Substrate/M_GLTF`) avec les
mĂȘmes textures. Si un futur import Fab/glTF Ă©choue Ă  l'auto-save de la mĂȘme façon, vĂ©rifier le(s)
slot(s) de matĂ©riau du StaticMesh importĂ© pour ce mĂȘme symptĂŽme.
⚠ **2026-09-05** : aprĂšs une coupure de courant, `survival_kit_bow.uasset` (StaticMesh) s'est retrouvĂ©
avec 0 vertex/0 triangle/bounds nuls (asset vidé, probablement une sauvegarde tronquée en cours
d'Ă©criture) — l'arc n'avait plus de mesh visible ni de collision en jeu. Diagnostic : toujours
vérifier `mesh.get_num_vertices(0)` / `get_bounds()` aprÚs une fermeture anormale de l'éditeur sur
un asset suspect avant de chercher ailleurs. Fix : ré-import depuis la source Fab d'origine
(chemin retrouvé via `mesh.get_editor_property("asset_import_data").extract_filenames()`, cache
`%LOCALAPPDATA%/Temp/FabLibrary/<guid>/`) vers un dossier scratch, puis renommage vers
`.../StaticMeshes/survival_kit_bow_fixed` (le nom d'origine `survival_kit_bow` n'a pas pu ĂȘtre
réutilisé : `delete_asset`/`consolidate_assets` échouaient silencieusement sur l'asset corrompu
malgrĂ© 0 rĂ©fĂ©renceur restant — laissĂ© orphelin en l'Ă©tat, sans danger). Toutes les rĂ©fĂ©rences
(`BP_Item_Bow::ItemMesh_STATIC`, `DT_Items` ligne "Bow" → `Spawner Preview Mesh`) repointĂ©es vers
`survival_kit_bow_fixed`. Le socket `ArrowSocket` a dĂ» ĂȘtre recréé (perdu avec l'ancien asset).
Content/Megaplant_Library/ → VĂ©gĂ©tation Megaplant : Plant_Bilberry/, Tree_European_Aspen/, Tree_Hornbeam/
(chacun avec Instances/, Materials/, Textures/)
Content/Audio/Foley/ → **(2026-09-08)** Systùme de sons de pas/foley du sample Game Animation Sample :
`AC_FoleyEvents` (ActorComponent, ajouté à `SandboxCharacter_Mover`, voir §8.2),
`S_FoleyEventParams`, `DefaultFoleyEventAudioBank`. Reste du dossier `Content/Audio/` :
sons/bancs audio divers du sample, non branchés sur le gameplay AGIS.
Content/MetaHumans/ et Content/Characters/Echo|Paragon|UE4_Mannequin|UE5_Mannequins/ → **SUPPRIMÉS le 2026-09-13**
(nettoyage) : assets MetaHuman et personnages de démonstration du sample Game Animation
Sample (~2,6 Go), utilisés uniquement par les BP de démo `RetargetedCharacters/BP_*`
(supprimés aussi). Copie de sauvegarde : `E:\projet\ProjectFv1_backup_migration_2026-09-13\nettoyage_etape1_demo\`.
`UEFN_Mannequin` reste le seul squelette de personnage humanoĂŻde du projet.
Content/Levels/, Content/Widgets/, Content/dev_map → **SUPPRIMÉS le 2026-09-14** : dĂ©mo du sample Game Animation Sample (kit de blockout
LevelPrototyping, widgets et map de démo). Seul lien avec le projet : le cast vers
`LevelBlock_Traversable` dans `Systems/Mover/AC_TraversalLogic`, retiré (voir §4.2.1).
Sauvegarde : commit git `6eb7516`.
Content/Thumbnails/ → Miniatures gĂ©nĂ©rĂ©es pour la Content Browser
Content/Misc/ → SandboxAnimCurveCompressionSettings.uasset (rĂ©glages de compression d'anim)
Content/__ExternalActors__/ → ⚙ GĂ©nĂ©rĂ© automatiquement par World Partition (donnĂ©es par-acteur des maps, ex. LocomotionAnimPack).
Content/__ExternalObjects__/ → ⚙ GĂ©nĂ©rĂ© automatiquement par World Partition (objets externes liĂ©s aux maps).
→ Ces deux derniers dossiers ne doivent JAMAIS ĂȘtre Ă©ditĂ©s ou documentĂ©s Ă  la main :
leur contenu suit automatiquement les maps et les acteurs qu'elles contiennent.
```
### 8.1 Import du sample Epic "Game Animation Sample" (2026-09-07/08)
L'utilisateur a importĂ© le contenu brut du sample officiel Epic **Game Animation Sample** directement dans `Content/Blueprints/` (structure de dossier d'origine du sample), dans le but de disposer d'une rĂ©fĂ©rence complĂšte/fonctionnelle du framework "Sandbox Character" (voir §4.2) pendant que sa migration vers le plugin `Mover` Ă©tait en cours. Ce sample est la source originelle du framework `Systems/Blueprints/` dĂ©jĂ  prĂ©sent dans ce projet : l'import a donc créé **95 collisions de nom exactes** (mĂȘmes noms : `SandboxCharacter_CMC`, `AC_TraversalLogic`, `BP_MovementMode_Walking`, `E_Gait`, `IMC_Sandbox`, tous les `IA_*` gĂ©nĂ©riques, etc.) entre le contenu fraĂźchement importĂ© et les versions dĂ©jĂ  personnalisĂ©es de ce projet.
**Politique appliquĂ©e (dĂ©cision utilisateur explicite)** : garder la version du projet partout, ne conserver du nouvel import que le contenu rĂ©ellement nouveau (RetargetedCharacters, SmartObjects/AI supplĂ©mentaires, Ragdoll, Audio/Foley, MetaHumans, Characters de dĂ©mo — voir §4.2/§8). Les 95+17 doublons (17 supplĂ©mentaires dĂ©couverts aprĂšs coup sous `Content/Input/` et `Content/Data/`, dont `IMC_Sandbox` lui-mĂȘme) ont Ă©tĂ© fusionnĂ©s via `EditorAssetLibrary.consolidate_assets()` (redirecteur automatique + suppression du doublon) quand c'Ă©tait possible, ou supprimĂ©s directement (`delete_asset`) quand rien ne rĂ©fĂ©rençait plus la copie importĂ©e. Le contenu unique restant a ensuite Ă©tĂ© dĂ©placĂ© vers son emplacement dĂ©finitif (`Content/Systems/Blueprints/`, `Content/Core/PlayerController/Input/`) via `AssetToolsHelpers.get_asset_tools().rename_assets()` (dĂ©placement avec réécriture de toutes les rĂ©fĂ©rences, pas un dĂ©placement de fichier brut).
**Reste Ă  faire / connu (2026-09-08)** :
- `Content/Systems/Mover/SmartObjects/AC_SmartObjectAnimation` (anciennement `Content/Blueprints/SmartObjects/`, dĂ©placĂ© le 2026-09-14) : un doublon n'a **pas** pu ĂȘtre fusionnĂ©/supprimĂ© (l'outil `consolidate_assets` fait planter l'Ă©diteur sur cet ActorComponent prĂ©cis, voir §9) — laissĂ© en l'Ă©tat intentionnellement, sans impact gameplay (fonctionnalitĂ© SmartObject annexe).
- ⚠ *ObsolĂšte depuis le 2026-09-13 (voir §8.2)* — `Content/Blueprints/` contenait alors de nombreux fichiers `ObjectRedirector` (rĂ©sidus de `consolidate_assets`) — passer un **"Fixup Redirectors" manuel** (clic droit sur le dossier dans le Content Browser) quand pratique ; aucune API Python trouvĂ©e pour cette opĂ©ration, et forcer une suppression brute des redirecteurs risquerait de casser des rĂ©fĂ©rences logicielles (soft references) non encore réécrites.
### 8.2 Migration `Project_F` → `ProjectFv1` (2026-09-13) — incident et Ă©tat aprĂšs rĂ©paration
`E:\projet\Project_F` est un **autre dĂ©pĂŽt git** (projet Game Animation Sample nettoyĂ©, oĂč l'utilisateur a dĂ©veloppĂ© un systĂšme de visĂ©e). Le 2026-09-13 (~22:00–22:12), l'Ă©diteur de `Project_F` a migrĂ© **3073 packages** directement dans `ProjectFv1/Content` **pendant que l'Ă©diteur de ProjectFv1 Ă©tait ouvert** (preuve : `Project_F/Saved/Logs/Project_F.log`, lignes `LogSavePackage: Moving ... to E:/projet/ProjectFv1/Content/...`). ⚠ Ne jamais migrer vers ce projet avec son Ă©diteur ouvert.
DĂ©gĂąts constatĂ©s : 1449 assets suivis par git de `Characters/UEFN_Mannequin/` Ă©crasĂ©s (animations + chooser tables, liĂ©es dĂ©sormais aux notifies/enums de `Content/Blueprints/`), 782 fichiers `Systems/Weather/UltraDynamicSky/` Ă©crasĂ©s (widgets mĂ©tĂ©o sans leurs structs `ST_Weather*` → 149 erreurs), `Content/Blueprints/` recréé comme copie complĂšte (137 vrais assets) du framework de `Systems/Blueprints/`. Les enums/structs des deux copies DIVERGENT (ex. `E_MovementDirection` : `F/B/LL/LR/RL/RR` vs `Forward/Back/left/
`, `E_Gait` sans Prone/Swimming) : ne JAMAIS les fusionner aveuglĂ©ment.
Réparation (décisions utilisateur) :
- **Météo** : les 782 fichiers UltraDynamicSky restaurés depuis le commit `d29748b` (LFS). Versions migrées sauvegardées dans `E:\projet\ProjectFv1_backup_migration_2026-09-13\`.
- **Anims/choosers migrĂ©s conservĂ©s**. Contexte (`ContextData`) des 7 chooser tables CMC/traversal remis aux valeurs du commit : `CHT_PoseSearchDatabases(+_Dense/_Sparse/_ExtremeSparse)` et `CHT_CMCCharacterAnimations` → `Systems/Blueprints/SandboxCharacter_CMC_ABP` (+ `S_ChooserOutputs` Systems) ; `CHT_TraversalMontages_CMC/_Mover` → `Systems/Blueprints/Data/S_TraversalChooserInputs/Outputs`. Lignes Bow du chooser Dense perdues (voir §1.3).
- **Structs strictement identiques fusionnĂ©es** (`consolidate_assets`, redirecteurs laissĂ©s) : `Content/Blueprints/Data/S_CHT_TraversalMontages_IN/OUT` → `Systems/.../S_TraversalChooserInputs/Outputs`, `Content/Blueprints/Data/S_ChooserOutputs` → `Systems/.../S_ChooserOutputs`. Pins de `Content/Blueprints/AC_TraversalLogic` (traversal du Mover) rebranchĂ©es.
- **Doublon GASP du personnage CMC supprimé** : `Content/Blueprints/SandboxCharacter_CMC` + `_ABP`. Références de démo redirigées vers `/Game/SandboxCharacter_CMC` (depuis déplacé dans `/Game/Core/Character/`) / l'ABP Systems (`PC_Sandbox.Characters`, `AC_PreCMCTick`, `GM_Sandbox.PawnClasses_Soft`, `EUW_PawnOverrideButton.Object`, variable locale de `BP_NotifyState_EarlyTransition`). (`Content/Widgets/` a été supprimé le 2026-09-14.)
- `STT_PlayAnimFromBestCost` (Systems) pointe désormais vers `Systems/Blueprints/SmartObjects/AC_SmartObjectAnimation`.
- `Config/DefaultGameplayTags.ini` : ajout des tags du sample `Foley.Event.*` et `SmartObject.ObjectType.Player/Bench` (manquants → avertissements massifs + StateTree SmartObject non liable).
Contenu du framework du sample conservé volontairement (déplacé le 2026-09-14, voir §4.2.1 et « Convergence partielle » ci-dessous) :
- `Systems/Aiming/` (`ABP_AimingMaster`, `ABP_AimingMaster_UEFN`, `CH_AO`, `CH_Base_AO_Animechooser`) → systĂšme de visĂ©e/aim offset dĂ©veloppĂ© dans `Project_F`, utilisĂ© par `SandboxCharacter_Mover_ABP`.
- `Systems/Mover/SandboxCharacter_Mover` / `_ABP` / `_Ragdoll` → personnage alternatif plugin Mover (+ `Systems/Mover/GM_Sandbox`/`PC_Sandbox` ; `dev_map` de dĂ©mo supprimĂ© le 2026-09-14).
- le reste du framework dupliqué, dans `Systems/Mover/` (AC_TraversalLogic, BPI_*, Data/, Cameras/
) ; ses notifies restent dans `Content/Blueprints/AnimNotifies/` (liés à 1278 animations). Chooser tables toujours liées à ce framework : `CHT_PoseSearchDatabases_Relaxed`, `CHT_MoverCharacterAnimations_PoseMatch`, `CHT_GetUpMontages`, `CHT_CharacterInteractionPSDs`.
**Convergence partielle (2026-09-14)** :
- **Enums fusionnés** : les 17 enums du sample de `Content/Blueprints/Data|Cameras|AnimNotifies/` (`E_Gait`, `E_Stance`, `E_MovementMode`, `E_MovementState`, `E_MovementDirection`, `E_MovementDirectionBias`, `E_RotationMode`, `E_AnalogStickBehavior`, `E_ExperimentalStateMachineState`, `E_RagdollInjuryState`, `E_TraversalActionType`, `E_CameraMode`, `E_CameraStyle`, `E_EarlyTransition_Condition`, `E_EarlyTransition_Destination`, `E_FoleyEventSide`, `E_TraversalBlendOutCondition`) ont été fusionnés dans leurs jumeaux de `Systems/Blueprints/` (17 redirecteurs laissés à l'ancien emplacement). Valeurs propres au sample ajoutées en fin d'enum cÎté projet (voir §4.2) et pins/switchs de la chaßne Mover remappés dessus. Interfaces et structs Systems enrichies en conséquence (§4.2).
- **Réparations consécutives** (piÚge décrit en §9) : variables d'enum vidées retypées et liens rebranchés dans `SandboxCharacter_Mover`, `SandboxCharacter_Mover_Ragdoll`, `SandboxCharacter_Mover_ABP` (restauré depuis sauvegarde puis remappé), `BP_AnimNotify_TriggerRagdoll`, `AnimNotifies/BP_NotifyState_EarlyTransition` et `BP_NotifyState_MontageBlendOut` (liens comparés un à un à leurs jumeaux Systems). Chaque asset validé par rechargement de son package.
- **ArrĂȘt de la convergence (dĂ©cision utilisateur)** : Ă  cause de ce piĂšge, le reste de `Content/Blueprints/` (Mover, Aiming, AC/BPI/structs/notifies/camĂ©ras du sample) n'est PAS fusionnĂ©. Il a Ă©tĂ© seulement dĂ©placĂ©, sans fusion, le 2026-09-14 (voir §4.2.1) ; les doublons restants sont une dette connue.
**Restauration Motion Matching (2026-09-14, dĂ©cision utilisateur)** : un audit des 189 donnĂ©es Motion Matching modifiĂ©es depuis `d29748b` (comparaison des rĂ©fĂ©rences `/Game/...` des blobs git avant/aprĂšs) a trouvĂ© 7 assets dĂ©gradĂ©s par la migration, tous restaurĂ©s depuis `d29748b` (toutes leurs rĂ©fĂ©rences existent encore) : `CHT_PoseSearchDatabases_Dense` (19 bases perdues : Bow, Prone, nage, cover accroupi, Push ; colonnes `E_CoverD`/`Weapon_Equip`), `Channels/PSC_Traversal_Head` et `_Pos` (pointaient sur la BPI du sample au lieu de `Systems/Blueprints/BPI_SandboxCharacter_ABP`), `Schemas/PSS_Default` et `PSS_Idle` (table miroir `Rigs/MDT_UEFN_Mannequin` perdue), `Normalization_Sets/PSN_Relaxed_All` (le Mover y perd `PSD_SM_Mover_TraversalTransitions`), `Databases/Dense/PSD_Dense_Stand_Idles` (mesh d'aperçu). Seul autre Ă©cart trouvĂ©, conservĂ© : `Traversal/CHT_TraversalMontages_Mover` (montages Climb Relaxed → Neutral, chooser du Mover).
État vĂ©rifiĂ© en fin de rĂ©paration : passe de compilation complĂšte, **435 Blueprints, 0 erreur** (non encore testĂ© en PIE). Dettes connues non liĂ©es Ă  la migration : tags `Attachments.*` (§2.1), animations AGIS sans squelette (voir §9 : `Systems/Inventory/Other/Animations/DeathAnim_AGIS` et `Rifle/*` — compatibles Ă  100 % avec `SK_UEFN_Mannequin`, Ă  assigner Ă  la main dans l'Ă©diteur ; `Jog/`, `WALK/`, `TurnInPlace/`, `AIM_Rifle_Idle_ADS` inutilisĂ©es et incompatibles), « Unknown structure » au chargement de `Systems/Blueprints/BPI_SandboxCharacter_Pawn` (antĂ©rieur au 2026-09-07).
---
## 9. Notes pour les IA : piĂšges connus de l'outillage
Ce projet est modifié via le plugin **VibeUE** (service MCP `unreal-mcp`, voir `.mcp.json`), qui expose des services Python (`unreal.BlueprintService`, `unreal.WidgetService`, `unreal.InputService`, `unreal.EnumStructService`, etc.) pour piloter l'éditeur Unreal sans l'ouvrir manuellement. Quelques piÚges rencontrés et leurs solutions :
- **PIÈGE — un nƓud `Evaluate Chooser` sans objet de contexte branchĂ© renvoie TOUJOURS une liste vide (2026-09-18)** : constatĂ© sur `SandboxCharacter_Mover_ABP::Update_MotionMatching`, dont les deux nƓuds avaient leur pin de contexte (`SandboxCharacter_Mover_ABP_C`) laissĂ© vide — aucune colonne du chooser ne pouvait ĂȘtre rĂ©solue, `ValidDatabases` restait vide en permanence et toute la branche Motion Matching de l'`AnimGraph` Ă©tait inerte (`MM Search Cost` = FLT_MAX, `CurrentSelectedAnim` = None) sans qu'aucune erreur ne soit loguĂ©e. Toujours brancher `self` (type id crĂ©able : `Variables|Getareferencetoself`). SymptĂŽme jumeau Ă  surveiller : un chooser dupliquĂ© dont on a changĂ© le `ContextData` fonctionne, les liaisons se rĂ©solvant par NOM de variable sur la nouvelle classe de contexte.
- **PIÈGE — crĂ©er par script un sous-objet instanciĂ© dans un template de composant casse la sauvegarde des Blueprints ENFANTS (2026-09-17)** : `unreal.new_object(cls, outer=<template du composant>)` (ici un mode de mouvement ajoutĂ© Ă  `CharacterMover.MovementModes`) produit un objet **privĂ©** ; l'enfant (`SandboxCharacter_Mover_Ragdoll`, qui surcharge le composant via son `InheritableComponentHandler`) le prend comme archĂ©type et sa sauvegarde Ă©choue avec `Illegal reference to private object`. PrĂ©fĂ©rer un enregistrement Ă  l'exĂ©cution (`MoverComponent::AddMovementModeFromClass` au BeginPlay) ou l'Ă©dition manuelle dans l'Ă©diteur.
- **PIÈGE — `SubobjectDataSubsystem.rename_subobject` juste aprĂšs `BlueprintService.remove_component` provoque une assertion fatale (2026-09-17)** : `Renaming an object (
Arrow_GEN_VARIABLE) on top of an existing object (
Arrow1_GEN_VARIABLE) is not allowed` — l'ancien template n'est pas encore collectĂ©. L'interprĂ©teur Python est ensuite corrompu (`0xC0000005` au premier accĂšs au Blueprint) et **le MCP sauvegarde les packages modifiĂ©s Ă  chaque appel**, donc l'Ă©tat intermĂ©diaire peut se retrouver sur disque : redĂ©marrer l'Ă©diteur et vĂ©rifier le Blueprint (composants, liaisons) aprĂšs ce type d'erreur. Corollaire : un composant ajoutĂ© par `BlueprintService.add_component(..., parent_name="Capsule")` reçoit un `ParentComponentOrVariableName` explicite qui dĂ©clenche l'avertissement `Reparenting the 'X' component 
 possible cyclic linkage` Ă  chaque compilation ; l'ajouter sans parent (comme les autres composants racine du SCS) Ă©vite l'avertissement.
- **Lire l'Ă©tat de jeu en PIE depuis Python est dĂ©calĂ© d'une frame ou deux** (surtout pour les variables d'Anim Blueprint Ă©crites dans l'update thread-safe) : une mĂȘme valeur peut sembler « fausse » (allure restĂ©e Ă  Run, `ValidDatabases` vide) puis correcte Ă  l'appel suivant. Toujours relire dans un second appel avant de conclure Ă  un bug. De plus, `BlueprintService.list_graphs`/`list_variables` renvoient **0 Ă©lĂ©ment pendant une session PIE** : arrĂȘter le PIE avant d'inspecter un graphe.
- **`build_graph`** attend un champ **`"ref"`** pour identifier chaque nƓud dans la liste `nodes` (pas `"id"`). Un mauvais nom de champ Ă©choue silencieusement (retourne `None` sans erreur explicite).
- Les **variables locales à une fonction** (paramÚtres, locals créés via `add_object_variable` avec `graph=...`) ne sont pas fiables avec `build_graph`'s `variable_get`/`variable_set` : préférer des **variables membres** du Blueprint.
- Un `K2Node_VariableSet`/`Get` créé via un spawner_key gĂ©nĂ©rique (`discover_nodes` + `create_node_by_key`) peut se rĂ©soudre sur la **mauvaise classe** si plusieurs Blueprints chargĂ©s ont une variable de mĂȘme nom affichĂ© (ex. "Owner Inventory" existe Ă  la fois sur `WB_Container` et sur un widget créé cette session) — toujours vĂ©rifier les pins du nƓud créé (`get_node_pins`) avant de cĂąbler, ou utiliser `build_graph` avec `type: "variable_set"` + `params.variable` en contexte "self" qui est fiable.
- Appeler une fonction Blueprint **préexistante** (créée dans une session antérieure/par le plugin marketplace) sur un **autre** Blueprint peut échouer silencieusement selon la méthode utilisée. Privilégier l'accÚs direct aux propriétés (`member_get`, fiable) plutÎt que l'appel de fonction cross-Blueprint quand c'est possible ; sinon tester `add_function_call_on_variable` et vérifier le résultat via `get_node_pins`/`get_nodes_in_graph`.
- Écrire une valeur littĂ©rale sur un pin de type `Text`, `Object` ou `Class` via `set_node_pin_value` Ă©choue souvent **silencieusement Ă  l'affichage** (`get_node_pins` renvoie toujours `default_value: ""`) mĂȘme quand l'assignation a rĂ©ellement fonctionnĂ© — vĂ©rifier via une **compilation** (erreurs de compilation manquantes = probablement OK) plutĂŽt que via une relecture du pin.
- **Toujours compiler ET sauvegarder** un Blueprint aprÚs modification (`BlueprintEditorLibrary.compile_blueprint` + `EditorAssetLibrary.save_loaded_asset`), puis **tester en PIE** et lire les logs (`EditorToolset.LogsToolset.GetLogEntries`, filtrer sur `"Blueprint Runtime Error"`) pour détecter les erreurs à l'exécution qu'une compilation réussie ne révÚle pas (ex. accÚs à un élément de tableau hors limites).
- Le clavier cible du jeu est **AZERTY** : vérifier les mappings de touches existants (`IMC_Sandbox`, `IMC_AGIS_Character`, `IMC_AGIS_InventoryTab`) avant d'assigner une nouvelle touche, et choisir des touches physiquement identiques entre QWERTY/AZERTY.
**PiÚges du toolset `editor_toolset.toolsets.blueprint.BlueprintTools`** (create_node/connect_pins/write_graph_dsl/get_node_infos, utilisé depuis le systÚme d'armes du 2026-09-05) :
- La **DSL `write_graph_dsl`** casse sur un `type_id` contenant une parenthĂšse fermante suivie d'autres caractĂšres (ex. `BOTH|GetItem(copy)ByUID` Ă©choue avec "does not exist" car le tokenizer coupe au premier `)`) ; un `type_id` se terminant PAR une parenthĂšse fonctionne (`Utilities|Array|Get(acopy)`). Dans ce cas, construire le nƓud Ă  la main (`create_node`/`connect_pins`) plutĂŽt que via la DSL.
- Un `type_id` retournĂ© par `find_node_types` (ex. `Variables|Default|GetACWeapon`) peut diffĂ©rer du `type_id` affichĂ© ensuite par `get_node_infos` pour CE nƓud (ex. `|GetAC_Weapon`, avec underscore) : toujours utiliser la chaĂźne de `find_node_types` pour **crĂ©er**/rĂ©fĂ©rencer un nƓud dans la DSL, jamais celle affichĂ©e aprĂšs coup par `get_node_infos`.
- Éditer une propriĂ©tĂ© d'un **composant SCS hĂ©ritĂ©** (ex. changer le mesh d'un `StaticMeshComponent` dĂ©fini dans une classe parente, depuis le Blueprint enfant) : `SubobjectDataBlueprintFunctionLibrary.get_object`/`get_associated_object` renvoient le **template du PARENT** (le modifier casse le parent pour toutes ses classes filles !). Utiliser `get_object_for_blueprint(data, child_blueprint)` qui crĂ©e correctement l'override propre Ă  l'enfant.
- Itérer un `Array[SubobjectDataHandle]` avec `for h in handles:` casse `get_data`/`get_variable_name` (erreur de nativisation) ; indexer explicitement (`for i in range(len(handles)): h = handles[i]`) fonctionne.
- Un Blueprint Function Library (BFL) se crée avec `unreal.BlueprintFunctionLibraryFactory()` (pas le `BlueprintFactory` générique + `parent_class`, qui échoue avec "Cannot create a blueprint based on the class").
- Depuis `execute_python_code`, appeler une fonction Blueprint sur une instance PIE via `actor.call_method(...)` nĂ©cessite le nom **d'affichage exact avec ses espaces** (ex. `"Set Item Actor Owner"`, `"Primary Use"`), pas une version condensĂ©e/snake_case — sauf pour certains getters de variable auto-gĂ©nĂ©rĂ©s dont le nom interne rĂ©el ne correspond ni Ă  l'un ni Ă  l'autre (Ă  vĂ©rifier au cas par cas).
- Créer un `InputAction` en Python : `unreal.InputAction_Factory()` (PAS `unreal.InputActionFactory`, qui n'existe pas) + `AssetToolsHelpers.get_asset_tools().create_asset(...)`. Pour mapper une touche dans un `InputMappingContext` existant, préférer `unreal.InputService.add_key_mapping(imc_path, ia_path, "F9")` (service VibeUE dédié) plutÎt que d'éditer la propriété dépréciée `mappings` de l'IMC directement en Python.
- Construire un widget UMG en Python : `unreal.WidgetService.add_component`/`set_property`/`bind_event` fonctionnent bien pour la hiĂ©rarchie et les propriĂ©tĂ©s, mais il n'existe **aucun** `unreal.WidgetBlueprintLibrary.create_widget` exposĂ© pour instancier un widget Ă  la volĂ©e. Pour un test rapide en PIE, utiliser `unreal.WidgetService.spawn_widget_in_pie(chemin)` (retourne un handle qu'on repasse Ă  `remove_widget_from_pie`) — mais ce handle n'expose pas d'appel de fonction arbitraire sur l'instance ; il ne sert qu'Ă  l'afficher/la retirer et lire ses propriĂ©tĂ©s (`get_live_property`).
- `EditorToolset.EditorAppToolset.CaptureAssetImage`/`CaptureViewport` sont le seul moyen fiable de **voir** un rĂ©sultat (mesh, matĂ©riau, scĂšne) : le retour JSON est trop volumineux pour ĂȘtre lu tel quel (limite de tokens) — toujours passer par un script Python (`base64.b64decode` du champ `returnValue.data`, Ă©crit dans le scratchpad) puis `Read` sur le PNG obtenu.
- Un asset `.uasset` peut se retrouver **corrompu Ă  vide** (0 vertex/triangle, bounds nuls) aprĂšs une fermeture anormale de l'Ă©diteur (coupure de courant, crash) sans que sa taille de fichier ni son chargement ne signalent d'erreur — toujours vĂ©rifier `get_num_vertices(0)`/`get_bounds()` (StaticMesh) aprĂšs un redĂ©marrage suspect avant de chercher un autre bug. `delete_asset`/`consolidate_assets` peuvent Ă©chouer silencieusement (retour `False`, sans exception) sur un tel asset corrompu mĂȘme sans rĂ©fĂ©renceur restant — dans ce cas, renommer le nouvel asset importĂ© sous un autre nom plutĂŽt que de s'acharner Ă  rĂ©utiliser le chemin d'origine.
- Toute variable membre **boolĂ©enne** (`bXxx`) affiche son nom SANS le prĂ©fixe `b` dans les nƓuds Get/Set gĂ©nĂ©rĂ©s par la DSL (`GetCanSeePlayer`, pas `GetbCanSeePlayer`) — mais son **nom de propriĂ©tĂ© brut**, tel qu'attendu par `get_editor_property`/`set_editor_property` en Python ET par `StateTreeService.bind_*_property_to_context` (chemins de contexte), **conserve le prĂ©fixe `b`** (`"bCanSeePlayer"`, `"AnimalSenses.bScentDetected"`). Utiliser le nom sans `b` dans l'un de ces deux contextes Ă©choue silencieusement en DSL (nƓud introuvable) ou avec "Malformed target property path" cĂŽtĂ© StateTree.
- `delete_node` sur un nƓud dont une pin est encore rĂ©fĂ©rencĂ©e par un AUTRE nƓud (ex. un nƓud `Self`/`GetXxx` pur, rĂ©utilisĂ© par plusieurs consommateurs) peut laisser une **connexion fantĂŽme** cĂŽtĂ© survivant : `get_node_infos` continue de lister une pin connectĂ©e vers le node supprimĂ© (qui n'apparaĂźt plus dans `find_nodes`), et la compilation Ă©choue avec l'erreur gĂ©nĂ©rique `Internal compiler error inside CreateExecutionSchedule` sans dĂ©signer le nƓud fautif. Diagnostic : dumper `get_node_infos` de TOUS les nƓuds du graphe et chercher une pin connectĂ©e vers un node absent de `find_nodes` ; le node fantĂŽme existe encore assez longtemps en mĂ©moire (non garbage-collectĂ©) pour que `break_pins` fonctionne dessus malgrĂ© tout — l'appeler explicitement sur cette connexion rĂšgle l'erreur.
- Une **UserDefinedEnum** (créée via `unreal.EnumStructService.create_enum`) ne peut PAS servir de type Ă  une variable membre, un paramĂštre de fonction ou un paramĂštre de dispatcher via les outils disponibles (`add_variable`/`add_function_param` n'acceptent que bool/int/float/byte/name/string/text/Vector/Rotator/Transform/Vector2D/LinearColor) — limitation dĂ©jĂ  notĂ©e pour `AC_Stats::OnDamaged`, reconfirmĂ©e pour `E_AnimalState`. Contournement : stocker en `byte` et convertir Ă  l'affichage seulement si besoin (`Utilities|String|ToString(Byte)`).
- Une **StateTree Task Blueprint** (`StateTreeTaskBlueprintBase`) ne tique QUE si sa CDO a `should_call_tick_only_on_events = true` — malgrĂ© son nom qui suggĂšre l'inverse ("seulement sur Ă©vĂ©nements"), c'est en rĂ©alitĂ© l'interrupteur gĂ©nĂ©ral qui active `ReceiveLatentTick`. Aucune property `bShouldCallTick` distincte n'est exposĂ©e.
- ChaĂźner plus de deux concatĂ©nations `(+ a b)` de **String** dans une mĂȘme expression DSL imbriquĂ©e casse avec `Could not connect pin ReturnValue to A` (constatĂ© sur `BP_AnimalBase::DebugTick`) — mĂȘme en sĂ©parant chaque Ă©tape par un `bind` intermĂ©diaire. Contournement : limiter chaque nƓud `+` Ă  DEUX opĂ©randes littĂ©raux/variables simples, et multiplier les appels `DrawDebugString`/lignes plutĂŽt que de construire une seule chaĂźne longue.
- Une donnĂ©e cross-instance (lire la variable d'un AUTRE acteur de mĂȘme classe, ex. le `HerdID` d'un autre `BP_AnimalBase` pendant `BroadcastAlertToHerd`) n'est PAS accessible via le getter `Variables|Default|GetXxx` gĂ©nĂ©rĂ© par la DSL (celui-ci n'a pas de pin `self`/cible, contrairement aux nƓuds `Class|MaClasse|MaFonction` qui, eux, exposent bien un `self` substituable). Solution : passer la donnĂ©e nĂ©cessaire en PARAMÈTRE de la fonction appelĂ©e sur l'autre instance plutĂŽt que de tenter de la lire directement (voir `ReceiveHerdAlert(ThreatLocation, CallerHerdID)`).
- Une nouvelle **StateTree** (`unreal.StateTreeService.create_state_tree`) dĂ©marre SANS aucun Ă©tat racine : `add_state(path, "Root", ...)` Ă©choue silencieusement (retourne `False`) tant qu'un premier `add_state(path, "", "Root", "State")` (parent_path **vide**) n'a pas créé l'Ă©tat racine lui-mĂȘme.
- Lier une `AC_Stats::OnDamaged` (dispatcher avec paramĂštres) directement Ă  une fonction Blueprint CLASSIQUE (pas un Custom Event) via `K2Node_CreateDelegate.set_create_delegate_function` **compile sans erreur immĂ©diate** mais casse la compilation Ă  la volĂ©e suivante avec `Internal compiler error inside CreateExecutionSchedule` — et peut mĂȘme faire apparaĂźtre des nƓuds `AddEvent` parasites (EventTick, EventActorBeginOverlap
) ailleurs dans le graphe. Un dispatcher avec paramĂštres doit ĂȘtre liĂ© Ă  un **Custom Event** (`add_event` avec un nom arbitraire crĂ©e un vrai `K2Node_CustomEvent`, contrairement Ă  `write_graph_dsl`'s `(event Nom ...)` qui n'accepte que les noms d'Ă©vĂ©nements natifs "AddEvent|Nom" connus). Un dispatcher SANS paramĂštre (ex. `AC_Stats::OnDeath`) ne pose aucun problĂšme liĂ© Ă  une fonction classique.
- (2026-09-14) `BlueprintTools.write_graph_dsl` **Ă©choue en silence** : en cas d'erreur (nƓud inexistant, pin incompatible) il ne lĂšve rien, retourne `None` et laisse le graphe inchangĂ© ou VIDE ; le vrai message n'apparaĂźt que dans `Saved/Logs/ProjectFv1.log` (`LogScript: Warning: AssertionError: The node could not be created / 
 does not exist` ou `RuntimeError: Could not connect pin 
`). Toujours vĂ©rifier chaque Ă©criture par `read_graph_dsl` (chercher un nƓud attendu) avant de sauvegarder. PiĂšges constatĂ©s : (a) `Math|Byte|Equal(Byte)` et `Math|Vector|vector+vector` sont RELUS par `read_graph_dsl` mais REFUSÉS Ă  l'Ă©criture — utiliser les opĂ©rateurs `==`/`+` ; (b) les appels de fonction du Blueprint lui-mĂȘme (`CallFunction|MaFonction`) ont une pin `self` en premiĂšre position : passer les arguments par mot-clĂ© (`:Anim x :Loop true`), sinon le premier argument est branchĂ© sur `self` ; (c) les getters de variables Ă  underscore perdent l'underscore dans le type_id (`Anim_Idle` → `Variables|Default|GetAnimIdle`), et `find_node_types` renvoie aussi `CallFunction|ApplyIdleforState` avec la casse de l'affichage ; (d) `read_graph_dsl` affiche parfois un nƓud sous une catĂ©gorie trompeuse (`CustomizableObjectInstance|SetCurrentState` pour un appel Ă  `BP_AnimalBase::SetCurrentState`) — vĂ©rifier avec `get_node_infos` avant de conclure Ă  un mauvais binding ; (e) `BlueprintTools.create_node` attend un `unreal.IntPoint` pour `pos`, pas un `Vector2D`.
- (2026-09-14) StateTree : une tĂąche Blueprint qui appelle `FinishTask` dans `EnterState` termine l'Ă©tat dans la mĂȘme frame (`TasksCompletion=Any`), et `StateTreeService.set_task_considered_for_completion(..., False)` renvoie `True` mais n'a PAS empĂȘchĂ© ce comportement en PIE (UE 5.8). Pour une tĂąche « fire and forget » (ex. `STT_SetAnimalState`), ne PAS appeler `FinishTask`. Diagnostic : `log LogStateTree Verbose` en PIE puis compter les `Enter state` dans le log.
- (2026-09-14) StateTree : une transition posĂ©e sur `Root` (trigger `OnTick`) reste Ă©valuĂ©e quand sa CIBLE est dĂ©jĂ  active et la rĂ©-entre Ă  chaque tick (Exit/Enter de toutes ses tĂąches). Toute transition globale doit donc porter une condition « pas dĂ©jĂ  dans cet Ă©tat » (ex. `bIsFleeing == false` pour `Root→Flee`).
- Un **NavMeshBoundsVolume** ajoutĂ© par script (`SceneTools.add_to_scene_from_class`) ne gĂ©nĂšre PAS automatiquement de NavMesh : le `RecastNavMesh-Default` correspondant naĂźt avec `RuntimeGeneration = Static` (aucune rĂ©gĂ©nĂ©ration, ni en Ă©diteur ni au runtime, sans un "Build Paths" manuel — introuvable dans l'API Python exposĂ©e). Fix : `recastNavMesh.set_editor_property("runtime_generation", unreal.RuntimeGenerationType.DYNAMIC)` PUIS `unreal.NavigationSystem.on_navigation_bounds_updated(nav_sys, volume)` pour forcer une premiĂšre gĂ©nĂ©ration immĂ©diate (sans ce second appel, le volume reste sans tuiles jusqu'au premier changement de gĂ©omĂ©trie pertinent).
- **FAUX AMI corrigĂ© (2026-09-05)** : `read_graph_dsl` insĂšre parfois un `_` de lisibilitĂ© dans le nom affichĂ© d'une variable membre commençant par un prĂ©fixe court en majuscule (ex. la variable rĂ©elle `LContainer` s'affiche `(|GetL_Container)`, PAS `(|GetLContainer)` — idem `LItemName`→`L_ItemName`). Utiliser cette forme AVEC underscore pour crĂ©er/Ă©crire un nƓud (`create_node`, `write_graph_dsl`) Ă©choue avec "... does not exist", ce qui peut faire croire Ă  tort Ă  une variable locale non-crĂ©able (variables locales et membres se comportent en rĂ©alitĂ© de façon identique cĂŽtĂ© crĂ©ation). Avant toute chirurgie sur un Get/Set qui Ă©choue ainsi, vĂ©rifier le nom SANS underscore via `find_node_types` (ex. filtrer `"LContainer"`) plutĂŽt que de supposer une limitation de l'outil — le nom rĂ©el de la propriĂ©tĂ© (`get_editor_property` en Python inclus) ne contient jamais cet underscore ajoutĂ© par l'affichage.
- Un Blueprint qui est la classe d'un **composant actif dans une session PIE en cours** peut compiler sans erreur mais **Ă©chouer silencieusement Ă  la sauvegarde** (`save_loaded_asset` renvoie `False` sans exception) tant que le PIE n'est pas arrĂȘtĂ© — toujours `StopPIE` avant de sauvegarder un asset qu'une session PIE utilise, et re-tenter la sauvegarde aprĂšs l'arrĂȘt.
- **CORROLAIRE DANGEREUX** : aprĂšs tout Ă©chec de `save_loaded_asset` dĂ» au PIE, **StopPIE PUIS ressauvegarder IMMÉDIATEMENT**, et **toujours revĂ©rifier via `read_graph_dsl`/`find_nodes` aprĂšs un redĂ©marrage/arrĂȘt du PIE** qu'une Ă©dition rĂ©cente est bien encore prĂ©sente avant de continuer Ă  construire dessus — une Ă©dition compilĂ©e avec succĂšs pendant que le PIE tournait peut sembler perdue Ă  la prochaine lecture si la sauvegarde qui a suivi a Ă©chouĂ© silencieusement.
- **PIÈGE MAJEUR — graphe d'implĂ©mentation d'interface Blueprint fragile et auto-rĂ©gĂ©nĂ©rĂ© (2026-09-06)** : le graphe qui implĂ©mente une fonction de Blueprint Interface (ex. `BP_Item_HoldableBase:On Item Attached`, implĂ©mentant `BPI_HoldableItem::On Item Attached`) peut **revenir tout seul Ă  son Ă©tat d'origine** aprĂšs une compilation ultĂ©rieure — de N'IMPORTE QUEL Blueprint de la hiĂ©rarchie, pas seulement celui qu'on Ă©dite — mĂȘme quand `read_graph_dsl` avait confirmĂ© l'Ă©dition juste aprĂšs une sauvegarde rĂ©ussie (`save_loaded_asset` → `True`, mtime du `.uasset` Ă  jour). Un `reload_packages` forcĂ© depuis Python confirme que c'est bien le contenu SUR DISQUE qui a rĂ©gressĂ©, pas juste l'Ă©tat en mĂ©moire — la persistance de ce type de graphe est donc bien plus fragile qu'une fonction normale. Reproduit 3 fois de suite sur le mĂȘme graphe dans cette session malgrĂ© des re-sauvegardes systĂ©matiques. **Ne JAMAIS hĂ©berger de logique mĂ©tier propre (dispatcher broadcast, etc.) directement dans un graphe d'implĂ©mentation d'interface** — s'en servir seulement comme passe-plat trivial si besoin, ou mieux : contourner entiĂšrement l'interface et appeler la fonction/le dispatcher cible directement depuis l'appelant via un `Cast` explicite vers la classe concrĂšte (ex. `Inventory_Equipper::AttachItemToOwner` caste `Item` en `BP_Item_HoldableBase` et appelle `CallOnHeldStateChanged` directement, sans jamais passer par l'interface) — cette approche s'est rĂ©vĂ©lĂ©e stable aprĂšs le contournement.
- **RĂ©fĂ©rence d'interface `K2Node_Message`/`K2Node_CallFunction` corrompue aprĂšs un renommage d'interface (2026-09-06)** : un appel d'interface existant peut afficher un `type_id`/catĂ©gorie qui ne correspond Ă  AUCUN asset actuel du projet (ex. `BPIEquippableItem|OnItemAttached` alors que seule `BPI_HoldableItem` existe — probablement un renommage `BPI_EquippableItem`→`BPI_HoldableItem` fait avant cette session) tout en compilant sans erreur et en affichant des types de pins statiquement cohĂ©rents (`get_node_type_pins` montrait bien `self: BPI Holdable Item Interface`) — la compilation rĂ©ussit car un systĂšme de redirection (CoreRedirects ou Ă©quivalent) fait correspondre le nom au niveau des TYPES, mais l'appel ne dĂ©clenche RIEN Ă  l'exĂ©cution (confirmĂ© par prints de debug ne s'affichant jamais cĂŽtĂ© rĂ©cepteur, malgrĂ© la classe cible confirmĂ©e implĂ©menter l'interface via `unreal.SystemLibrary.does_class_implement_interface`). Un nƓud fraĂźchement créé avec le MÊME `type_id` peut mĂȘme se rĂ©soudre en un type de nƓud DIFFÉRENT (`K2Node_CallFunction` au lieu du `K2Node_Message` d'origine) — signe supplĂ©mentaire d'une rĂ©fĂ©rence historique corrompue plutĂŽt que d'un vrai type_id valide. Diagnostic : ajouter un print cĂŽtĂ© rĂ©cepteur et vĂ©rifier empiriquement qu'il se dĂ©clenche ; si l'appelant confirme passer par le nƓud (print juste avant) mais que le rĂ©cepteur reste silencieux, suspecter ce piĂšge. Fix : ne pas rĂ©utiliser l'ancien nƓud ni recrĂ©er un nƓud du mĂȘme `type_id` — contourner l'interface entiĂšrement (voir point ci-dessus).
- **Consommer un stock d'inventaire PASSIF (jamais Ă©quipĂ©/lĂąchĂ©) ne doit jamais passer par un acteur monde (2026-09-06)** : la macro `ML_InventoryMacros::Get Item Actor` (et donc tout appel `Utilities|GetItemActor`) retourne TOUJOURS invalide pour un item empilĂ© qui n'a jamais Ă©tĂ© spawnĂ© comme acteur (ex. un stack de flĂšches "Arrow" qui reste en inventaire, jamais Ă©quipĂ©) — ce n'est pas un bug ponctuel, c'est le comportement normal : ces items n'ont simplement pas d'acteur tant qu'ils ne sont pas Ă©quipĂ©s/lĂąchĂ©s dans le monde. Une fonction du type `ConsumeArrow`/`ReduceAmmo` qui suit le patron `FindItem → GetItemActor → SERVER|ReduceAmount(item_actor, ...)` Ă©choue donc silencieusement (retourne false) pour ce genre d'item, mĂȘme quand `FindItem` a bien trouvĂ© le stack. Fix correct : opĂ©rer directement sur les DONNÉES via l'adresse de l'item, avec `Inventory__Main::AddItemAmount(ItemAddress, DeltaAmount)` (nĂ©gatif pour consommer) — une fonction RPC serveur qui gĂšre nativement le retrait de l'array si le montant tombe Ă  0, sans jamais nĂ©cessiter d'acteur. Le mĂ©canisme `GetItemActor`+fonction-sur-acteur reste correct UNIQUEMENT pour des items dont l'acteur est garanti d'exister (ex. un chargeur d'arme Ă©quipĂ©, actif comme `Magazine Reference`).
- **PIÈGE CRITIQUE — brancher UNE sortie de pose sur PLUSIEURS entrĂ©es dans un `AnimGraph` casse toute la locomotion (2026-09-06)** : contrairement Ă  un graphe Blueprint normal, une sortie `Pose Link` (`AnimGraphNode_*`) qui alimente directement 2+ entrĂ©es fait ÉVALUER EN DOUBLE, Ă  chaque frame, TOUT le sous-graphe en amont — pas seulement recalculer une valeur, mais rĂ©ellement re-tick les nƓuds AVEC ÉTAT (State Machine, Motion Matching, Blend Space avec accumulation de temps, Inertialization) une deuxiĂšme fois. SymptĂŽme observĂ© : le personnage "bouge de façon bizarre" (mouvement erratique/saccadĂ©) dĂšs qu'un nƓud en amont d'un fan-out a un Ă©tat interne dĂ©pendant du temps — la Motion Matching et les State Machines de ce projet en amont de `Slot 'DefaultSlot'` Ă©taient tickĂ©es deux fois par frame aprĂšs l'ajout (sans prĂ©caution) d'un second consommateur sur la sortie de ce Slot. Fix obligatoire : **ne jamais connecter directement une sortie de pose partagĂ©e Ă  plusieurs entrĂ©es** — insĂ©rer un `Save Cached Pose 'NomUnique'` juste aprĂšs la source, puis un `Use Cached Pose 'NomUnique'` par consommateur (le projet utilise dĂ©jĂ  ce patron ailleurs dans `SandboxCharacter_CMC_ABP`, ex. cache `MotionMachin` consommĂ© par `Mirror_0` ET `BlendListByBool_3` — repĂ©rable via `find_nodes`/`get_node_infos` avant de dupliquer une sortie, pour voir si le patron existe dĂ©jĂ ). Une rĂ©gression de ce type NE PROVOQUE AUCUNE ERREUR DE COMPILATION — le graphe compile et sauvegarde sans avertissement, le bug n'est visible qu'en jouant.
- **CrĂ©er un nƓud de Get/Set VARIABLE d'une AUTRE classe (pas une fonction) via `create_node` (2026-09-06)** : `find_node_types` avec `context_pins` pointant vers une pin dĂ©jĂ  castĂ©e Ă  la classe cible retourne bien le type_id correct (ex. `Variables|Default|SetWeapon`), mais ce type_id Ă©choue Ă  la crĂ©ation via `create_node` (`does not exist`) MÊME avec `declaring_class` renseignĂ© — `declaring_class` ne sert qu'Ă  dĂ©sambiguĂŻser un type_id dĂ©jĂ  trouvĂ©, pas Ă  Ă©largir la recherche. Fix : rappeler `find_node_types` avec `context_pins: []` (SANS contexte) sur le mĂȘme filtre — la forme SANS contexte rĂ©vĂšle le type_id RÉELLEMENT crĂ©able, qui suit la convention `Class|<NomClasseSansUnderscore>|<NomPropriĂ©tĂ©>` (ex. `Class|SandboxCharacterCMC|SetWeapon`), diffĂ©rent de la forme `Variables|Default|...` que le contexte affichait. Une fois créé, le nƓud peut ensuite s'afficher avec un LABEL DSL trompeur (catĂ©gorie `SandboxCharacterCMCABP` au lieu de `SandboxCharacterCMC` par ex.) — vĂ©rifier le type rĂ©el de la pin `self` via `get_node_infos` (pas la catĂ©gorie DSL) pour confirmer la bonne classe cible.
- **Configurer un nƓud `AnimGraphNode_*` (Slot, LayeredBoneBlend, etc.) au-delĂ  de ses pins (2026-09-06)** : le nom d'un `Slot` (`SlotName`) et les filtres de branche d'un `Layered Blend Per Bone` (`LayerSetup[i].BranchFilters`) ne sont PAS des pins — ce sont des propriĂ©tĂ©s du sous-struct `Node` (le `FAnimNode_*` runtime) portĂ© par le `AnimGraphNode_*`. Invisibles via `get_node_infos`/`create_node`/`set_pin_value`. AccĂšs uniquement via Python : `node.get_editor_property("Node")` renvoie le struct runtime, dont les champs se lisent/Ă©crivent avec `get_editor_property`/`set_editor_property` en snake_case (`slot_name`, `layer_setup`, `blend_weights`...) — mais pour un ARRAY de structs imbriquĂ©s (ex. `layer_setup: [InputBlendPose]`, chaque `InputBlendPose.branch_filters: [BranchFilter]`), modifier un Ă©lĂ©ment en place ne suffit pas : construire les structs indĂ©pendamment (`unreal.BranchFilter()`, `unreal.InputBlendPose()`), les remplir via `set_editor_property`, PUIS rĂ©assigner le tableau complet sur le struct parent (`ibp.set_editor_property("branch_filters", [bf])`, puis `node_struct.set_editor_property("layer_setup", [ibp])`, puis `node.set_editor_property("Node", node_struct)`) — une mutation in-place sans cette rĂ©assignation finale ne persiste pas. `to_dict()` sur ces structs renvoie souvent `{}` de façon trompeuse mĂȘme quand les propriĂ©tĂ©s sont bien accessibles individuellement via `get_editor_property` — ne pas se fier Ă  `to_dict()` pour vĂ©rifier un struct AnimGraph, toujours relire chaque propriĂ©tĂ© nommĂ©ment.
- Le cache de dĂ©couverte de `find_node_types` pour un AUTRE Blueprint rĂ©fĂ©rençant fraĂźchement une fonction ajoutĂ©e ailleurs (ex. `Class|InventoryPlayer|MaNouvelleFonction` cherchĂ© depuis un Widget qui possĂšde une variable de type `Inventory_Player`) peut rester pĂ©rimĂ© et ne rien retourner mĂȘme aprĂšs compilation+sauvegarde de la classe modifiĂ©e — `create_node` avec le `type_id` dĂ©duit par convention (`Class|<NomClasseSansUnderscore>|<NomFonction>`) fonctionne quand mĂȘme ; ne pas conclure Ă  une impossibilitĂ© sur la seule base d'un `find_node_types` vide.
- `unreal.get_editor_subsystem(unreal.EditorActorSubsystem).get_all_level_actors()` (et plus gĂ©nĂ©ralement les APIs `EditorActorSubsystem`) redevient **silencieusement vide** pendant qu'une session PIE est active — ces APIs ciblent le monde Ă©diteur, pas le monde PIE. Pour inspecter/piloter des acteurs PENDANT une session PIE (vĂ©rifier qu'une IA se dĂ©place, lire une variable en temps rĂ©el), utiliser `editor_toolset.toolsets.scene.SceneTools.find_actors` (qui rĂ©sout correctement vers le monde `UEDPIE_0_<NomNiveau>`) puis `unreal.load_object(None, refPath)` sur le chemin PIE retournĂ©.
- **`write_graph_dsl` (réécriture complĂšte d'un graphe) casse sur 2 instances DIFFÉREMMENT TYPÉES d'un mĂȘme nƓud wildcard enum (2026-09-06)** : `Utilities|Enum|NotEqual(Enum)`/`Equal(Enum)` sont des nƓuds gĂ©nĂ©riques dont le type se fixe au premier pin connectĂ© — un graphe EXISTANT peut trĂšs bien contenir plusieurs instances typĂ©es diffĂ©remment (constatĂ© sur `Get_MMInterruptMode` : 3× `NotEqual(Enum)` + 2× `Equal(Enum)`, types `E_MovementMode`/`E_MovementState`/`E_Gait` mĂ©langĂ©s, qui coexistent sans problĂšme). Mais **reconstruire ce mĂȘme graphe en un seul appel `write_graph_dsl`** en y ajoutant une NOUVELLE instance sur un type enum diffĂ©rent (ex. `Weapon_Equip`) Ă©choue systĂ©matiquement avec `Could not connect pin ReturnValue to B`, quel que soit l'ordre des clauses — le compilateur DSL semble partager le typage wildcard entre toutes les instances créées dans une mĂȘme passe, contrairement au graphe dĂ©jĂ  compilĂ© oĂč chaque instance est indĂ©pendante. Heureusement l'Ă©chec est bien transactionnel (`read_graph_dsl` confirme le graphe intact aprĂšs l'erreur, aucun Ă©tat partiel). Fix : ne PAS passer par une réécriture DSL complĂšte pour ce genre d'ajout — construire les nƓuds un par un (`create_node` pour les 2 `Variables|Default|GetXxx` + le nouveau `NotEqual(Enum)`, `connect_pins` pour les typer), puis brancher le rĂ©sultat sur le nƓud logique existant. Un nƓud `Math|Boolean|ORBoolean`/`ANDBoolean` (`K2Node_CommutativeAssociativeBinaryOperator`) accepte nativement un pin de PLUS via `add_node_pin` (retourne le nouveau `PinID`, ex. `C` aprĂšs `A`/`B`) — pas besoin de recrĂ©er tout l'arbre logique, juste ajouter le pin et y connecter la nouvelle condition. `read_graph_dsl` n'affiche ensuite QUE les 2 premiers pins d'un `or`/`and` Ă  3+ entrĂ©es (limite d'affichage du lecteur DSL) — vĂ©rifier un ajout de ce type via `get_node_infos` sur le nƓud lui-mĂȘme, pas via une relecture DSL qui semblerait ignorer l'Ă©dition.
- **Architecture Motion Matching — pourquoi une nouvelle base de donnĂ©es "valide" peut ĂȘtre ignorĂ©e indĂ©finiment (2026-09-06)** : `Update_MotionMatching` (fonction ABP thread-safe, tourne chaque frame) fait `EvaluateChooser → SetValidDatabases → MotionMatchingAnimNodeLibrary.SetDatabasesToSearch(node, databases, InterruptMode)` — le 3ᔉ paramĂštre (`Get_MMInterruptMode`, une fonction sĂ©parĂ©e) contrĂŽle si le nƓud `AnimGraphNode_MotionMatching` a le DROIT de remettre en cause la pose en cours (`DoNotInterrupt` = non, il continue de jouer/boucler l'ancienne sĂ©lection MÊME SI elle n'appartient plus Ă  `DatabasesToSearch` ; `InterruptOnDatabaseChange(AndInvalidateContinuingPose)` = oui, force une recherche complĂšte). **Changer `ValidDatabases` ne suffit donc JAMAIS Ă  lui seul Ă  faire rĂ©agir Motion Matching** si `Get_MMInterruptMode` ne dĂ©tecte pas la transition responsable — vĂ©rifier scrupuleusement, pour TOUT nouveau contexte de sĂ©lection (nouvelle variable de state driving un chooser), qu'il existe bien une condition correspondante dans cette fonction (schĂ©ma dĂ©jĂ  en place : une paire `GetXxx`/`GetXxxLastFrame`, mise Ă  jour chaque frame dans `Update_States`, comparĂ©e avec `Utilities|Enum|NotEqual(Enum)` puis OR-Ă©e dans la condition racine). Diagnostic fiable : `obj dump` sur l'instance `AnimInstance` vivante en PIE (`unreal.SystemLibrary.execute_console_command(actor, 'obj dump "<chemin_instance>"')`, rĂ©sultat lu via `EditorToolset.LogsToolset.GetLogEntries`) expose le struct COMPLET `AnimGraphNode_MotionMatching=(...)`, notamment `MotionMatchingState.SearchResult.(SelectedAnim|SelectedDatabase|bIsContinuingPoseSearch|SearchCost)` et `AnimPlayers[].InternalTimeAccumulator` (qui doit AVANCER entre deux dumps espacĂ©s dans le temps — sinon la pose est rĂ©ellement figĂ©e, pas juste "en cours de transition").
- **`PoseSearchDatabase` — Ă©chec silencieux de build d'index et non-avancement figĂ© sur une frame (2026-09-06)** : un `PoseSearchDatabase` en mode `PCAKD_TREE` (valeur par dĂ©faut) peut Ă©chouer Ă  construire son index dĂšs le chargement du projet si son jeu d'animations sources est trop petit/peu variĂ© pour que le calcul PCA converge — log `LogPoseSearch: Error: <hash> - <NomDatabase> BuildIndex Failed because of 'No Convergence' while calculating PCA data` (visible uniquement via `GetLogEntries` sur `LogPoseSearch`, jamais remontĂ© cĂŽtĂ© Blueprint/PIE). Tant que l'index reste dans cet Ă©tat, TOUTE recherche dessus Ă©choue en silence (`SearchCost=FLT_MAX`, `SelectedAnim=None`) et spam `LogPoseSearch: Warning: UPoseSearchLibrary::MotionMatch - some searches have been skipped, since databases AsyncBuildIndex are in still in progress or failed` Ă  chaque tick. Fix testĂ© et qui fonctionne : `db.set_editor_property("PoseSearchMode", unreal.PoseSearchMode.BRUTE_FORCE)` (au lieu de `PCAKD_TREE`) + sauvegarde (`EditorAssetLibrary.save_asset(path, only_if_is_dirty=False)` — nĂ©cessite PIE ARRÊTÉ, sinon retourne `False` sans erreur) — `BRUTE_FORCE` n'a pas besoin de PCA et convient trĂšs bien Ă  une petite database (coĂ»t de recherche linĂ©aire nĂ©gligeable Ă  cette Ă©chelle). **Second piĂšge indĂ©pendant, NON contournable trouvĂ© Ă  ce jour** : le tableau `DatabaseAnimationAssets` (alias historique `AnimationAssets`) d'un `PoseSearchDatabase` est protĂ©gĂ© en lecture ET Ă©criture pour TOUTE mĂ©thode testĂ©e (`get_editor_property`/`set_editor_property` Python, ET `ObjectTools.get_properties`/`set_properties` en C++ natif — qui pourtant contournent d'autres restrictions Python) ; seule la lecture BRUTE via `obj dump "<chemin>.<NomAsset>"` (PAS juste `<chemin>` seul, qui ne dump que le Package) expose son contenu texte (`DatabaseAnimationAssets(i)=(AnimAsset=...,SamplingRange=(Min=...,Max=...),...)`), sans Ă©quivalent en Ă©criture connu. **ATTENTION Ă  la sĂ©mantique de `SamplingRange`** (erreur de diagnostic commise puis corrigĂ©e le 2026-09-06) : `(Min=0, Max=0)` ne signifie PAS « plage vide » mais « ÉCHANTILLONNER TOUT LE CLIP » — c'est la valeur NORMALE, utilisĂ©e par toutes les databases saines du projet (vĂ©rifiĂ© sur `PSD_Dense_Stand_Walk_Loops`, `PSD_Dense_Stand_Idles`). Le cas rĂ©ellement pathologique est `Min == Max != 0`, en particulier `(longueur_du_clip, longueur_du_clip)` : fenĂȘtre de largeur nulle collĂ©e Ă  la FIN du clip. Double consĂ©quence : (a) plus rien Ă  lire, et (b) `FPoseSearchDatabaseSequence::IsLooping()` exige `SamplingRange == (0,0)` EN PLUS de `AnimSequence->bLoop`, donc l'asset devient non-bouclable (`bLoopAnimation=False` dans le dump du nƓud) mĂȘme si l'AnimSequence source est bien `Loop=True` — d'oĂč un gel sur une frame unique. SymptĂŽme observable : `AnimPlayers[].InternalTimeAccumulator` identique entre deux `obj dump` espacĂ©s de plusieurs secondes rĂ©elles en PIE. Correction = remettre la plage Ă  `0 / 0` Ă  la main dans l'Ă©diteur Pose Search Database.
- **PIÈGE CRITIQUE — `EditorAssetLibrary.consolidate_assets()` fait planter l'Ă©diteur sur certains types de classes (2026-09-08, dĂ©doublonnage de l'import Game Animation Sample)** : cette fonction (fusionne toutes les rĂ©fĂ©rences d'un doublon vers l'asset gardĂ©, supprime le doublon, laisse un `ObjectRedirector`) est **fiable Ă  100% sur les assets de donnĂ©es pures** (UserDefinedEnum, UserDefinedStruct, CurveFloat, ChooserTable, CameraRigAsset, CameraAsset, ControlRigBlueprint, ProxyAsset — testĂ© sur ~85 assets sans un seul Ă©chec) mais **crashe le processus Ă©diteur de façon reproductible** (`Assertion failed: NewEditorElement.Private_GetInternalData()->GetRefCount() <= 1 ... The old and new element both have external references!`, `LogPython: Error: Python execution caused a crash (exception code: 0xC0000005)` — l'Ă©diteur entier finit par `Engine exit requested`, pas juste l'interprĂ©teur Python) sur les **ActorComponent Blueprints activement utilisĂ©s comme composant** (constatĂ© sur `AC_PreCMCTick`, `AC_SmartObjectAnimation`) et sur les **Blueprint Interfaces largement implĂ©mentĂ©es** (`BPI_SandboxCharacter_Pawn`). Les Blueprints "de classe" simples (GameMode, modes de mouvement `Mover`, transitions) consolident sans problĂšme. **Ne jamais retenter `consolidate_assets` une seconde fois sur le mĂȘme asset aprĂšs un premier crash** — utiliser Ă  la place, dans l'ordre de prĂ©fĂ©rence : (1) `EditorAssetLibrary.delete_asset()` en brut si `AssetRegistry.get_referencers()` confirme que plus rien ne rĂ©fĂ©rence le doublon (le plus sĂ»r, fonctionne mĂȘme sur les types Ă  risque — mais peut prendre 1 Ă  4 minutes sur un projet avec des dizaines de milliers d'assets, l'appel `execute_python_code` timeout cĂŽtĂ© client mais l'opĂ©ration continue et rĂ©ussit cĂŽtĂ© serveur : revĂ©rifier via `does_asset_exist` plutĂŽt que de conclure Ă  un Ă©chec sur un timeout) ; (2) si des rĂ©fĂ©rences externes subsistent, les repointer manuellement d'abord — `BlueprintTools.set_parent()` pour un reparentage, `BlueprintTools.retarget_node_class(node, old_class, new_class)` pour un nƓud de cast/appel de fonction/Ă©vĂ©nement avec une classe figĂ©e Ă  l'ancien doublon (fonctionne bien, contrairement Ă  `consolidate_assets`) — puis `delete_asset` une fois les rĂ©fĂ©renceurs Ă  zĂ©ro. **Effet de bord du crash** : le processus Ă©diteur entier redĂ©marre, ce qui **remet l'interprĂ©teur Python Ă  zĂ©ro mais PAS l'Ă©tat sauvegardĂ© sur disque** — toute opĂ©ration dĂ©jĂ  sauvegardĂ©e (compile+save) avant le crash est conservĂ©e ; seule la commande en cours au moment du crash est perdue. Toujours vĂ©rifier `is_in_pie`/un `print("ping")` trivial aprĂšs CE type d'erreur avant de continuer : contrairement Ă  une simple corruption d'interprĂ©teur (`SystemError: unknown opcode`, rĂ©cupĂ©rable seulement par redĂ©marrage Ă©diteur elle aussi), celle-ci relance le processus tout seul en quelques dizaines de secondes.
- **Corollaire — reparenter un Blueprint dont les enfants rĂ©fĂ©rencent des COMPOSANTS PAR NOM (2026-09-08)** : si un Blueprint enfant (ex. `SandboxCharacter_Mover_Ragdoll`) a des nƓuds `Variables|Default|GetXxx`/`SetXxx` qui lisent des variables membres du PARENT (ex. `SkeletalMesh`, `Capsule`, `CharacterMover` — des variables de CACHE exposant les composants SCS rĂ©els, PAS les composants eux-mĂȘmes) et qu'on reparente vers un parent qui n'a pas ces variables sous ce nom exact, la compilation Ă©choue avec `The property associated with <Nom> could not be found`. **Fix simple et Ă  fort effet de levier** : ajouter au NOUVEAU parent des variables membres du mĂȘme nom/type exacts (`BlueprintTools.add_object_variable`/`add_struct_variable`/`add_variable`) — TOUS les nƓuds orphelins portant ce nom, dans TOUTES les fonctions de TOUS les enfants, se re-rĂ©solvent automatiquement Ă  la compilation suivante, sans toucher un seul nƓud individuellement (testĂ© sur ~90 rĂ©fĂ©rences rĂ©parties dans 28 fonctions, rĂ©solu par l'ajout de 8 variables). Il n'est PAS nĂ©cessaire que ces variables soient effectivement peuplĂ©es/cĂąblĂ©es pour que la compilation rĂ©ussisse — seule leur EXISTENCE avec le bon nom/type compte pour le compilateur ; les peupler (ex. dans `EventBeginPlay`) reste nĂ©cessaire pour un comportement correct Ă  l'exĂ©cution.
- **`connect_pins`/`write_graph_dsl` peuvent devenir silencieusement des no-op aprĂšs une session trĂšs longue avec plusieurs crashs Ă©diteur (2026-09-08)** : en fin de session, aprĂšs plusieurs cycles crash/redĂ©marrage, `connect_pins(PinID_output, PinID_input)` a cessĂ© de produire une erreur mais aussi de produire un effet (`get_node_infos` confirme aprĂšs coup qu'aucune des deux pins n'est connectĂ©e) — reproduit mĂȘme sur une paire de nƓuds fraĂźchement créés dans le MÊME appel Python, et mĂȘme sur un Blueprint diffĂ©rent qui avait fonctionnĂ© plus tĂŽt dans la session. Aucune cause identifiĂ©e avec certitude (suspectĂ© : Ă©tat de l'Ă©diteur dĂ©gradĂ© par l'accumulation de crashs, pas un bug systĂ©matique de l'outil — `connect_pins` a fonctionnĂ© de façon fiable pendant le reste de la session). Si `connect_pins` semble rĂ©ussir sans erreur mais que `get_node_infos` ne montre ensuite AUCUNE connexion sur les pins concernĂ©es, ne pas insister : passer Ă  une mĂ©thode alternative (repenser le fix pour n'avoir besoin que d'ajouts de variables/fonctions, voir point ci-dessus) plutĂŽt que de perdre du temps Ă  re-essayer la mĂȘme primitive.
- **Écrire dans une `ChooserTable` (colonnes) depuis un script (2026-09-06)** : `ColumnsStructs` se LIT en Python (`get_editor_property` + `.export_text()` sur chaque `InstancedStruct`) mais ne s'ÉCRIT PAS par cette voie — `InstancedStruct.import_text(nouveau_texte)` renvoie `True` en mentant (le contenu de l'Ă©lĂ©ment reste inchangĂ©), et le `set_editor_property("ColumnsStructs", ...)` qui suit ne remonte donc rien, sans lever d'exception. **Voie qui fonctionne** : le toolset Python `editor_toolset.toolsets.object.ObjectTools` (importable directement : `from editor_toolset.toolsets.object import ObjectTools`), qui expose la table en JSON — `json.loads(ObjectTools.get_properties(chooser, ["ColumnsStructs"]))`, modification du dict (ex. `col["rowValuesWithAny"][i] = "MatchAny"`), puis `ObjectTools.set_properties(chooser, json.dumps(data))` qui renvoie `True` et applique rĂ©ellement. `ResultsStructs` (rĂ©sultats des lignes) reste inaccessible par toutes les voies testĂ©es : on peut donc MODIFIER une ligne existante mais pas en AJOUTER une nouvelle par script.
- **`EditorAssetLibrary.save_asset`/`save_loaded_asset` peuvent renvoyer `False` alors que l'asset est parfaitement sauvegardable, PIE arrĂȘtĂ© et fichier non-verrouillĂ©** (constatĂ© sur `CHT_PoseSearchDatabases_Dense` aprĂšs une Ă©criture via `ObjectTools.set_properties`, probablement parce que le package n'est pas marquĂ© dirty par cette voie). Ne pas en conclure Ă  un Ă©chec : passer par `unreal.EditorLoadingAndSavingUtils.save_packages([obj.get_outermost()], False)` qui renvoie `True` et met bien Ă  jour le `mtime` du `.uasset` (Ă  vĂ©rifier via `os.path.getmtime`, seule preuve fiable d'Ă©criture disque).
- **Outillage dĂ©couvert le 2026-09-13 (rĂ©paration post-migration)** : (a) `BlueprintEditorLibrary.compile_blueprint` / `BlueprintTools.compile_blueprint` ne lĂšvent AUCUNE exception sur erreur de compilation — encadrer les compilations de marqueurs `unreal.log_warning("MARQUEUR")` puis lire `Saved/Logs/ProjectFv1.log` entre les marqueurs (`LogBlueprint: Error`). (b) `BlueprintTools.set_pin_value` (et d'autres outils du toolset) peut SAUVEGARDER l'asset sur disque sans le dire — sauvegarder le fichier avant d'expĂ©rimenter. (c) `retarget_node_class` / `change_member_variable_type` ne rafraĂźchissent pas les types de pins en cache (erreurs « X is not compatible with X ») : sauvegarder, `EditorLoadingAndSavingUtils.reload_packages`, recompiler — le rechargement reconstruit les nƓuds. (d) Le struct `PinID` n'est pas exposĂ© en Python : pour `connect_pins`/`break_pins`, passer par `ProgrammaticToolset.execute_tool_script` (appel `get_execution_environment` requis avant) avec des PinID JSON `{direction, index_id, node:{refPath}}`. (e) Les pins d'un nƓud `Evaluate Chooser` portent le NOM de l'asset struct de contexte : changer/renommer/fusionner la struct laisse des pins orphelines liĂ©es (« In use pin ... no longer exists ») Ă  rebrancher. (f) `EditorAssetLibrary.consolidate_assets` sur des `UserDefinedStruct` sauvegarde automatiquement les rĂ©fĂ©renceurs et crĂ©e les redirecteurs. (g) `ChooserTable.ContextData` s'Ă©crit via `ObjectTools.set_properties` (JSON `_structType`/`class`/`struct` → `refPath`) + `EditorLoadingAndSavingUtils.save_packages`. (h) Une rĂ©fĂ©rence passant par un `.ini` (ex. `GameplayTagTableList`) est invisible pour l'Asset Registry : vĂ©rifier `Config/` avant de dĂ©clarer un asset « sans rĂ©fĂ©rence ».
- **PIÈGE CRITIQUE — `consolidate_assets` sur une UserDefinedEnum (probablement aussi une UserDefinedStruct) vide silencieusement les types de variables des rĂ©fĂ©renceurs (2026-09-14)** : les rĂ©fĂ©rences dures sont réécrites, mais le `PinSubCategoryObject` des variables membres (et types de valeur de map) typĂ©es par l'enum fusionnĂ© tombe Ă  `None` : la variable devient un simple `byte`. La classe compilĂ©e garde le bon type, donc tout compile. Au rechargement suivant du package (les rĂ©fĂ©renceurs sont auto-sauvegardĂ©s par la fusion), les liens devenus incompatibles sont supprimĂ©s, des nƓuds `Equal/NotEqual (Enum)` redeviennent Wildcard et l'Ă©diteur insĂšre des `CastByteToEnum`. Une entrĂ©e qui garde une valeur littĂ©rale compile toujours : la logique est fausse SANS erreur (ex. `SandboxCharacter_Mover_ABP` : 19 variables vidĂ©es → 2259 erreurs au rechargement ; `BP_NotifyState_EarlyTransition` : 5 liens perdus dont 1 sans erreur). Les `K2Node_SwitchEnum` ne reconstruisent leurs sorties qu'au rechargement du package. ProcĂ©dure : sauvegarder tous les rĂ©fĂ©renceurs avant la fusion ; juste aprĂšs et AVANT tout rechargement, scanner `BlueprintEditorLibrary.get_member_variable_type(bp, v).export_text()` (`PinSubCategoryObject=None`) et retyper via `change_member_variable_type` ; si un rĂ©fĂ©renceur a dĂ©jĂ  Ă©tĂ© rechargĂ© cassĂ©, le restaurer depuis la sauvegarde (Ă©diteur fermĂ©) ou comparer ses liens Ă  un jumeau intact (`BlueprintTools.get_node_infos`), rebrancher et supprimer les casts (`delete_node`) ; valider par `reload_packages` d'UN seul package Ă  la fois (jamais un Blueprint en mĂȘme temps qu'un Blueprint dont il dĂ©pend). Pour des Blueprints entiers, prĂ©fĂ©rer un dĂ©placement Ă  une fusion.
- **CRASH ÉDITEUR — handles Python sur des nƓuds supprimĂ©s (2026-09-14)** : `BlueprintTools.delete_node` puis `compile_blueprint` dans le MÊME `exec`, alors que des variables Python gardent les handles de nƓuds / structs de pins (`get_node_infos`) des nƓuds supprimĂ©s → `EXCEPTION_ACCESS_VIOLATION` dans python311.dll et fermeture de l'Ă©diteur. Fix qui a fonctionnĂ© : exĂ©cuter l'Ă©dition, la compilation/sauvegarde et tout ce qui dĂ©clenche un GC (suppression d'assets, `reload_packages`) dans des callbacks `register_slate_post_tick_callback` SÉPARÉS, chacun dans un dictionnaire de globals neuf, avec `scope.clear()` + `gc.collect()` entre les Ă©tapes et `unreal.SystemLibrary.collect_garbage()` au dĂ©but de l'Ă©tape suivante. Ne sauvegarder qu'aprĂšs une vĂ©rification : un crash ne laisse alors rien de corrompu sur disque. Relance : `E:\UE_5.8\Engine\Binaries\Win64\UnrealEditor.exe "E:\projet\ProjectFv1\ProjectFv1.uproject"` (MCP disponible ~20 s aprĂšs).
- **Asset Registry pĂ©rimĂ© aprĂšs sauvegarde + rechargement (2026-09-14)** : `get_dependencies`/`get_referencers` peuvent encore lister une rĂ©fĂ©rence qu'un asset n'a plus (ex. `PC_Sandbox` → widget de dĂ©mo retirĂ©, alors que le `.uasset` ne contient plus ce nom). Forcer `AssetRegistry.scan_paths_synchronous([dossier], True)` avant de vĂ©rifier des rĂ©fĂ©renceurs.
- **`EditorAssetLibrary.delete_asset` sur une map non chargée** (`/Game/dev_map`) peut renvoyer `True` en laissant le `.umap` sur disque et une dépendance fantÎme dans l'Asset Registry (qui ferait recharger/réenregistrer la map lors d'un renommage de ses dépendances). Vérifier le fichier sur disque ; s'il reste, le supprimer puis `scan_files_synchronous([fichier], True)`.
- **Assigner un squelette à une animation qui n'en a pas (2026-09-14)** : `AnimSequence.Skeleton` est en lecture seule pour `set_editor_property` ET refusé par `ObjectTools.set_properties` ; aucun service VibeUE (`SkeletonService`, `AnimSequenceService`, `AnimMontageService`) ne l'expose (lecture/compatibilité seulement). Seule voie : ouvrir l'asset dans l'éditeur, qui propose de choisir un squelette, puis sauvegarder. Avant, vérifier la compatibilité : `AnimationLibrary.get_animation_track_names(anim)` comparé aux os du mesh cible (`SkinnedMeshComponent.get_num_bones`/`get_bone_name` sur le composant mesh du CDO du personnage).
- **Détecter des régressions d'assets binaires via git** : `git show <commit>:<fichier> | git lfs smudge` restitue l'ancien `.uasset` ; comparer l'ensemble des chaßnes `/Game/...` (table des noms) entre ancienne et nouvelle version révÚle les références perdues (lignes de chooser, bases d'un schéma, BPI d'un canal) sans ouvrir l'éditeur.
- **DĂ©placer un dossier via `AssetTools.rename_assets`** (liste d'`AssetRenameData`, un seul appel pour tout le lot) réécrit et sauvegarde lui-mĂȘme les rĂ©fĂ©renceurs, sans laisser de redirecteur quand tous sont chargeables (112 assets en ~32 s le 2026-09-14). Le coĂ»t dĂ©pend du nombre de rĂ©fĂ©renceurs : un notify utilisĂ© par des milliers d'animations oblige Ă  toutes les rĂ©enregistrer.
---
## 10. Maintenir ce document Ă  jour
Ce document **doit toujours reflĂ©ter l'Ă©tat rĂ©el du projet**. Un skill dĂ©diĂ© — **`maintain-architecture-doc`** (`.claude/skills/maintain-architecture-doc/SKILL.md`) — rappelle systĂ©matiquement aux modĂšles d'IA de :
1. Consulter ce document avant de modifier la structure du projet.
2. Le mettre à jour aprÚs toute création/suppression/déplacement/renommage de fichier ou dossier important.
3. Ajouter une description pour tout nouveau fichier important.
4. Retirer les entrées obsolÚtes (fichiers supprimés).
5. Corriger les chemins aprÚs un déplacement.
6. Mettre à jour les descriptions quand un systÚme évolue significativement.
7. Vérifier périodiquement la cohérence entre ce document et l'arborescence réelle.
8. Ne jamais laisser ce document dans un état sciemment obsolÚte aprÚs une modification de structure.
**RĂšgle de detail** : un fichier "important" Ă  documenter est un Blueprint/Widget/Data Asset/Struct/Enum/Input Action qui porte de la **logique ou des donnĂ©es de gameplay**. Les assets purement artistiques individuels (textures, meshes, clips d'animation, miniatures) n'ont pas besoin d'entrĂ©e dĂ©diĂ©e — il suffit que leur **dossier parent** soit listĂ© et dĂ©crit (cf. §8).
*Historique de nettoyage :*
- *2026-09-04 : `Content/Systems/Blueprints/d/SandboxCharacter_CMC_ABP` renommé/déplacé vers `Content/Systems/Blueprints/SandboxCharacter_CMC_ABP` via l'outil de renommage Unreal (les 7 références ont été recùblées automatiquement, compilation vérifiée OK).*
- *2026-09-04 : suppression de 8 redirecteurs Unreal morts (résidus de renommages passés, 0 référence chacun, confirmés sans danger) : `Content/Systems/Stats/ACStats.uasset`, `Content/Systems/Movement/BPC_ClimbingComponent.uasset`, `Content/Systems/Movement/ClimbingMaterial.uasset`, `Content/Systems/Movement/CR_ClimbingGraphV2.uasset`, `Content/Systems/Movement/CR_Mannequin_BasicFootIK.uasset`, `Content/BP_TickManager.uasset`, `Content/dqs.uasset`, `Content/E_WeaponType.uasset`. Les vraies versions de ces assets restent à `Content/Systems/Movement/Climbing/`, `Content/Core/Tick/BP_TickManager` et `Content/Data/Enums/E_WeaponType`.*
- *2026-09-13 : rĂ©paration aprĂšs la migration `Project_F` → `ProjectFv1` (voir §8.2) — 782 fichiers UltraDynamicSky restaurĂ©s depuis git, contexte de 7 chooser tables remis sur `Systems/Blueprints`, 3 structs identiques de `Content/Blueprints/Data/` fusionnĂ©es (redirecteurs), suppression de `Content/Blueprints/SandboxCharacter_CMC` + `_ABP` (doublon GASP), tags `Foley.Event.*`/`SmartObject.ObjectType.*` ajoutĂ©s. Compilation complĂšte : 435 Blueprints, 0 erreur.*
- *2026-09-13 (nettoyage, Ă©tapes 1-2) : suppression des personnages de dĂ©mo Echo/Paragon/UE4_Mannequin/UE5_Mannequins, de `MetaHumans/` et des BP `RetargetedCharacters/BP_*` ; rangement de `SandboxCharacter_CMC` → `Core/Character/`, `BP_GameInstance` → `Core/`, `DT_DemoTags` → `Data/` (config mise Ă  jour) ; suppression de `DT_DemoTags.T3D` et des dossiers vides (dont `Content/Collections/`, `Content/Developers/`). Compilation : 405 Blueprints, 0 erreur.*
- *2026-09-13/14 (nettoyage, étape 3) : suppression des redirecteurs morts aprÚs re-sauvegarde de leurs référenceurs (dont `Animations/anim_Swim*`, `Core/PlayerController/IA_InitiateClimb|IA_LeapFromClimb|IA_MovingClimb`, `PSD_Relaxed_Stand_Sprint_Pivots`, les redirecteurs du 2026-09-04 encore suivis par git, et les redirecteurs de structs de `Content/Blueprints/Data/`).*
- *2026-09-14 : convergence des enums du framework dupliquĂ© et rĂ©paration des Blueprints touchĂ©s (voir §8.2) ; `GM_Dev` → `SandboxCharacter_CMC`. Passe complĂšte : 405 Blueprints ; seule erreur restante `BP_Item_AdvancedRifle` (`Reload_Rifle_Hip` sans squelette, antĂ©rieure) + avertissement `Ultra_Dynamic_Weather`. Commit intermĂ©diaire (point de restauration) avant le dĂ©placement du reste de `Content/Blueprints/` vers `Content/Systems/Mover/`.*
- *2026-09-14 (isolation du framework du sample) : dĂ©mo GASP supprimĂ©e (`dev_map`, `Content/Levels/LevelPrototyping/`, `Content/Widgets/`) aprĂšs avoir dĂ©tachĂ© `AC_TraversalLogic` (branche `LevelBlock_Traversable`) et `PC_Sandbox` (widget de dĂ©mo) ; 17 redirecteurs d'enum supprimĂ©s ; 112 assets dĂ©placĂ©s sans fusion (`Content/Blueprints/` → `Systems/Mover/` sauf `AnimNotifies/`, `Aiming/` → `Systems/Aiming/`, `Content/Input/` → `Systems/Mover/Input/`, `E_WaponType` → `Data/Enums/`), rĂ©fĂ©rences réécrites, aucun redirecteur restant. ContrĂŽle aprĂšs redĂ©marrage complet de l'Ă©diteur : liens de graphe, valeurs de pins et types de variables des 66 Blueprints concernĂ©s encore existants identiques au relevĂ© pris avant le dĂ©placement (seuls Ă©carts : chemins réécrits et artefacts de l'outil de comparaison). Compilation complĂšte : 379 Blueprints, seule erreur `BP_Item_AdvancedRifle` (`Reload_Rifle_Hip` sans squelette, antĂ©rieure).*
- *2026-09-14 (reprise) : test PIE sur `MapDev1` — `GM_Dev` fait apparaĂźtre `SandboxCharacter_CMC`, aucune erreur de Blueprint Ă  l'exĂ©cution. Seuls messages, antĂ©rieurs : `Systems/Weather/UltraDynamicSky/Particles/Snow` rĂ©fĂ©rence encore l'ancien chemin `/Game/UltraDynamicSky/Particles/UDW_Niagara_Parameters`, et 4 avertissements Pose Search d'entrĂ©es vides dans `PSD_SwimmingIdle`/`PSD_SwimmingMovingSprint` (contenu inchangĂ© depuis `d29748b`, de nouveau chargĂ© grĂące au chooser restaurĂ©). Restauration depuis `d29748b` des 7 donnĂ©es Motion Matching dĂ©gradĂ©es par la migration (voir §8.2) avec fix MatchAny refait, et de `NS_Tornado`. Compilation complĂšte : 379 Blueprints, seule erreur `BP_Item_AdvancedRifle` (`Reload_Rifle_Hip` sans squelette, antĂ©rieure).*
- *2026-09-14 (mĂ©tĂ©o) : corrections du systĂšme mĂ©tĂ©o projet (`WB_WeatherControl`/`WB_WeatherZone`, voir §4 Weather). Bugs corrigĂ©s : `setWeatherbetweenZone` lisait deux fois Zone2 ; `GetNearZone` ne trouvait jamais de zone (distances locales Ă  0, poids 0/0 et inversĂ©s) ; `SetNuage`/`MakeTemp` retournaient 0 sans zone voisine (return dans la boucle) et clampaient la tempĂ©rature Ă  0–10 ; `makestate` renvoyait un Ă©tat vide (branche froide nuages ≄ 8) et ne calculait ni vent/brouillard/poussiĂšre ni humiditĂ© ; `MakeForcast` avait des pas de 0–1 s ; `UpdateWeatherstate` n'avançait jamais le temps (+0) ; rayon d'influence ≈ 900 uu et tri `Trie zone` pouvant boucler Ă  l'infini ; overlaps dĂ©clenchĂ©s par n'importe quel acteur ; saison en secondes. Nouveaux : fonctions `StepForecast`, `ApplyToUDW`, `GetZoneState` ; variables `ForecastStepMin/Max`, `InfluenceRadius`, `LastUpdateTime` (zone), `SeasonDurationScale` (contrĂŽleur). `[CoreRedirects]` UDS + 10 systĂšmes Niagara re-sauvegardĂ©s. PIE OK (zones prĂȘtes, 16 prĂ©visions, poids 0.52/0.48, UDW alimentĂ©, aucune erreur mĂ©tĂ©o). Restant : `WeatherData` des 2 zones de MapDev1 vide (0 °C / 0 %), `BP_TickManager` transmet un `DeltaTime` ~10× trop petit au contrĂŽleur (saison lente), avertissement d'attache des ChildActor UDS/UDW (mobilitĂ©). Sauvegarde des assets avant modification : `E:\projet\ProjectFv1_backup_migration_2026-09-13\weather_before_fixes_2026-09-14\`.*
- *2026-09-18 (migration cover / prone / nage du CMC vers le Mover, voir §4.2.2) : `SandboxCharacter_Mover` reçoit `AC_Cover` + `Arrow1`, les variables et fonctions de cover/prone/nage, l'enregistrement du mode « Swimming » au BeginPlay, et ses fonctions `Get_Gait`/`Get_MoveInput`/`Get_PropertiesForAnimation`/`On_PreSimulateTick` Ă©tendues. Nouveaux assets : `Systems/Mover/MovementModes/BP_MovementMode_Swimming`, `Characters/UEFN_Mannequin/Animations/MotionMatchingData/CHT_PoseSearchDatabases_Mover`. Corrections annexes : `BP_MovementMode_Walking` (vitesse/accĂ©lĂ©ration nulles pour les allures prone et nage), `MovementModeMap` (Ragdoll→Climbing et Flying→SwimmingOnSurface, sĂ©quelles de la fusion des enums — le ragdoll du pion refonctionne), `SandboxCharacter_Mover_ABP` (contexte `self` absent des `Evaluate Chooser` → branche Motion Matching inerte ; nouvelle bascule `AnimBranchIndex`). TestĂ© en PIE : prone (capsule 40 ↔ 86 via le `StanceModifier`, base `PSD_Dense_Prone_Idles` sĂ©lectionnĂ©e), nage dans `WaterBodyLake_0` (entrĂ©e/flottaison Ă  la profondeur cible/plongĂ©e/sortie, `PSD_SwimmingIdle` sĂ©lectionnĂ©e), accroupi de couverture. NON testĂ© : entrĂ©e en couverture par la touche P et dĂ©placement le long d'un vrai mur. Sauvegarde des assets avant modification : `E:\projet\ProjectFv1_backup_mover_migration_2026-09-17\`.*
- *2026-09-18 (suite) : Ă  la demande de l'utilisateur, `SandboxCharacter_Mover_ABP` passe **entiĂšrement en Motion Matching** — `Update_Logic` ne calcule plus la bascule, `AnimBranchIndex` est fixĂ© Ă  `0` (nƓuds `Select` / `Not Equal (Enum)` / `AND` / `Get Stance` supprimĂ©s, valeur par dĂ©faut de la variable mise Ă  0). Diagnostic de la **T-pose en nage** : ce n'Ă©tait pas l'ABP mais le contenu — 5 des 6 bases Pose Search de nage n'ont que des lignes Ă  `AnimAsset = None` et la table `SwimmingInDeepIdle` du chooser est vide ; seul `PSD_SwimmingMoving` est rempli. Contournement dans `CHT_PoseSearchDatabases_Mover` (lignes vers bases vides neutralisĂ©es, lignes 13/16 Ă©largies en MatchAny), dĂ©tail et procĂ©dure de correction dĂ©finitive en §4.2.2. VĂ©rifiĂ© en PIE avec MM permanent : idle debout (`PSD_Relaxed_Stand_Idles`), course avant (`PSD_Relaxed_Stand_Run_F_Starts/Loops`), prone (`PSD_Dense_Prone_Idles` → `anim_Prone_Idle`), nage surface immobile / en mouvement et nage profonde (`PSD_SwimmingMoving` → `RM_Breastroke_overwater_01_right_turn_Retargeted`).*
- *2026-09-19 (systĂšme d'armes sur le Mover, voir §4.2.3) : `AC_Weapon::Equip/Unequip` reçoivent une branche `Cast Failed` → `SandboxCharacter_Mover` (leur `Cast To SandboxCharacter_CMC` Ă©chouait en silence sur le Mover, d'oĂč `Weapon`/`isArmed` jamais renseignĂ©s). Nouvelle fonction `SandboxCharacter_Mover::SetEquippedWeapon` + variables `Weapon_Equip`/`WantToshot` sur le pion, `Weapon_Equip`/`Weapon_Equip Last Frame`/`Want Toshot` sur `SandboxCharacter_Mover_ABP` (alimentĂ©es dans `Update_PropertiesFromCharacter`, thread principal). `IA_Aim` renseigne `WantToshot`. `Update_MotionMatching` consulte aussi `CHT_PoseSearchDatabases_Mover` quand `Is Armed`, et `Get_MMInterruptMode` force l'interruption au changement d'arme. Dans `CHT_PoseSearchDatabases_Mover` : colonnes `Is Armed` et `IS Moving Object` rĂ©activĂ©es, ligne 1 (« Stand Idles Bow ») passĂ©e en `Gait = MatchAny`. TestĂ© en PIE de bout en bout (ramassage → main → anims arc → visĂ©e → tir) avec non-rĂ©gression prone/nage. `Inventory_Equipper.uasset` apparaĂźt modifiĂ© sans changement de contenu (resauvegarde automatique du MCP, mĂȘmes rĂ©fĂ©rences `/Game/`).*
- *2026-09-19 (MetaHuman en visual override, voir §4.2.4) : une premiÚre tentative mettait le corps MetaHuman directement comme mesh du pion (squelettes déclarés compatibles + sockets recopiées) ; elle a été **annulée** au profit de l'approche du sample GASP. Restauration de `ABP_GenericRetarget` depuis le commit initial (supprimé au nettoyage de la démo GASP), création de `IK_MetaHuman_test`, `RTG_UEFN_to_MetaHuman_test` et `BP_VisualOverride_MetaHuman` dans `Systems/Mover/VisualOverrides/`, `GM_Dev` reparenté sur `GM_Sandbox` (il était vide) et `VisualOverrides_Soft` renseigné, défaut du CVar `DDCvar.VisualOverride` passé de -1 à 0. Limite connue : les objets équipés restent accrochés au mannequin caché, ~12 cm d'écart avec la main du MetaHuman.*