From d8a0f859cedb46d78fe59a3ab6d6ae1ff4947b0e Mon Sep 17 00:00:00 2001 From: edujiro5972 <336072401+edujiro5972@users.noreply.github.com> Date: Wed, 7 Oct 2026 18:31:01 +0200 Subject: [PATCH] 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 --- Docs/Migration/07_Architecture_5.5.md | 1 + Docs/Migration/20_Chaine_Animations.md | 2 +- Docs/Migration/21_Sandbox_Addon.md | 22 +- Docs/Migration/Journal_Modifications.md | 1 + Docs/Migration/etat/sandbox/DC1/DC1-BUILD.md | 66 +++ Docs/Migration/etat/sandbox/DC1/DC1-DOC.md | 28 ++ Docs/Migration/etat/sandbox/DC1/DC1-IMPL.md | 35 ++ Docs/Migration/etat/sandbox/DC1/DC1-VAL.md | 124 +++++ Docs/Migration/etat/sandbox/S3/S3-INT.md | 6 +- LifePlay.uproject | 4 + Plugins/LPAnimCore/LPAnimCore.uplugin | 21 + .../Source/LPAnimCore/LPAnimCore.Build.cs | 18 + .../Source/LPAnimCore/LPAnimCore.cpp | 5 + .../Source/LPAnimCore/LPAnimRedirect.cpp | 62 +++ .../Source/LPAnimCore/LPAnimRedirect.h | 27 + .../Source/LPAnimCore/LPCoupleSim.h | 215 ++++++++ .../Source/LPAnimCore/LPCoupleSimCore.cpp | 474 ++++++++++++++++++ Source/LifePlay/LifePlay.Build.cs | 6 +- 18 files changed, 1103 insertions(+), 14 deletions(-) create mode 100644 Docs/Migration/etat/sandbox/DC1/DC1-BUILD.md create mode 100644 Docs/Migration/etat/sandbox/DC1/DC1-DOC.md create mode 100644 Docs/Migration/etat/sandbox/DC1/DC1-IMPL.md create mode 100644 Docs/Migration/etat/sandbox/DC1/DC1-VAL.md create mode 100644 Plugins/LPAnimCore/LPAnimCore.uplugin create mode 100644 Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.Build.cs create mode 100644 Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.cpp create mode 100644 Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.cpp create mode 100644 Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.h create mode 100644 Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSim.h create mode 100644 Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSimCore.cpp diff --git a/Docs/Migration/07_Architecture_5.5.md b/Docs/Migration/07_Architecture_5.5.md index 1e89973..daac3c0 100644 --- a/Docs/Migration/07_Architecture_5.5.md +++ b/Docs/Migration/07_Architecture_5.5.md @@ -80,6 +80,7 @@ Structure plate (pas de sous-dossier `Public/`) : `PublicIncludePaths.Add(Module | Plugin | Cible | Remarque | |---|---|---| | `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) | | `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) | diff --git a/Docs/Migration/20_Chaine_Animations.md b/Docs/Migration/20_Chaine_Animations.md index 628caf9..fd1b882 100644 --- a/Docs/Migration/20_Chaine_Animations.md +++ b/Docs/Migration/20_Chaine_Animations.md @@ -81,7 +81,7 @@ Rien n'est supprimé : un résultat précédent est déplacé dans `precedent_(Editor)"] -->|Public| LP["LifePlay
(Runtime, module primaire)"] LPET -->|Public
FLPPose dans LPAnimChecks.h| AM["LPAnimMeasure
(Runtime, nouveau S2)"] LP -->|Public
LPPairSandbox, LPDirectControl| AM - LP -.->|"StartupModule : SetUALRoot(QuaterniusUALRoot)"| AM + LP -->|Public DC1| AC["LPAnimCore
(plugin du jeu : LPAnimRedirect, LPCoupleSim)"] + AM -->|Private DC1| AC + SB["LPSandbox
(addon)"] -->|DC1| AM + SB -->|DC1| AC + SB -.->|"LPSandbox.cpp:17 : SetUALRoot(QuaterniusUALRoot)"| AC AM -->|Public| ENG["Core, CoreUObject, Engine"] AM -->|Private| JSON["Json"] ``` @@ -96,8 +100,11 @@ Règles : `LPAnimMeasure` ne dépend ni de `LifePlay` ni de `LifePlayEditorTools | De | Vers | Type | Motif | |---|---|---|---| | `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` | `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` | `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`). - **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 cd D:\Game 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 ``` @@ -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] 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`. -- [ ] **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). - [ ] **DC3** : Scripts, données et sources de mods vers la sandbox (DC3-T1..T5). - [ ] **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) | Lot | Objet (une ligne) | diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 46a9a72..432e5b6 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -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-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-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-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é | diff --git a/Docs/Migration/etat/sandbox/DC1/DC1-BUILD.md b/Docs/Migration/etat/sandbox/DC1/DC1-BUILD.md new file mode 100644 index 0000000..6074f0b --- /dev/null +++ b/Docs/Migration/etat/sandbox/DC1/DC1-BUILD.md @@ -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. diff --git a/Docs/Migration/etat/sandbox/DC1/DC1-DOC.md b/Docs/Migration/etat/sandbox/DC1/DC1-DOC.md new file mode 100644 index 0000000..44ffd39 --- /dev/null +++ b/Docs/Migration/etat/sandbox/DC1/DC1-DOC.md @@ -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. diff --git a/Docs/Migration/etat/sandbox/DC1/DC1-IMPL.md b/Docs/Migration/etat/sandbox/DC1/DC1-IMPL.md new file mode 100644 index 0000000..125b4e3 --- /dev/null +++ b/Docs/Migration/etat/sandbox/DC1/DC1-IMPL.md @@ -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). diff --git a/Docs/Migration/etat/sandbox/DC1/DC1-VAL.md b/Docs/Migration/etat/sandbox/DC1/DC1-VAL.md new file mode 100644 index 0000000..a410535 --- /dev/null +++ b/Docs/Migration/etat/sandbox/DC1/DC1-VAL.md @@ -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 -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\\` ; nouveau : `Saved\AnimPipeline\\`. 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.** diff --git a/Docs/Migration/etat/sandbox/S3/S3-INT.md b/Docs/Migration/etat/sandbox/S3/S3-INT.md index 056a29c..0706ed4 100644 --- a/Docs/Migration/etat/sandbox/S3/S3-INT.md +++ b/Docs/Migration/etat/sandbox/S3/S3-INT.md @@ -8,9 +8,9 @@ Date : 2026-10-06 — Role A-INT |---|---| | Repo addon | https://github.com/edujiro5972/LifePlay-Sandbox.git | | 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`) | -| 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 @@ -46,7 +46,7 @@ sur la copie effectivement chargee. Pin manuel a la place (ci-dessous). ```powershell cd D:\Game 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 ``` diff --git a/LifePlay.uproject b/LifePlay.uproject index 16b975b..6cffc0a 100644 --- a/LifePlay.uproject +++ b/LifePlay.uproject @@ -19,6 +19,10 @@ } ], "Plugins": [ + { + "Name": "LPAnimCore", + "Enabled": true + }, { "Name": "LPSandbox", "Enabled": true, diff --git a/Plugins/LPAnimCore/LPAnimCore.uplugin b/Plugins/LPAnimCore/LPAnimCore.uplugin new file mode 100644 index 0000000..1f33289 --- /dev/null +++ b/Plugins/LPAnimCore/LPAnimCore.uplugin @@ -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" + } + ] +} diff --git a/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.Build.cs b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.Build.cs new file mode 100644 index 0000000..3fdb197 --- /dev/null +++ b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.Build.cs @@ -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" }); + } +} diff --git a/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.cpp b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.cpp new file mode 100644 index 0000000..e0d6dd0 --- /dev/null +++ b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimCore.cpp @@ -0,0 +1,5 @@ +// Module LPAnimCore (lot DC1) : LPAnimRedirect, LPCoupleSim. + +#include "Modules/ModuleManager.h" + +IMPLEMENT_MODULE(FDefaultModuleImpl, LPAnimCore) diff --git a/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.cpp b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.cpp new file mode 100644 index 0000000..12dbfd5 --- /dev/null +++ b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.cpp @@ -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); + } +} diff --git a/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.h b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.h new file mode 100644 index 0000000..7047537 --- /dev/null +++ b/Plugins/LPAnimCore/Source/LPAnimCore/LPAnimRedirect.h @@ -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 (, Bridget B_, Michael M_) dans un dossier distinct de celui des + // clips source (pack UE5 : /Game/AnimPacks/). Vide = meme dossier que les clips source (Quaternius). + LPANIMCORE_API void SetBodyRoot(const FString& Root); + LPANIMCORE_API FString BodyClipPath(const FString& AssetName); +} diff --git a/Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSim.h b/Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSim.h new file mode 100644 index 0000000..edbbfb3 --- /dev/null +++ b/Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSim.h @@ -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& Parents, const TArray& Local, TArray& OutComponent); + + // Ajoute la position de chaque os (espace composant) a la boite, via MeshToActor. + LPANIMCORE_API void AccumulatePoints( + const TArray& 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 Move; + TEnumAsByte 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 IsWalkableSupport; + // Acteur a ignorer entierement (jeu : la coque de la chambre, nommee Wall / Wall_*). + TFunction IsIgnoredShell; + // Appele pour chaque acteur ignore par IsIgnoredShell, afin que l'appelant journalise. + TFunction 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); +} diff --git a/Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSimCore.cpp b/Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSimCore.cpp new file mode 100644 index 0000000..cd8aaab --- /dev/null +++ b/Plugins/LPAnimCore/Source/LPAnimCore/LPCoupleSimCore.cpp @@ -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& Parents, const TArray& Local, TArray& 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& 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& 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 Animated; + for (const FTrackToSkeletonMap& Track : Anim->GetCompressedTrackToSkeletonMapTable()) + { + if (RefPose.IsValidIndex(Track.BoneTreeIndex)) + { + Animated.Add(Track.BoneTreeIndex); + } + } + TArray 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 Local; + TArray Component; + Local.SetNum(Num); + Component.SetNum(Num); + for (int32 Step = 0; Step < Samples; ++Step) + { + const double Time = Length * Step / static_cast(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& 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(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 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 It(World); It; ++It) + { + Params.AddIgnoredActor(*It); + } + } + if (Options.IsIgnoredShell) + { + for (TActorIterator 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; +} + +} diff --git a/Source/LifePlay/LifePlay.Build.cs b/Source/LifePlay/LifePlay.Build.cs index 93247e9..64a524d 100644 --- a/Source/LifePlay/LifePlay.Build.cs +++ b/Source/LifePlay/LifePlay.Build.cs @@ -40,9 +40,9 @@ public class LifePlay : ModuleRules // Releve de collision (-LPCollisionLog) : nombre de triangles des trimesh Chaos. PrivateDependencyModuleNames.Add("Chaos"); - // Lot S2 / S3 : LPAnimMeasure (plugin LPSandbox) — LPDirectControlSubsystem et - // LifePlayEditorTools incluent LPAnimRedirect / LPPoseMeasure. - PublicDependencyModuleNames.Add("LPAnimMeasure"); + // Lot DC1 : LPAnimCore (plugin du jeu, Plugins/LPAnimCore) — LPDirectControlSubsystem inclut + // LPAnimRedirect, LPShowCoupleSubsystem.h inclut LPCoupleSim (dependance publique). + PublicDependencyModuleNames.Add("LPAnimCore"); // Uncomment if you are using Slate UI // PrivateDependencyModuleNames.AddRange(new string[] { "Slate", "SlateCore" });