248 lines
8.5 KiB
C#
248 lines
8.5 KiB
C#
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);
|
|
}
|
|
}
|
|
} |