using System; using System.Text; using Cielonos.MainGame.Buffs.Character; using Cielonos.MainGame.Characters.Inventory; 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")] public AttributeData attributeData; public VFXData vfxData; public BlockData blockData; public BaseAnimationGroup baseAnimationGroup; [TitleGroup("Submodules")] public SelfTimeSubmodule selfTimeSm; public AttributeSubmodule attributeSm; public EventSubmodule eventSm; public BuffSubmodule buffSm; public StatusSubmodule statusSm; [TitleGroup("Subcontrollers")] public CollisionSubcontrollerBase collisionSc; public MovementSubcontrollerBase movementSc; public AnimationSubcontrollerBase animationSc; 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() { selfTimeSm.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); } 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 float GetDamageValue(AttackValue attackValue) { string dealtMultiplier = attackValue.attackType.AttackTypeToString() + "DamageDealtMultiplier"; string gainMultiplier = attackValue.attackType.AttackTypeToString() + "DamageGainMultiplier"; float baseDamage = attackValue.damage; baseDamage *= attackValue.attacker is not null ? attackValue.attacker.attributeSm[dealtMultiplier] : 1; baseDamage *= attributeSm[gainMultiplier]; baseDamage *= attackValue.attacker is not null ? attackValue.attacker.attributeSm["FinalDamageDealtMultiplier"] : 1; baseDamage *= attributeSm["FinalDamageGainMultiplier"]; return (baseDamage + attackValue.additionalFlatDamage) * attackValue.damageMultiplier; } public void TakeDamage(ref AttackResult attackResult) { float damage = GetDamageValue(attackResult.attackValue); if (attributeSm.Has("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; if (attributeSm["Health"] <= 0) { attackResult.causedDeath = true; attributeSm["Health"] = 0; Die(); } else { attackResult.causedDeath = false; } } } public partial class CharacterBase { public virtual bool CheckBreakthrough(BreakthroughType breakthroughType) { return !reactionSc.breakthroughResistances[breakthroughType].Value; } public virtual bool CheckDisruption(DisruptionType disruptionType) { return animationSc.fullBodyFuncAnimSm.CheckDisruption(disruptionType); } public virtual bool GetHit(BreakthroughType breakthroughType, out float recoveryTime, DisruptionType disruptionType = DisruptionType.NormalExternal, Vector3 direction = default, string funcAnimName = "") { renderSc.GetHitBlink(); 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 }; if (string.IsNullOrEmpty(funcAnimName)) { funcAnimName = GetHitFuncAnimName(breakthroughType, direction); } if (CheckBreakthrough(breakthroughType)) { if (!animationSc.fullBodyFuncAnimSm.Stop(disruptionType)) { recoveryTime = 0f; return true; } if (breakthroughType >= BreakthroughType.Medium) { animationSc.PlayGetHitAnimation(funcAnimName, out recoveryTime); } else { recoveryTime = 0f; animationSc.PlayGetHitBoneShake(intensity, direction); } statusSm.AddStatus(StatusType.Stun, recoveryTime); return true; } recoveryTime = 0f; animationSc.PlayGetHitBoneShake(intensity, direction); return false; } protected virtual string GetHitFuncAnimName(BreakthroughType breakthroughType, Vector3 direction) { string prefix = "GetHitMedium"; if (breakthroughType >= BreakthroughType.Medium) { prefix = breakthroughType switch { BreakthroughType.Medium => "GetHitMedium", BreakthroughType.Heavy => "GetHitHeavy", BreakthroughType.Disruption => "GetHitHeavy", BreakthroughType.Forced => "GetHitForced", _ => "GetHit" }; } string directionStr = "Front"; if (direction != default) { float angle = Vector3.SignedAngle(transform.forward, direction, Vector3.up); directionStr = angle switch { > -45f and <= 45f => "Back", > 45f and <= 135f => "Left", > -135f and <= -45f => "Right", _ => "Front" }; if (directionStr != "Front" && !animationSc.fullBodyFuncAnimSm.collection.ContainsKey(prefix + directionStr)) { directionStr = "Front"; } } string fullName = prefix + directionStr; if (!animationSc.fullBodyFuncAnimSm.collection.ContainsKey(fullName)) { if (prefix == "GetHitForced") { prefix = "GetHitHeavy"; //如果没有专门的“强制等级”受击动画,就使用“重度等级”的动画 fullName = prefix + directionStr; } } return fullName; } } 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); } } }