FututeWand初步
This commit is contained in:
@@ -13,6 +13,9 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
|
||||
private NormalArea _activeSpinArea;
|
||||
|
||||
private VFXObject _spinAreaChargeVFX;
|
||||
private int _spinAreaUpgradeCount = 0;
|
||||
|
||||
private Transform Muzzle => viewObjects["Wand"].functionalParts["Muzzle"].transform;
|
||||
|
||||
public override void OnEquipped()
|
||||
@@ -45,32 +48,44 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
RecallActiveSpinArea();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻攻击(普攻)输入处理
|
||||
/// </summary>
|
||||
public override void OnPrimaryPress()
|
||||
{
|
||||
// 判定是否满足特殊蓄力攻击条件
|
||||
if (!_isHoldingAttack && player.inputSc.IsHoldingSpecialA)
|
||||
{
|
||||
PlayTargetedAnimation("HoldAttackStart", currentTarget);
|
||||
_isHoldingAttack = true;
|
||||
if (PlayHoldAttackStart(currentTarget))
|
||||
{
|
||||
_isHoldingAttack = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 常规地面轻攻击连击
|
||||
if (functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.main.NextCombo("L");
|
||||
functionSm["LightAttack"].Execute();
|
||||
currentTarget = CombatManager.EnemySm.GetNearestEnemy(25f);
|
||||
PlayTargetedAnimation("Attack" + comboSm.main.GetCurrentNodeName(), currentTarget, 5f);
|
||||
string nextNodeName = comboSm.main.GetNextNodeName("L");
|
||||
if (PlayNormalAttackL(currentTarget, nextNodeName))
|
||||
{
|
||||
comboSm.main.NextCombo("L");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻攻击释放处理
|
||||
/// </summary>
|
||||
public override void OnPrimaryRelease()
|
||||
{
|
||||
if (_isHoldingAttack)
|
||||
{
|
||||
string animName = player.animationSc.fullBodyFuncAnimSm.currentRuntimeFuncAnim?.animationName;
|
||||
if(animName == "HoldAttackLoop" || animName == "HoldAttackStart")
|
||||
if (animName == "HoldAttackLoop" || animName == "HoldAttackStart")
|
||||
{
|
||||
PlayTargetedAnimation("HoldAttackEnd");
|
||||
PlayHoldAttackEnd();
|
||||
}
|
||||
RecallActiveSpinArea();
|
||||
}
|
||||
@@ -78,30 +93,31 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
_isHoldingAttack = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重攻击输入处理
|
||||
/// </summary>
|
||||
public override void OnSecondaryPress()
|
||||
{
|
||||
if (_isHoldingAttack) return;
|
||||
|
||||
// 常规地面重攻击连击
|
||||
if (functionSm["HeavyAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.main.NextCombo("R");
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
currentTarget = CombatManager.EnemySm.GetNearestEnemy(25f);
|
||||
PlayTargetedAnimation("Attack" + comboSm.main.GetCurrentNodeName(), currentTarget, 5f);
|
||||
string nextNodeName = comboSm.main.GetNextNodeName("R");
|
||||
if (PlayNormalAttackR(currentTarget, nextNodeName))
|
||||
{
|
||||
comboSm.main.NextCombo("R");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class FutureWand
|
||||
{
|
||||
private void RecallActiveSpinArea()
|
||||
{
|
||||
if (_activeSpinArea?.moveSm is BoomerangMoveSubmodule boomerangSm)
|
||||
{
|
||||
boomerangSm.TriggerReturn(20, 10, float.MaxValue, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 玩家受击回调:中断当前的蓄力行为,并收回飞盘法阵
|
||||
/// </summary>
|
||||
private void OnPlayerGetHit(AttackAreaBase attackArea)
|
||||
{
|
||||
if (_isHoldingAttack)
|
||||
@@ -109,7 +125,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
string animName = player.animationSc.fullBodyFuncAnimSm.currentRuntimeFuncAnim?.animationName;
|
||||
if (animName == "HoldAttackLoop" || animName == "HoldAttackStart")
|
||||
{
|
||||
PlayTargetedAnimation("HoldAttackEnd");
|
||||
PlayHoldAttackEnd();
|
||||
}
|
||||
_isHoldingAttack = false;
|
||||
}
|
||||
|
||||
@@ -42,9 +42,39 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
GenerateGroundArea(p.str0, attackData[p.str1]);
|
||||
}
|
||||
|
||||
private void FAPF_GenerateSpinAreaCharge(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
_spinAreaChargeVFX = vfxData.SpawnVFX("SpinAreaCharge", player).GetComponent<VFXObject>();
|
||||
ParticleSystem coreParticle = _spinAreaChargeVFX?.parts["Core"].GetComponent<ParticleSystem>();
|
||||
_spinAreaUpgradeCount = 0;
|
||||
if (coreParticle != null)
|
||||
{
|
||||
var main = coreParticle.main;
|
||||
main.startSize = 0.5f;
|
||||
}
|
||||
}
|
||||
|
||||
private void FAPF_GenerateWandMuzzle(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
vfxData.SpawnMuzzleVFX("SpinAreaCharge", player, Muzzle);
|
||||
}
|
||||
|
||||
private void FAPF_UpgradeSpinArea(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
ParticleSystem coreParticle = _spinAreaChargeVFX?.parts["Core"].GetComponent<ParticleSystem>();
|
||||
if (coreParticle != null)
|
||||
{
|
||||
var main = coreParticle.main;
|
||||
_spinAreaUpgradeCount++;
|
||||
_spinAreaUpgradeCount = Mathf.Clamp(_spinAreaUpgradeCount, 0, 2);
|
||||
main.startSize = 0.5f + _spinAreaUpgradeCount * 1f;
|
||||
}
|
||||
}
|
||||
|
||||
private void FAPF_GenerateSpinArea(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
CustomFunction.PC_StringString p = rtFuncAnim.GetParams<CustomFunction.PC_StringString>();
|
||||
_spinAreaChargeVFX?.StopAndDespawn();
|
||||
GenerateSpinArea(p.str0, attackData[p.str1]);
|
||||
}
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
area.hitSm.AddHitSound(AK.EVENTS.FUTUREWAND_MEDIUMATTACKHIT);
|
||||
|
||||
AudioManager.Post(AK.EVENTS.FUTUREWAND_ULTIMATEBEAMPOWERUP, area.gameObject);
|
||||
area.timeSm.AddScheduleAction(() =>
|
||||
area.AddScheduleAction(() =>
|
||||
{
|
||||
AudioManager.Post(AK.EVENTS.FUTUREWAND_ULTIMATEBEAMSHOOT);
|
||||
feedbackSc.PlayFeedback("UltimateBeamShoot");
|
||||
@@ -146,28 +146,28 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
|
||||
private void GenerateSpinArea(string vfxName, AttackUnit attackUnit)
|
||||
{
|
||||
AttackUnit upgradedAttackUnit = attackUnit.Clone();
|
||||
float damageMultiplier = 1f + _spinAreaUpgradeCount * 0.25f;
|
||||
upgradedAttackUnit.startDamage *= damageMultiplier;
|
||||
|
||||
vfxData.SpawnMuzzleVFX(vfxName, player, Muzzle);
|
||||
NormalArea area = vfxData.SpawnVFX(vfxName, player).GetComponentInChildren<NormalArea>();
|
||||
AudioManager.Post(AK.EVENTS.FUTUREWAND_GROUNDAREA, area.gameObject);
|
||||
|
||||
area.Initialize<NormalArea>(player, this, Fraction.Enemy)
|
||||
.SetAttackSubmodule<NormalArea>(attackUnit)
|
||||
.SetAttackSubmodule<NormalArea>(upgradedAttackUnit)
|
||||
.SetTimeSubmodule<NormalArea>(int.MaxValue, 0.5f, int.MaxValue, null, Shrink)
|
||||
.SetHitSubmodule<NormalArea>(0.5f, int.MaxValue)
|
||||
.SetHitSubmodule<NormalArea>(0.25f, int.MaxValue)
|
||||
.SetBoomerangMoveSubmodule<NormalArea>(currentTarget, 10f, 0f, 360f, 0f, player.transform.forward)
|
||||
.SetTransformSubmodule<NormalArea>();
|
||||
|
||||
area.SetBoomerangMoveSubmodule(currentTarget, 10f, 360f, player.transform.forward)
|
||||
.WithAutoReturn(float.MaxValue)
|
||||
.WithCatch(1.5f, Shrink)
|
||||
.WithTargeting(true, 25f)
|
||||
.WithKeepFlat(true);
|
||||
|
||||
(area.moveSm as BoomerangMoveSubmodule)!.WithAutoReturn(5f + _spinAreaUpgradeCount * 2.5f).WithCatch(1.5f, Shrink).WithTargeting(true, 25f).WithKeepFlat(true).WithReturnParameters(20f, 10f, float.MaxValue, 0f);
|
||||
area.transformSm.ApplyScaleMove(Vector3.zero, Vector3.one * 3, 0.5f, EaseType.OutQuad);
|
||||
|
||||
area.SetImpulseSubmodule().WithSuction(4f);
|
||||
area.hitSm.AddHitSound(AK.EVENTS.FUTUREWAND_WEAKATTACKHIT);
|
||||
|
||||
int fusionStack = attackUnit.GetSubmodule<AttackUnit.ParameterSubmodule>().GetParameter<int>("Buff_Fusion_Stack");
|
||||
int fusionStack = upgradedAttackUnit.GetSubmodule<AttackUnit.ParameterSubmodule>().GetParameter<int>("Buff_Fusion_Stack");
|
||||
area.hitSm.AddHitEvent((enemy, hitPosition) =>
|
||||
{
|
||||
new Fusion(fusionStack).Apply(enemy);
|
||||
@@ -176,44 +176,39 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
|
||||
_activeSpinArea = area;
|
||||
|
||||
/*area.updateAction = () => // 消除敌方投射物
|
||||
{
|
||||
if (area.timeSm.enablingTimer > 3f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Projectile> toBeRemoved = new List<Projectile>();
|
||||
|
||||
foreach (Projectile projectile in CombatManager.AttackAreaSm.enemyAttackAreas.activeProjectiles)
|
||||
{
|
||||
if (area.areaCollider.IsPointInside(projectile.topParent.transform.position))
|
||||
{
|
||||
toBeRemoved.Add(projectile);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Projectile projectile in toBeRemoved)
|
||||
{
|
||||
projectile.Explode();
|
||||
}
|
||||
};*/
|
||||
|
||||
|
||||
return;
|
||||
|
||||
void Shrink()
|
||||
{
|
||||
if (_activeSpinArea == area)
|
||||
{
|
||||
_activeSpinArea = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
Debug.LogWarning("Trying to shrink a spin area that is not active.");
|
||||
}
|
||||
|
||||
area.transformSm.Reset().ApplyScaleMove(Vector3.one * 3, Vector3.zero, 0.5f, EaseType.OutQuad);
|
||||
area.timeSm.AddScheduleAction(() => LeanPool.Despawn(area.topParent.gameObject), 0.5f);
|
||||
if(_activeSpinArea == null) return;
|
||||
_activeSpinArea.transformSm.Reset().ApplyScaleMove(Vector3.one * 3, Vector3.zero, 0.5f, EaseType.OutQuad);
|
||||
_activeSpinArea.AddScheduleAction(() => LeanPool.Despawn(area.topParent.gameObject), 0.5f);
|
||||
_activeSpinArea = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*area.updateAction = () => // 消除敌方投射物
|
||||
{
|
||||
if (area.timeSm.enablingTimer > 3f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<Projectile> toBeRemoved = new List<Projectile>();
|
||||
|
||||
foreach (Projectile projectile in CombatManager.AttackAreaSm.enemyAttackAreas.activeProjectiles)
|
||||
{
|
||||
if (area.areaCollider.IsPointInside(projectile.topParent.transform.position))
|
||||
{
|
||||
toBeRemoved.Add(projectile);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Projectile projectile in toBeRemoved)
|
||||
{
|
||||
projectile.Explode();
|
||||
}
|
||||
};*/
|
||||
@@ -0,0 +1,71 @@
|
||||
using Cielonos.MainGame.Characters;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
public partial class FutureWand
|
||||
{
|
||||
/// <summary>
|
||||
/// 播放常规地面轻攻击连击
|
||||
/// </summary>
|
||||
/// <param name="target">攻击朝向的目标敌人</param>
|
||||
/// <param name="nextNodeName">连击树中的下一个节点名称</param>
|
||||
/// <returns>是否成功播放</returns>
|
||||
private bool PlayNormalAttackL(CharacterBase target, string nextNodeName)
|
||||
{
|
||||
bool played = PlayTargetedAnimation("Attack" + nextNodeName, target, 5f);
|
||||
if (played)
|
||||
{
|
||||
functionSm["LightAttack"].Execute();
|
||||
}
|
||||
return played;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放蓄力攻击起手动画
|
||||
/// </summary>
|
||||
/// <param name="target">朝向的敌方目标</param>
|
||||
/// <returns>是否成功播放</returns>
|
||||
private bool PlayHoldAttackStart(CharacterBase target)
|
||||
{
|
||||
return PlayTargetedAnimation("HoldAttackStart", target);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放蓄力攻击收招动画
|
||||
/// </summary>
|
||||
/// <returns>是否成功播放</returns>
|
||||
private bool PlayHoldAttackEnd()
|
||||
{
|
||||
return PlayTargetedAnimation("HoldAttackEnd");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 播放常规地面重攻击连击
|
||||
/// </summary>
|
||||
/// <param name="target">攻击朝向的目标敌人</param>
|
||||
/// <param name="nextNodeName">连击树中的下一个节点名称</param>
|
||||
/// <returns>是否成功播放</returns>
|
||||
private bool PlayNormalAttackR(CharacterBase target, string nextNodeName)
|
||||
{
|
||||
bool played = PlayTargetedAnimation("Attack" + nextNodeName, target, 5f);
|
||||
if (played)
|
||||
{
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
}
|
||||
return played;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 召回当前在战场中飞旋的法盘区域(SpinArea)
|
||||
/// </summary>
|
||||
private void RecallActiveSpinArea()
|
||||
{
|
||||
if (_activeSpinArea?.moveSm is BoomerangMoveSubmodule boomerangSm)
|
||||
{
|
||||
// 触发回力标的返回机制,将自动使用配置好的收回速度配置
|
||||
boomerangSm.TriggerReturn();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f21ea98ab9ac5d54f8f5866012e7651c
|
||||
@@ -74,6 +74,9 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
UpdateViewObjectVisuals();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 武器切入(换出至 Polychrome)时的逻辑入口
|
||||
/// </summary>
|
||||
public override void OnSwitchIn()
|
||||
{
|
||||
if (_passionSystem.LevelIndex >= 3)
|
||||
@@ -81,21 +84,10 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
List<Enemy> availableEnemies = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 4);
|
||||
List<Enemy> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
||||
Enemy target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies).ApplyScoreModifier(disruptable, 0f, 1f).BestEnemy();
|
||||
|
||||
if (target != null)
|
||||
{
|
||||
if (PlayTargetedAnimation("DisruptionAttackB", target))
|
||||
{
|
||||
FeedbackClip timeScaleModifierClip = player.feedbackSc.GetFeedbackData("DisruptionStartup")
|
||||
.Clip<TimeScaleModifierAction>("Time");
|
||||
float duration = fullBodyFuncAnimSm.collection["DisruptionAttackB"]
|
||||
.Interval(IntervalType.Startup).Duration * 2;
|
||||
|
||||
timeScaleModifierClip.duration = duration;
|
||||
player.feedbackSc.PlayFeedback("DisruptionStartup");
|
||||
comboSm.main.Reset();
|
||||
_passionSystem.DecreasePassion(100, false);
|
||||
}
|
||||
comboSm.main.Reset();
|
||||
PlayDisruptionAttack(target, "B");
|
||||
}
|
||||
|
||||
SetBlock(equipBlockData, false);
|
||||
@@ -113,71 +105,62 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻攻击(普攻)输入处理
|
||||
/// </summary>
|
||||
public override void OnPrimaryPress()
|
||||
{
|
||||
if (player.statusSm.HasStatus(StatusType.Stun))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.reactionSc.blockSm.HaveBlockSource(blockData.blockName))
|
||||
if (player.statusSm.HasStatus(StatusType.Stun) ||
|
||||
player.reactionSc.blockSm.HaveBlockSource(blockData.blockName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 空中轻攻击连击
|
||||
if (player.landMovementSc.isJumping)
|
||||
{
|
||||
if (!_canAirLightAttack || !functionSm["LightAttack"].IsAvailable())
|
||||
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")
|
||||
string airNodeName = comboSm["AirLight"].GetNextNodeName("L");
|
||||
if (PlayAirAttackL(airNodeName))
|
||||
{
|
||||
_canAirLightAttack = false;
|
||||
comboSm["AirLight"].NextCombo("L");
|
||||
if (comboSm["AirLight"].GetCurrentNodeName() == "L1")
|
||||
{
|
||||
_canAirLightAttack = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
CharacterBase target = CombatManager.EnemySm.GetBestEnemy(5);
|
||||
|
||||
// 冲刺/奔跑攻击
|
||||
if (player.landMovementSc.isSprinting && functionSm["LightAttack"].IsAvailable())
|
||||
{
|
||||
comboSm.main.Reset();
|
||||
if (PlayTargetedAnimation("RunAttack", target))
|
||||
if (PlayRunAttack(target))
|
||||
{
|
||||
comboSm.main.NextCombo("L");
|
||||
}
|
||||
|
||||
functionSm["LightAttack"].Execute();
|
||||
return;
|
||||
}
|
||||
|
||||
// 常规地面轻攻击连击
|
||||
if (functionSm["LightAttack"].IsAvailable())
|
||||
{
|
||||
string nextNodeName = comboSm.main.GetNextNodeName("L");
|
||||
bool keepAdsorption = nextNodeName is "L4" or "L5";
|
||||
if (PlayTargetedAnimation("Attack" + comboSm.main.GetNextNodeName("L"), target, 1f, keepAdsorption))
|
||||
if (PlayNormalAttackL(target, nextNodeName))
|
||||
{
|
||||
float totalTime = 0f;
|
||||
CombatManager.EnemySm.activeEnemiesList.ForEach(enemy =>
|
||||
{
|
||||
(enemy as Enemy).behaviorSc.DispatchContextEvent("PlayerLightAttack", totalTime);
|
||||
});
|
||||
|
||||
comboSm.main.NextCombo("L");
|
||||
functionSm["LightAttack"].Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重攻击/特殊/闪避反击输入处理
|
||||
/// </summary>
|
||||
public override void OnSecondaryPress()
|
||||
{
|
||||
if (player.statusSm.HasStatus(StatusType.Stun))
|
||||
@@ -186,164 +169,100 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
}
|
||||
|
||||
List<Enemy> availableEnemies = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 4);
|
||||
BlockSubmodule blockSm = player.reactionSc.blockSm;
|
||||
if (!blockSm.afterPerfectBlockTimer.isCompleted)
|
||||
{
|
||||
Enemy blockedTarget = blockSm.perfectBlockedTarget as Enemy;
|
||||
if (TryPlayParryAttack(blockedTarget, availableEnemies))
|
||||
{
|
||||
blockSm.afterPerfectBlockTimer.Complete();
|
||||
RemoveBlock();
|
||||
}
|
||||
}
|
||||
|
||||
DodgeSubmodule dodgeSm = player.reactionSc.dodgeSm;
|
||||
if (!dodgeSm.afterPerfectDodgeTimer.isCompleted)
|
||||
{
|
||||
Enemy dodgedTarget = dodgeSm.perfectDodgedTarget as Enemy;
|
||||
if (TryPlayParryAttack(dodgedTarget, availableEnemies))
|
||||
{
|
||||
dodgeSm.afterPerfectDodgeTimer.Complete();
|
||||
}
|
||||
}
|
||||
|
||||
if (player.reactionSc.blockSm.HaveBlockSource(blockData.blockName))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (PlayParryAttack(availableEnemies)) return;
|
||||
if (player.reactionSc.blockSm.HaveBlockSource(blockData.blockName)) return;
|
||||
|
||||
// 空中重攻击下砸
|
||||
if (player.landMovementSc.isJumping)
|
||||
{
|
||||
if (!_canAirHeavyAttack || !functionSm["HeavyAttack"].IsAvailable())
|
||||
if (_canAirHeavyAttack && functionSm["HeavyAttack"].IsAvailable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayTargetedAnimation("AirAttackRStart"))
|
||||
{
|
||||
player.landMovementSc.ExtraJump(10f);
|
||||
comboSm.main.Reset();
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
_canAirLightAttack = false;
|
||||
_canAirHeavyAttack = false;
|
||||
if (PlayAirAttackR())
|
||||
{
|
||||
_canAirLightAttack = false;
|
||||
_canAirHeavyAttack = false;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 颠覆攻击
|
||||
if (player.inputSc.IsHoldingSpecialB)
|
||||
{
|
||||
if (functionSm["DisruptionAttack"].IsAvailable())
|
||||
{
|
||||
bool isEnhanced = _passionSystem.LevelIndex >= 3;
|
||||
string suffix = isEnhanced ? "B" : "A";
|
||||
|
||||
List<Enemy> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
||||
Enemy target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies).ApplyScoreModifier(disruptable, 0f, 1f).BestEnemy();
|
||||
|
||||
if (PlayTargetedAnimation("DisruptionAttack" + suffix, target))
|
||||
{
|
||||
if (disruptable.Count > 0)
|
||||
{
|
||||
FeedbackClip timeScaleModifierClip = player.feedbackSc.GetFeedbackData("DisruptionStartup")
|
||||
.Clip<TimeScaleModifierAction>("Time");
|
||||
float duration = fullBodyFuncAnimSm.collection["DisruptionAttack" + suffix]
|
||||
.Interval(IntervalType.Startup).Duration * 2;
|
||||
|
||||
timeScaleModifierClip.duration = duration;
|
||||
player.feedbackSc.PlayFeedback("DisruptionStartup");
|
||||
}
|
||||
|
||||
if (isEnhanced)
|
||||
{
|
||||
_passionSystem.DecreasePassion(100f, false);
|
||||
}
|
||||
|
||||
comboSm.main.Reset();
|
||||
functionSm["DisruptionAttack"].Execute();
|
||||
}
|
||||
|
||||
comboSm.main.Reset();
|
||||
PlayDisruptionAttack(target, suffix);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// 常规地面重攻击连击
|
||||
if (functionSm["HeavyAttack"].IsAvailable())
|
||||
{
|
||||
//Enemy target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
||||
Enemy nt = CombatManager.EnemySm.GetBestEnemy(CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 15));
|
||||
string nextNodeName = comboSm.main.GetNextNodeName("R");
|
||||
bool keepAdsorption = nextNodeName is "RC";
|
||||
|
||||
Enemy target;
|
||||
bool shouldWarp = false;
|
||||
Vector3 targetPos = Vector3.zero;
|
||||
if (nt != null && nextNodeName == "RA" && HasExtender<PhotonWarper>())
|
||||
string suffix = comboSm.main.GetNextNodeName("R");
|
||||
|
||||
// 携带 PhotonWarper 时的中远距离敌人折跃判定
|
||||
if (!HasExtender<PhotonWarper>())
|
||||
{
|
||||
nextNodeName = "RB";
|
||||
float distance = Vector3.Distance(player.transform.position, nt.transform.position);
|
||||
if (distance > 2f)
|
||||
target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
||||
}
|
||||
else
|
||||
{
|
||||
List<Enemy> fartherRange = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 15);
|
||||
target = CombatManager.EnemySm.GetBestEnemy(fartherRange);
|
||||
if (target != null && suffix == "RA")
|
||||
{
|
||||
shouldWarp = true;
|
||||
player.movementSc.SmartTurnToTarget(nt, 360, true);
|
||||
targetPos = player.movementSc.GetSafePositionNearTarget(nt, 1.0f);
|
||||
float distance = Vector3.Distance(player.transform.position, target.transform.position);
|
||||
if (distance > 2f)
|
||||
{
|
||||
shouldWarp = true;
|
||||
player.movementSc.SmartTurnToTarget(target, 360, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nt != null && (shouldWarp || nextNodeName is "RC"))
|
||||
{
|
||||
player.movementSc.SmartTurnToTarget(nt, 360, true);
|
||||
}
|
||||
|
||||
if (PlayTargetedAnimation("Attack" + nextNodeName, nt, 1f, keepAdsorption))
|
||||
|
||||
if (PlayNormalAttackR(target, suffix))
|
||||
{
|
||||
comboSm.main.NextCombo("R");
|
||||
if (shouldWarp)
|
||||
{
|
||||
var interval = fullBodyFuncAnimSm.currentData.Interval(IntervalType.Startup);
|
||||
float warpDuration = fullBodyFuncAnimSm.GetIntervalScaledDuration(IntervalType.Startup);
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.AddAnimEvent(interval.StartTime, new SetFuncAnimSpeed()
|
||||
{
|
||||
applyMode = SetFuncAnimSpeed.SpeedApplyMode.Override,
|
||||
getFromBehaviorTree = false,
|
||||
targetSpeed = 1f
|
||||
});
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.AddAnimEvent(interval.EndTime, new SetFuncAnimSpeed()
|
||||
{
|
||||
applyMode = SetFuncAnimSpeed.SpeedApplyMode.Override,
|
||||
getFromBehaviorTree = false,
|
||||
targetSpeed = 1f
|
||||
});
|
||||
player.movementSc.Teleport(targetPos, warpDuration);
|
||||
FuncAnimInterval interval = fullBodyFuncAnimSm.currentData.Interval(IntervalType.Startup);
|
||||
WarpToTarget(target, interval, 1f);
|
||||
}
|
||||
|
||||
float totalTime = fullBodyFuncAnimSm.GetIntervalScaledDuration(IntervalType.Startup) - 0.2f;
|
||||
CombatManager.EnemySm.activeEnemiesList.ForEach(enemy =>
|
||||
{
|
||||
(enemy as Enemy)!.behaviorSc.DispatchContextEvent("PlayerHeavyAttack", totalTime);
|
||||
});
|
||||
comboSm.main.NextCombo("R");
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
bool TryPlayParryAttack(Enemy parryTarget, List<Enemy> enemies)
|
||||
{
|
||||
if (parryTarget != null && !enemies.Contains(parryTarget))
|
||||
{
|
||||
float distance = Vector3.Distance(player.transform.position.Flatten(), parryTarget.transform.position.Flatten());
|
||||
return PlayTargetedAnimation(distance > 2f ? "DodgeParryAttack" : "BlockParryAttack", parryTarget);
|
||||
}
|
||||
return PlayTargetedAnimation("BlockParryAttack");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 终极攻击(次元斩)输入处理
|
||||
/// </summary>
|
||||
public override void OnSpecialAPress()
|
||||
{
|
||||
if (player.statusSm.HasStatus(StatusType.Stun))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (functionSm["UltimateAttack"].IsAvailable())
|
||||
{
|
||||
PlayTargetedAnimation("UltimateAttack");
|
||||
functionSm["UltimateAttack"].Execute();
|
||||
ApplyUltimatePassionBoost();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格挡输入处理
|
||||
/// </summary>
|
||||
public override void OnSpecialCPress()
|
||||
{
|
||||
comboSm.main.Reset();
|
||||
@@ -359,71 +278,19 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 格挡输入释放
|
||||
/// </summary>
|
||||
public override void OnSpecialCRelease()
|
||||
{
|
||||
if (upperBodyFuncAnimSm.currentRuntimeFuncAnim is { animationName: "Block" })
|
||||
{
|
||||
upperBodyFuncAnimSm.Stop(DisruptionType.ForcedAction);
|
||||
upperBodyFuncAnimSm.Stop(DisruptionType.Must);
|
||||
}
|
||||
|
||||
player.selfTimeSm.AddLocalTimer(0.04f, () => RemoveBlock());
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Polychrome
|
||||
{
|
||||
private string _currentKatanaParticle;
|
||||
|
||||
private void UpdateViewObjectVisuals()
|
||||
{
|
||||
if (_passionSystem == null) return;
|
||||
|
||||
int level = _passionSystem.LevelIndex;
|
||||
|
||||
// 开关粒子特效
|
||||
if (level < 3) // C, B,A级
|
||||
{
|
||||
if (_currentKatanaParticle != string.Empty)
|
||||
{
|
||||
GetParticle().Stop();
|
||||
_currentKatanaParticle = string.Empty;
|
||||
}
|
||||
}
|
||||
else if (level <= 4) // S,SS级 (2)
|
||||
{
|
||||
if (_currentKatanaParticle != "ParticleStage0")
|
||||
{
|
||||
if (_currentKatanaParticle != string.Empty)
|
||||
{
|
||||
GetParticle().Stop();
|
||||
}
|
||||
_currentKatanaParticle = "ParticleStage0";
|
||||
GetParticle().Play();
|
||||
}
|
||||
}
|
||||
else // SSS级及以上
|
||||
{
|
||||
if (_currentKatanaParticle != "ParticleStage1")
|
||||
{
|
||||
if (_currentKatanaParticle != string.Empty)
|
||||
{
|
||||
GetParticle().Stop();
|
||||
}
|
||||
_currentKatanaParticle = "ParticleStage1";
|
||||
GetParticle().Play();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
ParticleSystem GetParticle()
|
||||
{
|
||||
return viewObjects["Katana"].functionalParts.TryGetValue(_currentKatanaParticle, out GameObject particleObj) ?
|
||||
particleObj.GetComponent<ParticleSystem>() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public partial class Polychrome
|
||||
{
|
||||
public override Dictionary<string, string> GetDescriptionArgs()
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
using Lean.Pool;
|
||||
using SLSUtilities.General;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Cielonos.MainGame.UI
|
||||
{
|
||||
public class PolychromeExtraUIContainer : MainWeaponExtraUIContainer
|
||||
{
|
||||
public GameObject techniqueStarPrefab;
|
||||
public HorizontalLayoutGroup layoutGroup;
|
||||
|
||||
public void SetStars(int starCount)
|
||||
{
|
||||
int currentStars = layoutGroup.transform.childCount;
|
||||
if (starCount != currentStars)
|
||||
{
|
||||
layoutGroup.transform.DespawnAllChildren();
|
||||
|
||||
for (int i = 0; i < starCount; i++)
|
||||
{
|
||||
GameObject star = LeanPool.Spawn(techniqueStarPrefab, layoutGroup.transform);
|
||||
star.SetActive(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2a8933650445a8c49a5807c1dcc42969
|
||||
@@ -48,8 +48,9 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
if (!HasExtender<PhotonDissociator>())
|
||||
{
|
||||
slash = CreateBaseSlash(vfxName, attackUnit, 1f, 0.04f);
|
||||
slash.SetImpulseSubmodule(1f).WithRepulsion(5f);
|
||||
}
|
||||
else // 如果有Photon Dissociator,重攻击拆分为五段,每段伤害为原来的30%,时间和判定也相应调整
|
||||
else // 如果有Photon Dissociator,重攻击拆分为3段,每段伤害为原来的50%,时间和判定也相应调整
|
||||
{
|
||||
AttackUnit modifiedUnit = attackUnit.Clone();
|
||||
modifiedUnit.startDamage *= 0.5f;
|
||||
@@ -64,10 +65,9 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
AudioManager.Post(AK.EVENTS.DISRUPT, hitPosition);
|
||||
};
|
||||
slash.SetImpulseSubmodule(1f).WithRepulsion(2.5f);
|
||||
}
|
||||
|
||||
slash.SetImpulseSubmodule(1f).WithRepulsion(5f);
|
||||
|
||||
slash.hitSm.AddHitSound(AK.EVENTS.POLYCHROME_HEAVYATTACKLHIT)
|
||||
.AddHitEvent((enemy, hitPosition) =>
|
||||
{
|
||||
|
||||
@@ -0,0 +1,236 @@
|
||||
using System.Collections.Generic;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using Cielonos.MainGame.Effects.Feedback;
|
||||
using Cielonos.MainGame.FunctionalAnimation;
|
||||
using SLSUtilities.Feedback;
|
||||
using SLSUtilities.FunctionalAnimation;
|
||||
using SLSUtilities.General;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
public partial class Polychrome
|
||||
{
|
||||
private bool PlayAirAttackL(string nodeName)
|
||||
{
|
||||
bool played = PlayTargetedAnimation("AirAttack" + nodeName);
|
||||
if (played)
|
||||
{
|
||||
ApplyPhotonAcceleratorIfEquipped();
|
||||
functionSm["LightAttack"].Execute();
|
||||
}
|
||||
return played;
|
||||
}
|
||||
|
||||
private bool PlayRunAttack(CharacterBase target)
|
||||
{
|
||||
bool played = PlayTargetedAnimation("RunAttack", target);
|
||||
if (played)
|
||||
{
|
||||
ApplyPhotonAcceleratorIfEquipped();
|
||||
functionSm["LightAttack"].Execute();
|
||||
}
|
||||
return played;
|
||||
}
|
||||
|
||||
private bool PlayNormalAttackL(CharacterBase target, string nextNodeName)
|
||||
{
|
||||
bool keepAdsorption = nextNodeName is "L4" or "L5";
|
||||
bool played = PlayTargetedAnimation("Attack" + nextNodeName, target, 1f, keepAdsorption);
|
||||
if (played)
|
||||
{
|
||||
ApplyPhotonAcceleratorIfEquipped();
|
||||
float totalTime = 0f;
|
||||
CombatManager.EnemySm.activeEnemiesList.ForEach(enemy => (enemy as Enemy)!.behaviorSc.DispatchContextEvent("PlayerLightAttack", totalTime));
|
||||
functionSm["LightAttack"].Execute();
|
||||
}
|
||||
return played;
|
||||
}
|
||||
|
||||
private bool PlayAirAttackR()
|
||||
{
|
||||
bool played = PlayTargetedAnimation("AirAttackRStart");
|
||||
if (played)
|
||||
{
|
||||
player.landMovementSc.ExtraJump(10f);
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
}
|
||||
return played;
|
||||
}
|
||||
|
||||
private bool PlayNormalAttackR(Enemy target, string suffix)
|
||||
{
|
||||
bool keepAdsorption = suffix is "RC";
|
||||
bool played = PlayTargetedAnimation("Attack" + suffix, target, 1f, keepAdsorption);
|
||||
if (played)
|
||||
{
|
||||
float totalTime = fullBodyFuncAnimSm.GetIntervalScaledDuration(IntervalType.Startup) - 0.2f;
|
||||
CombatManager.EnemySm.activeEnemiesList.ForEach(enemy => enemy!.behaviorSc.DispatchContextEvent("PlayerHeavyAttack", totalTime));
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
}
|
||||
return played;
|
||||
}
|
||||
|
||||
private void WarpToTarget(Enemy target, FuncAnimInterval interval, float multiplier = 1f)
|
||||
{
|
||||
float warpDuration = interval.Duration / multiplier;
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.AddAnimEvent(interval.StartTime, new SetFuncAnimSpeed(multiplier));
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.AddAnimEvent(interval.EndTime, new SetFuncAnimSpeed(1));
|
||||
player.movementSc.Teleport(target, warpDuration);
|
||||
}
|
||||
|
||||
private void PlayDisruptionAttack(Enemy target, string suffix)
|
||||
{
|
||||
string animName = "DisruptionAttack" + suffix;
|
||||
bool played = PlayTargetedAnimation(animName, target);
|
||||
if (played)
|
||||
{
|
||||
FeedbackClip timeScaleModifierClip = player.feedbackSc.GetFeedbackData("DisruptionStartup").Clip<TimeScaleModifierAction>("Time");
|
||||
float duration = fullBodyFuncAnimSm.collection[animName].Interval(IntervalType.Startup).Duration * 2;
|
||||
timeScaleModifierClip.duration = duration;
|
||||
player.feedbackSc.PlayFeedback("DisruptionStartup");
|
||||
if(suffix == "B") _passionSystem.DecreasePassion(100, false);
|
||||
functionSm["DisruptionAttack"].Execute();
|
||||
}
|
||||
}
|
||||
|
||||
private bool PlayParryAttack(List<Enemy> availableEnemies)
|
||||
{
|
||||
BlockSubmodule blockSm = player.reactionSc.blockSm;
|
||||
if (!blockSm.afterPerfectBlockTimer.isCompleted)
|
||||
{
|
||||
Enemy blockedTarget = blockSm.perfectBlockedTarget as Enemy;
|
||||
if (TryPlayParryAttack(blockedTarget, availableEnemies))
|
||||
{
|
||||
blockSm.afterPerfectBlockTimer.Complete();
|
||||
RemoveBlock();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DodgeSubmodule dodgeSm = player.reactionSc.dodgeSm;
|
||||
if (!dodgeSm.afterPerfectDodgeTimer.isCompleted)
|
||||
{
|
||||
Enemy dodgedTarget = dodgeSm.perfectDodgedTarget as Enemy;
|
||||
if (TryPlayParryAttack(dodgedTarget, availableEnemies))
|
||||
{
|
||||
dodgeSm.afterPerfectDodgeTimer.Complete();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
bool TryPlayParryAttack(Enemy parryTarget, List<Enemy> enemies)
|
||||
{
|
||||
if (parryTarget != null && !enemies.Contains(parryTarget))
|
||||
{
|
||||
float distance = Vector3.Distance(player.transform.position, parryTarget.transform.position);
|
||||
return PlayTargetedAnimation(distance > 2f ? "DodgeParryAttack" : "BlockParryAttack", parryTarget);
|
||||
}
|
||||
return PlayTargetedAnimation("BlockParryAttack");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Polychrome
|
||||
{
|
||||
private void ApplyPhotonAcceleratorIfEquipped()
|
||||
{
|
||||
if (HasExtender<PhotonAccelerator>())
|
||||
{
|
||||
FuncAnimInterval interval = fullBodyFuncAnimSm.currentData.Interval(IntervalType.Startup);
|
||||
if (interval != null)
|
||||
{
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.AddAnimEvent(
|
||||
interval.StartTime, new SetFuncAnimSpeed(1.5f, SetFuncAnimSpeed.SpeedApplyMode.Multiply));
|
||||
fullBodyFuncAnimSm.currentRuntimeFuncAnim.AddAnimEvent(
|
||||
interval.EndTime, new SetFuncAnimSpeed(1f / 1.5f, SetFuncAnimSpeed.SpeedApplyMode.Multiply));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyUltimatePassionBoost()
|
||||
{
|
||||
if (HasExtender<DisorderedPhotonCollector>())
|
||||
{
|
||||
var passionSystem = _passionSystem;
|
||||
if (passionSystem != null)
|
||||
{
|
||||
int currentLevel = passionSystem.LevelIndex;
|
||||
float currentPercent = passionSystem.Percent;
|
||||
|
||||
float targetIncrease = 100f;
|
||||
if (currentLevel == 0) // C level
|
||||
{
|
||||
targetIncrease = 200f - currentPercent;
|
||||
}
|
||||
|
||||
float amplifier = player.attributeSm[CharacterAttribute.PassionIncreaseAmplifier];
|
||||
float multiplier = passionSystem.passionLevels[currentLevel].increaseMultiplier;
|
||||
|
||||
float divisor = multiplier * (1f + amplifier);
|
||||
if (divisor > 0.001f)
|
||||
{
|
||||
float baseAmount = targetIncrease / divisor;
|
||||
passionSystem.IncreasePassion(baseAmount, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Polychrome
|
||||
{
|
||||
private string _currentKatanaParticle;
|
||||
|
||||
private void UpdateViewObjectVisuals()
|
||||
{
|
||||
if (_passionSystem == null) return;
|
||||
|
||||
int level = _passionSystem.LevelIndex;
|
||||
|
||||
// 开关粒子特效
|
||||
if (level < 3) // C, B,A级
|
||||
{
|
||||
if (_currentKatanaParticle != string.Empty)
|
||||
{
|
||||
GetParticle().Stop();
|
||||
_currentKatanaParticle = string.Empty;
|
||||
}
|
||||
}
|
||||
else if (level <= 4) // S,SS级 (2)
|
||||
{
|
||||
if (_currentKatanaParticle != "ParticleStage0")
|
||||
{
|
||||
if (_currentKatanaParticle != string.Empty)
|
||||
{
|
||||
GetParticle().Stop();
|
||||
}
|
||||
_currentKatanaParticle = "ParticleStage0";
|
||||
GetParticle().Play();
|
||||
}
|
||||
}
|
||||
else // SSS级及以上
|
||||
{
|
||||
if (_currentKatanaParticle != "ParticleStage1")
|
||||
{
|
||||
if (_currentKatanaParticle != string.Empty)
|
||||
{
|
||||
GetParticle().Stop();
|
||||
}
|
||||
_currentKatanaParticle = "ParticleStage1";
|
||||
GetParticle().Play();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
ParticleSystem GetParticle()
|
||||
{
|
||||
return viewObjects["Katana"].functionalParts.TryGetValue(_currentKatanaParticle, out GameObject particleObj) ?
|
||||
particleObj.GetComponent<ParticleSystem>() : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8872dc56cff645f429d7551c98ccfd5b
|
||||
Reference in New Issue
Block a user