using System; using System.Collections.Generic; using Cielonos.MainGame.Characters; using DamageNumbersPro; using Sirenix.OdinInspector; using SLSUtilities.FunctionalAnimation; using SLSUtilities.General; using UnityEngine; using Random = UnityEngine.Random; namespace Cielonos.MainGame { [Serializable] [HideReferenceObjectPicker] public partial class AttackUnit : ICloneable { [HideInInspector] public AttackData parentData; /// /// 和AttackData中的key对应,且用于运行时识别这个攻击单元 /// [HideInInspector] public string unitName; [Title("Attack Parameters")] [Tooltip("如果为true,则这个攻击单元不会造成伤害,也不会触发暴击等相关机制,通常用于仅触发特殊效果")] public bool isInvalidAttack; [HideIf("isInvalidAttack")] public Attack.Type type = Attack.Type.Kinetics; [Title("Attack Parameters Override")] [HideIf("isInvalidAttack")] [LabelText("覆盖默认参数")] public bool overrideParameters = false; [HideIf("@isInvalidAttack || overrideParameters")] [LabelText("伤害倍率")] public float damageCorrectionMultiplier = 1f; [HideIf("@isInvalidAttack || overrideParameters")] [LabelText("削韧倍率")] public float stanceDepletionCorrectionMultiplier = 1f; [Tooltip("是否在多段攻击时独立计算伤害和暴击")] [HideIf("isInvalidAttack")] public bool isIndependentPerHit = true; [HideIf("@isInvalidAttack || !overrideParameters")] [LabelText("攻击力")] public float startDamage; [HideIf("@isInvalidAttack || !overrideParameters")] [LabelText("攻击力标准差")] public float damageDeviation; [HideIf("@isInvalidAttack || !overrideParameters")] [LabelText("暴击率")] public float criticalChance; [HideIf("@isInvalidAttack || !overrideParameters")] [LabelText("暴击倍率")] public float criticalMultiplier; [HideIf("@isInvalidAttack || !overrideParameters")] [LabelText("削韧值")] public float stanceDepletion; [Title("Hit Effects")] public Breakthrough.Type breakthroughType = Breakthrough.Type.Weak; public DisruptionType disruptionType = DisruptionType.NormalExternal; [Title("Tags")] public List tags = new List(); [Title("Hit VFX")] public bool useVFXDataHit; [HideIf("useVFXDataHit")] public GameObject hitVFX; [ShowIf("useVFXDataHit")] public string vfxUnitName; public bool randomizeVfxRotation; [Title("Extra Modules")] [ListDrawerSettings(ShowFoldout = true)] [SerializeReference] public List submodules = new List(); public bool TryGetSubModule(out T submodule) where T : Submodule { submodule = submodules.Find(m => m is T) as T; return submodule != null; } public T GetSubmodule() where T : Submodule { return submodules.Find(m => m is T) as T; } public Attack.Value GetAttackValue(CharacterBase attacker) { bool isCritical = Random.value < GetFinalCriticalChance(attacker); float finalDamage = isCritical ? GetFinalCriticalDamage(attacker) : GetFinalRegularDamage(); float finalStanceDepletion = isCritical ? stanceDepletion * GetFinalCriticalMultiplier(attacker) : stanceDepletion; return new Attack.Value(isCritical, finalDamage, finalStanceDepletion, type, disruptionType, breakthroughType, tags); } public GameObject GetHitVFX() { return hitVFX != null ? hitVFX : parentData.defaultHitVFX; } public float GetFinalRegularDamage() { return startDamage + Random.Range(-damageDeviation, damageDeviation); } public float GetFinalCriticalDamage(CharacterBase attacker) { return (startDamage + Random.Range(-damageDeviation, damageDeviation)) * GetFinalCriticalMultiplier(attacker); } public float GetFinalCriticalChance(CharacterBase attacker) { float chance = criticalChance; if (attacker != null) { chance += attacker.attributeSm[CharacterAttribute.CriticalAttackChance]; } return chance; } public float GetFinalCriticalMultiplier(CharacterBase attacker) { float multiplier = 1 + criticalMultiplier; if (attacker != null) { multiplier += attacker.attributeSm[CharacterAttribute.CriticalAttackDamageAmplifier]; } return multiplier; } public AttackUnit Clone() { return (AttackUnit) this.MemberwiseClone(); } } public partial class AttackUnit { [Serializable] public abstract class Submodule : SubmoduleBase { public Submodule(AttackUnit owner) : base(owner) { } } [Serializable] public class ParameterSubmodule : Submodule { public Dictionary floatParameters = new Dictionary(); public Dictionary intParameters = new Dictionary(); public Dictionary stringParameters = new Dictionary(); public ParameterSubmodule() : base(null) { } public ParameterSubmodule(AttackUnit owner) : base(owner) { } public T GetParameter(string key) { if (typeof(T) == typeof(float)) { return floatParameters.TryGetValue(key, out float floatParameter) ? (T)(object)floatParameter : (T)(object)0f; } if (typeof(T) == typeof(int)) { return intParameters.TryGetValue(key, out int intParameter) ? (T)(object)intParameter : (T)(object)0; } if (typeof(T) == typeof(string)) { return stringParameters.TryGetValue(key, out string stringParameter) ? (T)(object)stringParameter : (T)(object)string.Empty; } throw new InvalidOperationException($"Unsupported parameter type: {typeof(T)}"); } } [Serializable] public class DamageNumberSubmodule : Submodule { public DamageNumber regularPrefab; public DamageNumber criticalPrefab; public Dictionary customPrefabs = new Dictionary(); public DamageNumberSubmodule() : base(null) { } public DamageNumberSubmodule(AttackUnit owner) : base(owner) { } } } }