Thats great, think using tags would be better, its dynamic, hardcoding names is not very great right? However it really depends how big the game is, you have humanoids, 4 legged ones, arachnas, monsters then better do tags 
Below is something I did similar just to show, how tags requested from a specific effect and what would happen in result. This is a code block of my game for a test that I did a projectile hit application of vampiric damage over time normally just changed it to get a magnitude and attribute tag and search attribute in another class, then make a change on attribute if found. So don’t be bothered by the name of the class.
// Mono Games Studio
#include "Execution/MGGEC_Vampiric.h"
#include "AbilitySystemComponent.h"
#include "GameplayEffectTypes.h"
#include "MGAttributeHelpers.h"
#include "Kismet/GameplayStatics.h"
UMGGEC_Vampiric::UMGGEC_Vampiric()
{
LifestealPercent = 1.0f; // default 100% lifesteal
//Seems I was going to make this also tag for different scales of application
}
void UMGGEC_Vampiric::Execute_Implementation(
const FGameplayEffectCustomExecutionParameters& ExecutionParams,
FGameplayEffectCustomExecutionOutput& OutExecutionOutput
) const
{
const FGameplayEffectSpec& Spec = ExecutionParams.GetOwningSpec();
UAbilitySystemComponent* SourceASC = ExecutionParams.GetSourceAbilitySystemComponent();
UAbilitySystemComponent* TargetASC = ExecutionParams.GetTargetAbilitySystemComponent();
if (!TargetASC) return;
// 1) Magnitude from injected SetByCaller . Basically we are looking for an impact data, did this take damage at all ?
float Value = Spec.GetSetByCallerMagnitude(
FGameplayTag::RequestGameplayTag("Gameplay.Data.Magnitude"),
false,
0.f
);
if (FMath::IsNearlyZero(Value))
{
UE_LOG(LogTemp, Warning, TEXT("[Vampiric] No Gameplay.Data.Magnitude in spec"));
return;
}
// 2) Attribute tag from injected DynamicAssetTags, This simply can be the limbs. I look if a tag for specific thing is forwarded to me and search in it. Maybe its not the best way however it works.
FGameplayTagContainer AssetTags;
Spec.GetAllAssetTags(AssetTags);
FGameplayTag AttributeTag;
for (const FGameplayTag& T : AssetTags)
{
if (T.ToString().StartsWith("Gameplay.Data.Attribute"))
{
AttributeTag = T;
break;
}
}
if (!AttributeTag.IsValid())
{
UE_LOG(LogTemp, Warning, TEXT("[Vampiric] No Gameplay.Data.Attribute.* tag in spec"));
return;
}
// 3) Ok so we have now the tag execution didn't stop now I go another class which is a dictionary for the attributes. FMGAttributeHelpers this can be the class where searches and holds all attributes of limbs in the actor. It better to be a running one somewhere so it doesn't search something heavy for each hit, which can be very demanding.
const FGameplayAttribute TargetAttr = FMGAttributeHelpers::ResolveAttributeFromTag(AttributeTag, TargetASC);
if (!TargetAttr.IsValid()) return;
OutExecutionOutput.AddOutputModifier(
FGameplayModifierEvaluatedData(
TargetAttr,
EGameplayModOp::Additive,
Value
)
);
}
You can have attributes like
Gameplay.Data.Attribute.Hit.Data.Location.Head
Gameplay.Data.Attribute.Hit.Data.Location.LeftArm
Gameplay.Data.Attribute.Hit.Data.Location.Carapace
Gameplay.Data.Attribute.Hit.Data.Location.Core
Helper can be more nicer for sure to designer add remove attribute tags with ease or a dictionary manager class so they can map whatever tag matches whatever attribute. It can provide some nice playground and experimentation aswell that time. Like 1 tag can effect all ?
if (AttributeTag == FGameplayTag::RequestGameplayTag("Gameplay.Data.Attribute.Hit.Data.Location.Head"))
{
if (FProperty* Prop = BPClass->FindPropertyByName(TEXT("Attr_Health_Head")))
return FGameplayAttribute(Prop);
}
Then life is easy,
Hit system : Adds tags magnitude, attribute to an effect spec. This can be a projectile impact, hitscan, overlap of melee whatever. Its a hit. GameplayEffectSpec applied on hit.
Gas (Execution) : Handles the impact, you don’t apply damage directly but let GAS do its job. Resolve tags and make the calculation. Since we are in GAS now it will do its job, validate, run with its own terms and come to execution to do the real job.
The trick over here when you have many execution classes for different aspects handling the order correctly or handling inputs and outputs in order.
PS : For a weapon, projectile and impact system, you don’t really to have ASC per weapon. ASC can be just one on the deployer (character / enemy / object), the tags and data can be carried over to impact without the abilityy system component. Upon impact (if hit and something needs to be done) you can simply go to the instigator ASC who is responsible already for the hit and request an effect to the impacted actor through the impact making actor.
My advice is to have one ASC per weaponCore / Deployer. Deployment can be a single nicely designed ability (preferable a custom one) that runs sub abilities, since deployment styles (actuation,rapid, burst, instant, heating, bolt action, dual action etc.) and methods (hitscan, projectile, continious) can change and child abilities can control those with ease, you don’t really need multiple ASC. Weapons change is a child ability that runs the master weapon ability to do all the black magic.
Happy developing, let us know how it goes.