2026-06-12 17:11:39 -04:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using Cielonos.MainGame.Characters;
|
|
|
|
|
|
using Cielonos.MainGame.Effects.Feedback;
|
2026-06-30 01:48:58 -04:00
|
|
|
|
using Cielonos.MainGame.FunctionalAnimation;
|
2026-06-12 17:11:39 -04:00
|
|
|
|
using Cielonos.MainGame.UI;
|
|
|
|
|
|
using SLSUtilities.Feedback;
|
|
|
|
|
|
using SLSUtilities.General;
|
|
|
|
|
|
using SLSUtilities.FunctionalAnimation;
|
|
|
|
|
|
using UnityEngine;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Cielonos.MainGame.Inventory.Collections
|
|
|
|
|
|
{
|
|
|
|
|
|
public partial class Polychrome : MainWeaponBase
|
|
|
|
|
|
{
|
|
|
|
|
|
private PassionSystem _passionSystem;
|
|
|
|
|
|
|
|
|
|
|
|
private bool _canAirLightAttack;
|
|
|
|
|
|
private bool _canAirHeavyAttack;
|
|
|
|
|
|
|
|
|
|
|
|
protected override void Update()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (player.inventorySc.equipmentSm.currentMainWeapon == this)
|
|
|
|
|
|
{
|
|
|
|
|
|
functionSm?.Update(player.selfTimeSm.DeltaTime);
|
|
|
|
|
|
}
|
2026-06-27 12:52:03 -04:00
|
|
|
|
|
2026-06-30 01:48:58 -04:00
|
|
|
|
/*if (Keyboard.current.xKey.wasPressedThisFrame)
|
2026-06-27 12:52:03 -04:00
|
|
|
|
{
|
|
|
|
|
|
SetBlock();
|
|
|
|
|
|
player.reactionSc.blockSm.GetBlockSource("Polychrome_Block").PerfectBlock(null, player.CenterPosition + player.transform.forward);
|
|
|
|
|
|
RemoveBlock();
|
|
|
|
|
|
}
|
2026-06-30 01:48:58 -04:00
|
|
|
|
|
|
|
|
|
|
if (Keyboard.current.zKey.wasPressedThisFrame)
|
|
|
|
|
|
{
|
|
|
|
|
|
player.reactionSc.dodgeSm.ApplyDodge(DodgeSource.Default(player));
|
|
|
|
|
|
player.reactionSc.dodgeSm.GetCurrentDodgeSource()?.PerfectDodge(null);
|
|
|
|
|
|
player.reactionSc.dodgeSm.RemoveDodge("DefaultDodge");
|
|
|
|
|
|
}*/
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnEquipped()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnEquipped();
|
|
|
|
|
|
|
|
|
|
|
|
_currentKatanaParticle = string.Empty;
|
|
|
|
|
|
|
|
|
|
|
|
player.eventSm.onFirstJump.TryAdd(nameof(Polychrome), new PrioritizedAction(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
_canAirLightAttack = true;
|
|
|
|
|
|
_canAirHeavyAttack = true;
|
|
|
|
|
|
comboSm["AirLight"].Reset();
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
_passionSystem = CombatManager.GetCombatSystem<PassionSystem>();
|
|
|
|
|
|
_passionSystem.OnLevelChanged += OnPassionLevelChanged;
|
|
|
|
|
|
|
|
|
|
|
|
RegisterFunctionsToAnimSc(StayBlocking);
|
|
|
|
|
|
viewObjects["Katana"].SetFadeAnim(0.2f);
|
|
|
|
|
|
viewObjects["Saya"].SetFadeAnim(0.2f);
|
|
|
|
|
|
|
|
|
|
|
|
UpdateViewObjectVisuals();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public override void OnUnequipped()
|
|
|
|
|
|
{
|
|
|
|
|
|
base.OnUnequipped();
|
|
|
|
|
|
_passionSystem.OnLevelChanged -= OnPassionLevelChanged;
|
|
|
|
|
|
player.eventSm.onFirstJump.Remove(nameof(Polychrome));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnPassionLevelChanged(int oldLevel, int newLevel)
|
|
|
|
|
|
{
|
|
|
|
|
|
UpdateViewObjectVisuals();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 武器切入(换出至 Polychrome)时的逻辑入口
|
|
|
|
|
|
/// </summary>
|
2026-06-12 17:11:39 -04:00
|
|
|
|
public override void OnSwitchIn()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_passionSystem.LevelIndex >= 3)
|
|
|
|
|
|
{
|
|
|
|
|
|
List<Enemy> availableEnemies = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 4);
|
|
|
|
|
|
List<Enemy> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
2026-06-13 18:43:40 -04:00
|
|
|
|
Enemy target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies).ApplyScoreModifier(disruptable, 0f, 1f).BestEnemy();
|
|
|
|
|
|
if (target != null)
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
comboSm.main.Reset();
|
|
|
|
|
|
PlayDisruptionAttack(target, "B");
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
2026-06-13 18:43:40 -04:00
|
|
|
|
|
|
|
|
|
|
SetBlock(equipBlockData, false);
|
|
|
|
|
|
player.selfTimeSm.AddLocalTimer(0.4f, () => RemoveBlock(equipBlockData));
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
PlayTargetedAnimation("EquipBlock");
|
|
|
|
|
|
SetBlock(equipBlockData);
|
|
|
|
|
|
player.selfTimeSm.AddLocalTimer(0.4f, () =>
|
|
|
|
|
|
{
|
|
|
|
|
|
RemoveBlock(equipBlockData);
|
|
|
|
|
|
fullBodyFuncAnimSm.Stop("EquipBlock");
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 轻攻击(普攻)输入处理
|
|
|
|
|
|
/// </summary>
|
2026-06-12 17:11:39 -04:00
|
|
|
|
public override void OnPrimaryPress()
|
|
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (player.statusSm.HasStatus(StatusType.Stun) ||
|
|
|
|
|
|
player.reactionSc.blockSm.HaveBlockSource(blockData.blockName))
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
// 空中轻攻击连击
|
2026-06-12 17:11:39 -04:00
|
|
|
|
if (player.landMovementSc.isJumping)
|
|
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (_canAirLightAttack && functionSm["LightAttack"].IsAvailable())
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
string airNodeName = comboSm["AirLight"].GetNextNodeName("L");
|
|
|
|
|
|
if (PlayAirAttackL(airNodeName))
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
comboSm["AirLight"].NextCombo("L");
|
|
|
|
|
|
if (comboSm["AirLight"].GetCurrentNodeName() == "L1")
|
|
|
|
|
|
{
|
|
|
|
|
|
_canAirLightAttack = false;
|
|
|
|
|
|
}
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CharacterBase target = CombatManager.EnemySm.GetBestEnemy(5);
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
// 冲刺/奔跑攻击
|
2026-06-12 17:11:39 -04:00
|
|
|
|
if (player.landMovementSc.isSprinting && functionSm["LightAttack"].IsAvailable())
|
|
|
|
|
|
{
|
|
|
|
|
|
comboSm.main.Reset();
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (PlayRunAttack(target))
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
|
|
|
|
|
comboSm.main.NextCombo("L");
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
// 常规地面轻攻击连击
|
2026-06-12 17:11:39 -04:00
|
|
|
|
if (functionSm["LightAttack"].IsAvailable())
|
|
|
|
|
|
{
|
|
|
|
|
|
string nextNodeName = comboSm.main.GetNextNodeName("L");
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (PlayNormalAttackL(target, nextNodeName))
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
|
|
|
|
|
comboSm.main.NextCombo("L");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 重攻击/特殊/闪避反击输入处理
|
|
|
|
|
|
/// </summary>
|
2026-06-12 17:11:39 -04:00
|
|
|
|
public override void OnSecondaryPress()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (player.statusSm.HasStatus(StatusType.Stun))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
List<Enemy> availableEnemies = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 4);
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (PlayParryAttack(availableEnemies)) return;
|
|
|
|
|
|
if (player.reactionSc.blockSm.HaveBlockSource(blockData.blockName)) return;
|
2026-06-12 17:11:39 -04:00
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
// 空中重攻击下砸
|
2026-06-12 17:11:39 -04:00
|
|
|
|
if (player.landMovementSc.isJumping)
|
|
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (_canAirHeavyAttack && functionSm["HeavyAttack"].IsAvailable())
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
|
|
|
|
|
comboSm.main.Reset();
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (PlayAirAttackR())
|
|
|
|
|
|
{
|
|
|
|
|
|
_canAirLightAttack = false;
|
|
|
|
|
|
_canAirHeavyAttack = false;
|
|
|
|
|
|
}
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
// 颠覆攻击
|
2026-06-30 01:48:58 -04:00
|
|
|
|
if (player.inputSc.IsHoldingSpecialB)
|
2026-06-12 17:11:39 -04:00
|
|
|
|
{
|
|
|
|
|
|
if (functionSm["DisruptionAttack"].IsAvailable())
|
|
|
|
|
|
{
|
|
|
|
|
|
bool isEnhanced = _passionSystem.LevelIndex >= 3;
|
|
|
|
|
|
string suffix = isEnhanced ? "B" : "A";
|
|
|
|
|
|
List<Enemy> disruptable = CombatManager.EnemySm.GetDisruptableEnemies(availableEnemies);
|
2026-06-13 18:43:40 -04:00
|
|
|
|
Enemy target = CombatManager.EnemySm.GetScoredEnemies(availableEnemies).ApplyScoreModifier(disruptable, 0f, 1f).BestEnemy();
|
2026-07-01 06:32:50 -04:00
|
|
|
|
|
|
|
|
|
|
comboSm.main.Reset();
|
|
|
|
|
|
PlayDisruptionAttack(target, suffix);
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
// 常规地面重攻击连击
|
2026-06-12 17:11:39 -04:00
|
|
|
|
if (functionSm["HeavyAttack"].IsAvailable())
|
|
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
Enemy target;
|
2026-06-30 01:48:58 -04:00
|
|
|
|
bool shouldWarp = false;
|
2026-07-01 06:32:50 -04:00
|
|
|
|
string suffix = comboSm.main.GetNextNodeName("R");
|
|
|
|
|
|
|
|
|
|
|
|
// 携带 PhotonWarper 时的中远距离敌人折跃判定
|
|
|
|
|
|
if (!HasExtender<PhotonWarper>())
|
2026-06-30 01:48:58 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
target = CombatManager.EnemySm.GetBestEnemy(availableEnemies);
|
2026-06-30 01:48:58 -04:00
|
|
|
|
}
|
2026-07-01 06:32:50 -04:00
|
|
|
|
else
|
2026-06-30 01:48:58 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
List<Enemy> fartherRange = CombatManager.EnemySm.GetEnemiesInRadius(player.transform.position, 15);
|
|
|
|
|
|
target = CombatManager.EnemySm.GetBestEnemy(fartherRange);
|
|
|
|
|
|
if (target != null && suffix == "RA")
|
2026-06-30 01:48:58 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
float distance = Vector3.Distance(player.transform.position, target.transform.position);
|
|
|
|
|
|
if (distance > 2f)
|
2026-06-30 01:48:58 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
shouldWarp = true;
|
|
|
|
|
|
player.movementSc.SmartTurnToTarget(target, 360, true);
|
|
|
|
|
|
}
|
2026-06-30 01:48:58 -04:00
|
|
|
|
}
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
2026-07-01 06:32:50 -04:00
|
|
|
|
|
|
|
|
|
|
if (PlayNormalAttackR(target, suffix))
|
2026-06-30 01:48:58 -04:00
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
comboSm.main.NextCombo("R");
|
|
|
|
|
|
if (shouldWarp)
|
|
|
|
|
|
{
|
|
|
|
|
|
FuncAnimInterval interval = fullBodyFuncAnimSm.currentData.Interval(IntervalType.Startup);
|
|
|
|
|
|
WarpToTarget(target, interval, 1f);
|
|
|
|
|
|
}
|
2026-06-30 01:48:58 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 终极攻击(次元斩)输入处理
|
|
|
|
|
|
/// </summary>
|
2026-06-30 01:48:58 -04:00
|
|
|
|
public override void OnSpecialAPress()
|
|
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
if (player.statusSm.HasStatus(StatusType.Stun))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-30 01:48:58 -04:00
|
|
|
|
if (functionSm["UltimateAttack"].IsAvailable())
|
|
|
|
|
|
{
|
|
|
|
|
|
PlayTargetedAnimation("UltimateAttack");
|
|
|
|
|
|
functionSm["UltimateAttack"].Execute();
|
2026-07-01 06:32:50 -04:00
|
|
|
|
ApplyUltimatePassionBoost();
|
2026-06-30 01:48:58 -04:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 格挡输入处理
|
|
|
|
|
|
/// </summary>
|
2026-06-12 17:11:39 -04:00
|
|
|
|
public override void OnSpecialCPress()
|
|
|
|
|
|
{
|
|
|
|
|
|
comboSm.main.Reset();
|
|
|
|
|
|
SetBlock();
|
|
|
|
|
|
|
|
|
|
|
|
if (!player.statusSm.HasStatus(StatusType.Stun))
|
|
|
|
|
|
{
|
|
|
|
|
|
CharacterBase target = CombatManager.EnemySm.GetBestEnemy(5);
|
|
|
|
|
|
if (fullBodyFuncAnimSm.Stop(DisruptionType.ForcedAction))
|
|
|
|
|
|
{
|
|
|
|
|
|
PlayTargetedAnimation("Block", target, 2f, false, true, upperBodyFuncAnimSm, 1f, 0.1f, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 06:32:50 -04:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 格挡输入释放
|
|
|
|
|
|
/// </summary>
|
2026-06-12 17:11:39 -04:00
|
|
|
|
public override void OnSpecialCRelease()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (upperBodyFuncAnimSm.currentRuntimeFuncAnim is { animationName: "Block" })
|
|
|
|
|
|
{
|
2026-07-01 06:32:50 -04:00
|
|
|
|
upperBodyFuncAnimSm.Stop(DisruptionType.Must);
|
2026-06-12 17:11:39 -04:00
|
|
|
|
}
|
|
|
|
|
|
player.selfTimeSm.AddLocalTimer(0.04f, () => RemoveBlock());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-07-01 06:32:50 -04:00
|
|
|
|
|
2026-06-12 17:11:39 -04:00
|
|
|
|
public partial class Polychrome
|
|
|
|
|
|
{
|
|
|
|
|
|
public override Dictionary<string, string> GetDescriptionArgs()
|
|
|
|
|
|
{
|
|
|
|
|
|
var args = new Dictionary<string, string>();
|
|
|
|
|
|
if (attackData == null) return args;
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 将下列 AttackUnit key 替换为 AttackData ScriptableObject 中的实际 key 名
|
|
|
|
|
|
TryPopulateAttack(args, "light", "ProbingAttack");
|
|
|
|
|
|
// TryPopulateAttack(args, "heavy", "HeavySlash");
|
|
|
|
|
|
// TryPopulateAttack(args, "disruption_a", "DisruptionA");
|
|
|
|
|
|
// TryPopulateAttack(args, "disruption_b", "DisruptionB");
|
|
|
|
|
|
// TryPopulateAttack(args, "disruption_c", "DisruptionC");
|
|
|
|
|
|
|
|
|
|
|
|
return args;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void TryPopulateAttack(Dictionary<string, string> args, string prefix, string unitKey)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (attackData.attackUnits.TryGetValue(unitKey, out AttackUnit unit))
|
|
|
|
|
|
{
|
|
|
|
|
|
DisplayTextResolver.PopulateAttackArgs(args, prefix, unit, player);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|