// 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(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(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(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(Element->GetOwningNode()) : nullptr; const UK2Node_VariableGet* Array = Loop ? Cast(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(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(nullptr, PersonPath); if (!IsModDaily || !Blueprint) { UE_LOG(LogLPPatchDailies, Error, TEXT("%s introuvable."), IsModDaily ? PersonPath : TEXT("ULPModDailiesLibrary::IsModDaily")); return 1; } TArray Graphs; Blueprint->GetAllGraphs(Graphs); TArray> Switches; TArray Splits; for (UEdGraph* Graph : Graphs) { for (UEdGraphNode* Node : Graph ? Graph->Nodes : TArray>()) { 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(); Graph->Modify(); bool bOk = true; if (!bSwitchDone) { UK2Node_CallFunction* Test = NewObject(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(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; }