Files
integration/Plugins/UEWingman/Source/UEWingman/Private/WingActorComponent.cpp
2026-03-30 20:10:23 -04:00

302 lines
9.5 KiB
C++

#include "WingActorComponent.h"
#include "WingServer.h"
#include "WingTypes.h"
#include "WingUtils.h"
#include "Engine/Blueprint.h"
#include "Engine/SCS_Node.h"
#include "Engine/SimpleConstructionScript.h"
#include "Components/ActorComponent.h"
#include "Components/SceneComponent.h"
#include "GameFramework/Actor.h"
#include "Kismet2/BlueprintEditorUtils.h"
#include "Engine/InheritableComponentHandler.h"
UActorComponent *UWingComponentReference::FindComponentInCDO(UClass *Class, FName Name)
{
if (!Class) return nullptr;
AActor* CDO = Cast<AActor>(Class->GetDefaultObject());
if (!CDO) return nullptr;
return FindObjectFast<UActorComponent>(CDO, Name);
}
USCS_Node* UWingComponentReference::FindSCSNodeByName(UBlueprint *BP, FName Name)
{
for (UBlueprint *WalkBP : WingUtils::GetAncestorBlueprints(BP))
{
if (!WalkBP->SimpleConstructionScript) continue;
USCS_Node* Node = WalkBP->SimpleConstructionScript->FindSCSNode(Name);
if (Node != nullptr) return Node;
}
return nullptr;
}
UWingComponentReference::FoundComponent UWingComponentReference::FindComponent(UBlueprint *BP, FName Name)
{
UClass* NativeClass = FBlueprintEditorUtils::FindFirstNativeClass(BP->ParentClass);
FoundComponent Result;
Result.Name = Name;
Result.SCS = FindSCSNodeByName(BP, Name);
Result.Native = FindComponentInCDO(NativeClass, Name);
return Result;
}
bool UWingComponentReference::CheckExists(UWingComponentReference::FoundComponent FC)
{
if ((FC.SCS == nullptr) && (FC.Native == nullptr))
{
UWingServer::Printf(TEXT("Cannot find component: %s\n"), *WingUtils::ExternalizeID(FC.Name));
return false;
}
return true;
}
bool UWingComponentReference::CheckValidParent(UWingComponentReference::FoundComponent FC)
{
if (FC.SCS && FC.Native)
{
UWingServer::Printf(TEXT("Weirdness, two components named %s\n"),
*WingUtils::ExternalizeID(FC.Name));
return false;
}
if ((FC.SCS == nullptr) && (FC.Native == nullptr))
{
UWingServer::Printf(TEXT("No such parent component: %s\n"),
*WingUtils::ExternalizeID(FC.Name));
return false;
}
if (FC.Native && (!Cast<USceneComponent>(FC.Native)))
{
UWingServer::Printf(TEXT("Not a SceneComponent, so cannot be parent: %s\n"),
*WingUtils::ExternalizeID(FC.Name));
return false;
}
return true;
}
bool UWingComponentReference::CheckNoSuchComponent(FoundComponent FC)
{
if (FC.SCS || FC.Native)
{
UWingServer::Printf(TEXT("A component named %s already exists"),
*WingUtils::ExternalizeID(FC.Name));
return false;
}
return true;
}
bool UWingComponentReference::CheckNotNative(FoundComponent FC, const TCHAR *Action)
{
if (FC.Native != nullptr)
{
UWingServer::Printf(TEXT("Component %s is native, cannot %s native components"),
*WingUtils::ExternalizeID(FC.Name), Action);
return false;
}
return true;
}
bool UWingComponentReference::CheckOwnedByBlueprint(FoundComponent FC, UBlueprint *BP)
{
if ((FC.SCS == nullptr) || (FC.SCS->GetSCS() != BP->SimpleConstructionScript))
{
UWingServer::Printf(TEXT("Component %s belongs to blueprint %s, edit that blueprint instead"),
*WingUtils::ExternalizeID(FC.Name), *WingUtils::FormatName(FC.SCS->GetSCS()->GetBlueprint()));
return false;
}
return true;
}
bool UWingComponentReference::CheckValidComponentClass(UClass *Class)
{
if (!Class->IsChildOf(UActorComponent::StaticClass()))
{
UWingServer::Printf(TEXT("Class does not derive from ActorComponent: %s\n"),
*WingUtils::FormatName(Class));
return false;
}
return true;
}
void UWingComponentReference::AddChildNode(UBlueprint *BP, USCS_Node *NewNode, FoundComponent Parent)
{
if (Parent.SCS)
{
if (Parent.SCS->GetSCS() == BP->SimpleConstructionScript)
{
Parent.SCS->AddChildNode(NewNode);
}
else
{
NewNode->SetParent(Parent.SCS);
BP->SimpleConstructionScript->AddNode(NewNode);
}
}
else
{
NewNode->SetParent(Cast<USceneComponent>(Parent.Native));
BP->SimpleConstructionScript->AddNode(NewNode);
}
}
bool UWingComponentReference::AddComponent(UBlueprint *BP, UClass *Class, UWingComponentReference *Parent, FName Name)
{
TSet<FName> Names;
FBlueprintEditorUtils::GetClassVariableList(BP, Names);
if (Names.Contains(Name))
{
UWingServer::Printf(TEXT("There is already a variable or component named %s in %s.\n"),
*WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class));
return false;
}
FoundComponent ExistingComponent = FindComponent(BP, Name);
if (!CheckNoSuchComponent(ExistingComponent)) return false;
FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName);
if (!CheckValidParent(ParentComponent)) return false;
if (!CheckValidComponentClass(Class)) return false;
USCS_Node *NewNode = BP->SimpleConstructionScript->CreateNode(Class, Name);
if (NewNode == nullptr)
{
UWingServer::Printf(TEXT("Could not create new component %s of class %s, unknown reason.\n"),
*WingUtils::ExternalizeID(Name), *WingUtils::FormatName(Class));
return false;
}
AddChildNode(BP, NewNode, ParentComponent);
return true;
}
bool UWingComponentReference::ReparentComponent(UWingComponentReference *Parent)
{
FoundComponent ParentComponent = FindComponent(BP, Parent->VariableName);
if (!CheckValidParent(ParentComponent)) return false;
FoundComponent ThisComponent = FindComponent(BP, VariableName);
if (!CheckExists(ThisComponent)) return false;
if (!CheckNotNative(ThisComponent, TEXT("reparent"))) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP)) return false;
if (ParentComponent.SCS && ParentComponent.SCS->IsChildOf(ThisComponent.SCS))
{
UWingServer::Printf(TEXT("Cannot parent a component to itself or its own child."));
return false;
}
BP->SimpleConstructionScript->RemoveNode(ThisComponent.SCS);
AddChildNode(BP, ThisComponent.SCS, ParentComponent);
return true;
}
bool UWingComponentReference::DeleteComponent()
{
FoundComponent ThisComponent = FindComponent(BP, VariableName);
if (!CheckExists(ThisComponent)) return false;
if (!CheckNotNative(ThisComponent, TEXT("delete"))) return false;
if (!CheckOwnedByBlueprint(ThisComponent, BP)) return false;
BP->SimpleConstructionScript->RemoveNodeAndPromoteChildren(ThisComponent.SCS);
return true;
}
TMap<FName, FName> UWingComponentReference::CalculateParentNames(USimpleConstructionScript *Script)
{
TMap<FName, FName> ParentNames;
for (USCS_Node* Node : Script->GetAllNodes())
{
ParentNames.Add(Node->GetVariableName(), Node->ParentComponentOrVariableName);
}
for (USCS_Node* Parent : Script->GetAllNodes())
{
for (USCS_Node *Child : Parent->GetChildNodes())
{
ParentNames[Child->GetVariableName()] = Parent->GetVariableName();
}
}
return ParentNames;
}
UActorComponent* UWingComponentReference::GetImmutableTemplate() const
{
if (Native) return FindComponentInCDO(BP->GeneratedClass, VariableName);
USCS_Node *Node = FindSCSNodeByName(BP, VariableName);
if (Node == nullptr) return nullptr;
FComponentKey Key(Node);
for (UBlueprint *WalkBP : WingUtils::GetAncestorBlueprints(BP, false))
{
if (Node->GetSCS()->GetBlueprint() == WalkBP) return Node->ComponentTemplate;
UInheritableComponentHandler* ICH = WalkBP->GetInheritableComponentHandler(/*bCreateIfNecessary=*/ false);
if (!ICH) continue;
UActorComponent* Override = ICH->GetOverridenComponentTemplate(Key);
if (Override) return Override;
}
return nullptr;
}
UActorComponent* UWingComponentReference::GetMutableTemplate() const
{
if (Native) return FindComponentInCDO(BP->GeneratedClass, VariableName);
USCS_Node* Node = FindSCSNodeByName(BP, VariableName);
if (!Node) return nullptr;
if (Node->GetSCS()->GetBlueprint() == BP) return Node->ComponentTemplate;
FComponentKey Key(Node);
UInheritableComponentHandler* ICH = BP->GetInheritableComponentHandler(/*bCreateIfNecessary=*/ true);
if (!ICH) return nullptr;
UActorComponent* Override = ICH->GetOverridenComponentTemplate(Key);
if (!Override) Override = ICH->CreateOverridenComponentTemplate(Key);
return Override;
}
TArray<UWingComponentReference*> UWingComponentReference::GetAll(UBlueprint* BP)
{
TArray<UWingComponentReference*> Result;
if (!BP) return Result;
// Find the native ancestor class
UClass* NativeClass = FBlueprintEditorUtils::FindFirstNativeClass(BP->ParentClass);
// Native components from the native ancestor's CDO
if (NativeClass)
{
if (AActor* CDO = Cast<AActor>(NativeClass->GetDefaultObject()))
{
TArray<UActorComponent*> NativeComponents;
CDO->GetComponents(NativeComponents);
for (UActorComponent* Comp : NativeComponents)
{
UWingComponentReference* Ref = NewObject<UWingComponentReference>();
Ref->BP = BP;
Ref->VariableName = Comp->GetFName();
Ref->TypeName = UWingTypes::TypeToText(Comp->GetClass());
if (USceneComponent* Scene = Cast<USceneComponent>(Comp))
if (USceneComponent* AttachParent = Scene->GetAttachParent())
Ref->ParentName = WingUtils::ExternalizeID(AttachParent->GetFName());
Ref->Native = true;
Ref->Inherited = true;
Result.Add(Ref);
}
}
}
// Visit all SCS nodes, oldest first.
for (UBlueprint *WalkBP : WingUtils::GetAncestorBlueprints(BP, true))
{
if (WalkBP->SimpleConstructionScript == nullptr) continue;
TMap<FName, FName> ParentNames = CalculateParentNames(WalkBP->SimpleConstructionScript);
for (USCS_Node* Node : WalkBP->SimpleConstructionScript->GetAllNodes())
{
UWingComponentReference* Ref = NewObject<UWingComponentReference>();
Ref->BP = BP;
Ref->VariableName = Node->GetVariableName();
Ref->TypeName = UWingTypes::TypeToText(Node->ComponentClass);
Ref->ParentName = WingUtils::ExternalizeID(ParentNames[Node->GetVariableName()]);
Ref->Native = false;
Ref->Inherited = (WalkBP != BP);
Result.Add(Ref);
}
}
return Result;
}