using System; using SickscoreGames.HUDNavigationSystem; using Sirenix.OdinInspector; using SLSUtilities.General; using SLSUtilities.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, IFuncAnimExecutor { public Fraction fraction; public Transform centerPoint => bodyPartsSc.flexibleCenterPoint; public Vector3 centerPosition => centerPoint.position; [TitleGroup("Data & Presets")] public AttributeData attributeData; public VFXData vfxData; public BaseAnimationGroup baseAnimationGroup; [TitleGroup("Submodules")] public SelfTimeSubmodule selfTimeSm; public AttributeSubmodule attributeSm; 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 void Awake() { InitializeSubmodules(); InitializeSubcontrollers(); } protected virtual void Start() { vfxData?.Initialize(this); selfTimeSm?.SetUp(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); 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 void GetAttacked(AttackAreaBase attackArea, ref AttackResult attackResult) { eventSm.onBeforeGetAttacked.Invoke(attackArea); attackResult.attackValue = attackArea.attackSm.attackValue; /*if (statusModule.IsInvincible) { isDead = false; GeneralUtilities.InstantiateInfo(flexibleCenterPoint.position, "无敌"); return finalDamage; }*/ TakeDamage(ref attackResult); } public float GetDamageValue(AttackValue attackValue) { string dealtMultiplier = attackValue.attackType.ToString() + "DamageDealtMultiplier"; string gainMultiplier = attackValue.attackType.ToString() + "DamageGainMultiplier"; attackValue.damage *= attackValue.attacker.attributeSm[dealtMultiplier]; attackValue.damage *= attributeSm[gainMultiplier]; attackValue.damage *= attackValue.attacker.attributeSm["FinalDamageDealtMultiplier"]; attackValue.damage *= attributeSm["FinalDamageGainMultiplier"]; return attackValue.damage; } public void TakeDamage(ref AttackResult attackResult) { float damage = GetDamageValue(attackResult.attackValue); if (attributeSm.HasAttribute("Shield") && attributeSm["Shield"] > 0) { attackResult.shieldBlockedDamage = Mathf.Min(damage, attributeSm["Shield"]); //GeneralUtilities.InstantiateBlockedDamageNumber(flexibleCenterPoint.position, blockedDamage); if(damage > attributeSm["Shield"]) { damage -= attributeSm["Shield"]; attributeSm["Shield"] = 0; } else { attributeSm["Shield"] -= damage; damage = 0; } } attributeSm["Health"] -= damage; attackResult.finalDamage = damage; AttackType attackType = attackResult.attackValue.attackType; bool isCritical = attackResult.attackValue.isCritical; MainGameBaseCollection.Instance.DamageNumber(attackType, isCritical) .Spawn(attackResult.hitPosition, damage) .SetSpamGroup(attackResult.spamGroupID); if (attributeSm["Health"] <= 0) { attackResult.causedDeath = true; attributeSm["Health"] = 0; Die(); } else { attackResult.causedDeath = false; } } } 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; } /*Debug.Log($"GetHit Disruption Animation Played: {disruptionType} - {breakthroughType}, Recovery Time: {recoveryTime}");*/ 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(centerPoint.position, cam); } } }