Files
Cielonos/Assets/Scripts/MainGame/Characters/Automata/Enemies/C1_Axe.cs

244 lines
11 KiB
C#
Raw Normal View History

2026-06-27 12:52:03 -04:00
using System.Collections.Generic;
using Cielonos.MainGame.AttackArea;
using Cielonos.MainGame.Buffs.Character;
using Lean.Pool;
using SLSUtilities.FunctionalAnimation;
using SLSUtilities.General;
using SLSUtilities.WwiseAssistance;
using UnityEngine;
namespace Cielonos.MainGame.Characters
{
public partial class C1_Axe : Enemy
{
protected override void InitializeSubmodules()
{
base.InitializeSubmodules();
// Boss 怯战惩罚机制5秒未受玩家攻击且未被玩家格挡每秒快速恢复20点能量
new CowardicePenalty().Apply(this);
eventSm.onGetBreakthrough.Add("GetBreakthrough_Weak", new PrioritizedAction<AttackAreaBase>(attackArea =>
{
new Weak(5).Apply(this);
GetHit(Breakthrough.Type.Forced, out _, DisruptionType.ForcedExternal, funcAnimName: "GetBreakthrough");
movementSc.impulseSm.ApplyKnockback(-transform.forward, 10f, 1f, null, true);
}));
}
}
public partial class C1_Axe
{
private void FAPF_GenerateSlash(RuntimeFuncAnim rtFuncAnim)
{
CustomFunction.PC_StringString p = rtFuncAnim.GetParams<CustomFunction.PC_StringString>();
GenerateSlash(p.str0, attackData[p.str1]);
}
private void FAPF_GenerateSmash(RuntimeFuncAnim rtFuncAnim)
{
CustomFunction.PC_StringString p = rtFuncAnim.GetParams<CustomFunction.PC_StringString>();
GenerateSmash(p.str0, attackData[p.str1]);
}
private void FAPF_GenerateTripleSmashes(RuntimeFuncAnim rtFuncAnim)
{
CustomFunction.PC_StringString p = rtFuncAnim.GetParams<CustomFunction.PC_StringString>();
float timeInterval = 0.2f;
Vector3 fwd = transform.forward;
for (int i = 0; i < 3; i++)
{
float delay = i * timeInterval;
int index = i;
selfTimeSm.AddLocalTimer(delay, () => GenerateSmash(p.str0, attackData[p.str1], fwd * (index * 5f), fwd));
}
}
private void FAPF_GenerateWhirlWind(RuntimeFuncAnim rtFuncAnim)
{
CustomFunction.PC_StringStringFloat p = rtFuncAnim.GetParams<CustomFunction.PC_StringStringFloat>();
GenerateWhirlWind(p.str0, attackData[p.str1], p.float0);
}
private void FAPF_GenerateMeteorStorm(RuntimeFuncAnim rtFuncAnim)
{
CustomFunction.PC_StringStringFloat p = rtFuncAnim.GetParams<CustomFunction.PC_StringStringFloat>();
string vfxName = p.str0;
string attackKey = p.str1;
float radius = p.float0;
AttackUnit attackUnit = attackData[attackKey];
for (int i = 0; i < 15; i++)
{
float delay = i * 1.0f; // 每秒生成一个,持续 15 秒
int index = i;
selfTimeSm.AddLocalTimer(delay, () =>
{
// 动态获取玩家当前状态
if (player == null || player.statusSm.isDead) return;
Vector3 targetPos;
if ((index + 2) % 5 == 0) // 每5个陨石中的第4个直接落在玩家当前位置增加难度
{
targetPos = player.transform.position;
}
else if ((index + 1) % 5 == 0) //每5个陨石的第5个预测玩家位置增加难度
{
targetPos = PredictPlayerPosition(0.85f);
}
else
{
// 随机在玩家周身一定半径内
Vector2 randomOffset = Random.insideUnitCircle * radius;
targetPos = player.transform.position + new Vector3(randomOffset.x, 0f, randomOffset.y);
}
// 利用 NavMesh 进行投影对齐,防止指示器浮空或生成在墙壁外部
if (UnityEngine.AI.NavMesh.SamplePosition(targetPos, out UnityEngine.AI.NavMeshHit hit, radius, UnityEngine.AI.NavMesh.AllAreas))
{
targetPos = hit.position;
}
GenerateMeteor(vfxName, attackUnit, targetPos);
// 额外生成第二枚陨石,避免与第一枚重合
List<Vector3> extraMeteors = AttackAreaDistributionHelper.GenerateNonOverlappingInCircle(
player.transform.position, radius, 4f, 1, new List<Vector3> { targetPos });
foreach (Vector3 pos in extraMeteors)
{
GenerateMeteor(vfxName, attackUnit, pos);
}
});
}
}
private void FAPF_GenerateMovingSlashes(RuntimeFuncAnim rtFuncAnim)
{
CustomFunction.PC_StringStringInt p = rtFuncAnim.GetParams<CustomFunction.PC_StringStringInt>();
string vfxName = p.str0;
string attackKey = p.str1;
int count = p.int0;
if (count <= 0) return;
Vector3 baseDir = transform.forward;
if (player != null && !player.statusSm.isDead)
{
baseDir = (player.transform.position - transform.position).normalized;
baseDir.y = 0;
if (baseDir == Vector3.zero) baseDir = transform.forward;
baseDir.Normalize();
}
for (int i = 0; i < count; i++)
{
float angle = (i - (count - 1) / 2.0f) * 30f;
Vector3 slashDir = Quaternion.Euler(0, angle, 0) * baseDir;
Vector3 upFix = Vector3.up * 1.5f;
GenerateMovingSlash(vfxName, attackData[attackKey], transform.position + upFix + slashDir * 2f, slashDir, 10f, 10f);
}
}
}
public partial class C1_Axe
{
private NormalArea GenerateSlash(string vfxName, AttackUnit attackUnit)
{
NormalArea slash = vfxData.SpawnVFX(vfxName, this).GetComponentInChildren<NormalArea>();
slash.Initialize<NormalArea>(this, Fraction.Player)
.SetAttackSubmodule<NormalArea>(attackUnit)
.SetTimeSubmodule<NormalArea>(3f)
.SetHitSubmodule<NormalArea>();
slash.SetImpulseSubmodule().WithRepulsion(3);
slash.hitSm.AddHitSound(AK.EVENTS.C1_AXE_SWINGHIT);
return slash;
}
private NormalArea GenerateSmash(string vfxName, AttackUnit attackUnit,
Vector3 positionOffset = default, Vector3 force = default)
{
NormalArea slash = vfxData.SpawnVFX(vfxName, this).GetComponentInChildren<NormalArea>();
slash.Initialize<NormalArea>(this, Fraction.Player)
.SetAttackSubmodule<NormalArea>(attackUnit)
.SetTimeSubmodule<NormalArea>(5f)
.SetHitSubmodule<NormalArea>();
slash.SetImpulseSubmodule();
if (force == default)
{
slash.impulseSm.WithRepulsion(8f);
}
else
{
slash.impulseSm.WithCustomForce(force.normalized * 8f);
}
slash.topParent.position += positionOffset;
AudioManager.Post(AK.EVENTS.C1_AXE_SMASH, slash.gameObject);
return slash;
}
private NormalArea GenerateWhirlWind(string vfxName, AttackUnit attackUnit, float duration)
{
NormalArea slash = vfxData.SpawnVFX(vfxName, this).GetComponentInChildren<NormalArea>();
slash.Initialize<NormalArea>(this, Fraction.Player)
.SetAttackSubmodule<NormalArea>(attackUnit)
.SetTimeSubmodule<NormalArea>(1.3f, 0.04f, 0.86f)
.SetHitSubmodule<NormalArea>(0.2f, 4);
slash.SetImpulseSubmodule().WithSuction(2);
slash.hitSm.AddHitSound(AK.EVENTS.C1_AXE_SWINGHIT);
var topParticle = slash.topParent.GetComponent<ParticleSystem>();
topParticle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear);
foreach (ParticleSystem particle in slash.topParent.GetComponent<VFXObject>().particles)
{
var main = particle.main;
main.duration = duration;
}
topParticle.Play();
return slash;
}
private void GenerateMeteor(string vfxName, AttackUnit attackUnit, Vector3 targetPosition)
{
// 直接生成包含指示器与陨石的单个特效
NormalArea meteor = vfxData.SpawnVFX(vfxName, this, targetPosition, Quaternion.identity).GetComponentInChildren<NormalArea>();
meteor.Initialize<NormalArea>(this, Fraction.Player)
.SetAttackSubmodule<NormalArea>(attackUnit)
.SetTimeSubmodule<NormalArea>(2, 0.85f, enableAction: ()=>
{
AudioManager.Post(AK.EVENTS.NEXUSCRAB_CLAWSTABBLAST, meteor.gameObject);
feedbackSc.PlayFeedback("MeteorImpact");
})
.SetHitSubmodule<NormalArea>();
// 施加冲击力与 repulsion
meteor.SetImpulseSubmodule().WithRepulsion(8);
}
private NormalArea GenerateMovingSlash(string vfxName, AttackUnit attackUnit, Vector3 position,
Vector3 direction, float duration, float speed)
{
NormalArea slash = vfxData.SpawnVFX(vfxName, this, position, Quaternion.LookRotation(direction)).GetComponentInChildren<NormalArea>();
GameObject blockVFX = vfxData.Get(vfxName).hitVFX;
slash.Initialize<NormalArea>(this, Fraction.Player)
.SetAttackSubmodule<NormalArea>(attackUnit)
.SetTimeSubmodule<NormalArea>(duration, 0.2f, duration - 0.2f)
.SetLinearDirectionMoveModule<NormalArea>(direction, speed, 5f, true, true, false, 1f)
.SetHitSubmodule<NormalArea>(); // 默认设置0.5秒判定间隔或单次判定
slash.hitSm.AddHitSound(AK.EVENTS.C1_AXE_SWINGHIT);
slash.SetImpulseSubmodule().WithDynamicForce(3f);
slash.reactionSm.hasPerfectWindowTime = false;
slash.reactionSm.generalBlockAction = blocker =>
{
var blockTransform = VFXObject.Spawn(blockVFX, this, slash.topParent.transform).transform;
blockTransform.SetParent(null);
LeanPool.Despawn(slash.topParent.gameObject);
};
return slash;
}
}
}