diff --git a/Config/DefaultGame.ini b/Config/DefaultGame.ini index 1272c1c..e5e2712 100644 --- a/Config/DefaultGame.ini +++ b/Config/DefaultGame.ini @@ -1,7 +1,7 @@ [/Script/EngineSettings.GeneralProjectSettings] ProjectID=543F4F9E4DE525696C82F5A52303C15C ProjectName=LifePlay -ProjectVersion=2026.9 beta +ProjectVersion=2026.10.4 Description=Portage UE 5.5 - assets integres depuis LifePlay 5.9 Full Source [/Script/MoviePlayer.MoviePlayerSettings] diff --git a/Content/UI/MainMenu.uasset b/Content/UI/MainMenu.uasset index d9607ad..7dcd5ae 100644 Binary files a/Content/UI/MainMenu.uasset and b/Content/UI/MainMenu.uasset differ diff --git a/Docs/Migration/14_Interface_V1.4.md b/Docs/Migration/14_Interface_V1.4.md new file mode 100644 index 0000000..9964bd2 --- /dev/null +++ b/Docs/Migration/14_Interface_V1.4.md @@ -0,0 +1,43 @@ +# 14. Interface du menu principal (lot UI14, avant la V1.4) + +Demande utilisateur (2026-10-02) : numéro de version au format année.mois.publication, liste des crédits vidée avec +Vinfamy comme auteur original, lien Donate vers le Patreon de LifePlay Revival, nouveau lien Discord, bouton +« Commission Game Content » masqué (il menait à une page de Vinfamy). + +## 1. Où se trouvent ces éléments + +| Élément | Emplacement | Avant | +|---|---|---| +| Texte de version | `UI/MainMenu`, fonction `Get_Version_Text_0` : `FormatText("LifePlay v{main}.{sub}{text}")` avec `GI.version_main`, `version_sub` (flottants, 5 et 9), `version_text` | « LifePlay v5.9 » | +| Titre des crédits | `UI/MainMenu`, `TextBlock_19` | « This project wouldn't be possible without the suppport from my generous patrons: » | +| Liste des crédits | `Content/Modules/donors.txt`, lu par `MainMenu` (`LoadTxt`, 4 colonnes) | 319 noms de donateurs de Vinfamy | +| Donate | `MainMenu` EventGraph, `K2Node_CallFunction_45` (`LaunchURL`, 3 boutons) | `https://www.patreon.com/vinfamy` | +| Discord | `MainMenu` EventGraph, `K2Node_CallFunction_32` (`LaunchURL`) | `https://discord.gg/eUEUhc6` | +| Commission Game Content | `MainMenu`, `CommissionButton` (visibilité liée à une fonction de l'écran titre) | visible | + +`GI.version_main` et `version_sub` servent aussi aux sauvegardes et à `GI.ReturnStatIfNeeded` : ils ne sont pas modifiés. + +## 2. Réalisation + +- **Version** : format du menu ramené à `LifePlay {text}` ; nouveau sous-système `Source/LifePlay/LPVersionSubsystem` + qui remplit `GI.version_text` au démarrage avec `ProjectVersion` de `Config/DefaultGame.ini` + (`[/Script/EngineSettings.GeneralProjectSettings]`). Format : **année.mois.publication**, le dernier nombre étant un + compteur de publications (V1.4 = 4) : `ProjectVersion=2026.10.4`. **À chaque publication**, mettre à jour cette ligne + (année et mois de la publication, compteur + 1). +- **Menu** : nouvelle commandlet `LPEditWidget` (`Source/LifePlayEditorTools`), pilotée par une spécification versionnée + `Tools/ui/MainMenu_V1.4.json` ; chaque modification indique la valeur attendue avant (sinon rien n'est écrit), le + widget est compilé sans erreur puis sauvegardé. Modifications : format de version, URL Donate + (`https://www.patreon.com/cw/edujiro5972`) et Discord (`https://discord.gg/q4689pNSy`), titre des crédits + (« Original author: »), infobulle Donate (« Support LifePlay Revival on Patreon »), `CommissionButton` masqué + (liaison de visibilité retirée). +- **Crédits** : `Content/Modules/donors.txt` réduit à `Vinfamy`. +- Liens laissés inchangés (non demandés) : f95zone et bilan 2021 de Vinfamy. + +## 3. Contrôles + +- Essai à blanc : 6 modifications trouvées, toutes conformes aux valeurs attendues. +- Comparaison avant/après (`LPDumpFunction`, `LPDumpWidgetTree`) : seuls changent les broches `main`/`sub` du format + (retirées), les deux URL, le titre, l'infobulle et la visibilité du bouton. +- Build `LifePlay_Build_5.5_UI14` : « Version affichee : LifePlay 2026.10.4 » ; avertissements de script identiques au + build précédent (16 « Accessed None », déjà présents). +- Sauvegarde : `D:\Game\LifePlay_Backup_5.5\UI14` (`MainMenu.uasset`, `donors.txt`) ; retour : `Tools\Restore.ps1 -Lot UI14`. diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 7846faf..ea1f24b 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -938,3 +938,4 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol | CHG-091 | 2026-10-02 | Doc | nouveaux `Docs/Migration/13_Protocole_Coiffure.md`, `Tools/HairImportFbx.py`, `Tools/HairMaterials.py` ; `README.md` (migration), `08_Guide_Developpeur.md`, `09_Commandes_Utiles.md` | protocole de conversion d'une coiffure (demande utilisateur) ; scripts ponctuels d'O7e rendus paramétrables ; protocole rejoué de bout en bout sur Pocolov 17 sous le nom `Pocolov17P` : bornes identiques au millimètre, dépendances OK ; essai retiré du projet (`D:\Game\LifePlay_Backup_5.5\O7e\proto_essai`) | `git revert` du commit | | CHG-092 | 2026-10-02 | HAIRS1 | nouveaux `Tools/HairPipeline.ps1`, `Tools/FbxMaterialMap.py`, `Tools/hairs/HAIRS1.json` ; `Tools/HairImportFbx.py` (rapport JSON), `Tools/HairMaterials.py` (calotte seulement si utilisée, contrôle des dépendances) ; assets `Content/Heavy/New5/Hairs/Pocolov03/06/11/15/16*` (hors dépôt) ; `Content/Modules/lp_Revival/Hairstyles/pocolov03/06/11/15/16.lphair` ; `Licenses/Free_3D_Models_Credits.txt` ; `13_Protocole_Coiffure.md` §7 | industrialisation de la conversion (demande utilisateur) et test sur 5 coiffures Pocolov (CC BY 4.0, téléchargées avec l'accord de l'utilisateur) ; 5 cas d'archives différents intégrés aux règles ; build `LifePlay_Build_5.5_HAIRS1` : 6 coiffures ajoutées (index 126 à 131) ; contrôle visuel en jeu en attente | retirer les `.lphair` ; assets et sources dans `D:\Game\LifePlay_Backup_5.5\HAIRS1` ; `git checkout HAIRS1-avant -- Tools Licenses Content/Modules` | | CHG-093 | 2026-10-02 | SKIN | `LPHairFromStaticMeshCommandlet` : liaison par transfert de poids depuis le corps (`-Skin=transfer` par défaut, 6 voisins, bras et jambes exclus) et sortie de la peau (`-Gap` 0,3 cm) ; `LifePlayEditorTools.Build.cs` (`SkeletalMeshDescription`, `AnimationCore`) ; `HairPipeline.ps1` (`-Rebuild`, options `skin`, `gap`) ; 6 coiffures Pocolov reconstruites (versions précédentes dans `HAIRS1\\precedente_*`), Pocolov 17 ajoutée au lot ; `.lphair` et crédits : description des modifications corrigée ; `13_Protocole_Coiffure.md` | vidéo utilisateur : mèches longues liées à la seule tête, qui traversent épaules et poitrine quand la tête tourne ; poids obtenus : tête 47 à 62 %, cou, clavicules, poitrine ; 254 à 950 sommets sortis de la peau par coiffure ; build `LifePlay_Build_5.5_SKIN` ; validé en jeu le 2026-10-02 (les mèches ne traversent plus la peau quand la tête tourne) | `-Skin=rigid` ; `git checkout SKIN-avant -- Source Tools` et remettre les versions précédentes | +| CHG-094 | 2026-10-02 | UI14 | nouveaux `Source/LifePlay/LPVersionSubsystem.h/.cpp`, `Source/LifePlayEditorTools/LPEditWidgetCommandlet.h/.cpp` (+ `Json` dans le `.Build.cs`), `Tools/ui/MainMenu_V1.4.json`, `Docs/Migration/14_Interface_V1.4.md` ; `Content/UI/MainMenu.uasset` (6 modifications, diff conforme) ; `Content/Modules/donors.txt` (→ `Vinfamy`) ; `Config/DefaultGame.ini` (`ProjectVersion=2026.10.4`) | demande utilisateur avant la V1.4 : version année.mois.publication, crédits vidés avec Vinfamy auteur original, Donate → Patreon edujiro5972, Discord → discord.gg/q4689pNSy, Commission masqué ; build `LifePlay_Build_5.5_UI14` : « LifePlay 2026.10.4 » ; contrôle en jeu en attente | `Tools\Restore.ps1 -Lot UI14` ; `git checkout UI14-avant -- Config Source Content/Modules/donors.txt` | diff --git a/Docs/Migration/README.md b/Docs/Migration/README.md index e8c9f33..1db0f92 100644 --- a/Docs/Migration/README.md +++ b/Docs/Migration/README.md @@ -24,6 +24,7 @@ La migration a été reprise le 2026-09-29, après l'échec d'un premier portage | [11_Audit_Optimisation.md](11_Audit_Optimisation.md) | pilotage | chantier d'optimisation UE5 : audit (lot O0), goulots, leviers, lots O1 à O8, Fab | | [12_Reconciliation_Vinfamy.md](12_Reconciliation_Vinfamy.md) | historique | comparaison avec `vinfamy/LifePlay`, fusion d'historique, rien à récupérer | | [13_Protocole_Coiffure.md](13_Protocole_Coiffure.md) | protocole | ajouter une coiffure (FBX statique, Fab) : import, ajustement sur la tête de Bridget, matériaux, fichier `.lphair`, contrôles | +| [14_Interface_V1.4.md](14_Interface_V1.4.md) | lot | menu principal : version année.mois.publication, crédits (Vinfamy, auteur original), liens Donate et Discord, bouton Commission masqué | | [Journal_Modifications.md](Journal_Modifications.md) | traçabilité | `CHG-001` à `CHG-057`, chacune avec son retour arrière | | [Registre_Erreurs.md](Registre_Erreurs.md) | traçabilité | `ERR-001` à `ERR-037`, statut et lot | | `correctifs/` | relecture | écarts de code 4.27 → 5.5 en patchs (`P01` à `P06`) | diff --git a/Source/LifePlay/LPVersionSubsystem.cpp b/Source/LifePlay/LPVersionSubsystem.cpp new file mode 100644 index 0000000..415a305 --- /dev/null +++ b/Source/LifePlay/LPVersionSubsystem.cpp @@ -0,0 +1,23 @@ +// Portage UE 5.5 - voir LPVersionSubsystem.h + +#include "LPVersionSubsystem.h" + +#include "Engine/GameInstance.h" +#include "GeneralProjectSettings.h" +#include "LifePlay.h" +#include "UObject/UnrealType.h" + +void ULPVersionSubsystem::Initialize(FSubsystemCollectionBase& Collection) +{ + Super::Initialize(Collection); + UGameInstance* GI = GetGameInstance(); + const FStrProperty* Prop = GI ? CastField(GI->GetClass()->FindPropertyByName(TEXT("version_text"))) : nullptr; + const FString Version = GetDefault()->ProjectVersion; + if (!Prop) + { + UE_LOG(LogLifePlay, Warning, TEXT("Version : GI.version_text introuvable sur %s, version %s non affichee"), *GetNameSafe(GI), *Version); + return; + } + Prop->SetPropertyValue_InContainer(GI, Version); + UE_LOG(LogLifePlay, Display, TEXT("Version affichee : LifePlay %s"), *Version); +} diff --git a/Source/LifePlay/LPVersionSubsystem.h b/Source/LifePlay/LPVersionSubsystem.h new file mode 100644 index 0000000..8e918f3 --- /dev/null +++ b/Source/LifePlay/LPVersionSubsystem.h @@ -0,0 +1,21 @@ +// Portage UE 5.5 - lot UI14 (Docs/Migration/14_Interface_V1.4.md). +// +// Le menu principal affiche "LifePlay {text}" (UI/MainMenu, Get_Version_Text_0) avec text = GI.version_text. Ce +// sous-systeme remplit GI.version_text au demarrage avec la version du projet (Config/DefaultGame.ini, +// [/Script/EngineSettings.GeneralProjectSettings] ProjectVersion, format annee.mois.publication : 2026.10.4). +// GI.version_main et GI.version_sub (5 et 9, utilises par les sauvegardes et ReturnStatIfNeeded) ne sont pas modifies. + +#pragma once + +#include "CoreMinimal.h" +#include "Subsystems/GameInstanceSubsystem.h" +#include "LPVersionSubsystem.generated.h" + +UCLASS() +class LIFEPLAY_API ULPVersionSubsystem : public UGameInstanceSubsystem +{ + GENERATED_BODY() + +public: + virtual void Initialize(FSubsystemCollectionBase& Collection) override; +}; diff --git a/Source/LifePlayEditorTools/LPEditWidgetCommandlet.cpp b/Source/LifePlayEditorTools/LPEditWidgetCommandlet.cpp new file mode 100644 index 0000000..6e48d38 --- /dev/null +++ b/Source/LifePlayEditorTools/LPEditWidgetCommandlet.cpp @@ -0,0 +1,240 @@ +// Portage UE 5.5 - voir LPEditWidgetCommandlet.h + +#include "LPEditWidgetCommandlet.h" + +#include "Blueprint/WidgetTree.h" +#include "Components/TextBlock.h" +#include "Components/Widget.h" +#include "Dom/JsonObject.h" +#include "EdGraph/EdGraph.h" +#include "EdGraphSchema_K2.h" +#include "K2Node.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Misc/FileHelper.h" +#include "Misc/PackageName.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" +#include "UObject/Package.h" +#include "UObject/SavePackage.h" +#include "WidgetBlueprint.h" + +DEFINE_LOG_CATEGORY_STATIC(LogLPEditWidget, Log, All); + +namespace +{ + UEdGraphNode* FindNode(UBlueprint* BP, const FString& GraphName, const FString& NodeName) + { + TArray Graphs; + BP->GetAllGraphs(Graphs); + for (UEdGraph* Graph : Graphs) + { + if (Graph && Graph->GetName() == GraphName) + { + for (UEdGraphNode* Node : Graph->Nodes) + { + if (Node && Node->GetName() == NodeName) + { + return Node; + } + } + } + } + return nullptr; + } + + /** Valeur par defaut d'une broche, texte ou non, telle qu'affichee. */ + FString PinValue(const UEdGraphPin* Pin) + { + return Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Text ? Pin->DefaultTextValue.ToString() : Pin->DefaultValue; + } + + bool ParseVisibility(const FString& Text, ESlateVisibility& Out) + { + const UEnum* Enum = StaticEnum(); + const int64 Value = Enum->GetValueByNameString(Text); + if (Value == INDEX_NONE) + { + return false; + } + Out = static_cast(Value); + return true; + } +} + +ULPEditWidgetCommandlet::ULPEditWidgetCommandlet() +{ + IsClient = false; + IsEditor = true; + IsServer = false; + LogToConsole = true; +} + +int32 ULPEditWidgetCommandlet::Main(const FString& Params) +{ + TArray Tokens, Switches; + TMap P; + ParseCommandLine(*Params, Tokens, Switches, P); + const bool bDryRun = Switches.Contains(TEXT("DryRun")); + FString Json; + TSharedPtr Spec; + if (!FFileHelper::LoadFileToString(Json, *P.FindRef(TEXT("Spec"))) || !FJsonSerializer::Deserialize(TJsonReaderFactory<>::Create(Json), Spec) || !Spec.IsValid()) + { + UE_LOG(LogLPEditWidget, Error, TEXT("Usage : -Spec= [-DryRun] (fichier illisible : %s)"), *P.FindRef(TEXT("Spec"))); + return 1; + } + const FString BPPath = Spec->GetStringField(TEXT("bp")); + UWidgetBlueprint* BP = LoadObject(nullptr, *(BPPath + TEXT(".") + FPackageName::GetShortName(BPPath))); + if (!BP || !BP->WidgetTree) + { + UE_LOG(LogLPEditWidget, Error, TEXT("Blueprint de widget introuvable : %s"), *BPPath); + return 1; + } + const UEdGraphSchema_K2* Schema = GetDefault(); + int32 Errors = 0, Changes = 0; + + // 1. Broches. + const TArray>* Pins = nullptr; + if (Spec->TryGetArrayField(TEXT("pins"), Pins)) + { + for (const TSharedPtr& V : *Pins) + { + const TSharedPtr& O = V->AsObject(); + const FString Where = O->GetStringField(TEXT("graph")) + TEXT(" / ") + O->GetStringField(TEXT("node")) + TEXT(" . ") + O->GetStringField(TEXT("pin")); + UEdGraphNode* Node = FindNode(BP, O->GetStringField(TEXT("graph")), O->GetStringField(TEXT("node"))); + UEdGraphPin* Pin = Node ? Node->FindPin(*O->GetStringField(TEXT("pin"))) : nullptr; + if (!Pin) + { + UE_LOG(LogLPEditWidget, Error, TEXT("Broche introuvable : %s"), *Where); + ++Errors; + continue; + } + const FString Before = PinValue(Pin); + const FString Expect = O->GetStringField(TEXT("expect")); + const FString Value = O->GetStringField(TEXT("value")); + if (Before == Value) + { + UE_LOG(LogLPEditWidget, Warning, TEXT(" %s : deja a la valeur cible"), *Where); + continue; + } + if (Before != Expect) + { + UE_LOG(LogLPEditWidget, Error, TEXT(" %s : valeur actuelle '%s', attendue '%s'"), *Where, *Before, *Expect); + ++Errors; + continue; + } + UE_LOG(LogLPEditWidget, Warning, TEXT(" %s : '%s' -> '%s'"), *Where, *Before, *Value); + if (!bDryRun) + { + if (Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Text) + { + Schema->TrySetDefaultText(*Pin, FText::FromString(Value)); + } + else + { + Schema->TrySetDefaultValue(*Pin, Value); + } + if (PinValue(Pin) != Value) + { + UE_LOG(LogLPEditWidget, Error, TEXT(" %s : valeur relue '%s' apres modification"), *Where, *PinValue(Pin)); + ++Errors; + } + } + ++Changes; + } + } + + // 2. Elements de l'arbre. + const TArray>* Widgets = nullptr; + if (Spec->TryGetArrayField(TEXT("widgets"), Widgets)) + { + for (const TSharedPtr& V : *Widgets) + { + const TSharedPtr& O = V->AsObject(); + const FString Name = O->GetStringField(TEXT("name")); + UWidget* Widget = BP->WidgetTree->FindWidget(FName(*Name)); + if (!Widget) + { + UE_LOG(LogLPEditWidget, Error, TEXT("Element introuvable : %s"), *Name); + ++Errors; + continue; + } + FString Text; + if (O->TryGetStringField(TEXT("text"), Text)) + { + UTextBlock* TextBlock = Cast(Widget); + const FString Before = TextBlock ? TextBlock->GetText().ToString() : FString(); + if (!TextBlock || Before != O->GetStringField(TEXT("expect_text"))) + { + UE_LOG(LogLPEditWidget, Error, TEXT(" %s : texte actuel '%s', attendu '%s'"), *Name, *Before, *O->GetStringField(TEXT("expect_text"))); + ++Errors; + } + else + { + UE_LOG(LogLPEditWidget, Warning, TEXT(" %s.Text : '%s' -> '%s'"), *Name, *Before, *Text); + if (!bDryRun) { TextBlock->SetText(FText::FromString(Text)); } + ++Changes; + } + } + FString Tooltip; + if (O->TryGetStringField(TEXT("tooltip"), Tooltip)) + { + UE_LOG(LogLPEditWidget, Warning, TEXT(" %s.ToolTipText : '%s' -> '%s'"), *Name, *Widget->GetToolTipText().ToString(), *Tooltip); + if (!bDryRun) { Widget->SetToolTipText(FText::FromString(Tooltip)); } + ++Changes; + } + FString VisibilityText; + if (O->TryGetStringField(TEXT("visibility"), VisibilityText)) + { + ESlateVisibility Visibility; + if (!ParseVisibility(VisibilityText, Visibility)) + { + UE_LOG(LogLPEditWidget, Error, TEXT(" %s : visibilite inconnue '%s'"), *Name, *VisibilityText); + ++Errors; + continue; + } + const int32 Bound = BP->Bindings.IndexOfByPredicate([&](const FDelegateEditorBinding& B) { return B.ObjectName == Name && B.PropertyName == TEXT("Visibility"); }); + UE_LOG(LogLPEditWidget, Warning, TEXT(" %s.Visibility : %s -> %s%s"), *Name, *StaticEnum()->GetNameStringByValue(int64(Widget->GetVisibility())), + *VisibilityText, Bound != INDEX_NONE ? TEXT(" (liaison de visibilite retiree)") : TEXT("")); + if (!bDryRun) + { + Widget->SetVisibility(Visibility); + if (Bound != INDEX_NONE) { BP->Bindings.RemoveAt(Bound); } + } + ++Changes; + } + } + } + + if (Errors > 0) + { + UE_LOG(LogLPEditWidget, Error, TEXT("%d erreur(s) : rien n'est sauvegarde"), Errors); + return 1; + } + if (bDryRun || Changes == 0) + { + UE_LOG(LogLPEditWidget, Warning, TEXT("%d modification(s) %s"), Changes, bDryRun ? TEXT("(essai a blanc, rien n'est sauvegarde)") : TEXT(": rien a faire")); + return 0; + } + + // 3. Compilation et sauvegarde. + BP->Modify(); + FKismetEditorUtilities::CompileBlueprint(BP); + if (BP->Status == BS_Error) + { + UE_LOG(LogLPEditWidget, Error, TEXT("Compilation en erreur : rien n'est sauvegarde")); + return 1; + } + UPackage* Package = BP->GetOutermost(); + Package->MarkPackageDirty(); + const FString Filename = FPackageName::LongPackageNameToFilename(Package->GetName(), FPackageName::GetAssetPackageExtension()); + FSavePackageArgs SaveArgs; + SaveArgs.TopLevelFlags = RF_Public | RF_Standalone; + SaveArgs.Error = GWarn; + if (!UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs)) + { + UE_LOG(LogLPEditWidget, Error, TEXT("Echec de sauvegarde de %s"), *Filename); + return 1; + } + UE_LOG(LogLPEditWidget, Warning, TEXT("%d modification(s), compile sans erreur, sauvegarde : %s"), Changes, *Filename); + return 0; +} diff --git a/Source/LifePlayEditorTools/LPEditWidgetCommandlet.h b/Source/LifePlayEditorTools/LPEditWidgetCommandlet.h new file mode 100644 index 0000000..0dc3eb4 --- /dev/null +++ b/Source/LifePlayEditorTools/LPEditWidgetCommandlet.h @@ -0,0 +1,28 @@ +// Portage UE 5.5 - lot UI14 (interface du menu principal avant la V1.4, Docs/Migration/14_Interface_V1.4.md). +// +// Applique a un Blueprint de widget les modifications decrites par un fichier JSON (versionne dans Tools/ui/) : +// "pins" : valeur par defaut d'une broche d'un noeud ({ "graph", "node", "pin", "expect", "value" }) ; pour une +// broche texte (Format d'un FormatText) la valeur est un texte, le noeud reconstruit ses broches ; +// "widgets" : proprietes d'un element de l'arbre ({ "name", "expect_text", "text", "tooltip", "visibility" }) ; +// "visibility" retire aussi une liaison (binding) de visibilite qui l'ecraserait. +// Chaque modification indique la valeur attendue AVANT ("expect", "expect_text") : au moindre ecart, ou element +// introuvable, rien n'est sauvegarde. Le Blueprint est ensuite compile (aucune erreur toleree) puis sauvegarde. +// +// Usage : +// UnrealEditor-Cmd.exe -run=LPEditWidget -Spec=/Tools/ui/.json [-DryRun] + +#pragma once + +#include "CoreMinimal.h" +#include "Commandlets/Commandlet.h" +#include "LPEditWidgetCommandlet.generated.h" + +UCLASS() +class ULPEditWidgetCommandlet : public UCommandlet +{ + GENERATED_BODY() + +public: + ULPEditWidgetCommandlet(); + virtual int32 Main(const FString& Params) override; +}; diff --git a/Source/LifePlayEditorTools/LifePlayEditorTools.Build.cs b/Source/LifePlayEditorTools/LifePlayEditorTools.Build.cs index faa863b..18a8a4f 100644 --- a/Source/LifePlayEditorTools/LifePlayEditorTools.Build.cs +++ b/Source/LifePlayEditorTools/LifePlayEditorTools.Build.cs @@ -45,7 +45,9 @@ public class LifePlayEditorTools : ModuleRules "SkeletalMeshUtilitiesCommon", // Transfert de poids (FSkeletalMeshAttributes, FBoneWeight) "SkeletalMeshDescription", - "AnimationCore" + "AnimationCore", + // UI14 : LPEditWidgetCommandlet lit sa specification JSON + "Json" }); } } diff --git a/Tools/ui/MainMenu_V1.4.json b/Tools/ui/MainMenu_V1.4.json new file mode 100644 index 0000000..c5e2baa --- /dev/null +++ b/Tools/ui/MainMenu_V1.4.json @@ -0,0 +1,16 @@ +{ + "bp": "/Game/UI/MainMenu", + "pins": [ + { "graph": "Get_Version_Text_0", "node": "K2Node_FormatText_0", "pin": "Format", + "expect": "LifePlay v{main}.{sub}{text}", "value": "LifePlay {text}" }, + { "graph": "EventGraph", "node": "K2Node_CallFunction_45", "pin": "URL", + "expect": "https://www.patreon.com/vinfamy", "value": "https://www.patreon.com/cw/edujiro5972" }, + { "graph": "EventGraph", "node": "K2Node_CallFunction_32", "pin": "URL", + "expect": "https://discord.gg/eUEUhc6", "value": "https://discord.gg/q4689pNSy" } + ], + "widgets": [ + { "name": "TextBlock_19", "expect_text": "This project wouldn't be possible without the suppport from my generous patrons:", "text": "Original author:" }, + { "name": "DonateButton", "tooltip": "Support LifePlay Revival on Patreon" }, + { "name": "CommissionButton", "visibility": "Collapsed" } + ] +}