using System.Collections.Generic; using ChocDino.UIFX; using Cielonos.MainGame.Buffs.Character; using Cielonos.MainGame.Effects.Feedback; using Cielonos.MainGame.UI; using SLSUtilities.General; using SLSUtilities.FunctionalAnimation; using SLSUtilities.WwiseAssistance; using UnityEngine; namespace Cielonos.MainGame.Characters.Inventory.Collections { public partial class Polychrome : MainWeaponBase { public float techniqueScore; private PolychromeExtraUIContainer ExtraUIContainer => extraUIContainer as PolychromeExtraUIContainer; private bool _canAirLightAttack; private bool _canAirHeavyAttack; protected override void Update() { if (player.inventorySc.equipmentSm.currentMainWeapon == this) { functionSm?.Update(player.selfTimeSm.DeltaTime); } } public override void OnEquipped() { base.OnEquipped(); extraUIContainer = Instantiate(extraUIContainerPrefab, PlayerCanvas.MainWeaponUIArea.transform).GetComponent(); extraUIContainer.mainWeapon = this; _currentKatanaParticle = string.Empty; player.eventSm.onFirstJump.Add("PolyChrome_OnFirstJump", new PrioritizedAction(() => { _canAirLightAttack = true; _canAirHeavyAttack = true; comboSm["AirLight"].Reset(); })); player.eventSm.onAfterGetAttacked.Add("PolyChrome_OnAfterGetAttacked", new PrioritizedAction((_, _) => { ModifyTechniqueScore(-0.2f); })); RegisterFunctionsToAnimSc(StayBlocking); viewObjects["Katana"].SetFadeAnim(0.2f); viewObjects["Saya"].SetFadeAnim(0.2f); PlayerCanvas.MainWeaponUIArea.displayer.SetFrameOutline(0.4f); PlayTargetedAnimation("EquipBlock"); SetBlock(equipBlockData); player.selfTimeSm.AddLocalTimer(0.4f, () => { RemoveBlock(equipBlockData); fullBodyFuncAnimSm.Stop("EquipBlock"); }); } public override void OnUnequipped() { base.OnUnequipped(); Destroy(extraUIContainer.gameObject); ClearTechniqueScore(); player.eventSm.onFirstJump.Remove("PolyChrome_OnFirstJump"); player.eventSm.onAfterGetAttacked.Remove("PolyChrome_OnAfterGetAttacked"); } public override void OnPrimaryPress() { if (player.statusSm.HasStatus(StatusType.Stun)) { return; } if (player.reactionSc.blockSm.HaveBlockSource(blockData.blockName)) { return; } if (player.landMovementSc.isJumping) { if (!_canAirLightAttack || !functionSm["LightAttack"].IsAvailable()) { return; } if (PlayTargetedAnimation("AirAttack" + comboSm["AirLight"].GetNextNodeName("L"))) { comboSm["AirLight"].NextCombo("L"); functionSm["LightAttack"].Execute(); if (comboSm["AirLight"].GetCurrentNodeName() == "L1") { _canAirLightAttack = false; } } return; } if (player.inputSc.IsHoldingSpecialA && functionSm["LightAttack"].IsAvailable()) { CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5); if (PlayTargetedAnimation("AttackRC", target, 1f, true)) { comboSm.main.NextCombo("L"); functionSm["LightAttack"].Execute(); } return; } if (player.landMovementSc.isSprinting && functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability()) { comboSm.main.Reset(); functionSm["LightAttack"].Execute(); CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(8); PlayTargetedAnimation("RunAttack", target); comboSm.main.NextCombo("L"); return; } if (functionSm["LightAttack"].IsAvailable()) { CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5); string nextNodeName = comboSm.main.GetNextNodeName("L"); bool keepAdsorption = nextNodeName is "L4" or "L5"; if (PlayTargetedAnimation("Attack" + comboSm.main.GetNextNodeName("L"), target, 1f, keepAdsorption)) { float totalTime = 0f; BattleManager.EnemySm.activeEnemiesList.ForEach(enemy => { (enemy as Enemy).behaviorSc.DispatchContextEvent("PlayerLightAttack", totalTime); }); comboSm.main.NextCombo("L"); functionSm["LightAttack"].Execute(); } } } public override void OnSecondaryPress() { if (player.statusSm.HasStatus(StatusType.Stun)) { return; } List availableEnemies = BattleManager.EnemySm.GetEnemiesInRadius(player.transform.position, 5); //完美格挡+反击 if (player.reactionSc.blockSm.afterPerfectBlockTimer > 0) { player.reactionSc.blockSm.afterPerfectBlockTimer = 0; CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(availableEnemies); PlayTargetedAnimation("BlockParryAttack", target); RemoveBlock(); return; } if (player.reactionSc.blockSm.HaveBlockSource(blockData.blockName)) { return; } //完美闪避+反击 if (player.reactionSc.dodgeSm.afterPerfectDodgeTimer > 0) { player.reactionSc.dodgeSm.afterPerfectDodgeTimer = 0; CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(12); PlayTargetedAnimation("DodgeParryAttack", target); return; } if (player.landMovementSc.isJumping) { if (!_canAirHeavyAttack || !functionSm["HeavyAttack"].IsAvailable()) { return; } if (PlayTargetedAnimation("AirAttackRStart")) { player.landMovementSc.ExtraJump(10f); comboSm.main.Reset(); functionSm["HeavyAttack"].Execute(); _canAirLightAttack = false; _canAirHeavyAttack = false; } return; } if (player.inputSc.IsHoldingSpecialA && functionSm["HeavyAttack"].IsAvailable()) { CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(availableEnemies); string suffix = techniqueScore switch { < 1f => "A", < 3f => "B", >= 3f => "C", _ => "A" }; if (PlayTargetedAnimation("DisruptionAttack" + suffix, target)) { if (BattleManager.EnemySm.GetDisruptableEnemies(availableEnemies).Count > 0) { var timeScaleModifierClip = player.feedbackSc.GetFeedbackData("DisruptionStartup").Clip("Time"); float duration = fullBodyFuncAnimSm.collection["DisruptionAttack" + suffix].Interval(IntervalType.Startup).Duration * 2; timeScaleModifierClip.duration = duration; player.feedbackSc.PlayFeedback("DisruptionStartup"); } if (suffix == "B") { ModifyTechniqueScore(-1, false); } else if (suffix == "C") { ModifyTechniqueScore(-3, false); } comboSm.main.Reset(); functionSm["HeavyAttack"].Execute(); } return; } if (functionSm["HeavyAttack"].IsAvailable()) { CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(availableEnemies); string nextNodeName = comboSm.main.GetNextNodeName("R"); bool keepAdsorption = nextNodeName is "RC"; if (PlayTargetedAnimation("Attack" + nextNodeName, target, 1f, keepAdsorption)) { float totalTime = fullBodyFuncAnimSm.GetIntervalScaledDuration(IntervalType.Startup) - 0.2f; BattleManager.EnemySm.activeEnemiesList.ForEach(enemy => { (enemy as Enemy)!.behaviorSc.DispatchContextEvent("PlayerHeavyAttack", totalTime); }); comboSm.main.NextCombo("R"); functionSm["HeavyAttack"].Execute(); } } } public override void OnSpecialCPress() { comboSm.main.Reset(); SetBlock(); if (!player.statusSm.HasStatus(StatusType.Stun)) { CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5); if (functionSm["Block"].IsAvailable() && fullBodyFuncAnimSm.Stop(DisruptionType.ForcedAction)) { PlayTargetedAnimation("Block", target, 2f, false, true, upperBodyFuncAnimSm, 1f, 0.1f, true); } } } public override void OnSpecialCRelease() { if (upperBodyFuncAnimSm.currentRuntimeFuncAnim is { animationName: "Block" }) { upperBodyFuncAnimSm.Stop(DisruptionType.ForcedAction); } player.selfTimeSm.AddLocalTimer(0.04f, () => RemoveBlock()); } public override void OnSpecialBPress() { //测试完美格挡和完美闪避 /*SetBlock(); player.reactionSc.blockSm.GetCurrentBlockSource().PerfectBlock(null, player.centerPosition); RemoveBlock();*/ /*DodgeSource defaultDodge = DodgeSource.Default(player); player.reactionSc.dodgeSm.ApplyDodge(defaultDodge); player.reactionSc.dodgeSm.GetCurrentDodgeSource().PerfectDodge(); player.reactionSc.dodgeSm.RemoveDodge("DefaultDodge");*/ } } public partial class Polychrome { private void FAPF_GenerateNormalSlash(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringString p = rtFuncAnim.GetParams(); string hitFeedback = p.str1 switch { "ProbingAttack" => "SingleNormalHit", _ => "MultiNormalHit" }; GenerateNormalSlash(p.str0, attackData[p.str1], hitFeedback); } private void FAPF_GenerateHeavySlash(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringString p = rtFuncAnim.GetParams(); GenerateHeavySlash(p.str0, attackData[p.str1]); } private void FAPF_GenerateUltimateSlash(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringString p = rtFuncAnim.GetParams(); GenerateUltimateSlash(p.str0, attackData[p.str1]); } private void FAPF_GenerateDisruptionSlash(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringString p = rtFuncAnim.GetParams(); GenerateDisruptionSlash(p.str0, attackData[p.str1]); } private void FAPF_GenerateMovingSlash(RuntimeFuncAnim rtFuncAnim) { CustomFunction.PC_StringString p = rtFuncAnim.GetParams(); GenerateDisruptionSlash(p.str0, attackData[p.str1]); } } public partial class Polychrome { private NormalArea GenerateNormalSlash(string vfxName, AttackUnit attackUnit, string hitFeedback) { NormalArea slash = vfxData.SpawnVFX(vfxName).GetComponentInChildren(); slash.Initialize(player, this, Fraction.Enemy) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(1f, 0.02f) .SetHitSubmodule() .SetForceSubmodule(1f, true); slash.attackSm.breakthroughAction = (enemy, hitPosition) => { AudioManager.Post(AK.EVENTS.DISRUPT, hitPosition); }; slash.hitSm.AddHitSound(AK.EVENTS.POLYCHROME_LIGHTATTACKHIT) .AddHitEvent((enemy, hitPosition) => { var positionShakeAction = feedbackSc.GetFeedbackData(hitFeedback).Action("Camera"); float magnitude = hitFeedback == "SingleNormalHit" ? 0.12f : 0.06f; positionShakeAction.amplitude = vfxData.Get(vfxName).slashScreenPosition.normalized * magnitude; feedbackSc.PlayFeedback(hitFeedback); ModifyTechniqueScore(0.02f); if (attackUnit.unitName == "InstantAttack") { if (enemy.buffSm.HasBuff()) { slash.attackSm.attackValue.damage *= 2f; } } }); return slash; } private NormalArea GenerateHeavySlash(string vfxName, AttackUnit attackUnit) { NormalArea slash = vfxData.SpawnVFX(vfxName).GetComponentInChildren(); slash.Initialize(player, this, Fraction.Enemy) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(1f, 0.04f) .SetHitSubmodule() .SetForceSubmodule(3f, true); slash.attackSm.breakthroughAction = (enemy, hitPosition) => { ModifyTechniqueScore(0.2f); AudioManager.Post(AK.EVENTS.DISRUPT, hitPosition); }; slash.hitSm.AddHitSound(AK.EVENTS.POLYCHROME_HEAVYATTACKLHIT) .AddHitEvent((enemy, hitPosition) => { var positionShakeAction = feedbackSc.GetFeedbackData("HeavyHit").Action("Camera"); positionShakeAction.amplitude = vfxData.Get(vfxName).slashScreenPosition.normalized * 0.18f; feedbackSc.PlayFeedback("HeavyHit"); ModifyTechniqueScore(0.05f); }); return slash; } private NormalArea GenerateDisruptionSlash(string vfxName, AttackUnit attackUnit) { NormalArea slash = vfxData.SpawnVFX(vfxName).GetComponentInChildren(); slash.Initialize(player, this, Fraction.Enemy) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(1f, 0.04f) .SetHitSubmodule() .SetForceSubmodule(3f, true); slash.attackSm.breakthroughAction = (enemy, hitPosition) => { ModifyTechniqueScore(0.2f); feedbackSc.PlayFeedback("Breakthrough"); AudioManager.Post(AK.EVENTS.DISRUPT, hitPosition); }; slash.hitSm .AddHitSound(AK.EVENTS.POLYCHROME_HEAVYATTACKLHIT) .AddHitEvent((enemy, hitPosition) => { var positionShakeAction = feedbackSc.GetFeedbackData("HeavyHit").Action("Camera"); positionShakeAction.amplitude = vfxData.Get(vfxName).slashScreenPosition.normalized * 0.18f; feedbackSc.PlayFeedback("HeavyHit"); }); return slash; } private NormalArea GenerateUltimateSlash(string vfxName, AttackUnit attackUnit) { NormalArea slash = vfxData.SpawnVFX(vfxName).GetComponentInChildren(); slash.Initialize(player, this, Fraction.Enemy) .SetAttackSubmodule(attackUnit) .SetTimeSubmodule(1f, 0.04f) .SetHitSubmodule() .SetForceSubmodule(6f, true); slash.attackSm.breakthroughAction = (enemy, hitPosition) => { AudioManager.Post(AK.EVENTS.DISRUPT, hitPosition); }; slash.hitSm .AddHitSound(AK.EVENTS.POLYCHROME_HEAVYATTACKLHIT) .AddHitEvent((enemy, hitPosition) => { var positionShakeAction = feedbackSc.GetFeedbackData("HeavyHit").Action("Camera"); positionShakeAction.amplitude = vfxData.Get(vfxName).slashScreenPosition.normalized * 0.18f; feedbackSc.PlayFeedback("HeavyHit"); feedbackSc.PlayFeedback("Breakthrough"); }); return slash; } } public partial class Polychrome { public BlockData equipBlockData; private string _blockAnimName = "BlockL"; private void SetBlock(BlockData blockData = null) { blockData ??= this.blockData; BlockSource blockSource = blockData.CreateBlockSource(player, this); player.landMovementSc.canDash = false; player.landMovementSc.canDodge = false; blockSource.onNormalBlock = (attackArea) => { _blockAnimName = _blockAnimName == "BlockL" ? "BlockR" : "BlockL"; animationSc.fullBodyFuncAnimSm.Play(_blockAnimName, 1, 0); var rotationShakeAction = feedbackSc.GetFeedbackData("NormalBlock").Action("Camera"); rotationShakeAction.amplitude = _blockAnimName == "BlockL" ? new Vector3(-0f, -2f, 1f) : new Vector3(-0f, 2f, -1f); feedbackSc.PlayFeedback("NormalBlock"); if (attackArea is NormalArea) { ModifyTechniqueScore(-0.1f); } }; blockSource.onPerfectBlock = (attackArea) => { _blockAnimName = _blockAnimName == "BlockL" ? "BlockR" : "BlockL"; animationSc.fullBodyFuncAnimSm.Play(_blockAnimName, 1, 0); var rotationShakeAction = feedbackSc.GetFeedbackData("PerfectBlock").Action("Camera"); rotationShakeAction.amplitude = _blockAnimName == "BlockL" ? new Vector3(0f, -4f, 2f) : new Vector3(0f, 4f, -2f); feedbackSc.PlayFeedback("PerfectBlock"); if (attackArea is NormalArea) { ModifyTechniqueScore(0.2f); } }; player.reactionSc.blockSm.ApplyBlock(blockSource); } private void StayBlocking() { if (player.inputSc.IsHoldingSpecialB) { player.movementSc.canMove.Modify(true); player.movementSc.canRotate.Modify(true); OnSpecialBPress(); } } private void RemoveBlock(BlockData blockData = null) { blockData ??= this.blockData; player.landMovementSc.canDash = true; player.landMovementSc.canDodge = true; player.reactionSc.blockSm.RemoveBlock(blockData.blockName); } } public partial class Polychrome { private string _currentKatanaParticle; /// /// 清空技巧分数 /// private void ClearTechniqueScore() { techniqueScore = 0; UpdateTechniqueVisuals(); } /// /// 增减技巧分数(0~3),当减少时不会扣除整数部分 /// private void ModifyTechniqueScore(float amount, bool protectInteger = true) { if (amount > 0) { techniqueScore = Mathf.Clamp(techniqueScore + amount, 0, 5); } else { float floor = Mathf.Floor(techniqueScore); if (protectInteger) { techniqueScore = Mathf.Max(techniqueScore + amount, floor); } else { techniqueScore = Mathf.Max(techniqueScore + amount, 0); } } UpdateTechniqueVisuals(); } private void UpdateTechniqueVisuals() { //开关粒子特效 if (techniqueScore < 1) { if (_currentKatanaParticle != string.Empty) { viewObjects["Katana"].functionalParts[_currentKatanaParticle].GetComponent().Stop(); _currentKatanaParticle = string.Empty; PlayerCanvas.MainWeaponUIArea.displayer.frame.GetComponent().Strength = 0f; } } else if (techniqueScore >= 1 && techniqueScore < 3) { if (_currentKatanaParticle != "ParticleStage0") { if (_currentKatanaParticle != string.Empty) { viewObjects["Katana"].functionalParts[_currentKatanaParticle].GetComponent().Stop(); } _currentKatanaParticle = "ParticleStage0"; viewObjects["Katana"].functionalParts[_currentKatanaParticle].GetComponent().Play(); PlayerCanvas.MainWeaponUIArea.displayer.frame.GetComponent().Strength = 0.3f; } } else if (techniqueScore >= 3) { if (_currentKatanaParticle != "ParticleStage1") { if (_currentKatanaParticle != string.Empty) { viewObjects["Katana"].functionalParts[_currentKatanaParticle].GetComponent().Stop(); } _currentKatanaParticle = "ParticleStage1"; viewObjects["Katana"].functionalParts[_currentKatanaParticle].GetComponent().Play(); PlayerCanvas.MainWeaponUIArea.displayer.frame.GetComponent().Strength = 0.5f; } } //设置UI星星数量 ExtraUIContainer.SetStars(Mathf.FloorToInt(techniqueScore)); } } }