Files
Cielonos/Assets/Scripts/MainGame/Characters/Base/CharacterBase.cs
SoulliesOfficial 50ee502684 完善
2026-02-13 09:22:11 -05:00

248 lines
8.4 KiB
C#

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);
}
}
}