14 KiB
SB1-SPEC — Registre de mods et barre latérale (modes enveloppés)
- Fiche : SB1-T1 — Agent : A-ARCH — Modèle : prévu opus (M4) | utilisé opus
- Dépend de : SB0 (
D:\Game\LifePlay-Sandbox\Docs\SB0-conception.md, commit3b21fd1, taglot-SB0-valide) - Dépôt : addon
D:\Game\LifePlay-Sandbox, branchelot/SB1-registrecréée depuislot-SB0-valide, taglot-SB1-avant - Principe SB1 : aucun code de mode n'est déplacé. Les trois modes actuels deviennent trois mods logiques
(descripteur + enveloppe qui appelle
SelectModeexistant). La liste déroulante des modes est remplacée par la barre latérale. Comportement de chaque mode strictement inchangé.
1. Fichiers
Fichier (sous Plugins/LPSandbox/Source/LPSandbox/) |
Fiche | Contenu |
|---|---|---|
LPSandboxMod.h (nouveau) |
T2 | descripteur, états, contexte, interface ILPSandboxMod |
LPSandboxModRegistry.h/.cpp (nouveaux) |
T2 | registre (§ 3), persistance (§ 4), journal |
LPSandboxModWrappers.cpp (nouveau) |
T2 | 3 enveloppes + enregistrement (§ 5) |
LPSandbox.cpp |
T2 | StartupModule : enregistrement des fabriques, Scan() |
LPPairSandbox.h/.cpp |
T2 | OpenModeLabel(const FString&) public ; choix du mod initial (§ 6) ; emplacement de la barre (§ 7) |
LPSandbox.Build.cs |
T2 | "Projects" en dépendance privée (IPluginManager) |
LPSandboxSidebar.h/.cpp (nouveaux) |
T3 | widget Slate SLPSandboxSidebar (§ 7) |
Plugins/LPSandbox/Modules/**/<id>.lpmod (nouveaux) |
T3 | 7 descripteurs (§ 8) |
2. Types (LPSandboxMod.h)
#pragma once
#include "CoreMinimal.h"
class SWidget;
class ALPPairSandboxCharacter;
class ALPPairSandboxStage;
enum class ELPSandboxModKind : uint8 { System, Library, Mod, Data }; // Data = .lpmod du jeu, lecture seule
enum class ELPSandboxModSource : uint8 { Sandbox, Game };
enum class ELPSandboxModState : uint8
{
Ready, // utilisable (mod activé, prérequis satisfaits)
Disabled, // case décochée
Missing, // prérequis absent ou désactivé (Reason renseignée)
Refused, // règle violée : doublon, mod requis par un mod, cycle (Reason renseignée)
NoFactory, // descripteur de la sandbox sans code enregistré
DataOnly // descripteur du jeu
};
struct FLPSandboxModDescriptor
{
FString Id; // MODULE_UNIQUEID
FString Name; // MODULE_NAME
FString Author; // MODULE_AUTHOR
FString Description, Version, License, Link, AuthorLink;
TArray<FString> Requirements; // MODULE_REQUIREMENTS, séparés par ',' ; espaces retirés ; vides ignorés
ELPSandboxModKind Kind = ELPSandboxModKind::Mod;
ELPSandboxModSource Source = ELPSandboxModSource::Sandbox;
bool bSystem = false, bHidden = false, bLocked = false, bDefaultEnabled = false;
int32 Index = -1; // index hexadécimal du dossier (0001_…), -1 sans index
FString Folder; // chemin complet du dossier
FString File; // chemin complet du .lpmod
TArray<FString> Warnings; // lecture : clé manquante, UNIQUEID ≠ nom du fichier, 3 premières lignes dans le désordre…
TMap<FString, FString> Keys; // toutes les clés lues (majuscules)
};
struct FLPSandboxContext
{
ALPPairSandboxCharacter* Player = nullptr; // SB1 : les enveloppes passent par le personnage joueur
ALPPairSandboxStage* Stage = nullptr;
};
class ILPSandboxMod
{
public:
virtual ~ILPSandboxMod() = default;
virtual FString GetId() const = 0;
virtual void Activate(const FLPSandboxContext& Context) = 0; // le mod prend la scène
virtual void Deactivate(const FLPSandboxContext& Context) = 0; // il la rend (SB1 : rien, SelectMode gère la transition)
virtual TSharedPtr<SWidget> BuildPanel() { return nullptr; } // SB1 : nul (les rangées actuelles du menu restent)
virtual void Tick(float DeltaSeconds) {}
};
3. Registre (FLPSandboxModRegistry, singleton Get())
3.1 API (figée : la barre latérale de T3 n'utilise que celle-ci)
struct FLPSandboxModEntry
{
FLPSandboxModDescriptor Desc;
ELPSandboxModState State = ELPSandboxModState::Disabled;
FString Reason; // texte affichable (infobulle ⚠)
bool bEnabled = false;
bool bOpen = false;
};
class LPSANDBOX_API FLPSandboxModRegistry
{
public:
static FLPSandboxModRegistry& Get();
void RegisterFactory(const FString& Id, TFunction<TSharedRef<ILPSandboxMod>()> Factory);
void Scan(); // découverte + lecture + contrôles + état persisté (§ 3.2-3.4)
const TArray<FLPSandboxModEntry>& GetEntries() const; // ordre d'affichage : système/bibliothèques, mods, jeu
const FLPSandboxModEntry* Find(const FString& Id) const;
bool SetEnabled(const FString& Id, bool bEnabled); // refus si verrouillé ou Data ; recalcul des états ; Save()
bool Move(const FString& Id, int32 Delta); // mods seulement (-1 monte, +1 descend) ; Save()
bool Open(const FString& Id, const FLPSandboxContext& Context); // Ready + activé exigé ; Deactivate du mod ouvert, Activate du nouveau ; Save()
FString GetOpenId() const;
FString IdForModeLabel(const FString& Label) const; // "Pair"→lps_couple, "Interaction"→lps_interaction, "Mesure"→lps_asset_integration
FSimpleMulticastDelegate OnChanged; // diffusé après tout changement
};
3.2 Découverte
- Sandbox :
IPluginManager::Get().FindPlugin(TEXT("LPSandbox"))->GetBaseDir()+/Modules/System/*/et/Modules/Mods/*/; dans chaque dossier, le premier*.lpmod. - Jeu (lecture seule) :
FPaths::ProjectContentDir() / TEXT("Modules");<dossier>/<dossier>.lpmod(même règle queULPModInfoSubsystemdu jeu). Source = Game, Kind = Data, état DataOnly. Absent (projetLPSandbox.uprojectseul) : section Jeu vide, sans erreur. - Index : si le nom du dossier commence par 4 chiffres hexadécimaux suivis de
_,Index= valeur ; sinon -1.
3.3 Lecture d'un .lpmod
- Ligne
CLÉ: valeur: clé = texte avant le premier:, sans espaces, mise en majuscules ; valeur coupée aux extrémités ; lignes vides et commençant par//ignorées ; première occurrence retenue. - Clés lues :
MODULE_UNIQUEID,MODULE_NAME,MODULE_AUTHOR,MODULE_DESCRIPTION,MODULE_VERSION,MODULE_LICENSE,MODULE_LINK,MODULE_AUTHOR_LINK,MODULE_REQUIREMENTS,MODULE_SYSTEM,MODULE_HIDDEN,MODULE_LOCKED,MODULE_KIND,MODULE_DEFAULT_ENABLED(booléens :trueinsensible à la casse, sinon faux). - Défauts :
Kind=systemsiMODULE_SYSTEM: true,librarysi l'identifiant commence parlps_lib_, sinonmod;MODULE_KINDexplicite prime.bHiddenetbLockedvalentbSystem || Kind==Librarysi la clé est absente.bDefaultEnabledvaut vrai pour système/bibliothèque, faux pour un mod si la clé est absente. - Avertissements (sans refus) :
MODULE_UNIQUEIDabsent (identifiant = nom du fichier) ; identifiant ≠ nom du fichier ; les 3 premières lignes utiles ne sont pas, dans l'ordre,MODULE_UNIQUEID,MODULE_NAME,MODULE_AUTHOR(rétrocompatibilité avec la lecture par position deGI).
3.4 Contrôles et états (dans cet ordre, recalculés après chaque changement)
- Identifiant en double (sandbox) : le second est
Refused(« identifiant déjà utilisé par »). - Kind Mod qui requiert un module de Kind Mod :
Refused(« un mod ne peut pas requérir un autre mod () : partager le composant dans une bibliothèque »). - Cycle dans les prérequis : tous les modules du cycle
Refused(« cycle de prérequis : a → b → a »). - Prérequis introuvable :
Missing(« prérequis absent : ») ; prérequis présent mais nonReadyou désactivé :Missing(« prérequis désactivé : »). Un prérequis peut être un module du jeu (Data) : présent = satisfait. - Pas de fabrique (descripteur sandbox sans code) :
NoFactorypour un mod ; une bibliothèque ou un système sans fabrique estReady(SB1 : services non encore extraits). - Sinon :
Readysi activé,Disabledsinon. Système et bibliothèques sont toujours activés. - Si le mod ouvert n'est plus
Ready:Deactivate, plus aucun mod ouvert (journal).
3.5 Ordre de GetEntries()
Système et bibliothèques par Index croissant (sans index : après, par identifiant) ; puis mods dans l'ordre persisté
(nouveaux en fin, par identifiant) ; puis modules du jeu par identifiant.
3.6 Journal LogLPSandboxMods (catégorie déclarée dans LPSandboxModRegistry.h, LPSANDBOX_API)
DisplayauScan:Modules : <n> sandbox, <m> jeu ; ordre : <id>, <id>, …Displaypar module :<id> [<Kind>] <État> <raison>;Warningpour chaque avertissement de lecture et chaque refus.Displayà chaqueSetEnabled,Move,Open:Ouvert : <id>(preuve de chargement du nouveau code).
4. Persistance : Saved/sandbox_mods.ini (projet hôte)
[Mods]
Order=lps_couple,lps_interaction,lps_asset_integration
Open=lps_couple
[Enabled]
lps_couple=true
lps_interaction=true
lps_asset_integration=true
Lecture au Scan (FConfigFile::Read, comme FLPSandboxConfig::LoadFile), écriture après chaque changement
(FConfigFile::Write). Fichier absent : ordre par identifiant, cases = bDefaultEnabled, aucun mod ouvert mémorisé.
5. Enveloppes (LPSandboxModWrappers.cpp)
Trois classes FLPSandboxModeWrapper : ILPSandboxMod paramétrées par (Id, ModeLabel) :
| Id | ModeLabel |
|---|---|
lps_couple |
Pair |
lps_interaction |
Interaction |
lps_asset_integration |
Mesure |
Activate(Context):Context.Player->OpenModeLabel(ModeLabel).Deactivate: rien (la transition, y comprisEndMeasure,SetAutoRun(false),SetFreeView(false)et la restauration du bras caméra, est déjà faite parSelectModequand le mode suivant s'ouvre).- Fermeture sans mod suivant (mod ouvert désactivé) :
OpenModeLabel("Pair")puis rangée d'animations masquée (Menu->AnimRowréduit) : scène neutre. - Enregistrement dans
FLPSandboxModule::StartupModule:RegisterFactory×3 puisScan().
6. Personnage (ALPPairSandboxCharacter)
- Ajouter
void OpenModeLabel(const FString& Label);public : chercheLabeldansMenu->Modeset appelleSelectMode(privé, inchangé) ; journalise si introuvable. - Le menu garde la liste interne
Menu->Modes(Pair, Interaction, Mesure) mais n'affiche plusModeCombo. - Mod initial, à la fin de
ShowAnimMenu(remplace le bloc actuelsandbox_mode.txt/[Sandbox.Mode], même priorité et même effacement desandbox_mode.txt) :Saved/sandbox_mode.txtprésent :Registry.Open(IdForModeLabel(contenu))(piloteAnimPipeline:Mesure) ;- sinon
Openpersisté s'il estReady; - sinon
[Sandbox.Mode]deLPSandbox.ini(défautPair) viaIdForModeLabel; - si le mod visé n'est pas
Ready: premier modReadydans l'ordre ; aucun : scène neutre (§ 5).
ModeLabelreste la vérité interne du mode ; le registre ne le modifie que parOpenModeLabel.
7. Barre latérale (SLPSandboxSidebar, T3)
- Remplace, dans
ShowAnimMenu, le slot deModeCombo(premier slot duSVerticalBox) ; le reste du panneau (Mesure, MC, PNJ, animation, vue, filtres, aide) reste en dessous, inchangé. SLATE_BEGIN_ARGS:_OnOpen(TFunction<void(const FString&)>, le personnage appelleRegistry.Open(Id, Context)).- S'abonne à
Registry.OnChangedet reconstruit ses lignes (désabonnement au destructeur). - Section Mods (Kind Mod, non masqués) — une ligne :
SCheckBox(désactivée si verrouillé) →SetEnabled; nom (Name) ; boutons ▲ ▼ →Move(Id, -1/+1); bouton ▶ (activé siReady, libellé « ouvert » sibOpen) →OnOpen(Id); siMissing/Refused/NoFactory: « ⚠ » avec infobulleReason. Ligne du mod ouvert surlignée. - Case Afficher les modules masqués (non persistée).
- Section Système (visible si la case est cochée) :
Indexen hexadécimal,Id,Kind,Version, état ; lecture seule. - Section Jeu (lecture) (repliée par défaut, en-tête « Jeu : modules ») :
Id,Name,Author,Requirements, avertissements de lecture en orange. - Bouton ◀ / ▶ en tête du panneau : replie / déplie tout le panneau du menu (
Menu->Widgetréduit à la barre de titre). - Style : polices et couleurs du menu actuel (
FCoreStyle::GetDefaultFontStyle("Regular", 14)/ 11 pour le détail), largeur 300 comme les listes existantes.
8. Descripteurs (T3)
Sous Plugins/LPSandbox/Modules/ ; trois premières lignes dans l'ordre MODULE_UNIQUEID, MODULE_NAME, MODULE_AUTHOR.
| Dossier | Id | Nom | Kind / clés | Prérequis |
|---|---|---|---|---|
System/0001_lps_system_core |
lps_system_core |
Socle sandbox | MODULE_SYSTEM: true |
— |
System/0002_lps_system_animmeasure |
lps_system_animmeasure |
Chaîne de mesure (LPAnimMeasure) | MODULE_SYSTEM: true |
lps_system_core |
System/0010_lps_lib_scene |
lps_lib_scene |
Bibliothèque scène | (library par préfixe) | lps_system_core |
System/0011_lps_lib_actors |
lps_lib_actors |
Bibliothèque personnages | (library) | lps_system_core, lps_system_animmeasure |
Mods/lps_couple |
lps_couple |
Couple | MODULE_DEFAULT_ENABLED: true |
lps_system_core, lps_lib_scene, lps_lib_actors, lps_system_animmeasure |
Mods/lps_interaction |
lps_interaction |
Interaction | MODULE_DEFAULT_ENABLED: true |
idem |
Mods/lps_asset_integration |
lps_asset_integration |
Asset integration | MODULE_DEFAULT_ENABLED: true |
lps_system_core, lps_system_animmeasure |
MODULE_AUTHOR: LifePlay Revival, MODULE_VERSION: 0.1, MODULE_DESCRIPTION d'une ligne.
9. Critères (repris par SB1-T5)
(a) chaque mod s'ouvre et se comporte comme l'ancien mode ; (b) couple → interaction → asset integration → couple ;
(c) mod décoché non ouvrable, et décocher le mod ouvert ramène la scène neutre ; (d) Mods/lps_test_bad (créé
pour l'essai puis retiré) requérant lps_couple → Refused avec raison ; (e) prérequis absent (renommer
temporairement lps_lib_scene) → Missing sur couple et interaction ; (f) ordre, cases et mod ouvert persistés après
relance ; (g) section Jeu : .lpmod du jeu lus, avertissements éventuels listés ; (h) pilote : Saved/sandbox_mode.txt
= Mesure + Saved/sandbox_tour.txt = vignettes planches quitter → tournée identique à AP7 (mesures, vignettes,
planches) ; (i) journal LogLPSandboxMods : 7 modules sandbox, ordre attendu, 0 refus hors essais.