void FBlueprintNativeCodeGenModule::GenerateStubs()
{
	TSet<TAssetPtr<UBlueprint>> AlreadyGenerated;
	while (AlreadyGenerated.Num() < StubsRequiredByGeneratedCode.Num())
	{
		const int32 OldGeneratedNum = AlreadyGenerated.Num();
		for (TAssetPtr<UBlueprint>& BPPtr : StubsRequiredByGeneratedCode)
		{
			bool bAlreadyGenerated = false;
			AlreadyGenerated.Add(BPPtr, &bAlreadyGenerated);
			if (bAlreadyGenerated)
			{
				continue;
			}

			ensureMsgf(AllPotentialStubs.Contains(BPPtr), TEXT("A required blueprint doesn't generate stub: %s"), *BPPtr.ToString());
			for (auto& PlatformName : TargetPlatformNames)
			{
				GenerateSingleStub(BPPtr.LoadSynchronous(), *PlatformName);
			}
		}

		if (!ensure(OldGeneratedNum != AlreadyGenerated.Num()))
		{
			break;
		}
	}

	UE_LOG(LogBlueprintCodeGen, Log, TEXT("GenerateStubs - all unconverted bp: %d, generated wrapers: %d"), AllPotentialStubs.Num(), StubsRequiredByGeneratedCode.Num());
}
UEnum* GetEnumForByteTrack(TSharedPtr<ISequencer> Sequencer, const FGuid& OwnerObjectHandle, FName PropertyName, UMovieSceneByteTrack* ByteTrack)
{
	
	UObject* RuntimeObject = Sequencer->GetFocusedMovieSceneSequence()->FindObject(OwnerObjectHandle);

	TSet<UEnum*> PropertyEnums;
	if (RuntimeObject != nullptr)
	{
		UProperty* Property = RuntimeObject->GetClass()->FindPropertyByName(PropertyName);
		if (Property != nullptr)
		{
			UByteProperty* ByteProperty = Cast<UByteProperty>(Property);
			if (ByteProperty != nullptr && ByteProperty->Enum != nullptr)
			{
				PropertyEnums.Add(ByteProperty->Enum);
			}
		}
	}

	UEnum* TrackEnum;
	if (PropertyEnums.Num() == 1)
	{
		TrackEnum = PropertyEnums.Array()[0];
	}
	else
	{
		TrackEnum = nullptr;
	}
	return TrackEnum;
}
void UAnimationGraphSchema::HandleGraphBeingDeleted(UEdGraph& GraphBeingRemoved) const
{
	if (UBlueprint* Blueprint = FBlueprintEditorUtils::FindBlueprintForGraph(&GraphBeingRemoved))
	{
		// Look for state nodes that reference this graph
		TArray<UAnimStateNode*> StateNodes;
		FBlueprintEditorUtils::GetAllNodesOfClass<UAnimStateNode>(Blueprint, /*out*/ StateNodes);

		TSet<UAnimStateNode*> NodesToDelete;
		for (int32 i = 0; i < StateNodes.Num(); ++i)
		{
			UAnimStateNode* StateNode = StateNodes[i];
			if (StateNode->BoundGraph == &GraphBeingRemoved)
			{
				NodesToDelete.Add(StateNode);
			}
		}

		// Delete the node that owns us
		ensure(NodesToDelete.Num() <= 1);
		for (TSet<UAnimStateNode*>::TIterator It(NodesToDelete); It; ++It)
		{
			UAnimStateNode* NodeToDelete = *It;

			// Prevent re-entrancy here
			NodeToDelete->BoundGraph = NULL;

			NodeToDelete->Modify();
			NodeToDelete->DestroyNode();
		}
	}
}
bool UParticleSystemAuditCommandlet::DumpSimpleSet(TSet<FString>& InSet, const TCHAR* InShortFilename, const TCHAR* InObjectClassName)
{
	if (InSet.Num() > 0)
	{
		check(InShortFilename != NULL);
		check(InObjectClassName != NULL);

		FArchive* OutputStream = GetOutputFile(InShortFilename);
		if (OutputStream != NULL)
		{
			UE_LOG(LogParticleSystemAuditCommandlet, Log, TEXT("Dumping '%s' results..."), InShortFilename);
			OutputStream->Logf(TEXT("%s,..."), InObjectClassName);
			for (TSet<FString>::TIterator DumpIt(InSet); DumpIt; ++DumpIt)
			{
				FString ObjName = *DumpIt;
				OutputStream->Logf(TEXT("%s"), *ObjName);
			}

			OutputStream->Close();
			delete OutputStream;
		}
		else
		{
			return false;
		}
	}
	return true;
}
Exemplo n.º 5
0
void SWidget::FindChildGeometries_Helper( const FGeometry& MyGeometry, const TSet< TSharedRef<SWidget> >& WidgetsToFind, TMap<TSharedRef<SWidget>, FArrangedWidget>& OutResult ) const
{
	// Perform a breadth first search!

	FArrangedChildren ArrangedChildren(EVisibility::Visible);
	this->ArrangeChildren( MyGeometry, ArrangedChildren );
	const int32 NumChildren = ArrangedChildren.Num();

	// See if we found any of the widgets on this level.
	for(int32 ChildIndex=0; ChildIndex < NumChildren; ++ChildIndex )
	{
		const FArrangedWidget& CurChild = ArrangedChildren[ ChildIndex ];
		
		if ( WidgetsToFind.Contains(CurChild.Widget) )
		{
			// We found one of the widgets for which we need geometry!
			OutResult.Add( CurChild.Widget, CurChild );
		}
	}

	// If we have not found all the widgets that we were looking for, descend.
	if ( OutResult.Num() != WidgetsToFind.Num() )
	{
		// Look for widgets among the children.
		for( int32 ChildIndex=0; ChildIndex < NumChildren; ++ChildIndex )
		{
			const FArrangedWidget& CurChild = ArrangedChildren[ ChildIndex ];
			CurChild.Widget->FindChildGeometries_Helper( CurChild.Geometry, WidgetsToFind, OutResult );
		}	
	}	
}
Exemplo n.º 6
0
void FSoundCueEditor::OnSelectedNodesChanged(const TSet<class UObject*>& NewSelection)
{
	TArray<UObject*> Selection;

	if(NewSelection.Num())
	{
		for(TSet<class UObject*>::TConstIterator SetIt(NewSelection);SetIt;++SetIt)
		{
			if (Cast<USoundCueGraphNode_Root>(*SetIt))
			{
				Selection.Add(GetSoundCue());
			}
			else if (USoundCueGraphNode* GraphNode = Cast<USoundCueGraphNode>(*SetIt))
			{
				Selection.Add(GraphNode->SoundNode);
			}
			else
			{
				Selection.Add(*SetIt);
			}
		}
		//Selection = NewSelection.Array();
	}
	else
	{
		Selection.Add(GetSoundCue());
	}

	SetSelection(Selection);
}
Exemplo n.º 7
0
void FStatsWriteFile::Finalize()
{
	FArchive& Ar = *File;

	// Write dummy compression size, so we can detect the end of the file.
	FCompressedStatsData::WriteEndOfCompressedData( Ar );

	// Real header, written at start of the file, but written out right before we close the file.

	// Write out frame table and update header with offset and count.
	Header.FrameTableOffset = Ar.Tell();
	Ar << FramesInfo;

	const FStatsThreadState& Stats = FStatsThreadState::GetLocalState();

	// Add FNames from the stats metadata.
	for( const auto& It : Stats.ShortNameToLongName )
	{
		const FStatMessage& StatMessage = It.Value;
		FNamesSent.Add( StatMessage.NameAndInfo.GetRawName().GetComparisonIndex() );
	}

	// Create a copy of names.
	TSet<int32> FNamesToSent = FNamesSent;
	FNamesSent.Empty( FNamesSent.Num() );

	// Serialize FNames.
	Header.FNameTableOffset = Ar.Tell();
	Header.NumFNames = FNamesToSent.Num();
	for( const int32 It : FNamesToSent )
	{
		WriteFName( Ar, FStatNameAndInfo(FName(It, It, 0),false) );
	}

	// Serialize metadata messages.
	Header.MetadataMessagesOffset = Ar.Tell();
	Header.NumMetadataMessages = Stats.ShortNameToLongName.Num();
	WriteMetadata( Ar );

	// Verify data.
	TSet<int32> BMinA = FNamesSent.Difference( FNamesToSent );
	struct FLocal
	{
		static TArray<FName> GetFNameArray( const TSet<int32>& NameIndices )
		{
			TArray<FName> Result;
			for( const int32 NameIndex : NameIndices )
			{
				new(Result) FName( NameIndex, NameIndex, 0 );
			}
			return Result;
		}
	};
	auto BMinANames = FLocal::GetFNameArray( BMinA );

	// Seek to the position just after a magic value of the file and write out proper header.
	Ar.Seek( sizeof(uint32) );
	Ar << Header;
}
Exemplo n.º 8
0
	static void AppendCollectionToArray(const TSet<FName>& InObjectSet, TArray<FName>& OutObjectArray)
	{
		OutObjectArray.Reserve(OutObjectArray.Num() + InObjectSet.Num());
		for (const FName& ObjectName : InObjectSet)
		{
			OutObjectArray.Add(ObjectName);
		}
	}
