PawnGlobal : Room::addObject bloque le canal Pawn, commandlet LPFixPawnResponse

Phase PawnGlobal (CHG-144).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
edujiro5972 2026-10-07 15:49:06 +02:00
parent 81f75d4e95
commit 3e773bf28b
3 changed files with 187 additions and 0 deletions

Binary file not shown.

View file

@ -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<UBlueprint>(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<UK2Node_CallFunction>(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<UPrimitiveComponent>(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;
}

View file

@ -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 <projet> -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;
};