FututeWand初步

This commit is contained in:
SoulliesOfficial
2026-07-01 06:32:50 -04:00
parent ddd387ef35
commit 347237443f
89 changed files with 290771 additions and 1084 deletions

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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;
}
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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;

View File

@@ -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 采样验证(寻找最近的可行进点)

View File

@@ -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;

View File

@@ -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);

View File

@@ -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("特效生成时的位置偏移")]

View File

@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
/// </summary>
public class BlackHoleDisplacer : ExtenderBase
{
public override void OnObtained()
{
hostType = typeof(FutureWand);
base.OnObtained();
}
}
}

View File

@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
/// </summary>
public class MissileSeparationMembrane : ExtenderBase
{
public override void OnObtained()
{
hostType = typeof(FutureWand);
base.OnObtained();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 168b9a81645e3d54bb1f412198dec8fb

View File

@@ -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
{
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 642c7e63e814fe3479d8424f8b612701

View File

@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
/// </summary>
public class PhotonDissociator : ExtenderBase
{
public override void OnObtained()
{
hostType = typeof(Polychrome);
base.OnObtained();
}
}
}

View File

@@ -8,6 +8,10 @@ namespace Cielonos.MainGame.Inventory.Collections
/// </summary>
public class PhotonPolarizer : ExtenderBase
{
public override void OnObtained()
{
hostType = typeof(Polychrome);
base.OnObtained();
}
}
}

View File

@@ -8,10 +8,6 @@ namespace Cielonos.MainGame.Inventory.Collections
/// </summary>
public class PhotonWarper : ExtenderBase
{
public override void OnObtained()
{
hostType = typeof(Polychrome);
base.OnObtained();
}
}
}

View File

@@ -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;
}

View File

@@ -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]);
}

View File

@@ -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();
}
};*/

View File

@@ -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();
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f21ea98ab9ac5d54f8f5866012e7651c

View File

@@ -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, BA级
{
if (_currentKatanaParticle != string.Empty)
{
GetParticle().Stop();
_currentKatanaParticle = string.Empty;
}
}
else if (level <= 4) // SSS级 (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()

View File

@@ -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);
}
}
}
}
}

View File

@@ -1,2 +0,0 @@
fileFormatVersion: 2
guid: 2a8933650445a8c49a5807c1dcc42969

View File

@@ -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) =>
{

View File

@@ -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, BA级
{
if (_currentKatanaParticle != string.Empty)
{
GetParticle().Stop();
_currentKatanaParticle = string.Empty;
}
}
else if (level <= 4) // SSS级 (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;
}
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 8872dc56cff645f429d7551c98ccfd5b