FututeWand初步
This commit is contained in:
@@ -80,6 +80,9 @@ namespace Cielonos.MainGame
|
||||
impulseSm = null;
|
||||
reactionSm = null;
|
||||
|
||||
scheduledActions.Clear();
|
||||
_toBeExecutedScheduledActions.Clear();
|
||||
|
||||
areaCollider = GetComponent<Collider>();
|
||||
|
||||
// 通过 VFXObject 组件确定 VFX 顶层节点,比遍历 parent 链更可靠。
|
||||
@@ -122,11 +125,62 @@ namespace Cielonos.MainGame
|
||||
hitSm?.Update();
|
||||
timeSm?.Update();
|
||||
moveSm?.Update();
|
||||
UpdateScheduledActions();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class AttackAreaBase
|
||||
{
|
||||
public class ScheduledAction
|
||||
{
|
||||
public Action action;
|
||||
public float delay;
|
||||
|
||||
public ScheduledAction(Action action, float delay)
|
||||
{
|
||||
this.action = action;
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
|
||||
[HideInEditorMode] public List<ScheduledAction> scheduledActions = new List<ScheduledAction>();
|
||||
private List<ScheduledAction> _toBeExecutedScheduledActions = new List<ScheduledAction>();
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个在指定延迟时间后执行的动作,只要 AttackArea 存在且 active 就会更新
|
||||
/// </summary>
|
||||
public AttackAreaBase AddScheduleAction(Action action, float delay)
|
||||
{
|
||||
scheduledActions.Add(new ScheduledAction(action, delay));
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新所有延迟执行的动作,不受子模块启用状态的影响
|
||||
/// </summary>
|
||||
private void UpdateScheduledActions()
|
||||
{
|
||||
if (scheduledActions.Count > 0)
|
||||
{
|
||||
_toBeExecutedScheduledActions.Clear();
|
||||
float dt = creator != null && creator.selfTimeSm != null ? creator.selfTimeSm.DeltaTime : Time.deltaTime;
|
||||
for (int i = scheduledActions.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var scheduledAction = scheduledActions[i];
|
||||
scheduledAction.delay -= dt;
|
||||
if (scheduledAction.delay <= 0)
|
||||
{
|
||||
_toBeExecutedScheduledActions.Add(scheduledAction);
|
||||
scheduledActions.RemoveAt(i);
|
||||
}
|
||||
}
|
||||
foreach (var scheduledAction in _toBeExecutedScheduledActions)
|
||||
{
|
||||
scheduledAction.action?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#region TransformSubmodule
|
||||
|
||||
public T SetTransformSubmodule<T>(Transform target = null, float delay = 0) where T : AttackAreaBase
|
||||
@@ -263,16 +317,6 @@ namespace Cielonos.MainGame
|
||||
keepFlat, straightThrowInitially, straightThrowDuration, stopWhenHit);
|
||||
return this as T;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置回旋镖/旋转区域追踪移动子模块(链式/Fluent 初始化),返回子模块以供进一步配置
|
||||
/// </summary>
|
||||
public BoomerangMoveSubmodule SetBoomerangMoveSubmodule(CharacterBase target, float moveSpeed, float angularSpeed, Vector3 initialDirection)
|
||||
{
|
||||
var submodule = new BoomerangMoveSubmodule(this, target, moveSpeed, angularSpeed, initialDirection);
|
||||
moveSm = submodule;
|
||||
return submodule;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region RaycastSubmodule
|
||||
|
||||
@@ -13,6 +13,11 @@ namespace Cielonos.MainGame
|
||||
public float angularSpeed;
|
||||
public float angularAcceleration;
|
||||
|
||||
public float returnMoveSpeed = -1f;
|
||||
public float returnMoveAcceleration = -1f;
|
||||
public float returnAngularSpeed = -1f;
|
||||
public float returnAngularAcceleration = -1f;
|
||||
|
||||
public bool isReturning;
|
||||
public float catchRadius;
|
||||
public float autoReturnTime; // Time in seconds after which it automatically returns if not triggered manually
|
||||
@@ -112,15 +117,33 @@ namespace Cielonos.MainGame
|
||||
return this;
|
||||
}
|
||||
|
||||
public BoomerangMoveSubmodule WithReturnParameters(float speed, float acceleration, float angularSpeed, float angularAcceleration)
|
||||
{
|
||||
this.returnMoveSpeed = speed;
|
||||
this.returnMoveAcceleration = acceleration;
|
||||
this.returnAngularSpeed = angularSpeed;
|
||||
this.returnAngularAcceleration = angularAcceleration;
|
||||
return this;
|
||||
}
|
||||
|
||||
public void TriggerReturn(float moveSpeed = -1f, float moveAcceleration = -1f, float angularSpeed = -1f, float angularAcceleration = -1f)
|
||||
{
|
||||
if (isReturning || !canMove) return;
|
||||
isReturning = true;
|
||||
target = owner.creator; // Start tracing the player/creator
|
||||
if (moveSpeed >= 0f) this.moveSpeed = moveSpeed;
|
||||
if (moveAcceleration >= 0f) this.moveAcceleration = moveAcceleration;
|
||||
if (angularSpeed >= 0f) this.angularSpeed = angularSpeed;
|
||||
if (angularAcceleration >= 0f) this.angularAcceleration = angularAcceleration;
|
||||
|
||||
float targetSpeed = moveSpeed >= 0f ? moveSpeed : returnMoveSpeed;
|
||||
if (targetSpeed >= 0f) this.moveSpeed = targetSpeed;
|
||||
|
||||
float targetAcceleration = moveAcceleration >= 0f ? moveAcceleration : returnMoveAcceleration;
|
||||
if (targetAcceleration >= 0f) this.moveAcceleration = targetAcceleration;
|
||||
|
||||
float targetAngularSpeed = angularSpeed >= 0f ? angularSpeed : returnAngularSpeed;
|
||||
if (targetAngularSpeed >= 0f) this.angularSpeed = targetAngularSpeed;
|
||||
|
||||
float targetAngularAcceleration = angularAcceleration >= 0f ? angularAcceleration : returnAngularAcceleration;
|
||||
if (targetAngularAcceleration >= 0f) this.angularAcceleration = targetAngularAcceleration;
|
||||
|
||||
autoConnect = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -15,8 +15,6 @@ namespace Cielonos.MainGame
|
||||
private bool hasInvokedEnableAction;
|
||||
public Action enableAction;
|
||||
public Action timeOutAction;
|
||||
public List<ScheduledAction> scheduledActions;
|
||||
private List<ScheduledAction> toBeExecutedScheduledActions = new List<ScheduledAction>();
|
||||
|
||||
/// <summary>
|
||||
/// enable 阶段开始前允许反应的提前量(秒),默认 0 表示无提前 grace window。
|
||||
@@ -30,7 +28,6 @@ namespace Cielonos.MainGame
|
||||
this.lifeTime = lifeTime;
|
||||
this.remainingLifeTime = lifeTime;
|
||||
this.enablingTimer = 0;
|
||||
this.scheduledActions = new List<ScheduledAction>();
|
||||
|
||||
if (attackArea is NormalArea)
|
||||
{
|
||||
@@ -67,7 +64,6 @@ namespace Cielonos.MainGame
|
||||
this.lifeTime = lifeTime;
|
||||
this.delayTime = delayTime;
|
||||
this.attackArea.isEnabling = delayTime <= 0;
|
||||
this.scheduledActions = new List<ScheduledAction>();
|
||||
|
||||
this.timeOutAction = timeOutAction ?? (() =>
|
||||
{
|
||||
@@ -93,11 +89,7 @@ namespace Cielonos.MainGame
|
||||
this.reactionGraceBefore = graceBefore;
|
||||
}
|
||||
|
||||
public TimeSubmodule AddScheduleAction(Action action, float delay)
|
||||
{
|
||||
scheduledActions.Add(new ScheduledAction(action, delay));
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 判断当前时刻是否处于反应窗口内(包含 grace 区间和 enable 阶段本身)。
|
||||
@@ -148,21 +140,7 @@ namespace Cielonos.MainGame
|
||||
enableAction?.Invoke();
|
||||
hasInvokedEnableAction = true;
|
||||
}
|
||||
|
||||
toBeExecutedScheduledActions.Clear();
|
||||
foreach (var scheduledAction in scheduledActions)
|
||||
{
|
||||
scheduledAction.delay -= attackArea.creator.selfTimeSm.DeltaTime;
|
||||
if (scheduledAction.delay <= 0)
|
||||
{
|
||||
toBeExecutedScheduledActions.Add(scheduledAction);
|
||||
}
|
||||
}
|
||||
foreach (var scheduledAction in toBeExecutedScheduledActions)
|
||||
{
|
||||
scheduledAction.action.Invoke();
|
||||
scheduledActions.Remove(scheduledAction);
|
||||
}
|
||||
|
||||
|
||||
if (remainingLifeTime <= 0)
|
||||
{
|
||||
@@ -187,18 +165,5 @@ namespace Cielonos.MainGame
|
||||
}
|
||||
}
|
||||
|
||||
public partial class TimeSubmodule
|
||||
{
|
||||
public class ScheduledAction
|
||||
{
|
||||
public Action action;
|
||||
public float delay;
|
||||
|
||||
public ScheduledAction(Action action, float delay)
|
||||
{
|
||||
this.action = action;
|
||||
this.delay = delay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -34,6 +34,14 @@ namespace Cielonos.MainGame.FunctionalAnimation
|
||||
[HideIf("getFromBehaviorTree")]
|
||||
public float targetSpeed = 1.0f;
|
||||
|
||||
public SetFuncAnimSpeed() { }
|
||||
public SetFuncAnimSpeed(float speed, SpeedApplyMode mode = SpeedApplyMode.Override, bool getFromBehaviorTree = false)
|
||||
{
|
||||
targetSpeed = speed;
|
||||
applyMode = mode;
|
||||
this.getFromBehaviorTree = getFromBehaviorTree;
|
||||
}
|
||||
|
||||
public override void Invoke()
|
||||
{
|
||||
if (Character == null || Character.animationSc == null) return;
|
||||
|
||||
@@ -52,6 +52,12 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
}
|
||||
|
||||
protected override void InitializeSubcontrollers()
|
||||
{
|
||||
base.InitializeSubcontrollers();
|
||||
behaviorSc?.Initialize();
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
@@ -93,9 +99,7 @@ namespace Cielonos.MainGame.Characters
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
protected virtual void UpdateGetHit()
|
||||
{
|
||||
_getHitTimer -= selfTimeSm.DeltaTime;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Cielonos.MainGame.Buffs.Character;
|
||||
using Opsive.BehaviorDesigner.Runtime;
|
||||
using SLSUtilities.General;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
|
||||
@@ -18,10 +19,74 @@ namespace Cielonos.MainGame.Characters
|
||||
navMeshAgent.isStopped = true;
|
||||
animatorMoveSpeedX = new LerpFloat(0f, 5f);
|
||||
animatorMoveSpeedZ = new LerpFloat(0f, 5f);
|
||||
//owner.selfTimeSm.timeScaleObservable.Subscribe(OnTimeScaleChanged).AddTo(owner);
|
||||
}
|
||||
|
||||
private Vector3 lastDirection;
|
||||
|
||||
// ────────────────────────────────────────────────────────────────────
|
||||
// Time Scale Sync
|
||||
// ────────────────────────────────────────────────────────────────────
|
||||
|
||||
private float unscaledNavSpeed;
|
||||
private float unscaledNavAccel;
|
||||
private float unscaledNavAngularSpeed;
|
||||
private bool wasNavStoppedBeforePause;
|
||||
private float lastTimeScale = -1f;
|
||||
private bool isTimePaused = false;
|
||||
|
||||
private void OnTimeScaleChanged(float timeScale)
|
||||
{
|
||||
if (navMeshAgent == null || !navMeshAgent.enabled)
|
||||
{
|
||||
if (timeScale == 0f && !isTimePaused)
|
||||
{
|
||||
isTimePaused = true;
|
||||
}
|
||||
else if (timeScale > 0f && isTimePaused)
|
||||
{
|
||||
isTimePaused = false;
|
||||
}
|
||||
lastTimeScale = timeScale;
|
||||
return;
|
||||
}
|
||||
|
||||
if (timeScale == 0f)
|
||||
{
|
||||
if (!isTimePaused)
|
||||
{
|
||||
isTimePaused = true;
|
||||
wasNavStoppedBeforePause = navMeshAgent.isStopped;
|
||||
navMeshAgent.isStopped = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
navMeshAgent.isStopped = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isTimePaused)
|
||||
{
|
||||
isTimePaused = false;
|
||||
navMeshAgent.isStopped = wasNavStoppedBeforePause;
|
||||
}
|
||||
|
||||
if (Mathf.Abs(navMeshAgent.speed - (unscaledNavSpeed * timeScale)) > 0.01f)
|
||||
{
|
||||
unscaledNavSpeed = navMeshAgent.speed;
|
||||
unscaledNavAccel = navMeshAgent.acceleration;
|
||||
unscaledNavAngularSpeed = navMeshAgent.angularSpeed;
|
||||
}
|
||||
|
||||
navMeshAgent.speed = unscaledNavSpeed * timeScale;
|
||||
navMeshAgent.acceleration = unscaledNavAccel * timeScale;
|
||||
navMeshAgent.angularSpeed = unscaledNavAngularSpeed * timeScale;
|
||||
}
|
||||
|
||||
lastTimeScale = timeScale;
|
||||
}
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
base.Update();
|
||||
|
||||
@@ -2,6 +2,8 @@ using System.Collections.Generic;
|
||||
using Opsive.BehaviorDesigner.Runtime;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AI;
|
||||
using UniRx;
|
||||
using Cielonos.MainGame.Buffs.Character;
|
||||
|
||||
namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
@@ -13,6 +15,37 @@ namespace Cielonos.MainGame.Characters
|
||||
public NavMeshAgent navMeshAgent;
|
||||
public Dictionary<string, Subtree> subBehaviorTrees;
|
||||
|
||||
private System.IDisposable timeScaleDisposable;
|
||||
private bool isTimePaused = false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
owner.selfTimeSm.timeScaleObservable.Subscribe(OnTimeScaleChanged).AddTo(owner);
|
||||
}
|
||||
|
||||
private void OnTimeScaleChanged(float timeScale)
|
||||
{
|
||||
if (mainBehaviorTree == null) return;
|
||||
|
||||
if (timeScale == 0f)
|
||||
{
|
||||
if (!isTimePaused)
|
||||
{
|
||||
isTimePaused = true;
|
||||
mainBehaviorTree.StopBehavior(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (isTimePaused)
|
||||
{
|
||||
isTimePaused = false;
|
||||
mainBehaviorTree.StartBehavior();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class AutomataEvent
|
||||
{
|
||||
public float Timestamp;
|
||||
|
||||
@@ -72,11 +72,120 @@ namespace Cielonos.MainGame.Characters
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 瞬移/闪现至目标位置(支持高速移动过程)
|
||||
/// 动态追踪折跃:在移动过程中实时追踪目标位置,确保降落时完美对齐且不产生重叠
|
||||
/// </summary>
|
||||
/// <param name="target">折跃追踪的目标敌人</param>
|
||||
/// <param name="duration">移动时间(秒)。若为 0 则为即时瞬移</param>
|
||||
/// <param name="stopDistance">目标表面前的停止缓冲距离(默认为 1.0f)</param>
|
||||
/// <param name="onComplete">完成回调</param>
|
||||
public void Teleport(CharacterBase target, float duration, float stopDistance = 1.0f, Action onComplete = null)
|
||||
{
|
||||
if (target == null)
|
||||
{
|
||||
Teleport(owner.transform.position, duration, onComplete);
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. 如果是即时瞬移(duration <= 0)
|
||||
if (duration <= 0f)
|
||||
{
|
||||
Vector3 instantDest = GetSafePositionNearTarget(target, stopDistance);
|
||||
if (NavMesh.SamplePosition(instantDest, out NavMeshHit hit, 5.0f, NavMesh.AllAreas))
|
||||
{
|
||||
instantDest = hit.position;
|
||||
}
|
||||
PerformMove(instantDest);
|
||||
SmartTurnToTarget(target, 360f, true);
|
||||
onComplete?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. 带有追踪的动态折跃位移
|
||||
Vector3 startPos = owner.transform.position;
|
||||
|
||||
// 赋予无敌状态
|
||||
owner.statusSm.AddStatus(StatusType.Invincible, duration);
|
||||
|
||||
// 锁定移动和旋转输入(使用高优先级 99 隔离普通移动控制)
|
||||
canMove.Modify(false, 99);
|
||||
canRotate.Modify(false, 99);
|
||||
|
||||
// 开启残影生成(位移开始时生成单个残影)
|
||||
var afterImageGen = owner.GetComponent<AfterImageGenerator>();
|
||||
if (afterImageGen != null)
|
||||
{
|
||||
afterImageGen.CreateAfterImageSnapshot();
|
||||
}
|
||||
|
||||
// 获取并临时关闭物理/寻路组件
|
||||
var agent = owner.GetComponent<NavMeshAgent>();
|
||||
bool wasAgentActive = agent != null && agent.isActiveAndEnabled;
|
||||
if (wasAgentActive) agent.enabled = false;
|
||||
|
||||
var cc = owner.collisionSc != null ? owner.collisionSc.characterController : null;
|
||||
bool wasCcActive = cc != null && cc.enabled;
|
||||
if (wasCcActive) cc.enabled = false;
|
||||
|
||||
var rb = owner.collisionSc != null ? owner.collisionSc.mainRigidbody : null;
|
||||
bool wasRbKinematic = rb != null && rb.isKinematic;
|
||||
if (rb != null) rb.isKinematic = true;
|
||||
|
||||
// 使用本地计时器,更新过程中的插值位移并实时朝向目标
|
||||
float elapsed = 0f;
|
||||
owner.selfTimeSm.AddLocalTimer(duration,
|
||||
onComplete: () =>
|
||||
{
|
||||
// 确保移动完毕并最终强制对齐
|
||||
Vector3 finalDest = GetSafePositionNearTarget(target, stopDistance);
|
||||
if (NavMesh.SamplePosition(finalDest, out NavMeshHit hit, 5.0f, NavMesh.AllAreas))
|
||||
{
|
||||
finalDest = hit.position;
|
||||
}
|
||||
owner.transform.position = finalDest;
|
||||
|
||||
// 恢复组件状态
|
||||
if (rb != null) rb.isKinematic = wasRbKinematic;
|
||||
if (cc != null && wasCcActive) cc.enabled = true;
|
||||
if (agent != null && wasAgentActive)
|
||||
{
|
||||
agent.enabled = true;
|
||||
agent.Warp(finalDest);
|
||||
}
|
||||
|
||||
// 解锁移动和旋转输入
|
||||
canMove.Modify(true, 99);
|
||||
canRotate.Modify(true, 99);
|
||||
|
||||
// 最终朝向目标
|
||||
SmartTurnToTarget(target, 360f, true);
|
||||
|
||||
// 触发完成回调
|
||||
onComplete?.Invoke();
|
||||
},
|
||||
onUpdate: () =>
|
||||
{
|
||||
elapsed += DeltaTime;
|
||||
float t = Mathf.Clamp01(elapsed / duration);
|
||||
|
||||
// 实时获取当前目标最新位置对应的安全落点,实现平滑追踪
|
||||
Vector3 currentDest = GetSafePositionNearTarget(target, stopDistance);
|
||||
Vector3 currentPos = Vector3.Lerp(startPos, currentDest, t);
|
||||
|
||||
if (rb != null) rb.position = currentPos;
|
||||
owner.transform.position = currentPos;
|
||||
|
||||
// 移动中保持看向目标
|
||||
SmartTurnToTarget(target, 360f, true);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 静态坐标折跃:瞬移/闪现至目标位置(支持高速移动过程,无追踪)
|
||||
/// </summary>
|
||||
/// <param name="destination">目标位置</param>
|
||||
/// <param name="duration">移动时间(秒)。若为 0 则为即时瞬移</param>
|
||||
/// <param name="onComplete">瞬移完成后(或即时瞬移时)的回调函数,可用于生成 VFX/音效</param>
|
||||
/// <param name="onComplete">瞬移完成后(或即时瞬移时)的回调函数</param>
|
||||
public void Teleport(Vector3 destination, float duration, Action onComplete = null)
|
||||
{
|
||||
// 1. 进行 NavMesh 采样验证(寻找最近的可行进点)
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Cielonos.MainGame.Characters
|
||||
public partial class SelfTimeSubmodule : SubmoduleBase<CharacterBase>
|
||||
{
|
||||
private TimeManager timeManager => TimeManager.Instance;
|
||||
private IObservable<float> timeScaleObservable;
|
||||
public IObservable<float> timeScaleObservable { get; private set; }
|
||||
|
||||
public FloatReactiveProperty localTimeScale;
|
||||
private float globalTimeScale => timeManager.globalTimeScale.Value;
|
||||
|
||||
@@ -40,12 +40,15 @@ namespace Cielonos.MainGame.Characters
|
||||
{
|
||||
backpackSm.ObtainItem<Polychrome>();
|
||||
backpackSm.ObtainItem<FutureWand>();
|
||||
backpackSm.ObtainItem<DualHarmony>();
|
||||
//backpackSm.ObtainItem<DualHarmony>();
|
||||
backpackSm.ObtainItem<Ascension>();
|
||||
backpackSm.ObtainItem<Passion>();
|
||||
backpackSm.ObtainItem<ThermalDetonator>();
|
||||
backpackSm.ObtainItem<PhotonWarper>();
|
||||
|
||||
backpackSm.ObtainItem<PhotonDissociator>();
|
||||
backpackSm.ObtainItem<PhotonAccelerator>();
|
||||
backpackSm.ObtainItem<PhotonPolarizer>();
|
||||
backpackSm.ObtainItem<DisorderedPhotonCollector>();
|
||||
foreach (MainWeaponBase mainWeapon in backpackSm.mainWeapons)
|
||||
{
|
||||
equipmentSm.PrepareMainWeapon(mainWeapon);
|
||||
|
||||
@@ -95,6 +95,9 @@ namespace Cielonos.MainGame
|
||||
[BoxGroup("VFX"), LabelWidth(150)]
|
||||
[PropertyTooltip("附加特效(可空)")]
|
||||
public List<GameObject> otherVFXList;
|
||||
[BoxGroup("VFX"), LabelWidth(150)]
|
||||
[PropertyTooltip("特殊标记")]
|
||||
public List<string> tags;
|
||||
|
||||
[BoxGroup("Transform"), LabelWidth(150)]
|
||||
[PropertyTooltip("特效生成时的位置偏移")]
|
||||
|
||||
@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
/// </summary>
|
||||
public class BlackHoleDisplacer : ExtenderBase
|
||||
{
|
||||
|
||||
public override void OnObtained()
|
||||
{
|
||||
hostType = typeof(FutureWand);
|
||||
base.OnObtained();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
/// </summary>
|
||||
public class MissileSeparationMembrane : ExtenderBase
|
||||
{
|
||||
|
||||
public override void OnObtained()
|
||||
{
|
||||
hostType = typeof(FutureWand);
|
||||
base.OnObtained();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 无序光子收集器 / Disordered Photon Collector
|
||||
/// Polychrome的扩展器,使用UltimateAttack之后,如果Passion等级为C级别,直接提升到A级的0%进度;如果是其它等级,直接提升一级(100%进度)。
|
||||
/// </summary>
|
||||
public class DisorderedPhotonCollector : ExtenderBase
|
||||
{
|
||||
public override void OnObtained()
|
||||
{
|
||||
hostType = typeof(Polychrome);
|
||||
base.OnObtained();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 168b9a81645e3d54bb1f412198dec8fb
|
||||
@@ -0,0 +1,13 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
/// <summary>
|
||||
/// 光子加速器 / Photon Accelerator
|
||||
/// Polychrome的扩展器,让所有轻攻击(Attack-L)的“StartUp”Interval,加速1.5倍。
|
||||
/// </summary>
|
||||
public class PhotonAccelerator : ExtenderBase
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 642c7e63e814fe3479d8424f8b612701
|
||||
@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
/// </summary>
|
||||
public class PhotonDissociator : ExtenderBase
|
||||
{
|
||||
|
||||
public override void OnObtained()
|
||||
{
|
||||
hostType = typeof(Polychrome);
|
||||
base.OnObtained();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
/// </summary>
|
||||
public class PhotonPolarizer : ExtenderBase
|
||||
{
|
||||
|
||||
public override void OnObtained()
|
||||
{
|
||||
hostType = typeof(Polychrome);
|
||||
base.OnObtained();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,10 +8,6 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
/// </summary>
|
||||
public class PhotonWarper : ExtenderBase
|
||||
{
|
||||
public override void OnObtained()
|
||||
{
|
||||
hostType = typeof(Polychrome);
|
||||
base.OnObtained();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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