void FWidgetBlueprintEditor::PasteWidgets()
{
	TSet<FWidgetReference> Widgets = GetSelectedWidgets();
	FWidgetReference Target = Widgets.Num() > 0 ? *Widgets.CreateIterator() : FWidgetReference();

	FWidgetBlueprintEditorUtils::PasteWidgets(SharedThis(this), GetWidgetBlueprintObj(), Target, PasteDropLocation);

	//TODO UMG - Select the newly selected pasted widgets.
}
bool FWidgetBlueprintEditor::CanPasteWidgets()
{
	TSet<FWidgetReference> Widgets = GetSelectedWidgets();
	if ( Widgets.Num() == 1 )
	{
		FWidgetReference Target = *Widgets.CreateIterator();
		const bool bIsPanel = Cast<UPanelWidget>(Target.GetTemplate()) != nullptr;
		return bIsPanel;
	}
	else if ( Widgets.Num() == 0 )
	{
		if ( GetWidgetBlueprintObj()->WidgetTree->RootWidget == nullptr )
		{
			return true;
		}
	}

	return false;
}
Exemplo n.º 11
0
void FRichCurve::ShiftCurve(float DeltaTime, TSet<FKeyHandle>& KeyHandles)
{
	for (auto It = KeyHandlesToIndices.CreateIterator(); It; ++It)
	{
		const FKeyHandle& KeyHandle = It.Key();
		if (KeyHandles.Num() != 0 && KeyHandles.Contains(KeyHandle))
		{
			SetKeyTime(KeyHandle, GetKeyTime(KeyHandle)+DeltaTime);
		}
	}
}
Exemplo n.º 12
0
void FRichCurve::ScaleCurve(float ScaleOrigin, float ScaleFactor, TSet<FKeyHandle>& KeyHandles)
{
	for (auto It = KeyHandlesToIndices.CreateIterator(); It; ++It)
	{
		const FKeyHandle& KeyHandle = It.Key();
		if (KeyHandles.Num() != 0 && KeyHandles.Contains(KeyHandle))
		{
			SetKeyTime(KeyHandle, (GetKeyTime(KeyHandle) - ScaleOrigin) * ScaleFactor + ScaleOrigin);
		}
	}
}
Exemplo n.º 13
0
void FGraphNodeClassHelper::UpdateAvailableBlueprintClasses()
{
	if (FModuleManager::Get().IsModuleLoaded(TEXT("AssetRegistry")))
	{
		FAssetRegistryModule& AssetRegistryModule = FModuleManager::GetModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
		const bool bSearchSubClasses = true;

		TArray<FName> ClassNames;
		TSet<FName> DerivedClassNames;

		for (TMap<UClass*, int32>::TIterator It(BlueprintClassCount); It; ++It)
		{
			ClassNames.Reset();
			ClassNames.Add(It.Key()->GetFName());

			DerivedClassNames.Empty(DerivedClassNames.Num());
			AssetRegistryModule.Get().GetDerivedClassNames(ClassNames, TSet<FName>(), DerivedClassNames);

			int32& Count = It.Value();
			Count = DerivedClassNames.Num();
		}
	}
}
Exemplo n.º 14
0
/*Function which retrieves an arranged list of assets with the same nature (coresponding tags: Item, Stackable, ItemType)
which are placed on top of one another in the world (eg: a stack of plates)
The list is used for picking up multiple items at once in the GrabWithTwoHands() method.
@param AActor* ContainedItem  -->  Actor contained within the stack needed
*/
TSet<AActor*> AMyCharacter::GetStack(AActor* ContainedItem)
{
	//Create an empty array to be populated with proper values
	TSet<AActor*> StackList;
	StackList.Empty();

	//Make sure that the function parameter is logicaly valid and if not return an empty stack and exit the function call
	if (!ContainedItem->ActorHasTag(FName(TEXT("Stackable"))))
	{
		return StackList;
	}

	/*Loop through the list of stackable items created in BeginPlay() and check for matching tags, as well as world positioning, 
	and populate the array with elements which are found to have the center on the same Z axis as the recieved parameter (+/- a small offset)
	*/
	for (const auto Iterator : AllStackableItems)
	{
		if (Iterator->Tags == ContainedItem->Tags)
		{
			if ((ContainedItem->GetActorLocation().X - 2 < Iterator->GetActorLocation().X) &&
				(Iterator->GetActorLocation().X < ContainedItem->GetActorLocation().X + 2) &&
				(ContainedItem->GetActorLocation().Y - 2 < Iterator->GetActorLocation().Y) &&
				(Iterator->GetActorLocation().Y< ContainedItem->GetActorLocation().Y + 2))
			{
				StackList.Add(Iterator);
			}
		}
	}

	//Bubble sort algorithm
	bool swapped = true;
	int j = 0;
	AActor* tmp;
	while (swapped) {
		swapped = false;
		j++;
		for (int i = 0; i < StackList.Num() - j; i++) {
			if (StackList[FSetElementId::FromInteger(i)]->GetActorLocation().Z > StackList[FSetElementId::FromInteger(i + 1)]->GetActorLocation().Z)
			{
				tmp = StackList[FSetElementId::FromInteger(i)];
				StackList[FSetElementId::FromInteger(i)] = StackList[FSetElementId::FromInteger(i + 1)];
				StackList[FSetElementId::FromInteger(i + 1)] = tmp;
				swapped = true;
			}
		}
	}
	return StackList;
}
	void GatherUserDefinedDependencies(UBlueprint& InBlueprint)
	{
		FGatherConvertedClassDependencies ClassDependencies(InBlueprint.GeneratedClass);
		for (auto Iter : ClassDependencies.ConvertedClasses)
		{
			DependentObjects.Add(Iter);
		}
		for (auto Iter : ClassDependencies.ConvertedStructs)
		{
			DependentObjects.Add(Iter);
		}
		for (auto Iter : ClassDependencies.ConvertedEnum)
		{
			DependentObjects.Add(Iter);
		}

		if (DependentObjects.Num())
		{
			TypeDependencies = LOCTEXT("ConvertedDependencies", "Converted Dependencies:\n").ToString();
		}
		else
		{
			TypeDependencies = LOCTEXT("NoConvertedAssets", "No Converted Dependencies was found.\n").ToString();
		}

		for (auto Obj : DependentObjects)
		{
			TypeDependencies += FString::Printf(TEXT("%s \t%s\n"), *Obj->GetClass()->GetName(), *Obj->GetPathName());
		}
		DependentObjects.Add(InBlueprint.GeneratedClass);

		bool bUnconvertedHeader = false;
		for (auto Asset : ClassDependencies.Assets)
		{
			if (auto BPGC = Cast<UBlueprintGeneratedClass>(Asset))
			{
				UnconvertedNeededClasses.Add(BPGC);
				if (!bUnconvertedHeader)
				{
					bUnconvertedHeader = true;
					TypeDependencies += LOCTEXT("NoConvertedDependencies", "\nUnconverted Dependencies, that require a warpper struct:\n").ToString();
				}
				TypeDependencies += FString::Printf(TEXT("%s \t%s\n"), *BPGC->GetClass()->GetName(), *BPGC->GetPathName());
			}
		}
	}
