2
0
Fork 0
You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

611 lines
20 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Entitas;
using System.Collections.Generic;
using UnityEngine;
using Game;
public class SettleSystem : IExecuteSystem, IInitializeSystem
{
private IGroup<GameEntity> _entities;
private readonly List<SkillHitInfo> _tempHitInfo = new List<SkillHitInfo>();
public void Initialize()
{
_entities = Util.GetGroup(GameMatcher.Skill);
}
public void Execute()
{
foreach (var entity in _entities)
{
SettleSkill(entity.skill);
SettleHit(entity.skill);
}
}
private static void SettleSkill(SkillComponent skill)
{
foreach (var hitInfo in skill.SkillHitInfo.Values)
{
if (hitInfo.IsDealed)
{
continue;
}
hitInfo.IsDealed = true;
var isEntity = hitInfo.HitEntity != 0;
if (isEntity)
{
var target = Util.GetEntity(hitInfo.HitEntity);
target.skill.HitInfo.Add(hitInfo);
}
}
}
private void SettleHit(SkillComponent skill)
{
while (true)
{
_tempHitInfo.Clear();
_tempHitInfo.AddRange(skill.HitInfo);
skill.HitInfo.Clear();
foreach (var hitInfo in _tempHitInfo)
{
var isEntity = hitInfo.HitEntity != 0;
if (!isEntity)
{
continue;
}
var entity = Util.GetEntity(hitInfo.OwnerEntity);
var target = Util.GetEntity(hitInfo.HitEntity);
if (entity == null || target == null)
{
continue;
}
var damageResult = SettleDamage(hitInfo, entity, target); //伤害结算
var hitLevel = SettleHitLevel(hitInfo, damageResult, entity, target); //命中效果等级
var flowSpeed = SettleFlowSpeed(hitInfo, hitLevel, entity, target); //强制位移(击退、击飞、击落等)
SettleSelfMoveForceScale(hitLevel, entity, target); //攻击者移动速度调整
SettleBreak(flowSpeed, hitLevel, damageResult, entity, target); //打断技能/动画
SettleBuff(hitLevel, entity, target); //添加硬直/击晕buff
SettlePause(hitInfo, hitLevel, entity, target); //卡帧
SettleTrigger(hitInfo, entity, target); //命中触发器
//纯表现
SettleHitText(hitLevel, damageResult, entity, target); //跳字
SettleHitEffect(flowSpeed, hitLevel, hitInfo.HitType, entity, target); //命中特效
SettleFlash(hitLevel, entity, target); //闪白
SettleShake(hitLevel, entity, target); //抖动
SettleScale(flowSpeed, entity, target); //抖动
SettleGlobalEffect(hitInfo, hitLevel, damageResult, entity, target); //全局特效
}
if (skill.HitInfo.Count > 0)
{
//结算过程中可能引入新的受击 直接继续结算
continue;
}
break;
}
}
public struct SettleDamageResult
{
public float Damage; //伤害
public bool HasShield; //是否结算前有护盾
public bool IsSkill; //是否技能造成伤害
public bool IsDead; //当次伤害造成死亡
public bool IsShieldBreak; //当次伤害破盾
public bool IsStun; //当次伤害眩晕
public bool IsFlowing; //是否浮空
}
private static SettleDamageResult SettleDamage(SkillHitInfo hitInfo, GameEntity entity, GameEntity target)
{
var param = hitInfo.SkillParam;
var targetHp = target.hp;
var ret = new SettleDamageResult();
ret.IsSkill = !string.IsNullOrEmpty(hitInfo.SkillId);
//伤害计算
{
var basicAttack = Util.GetProperty(entity, EProperty.BasicAttack);
ret.Damage = basicAttack * param.damageRate * hitInfo.AttackRate;
ret.HasShield = targetHp.Shield.Value > 0;
if (ret.HasShield)
{
ret.Damage = Mathf.Min(ret.Damage, targetHp.Shield.Value);
targetHp.Shield.Value -= ret.Damage;
ret.IsShieldBreak = targetHp.Shield.Value == 0;
}
else
{
ret.Damage = Mathf.Min(ret.Damage, targetHp.Hp.Value);
targetHp.Hp.Value -= ret.Damage;
}
targetHp.IsDamaged.Value = true;
//刷新护盾恢复cd
target.hp.ShieldRecoverTime = target.hp.ShieldRecoverTimeMax;
//刷新眩晕恢复cd
target.hp.StunRecoverTime = target.hp.StunRecoverTimeMax;
}
//异常值积累
{
if (targetHp.StunMax.Value > targetHp.Stun.Value)
{
//眩晕
var basicStun = Util.GetProperty(entity, EProperty.BasicStun);
var damageStun = basicStun * param.stunRate * hitInfo.StunkRate;
damageStun = Mathf.Min(damageStun, targetHp.StunMax.Value - targetHp.Stun.Value);
targetHp.Stun.Value += damageStun;
if (targetHp.Stun.Value >= targetHp.StunMax.Value)
{
ret.IsStun = true;
targetHp.Stun.Value = 0;
Util.AddStunBuff(target.ID());
}
}
}
//浮空判定 目标不在地面 或者可以从地面浮空
if (!target.move.IsGround || param.isFlow)
{
ret.IsFlowing = true;
}
//实体死亡
if (targetHp.IsAlive && targetHp.Hp.Value == 0)
{
ret.IsDead = true;
Util.EntityDie(target);
}
return ret;
}
private static EHitLevel SettleHitLevel(SkillHitInfo hitInfo, SettleDamageResult dmgResult, GameEntity entity,
GameEntity target)
{
if (!dmgResult.IsSkill)
{
return EHitLevel.None;
}
if (dmgResult.IsDead)
{
return EHitLevel.Kill;
}
if (dmgResult.IsShieldBreak)
{
return EHitLevel.ShieldBreak;
}
if (dmgResult.IsStun)
{
return EHitLevel.Stun;
}
var staggerDef = Util.GetProperty(target, EProperty.StaggerDefLevel);
if (dmgResult.HasShield)
{
staggerDef += Util.GetProperty(target, EProperty.ShieldStaggerDefLevel);
}
var sub = hitInfo.StaggerLevel - staggerDef;
sub = Mathf.Clamp(sub, 0, 3);
var ret = EHitLevel.None;
switch (sub)
{
case 0:
ret = EHitLevel.Block;
break;
case 1:
ret = EHitLevel.Stagger1;
break;
case 2:
ret = EHitLevel.Stagger2;
break;
case 3:
ret = EHitLevel.Stagger3;
break;
}
if (ret > EHitLevel.Block)
{
if (dmgResult.IsFlowing)
{
return EHitLevel.Flowing;
}
if (target.hp.LastDamageLevel == EHitLevel.Stagger3)
{
//后仰升级为浮空
return EHitLevel.Flowing;
}
}
return ret;
}
private static Vector3 SettleFlowSpeed(SkillHitInfo hitInfo, EHitLevel hitLevel, GameEntity entity,
GameEntity target)
{
var param = hitInfo.SkillParam;
//浮空数值
var flowSpeed = new Vector3(param.flowSpeed.x, param.flowSpeed.y,
param.flowSpeed.z * GameRandom.RollSymbol(0.5f));
var rot = Quaternion.FromToRotation(Vector3.right, hitInfo.HitDir);
flowSpeed = rot * flowSpeed;
if (hitInfo.IsBreak)
{
//近距离推开
//根据实体xz距离调整xz浮空速度距离为0时系数为1.5距离大于0.5时系数为1
var flowSpeedXZ = new Vector3(flowSpeed.x, 0, flowSpeed.z);
var dist = Util.EntityDistanceXZ(entity, target);
flowSpeedXZ *= Mathf.Clamp(1.5f - dist, 1, 2);
flowSpeed = new Vector3(flowSpeedXZ.x, flowSpeed.y, flowSpeedXZ.z);
}
if (hitLevel >= EHitLevel.Flowing)
{
target.move.IsFlowing = true;
}
else
{
flowSpeed = new Vector3(flowSpeed.x, 0, flowSpeed.z);
}
//根据命中等级调整系数
switch (hitLevel)
{
case EHitLevel.Block:
flowSpeed *= 0.2f;
break;
case EHitLevel.Stagger1:
flowSpeed *= 0.7f;
break;
case EHitLevel.Stagger2:
flowSpeed *= 1f;
break;
case EHitLevel.Stagger3:
flowSpeed *= 1.2f;
break;
}
//记录受击方向 添加强制移动buff
if (flowSpeed != Vector3.zero)
{
target.hp.LastDamageDir = flowSpeed;
if (flowSpeed.y < 0)
{
//击落
Util.AddHitDownBuff(target.ID());
}
//击退
Util.AddSetSpeedBuff(target.ID(), param.flowTime);
}
target.hp.LastDamageLevel = hitLevel;
//命中瞬间、在pause之前生效部分位移
var move = target.move;
var posAdd = flowSpeed * Time.deltaTime * 2;
var targetPos = move.Position + posAdd;
var startPos = move.Position - posAdd.normalized * 0.001f;
RaycastHit raycastHit;
if (Physics.Raycast(startPos, posAdd, out raycastHit, posAdd.magnitude, UtilPhysics.LayerMaskStatic))
{
targetPos = raycastHit.point;
}
move.Position = targetPos;
return flowSpeed;
}
private static void SettlePause(SkillHitInfo hitInfo, EHitLevel hitLevel, GameEntity entity, GameEntity target)
{
if (target.IsMaster())
{
//主角受击不卡帧
return;
}
var param = hitInfo.SkillParam;
var pauseTime = param.pauseTime;
if (hitLevel >= EHitLevel.ShieldBreak)
{
pauseTime = Mathf.Max(pauseTime, 0.05f);
//受击方向
var dir = target.hp.LastDamageDir;
//受击方向与实体方向夹角
var angle = Vector3.Angle(dir, target.move.Transform.forward);
//夹角越大,卡帧越长
pauseTime *= Mathf.Clamp(1.5f - angle / 180, 0.5f, 1);
}
//根据命中等级调整系数
switch (hitLevel)
{
case EHitLevel.ShieldBreak:
pauseTime *= 1.5f;
break;
case EHitLevel.Block:
pauseTime *= 1.2f;
break;
case EHitLevel.Kill:
pauseTime *= 1.5f;
break;
}
Util.EntityPause(entity, pauseTime);
Util.EntityPause(target, pauseTime);
}
private static void SettleSelfMoveForceScale(EHitLevel hitLevel, GameEntity entity, GameEntity target)
{
switch (hitLevel)
{
case EHitLevel.Stagger1:
case EHitLevel.Stagger2:
case EHitLevel.Stagger3:
Util.EntitySetTempMoveForceScale(entity.ID(), -0.8f);
break;
case EHitLevel.Block:
Util.EntitySetTempMoveForceScale(entity.ID(), -1.2f);
break;
}
}
private static void SettleBreak(Vector3 flowSpeed, EHitLevel hitLevel, SettleDamageResult dmgResult,
GameEntity entity, GameEntity target)
{
//打断技能
if (hitLevel > EHitLevel.Block)
{
Util.EndSkillTimeline(target);
}
//强制转向
if (hitLevel > EHitLevel.Stagger2)
{
Util.EntityTurn(target, flowSpeed.x < 0);
}
//受击动画
if (dmgResult.IsFlowing)
{
if (hitLevel > EHitLevel.Block)
{
target.animation.AirHitMsg = true;
}
}
else
{
if (Util.HasStunBuff(target.ID()))
{
if (hitLevel > EHitLevel.Stun)
target.animation.AirHitMsg = true;
else if (hitLevel > EHitLevel.Block)
target.animation.StunHitMsg = true;
}
else
{
switch (hitLevel)
{
case EHitLevel.Stagger1:
target.animation.HitMsg = true;
break;
case EHitLevel.Stagger2:
target.animation.MHitMsg = true;
break;
case EHitLevel.Stagger3:
target.animation.LHitMsg = true;
break;
case EHitLevel.ShieldBreak:
case EHitLevel.Stun:
target.animation.StunHitMsg = true;
break;
case EHitLevel.Flowing:
case EHitLevel.Kill:
target.animation.AirHitMsg = true;
break;
}
}
}
}
private static void SettleBuff(EHitLevel hitLevel, GameEntity entity, GameEntity target)
{
switch (hitLevel)
{
case EHitLevel.Stagger1:
Util.AddStaggerBuff(target.ID(), 0.2f);
break;
case EHitLevel.Stagger2:
Util.AddStaggerBuff(target.ID(), 0.5f);
break;
case EHitLevel.Stagger3:
Util.AddStaggerBuff(target.ID(), 1f);
break;
case EHitLevel.Flowing:
Util.AddStaggerBuff(target.ID(), -1f);
break;
case EHitLevel.ShieldBreak:
case EHitLevel.Stun:
Util.AddStunBuff(target.ID());
break;
}
}
private static void SettleTrigger(SkillHitInfo hitInfo, GameEntity entity, GameEntity target)
{
//触发buffTrigger
UtilBuff.BuffEffectAll(target.ID(), EBuffEffectTimingType.BeAttack);
if (((hitInfo.Performance & (1 << ((int)ESKillPerformance.Heavy - 1))) != 0) ||
(hitInfo.Performance & (1 << ((int)ESKillPerformance.Floating - 1))) != 0)
{
UtilBuff.BuffEffectAll(target.ID(), EBuffEffectTimingType.BeAttackHeavy);
}
//触发SkillTrigger
UtilSkillTrigger.Trigger(entity.ID(), target.ID(), hitInfo.SkillId, ESkillTriggerTimingType.Hit);
}
private static void SettleHitText(EHitLevel hitLevel, SettleDamageResult dmgResult, GameEntity entity,
GameEntity target)
{
//受击事件广播
var hitMsg = new PEntityHit()
{
Entity = entity.ID(),
Target = target.ID(),
Dmg = (int)dmgResult.Damage,
IsSkill = dmgResult.IsSkill,
};
EventManager.Instance.SendEvent<PEntityHit>(EEvent.EntityHit, hitMsg);
switch (hitLevel)
{
case EHitLevel.ShieldBreak:
//破盾事件广播
var breakmsg = new PEntityHitText()
{
Target = target.ID(),
Text = "Break",
};
EventManager.Instance.SendEvent<PEntityHitText>(EEvent.EntityHitText, breakmsg);
break;
// case EHitLevel.Block:
// //格挡事件广播
// var blockmsg = new PEntityHitText()
// {
// target = target.ID(),
// text = "Block",
// };
// EventManager.instance.SendEvent<PEntityHitText>(EEvent.EntityHitText, blockmsg);
// break;
// default:
// //测试
// var msg = new PEntityHitText()
// {
// target = target.ID(),
// text = hitLevel.ToString(),
// };
// EventManager.instance.SendEvent<PEntityHitText>(EEvent.EntityHitText, msg);
// break;
}
}
private static void SettleHitEffect(Vector3 flowSpeed, EHitLevel hitLevel, EHitType hitType, GameEntity entity,
GameEntity target)
{
//受击特效
var effectRot = GameRandom.RandomRot(Util.Vec3ToRot(new Vector3(flowSpeed.x, 0, flowSpeed.y)), 45);
switch (hitLevel)
{
case EHitLevel.Kill:
Util.CastHitEffect(GameConst.EffectHitKill, target.ID(), effectRot);
break;
case EHitLevel.ShieldBreak:
Util.CastHitEffect(GameConst.EffectHitShieldBreak, target.ID(), effectRot);
break;
case EHitLevel.Block:
Util.CastHitEffect(GameConst.EffectHitShield, target.ID(), effectRot);
break;
default:
switch (hitType)
{
case EHitType.Slash:
Util.CastHitEffect(GameConst.EffectHitSlash, target.ID(), effectRot);
break;
case EHitType.Blunt:
Util.CastHitEffect(GameConst.EffectHitBlunt, target.ID(), effectRot);
break;
default:
Util.CastHitEffect(GameConst.EffectHitSlash, target.ID(), effectRot);
break;
}
break;
}
}
private static void SettleFlash(EHitLevel hitLevel, GameEntity entity, GameEntity target)
{
switch (hitLevel)
{
case EHitLevel.ShieldBreak:
Util.EntityFlash(target, Color.red);
break;
case EHitLevel.Block:
Util.EntityFlash(target, Color.yellow);
break;
default:
Util.EntityFlash(target);
break;
}
}
private static void SettleShake(EHitLevel hitLevel, GameEntity entity, GameEntity target)
{
Util.EntityShake(target, 3);
Util.EntityShake(entity, 2);
}
private static void SettleScale(Vector3 flowSpeed, GameEntity entity, GameEntity target)
{
var dir = new Vector3(flowSpeed.x * (target.move.IsRight ? 1 : -1), flowSpeed.y, 0);
Util.EntityScale(target, dir);
}
private static void SettleGlobalEffect(SkillHitInfo hitInfo, EHitLevel hitLevel, SettleDamageResult dmgResult,
GameEntity entity, GameEntity target)
{
if (target.IsMaster())
{
//主角受击
Util.SetTimeScaleEffect(0.2f, 1, 0.5f);
Util.SetAberrationEffect(1, 0, 0.5f);
Util.SetFovEffect(0.9f, 1, 0.5f);
return;
}
var pauseTime = hitInfo.SkillParam.pauseTime;
if (pauseTime >= 0.1f)
{
Util.Shake();
var (aberrationFrom, aberrationTo) = (0.5f, 0);
var (timeScaleFrom, timeScaleTo) = (0.2f, 1);
var (fovFrom, fovTo) = (0.85f, 1);
var effectTime = pauseTime + 0.2f;
Util.SetAberrationEffect(aberrationFrom, aberrationFrom, pauseTime);
Util.AppendAberrationEffect(aberrationFrom, aberrationTo, 0.3f);
// Util.SetTimeScaleEffect(timeScaleFrom, timeScaleFrom, pauseTime);
// Util.AppendTimeScaleEffect(timeScaleFrom, timeScaleTo, 0.5f);
Util.SetFovEffect(fovFrom, fovFrom, pauseTime);
Util.AppendFovEffect(fovFrom, fovTo, 0.5f);
}
else if (pauseTime > 0)
{
Util.SetAberrationEffect(0.2f, 0, 0.2f);
Util.SetFovEffect(0.99f, 1, 0.2f);
}
else
{
Util.SetAberrationEffect(0.2f, 0, 0.2f);
Util.SetFovEffect(0.99f, 1, 0.2f);
}
}
}