LifePlay-Revival/Source/LifePlayEditorTools/LPPatchDailiesCommandlet.cpp

242 lines
9.2 KiB
C++
Raw Normal View History

// Lot AP5 - voir LPPatchDailiesCommandlet.h
#include "LPPatchDailiesCommandlet.h"
#include "EdGraph/EdGraph.h"
#include "EdGraph/EdGraphPin.h"
#include "EdGraphSchema_K2.h"
#include "Engine/Blueprint.h"
#include "K2Node_CallFunction.h"
#include "K2Node_IfThenElse.h"
#include "K2Node_MacroInstance.h"
#include "K2Node_SwitchString.h"
#include "K2Node_VariableGet.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Kismet2/KismetEditorUtilities.h"
#include "LPModDailies.h"
#include "Misc/PackageName.h"
#include "UObject/Package.h"
#include "UObject/SavePackage.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPPatchDailies, Log, All);
namespace
{
const TCHAR* PersonPath = TEXT("/Game/BPs/Person.Person");
const FName IsModDailyName(TEXT("IsModDaily"));
UEdGraphNode* LinkedNode(const UEdGraphPin* Pin)
{
return Pin && Pin->LinkedTo.Num() == 1 && Pin->LinkedTo[0] ? Pin->LinkedTo[0]->GetOwningNode() : nullptr;
}
bool Calls(const UEdGraphNode* Node, const FName Function)
{
const UK2Node_CallFunction* Call = Cast<UK2Node_CallFunction>(Node);
return Call && Call->FunctionReference.GetMemberName() == Function;
}
/** Aiguillage des cles de PlayDaily : SwitchString avec les cas martialart et na_sleep. */
UK2Node_SwitchString* IsKeySwitch(UEdGraphNode* Node)
{
UK2Node_SwitchString* Switch = Cast<UK2Node_SwitchString>(Node);
return Switch && Switch->FindPin(TEXT("martialart"), EGPD_Output) && Switch->FindPin(TEXT("na_sleep"), EGPD_Output)
? Switch : nullptr;
}
/** Split '_' de PlayDailyAnim : SourceString <- GetObjectName(Object <- element d'un ForEach sur Dailies). */
UK2Node_CallFunction* IsDailiesSplit(UEdGraphNode* Node)
{
UK2Node_CallFunction* Split = Cast<UK2Node_CallFunction>(Node);
const UEdGraphPin* InStr = Split && Calls(Split, TEXT("Split")) ? Split->FindPin(TEXT("InStr"), EGPD_Input) : nullptr;
if (!InStr || InStr->DefaultValue != TEXT("_") || InStr->LinkedTo.Num() > 0)
{
return nullptr;
}
UEdGraphNode* GetName = LinkedNode(Split->FindPin(TEXT("SourceString"), EGPD_Input));
if (!Calls(GetName, TEXT("GetObjectName")))
{
return nullptr;
}
const UEdGraphPin* Object = GetName->FindPin(TEXT("Object"), EGPD_Input);
const UEdGraphPin* Element = Object && Object->LinkedTo.Num() == 1 ? Object->LinkedTo[0] : nullptr;
const UK2Node_MacroInstance* Loop = Element ? Cast<UK2Node_MacroInstance>(Element->GetOwningNode()) : nullptr;
const UK2Node_VariableGet* Array = Loop ? Cast<UK2Node_VariableGet>(LinkedNode(Loop->FindPin(TEXT("Array"), EGPD_Input))) : nullptr;
return Array && Array->VariableReference.GetMemberName() == TEXT("Dailies") ? Split : nullptr;
}
/** Default deja branche par ce commandlet : Branch dont la condition vient d'IsModDaily. */
bool IsOurBranch(const UEdGraphNode* Node)
{
const UK2Node_IfThenElse* Branch = Cast<UK2Node_IfThenElse>(Node);
return Branch && Calls(LinkedNode(Branch->GetConditionPin()), IsModDailyName);
}
}
ULPPatchDailiesCommandlet::ULPPatchDailiesCommandlet()
{
IsClient = false;
IsServer = false;
IsEditor = true;
LogToConsole = true;
}
int32 ULPPatchDailiesCommandlet::Main(const FString& Params)
{
const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase);
UE_LOG(LogLPPatchDailies, Warning, TEXT("=== Person : cles d'animation des mods (AP5)%s ==="), bDryRun ? TEXT(" [DRY RUN]") : TEXT(""));
UFunction* IsModDaily = ULPModDailiesLibrary::StaticClass()->FindFunctionByName(IsModDailyName);
UBlueprint* Blueprint = LoadObject<UBlueprint>(nullptr, PersonPath);
if (!IsModDaily || !Blueprint)
{
UE_LOG(LogLPPatchDailies, Error, TEXT("%s introuvable."), IsModDaily ? PersonPath : TEXT("ULPModDailiesLibrary::IsModDaily"));
return 1;
}
TArray<UEdGraph*> Graphs;
Blueprint->GetAllGraphs(Graphs);
TArray<TPair<UEdGraph*, UK2Node_SwitchString*>> Switches;
TArray<UK2Node_CallFunction*> Splits;
for (UEdGraph* Graph : Graphs)
{
for (UEdGraphNode* Node : Graph ? Graph->Nodes : TArray<TObjectPtr<UEdGraphNode>>())
{
if (UK2Node_SwitchString* Switch = IsKeySwitch(Node))
{
Switches.Emplace(Graph, Switch);
}
if (UK2Node_CallFunction* Split = IsDailiesSplit(Node))
{
Splits.Add(Split);
}
}
}
if (Switches.Num() != 1 || Splits.Num() != 1)
{
UE_LOG(LogLPPatchDailies, Error, TEXT("Attendu 1 aiguillage et 1 Split : %d et %d trouves. Rien n'est modifie."),
Switches.Num(), Splits.Num());
return 1;
}
UEdGraph* Graph = Switches[0].Key;
UK2Node_SwitchString* Switch = Switches[0].Value;
UK2Node_CallFunction* Split = Splits[0];
UE_LOG(LogLPPatchDailies, Warning, TEXT(" aiguillage : %s :: %s ; Split : %s"), *Graph->GetName(), *Switch->GetName(), *Split->GetName());
UEdGraphPin* Default = Switch->GetDefaultPin();
UEdGraphPin* Selection = Switch->GetSelectionPin();
UEdGraphPin* Generic = Switch->FindPin(TEXT("martialart"), EGPD_Output);
UEdGraphPin* SearchDir = Split->FindPin(TEXT("SearchDir"), EGPD_Input);
if (!Default || !Selection || Selection->LinkedTo.Num() != 1 || !Generic || Generic->LinkedTo.Num() != 1 || !SearchDir)
{
UE_LOG(LogLPPatchDailies, Error, TEXT("Broches inattendues (Default, Selection, martialart ou SearchDir). Rien n'est modifie."));
return 1;
}
UEdGraphPin* KeySource = Selection->LinkedTo[0];
UEdGraphPin* GenericTarget = Generic->LinkedTo[0];
bool bSwitchDone = false;
if (Default->LinkedTo.Num() > 0)
{
if (Default->LinkedTo.Num() == 1 && IsOurBranch(Default->LinkedTo[0]->GetOwningNode()))
{
bSwitchDone = true;
UE_LOG(LogLPPatchDailies, Warning, TEXT(" 1. Default deja branche sur IsModDaily : laisse tel quel."));
}
else
{
UE_LOG(LogLPPatchDailies, Error, TEXT(" 1. Default deja relie a autre chose (%s). Rien n'est modifie."),
*LinkedNode(Default)->GetName());
return 1;
}
}
const bool bSplitDone = SearchDir->DefaultValue == TEXT("FromEnd");
if (!bSplitDone && SearchDir->DefaultValue != TEXT("FromStart"))
{
UE_LOG(LogLPPatchDailies, Error, TEXT(" 2. SearchDir inattendu '%s'. Rien n'est modifie."), *SearchDir->DefaultValue);
return 1;
}
UE_LOG(LogLPPatchDailies, Warning, TEXT(" 1. Default -> IsModDaily(%s.%s) -> %s.%s : %s"),
*KeySource->GetOwningNode()->GetName(), *KeySource->PinName.ToString(),
*GenericTarget->GetOwningNode()->GetName(), *GenericTarget->PinName.ToString(), bSwitchDone ? TEXT("deja fait") : TEXT("a faire"));
UE_LOG(LogLPPatchDailies, Warning, TEXT(" 2. Split SearchDir FromStart -> FromEnd : %s"), bSplitDone ? TEXT("deja fait") : TEXT("a faire"));
if (bDryRun || (bSwitchDone && bSplitDone))
{
UE_LOG(LogLPPatchDailies, Warning, TEXT("=== Termine sans modification. ==="));
return 0;
}
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
Graph->Modify();
bool bOk = true;
if (!bSwitchDone)
{
UK2Node_CallFunction* Test = NewObject<UK2Node_CallFunction>(Graph);
Test->SetFromFunction(IsModDaily);
Test->NodePosX = Switch->NodePosX + 300;
Test->NodePosY = Switch->NodePosY - 200;
Test->CreateNewGuid();
Test->PostPlacedNewNode();
Test->AllocateDefaultPins();
Graph->AddNode(Test, false, false);
UK2Node_IfThenElse* Branch = NewObject<UK2Node_IfThenElse>(Graph);
Branch->NodePosX = Switch->NodePosX + 600;
Branch->NodePosY = Switch->NodePosY - 200;
Branch->CreateNewGuid();
Branch->PostPlacedNewNode();
Branch->AllocateDefaultPins();
Graph->AddNode(Branch, false, false);
UEdGraphPin* Key = Test->FindPin(TEXT("Key"), EGPD_Input);
bOk = Key && Schema->TryCreateConnection(KeySource, Key)
&& Schema->TryCreateConnection(Test->GetReturnValuePin(), Branch->GetConditionPin())
&& Schema->TryCreateConnection(Default, Branch->GetExecPin())
&& Schema->TryCreateConnection(Branch->GetThenPin(), GenericTarget);
// Relecture : liaisons posees, et celles d'avant intactes.
bOk = bOk && IsOurBranch(LinkedNode(Default)) && Selection->LinkedTo.Contains(KeySource)
&& Generic->LinkedTo.Contains(GenericTarget) && Branch->GetThenPin()->LinkedTo.Contains(GenericTarget)
&& Branch->GetElsePin()->LinkedTo.Num() == 0;
}
if (bOk && !bSplitDone)
{
Split->Modify();
Schema->TrySetDefaultValue(*SearchDir, TEXT("FromEnd"));
bOk = SearchDir->DefaultValue == TEXT("FromEnd");
}
if (!bOk)
{
UE_LOG(LogLPPatchDailies, Error, TEXT("Liaison refusee ou relecture en ecart : Person NON sauvegarde."));
return 1;
}
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
FKismetEditorUtilities::CompileBlueprint(Blueprint);
if (Blueprint->Status == BS_Error)
{
UE_LOG(LogLPPatchDailies, Error, TEXT("Compilation de Person en erreur : NON sauvegarde."));
return 1;
}
UPackage* Package = Blueprint->GetOutermost();
FString Filename;
if (!FPackageName::TryConvertLongPackageNameToFilename(Package->GetName(), Filename, FPackageName::GetAssetPackageExtension()))
{
UE_LOG(LogLPPatchDailies, Error, TEXT("Chemin introuvable pour %s"), *Package->GetName());
return 1;
}
ResetLoaders(Package);
FSavePackageArgs SaveArgs;
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
SaveArgs.SaveFlags = SAVE_None;
SaveArgs.bWarnOfLongFilename = true;
SaveArgs.Error = GWarn;
if (!UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs))
{
UE_LOG(LogLPPatchDailies, Error, TEXT("ECHEC de sauvegarde de %s"), *Package->GetName());
return 1;
}
UE_LOG(LogLPPatchDailies, Warning, TEXT("=== Person sauvegarde. ==="));
return 0;
}