503 lines
40 KiB
Markdown
503 lines
40 KiB
Markdown
# 23. Cycle de vie d'un mod (2026-10-07)
|
||
|
||
> Depuis le lot DC3 (2026-10-07), ces scripts, `ModSources/` et `AnimSources/` sont dans le dépôt addon `LifePlay-Sandbox` (même disposition relative ; lancer depuis l'addon, `D:\Game\LifePlay-Sandbox`).
|
||
|
||
Référence pour les développeurs du jeu et des outils de mods : ce qu'est un mod LifePlay, comment il est fabriqué,
|
||
livré, découvert, activé et consommé, quelles propriétés sont réellement lues, et ce qui pourrait évoluer.
|
||
Constats relevés dans le code du jeu, des Blueprints (`GI`) et du moteur UE 5.5 installé
|
||
(`Engine/Source/Runtime/Projects`) le 2026-10-07. Guide des moddeurs (anglais) :
|
||
`Docs/Modding/FullGuides/Creating_Asset_Mods.md`.
|
||
|
||
Documents liés : [01](01_Architecture_Initiale.md) (données texte, `GI`), [15](15_Modules_Optionnels.md) (modules
|
||
livrés à part), [16](16_H3_Mods_Paquets.md) (mods d'assets H3), [17](17_Mods_Ecran_Titre.md) (gestionnaire de mods
|
||
depuis l'écran titre), [20](20_Chaine_Animations.md) (packs d'animations AP1–AP7).
|
||
|
||
## 1. Deux moitiés, deux mécanismes
|
||
|
||
Un mod LifePlay est un **module texte** du jeu, éventuellement accompagné d'un **plugin de contenu** Unreal.
|
||
|
||
| | Module LifePlay (texte) | Plugin de contenu (assets cuits) |
|
||
|---|---|---|
|
||
| Emplacement installé | `LifePlay/Content/Modules/<id>/` | `LifePlay/Mods/<id>/` |
|
||
| Descripteur | `<id>.lpmod` (`CLÉ: valeur`) | `<id>.uplugin` (JSON) |
|
||
| Contenu | `.lpaction`, `.lpscene`, `.lpstat`, `.lpquest`, `.lptalk`, `.lpdesc`, `.lpai`, `.lpcharacter`, `.lpworld`, `.lphair`, tables, crédits, catalogue | `Content/Paks/Windows/<id>LifePlay-Windows.pak/.utoc/.ucas` (IoStore, `AssetRegistry.bin` du mod dans le `.pak`) |
|
||
| Lu par | `ModdingExtra` (`LoadTxt`, `LPDataCache`), Blueprint `GI`, sous-systèmes C++ | Gestionnaire de plugins du moteur (montage), registre d'assets |
|
||
| Activation | case du gestionnaire de mods (`GI.ModEnableds`) | toujours monté s'il est présent (plugin `Mod` activé par défaut) |
|
||
| Existe depuis | jeu d'origine (Vinfamy) | lot H3 (2026-10-03) |
|
||
|
||
Mods texte seuls : `vin_*`. Mods d'assets : `lpr_hair_pocolov` (coiffures), `lpr_anim_quaternius` et
|
||
`lpr_anim_freeanims` (animations). Les deux moitiés portent le **même identifiant** `<id>` : `MODULE_UNIQUEID` du
|
||
`.lpmod` = nom du plugin = point de montage `/<id>/` des assets.
|
||
|
||
## 2. Cycle de vie
|
||
|
||
```
|
||
FABRICATION (projet source) LIVRAISON JEU (à chaque démarrage)
|
||
───────────────────────── ───────── ────────────────────────
|
||
Tools/NewMod.ps1 → ModSources/<id>/ <id>-<v>-for-LifePlay-<jeu>.zip
|
||
.uplugin, Module/<id>.lpmod, CREDITS LifePlay/Mods/<id>/… 1. moteur : découverte Mods/, montage
|
||
import des assets (HairPipeline, LifePlay/Content/Modules/<id>/ 2. registre d'assets : AssetRegistry.bin
|
||
AnimPipeline → DeliverAnims) README.txt 3. GI : LoadSettings → ModEnableds
|
||
Tools/BuildMod.ps1 -Name <id> → removeDeletedMods → PopulateRaws
|
||
cuisson DLC -basedonreleaseversion installation : extraire 4. sous-systèmes C++ : métadonnées,
|
||
contrôle du conteneur, archive dans le dossier du jeu coiffures, animations, avertissement
|
||
désinstallation : supprimer 5. gestionnaire de mods : cocher / décocher
|
||
les deux dossiers → PopulateRaws, SaveModSelection
|
||
```
|
||
|
||
### 2.1 Fabrication
|
||
|
||
| Étape | Outil | Résultat |
|
||
|---|---|---|
|
||
| Squelette | `Tools/NewMod.ps1 -Name <id> [-Hairs]` | `ModSources/<id>/<id>.uplugin`, `Module/<id>.lpmod` (trois premières lignes imposées, voir § 3.2), `CREDITS.txt` |
|
||
| Édition | `Tools/EditMod.ps1`, `Tools/ModSource.ps1` | le mod est déplacé dans `<projet>/Mods/` le temps du travail, puis remis dans `ModSources/` : le jeu de base n'en contient jamais rien |
|
||
| Assets : coiffures | `Tools/HairPipeline.ps1 -Config Tools/mods/<id>.json` | `/<id>/Hairs/…` + fichiers `.lphair` |
|
||
| Assets : animations | `Tools/anim/AnimPipeline.ps1` (`BuildMod.ps1 -Anim <pack>`), étape 6 `DeliverAnims` | `/<id>/Anims/Female/<clé>_F`, `/<id>/Anims/Male/<clé>_M`, `.lpmod` et `CREDITS.txt` tirés de `AnimSources/<pack>/pack.json`, catalogue, vignettes, planches |
|
||
| Cuisson | `Tools/BuildMod.ps1 -Name <id> -Version <jeu>` | cuisson DLC du seul plugin contre `Releases/<jeu>` ; refus si un autre plugin est dans `Mods/`, si une entrée du conteneur sort de `Mods/<id>/`, si un mod payant cite une licence non commerciale |
|
||
| Archive | `BuildMod.ps1` | `<id>-<version>-for-LifePlay-<jeu>.zip` testée ; le `.lpmod` livré reçoit `MODULE_PACKAGED` et `MODULE_GAME_VERSION`, le `.uplugin` livré `VersionName` et `CreatedByURL` |
|
||
|
||
Contrainte de compatibilité : un conteneur de mod est cuit **contre une version de référence précise** du jeu
|
||
(`Releases/<version>`, produite par `-createreleaseversion` à chaque publication). Les assets de base qu'il référence
|
||
(squelettes Genesis, matériau `DazHair`, paramètres de compression du projet) ne doivent plus être renommés ni déplacés.
|
||
Un mod ne doit référencer aucun contenu `/Engine/` non cuit par le jeu (cas des clips UE5 en AP7 : compression remise
|
||
au défaut du projet par `DeliverAnims.py`).
|
||
|
||
### 2.2 Installation et désinstallation
|
||
|
||
Le joueur extrait l'archive dans le dossier du jeu (celui de `LifePlay.exe`), en fusionnant le dossier `LifePlay`.
|
||
Pour désinstaller : supprimer `Content/Modules/<id>` **et** `Mods/<id>`. Aucun outil ne vérifie aujourd'hui qu'une
|
||
moitié n'a pas été laissée seule (voir § 6).
|
||
|
||
### 2.3 Démarrage du jeu
|
||
|
||
| Ordre | Qui | Ce qui se passe |
|
||
|---|---|---|
|
||
| 1 | Moteur, `FPluginManager::ReadAllPlugins` | lit **toujours** `<jeu>/Mods/` ; chaque `Mods/<id>/<id>.uplugin` devient un plugin de type `Mod`, activé par défaut (`FPlugin::IsEnabledByDefault` : chargé « depuis le projet ») |
|
||
| 2 | Moteur | `CanContainContent: true` : le point de montage `/<id>/` est ajouté, les conteneurs `Content/Paks/Windows/*` sont montés |
|
||
| 3 | Registre d'assets | ajoute le `AssetRegistry.bin` de chaque plugin de contenu : les assets du mod sont trouvables par chemin et par classe sans être chargés |
|
||
| 4 | `ULPModInfoSubsystem::Initialize` (C++) | lit chaque `Modules/<id>/<id>.lpmod` (toutes les clés), retient `MODULE_GAME_VERSION` ; journal `Modules : N .lpmod lu(s)` |
|
||
| 5 | `ULPModDailiesSubsystem::Initialize` (C++, AP5) | relève les `UAnimSequence` sous `/<plugin>/Anims` de chaque plugin `Mod` monté ; clé = nom sans suffixe `_F`/`_M` |
|
||
| 6 | `GI.LoadSettings` (Blueprint) | `GameSettings.sav` → `ModEnableds` ; `ReadModLauncher` (`Modules/ModLauncher.config`, absent en pratique) ; `removeDeletedMods` retire de la liste les modules dont le `.lpmod` n'existe plus |
|
||
| 7 | `ULPHairstyleSubsystem` (C++) | après `LoadSettings`, ajoute cochés les modules `MODULE_DEFAULT_ENABLED: true` absents de la liste ; relève les `.lphair` |
|
||
| 8 | `GI.PopulateRaws` (Blueprint) | pour **chaque module coché** : lecture des fichiers texte (`LoadTxt`, cache `LPDataCache`) → actions, scènes, stats, quêtes, dialogues, mondes |
|
||
| 9 | `ULPModDailiesSubsystem::Tick` | dès que `GI.Room` existe : ajoute les clips de mod à `Room.Dailies` (une fois par pièce) |
|
||
| 10 | `ULPModInfoSubsystem::Tick` (toutes les 2 s) | module coché dont `MODULE_GAME_VERSION` ≠ `ProjectVersion` : fenêtre « Mod version warning », une fois par module et par session |
|
||
|
||
### 2.4 Activation et désactivation
|
||
|
||
- État : tableau `GI.ModEnableds` de structures `ModEnabled` (`UniqueID`, `IsEnabled`). Un module **absent** de la
|
||
liste est inactif (`GI.IsModEnabled`, `ULPModInfoSubsystem::IsModuleEnabled`, `ULPHairstyleSubsystem::IsModuleEnabled`).
|
||
- Gestionnaire de mods (`UI/ModManager`) : en partie (entrée d'origine) ou depuis l'écran titre (lot UI15). À la
|
||
fermeture : `PopulateRaws` (données relues avec les modules choisis) ; depuis l'écran titre, le choix est écrit dans
|
||
`GameSettings.sav` par `ULPModSettingsLibrary::SaveModSelection` (seul `ModEnableds` est remplacé).
|
||
- Effet de la case selon la nature du contenu :
|
||
|
||
| Contenu | Décoché → |
|
||
|---|---|
|
||
| Données texte (actions, scènes, stats…) | non lues par `PopulateRaws` : absentes du jeu |
|
||
| Coiffures (`.lphair`) | retirées des listes (`LPHairstyleSubsystem`) ; paquet toujours monté, inutilisé |
|
||
| Animations (`/<id>/Anims`) | **toujours ajoutées** à `Room.Dailies` : `LPModDailies` ne consulte pas la case (§ 6, E1) |
|
||
| Avertissement de version | non affiché |
|
||
| Plugin Unreal | toujours monté : le moteur ignore `ModEnableds` |
|
||
|
||
Ordre de chargement (relevés `LPDumpFunction` de `GI` et `UI/ModuleTick`) :
|
||
|
||
- `GI.ModEnableds` est ordonné. Les boutons monter / descendre de chaque ligne du gestionnaire (`UI/ModuleTick`)
|
||
appellent l'événement `GI.SortLoadOrder(ForUp, ModuleIndex)`, qui échange deux éléments de `ModEnableds`
|
||
(`Array_Swap`) puis reconstruit la liste (`AddTempModuleTicks`). `GI.ToggleMod(Mod, IsEnabled)` parcourt
|
||
`ModEnableds` et remplace l'élément dont `UniqueID` correspond.
|
||
- L'affichage est inversé par rapport au tableau : indice dans `ModEnableds` = longueur − 1 − position affichée.
|
||
Monter vers la position affichée 0 est refusé (garde `ModuleIndex == 1 && ForUp == 1`), de même qu'un indice hors bornes.
|
||
- Effet (`PopulateRaws`, événement de `GI`) : boucle sur `ModEnableds` du début à la fin, modules cochés seulement.
|
||
Pour chaque type (`lpstat`, `lpaction`, `lpscene`, `lptalk`, `lpdesc`, `lpquest`, `lpai`), les fichiers de
|
||
`Modules/<id>` sont lus (`GetAllFilesFromFolder`) et un fichier est ignoré si **son nom de fichier** (partie après
|
||
le dernier `/`) a déjà été lu (`FilenamesUsed`). Le premier lu gagne : le module le plus bas dans la liste
|
||
affichée l'emporte, ce que dit le guide d'origine (`Docs/Modding/FullGuides/Getting_Started.txt` l. 27 : « the one
|
||
lowest in the load order wins »).
|
||
- Nuance : la comparaison porte sur le nom de fichier seul, pas sur le chemin (le guide dit « same file in the same place »).
|
||
- Réserve : le relevé n'affiche pas le nom des macros de boucle, et `GI` contient aussi une `ReverseForEachLoop`.
|
||
Le sens de la boucle est déduit de la cohérence avec l'affichage inversé et avec le guide ; à confirmer par un
|
||
relevé qui nomme les macros (§ 6, E15).
|
||
- Les animations et les assets ne suivent pas cet ordre (seuls les fichiers texte lus par `PopulateRaws` le suivent).
|
||
|
||
Verrouillage de `vin_Base` : il est en dur dans `UI/ModuleTick`. La case n'appelle `GI.ToggleMod` que si
|
||
`UniqueID != 'vin_Base'` ; pour `vin_Base`, les boutons monter / descendre sont masqués (`SetVisibility(Hidden)`).
|
||
Aucune clé `.lpmod` ne permet de verrouiller un autre module.
|
||
|
||
### 2.5 Consommation des assets
|
||
|
||
| Type | Déclaration côté texte | Résolution | Code |
|
||
|---|---|---|---|
|
||
| Coiffure | `.lphair` : `HAIRSTYLE: /<id>/Hairs/X.X` | registre d'assets, repli `FPackageName::DoesPackageExist` | `LPHairstyleSubsystem` |
|
||
| Animation | `ANIMATION: <clé>` (`.lpaction`), `Actor.animate(<clé>)` (`.lpscene`) | `Person::PlayDaily` : clés en dur, sinon (correctif `LPPatchDailies`) `IsModDaily(<clé>)` → `PlayDailyAnim` parcourt `Room.Dailies`, suffixe `_F`/`_M` selon le sexe | `LPModDailies`, `Person.uasset` patché |
|
||
|
||
Convention des clés d'animation : `<préfixe du pack>_<clip>` en minuscules (`qua_dance_loop`, `fap_as_talk`), assets
|
||
`<clé>_F` et `<clé>_M`. Une clé inconnue ne joue rien (journal `PlayDaily(<clé>) -> cle inconnue` hors Shipping).
|
||
|
||
### 2.6 Schémas du cycle de vie
|
||
|
||
Schémas tirés des relevés `LPDumpFunction` de `GI` (graphe principal, 6 199 nœuds) et de `UI/ModuleTick`, du code C++
|
||
du jeu et du moteur (`GameInstance.cpp`, `PluginManager.cpp`). Les noms entre parenthèses sont les paramètres des
|
||
fonctions ou des événements. `GI.X` désigne un événement ou une fonction du Blueprint `GI` ; `ULP…` une classe C++ du jeu.
|
||
|
||
#### 2.6.1 États d'un module
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> Installe : extraction de l'archive
|
||
Installe : Installé (fichiers sur disque)
|
||
Installe --> Monte : démarrage, FPluginManager (Mods/id)
|
||
Monte : Plugin monté (assets /id/ visibles)
|
||
Monte --> Liste : GI.LoadSettings, ULPModInfoSubsystem
|
||
Liste : Listé (.lpmod lu)
|
||
Liste --> Inscrit : ReadModLauncher ou GameSettings.sav, MODULE_DEFAULT_ENABLED
|
||
Inscrit : Inscrit dans GI.ModEnableds
|
||
Inscrit --> Actif : IsEnabled = true
|
||
Inscrit --> Inactif : IsEnabled = false
|
||
Actif --> Inactif : GI.ToggleMod(Mod, false)
|
||
Inactif --> Actif : GI.ToggleMod(Mod, true)
|
||
Actif : Actif (textes lus par GI.PopulateRaws)
|
||
Inactif : Inactif (textes ignorés, plugin toujours monté)
|
||
Liste --> Retire : dossier Modules/id supprimé
|
||
Retire : Retiré de ModEnableds (GI.removeDeletedMods)
|
||
Retire --> [*]
|
||
Actif --> Decharge : fermeture du jeu
|
||
Inactif --> Decharge : fermeture du jeu
|
||
Decharge : Déchargé (sous-systèmes Deinitialize, conteneurs démontés)
|
||
Decharge --> [*]
|
||
```
|
||
|
||
`vin_Base` ne peut pas passer à l'état Inactif : la case de `UI/ModuleTick` n'appelle `GI.ToggleMod` que si
|
||
`UniqueID != 'vin_Base'`.
|
||
|
||
#### 2.6.2 Démarrage : séquence
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
participant M as Moteur (FPluginManager)
|
||
participant R as Registre d'assets
|
||
participant GI as GI (Blueprint)
|
||
participant S as Sous-systèmes C++
|
||
participant D as Disque Modules/
|
||
M->>M: ReadAllPlugins : Mods/id/id.uplugin devient un plugin de type Mod
|
||
M->>M: ConfigureEnabledPlugins (dépendances Plugins, obligatoire absente = arrêt)
|
||
M->>R: CanContainContent : montage de /id/, MountPak(pak, 0)
|
||
R->>R: AssetRegistry.bin du mod ajouté
|
||
M->>GI: UGameInstance::Init puis ReceiveInit (minuterie 2 s)
|
||
M->>S: SubsystemCollection.Initialize
|
||
S->>D: ULPModInfoSubsystem lit Modules/*/id.lpmod
|
||
S->>R: ULPModDailiesSubsystem relève les UAnimSequence sous /id/Anims
|
||
GI->>GI: DelayedStart puis LoadSettings(LoadLastSave)
|
||
GI->>D: ReadModLauncher, ParseConfig(Content), ModEnableds si UseConfig
|
||
GI->>D: GameSettings.sav donne ModEnableds, puis removeDeletedMods
|
||
S->>GI: ULPHairstyleSubsystem ajoute les modules MODULE_DEFAULT_ENABLED
|
||
GI->>D: PopulateRaws lit les textes des modules cochés (LoadTxt, LPDataCache)
|
||
S->>GI: ULPModDailiesSubsystem::Tick ajoute les clips à Room.Dailies
|
||
S->>GI: ULPModInfoSubsystem::Tick (2 s) : avertissement MODULE_GAME_VERSION
|
||
```
|
||
|
||
#### 2.6.3 Arbre logique de `GI.LoadSettings(LoadLastSave)`
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["GI.LoadSettings(LoadLastSave)"] --> B["ThemeSong.SetActive(false), r.Streaming.PoolSize 1500, EnableDynamicResolution"]
|
||
B --> C{"DoesSaveGameExist('GameSettings', 0)"}
|
||
C -- oui --> D["LoadGameFromSlot('GameSettings', 0), cast GameSettings"]
|
||
D --> E["ModEnableds = GameSettings.ModEnableds"]
|
||
E --> F["GI.ReadModLauncher()"]
|
||
F --> G["GI.removeDeletedMods()"]
|
||
G --> H["environ 60 réglages copiés (DifficultyPlayerInput, SceneFrequency, MarkerSize...)"]
|
||
H --> I["loadAverageAttractiveness()"]
|
||
I --> J["DefaultDistributionIfNeeded()"]
|
||
J --> K["GI.PopulateRaws()"]
|
||
C -- non --> F2["GI.ReadModLauncher()"]
|
||
F2 --> K
|
||
K --> L{"LoadLastSave et réglages existants"}
|
||
L -- oui --> M["LoadThis = LastSaveGame"]
|
||
F -.-> F1["ParseConfig(Content = ModLauncher.config) : UseConfig, ModEnableds"]
|
||
F1 -.-> F1b{"UseConfig"}
|
||
F1b -. oui .-> F1c["ModEnableds = liste du fichier"]
|
||
```
|
||
|
||
`Modules/ModLauncher.config` est absent en pratique : `UseConfig` est faux et `ModEnableds` vient de
|
||
`GameSettings.sav` (ou de la valeur par défaut du Blueprint au premier lancement).
|
||
|
||
#### 2.6.4 Arbre logique de `GI.PopulateRaws()`
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
P["GI.PopulateRaws()"] --> P1["FillNamePool, readUniforms, PopulateAllMorphTags, PopulateBeautyStandardsDefault, FillPregnancyMorphs"]
|
||
P1 --> P2["FilenamesUsed.Clear()"]
|
||
P2 --> L{"Pour chaque ModEnabled (UniqueID, IsEnabled) de ModEnableds, dans l'ordre"}
|
||
L -- "IsEnabled faux" --> L
|
||
L -- "IsEnabled vrai" --> T["Pour chaque type : lpstat, lpaction, lpscene, lptalk, lpdesc, lpquest, lpai"]
|
||
T --> F["GetAllFilesFromFolder('Modules/' + UniqueID, Ext)"]
|
||
F --> N{"nom de fichier (après le dernier '/') déjà dans FilenamesUsed ?"}
|
||
N -- oui --> X["ignoré : le module lu avant l'emporte"]
|
||
N -- non --> Y["LoadTxt vers Raws_Stat, Raws_Action, Raws_Scene, Raws_Talks, Raws_Descs, Raws_Quests, AI_High, AI_Med ; FilenamesUsed.Add(nom)"]
|
||
L -- fin --> Q["PopulateStats, PopulateActions, PopulateScenes, PopulateQuests, PopulateTalksAndDescs, PopulateWorldPresets, RemoveNPCOnly, CleanRaws"]
|
||
Q --> R["IsModEnabled(...) : options liées à un module (Datings...)"]
|
||
```
|
||
|
||
Le premier fichier lu l'emporte. Comme l'affichage du gestionnaire est inversé (§ 2.4), c'est le module le plus bas
|
||
de la liste affichée. Les `.lpscene` passent en plus deux conditions sur leur contenu avant d'être ajoutées.
|
||
|
||
#### 2.6.5 Gestionnaire de mods : ouverture, actions, fermeture
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
O["Menu en partie : HideUI() puis GI.CreateModManagerWidget(GameStart)"] --> O2["CreateWidget ModManager, GameStart mémorisé, AddToViewport(0)"]
|
||
O1["Écran titre (UI15) : GI.CreateModManagerWidget(GameStart = false), ModManager.TitleMenu = menu"] --> O2
|
||
O2 --> O3["TempModuleTicks.Clear(), TempModules.Clear()"]
|
||
O3 --> O4["LPMods = GetAllFilesFromFolder('Modules', 'lpmod')"]
|
||
O4 --> O5{"Pour chaque .lpmod : UniqueID déjà dans TempModules ?"}
|
||
O5 -- non --> O6["TempModules.Add(UniqueID) ; CreateWidget ModuleTick (FullString = fichier entier, UniqueID)"]
|
||
O6 --> O7["CheckIfModAlreadyFound(UniqueID, ModuleTick) : AlreadyExisted"]
|
||
O7 --> O8["NameText, AuthorText depuis MODULE_NAME, MODULE_AUTHOR ; TempModuleTicks.Add"]
|
||
O5 -- fin --> O9["removeDeletedMods(), AddTempModuleTicks(), SetInputMode_UIOnly(ModManager)"]
|
||
O9 --> U{"Action du joueur dans UI/ModuleTick"}
|
||
U -- "case cochée ou décochée" --> T1{"UniqueID différent de 'vin_Base'"}
|
||
T1 -- oui --> T2["GI.ToggleMod(Mod = UniqueID, IsEnabled) : remplace l'élément de ModEnableds"]
|
||
T1 -- non --> T3["sans effet"]
|
||
U -- "monter ou descendre" --> S1["GI.SortLoadOrder(ForUp = 1 ou -1, ModuleIndex = position affichée)"]
|
||
S1 --> S2{"ModuleIndex = 1 et ForUp = 1, ou hors bornes ?"}
|
||
S2 -- non --> S3["Array_Swap(ModEnableds, L-1-ModuleIndex, L-1-ModuleIndex+ForUp), ClearChildren, AddTempModuleTicks"]
|
||
S2 -- oui --> S4["refusé"]
|
||
U -- "fermer, en partie" --> C1["GI.CloseModManager(GameStart) : ShowUI, ClearModManagerWidget, PopulateRaws, ModManagerOn = false"]
|
||
C1 --> C2{"GameStart"}
|
||
C2 -- oui --> C3["CharGenInput(GameStart = true)"]
|
||
C2 -- non --> C4["DelayThenFollowUp()"]
|
||
U -- "fermer, écran titre (UI15)" --> C5["menu titre réaffiché, ClearModManagerWidget, PopulateRaws, ULPModSettingsLibrary::SaveModSelection"]
|
||
```
|
||
|
||
#### 2.6.6 Appel d'une animation de mod
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant Sc as Scène ou action
|
||
participant GI as GI.CallFunction
|
||
participant P as Person (Blueprint patché)
|
||
participant L as ULPModDailiesLibrary
|
||
participant Ro as Room.Dailies
|
||
Sc->>GI: Actor.animate(clé) ou ANIMATION: clé
|
||
GI->>P: PlayDaily(clé), cas 'animate'
|
||
P->>P: SwitchString des clés du jeu (dance...)
|
||
P->>L: cas Default : IsModDaily(clé)
|
||
alt clé de mod
|
||
L-->>P: vrai (journal « cle de mod » hors Shipping)
|
||
P->>Ro: PlayDailyAnim parcourt Room.Dailies, suffixe _F ou _M selon IsMale
|
||
Ro-->>P: UAnimSequence /id/Anims/.../clé_F ou clé_M
|
||
P->>P: currentDaily joué par l'AnimBP Daily ou Daily_F
|
||
else clé inconnue
|
||
L-->>P: faux (journal « cle inconnue ») : rien n'est joué
|
||
end
|
||
```
|
||
|
||
La case du gestionnaire n'intervient pas dans cet arbre (§ 6, E1).
|
||
|
||
#### 2.6.7 Désactivation, désinstallation et déchargement
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A["Module décoché : GI.ToggleMod(Mod, false)"] --> B["fermeture du gestionnaire : GI.PopulateRaws()"]
|
||
B --> B1["textes du module non relus : actions, scènes, stats absentes"]
|
||
A --> C["ULPHairstyleSubsystem::IsModuleEnabled : coiffures filtrées à la demande"]
|
||
A --> D["ULPModInfoSubsystem : plus d'avertissement de version"]
|
||
A --> E["ULPModDailiesSubsystem : clips toujours dans Room.Dailies (E1)"]
|
||
A --> F["plugin /id/ toujours monté jusqu'à la fermeture"]
|
||
G["Dossier Modules/id supprimé"] --> H["démarrage suivant : GI.removeDeletedMods() retire l'entrée de ModEnableds"]
|
||
I["Dossier Mods/id supprimé"] --> J["démarrage suivant : plugin absent, clips et coiffures du mod introuvables"]
|
||
K["Fermeture du jeu"] --> K1["UGameInstance::Shutdown, ReceiveShutdown"]
|
||
K1 --> K2["SubsystemCollection.Deinitialize : LPModDailies retire son ticker et vide les clés ; LPModInfo retire son ticker et ferme l'avertissement"]
|
||
K2 --> K3["moteur : modules et conteneurs démontés à la sortie"]
|
||
```
|
||
|
||
Il n'existe pas de déchargement à chaud : un plugin de mod reste monté du démarrage à la fermeture, quel que soit
|
||
l'état de sa case.
|
||
|
||
## 3. Propriétés réellement lues
|
||
|
||
### 3.1 `<id>.uplugin` (moteur)
|
||
|
||
| Propriété | Lue | Effet |
|
||
|---|---|---|
|
||
| nom du fichier / dossier | oui | nom du plugin, point de montage `/<id>/` |
|
||
| `CanContainContent` | oui | **indispensable** : sans elle, le conteneur n'est pas monté |
|
||
| `EnabledByDefault` | oui (absente) | absente : plugin `Mod` activé |
|
||
| `ExplicitlyLoaded` | oui (absente) | absente : monté au démarrage |
|
||
| `Modules` | oui (absente) | aucun code : un mod Shipping ne peut pas en apporter (pas de binaire compatible) |
|
||
| `Plugins` : `[{ "Name": "...", "Enabled": true, "Optional": false }]` | oui, appliqué | dépendance obligatoire absente : la configuration des plugins échoue et le jeu ne démarre pas (`LaunchEngineLoop.cpp` renvoie `false` après `LoadModulesForEnabledPlugins`) ; `"Optional": true` : ignorée si absente |
|
||
| `Hidden` | oui | utilisé seulement par le navigateur de plugins de l'éditeur ; sans effet en jeu |
|
||
| `Sealed` | oui | interdit qu'un autre plugin en dépende |
|
||
| `DisallowedPlugins` | oui | interdit une dépendance donnée |
|
||
| `SupportedTargetPlatforms`, `EngineVersion` | oui si présentes | plateformes ; `EngineVersion` ne fait qu'avertir |
|
||
| `FriendlyName`, `Description`, `Category`, `CreatedBy`, `CreatedByURL`, `Version`, `VersionName`, `IsBetaVersion`, `Installed` | non en jeu | descriptifs (navigateur de plugins de l'éditeur) |
|
||
|
||
### 3.2 `<id>.lpmod` (jeu)
|
||
|
||
| Clé | Lue par | Effet |
|
||
|---|---|---|
|
||
| ligne 1 `MODULE_UNIQUEID` | `GI` (**par position**), sous-systèmes C++ (par clé) | identifiant du module |
|
||
| ligne 2 `MODULE_NAME` | `GI` (par position), `LPModInfoSubsystem` | nom dans le gestionnaire, dans l'avertissement |
|
||
| ligne 3 `MODULE_AUTHOR` | `GI` (par position) | auteur dans le gestionnaire |
|
||
| `MODULE_DEFAULT_ENABLED` | `LPHairstyleSubsystem` | coché au premier démarrage où il est présent |
|
||
| `MODULE_GAME_VERSION` | `LPModInfoSubsystem` | avertissement de version |
|
||
| `MODULE_LINK`, `MODULE_DESCRIPTION`, `MODULE_REQUIREMENTS`, `MODULE_VERSION`, `MODULE_CREATED`, `MODULE_LICENSE`, `MODULE_AUTHOR_LINK`, `MODULE_PRICE`, `MODULE_PACKAGED` | aucun code | affichées telles quelles : le gestionnaire montre le fichier entier ; `GetModuleMetadata` les rend accessibles aux Blueprints |
|
||
|
||
`MODULE_REQUIREMENTS` n'est pas vérifié : aucun contrôle, aucune chaîne `MODULE_REQUIREMENTS` dans `GI` ; seuls
|
||
`MODULE_UNIQUEID:`, `MODULE_NAME:` et `MODULE_AUTHOR:` sont découpés (`LPStringCompat::Split`). Un mod dont un
|
||
prérequis manque se charge quand même. Les trois premières lignes sont lues par position : `BuildMod.ps1` en contrôle
|
||
l'ordre. Réserve : la recherche dans les Blueprints porte sur les noms de clés écrits en clair ; une clé assemblée à
|
||
l'exécution n'y apparaîtrait pas.
|
||
|
||
### 3.3 Notions de visibilité, verrouillage, priorité et dépendances
|
||
|
||
| Notion | Jeu (`.lpmod`, Blueprints) | Moteur (`.uplugin`) | État |
|
||
|---|---|---|---|
|
||
| Visible / masqué | aucune notion : tout `.lpmod` présent est listé ; le gestionnaire affiche le fichier entier dans le panneau de détail (`FullString`) | `Hidden` : lu, utilisé seulement par le navigateur de plugins de l'éditeur ; sans effet en jeu | pas de masquage possible |
|
||
| Verrouillé / non désactivable | `vin_Base` en dur dans `UI/ModuleTick` (case sans effet, boutons d'ordre masqués) ; aucune clé `.lpmod` | aucun verrouillage d'un plugin `Mod` : monté dès qu'il est présent | seul `vin_Base` est verrouillé |
|
||
| Module système chargé avant les autres (dossier distinct) | aucun : tous les modules sont dans `Content/Modules/<id>` | les plugins du jeu (`Plugins/`, cuits avec lui) sont configurés avant ceux de `Mods/` | existe côté moteur seulement, hors de portée d'un mod |
|
||
| Ordre de chargement / priorité | `GI.ModEnableds` ordonné, `GI.SortLoadOrder` ; le plus bas dans la liste affichée l'emporte, par nom de fichier (§ 2.4) ; fichiers texte seulement | seul ordre réglable : `LoadingPhase` des modules de code (un mod n'en a pas) ; conteneurs montés avec la même priorité (`FCoreDelegates::MountPak.Execute(PakPath, 0)`, `PluginManager.cpp`) | ordre réel pour les textes, aucun pour les assets |
|
||
| Dépendances | `MODULE_REQUIREMENTS` non vérifié | `Plugins` appliqué ; dépendance obligatoire absente : le jeu ne démarre pas ; `Optional`, `Sealed`, `DisallowedPlugins` (§ 3.1) | côté jeu : aucune ; côté moteur : bloquante |
|
||
|
||
Conséquence pour les mods : ne jamais déclarer de dépendance obligatoire entre mods dans le `.uplugin` (un mod
|
||
manquant empêcherait le jeu de démarrer). Un prérequis entre mods se gère côté jeu (§ 6, E2).
|
||
|
||
### 3.4 Doublons entre descripteurs
|
||
|
||
| Information | `.uplugin` | `.lpmod` | `pack.json` / config | Autres | Source de vérité | Écrit par |
|
||
|---|---|---|---|---|---|---|
|
||
| Identifiant | nom du fichier | `MODULE_UNIQUEID` | `chaine.mod` | dossiers | aucune désignée | `NewMod.ps1`, `DeliverAnims.ps1` |
|
||
| Titre | `FriendlyName` | `MODULE_NAME` | `source` | 1re ligne de `CREDITS.txt` et de `README.txt` | aucune désignée | `NewMod.ps1`, `DeliverAnims.ps1` ; README par `BuildMod.ps1` |
|
||
| Auteur | `CreatedBy` | `MODULE_AUTHOR` | `auteur`, enveloppé en « LifePlay Revival (animations by <auteur>) » | | aucune désignée | `NewMod.ps1`, `DeliverAnims.ps1` |
|
||
| Description | `Description` | `MODULE_DESCRIPTION` | | | aucune désignée | `NewMod.ps1`, `DeliverAnims.ps1` |
|
||
| Version | `VersionName` | `MODULE_VERSION` | | README | `.lpmod` (recopié) | `BuildMod.ps1` : `MODULE_VERSION` → `VersionName` dans la copie livrée |
|
||
| Lien de l'auteur | `CreatedByURL` | `MODULE_AUTHOR_LINK` | `url_auteur` ou `url` | | `.lpmod` (recopié) | `BuildMod.ps1` : `MODULE_AUTHOR_LINK` → `CreatedByURL` |
|
||
| Licence | | `MODULE_LICENSE` | `licence`, `credit` | `CREDITS.txt`, description | aucune désignée | `DeliverAnims.ps1` |
|
||
| Version du jeu visée | | `MODULE_GAME_VERSION` | | README (×3), nom du zip | `BuildMod.ps1` | `BuildMod.ps1` (ajoute aussi `MODULE_PACKAGED`) |
|
||
|
||
`Tools/NewMod.ps1` et `Tools/anim/DeliverAnims.ps1` écrivent le `.uplugin` et le `.lpmod` source. `Tools/BuildMod.ps1`
|
||
retouche la copie livrée (clés ci-dessus, plus `MODULE_PACKAGED` et `MODULE_GAME_VERSION`) et écrit le README depuis
|
||
le `.lpmod`.
|
||
|
||
Cause des divergences : `DeliverAnims.ps1` crée les descripteurs une seule fois (`New-IfMissing`). Une modification
|
||
ultérieure de `pack.json` n'est jamais reportée. Divergences constatées :
|
||
|
||
- `lpr_anim_quaternius` : `MODULE_NAME` « Quaternius Animations » contre `pack.json:source` « Quaternius Universal
|
||
Animation Library (Standard) » ; `MODULE_LICENSE` « CC0 » contre `pack.json:licence` « CC0 1.0 ».
|
||
- `lpr_hair_pocolov` : auteur « LifePlay Revival (hairstyles by Pocolov) » (descripteurs) contre « Pocolov (Pocolov
|
||
Studio, artist Thang) » (`Tools/mods/lpr_hair_pocolov.json`, `CREDITS.txt`).
|
||
- `NewMod.ps1` n'écrit pas `MODULE_DEFAULT_ENABLED`.
|
||
|
||
Champs constants ou sans usage : `.uplugin` `FileVersion`, `Version` (entier), `Category`, `IsBetaVersion`,
|
||
`Installed` ; `.lpmod` `MODULE_REQUIREMENTS: vin_Base`, `MODULE_PRICE: Free`, `MODULE_LINK` (URL du dépôt),
|
||
identiques dans les trois mods.
|
||
|
||
## 4. Classes et fichiers
|
||
|
||
| Élément | Rôle |
|
||
|---|---|
|
||
| `FPluginManager` (moteur) | découverte de `Mods/`, activation, montage du contenu |
|
||
| Registre d'assets (moteur) | `AssetRegistry.bin` des plugins, recherche par chemin / classe |
|
||
| `Plugins/ModdingExtra` : `LoadTxt`, `SaveTxt`, `GetAllFilesFromFolder`, `LPDataCache` | accès disque aux `Modules` (fichiers nus, `DirectoriesToAlwaysStageAsNonUFS`) ; cache analysé des formats `CLÉ: valeur` dont `.lpmod` |
|
||
| `Content/BPs/GI.uasset` | `LoadSettings`, `ReadModLauncher`, `removeDeletedMods`, `PopulateRaws`, `IsModEnabled`, `CreateModManagerWidget`, `CloseModManager`, `ModEnableds` |
|
||
| `UI/ModManager`, `UI/MainMenu` | gestionnaire de mods ; entrée de l'écran titre (`LPTitleModManager`) |
|
||
| `ULPModInfoSubsystem` | lecture des `.lpmod`, `GetModuleMetadata`, `IsModuleEnabled`, avertissement de version |
|
||
| `ULPModSettingsLibrary::SaveModSelection` | enregistre `ModEnableds` seul dans `GameSettings.sav` |
|
||
| `ULPHairstyleSubsystem` | `MODULE_DEFAULT_ENABLED`, coiffures `.lphair` des modules cochés |
|
||
| `ULPModDailiesSubsystem`, `ULPModDailiesLibrary::IsModDaily` | clips de mod → `Room.Dailies` ; reconnaissance des clés de mod dans `Person::PlayDaily` |
|
||
| `ULPSessionLogSubsystem` | inventaire des modules au démarrage (journal) |
|
||
| Outils | `NewMod`, `EditMod`, `ModSource`, `BuildMod`, `SplitModules`, `HairPipeline`, `anim/AnimPipeline`, `anim/DeliverAnims` |
|
||
|
||
## 5. Capacités actuelles
|
||
|
||
Un mod peut, sans recompiler ni recuire le jeu :
|
||
|
||
- ajouter ou modifier des données de jeu en texte (actions, scènes, stats, quêtes, dialogues, IA, personnages, mondes) ;
|
||
- apporter des assets cuits : coiffures (H3), animations (AP5–AP7), tout asset que le jeu trouve par le registre ;
|
||
- être coché ou décoché dans le gestionnaire (sauf les animations, § 6 E1), coché d'office au premier démarrage ;
|
||
- être ordonné avec les autres modules dans le gestionnaire (le plus bas l'emporte, par nom de fichier, pour les
|
||
fichiers texte) ;
|
||
- prévenir le joueur s'il a été construit pour une autre version du jeu ;
|
||
- documenter licence, auteur, liens et crédits (affichés, non interprétés).
|
||
|
||
Il ne peut pas : apporter du code (C++ ou Blueprint exécuté en Shipping), remplacer un asset du jeu de base,
|
||
exiger un autre mod (`MODULE_REQUIREMENTS` n'est pas vérifié ; une dépendance `.uplugin` obligatoire bloquerait le
|
||
démarrage), ni être démonté à chaud (le plugin reste monté, décoché ou non).
|
||
|
||
## 6. Évolutions possibles
|
||
|
||
| Réf. | Évolution | Intérêt | Ampleur |
|
||
|---|---|---|---|
|
||
| E1 | `LPModDailies` : ne garder que les clips des modules cochés (même règle que `IsModuleEnabled`), au remplissage de `Room.Dailies` | un seul interrupteur pour tous les types de contenu ; clé « inconnue » quand le mod est décoché | petit lot C++, essai empaqueté coché / décoché |
|
||
| E2 | Vérifier `MODULE_REQUIREMENTS` au démarrage et à la fermeture du gestionnaire (avertissement comme pour la version) | prérequis manquant signalé au lieu d'un mod à moitié fonctionnel | petit lot C++ |
|
||
| E3 | Contrôle de cohérence des deux moitiés : `Mods/<id>` sans `Modules/<id>` (assets orphelins) ou l'inverse (`.lphair` / clés vers des assets absents) | désinstallation partielle détectée | petit lot C++ |
|
||
| E4 | Avertissement de version élargi aux mods d'assets décochés (un conteneur incompatible est monté même décoché) | prévenir avant un échec de chargement | très petit |
|
||
| E5 | Détection des collisions : deux mods qui déclarent la même clé d'animation, la même coiffure, le même identifiant d'action | comportement déterministe, message clair | moyen |
|
||
| E6 | Lecture des trois premières lignes du `.lpmod` par clé plutôt que par position (Blueprint `GI` ou relais C++) | un `.lpmod` écrit à la main dans le désordre ne casse plus le gestionnaire | moyen (graphe de `GI`) |
|
||
| E7 | Ne pas monter les plugins des mods décochés (liste lue avant le montage, `ExplicitlyLoaded` + montage par `IPluginManager::MountExplicitlyLoadedPlugin`) | mémoire et temps de démarrage ; mod réellement inerte | moyen, à prototyper (ordre de démarrage) |
|
||
| E8 | Déclaration explicite des animations (`.lpanim` : clé, clips F/M, catégorie, usage, posture) plutôt que par convention de nom | catalogue en jeu, filtres par posture, alias, plusieurs variantes par clé | moyen |
|
||
| E9 | Autres types d'assets par le même schéma registre + fichier texte : vêtements, meubles, décors de pièce (`.lpworld` vers `/<id>/…`) | extension naturelle de H3 | un lot par type |
|
||
| E10 | Empreintes dans l'archive (SHA-256 par fichier, vérifiées par un outil d'installation) | intégrité, mods modifiés détectés | petit (outillage) |
|
||
| E11 | Gestionnaire enrichi : description, licence, lien, version, état (cuit pour, prérequis, conflits), catalogue d'animations | information du joueur, aujourd'hui texte brut | moyen (UI) |
|
||
| E12 | Ordre étendu aux mods d'assets et clés d'animation (aujourd'hui seuls les fichiers texte suivent l'ordre) | surcharge contrôlée aussi pour les animations et les coiffures | moyen ; dépend de `PopulateRaws` et de `LPModDailies` |
|
||
| E13 | Notions déclarées dans le `.lpmod` : `MODULE_LOCKED`, `MODULE_HIDDEN`, `MODULE_PRIORITY` ou rang système, en remplacement du verrouillage en dur de `vin_Base` | verrouillage, masquage et priorité par module, sans Blueprint codé en dur | moyen (graphe de `UI/ModuleTick` et de `GI`) |
|
||
| E14 | Descripteur unique : `.lpmod` (ou `pack.json`) source de vérité, `.uplugin` et README générés à chaque construction, `New-IfMissing` remplacé par une régénération | fin des divergences entre descripteurs (§ 3.4) | petit à moyen (outillage) |
|
||
| E15 | Relevé `LPDumpFunction` : afficher le nom des événements personnalisés et des macros | confirmer le sens de la boucle de `PopulateRaws` (§ 2.4) | petit (outillage) |
|
||
|
||
Recommandation : E1 en premier (seule incohérence visible du joueur), puis E2 et E3 dans un même lot (mêmes
|
||
lectures que `LPModInfoSubsystem`), puis E14.
|
||
|
||
Un prototype des notions de la § 3.3 (prérequis, propriétés déclarées, ordre, état par mod) a été construit et validé dans la sandbox : voir la § 7.
|
||
|
||
## 7. Prototype dans la sandbox (lots SB0 à SB4)
|
||
|
||
La sandbox (addon `D:\Game\LifePlay-Sandbox`, plugin `LPSandbox`, doc [21](21_Sandbox_Addon.md)) a servi de banc d'essai
|
||
au concept « modules système, bibliothèques, mods » avant toute modification du jeu. Conception :
|
||
`D:\Game\LifePlay-Sandbox\Docs\SB0-conception.md` (décisions utilisateur du 2026-10-07). Le registre est en C++ et Slate,
|
||
sans le Blueprint `GI`. Preuves sous `Docs/Migration/etat/sandbox/` (dossiers `SB1`, `SB2`, `SB4`).
|
||
|
||
### 7.1 Ce qui est prouvé
|
||
|
||
| Élément | Résultat | Preuve |
|
||
|---|---|---|
|
||
| Registre C++ (`FLPSandboxModRegistry` : découverte, lecture, contrôles, états `Ready`, `Disabled`, `Missing`, `Refused`) | 7 modules sandbox découverts, tous `pret` au démarrage | `SB1/SB1-SPEC.md` (§ 2, 3) ; `SB1/SB1-VAL.md` E1 |
|
||
| `.lpmod` lu par clé, rétrocompatible | 16 `.lpmod` du jeu lus sans erreur ni avertissement (`Modules : 7 sandbox, 16 jeu`) | `SB1/SB1-VAL.md` E1 ; `SB2/SB2-VAL.md` E1 ; `SB4/SB4-VAL.md` F1 |
|
||
| Clés étendues `MODULE_SYSTEM`, `MODULE_HIDDEN`, `MODULE_LOCKED`, `MODULE_KIND`, `MODULE_DEFAULT_ENABLED` | portées par les descripteurs des 7 modules ; section « Système » et case « modules masqués » de la barre latérale | `SB1/SB1-SPEC.md` § 2, 8 ; `SB1/SB1-VAL.md` (contrôle visuel : aspect de la barre latérale, PASS) |
|
||
| Ordre système par index hexadécimal du dossier | `lps_system_core`, `lps_system_animmeasure`, `lps_lib_scene`, `lps_lib_actors`, puis les mods | `SB1/SB1-VAL.md` E1 ; `SB4/SB4-VAL.md` F1 |
|
||
| Ordre et cases des mods persistés | `Saved/sandbox_mods.ini` ; l'ordre du fichier prime sur l'ordre alphabétique ; mod décoché signalé `desactive` | `SB1/SB1-VAL.md` E4, E4b |
|
||
| Refus mod → mod | `lps_test_bad` refusé : « un mod ne peut pas requerir un autre mod » ; les 3 mods restent prêts | `SB1/SB1-VAL.md` E2 |
|
||
| Prérequis absent ou désactivé signalé sans bloquer | sans `lps_lib_scene` : mods en « prerequis manquant », scène neutre, aucun plantage ; l'ouverture mémorisée est refusée puis repli sur le premier mod prêt | `SB1/SB1-VAL.md` E3 ; `SB2/SB2-VAL.md` E3 |
|
||
| Cycle de prérequis refusé | règle spécifiée (tous les modules du cycle `Refused`) ; pas d'essai dédié dans les rapports de validation | `SB1/SB1-SPEC.md` § 3 (règle 3) |
|
||
| Activé ≠ ouvert | 3 mods activés et prêts, un seul ouvert à la fois ; enchaînement couple, interaction, mesure sans résidu | `SB1/SB1-VAL.md` E1 et contrôle visuel (b) ; `SB2/SB2-VAL.md` contrôle visuel (b) |
|
||
| Contexte filtré : un mod ne reçoit que les bibliothèques qu'il déclare | `FLPSandboxContext` rempli selon `MODULE_REQUIREMENTS` (`Scene` et `Actors` nuls si non requis) ; aucun accès de mod à mod | `SB2/SB2-CART.md` § 2.1 ; `SB2/SB2-VAL.md` E1 à E3 |
|
||
| État de chaque mode sorti dans son mod, sans régression | tournées Quaternius et FreeAnimationsPack : tous les `.json`, `.csv`, `.txt` identiques à la référence ; verdicts `avertissement` (8 avertissements identiques) et `ok` ; `LPAnimEval` identique | `SB2/SB2-VAL.md` E4 ; `SB4/SB4-VAL.md` F3 ; référence `SB2/SB2-REF.md` |
|
||
| Défaut O1 (pièce invisible après une Mesure) corrigé | `ShowRoom` symétrique de `HideRoom`, contrôle visuel utilisateur | `SB2/SB2-O1-BUILD.md` |
|
||
| Barre latérale (sections Mods, Système, Jeu ; cases, ▲ ▼, ▶, ⚠) | contrôle visuel utilisateur : pas de défaut | `SB1/SB1-VAL.md`, `SB2/SB2-VAL.md` |
|
||
| Déclencheur éphémère non mémorisé (SB4-T0) | `Open(Id, Context, bRemember)` ; `Saved/sandbox_mode.txt` n'écrit plus `Open` dans `sandbox_mods.ini` (journal `Ouvert : <id> (declencheur, non memorise)`) ; `Open=lps_couple` intact après deux tournées | `SB4/SB4-VAL.md` F2 ; build `SB4/SB4-BUILD.md` |
|
||
|
||
Les 20 images FreeAnimationsPack qui diffèrent d'une tournée à l'autre (rendu non déterministe, écarts de taille
|
||
inférieurs à 1,2 %) ont été jugées sans défaut par l'utilisateur (`SB1-VAL`, `SB2-VAL`).
|
||
|
||
### 7.2 Ce qui n'est pas prouvé, limites
|
||
|
||
| Limite | Détail |
|
||
|---|---|
|
||
| Séparation physique en plugins | abandonnée à la porte G-SB3 (décision utilisateur 2026-10-07) : les mods du jeu sont du contenu (`.lpmod` + pak), `LPAnimMeasure` est utilisé par le jeu en Shipping. Conséquence : sans `lps_lib_scene`, la pièce est quand même construite par le GameMode ; seul le registre refuse les mods (`SB2/SB2-VAL.md` E3, doc 21) |
|
||
| Persistance | propre à la sandbox (`Saved/sandbox_mods.ini`). La cohabitation avec `GI.ModEnableds` et `GameSettings.sav` n'est pas traitée |
|
||
| Mods du jeu | lecture des `.lpmod` seulement (état `DataOnly`) : pas d'activation du contenu du jeu, pas de lecture d'ordre depuis `ModEnableds` |
|
||
| Cycle de prérequis | non essayé dans les rapports de validation |
|
||
| Compilation | éditeur Development seulement ; le registre n'a pas été essayé en Shipping |
|
||
|
||
### 7.3 Transposition au jeu
|
||
|
||
| Élément prouvé | Évolution de la § 6 servie | Travail côté jeu |
|
||
|---|---|---|
|
||
| Contrôle des prérequis (états `Missing`, `Refused`, repli, sans bloquer) | E2 | lire `MODULE_REQUIREMENTS` au démarrage et à la fermeture du gestionnaire ; avertissement comme pour la version ; ne jamais passer par une dépendance `.uplugin` |
|
||
| Lecture par clé, 16 `.lpmod` du jeu sans erreur | E6 | remplacer le découpage par position de `GI` par une lecture par clé (relais C++ vers `LPDataCache`) |
|
||
| Propriétés déclarées (système, masqué, verrouillé, index de dossier) | E13 | remplacer le verrouillage en dur de `vin_Base` (`UI/ModuleTick`) par `MODULE_LOCKED` ; ajouter `MODULE_HIDDEN`, `MODULE_SYSTEM` et l'ordre système par index |
|
||
| Registre et état par mod | (nouveau) | studio de création de personnage comme module système ; Pocolov déclare `MODULE_REQUIREMENTS` vers le studio |
|
||
| Option legacy / modern | (nouveau, demande utilisateur) | d'abord un paramètre ; puis le legacy en mod ; puis retrait |
|
||
| Persistance | (nouveau) | `GI.ModEnableds` reste la source pour les mods du jeu ; le registre C++ s'y branche (à concevoir : lecture, écriture, ordre) |
|
||
| Déclencheur éphémère non mémorisé (SB4-T0) | principe | tout lancement automatisé (tournée, test, pilote) ne doit jamais modifier l'état persistant du joueur |
|