// Copyright Epic Games, Inc. All Rights Reserved. #include "K2Node_GetInputActionValue.h" #include "AssetRegistry/AssetRegistryModule.h" #include "InputAction.h" #include "Kismet2/BlueprintEditorUtils.h" #include "KismetCompiler.h" #include "Editor.h" #include "BlueprintNodeSpawner.h" #include "EditorCategoryUtils.h" #include "BlueprintActionDatabaseRegistrar.h" #include "K2Node_Self.h" #include "K2Node_InputActionValueAccessor.h" #include "Misc/PackageName.h" #include "Subsystems/AssetEditorSubsystem.h" #include "Modules/ModuleManager.h" #include UE_INLINE_GENERATED_CPP_BY_NAME(K2Node_GetInputActionValue) #define LOCTEXT_NAMESPACE "K2Node_GetInputActionValue" UK2Node_GetInputActionValue::UK2Node_GetInputActionValue(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) { } struct FValueTypeData { FValueTypeData(FName InCategory, FName InSubCategory = NAME_None, UScriptStruct* InSubCategoryObject = nullptr) : Category(InCategory) , SubCategory(InSubCategory) , SubCategoryObject(InSubCategoryObject) { } FName Category; FName SubCategory; UScriptStruct* SubCategoryObject; }; const TMap& GetValueLookups() { static const TMap ValueLookups = { { EInputActionValueType::Boolean, FValueTypeData(UEdGraphSchema_K2::PC_Boolean) }, { EInputActionValueType::Axis1D, FValueTypeData(UEdGraphSchema_K2::PC_Real, UEdGraphSchema_K2::PC_Double) }, { EInputActionValueType::Axis2D, FValueTypeData(UEdGraphSchema_K2::PC_Struct, NAME_None, TBaseStructure::Get()) }, { EInputActionValueType::Axis3D, FValueTypeData(UEdGraphSchema_K2::PC_Struct, NAME_None, TBaseStructure::Get()) }, }; return ValueLookups; } FName UK2Node_GetInputActionValue::GetValueCategory(const UInputAction* InputAction) { EInputActionValueType Type = InputAction ? InputAction->ValueType : EInputActionValueType::Boolean; return GetValueLookups()[Type].Category; } FName UK2Node_GetInputActionValue::GetValueSubCategory(const UInputAction* InputAction) { EInputActionValueType Type = InputAction ? InputAction->ValueType : EInputActionValueType::Boolean; return GetValueLookups()[Type].SubCategory; } UScriptStruct* UK2Node_GetInputActionValue::GetValueSubCategoryObject(const UInputAction* InputAction) { EInputActionValueType Type = InputAction ? InputAction->ValueType : EInputActionValueType::Boolean; return GetValueLookups()[Type].SubCategoryObject; } void UK2Node_GetInputActionValue::AllocateDefaultPins() { PreloadObject((UObject*)InputAction); Super::AllocateDefaultPins(); // Dynamically typed output FName SubCategory = UK2Node_GetInputActionValue::GetValueSubCategory(InputAction); if (SubCategory != NAME_None) { CreatePin(EGPD_Output, UK2Node_GetInputActionValue::GetValueCategory(InputAction), SubCategory, UEdGraphSchema_K2::PN_ReturnValue); } else { CreatePin(EGPD_Output, UK2Node_GetInputActionValue::GetValueCategory(InputAction), UK2Node_GetInputActionValue::GetValueSubCategoryObject(InputAction), UEdGraphSchema_K2::PN_ReturnValue); } } void UK2Node_GetInputActionValue::Initialize(const UInputAction* Action) { InputAction = Action; } FName UK2Node_GetInputActionValue::GetActionName() const { return InputAction ? InputAction->GetFName() : FName(); } FText UK2Node_GetInputActionValue::GetNodeTitle(ENodeTitleType::Type TitleType) const { if (TitleType == ENodeTitleType::MenuTitle) { return FText::FromName(GetActionName()); } else if (CachedNodeTitle.IsOutOfDate(this)) { FFormatNamedArguments Args; Args.Add(TEXT("InputActionName"), FText::FromName(GetActionName())); FText LocFormat = LOCTEXT("GetInputAction_Name", "Get {InputActionName}"); // FText::Format() is slow, so we cache this to save on performance CachedNodeTitle.SetCachedText(FText::Format(LocFormat, Args), this); } return CachedNodeTitle; } void UK2Node_GetInputActionValue::ExpandNode(class FKismetCompilerContext& CompilerContext, UEdGraph* SourceGraph) { Super::ExpandNode(CompilerContext, SourceGraph); const UEdGraphSchema_K2* Schema = CompilerContext.GetSchema(); UEdGraphPin* ReturnValue = FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue); if (ReturnValue->LinkedTo.Num() == 0) { return; } // Accessor does the call to UEnhancedInputLibrary::GetBoundActionValue, passing in self from SelfNode UK2Node_InputActionValueAccessor* AccessorNode = CompilerContext.SpawnIntermediateNode(this, SourceGraph); AccessorNode->Initialize(InputAction); AccessorNode->AllocateDefaultPins(); // Create a self node, and wire it to "Actor" pin on the InputActionValueAccessor UK2Node_Self* SelfNode = CompilerContext.SpawnIntermediateNode(this, SourceGraph); SelfNode->AllocateDefaultPins(); Schema->TryCreateConnection(SelfNode->FindPinChecked(UEdGraphSchema_K2::PN_Self), AccessorNode->FindPinChecked(TEXT("Actor"))); // And finally hook the InputActionValueAccessor's return value up to the GetInputActionValue return pin's connected nodes, automatically inserting appropriate conversion nodes. TArray LinkedTo(ReturnValue->LinkedTo); for (UEdGraphPin* EachLink : LinkedTo) { Schema->TryCreateConnection(AccessorNode->FindPinChecked(UEdGraphSchema_K2::PN_ReturnValue), EachLink); } } FText UK2Node_GetInputActionValue::GetTooltipText() const { if (CachedTooltip.IsOutOfDate(this)) { // FText::Format() is slow, so we cache this to save on performance FString ActionPath = InputAction ? InputAction->GetFullName() : TEXT(""); CachedTooltip.SetCachedText( FText::Format(LOCTEXT("GetInputAction_Tooltip", "Returns the current value of {0}. If input is disabled for the actor the value will be 0. \n\nNote: If the value is being altered by an Input Trigger or Input Modifier (such as a Released trigger) then the value may be 0."), FText::FromString(ActionPath)), this); } return CachedTooltip; } bool UK2Node_GetInputActionValue::IsCompatibleWithGraph(UEdGraph const* Graph) const { UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(Graph); UEdGraphSchema_K2 const* K2Schema = Cast(Graph->GetSchema()); bool const bIsConstructionScript = (K2Schema != nullptr) ? K2Schema->IsConstructionScript(Graph) : false; return (Blueprint != nullptr) && Blueprint->SupportsInputEvents() && !bIsConstructionScript && Super::IsCompatibleWithGraph(Graph); } UObject* UK2Node_GetInputActionValue::GetJumpTargetForDoubleClick() const { return const_cast(Cast(InputAction)); } void UK2Node_GetInputActionValue::JumpToDefinition() const { GEditor->GetEditorSubsystem()->OpenEditorForAsset(GetJumpTargetForDoubleClick()); } void UK2Node_GetInputActionValue::ValidateNodeDuringCompilation(class FCompilerResultsLog& MessageLog) const { Super::ValidateNodeDuringCompilation(MessageLog); if (!InputAction) { MessageLog.Error(*LOCTEXT("EnhancedInputAction_ErrorFmt", "EnhancedInputActionEvent references invalid 'null' action for @@").ToString(), this); } } void UK2Node_GetInputActionValue::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { auto CustomizeInputNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, TWeakObjectPtr Action) { UK2Node_GetInputActionValue* InputNode = CastChecked(NewNode); InputNode->Initialize(Action.Get()); }; // Do a first time registration using the node's class to pull in all existing actions if (ActionRegistrar.IsOpenForRegistration(GetClass())) { IAssetRegistry& AssetRegistry = FModuleManager::LoadModuleChecked("AssetRegistry").Get(); static bool bRegisterOnce = true; if (bRegisterOnce) { bRegisterOnce = false; if (AssetRegistry.IsLoadingAssets()) { AssetRegistry.OnFilesLoaded().AddLambda([]() { FBlueprintActionDatabase::Get().RefreshClassActions(StaticClass()); }); } } TArray ActionAssets; AssetRegistry.GetAssetsByClass(UInputAction::StaticClass()->GetClassPathName(), ActionAssets); for (const FAssetData& ActionAsset : ActionAssets) { UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); check(NodeSpawner != nullptr); if (FPackageName::GetPackageMountPoint(ActionAsset.PackageName.ToString()) != NAME_None) { if (const UInputAction* Action = Cast(ActionAsset.GetAsset())) { NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeInputNodeLambda, TWeakObjectPtr(Action)); ActionRegistrar.AddBlueprintAction(Action, NodeSpawner); } } } } else if (const UInputAction* Action = Cast(ActionRegistrar.GetActionKeyFilter())) { // If this is a specific UInputAction asset update it. UBlueprintNodeSpawner* NodeSpawner = UBlueprintNodeSpawner::Create(GetClass()); check(NodeSpawner != nullptr); NodeSpawner->CustomizeNodeDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeInputNodeLambda, TWeakObjectPtr(Action)); ActionRegistrar.AddBlueprintAction(Action, NodeSpawner); } } FText UK2Node_GetInputActionValue::GetMenuCategory() const { static FNodeTextCache CachedCategory; if (CachedCategory.IsOutOfDate(this)) { // FText::Format() is slow, so we cache this to save on performance CachedCategory.SetCachedText(FEditorCategoryUtils::BuildCategoryString(FCommonEditorCategory::Input, LOCTEXT("ActionMenuCategory", "Enhanced Action Values")), this); } return CachedCategory; } FBlueprintNodeSignature UK2Node_GetInputActionValue::GetSignature() const { FBlueprintNodeSignature NodeSignature = Super::GetSignature(); NodeSignature.AddKeyValue(GetActionName().ToString()); return NodeSignature; } #undef LOCTEXT_NAMESPACE