bool UGameplayTagsManager::AddLeafTagToContainer(FGameplayTagContainer& TagContainer, FGameplayTag& Tag) { // Check tag is not already in container if (TagContainer.HasTag(Tag, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit)) { return true; } // Check tag is not just a parent of an existing tag in the container for (auto It = TagContainer.CreateConstIterator(); It; ++It) { FGameplayTagContainer ParentTags = RequestGameplayTagParents(*It); if (ParentTags.HasTag(Tag, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit)) { return false; } } // Remove any tags in the container that are a parent to this tag FGameplayTagContainer ParentTags = RequestGameplayTagParents(Tag); for (auto It = ParentTags.CreateConstIterator(); It; ++It) { if (TagContainer.HasTag(*It, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit)) { TagContainer.RemoveTag(*It); } } // Add the tag TagContainer.AddTag(Tag); return true; }
void SGameplayTagWidget::VerifyAssetTagValidity() { FGameplayTagContainer LibraryTags; // Create a set that is the library of all valid tags TArray< TSharedPtr<FGameplayTagNode> > NodeStack; UGameplayTagsManager& TagsManager = IGameplayTagsModule::Get().GetGameplayTagsManager(); TagsManager.GetFilteredGameplayRootTags(TEXT(""), NodeStack); while (NodeStack.Num() > 0) { TSharedPtr<FGameplayTagNode> CurNode = NodeStack.Pop(); if (CurNode.IsValid()) { LibraryTags.AddTag(TagsManager.RequestGameplayTag(CurNode->GetCompleteTag())); NodeStack.Append(CurNode->GetChildTagNodes()); } } // Find and remove any tags on the asset that are no longer in the library for (int32 ContainerIdx = 0; ContainerIdx < TagContainers.Num(); ++ContainerIdx) { UObject* OwnerObj = TagContainers[ContainerIdx].TagContainerOwner.Get(); FGameplayTagContainer* Container = TagContainers[ContainerIdx].TagContainer; if (Container) { FGameplayTagContainer EditableContainer = *Container; FGameplayTagContainer InvalidTags; for (auto It = Container->CreateConstIterator(); It; ++It) { if (!LibraryTags.HasTag(*It, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit)) { InvalidTags.AddTag(*It); } } if (InvalidTags.Num() > 0) { FString InvalidTagNames; for (auto InvalidIter = InvalidTags.CreateConstIterator(); InvalidIter; ++InvalidIter) { EditableContainer.RemoveTag(*InvalidIter); InvalidTagNames += InvalidIter->ToString() + TEXT("\n"); } SetContainer(Container, &EditableContainer, OwnerObj); FFormatNamedArguments Arguments; Arguments.Add(TEXT("Objects"), FText::FromString( InvalidTagNames )); FText DialogText = FText::Format( LOCTEXT("GameplayTagWidget_InvalidTags", "Invalid Tags that have been removed: \n\n{Objects}"), Arguments ); OpenMsgDlgInt( EAppMsgType::Ok, DialogText, LOCTEXT("GameplayTagWidget_Warning", "Warning") ); } } } }
void FGameplayCueHandler::GameplayCueActivated(const FGameplayTagContainer& GameplayCueTags, float NormalizedMagnitude, const FGameplayEffectContextHandle& EffectContext) { check(Owner); bool InstigatorLocal = EffectContext.IsLocallyControlled(); bool TargetLocal = OwnerIsLocallyControlled(); for (auto TagIt = GameplayCueTags.CreateConstIterator(); TagIt; ++TagIt) { if (FGameplayCueViewInfo* View = GetBestMatchingView(EGameplayCueEvent::OnActive, *TagIt, InstigatorLocal, TargetLocal)) { View->SpawnViewEffects(Owner, NULL, EffectContext); } FName MatchedTag; UFunction *Func = UAbilitySystemGlobals::Get().GetGameplayCueFunction(*TagIt, Owner->GetClass(), MatchedTag); if (Func) { FGameplayCueParameters Params; Params.NormalizedMagnitude = NormalizedMagnitude; Params.EffectContext = EffectContext; IGameplayCueInterface::DispatchBlueprintCustomHandler(Owner, Func, EGameplayCueEvent::OnActive, Params); } } }
FGameplayCueViewInfo * FGameplayCueHandler::GetBestMatchingView(EGameplayCueEvent::Type Type, const FGameplayTag BaseTag, bool InstigatorLocal, bool TargetLocal) { IGameplayTagsModule& GameplayTagsModule = IGameplayTagsModule::Get(); FGameplayTagContainer TagAndParentsContainer = GameplayTagsModule.GetGameplayTagsManager().RequestGameplayTagParents(BaseTag); for (auto InnerTagIt = TagAndParentsContainer.CreateConstIterator(); InnerTagIt; ++InnerTagIt) { FGameplayTag Tag = *InnerTagIt; for (UGameplayCueView * Def : Definitions) { for (FGameplayCueViewInfo & View : Def->Views) { if (View.CueType == Type && (!View.InstigatorLocalOnly || InstigatorLocal) && (!View.TargetLocalOnly || TargetLocal) && View.Tags.HasTag(Tag, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit)) { return &View; } } } } return NULL; }
void FGameplayTagContainerCustomization::RefreshTagList() { // Rebuild Editable Containers as container references can become unsafe BuildEditableContainerList(); // Clear the list TagNames.Empty(); // Add tags to list for (int32 ContainerIdx = 0; ContainerIdx < EditableContainers.Num(); ++ContainerIdx) { FGameplayTagContainer* Container = EditableContainers[ContainerIdx].TagContainer; if (Container) { for (auto It = Container->CreateConstIterator(); It; ++It) { TagNames.Add(MakeShareable(new FString(It->ToString()))); } } } // Refresh the slate list if( TagListView.IsValid() ) { TagListView->RequestListRefresh(); } }
void IGameplayCueInterface::HandleGameplayCues(AActor *Self, const FGameplayTagContainer& GameplayCueTags, EGameplayCueEvent::Type EventType, FGameplayCueParameters Parameters) { for (auto TagIt = GameplayCueTags.CreateConstIterator(); TagIt; ++TagIt) { HandleGameplayCue(Self, *TagIt, EventType, Parameters); } }
void FGameplayTagCountContainer::UpdateTagMap_Internal(const FGameplayTag& Tag, int32 CountDelta) { const bool bTagAlreadyExplicitlyExists = ExplicitTags.HasTag(Tag, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit); // Need special case handling to maintain the explicit tag list correctly, adding the tag to the list if it didn't previously exist and a // positive delta comes in, and removing it from the list if it did exist and a negative delta comes in. if (!bTagAlreadyExplicitlyExists) { // Brand new tag with a positive delta needs to be explicitly added if (CountDelta > 0) { ExplicitTags.AddTag(Tag); } // Block attempted reduction of non-explicit tags, as they were never truly added to the container directly else { ABILITY_LOG(Warning, TEXT("Attempted to remove tag: %s from tag count container, but it is not explicitly in the container!"), *Tag.ToString()); return; } } else if (CountDelta < 0) { // Existing tag with a negative delta that would cause a complete removal needs to be explicitly removed; Count will be updated correctly below, // so that part is skipped for now int32& ExistingCount = GameplayTagCountMap.FindOrAdd(Tag); if ((ExistingCount + CountDelta) <= 0) { ExplicitTags.RemoveTag(Tag); } } // Check if change delegates are required to fire for the tag or any of its parents based on the count change FGameplayTagContainer TagAndParentsContainer = IGameplayTagsModule::Get().GetGameplayTagsManager().RequestGameplayTagParents(Tag); for (auto CompleteTagIt = TagAndParentsContainer.CreateConstIterator(); CompleteTagIt; ++CompleteTagIt) { const FGameplayTag& CurTag = *CompleteTagIt; // Get the current count of the specified tag. NOTE: Stored as a reference, so subsequent changes propogate to the map. int32& TagCount = GameplayTagCountMap.FindOrAdd(CurTag); const int32 OldCount = TagCount; // Apply the delta to the count in the map TagCount = FMath::Max(TagCount + CountDelta, 0); // If a significant change (new addition or total removal) occurred, trigger related delegates if (OldCount == 0 || TagCount == 0) { OnAnyTagChangeDelegate.Broadcast(CurTag, TagCount); FOnGameplayEffectTagCountChanged* CountChangeDelegate = GameplayTagEventMap.Find(CurTag); if (CountChangeDelegate) { CountChangeDelegate->Broadcast(CurTag, TagCount); } } } }
void FGameplayTagCountContainer::UpdateTagCount(const FGameplayTagContainer& Container, int32 CountDelta) { if (CountDelta != 0) { for (auto TagIt = Container.CreateConstIterator(); TagIt; ++TagIt) { UpdateTagMap_Internal(*TagIt, CountDelta); } } }
void UGameplayCueManager::HandleGameplayCues(AActor* TargetActor, const FGameplayTagContainer& GameplayCueTags, EGameplayCueEvent::Type EventType, const FGameplayCueParameters& Parameters) { if (GameplayCueRunOnDedicatedServer == 0 && IsDedicatedServerForGameplayCue()) { return; } for (auto It = GameplayCueTags.CreateConstIterator(); It; ++It) { HandleGameplayCue(TargetActor, *It, EventType, Parameters); } }
void FGameplayCueHandler::GameplayCueExecuted(const FGameplayTagContainer& GameplayCueTags, float NormalizedMagnitude, const FGameplayEffectContextHandle& EffectContext) { check(Owner); bool InstigatorLocal = EffectContext.IsLocallyControlled(); bool TargetLocal = OwnerIsLocallyControlled(); for (auto TagIt = GameplayCueTags.CreateConstIterator(); TagIt; ++TagIt) { if (FGameplayCueViewInfo* View = GetBestMatchingView(EGameplayCueEvent::Executed, *TagIt, InstigatorLocal, TargetLocal)) { View->SpawnViewEffects(Owner, NULL, EffectContext); } } }
void FGACountedTagContainer::RemoveTagContainer(const FGameplayTagContainer& TagsIn) { for (auto TagIt = TagsIn.CreateConstIterator(); TagIt; ++TagIt) { int32* count = CountedTags.Find(*TagIt); if (count) { *count -= 1; } if (*count <= 0) { CountedTags.Remove(*TagIt); AllTags.RemoveTag(*TagIt); } } }
void FGACountedTagContainer::AddTagContainer(const FGameplayTagContainer& TagsIn) { for (auto TagIt = TagsIn.CreateConstIterator(); TagIt; ++TagIt) { int32* count = CountedTags.Find(*TagIt); if (count) { *count += 1; } else { CountedTags.Add(*TagIt, 1); AllTags.AddTag(*TagIt); } } }
void FGameplayTagCountContainer::Notify_StackCountChange(const FGameplayTag& Tag) { // The purpose of this function is to let anyone listening on the EGameplayTagEventType::AnyCountChange event know that the // stack count of a GE that was backing this GE has changed. We do not update our internal map/count with this info, since that // map only counts the number of GE/sources that are giving that tag. FGameplayTagContainer TagAndParentsContainer = IGameplayTagsModule::Get().GetGameplayTagsManager().RequestGameplayTagParents(Tag); for (auto CompleteTagIt = TagAndParentsContainer.CreateConstIterator(); CompleteTagIt; ++CompleteTagIt) { const FGameplayTag& CurTag = *CompleteTagIt; FDelegateInfo* DelegateInfo = GameplayTagEventMap.Find(CurTag); if (DelegateInfo) { int32 TagCount = GameplayTagCountMap.FindOrAdd(CurTag); DelegateInfo->OnAnyChange.Broadcast(CurTag, TagCount); } } }
void UK2Node_GameplayCueEvent::GetMenuEntries(FGraphContextMenuBuilder& Context) const { Super::GetMenuEntries(Context); if (!IsCompatibleWithGraph(Context.CurrentGraph)) { return; } const FString FunctionCategory(TEXT("GameplayCue Event")); IGameplayTagsModule& GameplayTagsModule = IGameplayTagsModule::Get(); FGameplayTag RootTag = GameplayTagsModule.GetGameplayTagsManager().RequestGameplayTag(FName(TEXT("GameplayCue"))); FGameplayTagContainer CueTags = GameplayTagsModule.GetGameplayTagsManager().RequestGameplayTagChildren(RootTag); // Add a root GameplayCue function as a default CueTags.AddTag(RootTag); // Fixme: need to check if this function is already defined so that it can be reimplemented // -Checking MyBlueprint->GeneratedClass isn't enough since they may have added an event and not recompiled // -FEdGraphSchemaAction_K2AddCustomEvent does not check names/always ensures a valid name // -FEdGraphSchemaAction_K2AddEvent does check and recenters - but it looks at EventSignatureName/EventSignatureClass for equality and that // won't work here. // // Probably need a new EdGraphSchemaAction to do this properly. For now this is ok since they will get a compile error if they do drop in // two of the same GameplayCue even Nodes and it should be pretty clear that they can't do that. for (auto It = CueTags.CreateConstIterator(); It; ++It) { FGameplayTag Tag = *It; UK2Node_GameplayCueEvent* NodeTemplate = Context.CreateTemplateNode<UK2Node_GameplayCueEvent>(); NodeTemplate->CustomFunctionName = Tag.GetTagName(); const FString Category = FunctionCategory; const FText MenuDesc = NodeTemplate->GetNodeTitle(ENodeTitleType::ListView); const FString Tooltip = NodeTemplate->GetTooltipText().ToString(); const FString Keywords = NodeTemplate->GetKeywords(); TSharedPtr<FEdGraphSchemaAction_K2NewNode> NodeAction = FK2ActionMenuBuilder::AddNewNodeAction(Context, Category, MenuDesc, Tooltip, 0, Keywords); NodeAction->NodeTemplate = NodeTemplate; } }
void FGameplayCueHandler::GameplayCueAdded(const FGameplayTagContainer& GameplayCueTags, float NormalizedMagnitude, const FGameplayEffectContextHandle& EffectContext) { check(Owner); bool InstigatorLocal = EffectContext.IsLocallyControlled(); bool TargetLocal = OwnerIsLocallyControlled(); for (auto TagIt = GameplayCueTags.CreateConstIterator(); TagIt; ++TagIt) { TArray<TSharedPtr<FGameplayCueViewEffects> > &Effects = SpawnedViewEffects.FindOrAdd(*TagIt); // Clear old effects if they existed? This will vary case to case. Removing old effects is the easiest approach ClearEffects(Effects); check(Effects.Num() == 0); if (FGameplayCueViewInfo * View = GetBestMatchingView(EGameplayCueEvent::WhileActive, *TagIt, InstigatorLocal, TargetLocal)) { TSharedPtr<FGameplayCueViewEffects> SpawnedEffects = View->SpawnViewEffects(Owner, &SpawnedObjects, EffectContext); Effects.Add(SpawnedEffects); } } }
void UK2Node_GameplayCueEvent::GetMenuActions(FBlueprintActionDatabaseRegistrar& ActionRegistrar) const { // actions get registered under specific object-keys; the idea is that // actions might have to be updated (or deleted) if their object-key is // mutated (or removed)... here we use the node's class (so if the node // type disappears, then the action should go with it) UClass* ActionKey = GetClass(); // to keep from needlessly instantiating a UBlueprintNodeSpawner, first // check to make sure that the registrar is looking for actions of this type // (could be regenerating actions for a specific asset, and therefore the // registrar would only accept actions corresponding to that asset) if (!ActionRegistrar.IsOpenForRegistration(ActionKey)) { return; } auto CustomizeCueNodeLambda = [](UEdGraphNode* NewNode, bool bIsTemplateNode, FName TagName) { UK2Node_GameplayCueEvent* EventNode = CastChecked<UK2Node_GameplayCueEvent>(NewNode); EventNode->CustomFunctionName = TagName; }; IGameplayTagsModule& GameplayTagsModule = IGameplayTagsModule::Get(); FGameplayTag RootTag = GameplayTagsModule.GetGameplayTagsManager().RequestGameplayTag(FName(TEXT("GameplayCue"))); FGameplayTagContainer CueTags = GameplayTagsModule.GetGameplayTagsManager().RequestGameplayTagChildren(RootTag); // Add a root GameplayCue function as a default CueTags.AddTag(RootTag); for (auto TagIt = CueTags.CreateConstIterator(); TagIt; ++TagIt) { UBlueprintNodeSpawner::FCustomizeNodeDelegate PostSpawnDelegate = UBlueprintNodeSpawner::FCustomizeNodeDelegate::CreateStatic(CustomizeCueNodeLambda, TagIt->GetTagName()); UBlueprintNodeSpawner* NodeSpawner = UBlueprintEventNodeSpawner::Create(GetClass(), TagIt->GetTagName()); check(NodeSpawner != nullptr); NodeSpawner->CustomizeNodeDelegate = PostSpawnDelegate; ActionRegistrar.AddBlueprintAction(ActionKey, NodeSpawner); } }
void FGameplayTagCustomization::OnTagChanged() { TagName = TEXT(""); if (StructPropertyHandle.IsValid() && EditableContainers.Num() > 0) { TArray<void*> RawStructData; StructPropertyHandle->AccessRawData(RawStructData); if (RawStructData.Num() > 0) { FGameplayTag* Tag = (FGameplayTag*)(RawStructData[0]); // Update Tag from the one selected from list FGameplayTagContainer* Container = EditableContainers[0].TagContainer; if (Tag && Container) { for (auto It = Container->CreateConstIterator(); It; ++It) { *Tag = *It; TagName = It->ToString(); } } } } }
void IGameplayCueInterface::HandleGameplayCue(AActor *Self, FGameplayTag GameplayCueTag, EGameplayCueEvent::Type EventType, FGameplayCueParameters Parameters) { SCOPE_CYCLE_COUNTER(STAT_GameplayCueInterface_HandleGameplayCue); // Look up a custom function for this gameplay tag. UClass* Class = Self->GetClass(); IGameplayTagsModule& GameplayTagsModule = IGameplayTagsModule::Get(); FGameplayTagContainer TagAndParentsContainer = GameplayTagsModule.GetGameplayTagsManager().RequestGameplayTagParents(GameplayCueTag); Parameters.OriginalTag = GameplayCueTag; //Find entry for the class FGameplayCueTagFunctionList& GameplayTagFunctionList = PerClassGameplayTagToFunctionMap.FindOrAdd(Class); TArray<FCueNameAndUFunction>* FunctionList = GameplayTagFunctionList.Find(GameplayCueTag); if (FunctionList == NULL) { //generate new function list FunctionList = &GameplayTagFunctionList.Add(GameplayCueTag); for (auto InnerTagIt = TagAndParentsContainer.CreateConstIterator(); InnerTagIt; ++InnerTagIt) { UFunction* Func = NULL; FName CueName = InnerTagIt->GetTagName(); Func = Class->FindFunctionByName(CueName, EIncludeSuperFlag::IncludeSuper); // If the handler calls ForwardGameplayCueToParent, keep calling functions until one consumes the cue and doesn't forward it while (Func) { FCueNameAndUFunction NewCueFunctionPair; NewCueFunctionPair.Tag = *InnerTagIt; NewCueFunctionPair.Func = Func; FunctionList->Add(NewCueFunctionPair); Func = Func->GetSuperFunction(); } // Native functions cant be named with ".", so look for them with _. FName NativeCueFuncName = *CueName.ToString().Replace(TEXT("."), TEXT("_")); Func = Class->FindFunctionByName(NativeCueFuncName, EIncludeSuperFlag::IncludeSuper); while (Func) { FCueNameAndUFunction NewCueFunctionPair; NewCueFunctionPair.Tag = *InnerTagIt; NewCueFunctionPair.Func = Func; FunctionList->Add(NewCueFunctionPair); Func = Func->GetSuperFunction(); } } } //Iterate through all functions in the list until we should no longer continue check(FunctionList); bool bShouldContinue = true; for (int32 FunctionIndex = 0; bShouldContinue && (FunctionIndex < FunctionList->Num()); ++FunctionIndex) { FCueNameAndUFunction& CueFunctionPair = FunctionList->GetData()[FunctionIndex]; UFunction* Func = CueFunctionPair.Func; Parameters.MatchedTagName = CueFunctionPair.Tag; // Reset the forward parameter now, so we can check it after function bForwardToParent = false; IGameplayCueInterface::DispatchBlueprintCustomHandler(Self, Func, EventType, Parameters); bShouldContinue = bForwardToParent; } if (bShouldContinue) { TArray<UGameplayCueSet*> Sets; GetGameplayCueSets(Sets); for (UGameplayCueSet* Set : Sets) { bShouldContinue = Set->HandleGameplayCue(Self, GameplayCueTag, EventType, Parameters); if (!bShouldContinue) { break; } } } if (bShouldContinue) { Parameters.MatchedTagName = GameplayCueTag; GameplayCueDefaultHandler(EventType, Parameters); } }
bool FGameplayTagCountContainer::UpdateTagMap_Internal(const FGameplayTag& Tag, int32 CountDelta) { const bool bTagAlreadyExplicitlyExists = ExplicitTags.HasTag(Tag, EGameplayTagMatchType::Explicit, EGameplayTagMatchType::Explicit); // Need special case handling to maintain the explicit tag list correctly, adding the tag to the list if it didn't previously exist and a // positive delta comes in, and removing it from the list if it did exist and a negative delta comes in. if (!bTagAlreadyExplicitlyExists) { // Brand new tag with a positive delta needs to be explicitly added if (CountDelta > 0) { ExplicitTags.AddTag(Tag); } // Block attempted reduction of non-explicit tags, as they were never truly added to the container directly else { // only warn about tags that are in the container but will not be removed because they aren't explicitly in the container if (ExplicitTags.HasTag(Tag, EGameplayTagMatchType::IncludeParentTags, EGameplayTagMatchType::Explicit)) { ABILITY_LOG(Warning, TEXT("Attempted to remove tag: %s from tag count container, but it is not explicitly in the container!"), *Tag.ToString()); } return false; } } // Update the explicit tag count map. This has to be separate than the map below because otherwise the count of nested tags ends up wrong int32& ExistingCount = ExplicitTagCountMap.FindOrAdd(Tag); ExistingCount = FMath::Max(ExistingCount + CountDelta, 0); // If our new count is 0, remove us from the explicit tag list if (ExistingCount <= 0) { // Remove from the explicit list ExplicitTags.RemoveTag(Tag); } // Check if change delegates are required to fire for the tag or any of its parents based on the count change FGameplayTagContainer TagAndParentsContainer = IGameplayTagsModule::Get().GetGameplayTagsManager().RequestGameplayTagParents(Tag); bool CreatedSignificantChange = false; for (auto CompleteTagIt = TagAndParentsContainer.CreateConstIterator(); CompleteTagIt; ++CompleteTagIt) { const FGameplayTag& CurTag = *CompleteTagIt; // Get the current count of the specified tag. NOTE: Stored as a reference, so subsequent changes propogate to the map. int32& TagCountRef = GameplayTagCountMap.FindOrAdd(CurTag); const int32 OldCount = TagCountRef; // Apply the delta to the count in the map int32 NewTagCount = FMath::Max(OldCount + CountDelta, 0); TagCountRef = NewTagCount; // If a significant change (new addition or total removal) occurred, trigger related delegates bool SignificantChange = (OldCount == 0 || NewTagCount == 0); CreatedSignificantChange |= SignificantChange; if (SignificantChange) { OnAnyTagChangeDelegate.Broadcast(CurTag, NewTagCount); } FDelegateInfo* DelegateInfo = GameplayTagEventMap.Find(CurTag); if (DelegateInfo) { // Prior to calling OnAnyChange delegate, copy our OnNewOrRemove delegate, since things listening to OnAnyChange could add or remove // to this map causing our pointer to become invalid. FOnGameplayEffectTagCountChanged OnNewOrRemoveLocalCopy = DelegateInfo->OnNewOrRemove; DelegateInfo->OnAnyChange.Broadcast(CurTag, NewTagCount); if (SignificantChange) { OnNewOrRemoveLocalCopy.Broadcast(CurTag, NewTagCount); } } } return CreatedSignificantChange; }