302 lines
9.5 KiB
C++
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;
|
|
}
|
|
|