diff --git a/Content/BPs/Room.uasset b/Content/BPs/Room.uasset index 5c21322..24fe49d 100644 Binary files a/Content/BPs/Room.uasset and b/Content/BPs/Room.uasset differ diff --git a/Source/LifePlayEditorTools/LPFixPawnResponseCommandlet.cpp b/Source/LifePlayEditorTools/LPFixPawnResponseCommandlet.cpp new file mode 100644 index 0000000..47e5aa1 --- /dev/null +++ b/Source/LifePlayEditorTools/LPFixPawnResponseCommandlet.cpp @@ -0,0 +1,165 @@ +// Voir LPFixPawnResponseCommandlet.h + +#include "LPFixPawnResponseCommandlet.h" + +#include "EdGraph/EdGraph.h" +#include "EdGraphSchema_K2.h" +#include "Engine/Blueprint.h" +#include "Engine/SCS_Node.h" +#include "Engine/SimpleConstructionScript.h" +#include "K2Node_CallFunction.h" +#include "Kismet2/BlueprintEditorUtils.h" +#include "Kismet2/KismetEditorUtilities.h" +#include "Misc/PackageName.h" +#include "UObject/Package.h" +#include "UObject/SavePackage.h" + +DEFINE_LOG_CATEGORY_STATIC(LogLPFixPawn, Log, All); + +ULPFixPawnResponseCommandlet::ULPFixPawnResponseCommandlet() +{ + IsClient = false; + IsServer = false; + IsEditor = true; + LogToConsole = true; +} + +int32 ULPFixPawnResponseCommandlet::Main(const FString& Params) +{ + const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase); + UBlueprint* Room = LoadObject(nullptr, TEXT("/Game/BPs/Room.Room")); + if (!Room) + { + UE_LOG(LogLPFixPawn, Error, TEXT("Blueprint Room introuvable")); + return 1; + } + + UK2Node_CallFunction* PawnNode = nullptr; + UK2Node_CallFunction* TypeNode = nullptr; + UK2Node_CallFunction* ScaleNode = nullptr; + for (UEdGraph* Graph : Room->FunctionGraphs) + { + if (!Graph || Graph->GetName() != TEXT("addObject")) + { + continue; + } + for (UEdGraphNode* Node : Graph->Nodes) + { + UK2Node_CallFunction* Call = Cast(Node); + if (!Call) + { + continue; + } + const FName Function = Call->FunctionReference.GetMemberName(); + if (Function == TEXT("SetCollisionResponseToChannel")) + { + UEdGraphPin* Channel = Call->FindPin(TEXT("Channel")); + if (Channel && Channel->DefaultValue == TEXT("ECC_Pawn")) + { + PawnNode = Call; + } + } + else if (Function == TEXT("SetCollisionObjectType")) + { + TypeNode = Call; + } + else if (Function == TEXT("SetWorldScale3D")) + { + ScaleNode = Call; + } + } + } + if (!PawnNode || !TypeNode || !ScaleNode) + { + UE_LOG(LogLPFixPawn, Error, TEXT("noeuds addObject introuvables : Blueprint non sauvegarde")); + return 1; + } + + UEdGraphPin* Response = PawnNode->FindPin(TEXT("NewResponse")); + UEdGraphPin* TypeChannel = TypeNode->FindPin(TEXT("Channel")); + UEdGraphPin* PawnThen = PawnNode->FindPin(UEdGraphSchema_K2::PN_Then); + UEdGraphPin* TypeExec = TypeNode->FindPin(UEdGraphSchema_K2::PN_Execute); + UEdGraphPin* TypeThen = TypeNode->FindPin(UEdGraphSchema_K2::PN_Then); + UEdGraphPin* ScaleExec = ScaleNode->FindPin(UEdGraphSchema_K2::PN_Execute); + if (!Response || !TypeChannel || !PawnThen || !TypeExec || !TypeThen || !ScaleExec) + { + UE_LOG(LogLPFixPawn, Error, TEXT("broches introuvables : Blueprint non sauvegarde")); + return 1; + } + + UE_LOG(LogLPFixPawn, Warning, TEXT("Pawn %s, type %s"), *Response->DefaultValue, *TypeChannel->DefaultValue); + const bool bPawnDone = Response->DefaultValue == TEXT("ECR_Block"); + const bool bTypeDone = TypeChannel->DefaultValue == TEXT("ECC_WorldDynamic") && TypeExec->LinkedTo.Contains(PawnThen); + if (!bPawnDone && Response->DefaultValue != TEXT("ECR_Ignore")) + { + UE_LOG(LogLPFixPawn, Error, TEXT("reponse Pawn inattendue : Blueprint non sauvegarde")); + return 1; + } + + int32 Walls = 0; + const TCHAR* WallNames[] = { TEXT("Wall1"), TEXT("Wall2"), TEXT("Wall3"), TEXT("Wall4") }; + if (!bDryRun) + { + if (!bPawnDone) + { + Response->DefaultValue = TEXT("ECR_Block"); + } + if (!bTypeDone) + { + TypeChannel->DefaultValue = TEXT("ECC_WorldDynamic"); + PawnThen->BreakLinkTo(ScaleExec); + TypeExec->BreakAllPinLinks(); + TypeThen->BreakAllPinLinks(); + PawnThen->MakeLinkTo(TypeExec); + TypeThen->MakeLinkTo(ScaleExec); + } + if (Room->SimpleConstructionScript) + { + for (const TCHAR* Name : WallNames) + { + USCS_Node* Node = Room->SimpleConstructionScript->FindSCSNode(FName(Name)); + UPrimitiveComponent* Wall = Node ? Cast(Node->ComponentTemplate) : nullptr; + if (!Wall) + { + UE_LOG(LogLPFixPawn, Error, TEXT("mur %s introuvable"), Name); + return 1; + } + Wall->Modify(); + Wall->SetCollisionEnabled(ECollisionEnabled::QueryAndPhysics); + Wall->SetCollisionObjectType(ECC_WorldDynamic); + Wall->SetCollisionResponseToChannel(ECC_Pawn, ECR_Block); + ++Walls; + } + } + } + + UE_LOG(LogLPFixPawn, Warning, TEXT("=== addObject Pawn Block, type WorldDynamic, %d murs%s ==="), + bDryRun ? 4 : Walls, bDryRun ? TEXT(" [DRY RUN]") : TEXT("")); + if (bDryRun) + { + return 0; + } + + FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Room); + FKismetEditorUtilities::CompileBlueprint(Room); + UPackage* Package = Room->GetOutermost(); + FString Filename; + if (!FPackageName::TryConvertLongPackageNameToFilename(Package->GetName(), Filename, FPackageName::GetAssetPackageExtension())) + { + UE_LOG(LogLPFixPawn, Error, TEXT("chemin introuvable")); + 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(LogLPFixPawn, Error, TEXT("sauvegarde echouee")); + return 1; + } + UE_LOG(LogLPFixPawn, Warning, TEXT("Room sauvegarde : meubles et murs en WorldDynamic, Pawn Block")); + return 0; +} diff --git a/Source/LifePlayEditorTools/LPFixPawnResponseCommandlet.h b/Source/LifePlayEditorTools/LPFixPawnResponseCommandlet.h new file mode 100644 index 0000000..cbc5782 --- /dev/null +++ b/Source/LifePlayEditorTools/LPFixPawnResponseCommandlet.h @@ -0,0 +1,22 @@ +// Room::addObject : Pawn Block, et le type d'objet du meuble devient WorldDynamic +// (la capsule Person ignore WorldStatic, donc Pawn Block seul ne l'arrete pas). +// Les quatre murs du Blueprint passent aussi en WorldDynamic et bloquent Pawn. +// Tout ecart : Blueprint non sauvegarde. +// +// Usage : +// UnrealEditor-Cmd.exe -run=LPFixPawnResponse [-DryRun] +#pragma once + +#include "CoreMinimal.h" +#include "Commandlets/Commandlet.h" +#include "LPFixPawnResponseCommandlet.generated.h" + +UCLASS() +class ULPFixPawnResponseCommandlet : public UCommandlet +{ + GENERATED_BODY() + +public: + ULPFixPawnResponseCommandlet(); + virtual int32 Main(const FString& Params) override; +};