Files
Cielonos/Assets/Scripts/MainGame/Characters/Base/CharacterBase.cs

282 lines
9.4 KiB
C#
Raw Normal View History

2025-11-25 08:19:33 -05:00
using System;
2026-03-20 12:07:44 -04:00
using System.Text;
using Cielonos.MainGame.Buffs.Character;
using Cielonos.MainGame.Characters.Inventory;
2025-12-23 19:47:06 -05:00
using SickscoreGames.HUDNavigationSystem;
2025-11-25 08:19:33 -05:00
using Sirenix.OdinInspector;
2026-02-13 09:22:11 -05:00
using SLSUtilities.General;
using SLSUtilities.WwiseAssistance;
2025-11-25 08:19:33 -05:00
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
}
2026-01-17 11:35:49 -05:00
public partial class CharacterBase : SerializedMonoBehaviour, IFuncAnimExecutor
2025-11-25 08:19:33 -05:00
{
public Fraction fraction;
2026-02-13 09:22:11 -05:00
public Transform centerPoint => bodyPartsSc.flexibleCenterPoint;
public Vector3 centerPosition => centerPoint.position;
2025-11-25 08:19:33 -05:00
2026-03-20 12:07:44 -04:00
[TitleGroup("Data")]
2025-11-25 08:19:33 -05:00
public AttributeData attributeData;
2025-12-17 04:19:38 -05:00
public VFXData vfxData;
2026-03-20 12:07:44 -04:00
public BlockData blockData;
2025-11-25 08:19:33 -05:00
public BaseAnimationGroup baseAnimationGroup;
[TitleGroup("Submodules")]
public SelfTimeSubmodule selfTimeSm;
public AttributeSubmodule attributeSm;
2026-02-13 09:22:11 -05:00
2025-12-08 05:27:53 -05:00
public EventSubmodule eventSm;
public BuffSubmodule buffSm;
public StatusSubmodule statusSm;
2025-11-25 08:19:33 -05:00
[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;
2025-12-23 19:47:06 -05:00
[TitleGroup("Navigation")]
public HUDNavigationElement navigationElement;
2025-11-25 08:19:33 -05:00
2026-01-03 18:19:39 -05:00
protected void Awake()
2025-11-25 08:19:33 -05:00
{
InitializeSubmodules();
2026-01-03 18:19:39 -05:00
InitializeSubcontrollers();
2025-11-25 08:19:33 -05:00
}
protected virtual void Start()
{
2026-01-03 18:19:39 -05:00
vfxData?.Initialize(this);
selfTimeSm?.SetUp(this);
2025-12-17 04:19:38 -05:00
2025-11-25 08:19:33 -05:00
if (fraction == Fraction.Enemy)
{
BattleManager.EnemySm.activeEnemiesList.Add(this);
}
}
2025-12-08 05:27:53 -05:00
protected virtual void Update()
{
2026-03-20 12:07:44 -04:00
selfTimeSm.Update();
2025-12-08 05:27:53 -05:00
buffSm.Update();
}
2025-11-25 08:19:33 -05:00
public virtual void Die()
{
Destroy(gameObject);//TODO: 后续改为死亡动画+回收
}
}
public partial class CharacterBase
{
protected virtual void InitializeSubmodules()
{
selfTimeSm ??= new SelfTimeSubmodule(this);
attributeSm ??= new AttributeSubmodule(this);
2025-12-08 05:27:53 -05:00
eventSm ??= new EventSubmodule(this);
buffSm ??= new BuffSubmodule(this);
statusSm ??= new StatusSubmodule(this);
2025-11-25 08:19:33 -05:00
}
protected virtual void InitializeSubcontrollers()
{
renderSc?.Initialize();
movementSc?.Initialize();
animationSc?.Initialize();
collisionSc?.Initialize();
bodyPartsSc?.Initialize();
audioSc?.Initialize();
reactionSc?.Initialize();
feedbackSc?.Initialize();
}
}
public partial class CharacterBase
{
2026-02-13 09:22:11 -05:00
public float GetDamageValue(AttackValue attackValue)
{
2026-03-20 12:07:44 -04:00
string dealtMultiplier = attackValue.attackType.AttackTypeToString() + "DamageDealtMultiplier";
string gainMultiplier = attackValue.attackType.AttackTypeToString() + "DamageGainMultiplier";
float baseDamage = attackValue.damage;
2026-02-13 09:22:11 -05:00
2026-03-20 12:07:44 -04:00
baseDamage *= attackValue.attacker.attributeSm[dealtMultiplier];
baseDamage *= attributeSm[gainMultiplier];
2026-02-13 09:22:11 -05:00
2026-03-20 12:07:44 -04:00
baseDamage *= attackValue.attacker.attributeSm["FinalDamageDealtMultiplier"];
baseDamage *= attributeSm["FinalDamageGainMultiplier"];
2026-02-13 09:22:11 -05:00
2026-03-20 12:07:44 -04:00
return (baseDamage + attackValue.additionalFlatDamage) * attackValue.damageMultiplier;
2026-02-13 09:22:11 -05:00
}
2025-11-25 08:19:33 -05:00
2026-02-13 09:22:11 -05:00
public void TakeDamage(ref AttackResult attackResult)
{
float damage = GetDamageValue(attackResult.attackValue);
2026-03-20 12:07:44 -04:00
if (attributeSm.Has("Shield") && attributeSm["Shield"] > 0)
2025-11-25 08:19:33 -05:00
{
2026-02-13 09:22:11 -05:00
attackResult.shieldBlockedDamage = Mathf.Min(damage, attributeSm["Shield"]);
2025-11-25 08:19:33 -05:00
//GeneralUtilities.InstantiateBlockedDamageNumber(flexibleCenterPoint.position, blockedDamage);
2026-02-13 09:22:11 -05:00
if(damage > attributeSm["Shield"])
2025-11-25 08:19:33 -05:00
{
2026-02-13 09:22:11 -05:00
damage -= attributeSm["Shield"];
2025-11-25 08:19:33 -05:00
attributeSm["Shield"] = 0;
}
else
{
2026-02-13 09:22:11 -05:00
attributeSm["Shield"] -= damage;
damage = 0;
2025-11-25 08:19:33 -05:00
}
}
2026-02-13 09:22:11 -05:00
attributeSm["Health"] -= damage;
attackResult.finalDamage = damage;
2026-03-20 12:07:44 -04:00
Attack.AttackType attackType = attackResult.attackValue.attackType;
2026-02-13 09:22:11 -05:00
bool isCritical = attackResult.attackValue.isCritical;
MainGameBaseCollection.Instance.DamageNumber(attackType, isCritical)
2026-03-20 12:07:44 -04:00
.Spawn(attackResult.hitPosition, damage, transform)
2026-02-13 09:22:11 -05:00
.SetSpamGroup(attackResult.spamGroupID);
2025-12-08 05:27:53 -05:00
2025-11-25 08:19:33 -05:00
if (attributeSm["Health"] <= 0)
{
2026-02-13 09:22:11 -05:00
attackResult.causedDeath = true;
2025-11-25 08:19:33 -05:00
attributeSm["Health"] = 0;
Die();
}
else
{
2026-02-13 09:22:11 -05:00
attackResult.causedDeath = false;
2025-11-25 08:19:33 -05:00
}
}
}
public partial class CharacterBase
{
2026-03-20 12:07:44 -04:00
public virtual bool CheckBreakthrough(BreakthroughType breakthroughType)
{
return !reactionSc.breakthroughResistances[breakthroughType].Value;
}
public virtual bool CheckDisruption(DisruptionType disruptionType)
{
return animationSc.fullBodyFuncAnimSm.CheckDisruption(disruptionType);
}
2025-12-17 04:19:38 -05:00
public virtual bool GetHit(BreakthroughType breakthroughType, out float recoveryTime,
2026-03-20 12:07:44 -04:00
DisruptionType disruptionType = DisruptionType.NormalExternal,
Vector3 direction = default, string funcAnimName = "")
2025-11-25 08:19:33 -05:00
{
renderSc.GetHitBlink();
2025-12-17 04:19:38 -05:00
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
};
2026-03-20 12:07:44 -04:00
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);
}
2025-12-17 04:19:38 -05:00
2026-03-20 12:07:44 -04:00
statusSm.AddStatus(StatusType.Stun, recoveryTime);
return true;
}
2025-12-17 04:19:38 -05:00
recoveryTime = 0f;
animationSc.PlayGetHitBoneShake(intensity, direction);
2025-11-25 08:19:33 -05:00
return false;
}
2026-03-20 12:07:44 -04:00
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;
return fullName;
}
2025-11-25 08:19:33 -05:00
}
2025-12-22 18:36:29 -05:00
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.");
}
}
2026-02-13 09:22:11 -05:00
return SpaceConverter.WorldPointToNormalizedScreenPoint(centerPoint.position, cam);
2025-12-22 18:36:29 -05:00
}
}
2025-11-25 08:19:33 -05:00
}