整合SLSUtilities
This commit is contained in:
@@ -1,5 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using Cielonos.MainGame.FunctionalAnimation;
|
||||
using Sirenix.OdinInspector;
|
||||
@@ -19,7 +21,7 @@ namespace Cielonos.MainGame.Inventory
|
||||
private List<string> registeredFunctionNames = new List<string>();
|
||||
|
||||
[TitleGroup("Data")]
|
||||
public List<FuncAnimData> fullBodyFuncAnims = new List<FuncAnimData>();
|
||||
public FuncAnimDataCollection fullBodyFuncAnims;
|
||||
public ContentData contentData;
|
||||
public ViewObjectData viewObjectData;
|
||||
public VFXData vfxData;
|
||||
@@ -112,10 +114,10 @@ namespace Cielonos.MainGame.Inventory
|
||||
|
||||
public partial class ItemBase
|
||||
{
|
||||
protected RuntimeFuncAnim PlayTargetedAnimation(string animationName, CharacterBase target = null,
|
||||
float adsorptionMinDistance = 1f, bool autoRotate = true,
|
||||
FunctionalAnimationSubmodule funcAnimSm = null,
|
||||
float animationSpeedMultiplier = 1f, float transitionDuration = 0.1f, bool isNormalizedTransition = false)
|
||||
protected bool PlayTargetedAnimation(string animationName, CharacterBase target = null,
|
||||
float adsorptionMinDistance = 1f, bool autoRotate = true, FunctionalAnimationSubmodule funcAnimSm = null,
|
||||
float animationSpeedMultiplier = 1f, float transitionDuration = 0.1f, bool isNormalizedTransition = false,
|
||||
string comboTreeName = "Main")
|
||||
{
|
||||
|
||||
funcAnimSm ??= fullBodyFuncAnimSm;
|
||||
@@ -124,7 +126,7 @@ namespace Cielonos.MainGame.Inventory
|
||||
{
|
||||
float actionCoolDownTime = fullBodyFuncAnimSm.currentData.Interval(IntervalType.ActionDisruption).StartTime /
|
||||
fullBodyFuncAnimSm.currentPlaySpeedMultiplier;
|
||||
comboSm?.SuspendThenSetup(actionCoolDownTime);
|
||||
comboSm?[comboTreeName].SuspendThenSetup(actionCoolDownTime);
|
||||
//GameManager.Player.eventController.GeneralAttackEvents.InvokeAllEvents();
|
||||
|
||||
if (target != null)
|
||||
@@ -133,10 +135,10 @@ namespace Cielonos.MainGame.Inventory
|
||||
funcAnimSm.currentRuntimeFuncAnim.AddUpdateUntilEvent(new SetRootAdsorptionAdjustment(target, adsorptionMinDistance));
|
||||
}
|
||||
|
||||
return funcAnimSm.currentRuntimeFuncAnim;
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -144,7 +146,9 @@ namespace Cielonos.MainGame.Inventory
|
||||
{
|
||||
public void RegisterFullBodyFuncAnims()
|
||||
{
|
||||
foreach (FuncAnimData funcAnim in fullBodyFuncAnims)
|
||||
if (fullBodyFuncAnims == null) return;
|
||||
|
||||
foreach (FuncAnimData funcAnim in fullBodyFuncAnims.animDataList)
|
||||
{
|
||||
player.animationSc.fullBodyFuncAnimSm.Add(funcAnim);
|
||||
}
|
||||
@@ -166,19 +170,23 @@ namespace Cielonos.MainGame.Inventory
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void RegisterFunctionsToAnimSc(params Action<RuntimeFuncAnim>[] functions)
|
||||
protected virtual void RegisterFunctionsToAnimSc()
|
||||
{
|
||||
foreach (Action<RuntimeFuncAnim> function in functions)
|
||||
foreach (CustomFunction function in fullBodyFuncAnims.preloadFunctions)
|
||||
{
|
||||
string functionName = function.Method.Name;
|
||||
if (!player.animationSc.registeredFunctions.TryAdd(functionName, function))
|
||||
string functionName = function.functionName;
|
||||
string FAPF_functionName = new StringBuilder(functionName).Insert(0, "FAPF_").ToString();
|
||||
MethodInfo method = GetType().GetMethod(FAPF_functionName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
|
||||
|
||||
if (method == null)
|
||||
{
|
||||
Debug.LogWarning($"Function {functionName} is already registered.");
|
||||
}
|
||||
else
|
||||
{
|
||||
registeredFunctionNames.Add(functionName);
|
||||
}
|
||||
Debug.LogWarning($"Function {functionName} not found in {this.GetType().Name}. Skipping registration.");
|
||||
continue;
|
||||
}
|
||||
|
||||
Action<RuntimeFuncAnim> action = Delegate.CreateDelegate(typeof(Action<RuntimeFuncAnim>), this, method) as Action<RuntimeFuncAnim>;
|
||||
registeredFunctionNames.Add(functionName);
|
||||
player.animationSc.registeredFunctions.TryAdd(functionName, action);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,17 +201,5 @@ namespace Cielonos.MainGame.Inventory
|
||||
}
|
||||
registeredFunctionNames.Clear();
|
||||
}
|
||||
|
||||
protected virtual void RemoveFunctionsFromAnimSc(params Action[] functions)
|
||||
{
|
||||
foreach (Action function in functions)
|
||||
{
|
||||
string functionName = function.Method.Name;
|
||||
if (!player.animationSc.registeredFunctions.Remove(functionName))
|
||||
{
|
||||
Debug.LogWarning($"Function {functionName} is not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Sirenix.OdinInspector;
|
||||
using SoftCircuits.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory
|
||||
@@ -11,28 +12,32 @@ namespace Cielonos.MainGame.Inventory
|
||||
/// 这是你的“蓝图”资产。
|
||||
/// </summary>
|
||||
[CreateAssetMenu(fileName = "ComboData", menuName = "Cielonos/Items/ComboData")]
|
||||
public class ComboData : ScriptableObject
|
||||
public class ComboData : SerializedScriptableObject
|
||||
{
|
||||
public float defaultDisappearTime;
|
||||
//[InlineProperty] public ComboTreeData tree;
|
||||
|
||||
[InlineProperty] [HideLabel] public ComboTreeData tree;
|
||||
[InlineProperty]
|
||||
[Title("连招树编辑器", "可视化编辑连招树的节点和分支")]
|
||||
public Dictionary<string, ComboTreeData> comboTrees;
|
||||
|
||||
public ComboTreeData mainTree => comboTrees["Main"];
|
||||
|
||||
[OnInspectorInit]
|
||||
private void OnEnable()
|
||||
{
|
||||
defaultDisappearTime = 0.25f;
|
||||
tree ??= new ComboTreeData();
|
||||
|
||||
// 确保总有一个 RootNode
|
||||
if (tree.nodes == null || tree.nodes.Count == 0)
|
||||
if (comboTrees == null)
|
||||
{
|
||||
tree.nodes = new List<ComboNodeData>();
|
||||
// 添加不可删除的 RootNode
|
||||
tree.nodes.Add(new ComboNodeData() { referenceName = "Root" });
|
||||
}
|
||||
comboTrees = new Dictionary<string, ComboTreeData>();
|
||||
ComboTreeData tree = new ComboTreeData();
|
||||
if (tree.nodes == null || tree.nodes.Count == 0)
|
||||
{
|
||||
tree.nodes = new List<ComboNodeData>();
|
||||
tree.nodes.Add(new ComboNodeData() { referenceName = "Root" });
|
||||
}
|
||||
|
||||
// 关键一步:将树实例传递给所有子节点,以便它们能填充下拉列表
|
||||
tree.InitializeNodeReferences();
|
||||
tree.InitializeNodeReferences();
|
||||
comboTrees["Main"] = tree;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -42,17 +47,17 @@ namespace Cielonos.MainGame.Inventory
|
||||
[Serializable]
|
||||
public class ComboTreeData
|
||||
{
|
||||
// --- 修改点 ---
|
||||
// 将标题移到这里,使其在内联属性的顶部显示
|
||||
[Title("连招树编辑器", "可视化编辑连招树的节点和分支")]
|
||||
// --- 修改结束 ---
|
||||
public float resetTime = 0.2f;
|
||||
|
||||
[ListDrawerSettings(
|
||||
DraggableItems = true, // 现在可以安全地拖动排序了!
|
||||
NumberOfItemsPerPage = 20,
|
||||
CustomAddFunction = "AddNode",
|
||||
CustomRemoveElementFunction = "RemoveNode")]
|
||||
CustomRemoveElementFunction = "RemoveNode",
|
||||
ListElementLabelName = "GetComboNodeLabel")]
|
||||
[HideReferenceObjectPicker]
|
||||
public List<ComboNodeData> nodes;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Odin Inspector 自定义添加按钮的实现
|
||||
/// </summary>
|
||||
@@ -107,9 +112,9 @@ namespace Cielonos.MainGame.Inventory
|
||||
return nodes.Select(n => n.referenceName).Where(n => !string.IsNullOrEmpty(n));
|
||||
}
|
||||
|
||||
public ComboSubmodule.ComboTree ToRuntime()
|
||||
public ComboSubmodule.ComboTree ToRuntime(ComboSubmodule owner)
|
||||
{
|
||||
var runtimeTree = new ComboSubmodule.ComboTree();
|
||||
var runtimeTree = new ComboSubmodule.ComboTree(owner, resetTime);
|
||||
runtimeTree.nodes = new List<ComboSubmodule.ComboTree.Node>();
|
||||
|
||||
// 创建运行时节点
|
||||
@@ -129,6 +134,7 @@ namespace Cielonos.MainGame.Inventory
|
||||
|
||||
return runtimeTree;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -141,7 +147,8 @@ namespace Cielonos.MainGame.Inventory
|
||||
[InfoBox("这是 Root 节点,是所有连招的起点。", InfoMessageType.Info, "IsRootNode")]
|
||||
public string referenceName;
|
||||
|
||||
[ListDrawerSettings(NumberOfItemsPerPage = 5)]
|
||||
[ListDrawerSettings(NumberOfItemsPerPage = 10, CustomAddFunction = "AddBranch")]
|
||||
[HideReferenceObjectPicker]
|
||||
public List<ComboBranchData> branches = new List<ComboBranchData>();
|
||||
|
||||
private bool IsRootNode() => referenceName == "Root";
|
||||
@@ -156,6 +163,31 @@ namespace Cielonos.MainGame.Inventory
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void AddBranch()
|
||||
{
|
||||
branches.Add(new ComboBranchData());
|
||||
}
|
||||
|
||||
private string GetComboNodeLabel()
|
||||
{
|
||||
// 1. 获取当前节点名称
|
||||
string name = string.IsNullOrEmpty(referenceName) ? "[未命名]" : referenceName;
|
||||
|
||||
// 2. 如果没有分支,直接返回节点名
|
||||
if (branches == null || branches.Count == 0)
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
// 3. 拼接子节点名称
|
||||
// 这里使用 Linq 提取所有分支的目标节点名
|
||||
// 格式示例: "Root - (1, 2, 3)"
|
||||
List<string> targetNodes = branches
|
||||
.Select(b => b == null || string.IsNullOrEmpty(b.nextNodeRefName) ? "?" : $"{b.operation}->{b.nextNodeRefName}").ToList();
|
||||
|
||||
return $"{name} - ({string.Join(", ", targetNodes)})";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -171,7 +203,5 @@ namespace Cielonos.MainGame.Inventory
|
||||
[HorizontalGroup("Branch")]
|
||||
[LabelText("下一个节点")]
|
||||
public string nextNodeRefName; // <-- 使用名称引用,而不是索引
|
||||
|
||||
// --- Odin 辅助字段和方法 ---
|
||||
}
|
||||
}
|
||||
@@ -23,16 +23,16 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
if (functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.NextCombo("L");
|
||||
comboSm.main.NextCombo("L");
|
||||
functionSm["LightAttack"].Execute();
|
||||
currentTarget = BattleManager.EnemySm.GetNearestEnemy(25f);
|
||||
if (currentTarget != null)
|
||||
{
|
||||
PlayTargetedAnimation("LightAttack" + comboSm.GetCurrentNodeName(), currentTarget, 5f);
|
||||
PlayTargetedAnimation("LightAttack" + comboSm.main.GetCurrentNodeName(), currentTarget, 5f);
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayTargetedAnimation("LightAttack" + comboSm.GetCurrentNodeName());
|
||||
PlayTargetedAnimation("LightAttack" + comboSm.main.GetCurrentNodeName());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
if (functionSm["HeavyAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
comboSm.main.Reset();
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
currentTarget = BattleManager.EnemySm.GetNearestEnemy(10f);
|
||||
if (currentTarget != null)
|
||||
|
||||
@@ -1,9 +1,14 @@
|
||||
using AutoLOD.MeshDecimator.QualityMeshDecimator.Internal;
|
||||
using Cielonos.MainGame.Buffs;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using Cielonos.MainGame.FunctionalAnimation;
|
||||
using Cielonos.UI;
|
||||
using SLSFramework.General;
|
||||
using SLSUtilities.FunctionalAnimation;
|
||||
using SoftCircuits.Collections;
|
||||
using Unity.Cinemachine;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
|
||||
namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
@@ -12,6 +17,9 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
public BlockData equipBlockData;
|
||||
public float perfectBlockedTimer;
|
||||
|
||||
private bool canAirLightAttack;
|
||||
private bool canAirHeavyAttack;
|
||||
|
||||
protected override void Update()
|
||||
{
|
||||
if (player.inventorySc.equipmentSm.currentMainWeapon == this)
|
||||
@@ -24,11 +32,15 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
public override void OnEquipped()
|
||||
{
|
||||
base.OnEquipped();
|
||||
RegisterFunctionsToAnimSc(
|
||||
LightAttack0, LightAttack1, LightAttack2, LightAttack3,
|
||||
TripleAttack_0, TripleAttack_1, TripleAttack_2,
|
||||
DisruptAttack, HeavyAttack, RunAttack, ParryAttack, StayBlocking);
|
||||
|
||||
|
||||
player.eventSm.onFirstJump.Add("PolyChrome_OnFirstJump", new PrioritizedAction(() =>
|
||||
{
|
||||
canAirLightAttack = true;
|
||||
canAirHeavyAttack = true;
|
||||
comboSm["AirLight"].Reset();
|
||||
}));
|
||||
RegisterFunctionsToAnimSc();
|
||||
RegisterFunctionsToAnimSc(ImpaleLine, RunAttack, ParryAttack, StayBlocking);
|
||||
viewObjects["Katana"].SetFadeAnim(0.2f);
|
||||
viewObjects["Saya"].SetFadeAnim(0.2f);
|
||||
|
||||
@@ -42,23 +54,40 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
fullBodyFuncAnimSm.Stop("EquipBlock");
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
public override void OnUnequipped()
|
||||
{
|
||||
base.OnUnequipped();
|
||||
player.eventSm.onFirstJump.Remove("PolyChrome_OnFirstJump");
|
||||
}
|
||||
|
||||
|
||||
public override void OnPrimaryPress()
|
||||
{
|
||||
if (player.inputSc.IsHoldingSpecialA && functionSm["TripleAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
if (player.landMovementSc.isJumping)
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
functionSm["TripleAttack"].Execute();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
PlayTargetedAnimation("TripleAttack", target, 1f);
|
||||
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
|
||||
if (!canAirLightAttack || !functionSm["LightAttack"].IsAvailable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayTargetedAnimation("AirLightAttack" + comboSm["AirLight"].GetNextNodeName("L")))
|
||||
{
|
||||
comboSm["AirLight"].NextCombo("L");
|
||||
functionSm["LightAttack"].Execute();
|
||||
|
||||
if (comboSm["AirLight"].GetCurrentNodeName() == "1")
|
||||
{
|
||||
canAirLightAttack = false;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.landMovementSc.isSprinting && functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
comboSm.main.Reset();
|
||||
functionSm["LightAttack"].Execute();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(8);
|
||||
PlayTargetedAnimation("RunAttack", target, 1f);
|
||||
@@ -66,29 +95,42 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
return;
|
||||
}
|
||||
|
||||
if (functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
if (functionSm["LightAttack"].IsAvailable())
|
||||
{
|
||||
comboSm.NextCombo("L");
|
||||
functionSm["LightAttack"].Execute();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
PlayTargetedAnimation("LightAttack" + comboSm.GetCurrentNodeName(), target, 1f);
|
||||
if (PlayTargetedAnimation("Attack" + comboSm.main.GetNextNodeName("L"), target))
|
||||
{
|
||||
comboSm.main.NextCombo("L");
|
||||
functionSm["LightAttack"].Execute();
|
||||
}
|
||||
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnSecondaryPress()
|
||||
{
|
||||
if (player.landMovementSc.isJumping && functionSm["HeavyAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
if (player.landMovementSc.isJumping)
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
PlayTargetedAnimation("AirAttackStart0");
|
||||
if (!canAirHeavyAttack || !functionSm["HeavyAttack"].IsAvailable())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (PlayTargetedAnimation("AirHeavyAttackStart"))
|
||||
{
|
||||
player.landMovementSc.ExtraJump();
|
||||
comboSm.main.Reset();
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
canAirLightAttack = false;
|
||||
canAirHeavyAttack = false;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (perfectBlockedTimer > 0f && functionSm["HeavyAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability(DisruptionType.ForcedAction))
|
||||
{
|
||||
perfectBlockedTimer = 0f;
|
||||
comboSm.ResetCombo();
|
||||
comboSm.main.Reset();
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
PlayTargetedAnimation("ParryAttack", target, 1f);
|
||||
@@ -96,24 +138,23 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
return;
|
||||
}
|
||||
|
||||
if (player.inputSc.IsHoldingSpecialA &&
|
||||
functionSm["DisruptAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
if (functionSm["HeavyAttack"].IsAvailable())
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
functionSm["DisruptAttack"].Execute();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
PlayTargetedAnimation("DisruptAttack", target, 1f);
|
||||
if (PlayTargetedAnimation("Attack" + comboSm.main.GetNextNodeName("R"), target))
|
||||
{
|
||||
comboSm.main.NextCombo("R");
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
}
|
||||
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
|
||||
return;
|
||||
}
|
||||
|
||||
if (functionSm["HeavyAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
}
|
||||
|
||||
public override void OnSpecialAPress()
|
||||
{
|
||||
if (PlayTargetedAnimation("SkillA"))
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
functionSm["HeavyAttack"].Execute();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
PlayTargetedAnimation("HeavyAttack", target, 1f);
|
||||
//player.viewSc.cameraRotationSm.TriggerCameraRecenter(player.transform.forward);
|
||||
comboSm.main.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -121,7 +162,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
if (functionSm["Block"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability(DisruptionType.ForcedAction))
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
comboSm.main.Reset();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
PlayTargetedAnimation("Block", target, 2f, true, null, 1f, 0.1f, true);
|
||||
SetBlock();
|
||||
@@ -145,17 +186,21 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
|
||||
public partial class Polychrome
|
||||
{
|
||||
private void LightAttack0() => GenerateNormalSlash("LightAttack0", new Vector3(0.5f, 1f, 0));
|
||||
private void LightAttack1() => GenerateNormalSlash("LightAttack1", new Vector3(-0.5f, -1f, 0));
|
||||
private void LightAttack2() => GenerateNormalSlash("LightAttack2", new Vector3(0.8f, -1.2f, 0));
|
||||
private void LightAttack3() => GenerateNormalSlash("LightAttack3", new Vector3(0.5f, 1.5f, 0));
|
||||
private void TripleAttack_0() => GenerateFastSlash("TripleAttack_0", new Vector3(1f, 0.6f, 0));
|
||||
private void TripleAttack_1() => GenerateFastSlash("TripleAttack_1", new Vector3(-1f, -0.6f, 0));
|
||||
private void TripleAttack_2() => GenerateFastSlash("TripleAttack_2", new Vector3(0.6f, 1f, 0));
|
||||
private void HeavyAttack() => GenerateHeavySlash("HeavyAttack", new Vector3(3, -2, -5));
|
||||
private void FAPF_GenerateNormalSlash(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
CustomFunction.PC_String p = rtFuncAnim.GetParams<CustomFunction.PC_String>();
|
||||
GenerateNormalSlash(p.str0, Vector3.zero);
|
||||
}
|
||||
|
||||
private void FAPF_GenerateHeavySlash(RuntimeFuncAnim rtFuncAnim)
|
||||
{
|
||||
CustomFunction.PC_String p = rtFuncAnim.GetParams<CustomFunction.PC_String>();
|
||||
GenerateHeavySlash(p.str0, Vector3.zero);
|
||||
}
|
||||
|
||||
private void ImpaleLine() => GenerateHeavySlash("ImpaleLine", new Vector3(0, 1f, 0));
|
||||
private void RunAttack() => GenerateMoveSlash("RunAttack", new Vector3(1f, 0.6f, 0), player.transform.forward * 10f);
|
||||
private void ParryAttack() => GenerateParrySlash("ParryAttack", new Vector3(5, 3, -8));
|
||||
private void DisruptAttack() => GenerateDisruptSlash("DisruptAttack", new Vector3(0.6f, 2f, 0));
|
||||
}
|
||||
|
||||
public partial class Polychrome
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
fullBodyFuncAnimSm.CheckPlayability() && functionSm["AirAttack"].IsAvailable())
|
||||
{
|
||||
fallDamageMultiplier = (player.landMovementSc.groundDetector.GetDistanceToGround() - 1.4f);
|
||||
comboSm.ResetCombo();
|
||||
comboSm.main.Reset();
|
||||
functionSm["AirAttack"].Execute();
|
||||
PlayTargetedAnimation("AirAttackStart");
|
||||
}
|
||||
@@ -34,10 +34,10 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
|
||||
if (functionSm["LightAttack"].IsAvailable() && fullBodyFuncAnimSm.CheckPlayability())
|
||||
{
|
||||
comboSm.NextCombo("L");
|
||||
comboSm.main.NextCombo("L");
|
||||
functionSm["LightAttack"].Execute();
|
||||
CharacterBase target = BattleManager.EnemySm.GetNearestEnemy(5);
|
||||
PlayTargetedAnimation("LightAttack" + comboSm.GetCurrentNodeName(), target, 0.8f);
|
||||
PlayTargetedAnimation("LightAttack" + comboSm.main.GetCurrentNodeName(), target, 0.8f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace Cielonos.MainGame.Inventory.Collections
|
||||
{
|
||||
if (player.landMovementSc.groundDetector.DetectGround(0.4f))
|
||||
{
|
||||
comboSm.ResetCombo();
|
||||
comboSm.main.Reset();
|
||||
PlayTargetedAnimation("AirAttackEnd");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Cielonos.MainGame.Characters;
|
||||
using UniRx;
|
||||
using UnityEngine;
|
||||
|
||||
@@ -8,130 +9,41 @@ namespace Cielonos.MainGame.Inventory
|
||||
{
|
||||
public partial class ComboSubmodule : SubmoduleBase<ItemBase>
|
||||
{
|
||||
private IDisposable comboTimer;
|
||||
private IDisposable suspender;
|
||||
private float defaultDisappearTime;
|
||||
|
||||
public ComboTree comboTree;
|
||||
public Dictionary<string, ComboTree> comboTrees;
|
||||
public ComboTree main => comboTrees["Main"];
|
||||
public ComboTree this[string name] => comboTrees[name];
|
||||
|
||||
public ComboSubmodule(ItemBase owner, ComboData asset) : base(owner)
|
||||
{
|
||||
this.defaultDisappearTime = asset.defaultDisappearTime;
|
||||
this.comboTree = asset.tree.ToRuntime();
|
||||
this.comboTree.Reset();
|
||||
}
|
||||
|
||||
public ComboSubmodule(ItemBase owner, float disappearTime) : base(owner)
|
||||
{
|
||||
this.defaultDisappearTime = disappearTime;
|
||||
this.comboTree = new ComboTree();
|
||||
this.comboTree.AddNode(0, "Root");
|
||||
this.comboTree.Reset();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置连击计时器,在指定时间后重置连击树
|
||||
/// </summary>
|
||||
/// <param name="disappearTime">连击计时器持续时间,若小于等于0则使用默认时间</param>
|
||||
public void Setup(float disappearTime = -1)
|
||||
{
|
||||
disappearTime = disappearTime <= 0 ? defaultDisappearTime : disappearTime;
|
||||
comboTimer = owner.player.selfTimeSm.AddLocalTimer(disappearTime, () => comboTree.Reset());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停连击计时器,在指定时间后继续重新设置连击计时器
|
||||
/// </summary>
|
||||
public void SuspendThenSetup(float duration)
|
||||
{
|
||||
comboTimer?.Dispose();
|
||||
suspender?.Dispose();
|
||||
suspender = owner.player.selfTimeSm.AddLocalTimer(duration, () => Setup());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前连招节点的引用名称
|
||||
/// </summary>
|
||||
public string GetCurrentNodeName()
|
||||
{
|
||||
return comboTree.currentNode.referenceName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据操作指令进入下一个连招节点
|
||||
/// </summary>
|
||||
/// <param name="operation">操作指令字符串,例如"L","R"</param>
|
||||
/// <param name="autoReset">若无法进入下一个节点,是否返回首个节点</param>
|
||||
/// <returns>是否成功进入下一个节点</returns>
|
||||
public bool NextCombo(string operation, bool autoReset = true)
|
||||
{
|
||||
if (comboTree.currentNode.branches.Count == 0)
|
||||
this.comboTrees = new Dictionary<string, ComboTree>();
|
||||
foreach (KeyValuePair<string, ComboTreeData> treeData in asset.comboTrees)
|
||||
{
|
||||
comboTree.Reset();
|
||||
comboTrees[treeData.Key] = treeData.Value.ToRuntime(this);
|
||||
comboTrees[treeData.Key].Reset();
|
||||
}
|
||||
|
||||
foreach (var branch in comboTree.currentNode.branches.Where(branch => branch.operation == operation))
|
||||
{
|
||||
comboTree.lastNode = comboTree.currentNode;
|
||||
comboTree.currentNode = comboTree.nodes[branch.nextNodeIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (autoReset)
|
||||
{
|
||||
comboTree.Reset();
|
||||
NextCombo(operation);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接设置连招节点
|
||||
/// </summary>
|
||||
/// <param name="index">节点索引</param>
|
||||
public void SetCombo(int index)
|
||||
{
|
||||
comboTree.lastNode = comboTree.currentNode;
|
||||
comboTree.currentNode = comboTree.nodes[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接设置连招节点
|
||||
/// </summary>
|
||||
/// <param name="refName">节点引用名称</param>
|
||||
public void SetCombo(string refName)
|
||||
{
|
||||
comboTree.lastNode = comboTree.currentNode;
|
||||
comboTree.currentNode = comboTree.nodes.Find(x => x.referenceName == refName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回退到上一个连招节点
|
||||
/// </summary>
|
||||
public void RevertCombo()
|
||||
{
|
||||
comboTree.currentNode = comboTree.lastNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置连招树到初始节点
|
||||
/// </summary>
|
||||
public void ResetCombo()
|
||||
{
|
||||
comboTree.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ComboSubmodule
|
||||
{
|
||||
[Serializable]
|
||||
public class ComboTree
|
||||
public partial class ComboTree : SubmoduleBase<ComboSubmodule>
|
||||
{
|
||||
public List<Node> nodes = new();
|
||||
private Player player => owner.owner.player;
|
||||
|
||||
private IDisposable comboTimer;
|
||||
private IDisposable suspender;
|
||||
private float resetTime;
|
||||
|
||||
public List<Node> nodes;
|
||||
public Node lastNode;
|
||||
public Node currentNode;
|
||||
|
||||
public ComboTree(ComboSubmodule owner, float resetTime) : base(owner)
|
||||
{
|
||||
this.resetTime = resetTime;
|
||||
this.nodes = new List<Node>();
|
||||
}
|
||||
|
||||
public void AddNode(int index, string referenceName)
|
||||
{
|
||||
nodes.Add(new Node(index, referenceName));
|
||||
@@ -152,7 +64,112 @@ namespace Cielonos.MainGame.Inventory
|
||||
{
|
||||
currentNode = nodes[0];
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ComboTree
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置连击计时器,在指定时间后重置连击树
|
||||
/// </summary>
|
||||
/// <param name="overrideResetTime">连击计时器持续时间,若小于等于0则使用默认时间</param>
|
||||
public void Setup(float overrideResetTime = -1)
|
||||
{
|
||||
overrideResetTime = overrideResetTime <= 0 ? resetTime : overrideResetTime;
|
||||
comboTimer = player.selfTimeSm.AddLocalTimer(overrideResetTime, Reset);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停连击计时器,在指定时间后继续重新设置连击计时器
|
||||
/// </summary>
|
||||
public void SuspendThenSetup(float duration)
|
||||
{
|
||||
comboTimer?.Dispose();
|
||||
suspender?.Dispose();
|
||||
suspender = player.selfTimeSm.AddLocalTimer(duration, () => Setup());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前连招节点的引用名称
|
||||
/// </summary>
|
||||
public string GetCurrentNodeName()
|
||||
{
|
||||
return currentNode.referenceName;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前连招节点的下一个节点的引用名称,如果没有符合条件的下一个节点,则返回首个节点的引用名称
|
||||
/// </summary>
|
||||
public string GetNextNodeName(string operation)
|
||||
{
|
||||
foreach (var branch in currentNode.branches.Where(branch => branch.operation == operation))
|
||||
{
|
||||
return nodes[branch.nextNodeIndex].referenceName;
|
||||
}
|
||||
|
||||
return nodes[0].branches.Where(branch => branch.operation == operation)
|
||||
.Select(branch => nodes[branch.nextNodeIndex].referenceName).FirstOrDefault();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据操作指令进入下一个连招节点
|
||||
/// </summary>
|
||||
/// <param name="operation">操作指令字符串,例如"L","R"</param>
|
||||
/// <param name="autoReset">若无法进入下一个节点,是否返回首个节点</param>
|
||||
/// <returns>是否成功进入下一个节点</returns>
|
||||
public bool NextCombo(string operation, bool autoReset = true)
|
||||
{
|
||||
if (currentNode.branches.Count == 0)
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
foreach (var branch in currentNode.branches.Where(branch => branch.operation == operation))
|
||||
{
|
||||
lastNode = currentNode;
|
||||
currentNode = nodes[branch.nextNodeIndex];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (autoReset)
|
||||
{
|
||||
Reset();
|
||||
NextCombo(operation);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接设置连招节点
|
||||
/// </summary>
|
||||
/// <param name="index">节点索引</param>
|
||||
public void SetCombo(int index)
|
||||
{
|
||||
lastNode = currentNode;
|
||||
currentNode = nodes[index];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 直接设置连招节点
|
||||
/// </summary>
|
||||
/// <param name="refName">节点引用名称</param>
|
||||
public void SetCombo(string refName)
|
||||
{
|
||||
lastNode = currentNode;
|
||||
currentNode = nodes.Find(x => x.referenceName == refName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 回退到上一个连招节点
|
||||
/// </summary>
|
||||
public void RevertCombo()
|
||||
{
|
||||
currentNode = lastNode;
|
||||
}
|
||||
}
|
||||
|
||||
public partial class ComboTree
|
||||
{
|
||||
[Serializable]
|
||||
public class Node
|
||||
{
|
||||
|
||||
@@ -78,7 +78,7 @@ namespace Cielonos.MainGame.Inventory
|
||||
private void ConsumeEnergy()
|
||||
{
|
||||
character.attributeSm["Energy"] -= data.energyCost;
|
||||
character.eventSm.onUseEnergy.Invoke(character, data.energyCost);
|
||||
character.eventSm.onEnergyChanged.Invoke(character, data.energyCost);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user