DC1 : plugin LPAnimCore (LPAnimRedirect, LPCoupleSim) ; LifePlay ne depend plus de LPAnimMeasure (CHG-234)

Plugins/LPAnimCore (Runtime, PreDefault, toutes configurations) recoit la partie runtime
de l'ancien LPAnimMeasure de l'addon. LifePlay.Build.cs : LPAnimMeasure -> LPAnimCore.
LifePlayEditorTools depend encore de LPAnimMeasure jusqu'a DC2. Pin addon 3e9c32f.
Validation : DC1-BUILD code 0, DC1-VAL PASS (tournees Mesure identiques a SB2-REF,
controle visuel utilisateur marche et ShowCouple).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
edujiro5972 2026-10-07 18:31:01 +02:00
parent 19e704b02c
commit d8a0f859ce
18 changed files with 1103 additions and 14 deletions

View file

@ -80,6 +80,7 @@ Structure plate (pas de sous-dossier `Public/`) : `PublicIncludePaths.Add(Module
| Plugin | Cible | Remarque | | Plugin | Cible | Remarque |
|---|---|---| |---|---|---|
| `ModdingExtra` | runtime | entrée/sortie des données ; trace d'appel en catégorie `LogModdingExtra` (`Verbose`) | | `ModdingExtra` | runtime | entrée/sortie des données ; trace d'appel en catégorie `LogModdingExtra` (`Verbose`) |
| `LPAnimCore` | runtime | lot DC1 : `LPAnimRedirect` (redirection des clips) et `LPCoupleSim` (géométrie du couple), rapatriés de l'addon ; module `LPAnimCore` dans `Plugins/LPAnimCore` |
| `StreetMap` | runtime | porté LWC ; 280 villes vérifiées identiques à 4.27 (L7h) | | `StreetMap` | runtime | porté LWC ; 280 villes vérifiées identiques à 4.27 (L7h) |
| `EnhancedInput` | runtime | système d'entrée actif ; exécute les 34 actions héritées de `DefaultInput.ini` | | `EnhancedInput` | runtime | système d'entrée actif ; exécute les 34 actions héritées de `DefaultInput.ini` |
| `McpAutomationBridge` | **éditeur uniquement** (`TargetAllowList: Editor`) | pilotage de l'éditeur par serveur MCP ; jamais publié (contient un jeton) | | `McpAutomationBridge` | **éditeur uniquement** (`TargetAllowList: Editor`) | pilotage de l'éditeur par serveur MCP ; jamais publié (contient un jeton) |

View file

@ -81,7 +81,7 @@ Rien n'est supprimé : un résultat précédent est déplacé dans `precedent_<d
Réalisé par `Tools/anim/AnimPipeline.ps1` (CHG-223) ; ordre réel des étapes 6 et 7 : la livraison dans le mod précède les fiches et le catalogue, puis vient la cuisson DLC. Réalisé par `Tools/anim/AnimPipeline.ps1` (CHG-223) ; ordre réel des étapes 6 et 7 : la livraison dans le mod précède les fiches et le catalogue, puis vient la cuisson DLC.
Le jeu résout les clés par `Room.Dailies` ; les clips des mods y sont ajoutés par `ULPModDailiesSubsystem` (CHG-209). Le jeu résout les clés par `Room.Dailies` ; les clips des mods y sont ajoutés par `ULPModDailiesSubsystem` (CHG-209).
`LPAnimRedirect` ne sert plus qu'à la sandbox et au personnage piloté. `LPAnimRedirect` ne sert plus qu'à la sandbox et au personnage piloté. Depuis le lot DC1, `LPAnimRedirect` et `LPCoupleSim` sont dans le plugin `LPAnimCore` du jeu (`Plugins/LPAnimCore`) ; la mesure reste dans le module `LPAnimMeasure` de l'addon.
--- ---

View file

