using System; using SickscoreGames.HUDNavigationSystem; using Sirenix.OdinInspector; using SLSFramework.General; using SLSFramework.WwiseAssistance; using SLSUtilities.FunctionalAnimation; using UniRx; using UnityEngine; using UnityEngine.AI; using UnityEngine.Serialization; namespace Cielonos.MainGame.Characters { public enum Fraction { Player = 0, AlliedMinion = 1, Enemy = 10, Neutral = 20 } public partial class CharacterBase : SerializedMonoBehaviour { public Fraction fraction; public Transform flexibleCenterPoint; public Transform footPoint; [TitleGroup("Data & Presets")] public AttributeData attributeData; public VFXData vfxData; public BaseAnimationGroup baseAnimationGroup; [TitleGroup("Submodules")] public SelfTimeSubmodule selfTimeSm; public AttributeSubmodule attributeSm; public AdditionalForceSubmodule additionalForceSm; public EventSubmodule eventSm; public BuffSubmodule buffSm; public StatusSubmodule statusSm; public CompositeDisposable disposables; [TitleGroup("Subcontrollers")] public CollisionSubcontrollerBase collisionSc; public MovementSubcontrollerBase movementSc; [Required] public AnimationSubcontrollerBase animationSc; [Required] public RenderSubcontrollerBase renderSc; public BodyPartsSubcontroller bodyPartsSc; public AudioSubcontroller audioSc; public ReactionSubcontroller reactionSc; public FeedbackSubcontroller feedbackSc; [TitleGroup("Navigation")] public HUDNavigationElement navigationElement; protected virtual void Awake() { InitializeSubcontrollers(); InitializeSubmodules(); } #if UNITY_EDITOR private void Reset() { InitializeSubmodules(); } #endif protected virtual void Start() { vfxData.Initialize(this); if (fraction == Fraction.Enemy) { BattleManager.EnemySm.activeEnemiesList.Add(this); } } protected virtual void Update() { buffSm.Update(); } public virtual void Die() { Destroy(gameObject);//TODO: 后续改为死亡动画+回收 } } public partial class CharacterBase { protected virtual void InitializeSubmodules() { selfTimeSm ??= new SelfTimeSubmodule(this); attributeSm ??= new AttributeSubmodule(this); additionalForceSm ??= new AdditionalForceSubmodule(this); eventSm ??= new EventSubmodule(this); buffSm ??= new BuffSubmodule(this); statusSm ??= new StatusSubmodule(this); disposables ??= new CompositeDisposable(); } protected virtual void InitializeSubcontrollers() { renderSc?.Initialize(); movementSc?.Initialize(); animationSc?.Initialize(); collisionSc?.Initialize(); bodyPartsSc?.Initialize(); audioSc?.Initialize(); reactionSc?.Initialize(); feedbackSc?.Initialize(); } } public partial class CharacterBase { public AttackResult DealAttack(AttackValue attackValue, out bool isDead) { AttackResult result = new AttackResult(attackValue.damage); /*if (statusModule.IsInvincible) { isDead = false; GeneralUtilities.InstantiateInfo(flexibleCenterPoint.position, "无敌"); return finalDamage; }*/ float finalDamage = GetDamageValueAfterResistance(attackValue); if (attributeSm.HasAttribute("Shield") && attributeSm["Shield"] > 0) { result.blockedDamage = Mathf.Min(finalDamage, attributeSm["Shield"]); //GeneralUtilities.InstantiateBlockedDamageNumber(flexibleCenterPoint.position, blockedDamage); if(finalDamage > attributeSm["Shield"]) { finalDamage -= attributeSm["Shield"]; attributeSm["Shield"] = 0; } else { attributeSm["Shield"] -= finalDamage; finalDamage = 0; } } attributeSm["Health"] -= finalDamage; result.finalDamage = finalDamage; if (attributeSm["Health"] <= 0) { isDead = true; attributeSm["Health"] = 0; Die(); } else { isDead = false; } return result; } public float GetDamageValueAfterResistance(AttackValue attackValue) { /*float reductionRate = attributeModule.currentAttributes.TryGetValue("DamageReduction", out float rate) ? rate : 0; return attackValue.attackType switch { AttackType.Energy => attackValue.damage * GetDamageReduction(attributeModule.GetCurrentAttribute("EnergyAttackResistance"), reductionRate), AttackType.Kinetics => attackValue.damage * GetDamageReduction(attributeModule.GetCurrentAttribute("KineticAttackResistance"), reductionRate), AttackType.Explosion => attackValue.damage * GetDamageReduction(attributeModule.GetCurrentAttribute("ExplosionAttackResistance"), reductionRate), AttackType.Magic => attackValue.damage * GetDamageReduction(attributeModule.GetCurrentAttribute("MagicAttackResistance"), reductionRate), AttackType.Elemental => attackValue.damage * GetDamageReduction(attributeModule.GetCurrentAttribute("ElementalAttackResistance"), reductionRate), AttackType.Pure => attackValue.damage * GetDamageReduction(0, reductionRate), _ => throw new ArgumentOutOfRangeException() };*/ return attackValue.damage; } } public partial class CharacterBase { public virtual bool GetHit(BreakthroughType breakthroughType, out float recoveryTime, DisruptionType disruptionType = DisruptionType.NormalExternal, Vector3 direction = default) { renderSc.GetHitBlink(); if (animationSc.SetGetHitDisruption(disruptionType, breakthroughType)) { switch (breakthroughType) { case BreakthroughType.Medium: animationSc.PlayGetHitMediumAnimation(out recoveryTime, direction); break; case BreakthroughType.Heavy: animationSc.PlayGetHitHeavyAnimation(out recoveryTime,direction); break; case BreakthroughType.Disruption: case BreakthroughType.Forced: animationSc.PlayGetHitDisruptionAnimation(out recoveryTime,direction); break; default: animationSc.PlayGetHitBoneShake(0.4f, direction); recoveryTime = 0f; break; } return true; } float intensity = breakthroughType switch { BreakthroughType.None => 0, BreakthroughType.Weak => 0.2f, BreakthroughType.Medium => 0.4f, BreakthroughType.Heavy or BreakthroughType.Disruption or BreakthroughType.Forced => 0.8f, _ => 0 }; recoveryTime = 0f; animationSc.PlayGetHitBoneShake(intensity, direction); return false; } } public partial class CharacterBase { public Vector2 GetNormalizedScreenPosition(Camera cam = null) { if (this is Player player) { cam ??= player.viewSc.playerCamera; } else { if (cam == null) { throw new ArgumentNullException(nameof(cam), "Camera must be provided for non-player characters."); } } return SpaceConverter.WorldPointToNormalizedScreenPoint(flexibleCenterPoint.position, cam); } } }