2025-10-26 15:46:04 +08:00
|
|
|
|
using Level.Role;
|
|
|
|
|
|
using UnrealSharp;
|
|
|
|
|
|
using UnrealSharp.Attributes;
|
|
|
|
|
|
using UnrealSharp.BusyRabbit;
|
|
|
|
|
|
using UnrealSharp.CoreUObject;
|
|
|
|
|
|
using UnrealSharp.Engine;
|
|
|
|
|
|
using UnrealSharp.GameplayAbilities;
|
|
|
|
|
|
using UnrealSharp.GameplayTags;
|
|
|
|
|
|
using UnrealSharp.SpinePlugin;
|
|
|
|
|
|
|
|
|
|
|
|
namespace GameAbilitySystem.Ability.Role.Fox;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[UClass]
|
|
|
|
|
|
public class UFoxUltimate : UBusyGameAbility
|
|
|
|
|
|
{
|
|
|
|
|
|
protected bool bIsEventBinded = false;
|
|
|
|
|
|
protected ABusyFoxRole? Owner = null;
|
|
|
|
|
|
protected UFoxUltimateDataAsset? FoxData = null;
|
|
|
|
|
|
protected UBusyAbilitySystemComponent? ASC = null;
|
|
|
|
|
|
protected FActiveGameplayEffectHandle CastStateHandle;
|
|
|
|
|
|
protected FActiveGameplayEffectHandle SkillStateHandle;
|
|
|
|
|
|
|
|
|
|
|
|
protected FActiveGameplayEffectHandle AccelerateHandle; // 大招移动速度增加的Effect
|
|
|
|
|
|
protected FActiveGameplayEffectHandle SprintHandle; // 大招三阶移速再次增加的Effect
|
|
|
|
|
|
|
|
|
|
|
|
[UFunction()]
|
|
|
|
|
|
protected void OnGamePlayTagAddOrRemove(FGameplayTag tag, int isAdd)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isAdd == 0 && !IsActive)
|
|
|
|
|
|
{
|
|
|
|
|
|
CommitAbilityCooldown();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 初始化技能依赖的属性
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>false->初始化失败</returns>
|
|
|
|
|
|
protected bool InitAbilityOnce()
|
|
|
|
|
|
{
|
|
|
|
|
|
Owner = OwningActorFromActorInfo as ABusyFoxRole;
|
|
|
|
|
|
if (Owner == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
FoxData = Data as UFoxUltimateDataAsset;
|
|
|
|
|
|
|
|
|
|
|
|
ASC = AbilitySystemLibrary.GetAbilitySystemComponent(Owner) as UBusyAbilitySystemComponent;
|
|
|
|
|
|
if (FoxData == null || ASC == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
PrintString("asdafdasdf");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (bIsEventBinded) return true;
|
|
|
|
|
|
TDelegate<GameplayTagAddOrRemoveDelegate> Delegate = new TDelegate<GameplayTagAddOrRemoveDelegate>();
|
|
|
|
|
|
Delegate.BindUFunction(this, "OnGamePlayTagAddOrRemove");
|
|
|
|
|
|
Owner.BindGameplayTagAddOrRemove(FoxData.SecondStageCastTag, Delegate);
|
|
|
|
|
|
Owner.BindGameplayTagAddOrRemove(FoxData.LastStageCastTag, Delegate);
|
|
|
|
|
|
bIsEventBinded = true;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取当前大招的阶段
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
protected int GetCurrentAbilityStage()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ASC == null || FoxData == null) return 0;
|
|
|
|
|
|
if (ASC.HasMatchingGameplayTag(FoxData.LastStageCastTag))
|
|
|
|
|
|
{
|
|
|
|
|
|
return 3;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ASC.HasMatchingGameplayTag(FoxData.SecondStageCastTag))
|
|
|
|
|
|
{
|
|
|
|
|
|
return 2;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (ASC.HasMatchingGameplayTag(FoxData.FirstStageCastTag))
|
|
|
|
|
|
{
|
|
|
|
|
|
return 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void ActivateAbilityFromEvent(FGameplayEventData eventData)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.ActivateAbilityFromEvent(eventData);
|
|
|
|
|
|
if (!InitAbilityOnce())
|
|
|
|
|
|
{
|
|
|
|
|
|
EndAbility();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Owner == null || !CommitAbilityCost(Owner))
|
|
|
|
|
|
{
|
|
|
|
|
|
EndAbility();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
int stage = GetCurrentAbilityStage();
|
|
|
|
|
|
if (stage != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
TriggerAbility(stage);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
EndAbility();
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 当被授予技能时,立刻获得可释放第一段大招的常驻Tag
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="actorInfo"></param>
|
|
|
|
|
|
/// <param name="spec"></param>
|
|
|
|
|
|
public override void OnGiveAbility(FGameplayAbilityActorInfo actorInfo, FGameplayAbilitySpec spec)
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnGiveAbility(actorInfo, spec);
|
|
|
|
|
|
InitAbilityOnce();
|
|
|
|
|
|
|
|
|
|
|
|
if (FoxData == null || ASC == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
UBusyGameplayEffectSubSystem EffectSubSystem = GetGameInstanceSubsystem<UBusyGameplayEffectSubSystem>();
|
|
|
|
|
|
if (EffectSubSystem == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
UGameplayEffect effect = EffectSubSystem.GetTagGrantEffectWithDuration(
|
|
|
|
|
|
new FGameplayTagContainer(FoxData.FirstStageCastTag),
|
|
|
|
|
|
new FGameplayTagContainer(), 0
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
FGameplayEffectSpecHandle handle = UBusyAscLibrary.MakeGameplayEffectSpecHandle(ASC, effect, 1);
|
|
|
|
|
|
ASC.ApplyGameplayEffectSpecToSelf(handle);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected bool ApplayCastEffect(FGameplayTag CastTag)
|
|
|
|
|
|
{
|
|
|
|
|
|
UBusyGameplayEffectSubSystem EffectSubSystem = GetGameInstanceSubsystem<UBusyGameplayEffectSubSystem>();
|
|
|
|
|
|
if (ASC == null || EffectSubSystem == null || FoxData == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
ASC.RemoveActiveGameplayEffect(CastStateHandle);
|
|
|
|
|
|
|
|
|
|
|
|
if (CastTag.IsValid)
|
|
|
|
|
|
{
|
|
|
|
|
|
UGameplayEffect cast_effect = EffectSubSystem.GetTagGrantEffectWithDuration(
|
|
|
|
|
|
new FGameplayTagContainer(CastTag),
|
|
|
|
|
|
new FGameplayTagContainer(), FoxData.RecastWindow
|
|
|
|
|
|
);
|
|
|
|
|
|
FGameplayEffectSpecHandle CastSpecHandle = UBusyAscLibrary.MakeGameplayEffectSpecHandle(ASC, cast_effect, 1);
|
|
|
|
|
|
CastStateHandle = ASC.ApplyGameplayEffectSpecToSelf(CastSpecHandle);
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
protected bool ApplyEffect(FGameplayTag stateTag, FGameplayTag CastTag)
|
|
|
|
|
|
{
|
|
|
|
|
|
UBusyGameplayEffectSubSystem EffectSubSystem = GetGameInstanceSubsystem<UBusyGameplayEffectSubSystem>();
|
|
|
|
|
|
if (ASC == null || EffectSubSystem == null || FoxData == null) return false;
|
|
|
|
|
|
|
|
|
|
|
|
ApplayCastEffect(CastTag);
|
|
|
|
|
|
|
|
|
|
|
|
ASC.RemoveActiveGameplayEffect(SkillStateHandle);
|
|
|
|
|
|
UGameplayEffect state_effect = EffectSubSystem.GetTagGrantEffectWithDuration(
|
|
|
|
|
|
new FGameplayTagContainer(stateTag),
|
|
|
|
|
|
new FGameplayTagContainer(), 0
|
|
|
|
|
|
);
|
|
|
|
|
|
FGameplayEffectSpecHandle stateSpecHandle = UBusyAscLibrary.MakeGameplayEffectSpecHandle(ASC, state_effect, 1);
|
|
|
|
|
|
SkillStateHandle = ASC.ApplyGameplayEffectSpecToSelf(stateSpecHandle);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
protected bool TriggerAbility(int stage)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (FoxData == null) return false;
|
|
|
|
|
|
if (stage == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!ApplyEffect(FoxData.FirstStageTag, FoxData.SecondStageCastTag)) return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (stage == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!ApplyEffect(FoxData.SecondStageTag, new FGameplayTag())) return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if(stage == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!ApplyEffect(FoxData.LastStageTag, new FGameplayTag())) return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
PlayAnimation(stage);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected string GetAnimationName(FVector2D direction, int stage)
|
|
|
|
|
|
{
|
|
|
|
|
|
string animation_name_prefix = "";
|
|
|
|
|
|
if (direction.X >= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
animation_name_prefix = "Ultimate/Right/";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
animation_name_prefix = "Ultimate/Left/";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (stage == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
return animation_name_prefix + "UltimateStage1";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (stage == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
return animation_name_prefix + "UltimateStage2";
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (stage == 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
return animation_name_prefix + "UltimateStage3";
|
|
|
|
|
|
}
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[UFunction()]
|
|
|
|
|
|
protected void OnAnimationComplete(UTrackEntry Entry)
|
|
|
|
|
|
{
|
|
|
|
|
|
Entry.AnimationComplete.Remove(OnAnimationComplete);
|
|
|
|
|
|
if (ASC != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ASC.HasMatchingGameplayTag(FoxData.LastStageTag))
|
|
|
|
|
|
{
|
|
|
|
|
|
CommitAbilityCooldown();
|
|
|
|
|
|
}
|
|
|
|
|
|
ASC.RemoveActiveGameplayEffect(SkillStateHandle);
|
|
|
|
|
|
}
|
|
|
|
|
|
EndAbility();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[UFunction()]
|
|
|
|
|
|
protected void OnTailBeginOverlay(UPrimitiveComponent OverlappedComponent, AActor OtherActor,
|
|
|
|
|
|
UPrimitiveComponent OtherComp, int OtherBodyIndex, bool bFromSweep, FHitResult SweepResult)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ASC == null || FoxData == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
bool bIsStage2 = ASC.HasMatchingGameplayTag(FoxData.SecondStageTag);
|
|
|
|
|
|
|
|
|
|
|
|
if (bIsStage2 && !ASC.HasMatchingGameplayTag(FoxData.LastStageCastTag))
|
|
|
|
|
|
{
|
|
|
|
|
|
ApplayCastEffect(FoxData.LastStageCastTag);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[UFunction()]
|
|
|
|
|
|
protected void OnAnimationEvent(UTrackEntry Entry, FSpineEvent Event)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ASC == null || FoxData == null || Owner == null) return;
|
|
|
|
|
|
if (Event.Name == "OnSpeedChange")
|
|
|
|
|
|
{
|
|
|
|
|
|
AccelerateHandle = MakeAccelerate(FoxData.LastStageSprintSpeedFactor, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Event.Name == "OnSpeedReset")
|
|
|
|
|
|
{
|
|
|
|
|
|
ASC.RemoveActiveGameplayEffect(AccelerateHandle);
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (Event.Name == "OnDamageBegin")
|
|
|
|
|
|
{
|
|
|
|
|
|
Owner.SetTailCollisionEnabled(true);
|
|
|
|
|
|
Owner.FoxTailCollision.OnComponentBeginOverlap.Add(OnTailBeginOverlay);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
else if (Event.Name == "OnDamageEnd")
|
|
|
|
|
|
{
|
|
|
|
|
|
Owner.SetTailCollisionEnabled(false);
|
|
|
|
|
|
Owner.FoxTailCollision.OnComponentBeginOverlap.Remove(OnTailBeginOverlay);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected FActiveGameplayEffectHandle MakeAccelerate(float SpeedFactor, float TotalTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
GetAbilityEffectSpecHandle("Accelerate", ASC, 1, out FGameplayEffectSpecHandle handle);
|
|
|
|
|
|
AbilitySystemLibrary.AssignTagSetByCallerMagnitude(
|
|
|
|
|
|
handle, UBusyGameplayLibrary.RequestGameplayTag("Effect.Factor"), SpeedFactor
|
|
|
|
|
|
);
|
|
|
|
|
|
AbilitySystemLibrary.AssignTagSetByCallerMagnitude(
|
|
|
|
|
|
handle, UBusyGameplayLibrary.RequestGameplayTag("Effect.Duration"), TotalTime
|
|
|
|
|
|
);
|
|
|
|
|
|
return ApplyGameplayEffectSpecToOwner(handle);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
protected bool PlayAnimation(int stage)
|
|
|
|
|
|
{
|
|
|
|
|
|
FVector2D AbilityDirection = new FVector2D(0, 1);
|
|
|
|
|
|
if (UGameplayStatics.GetPlayerController(0) is ALevelPlayerController PC)
|
|
|
|
|
|
{
|
|
|
|
|
|
PC.GetCursorDirection(out AbilityDirection);
|
|
|
|
|
|
}
|
|
|
|
|
|
string AnimationName = GetAnimationName(AbilityDirection, stage);
|
|
|
|
|
|
if (AnimationName == "") return false;
|
|
|
|
|
|
|
|
|
|
|
|
USpineSkeletonAnimationComponent Animation = Owner.SpineAnimationComponent;
|
|
|
|
|
|
UTrackEntry AnimEntry = Animation.SetAnimation(0, AnimationName, false);
|
|
|
|
|
|
AnimEntry.AnimationEvent.Add(OnAnimationEvent);
|
|
|
|
|
|
AnimEntry.AnimationComplete.Add(OnAnimationComplete);
|
|
|
|
|
|
|
|
|
|
|
|
float TotalTime = AnimEntry.GetAnimationEnd();
|
|
|
|
|
|
|
|
|
|
|
|
if (stage == 1) MakeAccelerate(FoxData.FirstStageSpeedFactor, TotalTime);
|
|
|
|
|
|
else if (stage == 2) MakeAccelerate(FoxData.SecondStageSpeedFactor, TotalTime);
|
|
|
|
|
|
else if (stage == 3) MakeAccelerate(FoxData.LastStageNormalSpeedFactor, TotalTime);
|
|
|
|
|
|
Owner.MovementComponent.ActivateSprint(AbilityDirection, TotalTime);
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-10-25 05:53:20 +08:00
|
|
|
|
|
|
|
|
|
|
// function FoxUltimate:ctor()
|
|
|
|
|
|
// self.ultimate_phase = 1 -- 大招阶段
|
|
|
|
|
|
// self.active_recast_handle = nil
|
|
|
|
|
|
// self.active_accelerate_handle = nil
|
|
|
|
|
|
|
|
|
|
|
|
// self.overlap_delegate_handle = nil
|
|
|
|
|
|
// self.tag_add_or_remove_delegate = nil
|
|
|
|
|
|
// end
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// function FoxUltimate:OnAnimationComplete(entry)
|
|
|
|
|
|
// local new_phase = self.ultimate_phase + 1
|
|
|
|
|
|
// if new_phase > 3 then
|
|
|
|
|
|
// self.ultimate_phase = 1
|
|
|
|
|
|
// else
|
|
|
|
|
|
// self.ultimate_phase = new_phase
|
|
|
|
|
|
// end
|
|
|
|
|
|
// print("FoxUltimate:OnAnimationComplete", self.ultimate_phase)
|
|
|
|
|
|
|
|
|
|
|
|
// if not self.asc:HasMatchingGameplayTag(self.recast_tag) then
|
|
|
|
|
|
// self.ultimate_phase = 1
|
|
|
|
|
|
// self:K2_CommitAbilityCooldown(false, false)
|
|
|
|
|
|
// end
|
|
|
|
|
|
|
|
|
|
|
|
// entry.AnimationComplete:Clear()
|
|
|
|
|
|
// entry.AnimationEvent:Clear()
|
|
|
|
|
|
// self:K2_EndAbility()
|
|
|
|
|
|
// end
|
|
|
|
|
|
|
|
|
|
|
|
|