@ -76,7 +76,7 @@ Le lot S2 sort la chaîne de mesure de `LifePlay` vers un module Runtime dédié
- **Contenu** : `LPPoseMeasure`, `LPSkeletonProfile`, `LPPoseOrchestrator`, `LPAnimRedirect` (8 fichiers déplacés) + `LPAnimMeasure.Build.cs`, `LPAnimMeasure.h/.cpp` (catégorie de journal `LogLPAnimMeasure`, ex-`LogLifePlay` pour les lignes `POSET` / `PROFIL` / `ORCH` / `MESURE`). - **Contenu** : `LPPoseMeasure`, `LPSkeletonProfile`, `LPPoseOrchestrator`, `LPAnimRedirect` (8 fichiers déplacés) + `LPAnimMeasure.Build.cs`, `LPAnimMeasure.h/.cpp` (catégorie de journal `LogLPAnimMeasure`, ex-`LogLifePlay` pour les lignes `POSET` / `PROFIL` / `ORCH` / `MESURE`).
- **Exports** : `LPANIMMEASURE_API` remplace `LIFEPLAY_API` ; 6 symboles ajoutés pour `LPPairSandbox.cpp` (`KeyBones`, `LogReport`, `TFitReport`, `Run`, `Restore`, `SampleAt`) ; les structs de données restent header-only. - **Exports** : `LPANIMMEASURE_API` remplace `LIFEPLAY_API` ; 6 symboles ajoutés pour `LPPairSandbox.cpp` (`KeyBones`, `LogReport`, `TFitReport`, `Run`, `Restore`, `SampleAt`) ; les structs de données restent header-only.
- **Rupture du couplage** : `LPAnimRedirect` ne lit plus `LPSandboxConfig`. `FLifePlayModule::StartupModule()` (`LifePlay.cpp`) appelle `LPAnimRedirect::SetUALRoot(FLPSandboxConfig::Get().QuaterniusUALRoot)` ; défaut interne `/Game/Quaternius/UAL` (identique à celui de la config). - **Rupture du couplage** : `LPAnimRedirect` ne lit plus `LPSandboxConfig`. l'addon (`LPSandbox.cpp:17`) appelle `LPAnimRedirect::SetUALRoot(FLPSandboxConfig::Get().QuaterniusUALRoot)` (`FLifePlayModule::StartupModule()` ne l'appelle plus) ; depuis DC1, `LPAnimRedirect` et `LPCoupleSim` sont dans le plugin du jeu `Plugins/LPAnimCore` ; défaut interne `/Game/Quaternius/UAL` (identique à celui de la config).
- **Déclaration** : entrée `Runtime` / `Default` dans `LifePlay.uproject`, placée avant `LifePlay` ; `ExtraModuleNames` des deux `*.Target.cs`. - **Déclaration** : entrée `Runtime` / `Default` dans `LifePlay.uproject`, placée avant `LifePlay` ; `ExtraModuleNames` des deux `*.Target.cs`.
### Graphe de dépendances des modules ### Graphe de dépendances des modules
@ -86,7 +86,11 @@ flowchart LR
LPET["LifePlayEditorTools<br/>(Editor)"] -->|Public| LP["LifePlay<br/>(Runtime, module primaire)"] LPET["LifePlayEditorTools<br/>(Editor)"] -->|Public| LP["LifePlay<br/>(Runtime, module primaire)"]
LPET -->|Public<br/>FLPPose dans LPAnimChecks.h| AM["LPAnimMeasure<br/>(Runtime, nouveau S2)"] LPET -->|Public<br/>FLPPose dans LPAnimChecks.h| AM["LPAnimMeasure<br/>(Runtime, nouveau S2)"]
LP -->|Public<br/>LPPairSandbox, LPDirectControl| AM LP -->|Public<br/>LPPairSandbox, LPDirectControl| AM
LP -.->|"StartupModule : SetUALRoot(QuaterniusUALRoot)"| AM LP -->|Public DC1| AC["LPAnimCore<br/>(plugin du jeu : LPAnimRedirect, LPCoupleSim)"]
AM -->|Private DC1| AC
SB["LPSandbox<br/>(addon)"] -->|DC1| AM
SB -->|DC1| AC
SB -.->|"LPSandbox.cpp:17 : SetUALRoot(QuaterniusUALRoot)"| AC
AM -->|Public| ENG["Core, CoreUObject, Engine"] AM -->|Public| ENG["Core, CoreUObject, Engine"]
AM -->|Private| JSON["Json"] AM -->|Private| JSON["Json"]
``` ```
@ -96,8 +100,11 @@ Règles : `LPAnimMeasure` ne dépend ni de `LifePlay` ni de `LifePlayEditorTools
| De | Vers | Type | Motif | | De | Vers | Type | Motif |
|---|---|---|---| |---|---|---|---|
| `LifePlay` | `LPAnimMeasure` | Public | `LPPairSandbox.h/.cpp`, `LPDirectControlSubsystem.cpp`, `LifePlay.cpp` | | `LifePlay` | `LPAnimMeasure` | Public | `LPPairSandbox.h/.cpp`, `LPDirectControlSubsystem.cpp`, `LifePlay.cpp` |
| `LifePlay` | `LPAnimCore` | Public | DC1 : `LPAnimRedirect`, `LPCoupleSim` (plugin du jeu `Plugins/LPAnimCore`) |
| `LPAnimMeasure` | `LPAnimCore` | Private | DC1 : la mesure utilise `LPAnimRedirect` |
| `LPSandbox` | `LPAnimMeasure`, `LPAnimCore` | Public | DC1 : mesure et redirection / géométrie du couple |
| `LifePlayEditorTools` | `LifePlay` | Public | existant | | `LifePlayEditorTools` | `LifePlay` | Public | existant |
| `LifePlayEditorTools` | `LPAnimMeasure` | Public | `LPAnimChecks.h` expose `FLPPose` ; commandlets `LPAnimEval`, `LPAnimCatalog`, `LPAnimTwist` | | `LifePlayEditorTools` | `LPAnimMeasure` | Public | `LPAnimChecks.h` expose `FLPPose` ; commandlets `LPAnimEval`, `LPAnimCatalog`, `LPAnimTwist` (jusqu'à DC2) |
| `LPAnimMeasure` | `Core`, `CoreUObject`, `Engine` / `Json` | Public / Privé | animations, squelettes ; lecture de `Tools/anim/profiles/*.json` | | `LPAnimMeasure` | `Core`, `CoreUObject`, `Engine` / `Json` | Public / Privé | animations, squelettes ; lecture de `Tools/anim/profiles/*.json` |
| `LPAnimMeasure` | `LifePlay`, `LifePlayEditorTools` | **aucune** | évite un cycle de modules | | `LPAnimMeasure` | `LifePlay`, `LifePlayEditorTools` | **aucune** | évite un cycle de modules |
@ -117,12 +124,12 @@ D:\Game\
- **Liaison** : `LifePlay.uproject` déclare `"AdditionalPluginDirectories": ["../LifePlay-Sandbox/Plugins"]` (chemin relatif au `.uproject`) et active `LPSandbox` dans `Plugins`. UBT et l'éditeur y trouvent `LPSandbox.uplugin` (modules `LPSandbox` et `LPAnimMeasure`) ; les DLL du plugin sont compilées avec le jeu (`Build.bat LifePlayEditor Win64 Development`). - **Liaison** : `LifePlay.uproject` déclare `"AdditionalPluginDirectories": ["../LifePlay-Sandbox/Plugins"]` (chemin relatif au `.uproject`) et active `LPSandbox` dans `Plugins`. UBT et l'éditeur y trouvent `LPSandbox.uplugin` (modules `LPSandbox` et `LPAnimMeasure`) ; les DLL du plugin sont compilées avec le jeu (`Build.bat LifePlayEditor Win64 Development`).
- **Pourquoi pas un submodule** : l'addon est un `uproject` mince qui contient le plugin sous `Plugins/LPSandbox/`. Un submodule monté sur `Plugins/LPSandbox` du jeu donnerait `Plugins/LPSandbox/Plugins/LPSandbox/` (plugin imbriqué, non scanné au bon niveau) ; un submodule `External/LifePlay-Sandbox` dupliquerait l'addon à côté du sibling et rendrait ambigu l'exemplaire chargé. Un clone sibling évite les deux problèmes. - **Pourquoi pas un submodule** : l'addon est un `uproject` mince qui contient le plugin sous `Plugins/LPSandbox/`. Un submodule monté sur `Plugins/LPSandbox` du jeu donnerait `Plugins/LPSandbox/Plugins/LPSandbox/` (plugin imbriqué, non scanné au bon niveau) ; un submodule `External/LifePlay-Sandbox` dupliquerait l'addon à côté du sibling et rendrait ambigu l'exemplaire chargé. Un clone sibling évite les deux problèmes.
- **Pin** : le jeu consomme un commit précis de l'addon, consigné à la main dans `S3-INT.md` (aujourd'hui `16905c68070992f5139dab907b41b461fb765957`, `lot/SB4-final`, SB4 ouverture par déclencheur éphémère non mémorisée ; précédents : `3d858527fccfdddd8711c7c7448fa001442e0b8a` SB2-O1 correction de `ShowRoom`, `fece7e9a0e63b3058b5d24d96d669a3c9c0e9add` SB2 bibliothèques de scène et de personnages, caméra du socle, état des modes sorti vers les mods, `82dae58380b76c45f60541fd7cb5a0a409700e10` SB1 registre de mods et barre latérale, `3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362` SB0 conception de la sandbox modulaire, `f09c42e5a2bc613cf269cce76c878461ca41c8b7` S7-T2 uplugin, `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` S5-T2 `LPCoupleSim`, `f8102647ccdebad37e4977ccbf2c51e583823a80`, S4-T3). Le pin est mis à jour (hash dans `S3-INT.md` et README de l'addon) à chaque évolution de l'addon que le jeu doit prendre. Reproduire : - **Pin** : le jeu consomme un commit précis de l'addon, consigné à la main dans `S3-INT.md` (aujourd'hui `3e9c32f1f27b1abdaf7731dc90496c27ee061718`, `lot/DC1-animcore`, DC1 `LPAnimRedirect` et `LPCoupleSim` rapatriés dans le plugin `LPAnimCore` du jeu ; précédents : `16905c68070992f5139dab907b41b461fb765957` SB4 ouverture par déclencheur éphémère non mémorisée, `3d858527fccfdddd8711c7c7448fa001442e0b8a` SB2-O1 correction de `ShowRoom`, `fece7e9a0e63b3058b5d24d96d669a3c9c0e9add` SB2 bibliothèques de scène et de personnages, caméra du socle, état des modes sorti vers les mods, `82dae58380b76c45f60541fd7cb5a0a409700e10` SB1 registre de mods et barre latérale, `3b21fd180c04f0fa49a1b5c9ef1b517ade1cb362` SB0 conception de la sandbox modulaire, `f09c42e5a2bc613cf269cce76c878461ca41c8b7` S7-T2 uplugin, `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` S5-T2 `LPCoupleSim`, `f8102647ccdebad37e4977ccbf2c51e583823a80`, S4-T3). Le pin est mis à jour (hash dans `S3-INT.md` et README de l'addon) à chaque évolution de l'addon que le jeu doit prendre. Reproduire :
```powershell ```powershell
cd D:\Game cd D:\Game
git clone https://github.com/edujiro5972/LifePlay-Sandbox.git git clone https://github.com/edujiro5972/LifePlay-Sandbox.git
git -C LifePlay-Sandbox checkout 16905c68070992f5139dab907b41b461fb765957 git -C LifePlay-Sandbox checkout 3e9c32f1f27b1abdaf7731dc90496c27ee061718
# le jeu doit être cloné à côté : D:\Game\LifePlay-master 5.5 # le jeu doit être cloné à côté : D:\Game\LifePlay-master 5.5
``` ```
@ -179,11 +186,12 @@ Le lot S5 ferme la **dérive de fork** entre la sandbox (`LPSandbox/LPCoupleSim.
- [x] **SB2-O1** : correction du défaut O1 (`etat/sandbox/SB2/SB2-O1-BUILD.md` PASS, contrôle visuel utilisateur 2026-10-07 ; tag `lot-SB2-O1-valide`). - [x] **SB2-O1** : correction du défaut O1 (`etat/sandbox/SB2/SB2-O1-BUILD.md` PASS, contrôle visuel utilisateur 2026-10-07 ; tag `lot-SB2-O1-valide`).
- [x] Reports SB2 : (O1) traité (SB2-O1, `3d858527fccfdddd8711c7c7448fa001442e0b8a` : `ShowRoom` symétrique de `HideRoom`, contrôle visuel utilisateur 2026-10-07 « c'est bon ») ; (E3) sans `lps_lib_scene`, la pièce est construite quand même : non traité, séparation physique abandonnée à la porte G-SB3 (décision utilisateur 2026-10-07). - [x] Reports SB2 : (O1) traité (SB2-O1, `3d858527fccfdddd8711c7c7448fa001442e0b8a` : `ShowRoom` symétrique de `HideRoom`, contrôle visuel utilisateur 2026-10-07 « c'est bon ») ; (E3) sans `lps_lib_scene`, la pièce est construite quand même : non traité, séparation physique abandonnée à la porte G-SB3 (décision utilisateur 2026-10-07).
- [x] **DC0** : cartographie et SPEC du découplage jeu / sandbox (`etat/sandbox/DC0/DC0-SPEC.md`, approuvée par l'utilisateur 2026-10-07) ; plan `24_Plan_Decouplage_Sandbox.md`. - [x] **DC0** : cartographie et SPEC du découplage jeu / sandbox (`etat/sandbox/DC0/DC0-SPEC.md`, approuvée par l'utilisateur 2026-10-07) ; plan `24_Plan_Decouplage_Sandbox.md`.
- [ ] **DC1** : Plugin LPAnimCore dans le jeu, LPAnimMeasure réduit à la mesure (DC1-T1..T5). - [x] **DC1** : Plugin LPAnimCore dans le jeu, LPAnimMeasure réduit à la mesure (DC1-T1..T5) (DC1-VAL PASS, contrôle visuel utilisateur 2026-10-07 ; tag addon lot-DC1-valide).
- [ ] **DC2** : Commandlets de mods vers un module éditeur de la sandbox (DC2-T1..T5). - [ ] **DC2** : Commandlets de mods vers un module éditeur de la sandbox (DC2-T1..T5).
- [ ] **DC3** : Scripts, données et sources de mods vers la sandbox (DC3-T1..T5). - [ ] **DC3** : Scripts, données et sources de mods vers la sandbox (DC3-T1..T5).
- [ ] **DC4** : Preuve « jeu seul », docs, évaluation (DC4-T1..T4). - [ ] **DC4** : Preuve « jeu seul », docs, évaluation (DC4-T1..T4).
- [ ] Reports DC0 (lot propriétaire **DC1**) : § Rupture du couplage : la mention d'un appel à SetUALRoot par FLifePlayModule::StartupModule() est périmée ; seul l'addon l'appelle (LPSandbox.cpp:17, LPSandboxMeasure.cpp:39) — voir DC0-CART b2. - [ ] Intégration des déplacements dans le jeu à améliorer (remarque utilisateur au contrôle visuel DC1, 2026-10-07) ; lot propriétaire : à définir, hors DC.
- [x] Reports DC0 (lot propriétaire **DC1**, traité : texte corrigé en § Rupture du couplage) : § Rupture du couplage : la mention d'un appel à SetUALRoot par FLifePlayModule::StartupModule() est périmée ; seul l'addon l'appelle (LPSandbox.cpp:17, LPSandboxMeasure.cpp:39) — voir DC0-CART b2.
## Lots (S0 … S8) ## Lots (S0 … S8)
| Lot | Objet (une ligne) | | Lot | Objet (une ligne) |

View file

@ -1131,6 +1131,7 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol
| CHG-155 | 2026-10-05 | ShowCouple | Un lit est reconnu par son nom. Il ne bloque un déplacement que vers le bas, pour se poser ; le retour horizontal n'est plus arrêté et le contact ne renvoie plus la boîte | après avoir quitté le lit, la boîte ne pouvait plus y revenir | revenir au test `DoesPackageExist` et au filtre du flanc seul | | CHG-155 | 2026-10-05 | ShowCouple | Un lit est reconnu par son nom. Il ne bloque un déplacement que vers le bas, pour se poser ; le retour horizontal n'est plus arrêté et le contact ne renvoie plus la boîte | après avoir quitté le lit, la boîte ne pouvait plus y revenir | revenir au test `DoesPackageExist` et au filtre du flanc seul |
| CHG-156 | 2026-10-05 | Clavier | `LPKeyboardSubsystem` : bouton « Clavier » ajouté à l'exécution dans `MainMenu.SettingsScrollBox` ; panneau pour changer, ajouter ou retirer une touche (actions et axes clavier). Écrit dans `Saved/Config/Windows`, pas dans `GameSettings.sav` | pouvoir réaffecter les touches, dont Q, sans modifier les Blueprints | retirer `LPKeyboardSubsystem.h/.cpp` et la dépendance `UMG` | | CHG-156 | 2026-10-05 | Clavier | `LPKeyboardSubsystem` : bouton « Clavier » ajouté à l'exécution dans `MainMenu.SettingsScrollBox` ; panneau pour changer, ajouter ou retirer une touche (actions et axes clavier). Écrit dans `Saved/Config/Windows`, pas dans `GameSettings.sav` | pouvoir réaffecter les touches, dont Q, sans modifier les Blueprints | retirer `LPKeyboardSubsystem.h/.cpp` et la dépendance `UMG` |
| CHG-158 | 2026-10-05 | Réglages | `LPSettingsSubsystem` : onglets Audio, Video, Lewd, General, Keyboard, Misc au-dessus de `SettingsScrollBox`. Les lignes du Blueprint restent en place et sont montrées selon leur texte ; le reste va dans Misc. Keyboard affiche la liste des touches | la liste unique ne montrait pas le clavier | retirer `LPSettingsSubsystem.h/.cpp` | | CHG-158 | 2026-10-05 | Réglages | `LPSettingsSubsystem` : onglets Audio, Video, Lewd, General, Keyboard, Misc au-dessus de `SettingsScrollBox`. Les lignes du Blueprint restent en place et sont montrées selon leur texte ; le reste va dans Misc. Keyboard affiche la liste des touches | la liste unique ne montrait pas le clavier | retirer `LPSettingsSubsystem.h/.cpp` |
| CHG-234 | 2026-10-07 | DC1 | jeu : `Plugins/LPAnimCore/*` (nouveau plugin : `LPAnimRedirect.*`, `LPCoupleSim.h`, `LPCoupleSimCore.cpp`), `LifePlay.uproject`, `Source/LifePlay/LifePlay.Build.cs`, docs (`21_Sandbox_Addon.md`, `20_Chaine_Animations.md`, `07_Architecture_5.5.md`, `etat/sandbox/S3/S3-INT.md`, `etat/sandbox/DC1/*`) ; addon `D:\Game\LifePlay-Sandbox` : `LPSandbox.uproject`, `LPSandbox.uplugin`, `LPAnimMeasure.Build.cs`, `LPAnimMeasure.h`, `LPSandbox.Build.cs`, suppression de `LPAnimRedirect.*`, `LPCoupleSim.h`, `LPCoupleSimCore.cpp` (commit `3e9c32f1f27b1abdaf7731dc90496c27ee061718`, branche `lot/DC1-animcore`, tag `lot-DC1-valide`) | lot DC1 : `LPAnimRedirect` et `LPCoupleSim` rapatriés dans le plugin `LPAnimCore` du jeu, `LPAnimMeasure` réduit à la mesure ; compilation PASS, DC1-VAL PASS, contrôle visuel utilisateur 2026-10-07 ; report DC0 SetUALRoot traité. Motif : le jeu public doit compiler sans le dépôt privé `LifePlay-Sandbox` (premier pas du découplage) | pin `16905c6` → `3e9c32f` (hash complet `3e9c32f1f27b1abdaf7731dc90496c27ee061718`) | `.\Tools\Restore.ps1 -Lot DC1` (jeu) et `.\Tools\Restore.ps1 -Lot DC1-addon -ProjectRoot D:\Game\LifePlay-Sandbox` ; pin `16905c6` |
| CHG-233 | 2026-10-07 | DC0 | `Docs/Migration/24_Plan_Decouplage_Sandbox.md` (nouveau), `Docs/Migration/etat/sandbox/DC0/DC0-CART.md`, `DC0-SPEC.md`, `DC0-VAL.md` (nouveaux) | lot DC0 : plan du découplage jeu / sandbox (lots DC0 à DC4), cartographie et SPEC approuvées par l'utilisateur le 2026-10-07 (plugin `LPAnimCore` dans le jeu, module éditeur `LPModTools` et tout l'outillage de mods dans l'addon, dépôt Sandbox privé) ; documentation seule, aucun code. Motif : le jeu public doit compiler sans le dépôt privé `LifePlay-Sandbox` (`LifePlay` et `LifePlayEditorTools` dépendent de `LPAnimMeasure`) | aucun pin (documentation seule) | supprimer ces fichiers (documentation seule) ; rien n'est commité dans le dépôt du jeu ni poussé | | CHG-233 | 2026-10-07 | DC0 | `Docs/Migration/24_Plan_Decouplage_Sandbox.md` (nouveau), `Docs/Migration/etat/sandbox/DC0/DC0-CART.md`, `DC0-SPEC.md`, `DC0-VAL.md` (nouveaux) | lot DC0 : plan du découplage jeu / sandbox (lots DC0 à DC4), cartographie et SPEC approuvées par l'utilisateur le 2026-10-07 (plugin `LPAnimCore` dans le jeu, module éditeur `LPModTools` et tout l'outillage de mods dans l'addon, dépôt Sandbox privé) ; documentation seule, aucun code. Motif : le jeu public doit compiler sans le dépôt privé `LifePlay-Sandbox` (`LifePlay` et `LifePlayEditorTools` dépendent de `LPAnimMeasure`) | aucun pin (documentation seule) | supprimer ces fichiers (documentation seule) ; rien n'est commité dans le dépôt du jeu ni poussé |
| CHG-232 | 2026-10-07 | SB4 | addon `D:\Game\LifePlay-Sandbox` : `Plugins/LPSandbox/Source/LPSandbox/LPPairSandbox.cpp`, `LPSandboxModRegistry.cpp`, `LPSandboxModRegistry.h` (SB4-T0 : ouverture par déclencheur éphémère non mémorisée, `sandbox_mode.txt` du pilote), `README.md` (commits `16905c68070992f5139dab907b41b461fb765957` code + pin, tag `lot-SB4-valide`, branche `lot/SB4-final`, tag d'entrée `lot-SB4-avant` = `30e8c18`) ; jeu : `Docs/Migration/etat/sandbox/SB4/SB4-BUILD.md`, `SB4-VAL.md`, `SB4-EVAL.md`, `Docs/Migration/23_Cycle_De_Vie_Mod.md` (§ 7), `Docs/Migration/21_Sandbox_Addon.md`, `Docs/Migration/etat/sandbox/S3/S3-INT.md` | lot SB4 (dernier lot de la sandbox modulaire) : SB4-T0 correctif (ouverture par déclencheur éphémère, `Open=lps_couple` intact), compilation PASS, validation finale F1 à F4 PASS (démarrage, tournées Quaternius et FreeAnimationsPack identiques à SB2-REF, LPAnimEval identique), doc 23 § 7, évaluation `SB4-EVAL.md` : décision GO pour transposer au jeu par petits lots | pin `3d85852` → `16905c6` (hash complet `16905c68070992f5139dab907b41b461fb765957`) | `.\Tools\Restore.ps1 -Lot SB4` (docs du jeu), `.\Tools\Restore.ps1 -Lot SB4-addon -ProjectRoot D:\Game\LifePlay-Sandbox` et `git -C D:\Game\LifePlay-Sandbox checkout lot-SB4-avant` ; rien n'est commité dans le dépôt du jeu ni poussé | | CHG-232 | 2026-10-07 | SB4 | addon `D:\Game\LifePlay-Sandbox` : `Plugins/LPSandbox/Source/LPSandbox/LPPairSandbox.cpp`, `LPSandboxModRegistry.cpp`, `LPSandboxModRegistry.h` (SB4-T0 : ouverture par déclencheur éphémère non mémorisée, `sandbox_mode.txt` du pilote), `README.md` (commits `16905c68070992f5139dab907b41b461fb765957` code + pin, tag `lot-SB4-valide`, branche `lot/SB4-final`, tag d'entrée `lot-SB4-avant` = `30e8c18`) ; jeu : `Docs/Migration/etat/sandbox/SB4/SB4-BUILD.md`, `SB4-VAL.md`, `SB4-EVAL.md`, `Docs/Migration/23_Cycle_De_Vie_Mod.md` (§ 7), `Docs/Migration/21_Sandbox_Addon.md`, `Docs/Migration/etat/sandbox/S3/S3-INT.md` | lot SB4 (dernier lot de la sandbox modulaire) : SB4-T0 correctif (ouverture par déclencheur éphémère, `Open=lps_couple` intact), compilation PASS, validation finale F1 à F4 PASS (démarrage, tournées Quaternius et FreeAnimationsPack identiques à SB2-REF, LPAnimEval identique), doc 23 § 7, évaluation `SB4-EVAL.md` : décision GO pour transposer au jeu par petits lots | pin `3d85852` → `16905c6` (hash complet `16905c68070992f5139dab907b41b461fb765957`) | `.\Tools\Restore.ps1 -Lot SB4` (docs du jeu), `.\Tools\Restore.ps1 -Lot SB4-addon -ProjectRoot D:\Game\LifePlay-Sandbox` et `git -C D:\Game\LifePlay-Sandbox checkout lot-SB4-avant` ; rien n'est commité dans le dépôt du jeu ni poussé |
| CHG-231 | 2026-10-07 | SB2-O1 | addon `D:\Game\LifePlay-Sandbox` : `Plugins/LPSandbox/Source/LPSandbox/LPSandboxScene.cpp` (`ALPSandboxScene::ShowRoom` : `SetHiddenInGame(false)` puis `SetVisibility(true, true)` sur meubles et murs), `README.md` (commits `3d858527fccfdddd8711c7c7448fa001442e0b8a` code + `30e8c18` pin, tag `lot-SB2-O1-valide`, branche `lot/SB2-O1`, tag d'entrée `lot-SB2-O1-avant` = `b4ca206`) ; jeu : `Docs/Migration/etat/sandbox/SB2/SB2-O1-BUILD.md`, `Docs/Migration/etat/sandbox/S3/S3-INT.md`, `Docs/Migration/21_Sandbox_Addon.md` (pin, reports SB2, porte G-SB3) | mini-lot SB2-O1 : correction du défaut O1 (meubles et murs invisibles au retour de Mesure, `ShowRoom` rendu symétrique de `HideRoom`) ; compilation PASS ; tournée freeanims identique à SB2-REF (73/73 fichiers, JSON/CSV/TXT identiques) ; contrôle visuel utilisateur « c'est bon » (meubles et murs visibles en revenant de Mesure vers Couple et Interaction). Porte G-SB3 (décision utilisateur 2026-10-07) : SB3 abandonné (plugins physiques séparés non faits) ; les mods du jeu sont du contenu (.lpmod + pak), leurs dépendances sont vérifiées par le registre (prouvé en SB1/SB2), coût ≈ 1 à 1,5 lot, `LPAnimMeasure` utilisé par le jeu en Shipping ; on passe à SB4 ; E3 non traité | pin `fece7e9` → `3d85852` (hash complet `3d858527fccfdddd8711c7c7448fa001442e0b8a`) | `.\Tools\Restore.ps1 -Lot SB2-O1` (docs du jeu), `.\Tools\Restore.ps1 -Lot SB2-O1-addon -ProjectRoot D:\Game\LifePlay-Sandbox` et `git -C D:\Game\LifePlay-Sandbox checkout lot-SB2-O1-avant` ; rien n'est commité dans le dépôt du jeu ni poussé | | CHG-231 | 2026-10-07 | SB2-O1 | addon `D:\Game\LifePlay-Sandbox` : `Plugins/LPSandbox/Source/LPSandbox/LPSandboxScene.cpp` (`ALPSandboxScene::ShowRoom` : `SetHiddenInGame(false)` puis `SetVisibility(true, true)` sur meubles et murs), `README.md` (commits `3d858527fccfdddd8711c7c7448fa001442e0b8a` code + `30e8c18` pin, tag `lot-SB2-O1-valide`, branche `lot/SB2-O1`, tag d'entrée `lot-SB2-O1-avant` = `b4ca206`) ; jeu : `Docs/Migration/etat/sandbox/SB2/SB2-O1-BUILD.md`, `Docs/Migration/etat/sandbox/S3/S3-INT.md`, `Docs/Migration/21_Sandbox_Addon.md` (pin, reports SB2, porte G-SB3) | mini-lot SB2-O1 : correction du défaut O1 (meubles et murs invisibles au retour de Mesure, `ShowRoom` rendu symétrique de `HideRoom`) ; compilation PASS ; tournée freeanims identique à SB2-REF (73/73 fichiers, JSON/CSV/TXT identiques) ; contrôle visuel utilisateur « c'est bon » (meubles et murs visibles en revenant de Mesure vers Couple et Interaction). Porte G-SB3 (décision utilisateur 2026-10-07) : SB3 abandonné (plugins physiques séparés non faits) ; les mods du jeu sont du contenu (.lpmod + pak), leurs dépendances sont vérifiées par le registre (prouvé en SB1/SB2), coût ≈ 1 à 1,5 lot, `LPAnimMeasure` utilisé par le jeu en Shipping ; on passe à SB4 ; E3 non traité | pin `fece7e9` → `3d85852` (hash complet `3d858527fccfdddd8711c7c7448fa001442e0b8a`) | `.\Tools\Restore.ps1 -Lot SB2-O1` (docs du jeu), `.\Tools\Restore.ps1 -Lot SB2-O1-addon -ProjectRoot D:\Game\LifePlay-Sandbox` et `git -C D:\Game\LifePlay-Sandbox checkout lot-SB2-O1-avant` ; rien n'est commité dans le dépôt du jeu ni poussé |

View file

@ -0,0 +1,66 @@
# DC1-BUILD : Compilation LifePlayEditor
## Fiche DC1-T2
| Élément | Valeur |
|---------|--------|
| Lot | DC1 (plugin LPAnimCore) |
| Tâche | DC1-T2 |
| Agent | A-BUILD |
| Modèle prévu | haiku |
| Modèle utilisé | haiku |
| Date | 2026-10-07 |
## Commande lancée
```
& 'D:\Game\Launcher\Engine\UE_5.5\Engine\Build\BatchFiles\Build.bat' LifePlayEditor Win64 Development -Project='D:\Game\LifePlay-master 5.5\LifePlay.uproject' -WaitMutex *> 'D:\Game\LifePlay-master 5.5\Docs\Migration\etat\sandbox\DC1\DC1_Build_LifePlayEditor.txt'
```
## Verdict
**PASS** — Code de sortie : **0**
## Erreurs
Aucune
## Avertissements
- **2 messages UBT** : lignes 46-47, non-blocants, concernant le renommage des niveaux d'avertissement en UBT (DeprecationWarningLevel, MSVCDeprecationWarningLevel).
- **1 dépendance dépréciée** : ligne 50, McpAutomationBridge dépend de StructUtils (déprécié en 5.5).
- **Aucun avertissement** concernant LPAnimCore, LPAnimMeasure, LPSandbox ou LifePlay.Build.cs
## Tableau des DLL
| Nom | Date (LastWriteTime) | Taille |
|-----|----------------------|--------|
| UnrealEditor-LPAnimCore.dll | 07/10/2026 16:16:52 | 0,23 MB |
| UnrealEditor-LPAnimMeasure.dll | 07/10/2026 16:16:56 | 0,50 MB |
| UnrealEditor-LifePlay.dll | 07/10/2026 16:17:14 | 1,16 MB |
| UnrealEditor-LifePlayEditorTools.dll | 07/10/2026 16:17:21 | 1,06 MB |
Toutes les DLL datent d'aujourd'hui (07/10/2026), témoignant de la compilation réussie.
## Structures compilées
- **Module LPAnimCore** : compilé avec succès, DLL générée et présente.
- **Module LPAnimMeasure** : compilé avec succès, DLL générée et présente.
- **Module LifePlay** : compilé avec succès, DLL générée et présente.
- **Module LifePlayEditorTools** : compilé avec succès, DLL générée et présente.
- **Module LPSandbox** : compilé avec succès depuis les plugins additionnels (AdditionalPluginDirectories).
## Vérifications
- **Éditeur Unreal** : aucun processus en cours au démarrage ✓
- **D:\Game\LifePlay-Sandbox\Plugins\LPSandbox\Binaries** : N'existe pas (conforme : le dépôt addon est séparé) ✓
## Journal complet
- Fichier : `D:\Game\LifePlay-master 5.5\Docs\Migration\etat\sandbox\DC1\DC1_Build_LifePlayEditor.txt`
- Durée totale : 58,66 secondes
- Actions exécutées : 95/95
## Conclusion
Compilation complétée avec succès. Le plugin LPAnimCore est compilé et prêt pour la suite du pipeline DC1.

View file

@ -0,0 +1,28 @@
# DC1-DOC — Pin et documentation du lot DC1
**Fiche DC1-T4 (A-INT, partie pin) et DC1-T5 (A-DOC) — Modèle prévu sonnet | utilisé sonnet**
- Date : 2026-10-07. Aucune commande git d'écriture, aucun fichier de code. Les fins de ligne (LF) des fichiers sont conservées.
- Nouveau pin de l'addon : `3e9c32f1f27b1abdaf7731dc90496c27ee061718` (`3e9c32f`, branche `lot/DC1-animcore`, tag `lot-DC1-valide`, local, non poussé). Pin précédent : `16905c68070992f5139dab907b41b461fb765957` (SB4).
## DC1-T4 (partie pin)
| Fichier | Lignes touchées |
|---|---|
| `Docs/Migration/etat/sandbox/S3/S3-INT.md` | 11 (commit pin), 13 (ancien pin ajouté en tête des pins précédents), 49 (checkout) |
| `Docs/Migration/21_Sandbox_Addon.md` | 127 (paragraphe Pin : nouveau hash en tête, SB4 en tête des précédents), 132 (checkout) |
| `D:\Game\LifePlay-Sandbox\README.md` | 69 (commit pin), 75 (checkout) |
## DC1-T5 (documentation)
| Fichier | Lignes touchées |
|---|---|
| `Docs/Migration/Journal_Modifications.md` | 1134 : CHG-234 (7 colonnes), juste au-dessus de CHG-233 |
| `Docs/Migration/21_Sandbox_Addon.md` | 79 (Rupture du couplage : c'est l'addon `LPSandbox.cpp:17` qui appelle `SetUALRoot`, `LPAnimRedirect` / `LPCoupleSim` dans `Plugins/LPAnimCore`) ; 89 à 93 (graphe mermaid : `LPAnimCore`, `LPSandbox`) ; 103 à 105 et 107 (tableau des dépendances) ; 189 (case DC1 cochée) ; 193 (entrée de registre non cochée « Intégration des déplacements… », lot à définir hors DC) ; 194 (report DC0 SetUALRoot coché) |
| `Docs/Migration/20_Chaine_Animations.md` | 84 (précision : `LPAnimRedirect` et `LPCoupleSim` dans le plugin `LPAnimCore` du jeu, mesure dans `LPAnimMeasure`) |
| `Docs/Migration/07_Architecture_5.5.md` | 83 (ligne `LPAnimCore` dans le tableau 2.3 Plugins) |
| `Docs/Migration/etat/sandbox/DC1/DC1-DOC.md` | nouveau |
## Contrôle
`git diff --stat` (lecture seule) : 07 = 1 ligne, 20 = 1 ligne modifiée, 21 = 22 lignes (15 ajoutées, 7 retirées), Journal = 1 ligne, S3-INT = 3 lignes modifiées, README addon = 2 lignes modifiées. Aucun fichier réécrit en entier.

View file

@ -0,0 +1,35 @@
# DC1-IMPL — Plugin LPAnimCore
- **Fiche** : DC1-T1 (A-IMPL-H) — **Modèle** : M3 opus | opus (session principale) — **Dépend de** : DC0-SPEC § 1
- **Instantanés** : `D:\Game\LifePlay_Backup_5.5\DC1` (jeu : `LifePlay.uproject`, `LifePlay.Build.cs`, `Plugins\LPAnimCore` absent)
et `DC1-addon` (16 fichiers de l'addon). Retour arrière proposé : `.\Tools\Restore.ps1 -Lot DC1` et
`.\Tools\Restore.ps1 -Lot DC1-addon -ProjectRoot D:\Game\LifePlay-Sandbox`.
- **Branche addon** : `lot/DC1-animcore` (depuis `lot/SB4-final` = `4795ea2`).
## Jeu
| Fichier | Changement |
|---|---|
| `Plugins/LPAnimCore/LPAnimCore.uplugin` | nouveau : module `LPAnimCore`, Runtime, `PreDefault`, Win64, toutes configurations |
| `Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.Build.cs` | nouveau : `Core`, `CoreUObject`, `Engine` publics, structure plate |
| `Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.cpp` | nouveau : `IMPLEMENT_MODULE(FDefaultModuleImpl, LPAnimCore)` |
| `…/LPAnimRedirect.h/.cpp`, `LPCoupleSim.h`, `LPCoupleSimCore.cpp` | copiés de l'addon ; seules différences : `LPANIMMEASURE_API` → `LPANIMCORE_API` (6 dans `LPAnimRedirect.h` dont 3 lignes, 18 dans `LPCoupleSim.h`) et le commentaire de `SetUALRoot` (périmé : c'est l'addon qui l'appelle, pas `LifePlay`) ; `LPAnimRedirect.cpp` et `LPCoupleSimCore.cpp` identiques octet pour octet (CRLF conservé) |
| `LifePlay.uproject` | plugin `LPAnimCore` activé (avant `LPSandbox`, qui reste `Optional`) |
| `Source/LifePlay/LifePlay.Build.cs` | `LPAnimMeasure` → `LPAnimCore` (public) |
Empreintes SHA-256 (16 premiers caractères) : `LPAnimCore.Build.cs` cabb05d300fb9f33, `LPAnimCore.cpp` 2eabcff54d205a58,
`LPAnimRedirect.cpp` b0a04db568abfae1, `LPAnimRedirect.h` 89304d2e67da47f1, `LPCoupleSim.h` c560a4a8811a2a87,
`LPCoupleSimCore.cpp` 7a0a276fa9638a75.
## Addon
| Fichier | Changement |
|---|---|
| `Plugins/LPSandbox/Source/LPAnimMeasure/LPAnimRedirect.h/.cpp`, `LPCoupleSim.h`, `LPCoupleSimCore.cpp` | supprimés (`git rm`) |
| `LPAnimMeasure.Build.cs` | `LPAnimCore` en dépendance privée (`LPPoseOrchestrator.cpp`) |
| `LPAnimMeasure.h` | commentaire d'en-tête |
| `LPSandbox/LPSandbox.Build.cs` | `LPAnimCore` en dépendance publique |
| `LPSandbox.uplugin` | `"Plugins": [ LPAnimCore ]`, description |
| `LPSandbox.uproject` | `AdditionalPluginDirectories` → `../LifePlay-master 5.5/Plugins/LPAnimCore` |
`LifePlayEditorTools.Build.cs` inchangé (garde `LPAnimMeasure` jusqu'à DC2).

View file

@ -0,0 +1,124 @@
# DC1-VAL — Validation automatique du lot DC1 (DC1-T3, partie automatique)
**Fiche DC1-T3 — Modèle prévu sonnet | utilisé sonnet**
- Date : 2026-10-07. Build testé : celui de `DC1-BUILD.md` (code 0, non recompilé). Lot DC1 : LPAnimRedirect et LPCoupleSim déplacés dans le plugin du jeu `Plugins\LPAnimCore`.
- Journaux copiés dans `D:\Game\LifePlay_Backup_5.5\DC1-VAL\` : `F1.log`, `F2_quaternius_sandbox.log`, `F2_quaternius_console.txt`, `F2_freeanims_sandbox.log`, `F2_freeanims_console.txt` (+ `F2_*_result.txt` : code et durée).
- Procédure : méthode de `SB4-VAL.md` / `SB2-VAL.md` (lanceur `LifePlay_pair_sandbox.bat` lancé depuis PowerShell, attente de `Ouvert :`, copie du journal, arrêt du seul `UnrealEditor` lancé ; tournées par `AnimPipeline.ps1 -Pack <pack> -From 5 -To 5`, aucun éditeur avant chaque lancement). Aucun git (hors `status`), aucune modification de source, script ou ini.
- Incident sans effet : une première tentative freeanims, lancée par erreur dans Windows PowerShell 5.1 (`Start-Process powershell`), a échoué immédiatement : `ECHEC : Impossible de trouver un paramètre correspondant au nom « AsHashtable ».` (cmdlet PowerShell 7 ; erreur de mon lancement, pas du build ; aucun éditeur lancé). Relancée dans pwsh 7 (comme le quaternius) ; seul ce second lancement est retenu.
## Tableau des essais
| Essai | Attendu | Constaté (lignes recopiées) | Verdict |
|---|---|---|---|
| F1 démarrage | 7 modules, ordre, aucun refus, `Ouvert : lps_couple`, aucun Error/Ensure/Fatal LPSandbox/LPAnimCore/LPAnimMeasure | `LogLPSandboxMods: Display: Modules : 7 sandbox, 16 jeu ; ordre : lps_system_core, lps_system_animmeasure, lps_lib_scene, lps_lib_actors, lps_couple, lps_interaction, lps_asset_integration` ; les 7 en `pret` ; `LogLPSandboxMods: Display: Ouvert : lps_couple`. Lignes citant LPAnimCore : `LogPluginManager: Mounting Project plugin LPAnimCore` et `LogConfig: Branch 'LPAnimCore' had been unloaded. Reloading on-demand took 0.10ms` (aucune erreur de chargement du module). Aucune ligne de refus de module (le mot « refus » n'apparaît que dans `UTS: Unable to launch the Unreal Trace Server ... Accès refusé`, avertissement d'environnement). Seul `Error` : `LogLifePlay: Error: Coiffures : GI.HairstyleTable ou GI.MorphableHairs introuvable sur GameInstance_0, table laissee telle quelle` (connu, préexistant, hors LPSandbox). Aucun `Ensure`/`Fatal`. Corps, boîte AgMakeout et poses chargés comme à SB2. | PASS |
| F2 quaternius : avant | `Open=lps_couple` | `Saved\sandbox_mods.ini:3:Open=lps_couple` | PASS |
| F2 quaternius : journal | `Ouvert : lps_asset_integration (declencheur, non memorise)` + `TOURNEE fermeture demandee` | `LogLPSandbox: Warning: TOURNEE debut 43 clips, 129 etapes` ; `LogLPSandboxMods: Display: Ouvert : lps_asset_integration (declencheur, non memorise)` ; tournée 43 clips ; fermeture demandée. Code 0, 1139 s, verdict `avertissement`. Seul Error : Coiffures. | PASS |
| F2 quaternius : après | `Open=lps_couple` | `Saved\sandbox_mods.ini:3:Open=lps_couple` | PASS |
| F2 freeanims : avant | `Open=lps_couple` | `Saved\sandbox_mods.ini:3:Open=lps_couple` | PASS |
| F2 freeanims : journal | idem | `LogLPSandbox: Warning: TOURNEE debut 10 clips, 30 etapes` ; `LogLPSandboxMods: Display: Ouvert : lps_asset_integration (declencheur, non memorise)` ; `TOURNEE debut 10 clips, 697 etapes` ; `LogLPSandbox: Warning: TOURNEE fermeture demandee`. Code 0, 458 s, verdict `ok`. Seul Error : Coiffures. | PASS |
| F2 freeanims : après | `Open=lps_couple` | `Saved\sandbox_mods.ini:3:Open=lps_couple` | PASS |
| F3 quaternius | fichiers identiques, verdict `avertissement`, 8 mêmes avertissements | voir détail | PASS |
| F3 freeanims | fichiers de données identiques, verdict `ok` | voir détail (20 images diffèrent, les mêmes qu'à SB2/SB4) | PASS (images : à juger) |
| F4 état final | ini standard, aucun éditeur, git recopié | voir ci-dessous | PASS |
## Détail F3
Référence : `D:\Game\LifePlay_Backup_5.5\SB2-REF\<pack>\` ; nouveau : `Saved\AnimPipeline\<pack>\`. Comparaison par SHA-256 de chaque fichier commun.
### quaternius (verdict `avertissement`)
- Liste de fichiers : 44 / 44, identique ; 44 fichiers comparés (critère > 40 : OK).
- Seuls fichiers dont le SHA-256 diffère : `etapes\5_evaluation.json` (champs `debut`, `fin`, `duree_s` 1136 → 1139), `etat.json` (`fin`, `maj`) et `rapport.html` (seule ligne différente : « Généré le » 07/10/2026 12:10:18 → 16:21:02). Tous les autres `.json`, `.csv`, `.txt` (dont `rapport.json`, `eval_*.csv`, `controles_*.csv`, `retarget_pose_*.txt`) sont identiques : 41 fichiers identiques sur 44.
- Verdict `5_evaluation.json` : `avertissement` dans la référence et dans le nouveau ; 8 avertissements dans chacun, listes identiques (comparées en JSON).
- Images : aucune dans ce pack.
### freeanims (verdict `ok`, 0 avertissement)
- Liste de fichiers : 73 / 73, identique ; 73 fichiers comparés (critère > 70 : OK).
- Mêmes exclusions : `etapes\5_evaluation.json` (`debut`, `fin`, `duree_s` 435 → 458), `etat.json` (`fin`, `maj`) et `rapport.html` (« Généré le » 12:31:18 → 17:08:32 uniquement) diffèrent ; tous les `.json`/`.csv`/`.txt` hors ces fichiers sont identiques.
- Verdict `ok` des deux côtés, 0 avertissement des deux côtés.
- Images qui diffèrent : 20 fichiers, exactement les mêmes noms que dans SB4-VAL (rendu non déterministe déjà constaté à SB1/SB2/SB4), octets réf → nouveau :
| Fichier (sous `sandbox\`) | Réf → nouveau |
|---|---|
| planches\AS_AirKiss.jpg | 148577 → 148509 |
| planches\AS_AttackInLevitation2.jpg | 139248 → 139290 |
| planches\AS_ChillDriving.jpg | 187537 → 187639 |
| planches\AS_ComboHands.jpg | 159892 → 160037 |
| planches\AS_EatingOnParty.jpg | 141254 → 141301 |
| planches\AS_FlyingUp.jpg | 119045 → 118961 |
| planches\AS_GoForward.jpg | 150812 → 150701 |
| planches\AS_Rest.jpg | 138425 → 138540 |
| planches\AS_RightLegKick2.jpg | 88121 → 88080 |
| planches\AS_Talk.jpg | 144201 → 144319 |
| vignettes\AS_AirKiss.png | 261470 → 261921 |
| vignettes\AS_AttackInLevitation2.png | 252870 → 253271 |
| vignettes\AS_ChillDriving.png | 289192 → 289225 |
| vignettes\AS_ComboHands.png | 269493 → 269106 |
| vignettes\AS_EatingOnParty.png | 263747 → 263738 |
| vignettes\AS_FlyingUp.png | 263615 → 263387 |
| vignettes\AS_GoForward.png | 257039 → 256829 |
| vignettes\AS_Rest.png | 242540 → 242649 |
| vignettes\AS_RightLegKick2.png | 265941 → 265478 |
| vignettes\AS_Talk.png | 260083 → 260572 |
Écarts de taille inférieurs à 0,2 %. Soit 73 − 3 (fichiers exclus) − 20 (images) = 50 fichiers de données strictement identiques.
## État final (F4)
- `Saved\sandbox_mods.ini` :
```
[Mods]
Order=lps_couple,lps_interaction,lps_asset_integration
Open=lps_couple
[Enabled]
lps_couple=true
lps_interaction=true
lps_asset_integration=true
```
- Processus `UnrealEditor` : 0.
- `git -C D:\Game\LifePlay-Sandbox status --short` (changements DC1 non commités, attendu ; code de l'addon retiré vers le jeu) :
```
M LPSandbox.uproject
M Plugins/LPSandbox/LPSandbox.uplugin
M Plugins/LPSandbox/Source/LPAnimMeasure/LPAnimMeasure.Build.cs
M Plugins/LPSandbox/Source/LPAnimMeasure/LPAnimMeasure.h
D Plugins/LPSandbox/Source/LPAnimMeasure/LPAnimRedirect.cpp
D Plugins/LPSandbox/Source/LPAnimMeasure/LPAnimRedirect.h
D Plugins/LPSandbox/Source/LPAnimMeasure/LPCoupleSim.h
D Plugins/LPSandbox/Source/LPAnimMeasure/LPCoupleSimCore.cpp
M Plugins/LPSandbox/Source/LPSandbox/LPSandbox.Build.cs
```
## Anomalies
- Aucune nouvelle. `Error` Coiffures (LogLifePlay) présent à chaque lancement, préexistant. Avertissement `UTS: Unable to launch the Unreal Trace Server ... Accès refusé` : environnement, sans lien.
- 20 images freeanims différentes (rendu non déterministe, mêmes fichiers qu'à SB2 et SB4) : à juger visuellement si souhaité.
- Durées légèrement plus longues que la référence (quaternius 1139 s contre 1136 s ; freeanims 458 s contre 435 s) : sans signification (charge machine), dans l'ordre de grandeur.
- Tentative freeanims ratée par mon mauvais interpréteur (voir en-tête) : sans conséquence.
## Verdict global de la partie automatique
**PASS** : F1 (7 modules, ordre inchangé, plugin LPAnimCore monté, aucun refus ni Error du module), F2 (ouverture par déclencheur non mémorisée, `Open=lps_couple` intact après les deux tournées), F3 (quaternius `avertissement` avec les 8 mêmes avertissements, 44 fichiers ; freeanims `ok`, 73 fichiers ; tous fichiers de données identiques, seules différences : dates, « Généré le » et 20 images non déterministes) et F4 satisfaits. Le déplacement de LPAnimRedirect et LPCoupleSim n'a rien changé aux résultats des commandlets AP.
## Reste à valider par l'utilisateur
(a) Marche du jeu avec clips convertis (`LPAnimTarget.ini` = game).
(b) ShowCouple.
## Modèles par fiche
| Fiche | Modèle prévu | Modèle utilisé |
|---|---|---|
| DC1-T2 | haiku | haiku |
| DC1-T3 | sonnet | sonnet |
## Contrôle visuel utilisateur (2026-10-07)
(a) marche du jeu avec clips convertis (`LPAnimTarget.ini` = `game`, jeu lancé depuis l'éditeur compilé DC1) et
(b) ShowCouple : **confirmés** par l'utilisateur (« oui, je confirme »). `LPAnimTarget.ini` remis à son état d'avant le
test (`Target=sandbox`). Remarque utilisateur : l'intégration des déplacements dans le jeu reste à améliorer (hors
périmètre DC, inscrit au registre des reports de `21`).
**Verdict DC1-T3 : PASS.**

View file

@ -8,9 +8,9 @@ Date : 2026-10-06 — Role A-INT
|---|---| |---|---|
| Repo addon | https://github.com/edujiro5972/LifePlay-Sandbox.git | | Repo addon | https://github.com/edujiro5972/LifePlay-Sandbox.git |
| Branche | `main` | | Branche | `main` |
| **Commit pin** | `16905c68070992f5139dab907b41b461fb765957` (`16905c6`, SB4, **local, non pousse** au 2026-10-07) | | **Commit pin** | `3e9c32f1f27b1abdaf7731dc90496c27ee061718` (`3e9c32f`, DC1 : `LPAnimRedirect` et `LPCoupleSim` rapatries dans le plugin `LPAnimCore` du jeu, branche `lot/DC1-animcore`, tag `lot-DC1-valide`, **local, non pousse** au 2026-10-07) |
| Contenu du commit | S5-T2 : `LPCoupleSim` (géométrie du couple partagée jeu/addon) ; inclut S4-T3 (retrait GM Interaction legacy), S4-T2 (découpe `LPPairSandbox.cpp`) et S3-T4 (`StartupModule` → `SetUALRoot`) | | Contenu du commit | S5-T2 : `LPCoupleSim` (géométrie du couple partagée jeu/addon) ; inclut S4-T3 (retrait GM Interaction legacy), S4-T2 (découpe `LPPairSandbox.cpp`) et S3-T4 (`StartupModule` → `SetUALRoot`) |
| Pins précédents | `3d858527fccfdddd8711c7c7448fa001442e0b8a` (SB2-O1), `82dae58380b76c45f60541fd7cb5a0a409700e10` (SB1), `f09c42e5a2bc613cf269cce76c878461ca41c8b7` (S7-T2), `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` (S5-T2), `f8102647ccdebad37e4977ccbf2c51e583823a80` (S4-T3), `4de1f0b6764ed3bc575bb687630d3f070e1be221` (S3-T4) | | Pins précédents | `16905c68070992f5139dab907b41b461fb765957` (SB4), `3d858527fccfdddd8711c7c7448fa001442e0b8a` (SB2-O1), `82dae58380b76c45f60541fd7cb5a0a409700e10` (SB1), `f09c42e5a2bc613cf269cce76c878461ca41c8b7` (S7-T2), `c1fdd29e2c1036c7d6365ba6389eff5d2f8c8c96` (S5-T2), `f8102647ccdebad37e4977ccbf2c51e583823a80` (S4-T3), `4de1f0b6764ed3bc575bb687630d3f070e1be221` (S3-T4) |
## Structure ## Structure
@ -46,7 +46,7 @@ sur la copie effectivement chargee. Pin manuel a la place (ci-dessous).
```powershell ```powershell
cd D:\Game cd D:\Game
git clone https://github.com/edujiro5972/LifePlay-Sandbox.git git clone https://github.com/edujiro5972/LifePlay-Sandbox.git
git -C LifePlay-Sandbox checkout 16905c68070992f5139dab907b41b461fb765957 git -C LifePlay-Sandbox checkout 3e9c32f1f27b1abdaf7731dc90496c27ee061718
# Le jeu doit etre clone a cote : D:\Game\LifePlay-master 5.5 # Le jeu doit etre clone a cote : D:\Game\LifePlay-master 5.5
``` ```

View file

@ -19,6 +19,10 @@
} }
], ],
"Plugins": [ "Plugins": [
{
"Name": "LPAnimCore",
"Enabled": true
},
{ {
"Name": "LPSandbox", "Name": "LPSandbox",
"Enabled": true, "Enabled": true,

View file

@ -0,0 +1,21 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0.0",
"FriendlyName": "LPAnimCore",
"Description": "LifePlay : redirection des clips convertis (LPAnimRedirect) et geometrie commune du couple (LPCoupleSim). Utilise par le jeu (LifePlay) et par l'addon LifePlay-Sandbox. Compile dans toutes les configurations.",
"Category": "LifePlay",
"CreatedBy": "LifePlay",
"CanContainContent": false,
"Installed": false,
"SupportedTargetPlatforms": [
"Win64"
],
"Modules": [
{
"Name": "LPAnimCore",
"Type": "Runtime",
"LoadingPhase": "PreDefault"
}
]
}

View file

@ -0,0 +1,18 @@
// Module LPAnimCore (lot DC1, Docs/Migration/24_Plan_Decouplage_Sandbox.md) : partie runtime de l'ancien
// LPAnimMeasure utilisee par le jeu (LPAnimRedirect, LPCoupleSim). Aucune dependance vers LifePlay ni vers
// l'addon LifePlay-Sandbox ; l'addon (LPSandbox, LPAnimMeasure) depend de ce module, jamais l'inverse.
using UnrealBuildTool;
public class LPAnimCore : ModuleRules
{
public LPAnimCore(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;
// Structure plate (comme LPAnimMeasure).
PublicIncludePaths.Add(ModuleDirectory);
PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine" });
}
}

View file

@ -0,0 +1,5 @@
// Module LPAnimCore (lot DC1) : LPAnimRedirect, LPCoupleSim.
#include "Modules/ModuleManager.h"
IMPLEMENT_MODULE(FDefaultModuleImpl, LPAnimCore)

View file

@ -0,0 +1,62 @@
#include "LPAnimRedirect.h"
#include "Misc/ConfigCacheIni.h"
#include "Misc/Paths.h"
namespace LPAnimRedirect
{
static FString IniPath()
{
return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Config/Windows/LPAnimTarget.ini"));
}
// Defaut identique a FLPSandboxConfig::QuaterniusUALRoot : sans appel a SetUALRoot
// (commandlet isole, test), le comportement est inchange.
static FString& UALRoot()
{
static FString Root = TEXT("/Game/Quaternius/UAL");
return Root;
}
void SetUALRoot(const FString& Root)
{
if (!Root.IsEmpty())
{
UALRoot() = Root;
}
}
const FString& GetUALRoot()
{
return UALRoot();
}
bool IsGameTarget()
{
FString Target;
GConfig->GetString(TEXT("LifePlay.Anim"), TEXT("Target"), Target, IniPath());
return Target.Equals(TEXT("game"), ESearchCase::IgnoreCase);
}
FString ClipPath(const FString& AssetName)
{
return FString::Printf(TEXT("%s/%s.%s"), *GetUALRoot(), *AssetName, *AssetName);
}
static FString& BodyRoot()
{
static FString Root;
return Root;
}
void SetBodyRoot(const FString& Root)
{
BodyRoot() = Root;
}
FString BodyClipPath(const FString& AssetName)
{
const FString& Root = BodyRoot().IsEmpty() ? GetUALRoot() : BodyRoot();
return FString::Printf(TEXT("%s/%s.%s"), *Root, *AssetName, *AssetName);
}
}

View file

@ -0,0 +1,27 @@
// Choisit qui joue les clips convertis (Bridget / Michael).
// Saved/Config/Windows/LPAnimTarget.ini : Target=sandbox ou Target=game.
// sandbox : seule la sandbox les joue. game : la marche du jeu les joue.
// Les fichiers restent dans /Game/Quaternius/UAL.
#pragma once
#include "CoreMinimal.h"
namespace LPAnimRedirect
{
LPANIMCORE_API bool IsGameTarget();
LPANIMCORE_API FString ClipPath(const FString& AssetName);
// Racine des clips convertis. L'addon LifePlay-Sandbox (module LPSandbox) la fixe au
// demarrage depuis Config/LPSandbox.ini (cle Sandbox.Roots/QuaterniusUALRoot) ; sans
// l'addon, le defaut interne s'applique (lot DC1 : ce module ne lit aucune config de l'addon).
LPANIMCORE_API void SetUALRoot(const FString& Root);
LPANIMCORE_API const FString& GetUALRoot();
// Lot AP7 : clips convertis (<Prefixe><Clip>, Bridget B_, Michael M_) dans un dossier distinct de celui des
// clips source (pack UE5 : /Game/AnimPacks/<pack>). Vide = meme dossier que les clips source (Quaternius).
LPANIMCORE_API void SetBodyRoot(const FString& Root);
LPANIMCORE_API FString BodyClipPath(const FString& AssetName);
}

View file

@ -0,0 +1,215 @@
// Geometrie commune du couple (lot S5-T2, voir Docs/Migration/etat/sandbox/S5/S5-CART.md).
// Une seule source de verite pour : la transformation mesh -> acteur, l'echantillonnage des poses,
// le padding et le recalage de la boite englobante, le placement des corps, et le glissement du couple.
//
// Deux appelants, qui partagent la geometrie mais gardent leurs reglages :
// - addon : ALPPairSandboxStage (LPSandbox/LPCoupleSim.cpp) -> FSweepOptions::Sandbox()
// - jeu : ULPShowCoupleSubsystem (LifePlay/LPShowCoupleSubsystem.cpp) -> FSweepOptions::Show()
//
// Tout ce qui differe d'un cote a l'autre passe par FBoxFitParams ou FSweepOptions : le code
// de mesure et de boite n'est plus duplique, mais aucune option n'est alignee d'office.
// Regle du module : aucune dependance vers LifePlay ni vers LPSandbox.
#pragma once
#include "CoreMinimal.h"
#include "Engine/EngineTypes.h"
#include "Templates/Function.h"
class AActor;
class ACharacter;
class UAnimSequence;
class UBoxComponent;
class UCharacterMovementComponent;
class UPrimitiveComponent;
class USkeletalMeshComponent;
class UWorld;
namespace LPCoupleSim
{
// Demi-hauteur de capsule par defaut (Person et sandbox ont la meme valeur nominale).
inline constexpr double DefaultCapsuleHalf = 88.0;
// Pas d'echantillonnage d'une AnimSequence (0, L/8, ... 7L/8).
inline constexpr int32 PoseSamples = 8;
// Normale au-dela de laquelle une surface est consideree comme un appui (marche, lit).
inline constexpr float WalkableNormalZ = 0.6f;
// Penetration en-dessous de laquelle on ne depenetre pas.
inline constexpr float MinPenetrationDepth = 1.f;
// Tolerance de comparaison de position avant teleportation.
inline constexpr float MoveEpsilon = 0.1f;
// Rotation du mesh squelettique sous l'acteur (lacet -90).
inline constexpr double MeshYawDegrees = -90.0;
// Parametres de mesure. Les valeurs par defaut sont celles du jeu ; la sandbox surcharge
// CapsuleHalf et FloorLocalZ (son sol est a z = +10, donc son plancher local vaut 18 - PlayerSpot.Z).
struct FBoxFitParams
{
double CapsuleHalf = DefaultCapsuleHalf;
// Voie "animations" : FitCoupleBox (sandbox) et FitBox (jeu).
FVector AnimPad = FVector(12.0, 12.0, 6.0);
double AnimMaxSize = 800.0;
// Voie "corps vivants" : FitToBodies (jeu).
FVector LivePad = FVector(4.0, 4.0, 4.0);
FVector LiveMinExtent = FVector(8.0, 8.0, 8.0);
double LiveMaxSize = 400.0;
double LiveAnchorRadius = 140.0;
// Fond de boite, en Z local de l'acteur : la plante des pieds est a -CapsuleHalf.
double FloorLocalZ = -DefaultCapsuleHalf;
double MinExtentZ = 20.0;
};
struct FBoxFit
{
FVector Center = FVector::ZeroVector;
FVector Extent = FVector::ZeroVector;
// false : mesure absente ou hors plafond ; l'appelant garde la boite precedente.
bool bValid = false;
};
// --- Niveau pur : aucun UObject, aucun UWorld. Testable sans monde. ---
// Transformation commune du repere du mesh vers celui de l'acteur.
LPANIMCORE_API FTransform MeshToActor(double CapsuleHalf);
// Espace composant a partir des transformations locales et de la table des parents
// (Parents[i] < i, ou INDEX_NONE pour la racine, comme dans un FReferenceSkeleton).
LPANIMCORE_API void ComposeComponentSpace(
const TArray<int32>& Parents, const TArray<FTransform>& Local, TArray<FTransform>& OutComponent);
// Ajoute la position de chaque os (espace composant) a la boite, via MeshToActor.
LPANIMCORE_API void AccumulatePoints(
const TArray<FTransform>& Component, const FTransform& ToActor, FBox& InOutBounds);
// Os ecartes de la mesure des corps vivants (extremites et accessoires : la boite suivrait une
// main tendue). La voie "animations" ne les ecarte pas : ecart E6 de la cartographie.
LPANIMCORE_API bool IsExcludedLiveBone(const FString& BoneName);
// Boite brute -> boite finale, voie "animations" : padding, plafond, recalage du fond sur FloorLocalZ.
LPANIMCORE_API FBoxFit FitFromAnimBounds(const FBox& Bounds, const FBoxFitParams& Params);
// Boite brute -> boite finale, voie "corps vivants" : padding, etendue minimale, plafond. Pas de recalage.
LPANIMCORE_API FBoxFit FitFromLiveBounds(const FBox& Bounds, const FBoxFitParams& Params);
// Glissement le long d'une surface. TimeFraction : 1 - Hit.Time pour la part de course restante.
// bFlattenZ : la sandbox annule la composante verticale (son Z est verrouille par ailleurs).
LPANIMCORE_API FVector SlideWish(
const FVector& Wish, const FVector& Normal, float TimeFraction, bool bFlattenZ);
// --- Niveau lecture : assets et composants, sans effet de bord. ---
// Demi-hauteur reelle du personnage, avec repli (une capsule non initialisee mesure 0).
LPANIMCORE_API double CapsuleHalfOf(const ACharacter* Who, double Fallback = DefaultCapsuleHalf);
// Echantillonne une AnimSequence et ajoute les os a la boite. Os animes seulement,
// GetBoneTransform en donnees compressees (raw = false), rejet des poses NaN.
LPANIMCORE_API bool AccumulateAnimBounds(
const UAnimSequence* Anim, const FTransform& ToActor, FBox& InOutBounds, int32 Samples = PoseSamples);
// Ajoute les os affiches d'un mesh squelettique a la boite : ancre pelvis / hip, rayon
// LiveAnchorRadius, os exclus. Le mesh doit etre a jour (appel apres le tick d'animation).
LPANIMCORE_API void AccumulateLiveBody(
const USkeletalMeshComponent* Mesh, const FBoxFitParams& Params, FBox& InOutBounds);
// --- Niveau effets de bord : boite, corps, balayage. ---
// Cree la boite si besoin (profil commun : objet Pawn, tout Ignore sauf WorldDynamic = Block,
// cachee, hors navigation) et applique l'etat de collision. Retourne la boite a stocker.
LPANIMCORE_API UBoxComponent* EnsureBox(
ACharacter* Lead, UBoxComponent* Existing, FName Name, bool bEnabled);
// Etendue, centre et rotation nulle. Sans effet si Fit n'est pas valide.
LPANIMCORE_API void ApplyBoxFit(UBoxComponent* Box, const FBoxFit& Fit);
// Place le mesh squelettique sous l'acteur (plante des pieds a -CapsuleHalf, lacet -90).
LPANIMCORE_API void PlaceBodyMesh(ACharacter* Body, double CapsuleHalf);
// --- Soudure du partenaire ---
// Etat de mouvement memorise avant gel, pour le rendre a la sortie du show.
struct FSavedMove
{
TWeakObjectPtr<UCharacterMovementComponent> Move;
TEnumAsByte<EMovementMode> Mode = MOVE_None;
float Gravity = 1.f;
bool bTick = true;
};
// Arrete le mouvement, le desactive et coupe son tick. Retourne l'etat d'avant.
LPANIMCORE_API FSavedMove SaveAndFreeze(ACharacter* Body);
// Rend l'etat memorise, puis vide la memoire.
LPANIMCORE_API void RestoreMove(FSavedMove& Saved);
// Attache le partenaire au porteur et le pose sur son point (relatif nul).
LPANIMCORE_API void AttachPartner(ACharacter* Lead, ACharacter* Partner);
// Detache en gardant la position monde.
LPANIMCORE_API void Unweld(ACharacter* Partner);
// --- Balayage du couple ---
// Tout ce qui differe entre la sandbox et le jeu. Partir de Sandbox() ou de Show().
struct FSweepOptions
{
// Nombre de passes de glissement.
int32 MaxIterations = 3;
// SweepMultiByChannel (jeu : lits et depenetration ont besoin de tous les contacts)
// ou SweepSingleByChannel (sandbox).
bool bMultiSweep = true;
// Annule la composante verticale du glissement (sandbox).
bool bFlattenZ = false;
// Un contact dont la normale depasse WalkableNormalZ laisse passer le pas entier (sandbox).
// Voie simple seulement.
bool bAcceptWalkableStep = false;
// Sortir des penetrations au lieu de s'arreter net. Voie multiple seulement.
bool bDepenetrate = true;
// Ignorer tous les ACharacter du monde (jeu : les PNJ ne bloquent pas le couple).
bool bIgnoreAllCharacters = true;
// Appui franchissable : le couple se pose dessus au lieu d'etre bloque (jeu : les lits).
TFunction<bool(const UPrimitiveComponent*)> IsWalkableSupport;
// Acteur a ignorer entierement (jeu : la coque de la chambre, nommee Wall / Wall_*).
TFunction<bool(const AActor*)> IsIgnoredShell;
// Appele pour chaque acteur ignore par IsIgnoredShell, afin que l'appelant journalise.
TFunction<void(const AActor*)> OnIgnoredShell;
// Reglages du jeu : 3 passes, balayage multiple, depenetration, PNJ et coque ignores.
static FSweepOptions Show()
{
FSweepOptions Out;
return Out;
}
// Reglages de la sandbox : 2 passes, balayage simple, arret net en penetration,
// marches franchissables, glissement a plat, aucun PNJ ignore.
static FSweepOptions Sandbox()
{
FSweepOptions Out;
Out.MaxIterations = 2;
Out.bMultiSweep = false;
Out.bFlattenZ = true;
Out.bAcceptWalkableStep = true;
Out.bDepenetrate = false;
Out.bIgnoreAllCharacters = false;
return Out;
}
};
struct FSweepResult
{
// Position d'acteur retenue (From si rien n'a bouge).
FVector Location = FVector::ZeroVector;
// Dernier bloqueur en penetration, "Acteur/Composant", pour le journal. Vide sinon.
FString Blocker;
FVector BlockerNormal = FVector::ZeroVector;
float BlockerDepth = 0.f;
bool bMoved = false;
};
// Resout un deplacement du couple : la boite glisse, From est la position d'acteur de depart
// (sandbox : la position courante du porteur ; jeu : la derniere position sure), Wish le
// deplacement voulu. La fonction ne deplace rien : l'appelant applique FSweepResult::Location.
LPANIMCORE_API FSweepResult ResolveCoupleMove(
UWorld* World, const UBoxComponent* Box, const ACharacter* Lead, const ACharacter* Partner,
const FVector& From, const FVector& Wish, const FSweepOptions& Options);
}

View file

@ -0,0 +1,474 @@
// Geometrie commune du couple (lot S5-T2) - voir LPCoupleSim.h.
// Nom de fichier distinct de LPSandbox/LPCoupleSim.cpp : deux modules, deux unites de traduction.
#include "LPCoupleSim.h"
#include "Animation/AnimSequence.h"
#include "Animation/Skeleton.h"
#include "Components/BoxComponent.h"
#include "Components/CapsuleComponent.h"
#include "Components/SkeletalMeshComponent.h"
#include "Engine/World.h"
#include "EngineUtils.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
namespace LPCoupleSim
{
FTransform MeshToActor(const double CapsuleHalf)
{
return FTransform(FRotator(0.0, MeshYawDegrees, 0.0), FVector(0.0, 0.0, -CapsuleHalf));
}
void ComposeComponentSpace(const TArray<int32>& Parents, const TArray<FTransform>& Local, TArray<FTransform>& OutComponent)
{
const int32 Num = Local.Num();
OutComponent.SetNum(Num, EAllowShrinking::No);
for (int32 Bone = 0; Bone < Num; ++Bone)
{
const int32 Parent = Parents.IsValidIndex(Bone) ? Parents[Bone] : INDEX_NONE;
OutComponent[Bone] = Parent == INDEX_NONE ? Local[Bone] : Local[Bone] * OutComponent[Parent];
}
}
void AccumulatePoints(const TArray<FTransform>& Component, const FTransform& ToActor, FBox& InOutBounds)
{
for (const FTransform& Bone : Component)
{
InOutBounds += ToActor.TransformPosition(Bone.GetLocation());
}
}
bool IsExcludedLiveBone(const FString& BoneName)
{
static const TCHAR* Excluded[] = {
TEXT("hand"), TEXT("finger"), TEXT("foot"), TEXT("toe"), TEXT("twist"),
TEXT("breast"), TEXT("hair"), TEXT("eye"), TEXT("jaw"), TEXT("weapon"), TEXT("ik")
};
for (const TCHAR* Part : Excluded)
{
if (BoneName.Contains(Part, ESearchCase::IgnoreCase))
{
return true;
}
}
return false;
}
FBoxFit FitFromAnimBounds(const FBox& Bounds, const FBoxFitParams& Params)
{
FBoxFit Fit;
if (!Bounds.IsValid || Bounds.GetSize().GetMax() > Params.AnimMaxSize)
{
return Fit;
}
Fit.Center = Bounds.GetCenter();
Fit.Extent = Bounds.GetExtent() + Params.AnimPad;
const double Bottom = Fit.Center.Z - Fit.Extent.Z;
if (Bottom < Params.FloorLocalZ)
{
const double Cut = Params.FloorLocalZ - Bottom;
Fit.Extent.Z = FMath::Max(Params.MinExtentZ, Fit.Extent.Z - Cut * 0.5);
Fit.Center.Z += Cut * 0.5;
}
Fit.bValid = true;
return Fit;
}
FBoxFit FitFromLiveBounds(const FBox& Bounds, const FBoxFitParams& Params)
{
FBoxFit Fit;
if (!Bounds.IsValid || Bounds.GetSize().GetMax() > Params.LiveMaxSize)
{
return Fit;
}
Fit.Center = Bounds.GetCenter();
Fit.Extent = Bounds.GetExtent() + Params.LivePad;
Fit.Extent.X = FMath::Max(Fit.Extent.X, Params.LiveMinExtent.X);
Fit.Extent.Y = FMath::Max(Fit.Extent.Y, Params.LiveMinExtent.Y);
Fit.Extent.Z = FMath::Max(Fit.Extent.Z, Params.LiveMinExtent.Z);
Fit.bValid = true;
return Fit;
}
FVector SlideWish(const FVector& Wish, const FVector& Normal, const float TimeFraction, const bool bFlattenZ)
{
FVector Out = FVector::VectorPlaneProject(Wish * TimeFraction, Normal);
if (bFlattenZ)
{
Out.Z = 0.0;
}
return Out;
}
double CapsuleHalfOf(const ACharacter* Who, const double Fallback)
{
const UCapsuleComponent* Cap = Who ? Who->GetCapsuleComponent() : nullptr;
return (Cap && Cap->GetUnscaledCapsuleHalfHeight() > 1.f) ? Cap->GetUnscaledCapsuleHalfHeight() : Fallback;
}
bool AccumulateAnimBounds(const UAnimSequence* Anim, const FTransform& ToActor, FBox& InOutBounds, const int32 Samples)
{
const USkeleton* Skeleton = Anim ? Anim->GetSkeleton() : nullptr;
if (!Skeleton || Samples <= 0)
{
return false;
}
const FReferenceSkeleton& Ref = Skeleton->GetReferenceSkeleton();
const TArray<FTransform>& RefPose = Ref.GetRefBonePose();
const int32 Num = RefPose.Num();
if (Num <= 0)
{
return false;
}
// Seuls les os pilotes par le clip sont lus ; les autres gardent leur repos.
TArray<int32> Animated;
for (const FTrackToSkeletonMap& Track : Anim->GetCompressedTrackToSkeletonMapTable())
{
if (RefPose.IsValidIndex(Track.BoneTreeIndex))
{
Animated.Add(Track.BoneTreeIndex);
}
}
TArray<int32> Parents;
Parents.SetNumUninitialized(Num);
for (int32 Bone = 0; Bone < Num; ++Bone)
{
Parents[Bone] = Ref.GetParentIndex(Bone);
}
const double Length = FMath::Max(Anim->GetPlayLength(), 0.01);
TArray<FTransform> Local;
TArray<FTransform> Component;
Local.SetNum(Num);
Component.SetNum(Num);
for (int32 Step = 0; Step < Samples; ++Step)
{
const double Time = Length * Step / static_cast<double>(Samples);
for (int32 Bone = 0; Bone < Num; ++Bone)
{
Local[Bone] = RefPose[Bone];
}
for (const int32 Index : Animated)
{
FTransform Xform = RefPose[Index];
Anim->GetBoneTransform(Xform, FSkeletonPoseBoneIndex(Index), Time, false);
if (!Xform.ContainsNaN())
{
Local[Index] = Xform;
}
}
ComposeComponentSpace(Parents, Local, Component);
AccumulatePoints(Component, ToActor, InOutBounds);
}
return true;
}
void AccumulateLiveBody(const USkeletalMeshComponent* Mesh, const FBoxFitParams& Params, FBox& InOutBounds)
{
if (!Mesh)
{
return;
}
const TArray<FTransform>& Bones = Mesh->GetComponentSpaceTransforms();
const int32 Num = Bones.Num();
if (Num <= 0)
{
return;
}
const FTransform ToActor = Mesh->GetRelativeTransform();
int32 Pelvis = INDEX_NONE;
for (int32 Bone = 0; Bone < Num; ++Bone)
{
const FName Name = Mesh->GetBoneName(Bone);
if (Name == TEXT("pelvis") || Name == TEXT("hip"))
{
Pelvis = Bone;
break;
}
}
FVector Anchor = FVector::ZeroVector;
const bool bAnchor = Pelvis != INDEX_NONE && !Bones[Pelvis].ContainsNaN();
if (bAnchor)
{
Anchor = ToActor.TransformPosition(Bones[Pelvis].GetLocation());
}
const double RadiusSquared = Params.LiveAnchorRadius * Params.LiveAnchorRadius;
for (int32 Bone = 0; Bone < Num; ++Bone)
{
if (Bones[Bone].ContainsNaN() || IsExcludedLiveBone(Mesh->GetBoneName(Bone).ToString()))
{
continue;
}
const FVector Point = ToActor.TransformPosition(Bones[Bone].GetLocation());
if (bAnchor && FVector::DistSquared(Point, Anchor) > RadiusSquared)
{
continue;
}
InOutBounds += Point;
}
}
UBoxComponent* EnsureBox(ACharacter* Lead, UBoxComponent* Existing, const FName Name, const bool bEnabled)
{
if (!Lead)
{
return Existing;
}
UBoxComponent* Box = Existing;
if (!Box)
{
Box = NewObject<UBoxComponent>(Lead, Name);
Box->SetupAttachment(Lead->GetRootComponent());
Box->SetCollisionObjectType(ECC_Pawn);
Box->SetCollisionResponseToAllChannels(ECR_Ignore);
Box->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Block);
Box->SetHiddenInGame(true);
Box->SetCanEverAffectNavigation(false);
Box->RegisterComponent();
}
Box->SetCollisionEnabled(bEnabled ? ECollisionEnabled::QueryAndPhysics : ECollisionEnabled::NoCollision);
return Box;
}
void ApplyBoxFit(UBoxComponent* Box, const FBoxFit& Fit)
{
if (!Box || !Fit.bValid)
{
return;
}
Box->SetBoxExtent(Fit.Extent);
Box->SetRelativeLocation(Fit.Center);
Box->SetRelativeRotation(FRotator::ZeroRotator);
}
void PlaceBodyMesh(ACharacter* Body, const double CapsuleHalf)
{
if (USkeletalMeshComponent* Mesh = Body ? Body->GetMesh() : nullptr)
{
Mesh->SetRelativeLocation(FVector(0.0, 0.0, -CapsuleHalf));
Mesh->SetRelativeRotation(FRotator(0.0, MeshYawDegrees, 0.0));
}
}
FSavedMove SaveAndFreeze(ACharacter* Body)
{
FSavedMove Saved;
UCharacterMovementComponent* Move = Body ? Body->GetCharacterMovement() : nullptr;
if (!Move)
{
return Saved;
}
Saved.Move = Move;
Saved.Mode = Move->MovementMode;
Saved.Gravity = Move->GravityScale;
Saved.bTick = Move->IsComponentTickEnabled();
Move->StopMovementImmediately();
Move->DisableMovement();
Move->SetComponentTickEnabled(false);
return Saved;
}
void RestoreMove(FSavedMove& Saved)
{
if (UCharacterMovementComponent* Move = Saved.Move.Get())
{
Move->SetComponentTickEnabled(Saved.bTick);
Move->SetMovementMode(Saved.Mode);
Move->GravityScale = Saved.Gravity;
}
Saved = FSavedMove();
}
void AttachPartner(ACharacter* Lead, ACharacter* Partner)
{
if (!Lead || !Partner)
{
return;
}
if (Partner->GetAttachParentActor() != Lead)
{
Partner->AttachToActor(Lead, FAttachmentTransformRules::SnapToTargetNotIncludingScale);
}
Partner->SetActorRelativeLocation(FVector::ZeroVector);
Partner->SetActorRelativeRotation(FRotator::ZeroRotator);
}
void Unweld(ACharacter* Partner)
{
if (Partner)
{
Partner->DetachFromActor(FDetachmentTransformRules::KeepWorldTransform);
}
}
namespace
{
// Voie simple (sandbox) : un contact a la fois, arret net en penetration, marches franchissables.
void ResolveSingle(UWorld& World, const FCollisionShape& Shape, const FQuat& Rot, const FVector& BoxOffset,
const FCollisionQueryParams& Params, const FSweepOptions& Options, const FVector& Wish, FVector& InOutBase)
{
FVector Slide = Wish;
for (int32 Step = 0; Step < Options.MaxIterations && !Slide.IsNearlyZero(); ++Step)
{
const FVector Start = InOutBase + BoxOffset;
FHitResult Hit;
const bool bHit = World.SweepSingleByChannel(Hit, Start, Start + Slide, Rot, ECC_Pawn, Shape, Params);
if (bHit && Options.bAcceptWalkableStep && Hit.Normal.Z > WalkableNormalZ)
{
InOutBase += Slide;
return;
}
const FVector StepDelta = Slide * (bHit ? Hit.Time : 1.f);
if (!StepDelta.IsNearlyZero() && !Hit.bStartPenetrating)
{
InOutBase += StepDelta;
}
if (!bHit || Hit.bStartPenetrating)
{
return;
}
Slide = SlideWish(Slide, Hit.Normal, 1.f - Hit.Time, Options.bFlattenZ);
}
}
// Voie multiple (jeu) : tous les contacts, depenetration, appuis franchissables.
void ResolveMulti(UWorld& World, const FCollisionShape& Shape, const FQuat& Rot, const FVector& BoxOffset,
const FCollisionQueryParams& Params, const FSweepOptions& Options, const FVector& Wish,
FVector& InOutBase, FSweepResult& InOutResult)
{
FVector Slide = Wish;
for (int32 Step = 0; Step < Options.MaxIterations; ++Step)
{
const FVector Start = InOutBase + BoxOffset;
TArray<FHitResult> Hits;
World.SweepMultiByChannel(Hits, Start, Start + Slide, Rot, ECC_Pawn, Shape, Params);
const FHitResult* Block = nullptr;
const FHitResult* Deepest = nullptr;
FVector Exit = FVector::ZeroVector;
bool bStopNet = false;
for (const FHitResult& Hit : Hits)
{
if (Options.IsWalkableSupport && Options.IsWalkableSupport(Hit.GetComponent()))
{
// Descente sur l'appui : on s'y pose. Deja dedans : on annule la descente et on le traverse.
if (!Hit.bStartPenetrating && Hit.Normal.Z > WalkableNormalZ && Slide.Z < -1.f)
{
Block = &Hit;
break;
}
if (Hit.bStartPenetrating && Hit.Normal.Z > WalkableNormalZ && Slide.Z < 0.f)
{
Slide.Z = 0.0;
}
continue;
}
if (Hit.bStartPenetrating)
{
if (!Options.bDepenetrate)
{
bStopNet = true;
break;
}
if (Hit.PenetrationDepth < MinPenetrationDepth)
{
continue;
}
if (!Deepest || Hit.PenetrationDepth > Deepest->PenetrationDepth)
{
Deepest = &Hit;
Exit = Hit.Normal * Hit.PenetrationDepth;
}
continue;
}
Block = &Hit;
break;
}
if (bStopNet)
{
return;
}
// Mur vertical traverse et course qui pointe dedans : le glissement longe le mur.
if (Deepest && FMath::Abs(Deepest->Normal.Z) <= WalkableNormalZ && (Slide | Deepest->Normal) < 0.f)
{
InOutResult.Blocker = FString::Printf(TEXT("%s/%s"),
*GetNameSafe(Deepest->GetActor()), *GetNameSafe(Deepest->GetComponent()));
InOutResult.BlockerNormal = Deepest->Normal;
InOutResult.BlockerDepth = Deepest->PenetrationDepth;
Slide = SlideWish(Slide, Deepest->Normal, 1.f, Options.bFlattenZ);
}
if (!Exit.IsNearlyZero())
{
InOutBase += Exit;
continue;
}
if (Slide.IsNearlyZero())
{
return;
}
if (!Block)
{
InOutBase += Slide;
return;
}
InOutBase += Slide * Block->Time;
Slide = SlideWish(Slide, Block->Normal, 1.f - Block->Time, Options.bFlattenZ);
}
}
}
FSweepResult ResolveCoupleMove(UWorld* World, const UBoxComponent* Box, const ACharacter* Lead,
const ACharacter* Partner, const FVector& From, const FVector& Wish, const FSweepOptions& Options)
{
FSweepResult Result;
Result.Location = From;
if (!World || !Box || !Lead || Wish.IsNearlyZero())
{
return Result;
}
FCollisionQueryParams Params(SCENE_QUERY_STAT(LPCoupleSim), false, Lead);
if (Partner)
{
Params.AddIgnoredActor(Partner);
}
if (Options.bIgnoreAllCharacters)
{
for (TActorIterator<ACharacter> It(World); It; ++It)
{
Params.AddIgnoredActor(*It);
}
}
if (Options.IsIgnoredShell)
{
for (TActorIterator<AActor> It(World); It; ++It)
{
if (!Options.IsIgnoredShell(*It))
{
continue;
}
Params.AddIgnoredActor(*It);
if (Options.OnIgnoredShell)
{
Options.OnIgnoredShell(*It);
}
}
}
// La boite suit le porteur : son decalage monde est fixe pendant la resolution (la rotation ne change pas).
const FVector BoxOffset = Box->GetComponentLocation() - Lead->GetActorLocation();
const FCollisionShape Shape = FCollisionShape::MakeBox(Box->GetScaledBoxExtent());
const FQuat Rot = Box->GetComponentQuat();
FVector Base = From;
if (Options.bMultiSweep)
{
ResolveMulti(*World, Shape, Rot, BoxOffset, Params, Options, Wish, Base, Result);
}
else
{
ResolveSingle(*World, Shape, Rot, BoxOffset, Params, Options, Wish, Base);
}
Result.Location = Base;
Result.bMoved = !Base.Equals(From, MoveEpsilon);
return Result;
}
}

View file

@ -40,9 +40,9 @@ public class LifePlay : ModuleRules
// Releve de collision (-LPCollisionLog) : nombre de triangles des trimesh Chaos. // Releve de collision (-LPCollisionLog) : nombre de triangles des trimesh Chaos.
PrivateDependencyModuleNames.Add("Chaos"); PrivateDependencyModuleNames.Add("Chaos");
// Lot S2 / S3 : LPAnimMeasure (plugin LPSandbox) — LPDirectControlSubsystem et // Lot DC1 : LPAnimCore (plugin du jeu, Plugins/LPAnimCore) — LPDirectControlSubsystem inclut
// LifePlayEditorTools incluent LPAnimRedirect / LPPoseMeasure. // LPAnimRedirect, LPShowCoupleSubsystem.h inclut LPCoupleSim (dependance publique).
PublicDependencyModuleNames.Add("LPAnimMeasure"); PublicDependencyModuleNames.Add("LPAnimCore");
// Uncomment if you are using Slate UI // Uncomment if you are using Slate UI
// PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" }); // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });