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(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(); GenerateSlash(p.str0, attackData[p.str1]); } private void FAPF_GenerateSmash(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringString p = rtFuncAnim.GetParams(); GenerateSmash(p.str0, attackData[p.str1]); } private void FAPF_GenerateTripleSmashes(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringString p = rtFuncAnim.GetParams(); 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(); GenerateWhirlWind(p.str0, attackData[p.str1], p.float0); } private void FAPF_GenerateMeteorStorm(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringStringFloat p = rtFuncAnim.GetParams(); 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 extraMeteors = AttackAreaDistributionHelper.GenerateNonOverlappingInCircle( player.transform.position, radius, 4f, 1, new List { targetPos }); foreach (Vector3 pos in extraMeteors) { GenerateMeteor(vfxName, attackUnit, pos); } }); } } private void FAPF_GenerateMovingSlashes(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringStringInt p = rtFuncAnim.GetParams(); 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(); slash.Initialize(this, Fraction.Player) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(3f) .SetHitSubmodule(); 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(); slash.Initialize(this, Fraction.Player) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(5f) .SetHitSubmodule(); 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(); slash.Initialize(this, Fraction.Player) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(1.3f, 0.04f, 0.86f) .SetHitSubmodule(0.2f, 4); slash.SetImpulseSubmodule().WithSuction(2); slash.hitSm.AddHitSound(AK.EVENTS.C1_AXE_SWINGHIT); var topParticle = slash.topParent.GetComponent(); topParticle.Stop(true, ParticleSystemStopBehavior.StopEmittingAndClear); foreach (ParticleSystem particle in slash.topParent.GetComponent().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(); meteor.Initialize(this, Fraction.Player) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(2, 0.85f, enableAction: ()=> { AudioManager.Post(AK.EVENTS.NEXUSCRAB_CLAWSTABBLAST, meteor.gameObject); feedbackSc.PlayFeedback("MeteorImpact"); }) .SetHitSubmodule(); // 施加冲击力与 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(); GameObject blockVFX = vfxData.Get(vfxName).hitVFX; slash.Initialize(this, Fraction.Player) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(duration, 0.2f, duration - 0.2f) .SetLinearDirectionMoveModule(direction, speed, 5f, true, true, false, 1f) .SetHitSubmodule(); // 默认设置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; } } }