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.

595 lines
20 KiB
C#

2 years ago
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)
{
SettleDebugDraw(entity);
SettleSkill(entity);
2 years ago
}
}
private static void SettleDebugDraw(GameEntity entity)
{
if (!entity.hasHp)
return;
Util.DrawShape(entity.hp.HitBoxShape, entity.Pos(), Vector3.right, new Color(0, 1, 0, 0.4f));
}
private static void SettleSkill(GameEntity entity)
2 years ago
{
if (Util.IsPause(entity))
return;
while (entity.skill.HitInfo.Count > 0)
if (SettleHit(entity.skill.HitInfo.Dequeue()))
break;
2 years ago
}
private static bool SettleHit(SkillHitInfo hitInfo)
2 years ago
{
2 years ago
var entity = Util.GetEntity(hitInfo.OwnerEntity);
var target = Util.GetEntity(hitInfo.HitEntity);
if (entity == null || target == null)
2 years ago
{
return false;
2 years ago
}
2 years ago
var damageResult = SettleDamage(hitInfo, entity, target); //伤害结算
var hitLevel = SettleHitLevel(hitInfo, damageResult, entity, target); //命中效果等级
var flowSpeed = SettleFlowSpeed(hitInfo, hitLevel, entity, target); //强制位移(击退、击飞、击落等)
var pauseTime = SettlePause(hitInfo, hitLevel, entity, target); //卡帧
SettleSelfMoveForceScale(hitLevel, entity, target); //攻击者移动速度调整
SettleBreak(flowSpeed, hitLevel, damageResult, entity, target); //打断技能/动画
SettleBuff(hitLevel, entity, target); //添加硬直/击晕buff
SettleTrigger(hitInfo, entity, target); //命中触发器
//纯表现
SettleHitText(hitLevel, damageResult, entity, target); //跳字
SettleHitEffect(flowSpeed, hitLevel, hitInfo.SkillParam.hitType, entity, target); //命中特效
SettleFlash(hitLevel, entity, target); //闪白
SettleShake(hitLevel, entity, target); //抖动
SettleScale(flowSpeed, entity, target); //抖动
SettleGlobalEffect(target, pauseTime); //全局特效
//投技中断
if (!(hitInfo.SkillParam.throwTimeline is null))
{
entity.skill.ThrowTarget = target.ID();
TimelineManager.Instance.RunSkillTimeline(entity, hitInfo.SkillParam.throwTimeline.name);
return true;
}
return !hitInfo.Continue;
2 years ago
}
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);
}
2 years ago
var sub = hitInfo.SkillParam.staggerLevel - staggerDef;
2 years ago
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;
2 years ago
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;
}
Util.SetEntityPos(target, targetPos);
2 years ago
return flowSpeed;
}
private static float SettlePause(SkillHitInfo hitInfo, EHitLevel hitLevel, GameEntity entity, GameEntity target)
2 years ago
{
if (target.IsMaster())
{
//主角受击不卡帧
return 0f;
2 years ago
}
var param = hitInfo.SkillParam;
var pauseTime = 1f * param.pauseTime / GameConst.FPS;
2 years ago
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;
}
//单次攻击命中多目标:每2帧结算一次
var isLastHit = hitInfo.HitIndex == hitInfo.HitCount - 1;
if (isLastHit)
Util.EntityPause(entity, pauseTime, false);
else
Util.EntityPause(entity, 1f / GameConst.FPS, false);
2 years ago
Util.EntityPause(target, pauseTime);
return pauseTime;
2 years ago
}
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);
//触发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)
2 years ago
{
//受击特效
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;
}
2 years ago
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);
2 years ago
Util.EntityShake(entity, 1);
2 years ago
}
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(GameEntity target, float pauseTime)
2 years ago
{
if (target.IsMaster())
{
//主角受击
Util.SetTimeScaleEffect(0.2f, 1, 0.5f);
Util.SetAberrationEffect(1, 0, 0.5f);
Util.SetFovEffect(0.9f, 1, 0.5f);
return;
}
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);
}
}
}