142 lines
4.4 KiB
C++
142 lines
4.4 KiB
C++
#pragma once
|
|
|
|
#include "CoreMinimal.h"
|
|
#include "MCPHandler.h"
|
|
#include "MCPAssetFinder.h"
|
|
#include "MCPUtils.h"
|
|
#include "Engine/Blueprint.h"
|
|
#include "Engine/SimpleConstructionScript.h"
|
|
#include "Engine/SCS_Node.h"
|
|
#include "Components/ActorComponent.h"
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#include "Blueprint_AddComponent.generated.h"
|
|
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
// ---------------------------------------------------------------------------
|
|
|
|
UCLASS()
|
|
class UMCP_Blueprint_AddComponent : public UObject, public IMCPHandler
|
|
{
|
|
GENERATED_BODY()
|
|
|
|
public:
|
|
UPROPERTY(meta=(Description="Blueprint name or package path"))
|
|
FString Blueprint;
|
|
|
|
UPROPERTY(meta=(Description="Component class name (e.g. StaticMeshComponent, SceneComponent)"))
|
|
FString ComponentClass;
|
|
|
|
UPROPERTY(meta=(Description="Component name for the new component"))
|
|
FString Component;
|
|
|
|
UPROPERTY(meta=(Optional, Description="Name of the parent component to attach to"))
|
|
FString ParentComponent;
|
|
|
|
virtual FString GetDescription() const override
|
|
{
|
|
return TEXT("Add a component to a Blueprint's SimpleConstructionScript. "
|
|
"Optionally attach it to an existing parent component.");
|
|
}
|
|
|
|
virtual void Handle(const FJsonObject* Json, FStringBuilderBase& Result) override
|
|
{
|
|
MCPAssets<UBlueprint> Assets;
|
|
if (!Assets.Exact(Blueprint).Errors(Result).ENone().ETwo().Load()) return;
|
|
UBlueprint* BP = Assets.Object();
|
|
|
|
USimpleConstructionScript* SCS = BP->SimpleConstructionScript;
|
|
if (!SCS)
|
|
{
|
|
MCPErrorCallback(Result).SetError(FString::Printf(
|
|
TEXT("Blueprint '%s' does not have a SimpleConstructionScript (not an Actor Blueprint)"),
|
|
*MCPUtils::FormatName(BP)));
|
|
return;
|
|
}
|
|
|
|
// Check for duplicate component names
|
|
const TArray<USCS_Node*>& ExistingNodes = SCS->GetAllNodes();
|
|
for (USCS_Node* Existing : ExistingNodes)
|
|
{
|
|
if (Existing && Existing->ComponentTemplate &&
|
|
MCPUtils::Identifies(Component, Existing->ComponentTemplate))
|
|
{
|
|
MCPErrorCallback(Result).SetError(FString::Printf(
|
|
TEXT("A component named '%s' already exists in Blueprint '%s'"),
|
|
*Component, *MCPUtils::FormatName(BP)));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Resolve the component class by name
|
|
UClass* ComponentClassObj = MCPUtils::FindClassByName(ComponentClass);
|
|
if (!ComponentClassObj || !ComponentClassObj->IsChildOf(UActorComponent::StaticClass()))
|
|
{
|
|
MCPErrorCallback(Result).SetError(FString::Printf(
|
|
TEXT("Component class '%s' not found or is not a subclass of UActorComponent. "
|
|
"Common classes: StaticMeshComponent, SkeletalMeshComponent, AudioComponent, "
|
|
"SceneComponent, BoxCollisionComponent, SphereCollisionComponent, CapsuleComponent, "
|
|
"ArrowComponent, ChildActorComponent, SpotLightComponent, PointLightComponent, "
|
|
"WidgetComponent, BillboardComponent"),
|
|
*ComponentClass));
|
|
return;
|
|
}
|
|
|
|
// If parent component specified, find its SCS node
|
|
USCS_Node* ParentSCSNode = nullptr;
|
|
if (!ParentComponent.IsEmpty())
|
|
{
|
|
for (USCS_Node* Node : ExistingNodes)
|
|
{
|
|
if (Node && Node->ComponentTemplate &&
|
|
MCPUtils::Identifies(ParentComponent, Node->ComponentTemplate))
|
|
{
|
|
ParentSCSNode = Node;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!ParentSCSNode)
|
|
{
|
|
MCPErrorCallback(Result).SetError(FString::Printf(
|
|
TEXT("Parent component '%s' not found in Blueprint '%s'"),
|
|
*ParentComponent, *MCPUtils::FormatName(BP)));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// Create the SCS node
|
|
USCS_Node* NewNode = SCS->CreateNode(ComponentClassObj, FName(*Component));
|
|
if (!NewNode)
|
|
{
|
|
MCPErrorCallback(Result).SetError(FString::Printf(
|
|
TEXT("Failed to create SCS node for component '%s' with class '%s'"),
|
|
*Component, *MCPUtils::FormatName(ComponentClassObj)));
|
|
return;
|
|
}
|
|
|
|
// Add to the hierarchy
|
|
if (ParentSCSNode)
|
|
{
|
|
ParentSCSNode->AddChildNode(NewNode);
|
|
}
|
|
else
|
|
{
|
|
SCS->AddNode(NewNode);
|
|
}
|
|
|
|
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(BP);
|
|
bool bSaved = MCPUtils::SaveBlueprintPackage(BP);
|
|
|
|
Result.Appendf(TEXT("Added component %s (%s)"),
|
|
*MCPUtils::FormatName(NewNode->ComponentTemplate),
|
|
*MCPUtils::FormatName(ComponentClassObj));
|
|
if (ParentSCSNode)
|
|
{
|
|
Result.Appendf(TEXT(" under %s"), *MCPUtils::FormatName(ParentSCSNode->ComponentTemplate));
|
|
}
|
|
Result.Appendf(TEXT("\nSaved: %s\n"), bSaved ? TEXT("true") : TEXT("false"));
|
|
}
|
|
};
|