void FSoundClassEditor::OnSelectedNodesChanged(const TSet<class UObject*>& NewSelection)
{
	TArray<UObject*> Selection;

	if(NewSelection.Num())
	{
		for(TSet<class UObject*>::TConstIterator SetIt(NewSelection);SetIt;++SetIt)
		{
			USoundClassGraphNode* GraphNode = CastChecked<USoundClassGraphNode>(*SetIt);
			Selection.Add(GraphNode->SoundClass);
		}
		DetailsView->SetObjects(Selection);
	}
	else
	{
		DetailsView->SetObject(SoundClass);
	}
}
Exemplo n.º 17
0
/**
 * Dissociates all linker import and forced export object references. This currently needs to 
 * happen as the referred objects might be destroyed at any time.
 */
void DissociateImportsAndForcedExports()
{
	if( GImportCount && GObjLoadersWithNewImports.Num())
	{
		for (TSet< ULinkerLoad*>::TIterator It(GObjLoadersWithNewImports); It; ++It)
		{
			ULinkerLoad* Linker = *It;
			for( int32 ImportIndex=0; ImportIndex<Linker->ImportMap.Num(); ImportIndex++ )
			{
				FObjectImport& Import = Linker->ImportMap[ImportIndex];
				if( Import.XObject && !Import.XObject->HasAnyFlags(RF_Native) )
				{
					Import.XObject = NULL;
				}
				Import.SourceLinker = NULL;
				// when the SourceLinker is reset, the SourceIndex must also be reset, or recreating
				// an import that points to a redirector will fail to find the redirector
				Import.SourceIndex = INDEX_NONE;
			}
		}
	}
	GImportCount = 0;
	GObjLoadersWithNewImports.Empty();

	if( GForcedExportCount )
	{
		for (TMap<UPackage*, ULinkerLoad*>::TIterator It(GObjLoaders); It; ++It)
		{
			ULinkerLoad* Linker = It.Value();
			//@todo optimization: only dissociate exports for loaders that had forced exports created
			//@todo optimization: since the last time this function was called.
			for( int32 ExportIndex=0; ExportIndex<Linker->ExportMap.Num(); ExportIndex++ )
			{
				FObjectExport& Export = Linker->ExportMap[ExportIndex];
				if( Export.Object && Export.bForcedExport )
				{
					Export.Object->SetLinker( NULL, INDEX_NONE );
					Export.Object = NULL;
				}
			}
		}
	}
	GForcedExportCount = 0;
}
void FPropertyTable::RemoveColumn( const TSharedRef< class IPropertyTableColumn >& Column )
{
	// Update the selection to exclude cells in the column we are removing
	TSet<TSharedRef<IPropertyTableCell>> NewSelectedCells;
	for(const TSharedRef<IPropertyTableCell>& CurrentSelectedCell : SelectedCells)
	{
		if(CurrentSelectedCell->GetColumn() != Column)
		{
			NewSelectedCells.Add(CurrentSelectedCell);
		}
	}

	Columns.Remove( Column );
	ColumnsChanged.Broadcast();

	if(NewSelectedCells.Num() != SelectedCells.Num())
	{
		SetSelectedCells(NewSelectedCells);
	}
}
Exemplo n.º 19
0
void FNiagaraEditor::OnSelectedNodesChanged(const TSet<class UObject*>& NewSelection)
{
	TArray<UObject*> SelectedObjects;

	UObject* EditObject = Script;

	if (NewSelection.Num() == 0)
	{
		SelectedObjects.Add(EditObject);
	}
	else
	{
		for (TSet<class UObject*>::TConstIterator SetIt(NewSelection); SetIt; ++SetIt)
		{
			SelectedObjects.Add(*SetIt);
		}
	}

	GetDetailView()->SetObjects(SelectedObjects, true);
}
/**
* Recursively sets expanded items for a node
*
* @param InNode			The node to set expanded items on
* @param OutExpandedItems	List of expanded items to set
*/
void SetExpandedItems(TSharedPtr<FPropertyNode> InPropertyNode, const TSet<FString>& InExpandedItems)
{
	if (InExpandedItems.Num() > 0)
	{
		const bool bWithArrayIndex = true;
		FString Path;
		Path.Empty(128);
		InPropertyNode->GetQualifiedName(Path, bWithArrayIndex);

		if (InExpandedItems.Contains(Path))
		{
			InPropertyNode->SetNodeFlags(EPropertyNodeFlags::Expanded, true);
		}

		for (int32 NodeIndex = 0; NodeIndex < InPropertyNode->GetNumChildNodes(); ++NodeIndex)
		{
			SetExpandedItems(InPropertyNode->GetChildNode(NodeIndex), InExpandedItems);
		}
	}
}
Exemplo n.º 21
0
void FPropertyEditorToolkit::GridSelectionChanged()
{
	TArray< TWeakObjectPtr< UObject > > SelectedObjects;
	PropertyTable->GetSelectedObjects( SelectedObjects );
	PropertyTree->SetObjectArray( SelectedObjects );

	const TSet< TSharedRef< IPropertyTableRow > > SelectedRows = PropertyTable->GetSelectedRows();

	if ( SelectedRows.Num() == 1 )
	{
		for( auto RowIter = SelectedRows.CreateConstIterator(); RowIter; ++RowIter )
		{
			PropertyTree->SetRootPath( PropertyTable->GetRootPath()->ExtendPath( (*RowIter)->GetPartialPath() ) );
			break;
		}
	}
	else if ( !FPropertyPath::AreEqual( PropertyTree->GetRootPath(), PropertyTable->GetRootPath() ) )
	{
		PropertyTree->SetRootPath( PropertyTable->GetRootPath() );
	}
}
void FPropertyTable::RemoveRow( const TSharedRef< class IPropertyTableRow >& Row )
{
	//@todo Consider encapsulating the logic for this check [12/7/2012 Justin.Sargent]
	if ( !Row->HasChildren() && !Row->GetDataSource()->AsPropertyPath().IsValid() )
	{
		const TWeakObjectPtr< UObject > Object = Row->GetDataSource()->AsUObject();
		SourceObjectPropertyNodes.Remove( Object );

		if ( !Object.IsValid() )
		{
			PurgeInvalidObjectNodes();
		}
	}

	// Update the selection to exclude cells in the row we are removing
	TSet<TSharedRef<IPropertyTableCell>> NewSelectedCells;
	for(const TSharedRef<IPropertyTableCell>& CurrentSelectedCell : SelectedCells)
	{
		if(CurrentSelectedCell->GetRow() != Row)
		{
			NewSelectedCells.Add(CurrentSelectedCell);
		}
	}

	Rows.Remove( Row );
	RowsChanged.Broadcast();

	for( const TSharedRef< IPropertyTableColumn >& Column : Columns )
	{
		Column->RemoveCellsForRow( Row );
	}

	if(NewSelectedCells.Num() != SelectedCells.Num())
	{
		SetSelectedCells(NewSelectedCells);
	}
}
void FWidgetBlueprintEditor::CleanSelection()
{
	TSet<FWidgetReference> TempSelection;

	TArray<UWidget*> WidgetsInTree;
	GetWidgetBlueprintObj()->WidgetTree->GetAllWidgets(WidgetsInTree);
	TSet<UWidget*> TreeWidgetSet(WidgetsInTree);

	for ( FWidgetReference& WidgetRef : SelectedWidgets )
	{
		if ( WidgetRef.IsValid() )
		{
			if ( TreeWidgetSet.Contains(WidgetRef.GetTemplate()) )
			{
				TempSelection.Add(WidgetRef);
			}
		}
	}

	if ( TempSelection.Num() != SelectedWidgets.Num() )
	{
		SelectWidgets(TempSelection, false);
	}
}
uint32 FAssetDataDiscovery::Run()
{
	double DiscoverStartTime = FPlatformTime::Seconds();
	int32 NumDiscoveredFiles = 0;

	FString LocalFilenamePathToPrioritize;

	TSet<FString> LocalDiscoveredPathsSet;
	TArray<FString> LocalDiscoveredDirectories;

	TArray<FDiscoveredPackageFile> LocalPriorityFilesToSearch;
	TArray<FDiscoveredPackageFile> LocalNonPriorityFilesToSearch;

	// This set contains the folders that we should hide by default unless they contain assets
	TSet<FString> PathsToHideIfEmpty;
	PathsToHideIfEmpty.Add(TEXT("/Game/Collections"));

	auto FlushLocalResultsIfRequired = [&]()
	{
		if (LocalPriorityFilesToSearch.Num() > 0 || LocalNonPriorityFilesToSearch.Num() > 0 || LocalDiscoveredPathsSet.Num() > 0)
		{
			TArray<FString> LocalDiscoveredPathsArray = LocalDiscoveredPathsSet.Array();

			{
				FScopeLock CritSectionLock(&WorkerThreadCriticalSection);

				// Place all the discovered files into the files to search list
				DiscoveredPaths.Append(MoveTemp(LocalDiscoveredPathsArray));

				PriorityDiscoveredFiles.Append(MoveTemp(LocalPriorityFilesToSearch));
				NonPriorityDiscoveredFiles.Append(MoveTemp(LocalNonPriorityFilesToSearch));
			}
		}

		LocalDiscoveredPathsSet.Reset();

		LocalPriorityFilesToSearch.Reset();
		LocalNonPriorityFilesToSearch.Reset();
	};

	auto IsPriorityFile = [&](const FString& InPackageFilename) -> bool
	{
		return !bIsSynchronous && !LocalFilenamePathToPrioritize.IsEmpty() && InPackageFilename.StartsWith(LocalFilenamePathToPrioritize);
	};

	auto OnIterateDirectoryItem = [&](const TCHAR* InPackageFilename, const FFileStatData& InPackageStatData) -> bool
	{
		if (StopTaskCounter.GetValue() != 0)
		{
			// Requested to stop - break out of the directory iteration
			return false;
		}

		const FString PackageFilenameStr = InPackageFilename;

		if (InPackageStatData.bIsDirectory)
		{
			LocalDiscoveredDirectories.Add(PackageFilenameStr / TEXT(""));

			FString PackagePath;
			if (FPackageName::TryConvertFilenameToLongPackageName(PackageFilenameStr, PackagePath) && !PathsToHideIfEmpty.Contains(PackagePath))
			{
				LocalDiscoveredPathsSet.Add(PackagePath);
			}
		}
		else if (FPackageName::IsPackageFilename(PackageFilenameStr))
		{
			if (IsValidPackageFileToRead(PackageFilenameStr))
			{
				const FString LongPackageNameStr = FPackageName::FilenameToLongPackageName(PackageFilenameStr);

				if (IsPriorityFile(PackageFilenameStr))
				{
					LocalPriorityFilesToSearch.Add(FDiscoveredPackageFile(PackageFilenameStr, InPackageStatData.ModificationTime));
				}
				else
				{
					LocalNonPriorityFilesToSearch.Add(FDiscoveredPackageFile(PackageFilenameStr, InPackageStatData.ModificationTime));
				}

				LocalDiscoveredPathsSet.Add(FPackageName::GetLongPackagePath(LongPackageNameStr));

				++NumDiscoveredFiles;

				// Flush the data if we've processed enough
				if (!bIsSynchronous && (LocalPriorityFilesToSearch.Num() + LocalNonPriorityFilesToSearch.Num()) >= AssetDataGathererConstants::MaxFilesToDiscoverBeforeFlush)
				{
					FlushLocalResultsIfRequired();
				}
			}
		}

		return true;
	};

	bool bIsIdle = true;

	while (StopTaskCounter.GetValue() == 0)
	{
		FString LocalDirectoryToSearch;
		{
			FScopeLock CritSectionLock(&WorkerThreadCriticalSection);

			if (DirectoriesToSearch.Num() > 0)
			{
				bIsDiscoveringFiles = true;

				LocalFilenamePathToPrioritize = FilenamePathToPrioritize;

				// Pop off the first path to search
				LocalDirectoryToSearch = DirectoriesToSearch[0];
				DirectoriesToSearch.RemoveAt(0, 1, false);
			}
		}

		if (LocalDirectoryToSearch.Len() > 0)
		{
			if (bIsIdle)
			{
				bIsIdle = false;

				// About to start work - reset these
				DiscoverStartTime = FPlatformTime::Seconds();
				NumDiscoveredFiles = 0;
			}

			// Iterate the current search directory
			FLambdaDirectoryStatVisitor Visitor(OnIterateDirectoryItem);
			IFileManager::Get().IterateDirectoryStat(*LocalDirectoryToSearch, Visitor);

			{
				FScopeLock CritSectionLock(&WorkerThreadCriticalSection);

				// Push back any newly discovered sub-directories
				if (LocalDiscoveredDirectories.Num() > 0)
				{
					// Use LocalDiscoveredDirectories as scratch space, then move it back out - this puts the directories we just 
					// discovered at the start of the list for the next iteration, which can help with disk locality
					LocalDiscoveredDirectories.Append(MoveTemp(DirectoriesToSearch));
					DirectoriesToSearch = MoveTemp(LocalDiscoveredDirectories);
				}
				LocalDiscoveredDirectories.Reset();

				if (!bIsSynchronous)
				{
					FlushLocalResultsIfRequired();
					SortPathsByPriority(1);
				}
			}
		}
		else
		{
			if (!bIsIdle)
			{
				bIsIdle = true;

				{
					FScopeLock CritSectionLock(&WorkerThreadCriticalSection);
					bIsDiscoveringFiles = false;
				}

				UE_LOG(LogAssetRegistry, Verbose, TEXT("Discovery took %0.6f seconds and found %d files to process"), FPlatformTime::Seconds() - DiscoverStartTime, NumDiscoveredFiles);
			}

			// Ran out of things to do... if we have any pending results, flush those now
			FlushLocalResultsIfRequired();

			if (bIsSynchronous)
			{
				// This is synchronous. Since our work is done, we should safely exit
				Stop();
			}
			else
			{
				// No work to do. Sleep for a little and try again later.
				FPlatformProcess::Sleep(0.1);
			}
		}
	}

	return 0;
}
Exemplo n.º 25
0
void FNiagaraEditor::PasteNodesHere(const FVector2D& Location)
{
	TSharedPtr<SGraphEditor> CurrentGraphEditor = NodeGraphEditorPtr.Pin();
	if (!CurrentGraphEditor.IsValid())
	{
		return;
	}

	// Undo/Redo support
	const FScopedTransaction Transaction(FGenericCommands::Get().Paste->GetDescription());
	UEdGraph* EdGraph = Cast<UEdGraph>(CurrentGraphEditor->GetCurrentGraph());
	EdGraph->Modify();

	const FGraphPanelSelectionSet SelectedNodes = CurrentGraphEditor->GetSelectedNodes();

	// Clear the selection set (newly pasted stuff will be selected)
	CurrentGraphEditor->ClearSelectionSet();

	// Grab the text to paste from the clipboard.
	FString TextToImport;
	FPlatformMisc::ClipboardPaste(TextToImport);

	// Import the nodes
	TSet<UEdGraphNode*> PastedNodes;
	FEdGraphUtilities::ImportNodesFromText(EdGraph, TextToImport, /*out*/ PastedNodes);

	//Average position of nodes so we can move them while still maintaining relative distances to each other
	FVector2D AvgNodePosition(0.0f, 0.0f);

	for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
	{
		UEdGraphNode* Node = (*It);
		if (Node)
		{
			AvgNodePosition.X += Node->NodePosX;
			AvgNodePosition.Y += Node->NodePosY;
		}
	}

	if (PastedNodes.Num() > 0)
	{
		float InvNumNodes = 1.0f / float(PastedNodes.Num());
		AvgNodePosition.X *= InvNumNodes;
		AvgNodePosition.Y *= InvNumNodes;
	}
	
	for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
	{
		UEdGraphNode* PasteNode = (*It);
		if (PasteNode)
		{
			// Select the newly pasted stuff
			CurrentGraphEditor->SetNodeSelection(PasteNode, true);

			PasteNode->NodePosX = (PasteNode->NodePosX - AvgNodePosition.X) + Location.X;
			PasteNode->NodePosY = (PasteNode->NodePosY - AvgNodePosition.Y) + Location.Y;

			PasteNode->SnapToGrid(16);

			// Give new node a different Guid from the old one
			PasteNode->CreateNewGuid();
		}
	}


	// Update UI
	CurrentGraphEditor->NotifyGraphChanged();

	UObject* GraphOwner = EdGraph->GetOuter();
	if (GraphOwner)
	{
		GraphOwner->PostEditChange();
		GraphOwner->MarkPackageDirty();
	}
}
bool FActorFolders::RenameFolderInWorld(UWorld& World, FName OldPath, FName NewPath)
{
	if (OldPath.IsNone() || OldPath == NewPath || PathIsChildOf(NewPath.ToString(), OldPath.ToString()))
	{
		return false;
	}

	const FScopedTransaction Transaction(LOCTEXT("UndoAction_RenameFolder", "Rename Folder"));

	const FString OldPathString = OldPath.ToString();
	const FString NewPathString = NewPath.ToString();

	TSet<FName> RenamedFolders;

	// Move any folders we currently hold - old ones will be deleted later
	UEditorActorFolders& FoldersInWorld = GetOrCreateFoldersForWorld(World);
	FoldersInWorld.Modify();

	auto ExistingFoldersCopy = FoldersInWorld.Folders;
	for (const auto& Pair : ExistingFoldersCopy)
	{
		auto Path = Pair.Key;

		const FString FolderPath = Path.ToString();
		if (OldPath == Path || PathIsChildOf(FolderPath, OldPathString))
		{
			const FName NewFolder = OldPathToNewPath(OldPathString, NewPathString, FolderPath);
			if (!FoldersInWorld.Folders.Contains(NewFolder))
			{
				// Use the existing properties for the folder if we have them
				if (FActorFolderProps* ExistingProperties = FoldersInWorld.Folders.Find(Path))
				{
					FoldersInWorld.Folders.Add(NewFolder, *ExistingProperties);
				}
				else
				{
					// Otherwise use default properties
					FoldersInWorld.Folders.Add(NewFolder);
				}
				OnFolderMove.Broadcast(World, Path, NewFolder);
				OnFolderCreate.Broadcast(World, NewFolder);
			}
			RenamedFolders.Add(Path);
		}
	}

	// Now that we have folders created, move any actors that ultimately reside in that folder too
	for (auto ActorIt = FActorIterator(&World); ActorIt; ++ActorIt)
	{
		const FName& OldActorPath = ActorIt->GetFolderPath();
		
		AActor* Actor = *ActorIt;
		if (OldActorPath.IsNone())
		{
			continue;
		}

		if (Actor->GetFolderPath() == OldPath || PathIsChildOf(OldActorPath.ToString(), OldPathString))
		{
			RenamedFolders.Add(OldActorPath);

			ActorIt->SetFolderPath_Recursively(OldPathToNewPath(OldPathString, NewPathString, OldActorPath.ToString()));
		}
	}

	// Cleanup any old folders
	for (const auto& Path : RenamedFolders)
	{
		FoldersInWorld.Folders.Remove(Path);
		OnFolderDelete.Broadcast(World, Path);
	}

	return RenamedFolders.Num() != 0;
}
Exemplo n.º 27
0
FReply SGraphPin::OnPinMouseDown( const FGeometry& SenderGeometry, const FPointerEvent& MouseEvent )
{
	bIsMovingLinks = false;

	if (MouseEvent.GetEffectingButton() == EKeys::LeftMouseButton)
	{
		if (!GraphPinObj->bNotConnectable && IsEditable.Get())
		{
			if (MouseEvent.IsAltDown())
			{
				// Alt-Left clicking will break all existing connections to a pin
				const UEdGraphSchema* Schema = GraphPinObj->GetSchema();
				Schema->BreakPinLinks(*GraphPinObj, true);
				return FReply::Handled();
			}

			auto OwnerNodePinned = OwnerNodePtr.Pin();
			if (MouseEvent.IsControlDown() && (GraphPinObj->LinkedTo.Num() > 0))
			{
				// Get a reference to the owning panel widget
				check(OwnerNodePinned.IsValid());
				TSharedPtr<SGraphPanel> OwnerPanelPtr = OwnerNodePinned->GetOwnerPanel();
				check(OwnerPanelPtr.IsValid());

				// Obtain the set of all pins within the panel
				TSet<TSharedRef<SWidget> > AllPins;
				OwnerPanelPtr->GetAllPins(AllPins);

				// Construct a UEdGraphPin->SGraphPin mapping for the full pin set
				TMap< UEdGraphPin*, TSharedRef<SGraphPin> > PinToPinWidgetMap;
				for( TSet< TSharedRef<SWidget> >::TIterator ConnectorIt(AllPins); ConnectorIt; ++ConnectorIt )
				{
					const TSharedRef<SWidget>& SomePinWidget = *ConnectorIt;
					const SGraphPin& PinWidget = static_cast<const SGraphPin&>(SomePinWidget.Get());

					PinToPinWidgetMap.Add(PinWidget.GetPinObj(), StaticCastSharedRef<SGraphPin>(SomePinWidget));
				}

				// Define a local struct to temporarily store lookup information for pins that we are currently linked to
				struct LinkedToPinInfo
				{
					// Pin name string
					FString PinName;

					// A weak reference to the node object that owns the pin
					TWeakObjectPtr<UEdGraphNode> OwnerNodePtr;
				};

				// Build a lookup table containing information about the set of pins that we're currently linked to
				TArray<LinkedToPinInfo> LinkedToPinInfoArray;
				for( TArray<UEdGraphPin*>::TIterator LinkArrayIter(GetPinObj()->LinkedTo); LinkArrayIter; ++LinkArrayIter )
				{
					if (auto PinWidget = PinToPinWidgetMap.Find(*LinkArrayIter))
					{
						check((*PinWidget)->OwnerNodePtr.IsValid());

						LinkedToPinInfo PinInfo;
						PinInfo.PinName = (*PinWidget)->GetPinObj()->PinName;
						PinInfo.OwnerNodePtr = (*PinWidget)->OwnerNodePtr.Pin()->GetNodeObj();
						LinkedToPinInfoArray.Add(PinInfo);
					}
				}

				// Control-Left clicking will break all existing connections to a pin
				// Note that for some nodes, this can cause reconstruction. In that case, pins we had previously linked to may now be destroyed.
				const UEdGraphSchema* Schema = GraphPinObj->GetSchema();
				Schema->BreakPinLinks(*GraphPinObj, true);

				// Check to see if the panel has been invalidated by a graph change notification
				if (!OwnerPanelPtr->Contains(OwnerNodePinned->GetNodeObj()))
				{
					// Force the panel to update. This will cause node & pin widgets to be reinstanced to match any reconstructed node/pin object references.
					OwnerPanelPtr->Update();

					// Obtain the full set of pins again after the update
					AllPins.Empty(AllPins.Num());
					OwnerPanelPtr->GetAllPins(AllPins);

					// Rebuild the UEdGraphPin->SGraphPin mapping for the full pin set
					PinToPinWidgetMap.Empty(PinToPinWidgetMap.Num());
					for( TSet< TSharedRef<SWidget> >::TIterator ConnectorIt(AllPins); ConnectorIt; ++ConnectorIt )
					{
						const TSharedRef<SWidget>& SomePinWidget = *ConnectorIt;
						const SGraphPin& PinWidget = static_cast<const SGraphPin&>(SomePinWidget.Get());

						PinToPinWidgetMap.Add(PinWidget.GetPinObj(), StaticCastSharedRef<SGraphPin>(SomePinWidget));
					}
				}
				
				// Now iterate over our lookup table to find the instances of pin widgets that we had previously linked to
				TArray<TSharedRef<SGraphPin>> PinArray;
				for(auto LinkedToPinInfoIter = LinkedToPinInfoArray.CreateConstIterator(); LinkedToPinInfoIter; ++LinkedToPinInfoIter)
				{
					LinkedToPinInfo PinInfo = *LinkedToPinInfoIter;
					UEdGraphNode* OwnerNodeObj = PinInfo.OwnerNodePtr.Get();
					if(OwnerNodeObj != NULL)
					{
						for(auto PinIter = PinInfo.OwnerNodePtr.Get()->Pins.CreateConstIterator(); PinIter; ++PinIter)
						{
							UEdGraphPin* Pin = *PinIter;
							if(Pin->PinName == PinInfo.PinName)
							{
								if (auto pWidget = PinToPinWidgetMap.Find(Pin))
								{
									PinArray.Add(*pWidget);
								}
							}
						}
					}
				}

				if(PinArray.Num() > 0)
				{
					bIsMovingLinks = true;

					return FReply::Handled().BeginDragDrop(SpawnPinDragEvent(OwnerPanelPtr.ToSharedRef(), PinArray, /*bIsShiftOperation=*/ false));
				}
				else
				{
					// Shouldn't get here, but just in case we lose our previous links somehow after breaking them, we'll just skip the drag.
					return FReply::Handled();
				}
			}
			
			// Start a drag-drop on the pin
			if (ensure(OwnerNodePinned.IsValid()))
			{
				TArray<TSharedRef<SGraphPin>> PinArray;
				PinArray.Add(SharedThis(this));

				return FReply::Handled().BeginDragDrop(SpawnPinDragEvent(OwnerNodePinned->GetOwnerPanel().ToSharedRef(), PinArray, MouseEvent.IsShiftDown()));
			}
			else
			{
				return FReply::Unhandled();
			}
		}
		else
		{
			// It's not connectable, but we don't want anything above us to process this left click.
			return FReply::Handled();
		}
	}
	else
	{
		return FReply::Unhandled();
	}
}
Exemplo n.º 28
0
void FSpriteGeometryEditingHelper::DeleteSelectedItems()
{
	// Determine which vertices or entire shapes should be deleted
	TSet<FShapeVertexPair> CompositeIndicesSet;
	TSet<int32> ShapesToDeleteSet;

	if (IsEditingGeometry())
	{
		FSpriteGeometryCollection& Geometry = GetGeometryChecked();

		for (TSharedPtr<FSelectedItem> SelectionIt : GetSelectionSet())
		{
			if (const FSpriteSelectedVertex* SelectedVertex = SelectionIt->CastTo<const FSpriteSelectedVertex>(FSelectionTypes::Vertex))
			{
				CompositeIndicesSet.Add(FShapeVertexPair(SelectedVertex->ShapeIndex, SelectedVertex->VertexIndex));

				if (SelectedVertex->IsA(FSelectionTypes::Edge)) // add the "next" point for the edge
				{
					const int32 NextIndex = (SelectedVertex->VertexIndex + 1) % Geometry.Shapes[SelectedVertex->ShapeIndex].Vertices.Num();
					CompositeIndicesSet.Add(FShapeVertexPair(SelectedVertex->ShapeIndex, NextIndex));
				}
			}
			else if (const FSpriteSelectedShape* SelectedShape = SelectionIt->CastTo<const FSpriteSelectedShape>(FSelectionTypes::GeometryShape))
			{
				ShapesToDeleteSet.Add(SelectedShape->ShapeIndex);
			}
		}
	}

	// See if anything else can be deleted
	bool bCanDeleteNonGeometry = false;
	for (const TSharedPtr<FSelectedItem> SelectedItem : GetSelectionSet())
	{
		if (SelectedItem->CanBeDeleted())
		{
			bCanDeleteNonGeometry = true;
			break;
		}
	}

	// Now delete the stuff that was selected in the correct order so that indices aren't messed up
	const bool bDeletingGeometry = (CompositeIndicesSet.Num() > 0) || (ShapesToDeleteSet.Num() > 0);
	if (bDeletingGeometry || bCanDeleteNonGeometry)
	{
		EditorContext->BeginTransaction(LOCTEXT("DeleteSelectionTransaction", "Delete Selection"));
		EditorContext->MarkTransactionAsDirty();

		if (bDeletingGeometry)
		{
			FSpriteGeometryCollection& Geometry = GetGeometryChecked();

			// Delete the selected vertices first, as they may cause entire shapes to need to be deleted (sort so we delete from the back first)
			TArray<FShapeVertexPair> CompositeIndices = CompositeIndicesSet.Array();
			CompositeIndices.Sort([](const FShapeVertexPair& A, const FShapeVertexPair& B) { return (A.VertexIndex > B.VertexIndex); });
			for (const FShapeVertexPair& Composite : CompositeIndices)
			{
				const int32 ShapeIndex = Composite.ShapeIndex;
				const int32 VertexIndex = Composite.VertexIndex;
				if (DeleteVertexInPolygonInternal(Geometry, ShapeIndex, VertexIndex))
				{
					ShapesToDeleteSet.Add(ShapeIndex);
				}
			}

			// Delete the selected shapes (plus any shapes that became empty due to selected vertices)
			if (ShapesToDeleteSet.Num() > 0)
			{
				// Sort so we delete from the back first
				TArray<int32> ShapesToDeleteIndicies = ShapesToDeleteSet.Array();
				ShapesToDeleteIndicies.Sort([](const int32& A, const int32& B) { return (A > B); });
				for (const int32 ShapeToDeleteIndex : ShapesToDeleteIndicies)
				{
					Geometry.Shapes.RemoveAt(ShapeToDeleteIndex);
				}
			}

			Geometry.GeometryType = ESpritePolygonMode::FullyCustom;
		}

		// Delete everything else
		if (bCanDeleteNonGeometry)
		{
			for (TSharedPtr<FSelectedItem> SelectedItem : GetSelectionSet())
			{
				if (SelectedItem->CanBeDeleted())
				{
					SelectedItem->DeleteThisItem();
				}
			}
		}

		EditorContext->EndTransaction();
	}

	ClearSelectionSet();
	ResetAddPolygonMode();
}
Exemplo n.º 29
0
void FSoundCueEditor::PasteNodesHere(const FVector2D& Location)
{
	// Undo/Redo support
	const FScopedTransaction Transaction( NSLOCTEXT("UnrealEd", "SoundCueEditorPaste", "Paste Sound Cue Node") );
	SoundCue->GetGraph()->Modify();
	SoundCue->Modify();

	// Clear the selection set (newly pasted stuff will be selected)
	SoundCueGraphEditor->ClearSelectionSet();

	// Grab the text to paste from the clipboard.
	FString TextToImport;
	FPlatformMisc::ClipboardPaste(TextToImport);

	// Import the nodes
	TSet<UEdGraphNode*> PastedNodes;
	FEdGraphUtilities::ImportNodesFromText(SoundCue->GetGraph(), TextToImport, /*out*/ PastedNodes);

	//Average position of nodes so we can move them while still maintaining relative distances to each other
	FVector2D AvgNodePosition(0.0f,0.0f);

	for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
	{
		UEdGraphNode* Node = *It;
		AvgNodePosition.X += Node->NodePosX;
		AvgNodePosition.Y += Node->NodePosY;
	}

	if ( PastedNodes.Num() > 0 )
	{
		float InvNumNodes = 1.0f/float(PastedNodes.Num());
		AvgNodePosition.X *= InvNumNodes;
		AvgNodePosition.Y *= InvNumNodes;
	}

	for (TSet<UEdGraphNode*>::TIterator It(PastedNodes); It; ++It)
	{
		UEdGraphNode* Node = *It;

		if (USoundCueGraphNode* SoundGraphNode = Cast<USoundCueGraphNode>(Node))
		{
			SoundCue->AllNodes.Add(SoundGraphNode->SoundNode);
		}

		// Select the newly pasted stuff
		SoundCueGraphEditor->SetNodeSelection(Node, true);

		Node->NodePosX = (Node->NodePosX - AvgNodePosition.X) + Location.X ;
		Node->NodePosY = (Node->NodePosY - AvgNodePosition.Y) + Location.Y ;

		Node->SnapToGrid(SNodePanel::GetSnapGridSize());

		// Give new node a different Guid from the old one
		Node->CreateNewGuid();
	}

	// Force new pasted SoundNodes to have same connections as graph nodes
	SoundCue->CompileSoundNodesFromGraphNodes();

	// Update UI
	SoundCueGraphEditor->NotifyGraphChanged();

	SoundCue->PostEditChange();
	SoundCue->MarkPackageDirty();
}
Exemplo n.º 30
0
/*Responds to Right Click input
Picks up stacks of items at once using both 'hands'
*/
void AMyCharacter::GrabWithTwoHands()
{
	/**
		Section to treat unacceptable function calls
	*/

	//If something is allready held in hands
	if (TwoHandSlot.Num() || LeftHandSlot || RightHandSlot)
	{
		PopUp.Broadcast(FString(TEXT("Allready holding something!")));
		return;
	}

	//If no object is blocking the hit
	if (!HitObject.IsValidBlockingHit())
	{
		//PopUpMessage = ActionNotValid;
		PopUp.Broadcast(FString(TEXT("Action not valid!")));
		return;                                                                                                           
	}

	//No actor focused
	if (!HighlightedActor)
	{
		PopUp.Broadcast(FString(TEXT("Nothing to pick")));
	}

	//If highlighted actor is not pickable
	if (!HitObject.GetActor()->ActorHasTag(FName(TEXT("Stackable"))))
	{
		PopUp.Broadcast(FString(TEXT("Can't pick that with two hands")));
		return;
	}

	//If object is too far away
	if (HitObject.Distance > MaxGraspLength)
	{
		PopUp.Broadcast(FString(TEXT("You need to get closer!")));
		return;
	}

	//Local variables to perform computation
	TSet<AActor*> LocalStackVariable = GetStack(HighlightedActor);
	TSet<AActor*> ReturnStack;

	//Making sure stack is pickable by not having any elements on top (eg: Spoon, Knife)
	if (HasAnyOnTop(LocalStackVariable[FSetElementId::FromInteger(LocalStackVariable.Num() - 1)]))
	{
		PopUp.Broadcast(FString(TEXT("Make sure no item is on top!")));
		return;
	}

	if (HighlightedActor->ActorHasTag(FName(TEXT("Stackable"))))
	{
		int FirstIndex = 0;
		if (LocalStackVariable.Num() > StackGrabLimit)
		{
			FirstIndex = LocalStackVariable.Num() - StackGrabLimit;
		}

		for (int i = FirstIndex; i < LocalStackVariable.Num(); i++)
		{
			GetStaticMesh(LocalStackVariable[FSetElementId::FromInteger(i)])->SetEnableGravity(false);
			GetStaticMesh(LocalStackVariable[FSetElementId::FromInteger(i)])->SetCollisionEnabled(ECollisionEnabled::NoCollision);
			ReturnStack.Add(LocalStackVariable[FSetElementId::FromInteger(i)]);
		}
		TwoHandSlot = ReturnStack;
		SelectedObject = LocalStackVariable[FSetElementId::FromInteger(LocalStackVariable.Num()-1)];
		GetStaticMesh(SelectedObject)->SetCustomDepthStencilValue(2);
	}
	
	UpdateCharacterSpeed();
}