Files
Cielonos/Assets/Scripts/MainGame/AttackArea/AttackAreaBase.cs
SoulliesOfficial ef7b479712 initial
2025-11-25 08:19:33 -05:00

347 lines
12 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.Linq;
using Cielonos.MainGame.Characters;
using Cielonos.MainGame.Inventory;
using Sirenix.OdinInspector;
using SLSFramework.LeanPoolAssistance;
using SLSFramework.WwiseAssistance;
using SLSUtilities.FunctionalAnimation;
using UnityEngine;
namespace Cielonos.MainGame
{
public abstract partial class AttackAreaBase : PooledObject
{
[Title("References")]
public CharacterBase creator;
public bool isGeneratedByPlayer => creator == MainGameManager.Instance.player;
public ItemBase itemSource;
public List<Fraction> targetFractions;
public Transform topParent;
public AudioContainer audioContainer;
public Collider areaCollider;
public Dictionary<string, GameObject> functionalParts;
[Title("Status")]
public bool isEnabling;
public bool canTriggerHitEvent = true;
public Action updateAction;
[Title("Submodules")]
[HideInEditorMode] public AttackSubmodule attackSm;
[HideInEditorMode] public TimeSubmodule timeSm;
[HideInEditorMode] public HitSubmodule hitSm;
[HideInEditorMode] public MoveSubmoduleBase moveSm;
[HideInEditorMode] public RaycastSubmodule raycastSm;
[HideInEditorMode] public ForceSubmodule forceSm;
[HideInEditorMode] public ReactionSubmodule reactionSm;
#if UNITY_EDITOR
private void Reset()
{
isAutoDespawn = false;
}
#endif
public T Initialize<T>(CharacterBase creator, params Fraction[] targetFractions) where T : AttackAreaBase
{
return Initialize<T>(creator, null, targetFractions);
}
public T Initialize<T>(CharacterBase creator, ItemBase itemSource, params Fraction[] targetFractions) where T : AttackAreaBase
{
this.isEnabling = true;
this.creator = creator;
this.itemSource = itemSource;
this.targetFractions = targetFractions.ToList();
this.topParent = transform;
this.canTriggerHitEvent = true;
areaCollider = GetComponent<Collider>();
if (areaCollider != null)
{
//areaCollider.excludeLayers = LayerMask.GetMask("AttackAreaVFX", "DecoVFX", "Ignore Raycast");
}
audioContainer = GetComponent<AudioContainer>();
if (audioContainer != null)
{
audioContainer.soundEventDictionary = new Dictionary<string, AK.Wwise.Event>();
}
while (topParent.parent != null &&
//topParent.parent != creator.flexibleCenterPoint &&
//topParent.parent != creator.staticCenterPoint &&
topParent.parent != creator.transform)
{
topParent = topParent.parent;
}
foreach (TrailRenderer trail in GetComponentsInChildren<TrailRenderer>())
{
trail.Clear();
}
this.SetReactionSubmodule<T>();
return this as T;
}
protected virtual void Update()
{
raycastSm?.Update();
updateAction?.Invoke();
hitSm?.Update();
timeSm?.Update();
moveSm?.Update();
}
}
public partial class AttackAreaBase
{
#region AttackSubmodule
public T SetAttackSubmodule<T>(AttackUnit attackUnit, GameObject hitVFX = null) where T : AttackAreaBase
{
attackSm = new AttackSubmodule(this, attackUnit.GetAttackValue(), hitVFX == null ? attackUnit.GetHitVFX() : hitVFX);
return this as T;
}
#endregion
#region TimeSubmodule
public T SetTimeSubmodule<T>(float lifeTime) where T : AttackAreaBase
{
timeSm = new TimeSubmodule(this, lifeTime);
return this as T;
}
public T SetTimeSubmodule<T>(float lifeTime, float enableTime, float enableDelay,
Action enableAction = null, Action timeOutAction = null) where T : AttackAreaBase
{
timeSm = new TimeSubmodule(this, lifeTime, enableTime, enableDelay, enableAction, timeOutAction);
return this as T;
}
#endregion
#region HitSubmodule
public T SetHitSubmodule<T>() where T : AttackAreaBase
{
hitSm = new HitSubmodule(this);
return this as T;
}
public T SetHitSubmodule<T>(float hitInterval, int hitCount) where T : AttackAreaBase
{
hitSm = new HitSubmodule(this, hitInterval, hitCount);
return this as T;
}
#endregion
#region LinearDirectionMoveModule
public T SetLinearDirectionMoveModule<T>(Vector3 direction, float speed,
float acceleration = 0, bool overrideRotation = true, float timeScaleCoefficient = 1) where T : AttackAreaBase
{
moveSm = new LinearDirectionMoveSubmodule(this, direction, speed, acceleration, overrideRotation, timeScaleCoefficient);
return this as T;
}
#endregion
#region TraceMoveModule
public T SetTraceMoveModule<T>(CharacterBase target, float moveSpeed, float moveAcceleration,
float angularSpeed, float angularAcceleration, Vector3 initialDirection) where T : AttackAreaBase
{
moveSm = new TraceMoveSubmodule(this, target, moveSpeed, moveAcceleration, angularSpeed, angularAcceleration, initialDirection);
return this as T;
}
#endregion
#region RaycastSubmodule
/// <summary>
/// 设置射线检测子模块
/// </summary>
/// <param name="direction">射线方向</param>
/// <param name="rayRadius">球形射线半径若小于等于0则为直线射线</param>
/// <param name="rayLength">射线长度若小于0则为动态长度与移动速度相等</param>
public T SetRaycastSubmodule<T>(Vector3 direction = default, float rayRadius = -1f, float rayLength = -1f) where T : AttackAreaBase
{
raycastSm = new RaycastSubmodule(this, direction, rayLength, rayRadius);
raycastSm.Update();
return this as T;
}
#endregion
#region ForceSubmodule
public T SetForceSubmodule<T>(float dynamicForce) where T : AttackAreaBase
{
forceSm = new ForceSubmodule(this, dynamicForce);
return this as T;
}
public T SetForceSubmodule<T>(Vector3 customForce) where T : AttackAreaBase
{
forceSm = new ForceSubmodule(this, customForce);
return this as T;
}
public T SetForceSubmodule<T>(float strengthXZ, bool isRepulsion, float strengthY = 0) where T : AttackAreaBase
{
forceSm = new ForceSubmodule(this, strengthXZ, isRepulsion, strengthY);
return this as T;
}
#endregion
#region ReactionSubmodule
public T SetReactionSubmodule<T>() where T : AttackAreaBase
{
reactionSm = new ReactionSubmodule(this);
return this as T;
}
public T SetReactionSubmodule<T>(bool canBeBlocked, bool hasPerfectBlock, bool canBreakBlock,
bool canBeDodged, bool hasPerfectDodge, bool canBreakDodge) where T : AttackAreaBase
{
reactionSm = new ReactionSubmodule(this);
reactionSm.SetBlock(canBeBlocked, hasPerfectBlock, canBreakBlock);
reactionSm.SetDodge(canBeDodged, hasPerfectDodge, canBreakDodge);
return this as T;
}
#endregion
}
public partial class AttackAreaBase
{
public virtual void HitCharacter(Collider characterCollider, Vector3 hitPosition)
{
}
public virtual void HitEnvironment(Collider other, Vector3 hitPosition)
{
}
protected virtual void HitOnTarget(Collider hitCollider, Vector3 hitPosition)
{
CharacterBase target = hitCollider.GetComponentInParent<CharacterBase>();
if (target == null) return;
bool isBlocked = reactionSm?.CheckBlock(target, hitPosition) ?? false;
bool isDodged = reactionSm?.CheckDodge(target) ?? false;
bool isMissed = false; // reactionModule?.SetMiss(creator.attributeModule.currentAttributes.GetValue("AttackMissProbability", 0)) ?? false;
bool isEvaded = false; // reactionModule?.SetEvasion(player.attributeModule.currentAttributes.GetValue("EvasionProbability", 0)) ?? false;
bool isDead = false;
if (!isBlocked && !isDodged && !isMissed && !isEvaded)
{
//受击事件
InvokeHitEvents(target, hitPosition);
//最终伤害结算
float finalDamage = CalculateFinalDamage(target, out isDead);
if (finalDamage > 0)
{
//player.eventController.GetHurtEvents.InvokeAllEvents(this, finalDamage);
GenerateDamageNumber(finalDamage, hitPosition);
}
//特效
GenerateHitEffect(target, hitCollider, hitPosition);
//音效
PlaySoundFX(hitPosition);
//计算力的方向
//CalculateAdditionalForceDirection(player);
//打断,击退,受击动画
//float disruptionBreakLevel = attackModule.modifiedAttackValue.baseDisruptionBreakLevel;
//SetReaction(player, disruptionBreakLevel);
target.GetHit(attackSm.modifiedAttackValue.breakthroughType);
}
//应用额外力
if (!isDead && !isDodged && !isMissed && !isEvaded)
{
forceSm?.ApplyForce(target);
}
}
}
public partial class AttackAreaBase
{
protected virtual void InvokeHitEvents(CharacterBase target, Vector3 hitPosition)
{
hitSm.InvokeAllHitEvents(target, hitPosition);
if (target is Player player)
{
//player.eventController.GetHitEvents.InvokeAllEvents(this);
}
/*else if (target is EnemyController enemy)
{
//enemy.enemyEventController.GetHitEvents.InvokeAllEvents(this);
}*/
}
protected virtual float CalculateFinalDamage(CharacterBase target, out bool isDead)
{
if (attackSm == null)
{
isDead = false;
return 0;
}
float finalDamage = target.DealAttack(attackSm.modifiedAttackValue, out isDead);
return finalDamage;
}
protected virtual void GenerateDamageNumber(float finalDamage, Vector3 hitPosition)
{
bool isCritical = attackSm.modifiedAttackValue.isCritical;
attackSm.SpawnDamageNumber(hitPosition, isCritical, finalDamage);
}
protected virtual GameObject GenerateHitEffect(CharacterBase target, Collider hitCollider, Vector3 hitPosition)
{
GameObject hitEffect = attackSm.SpawnHitVFX(hitPosition);
attackSm.modifyHitEffectAction?.Invoke(hitEffect, target);
return hitEffect;
}
protected virtual GameObject GenerateHitEffect(Vector3 hitPosition)
{
GameObject hitEffect = attackSm.SpawnHitVFX(hitPosition);
attackSm.modifyHitEffectAction?.Invoke(hitEffect, null);
return hitEffect;
}
protected virtual void PlaySoundFX(Vector3 hitPosition, string soundKey = "")
{
if (string.IsNullOrEmpty(soundKey))
{
if (hitSm.isAutoPlayHitSound)
{
hitSm.hitSoundList.ForEach(hitSound => audioContainer.PlaySoundFX(hitSound, hitPosition));
}
}
else
{
audioContainer.PlaySoundFX(soundKey, hitPosition);
}
}
}
public class AttackAreaSubmoduleBase : SubmoduleBase<AttackAreaBase>
{
protected AttackAreaBase attackArea => owner;
public bool isEnabling;
public AttackAreaSubmoduleBase(AttackAreaBase owner) : base(owner)
{
isEnabling = true;
}
}
}