using FL.Battle.Actions;
using FL.Battle.Skills;
using System;
using System.Collections.Generic;
using UnityEngine;
using XGame;
using XGame.Database;
using XGame.Framework;
using XGame.Framework.Components;
namespace FL.Battle.Components.AI
{
public interface IBulletAIContext
{
long UID { get; }
long MasterId { get; }
int TableId { get; }
ITargetSelector Selector { get; }
Vector3 Position { get; }
VfxComponent Vfx { get; }
MoveComponent Move { get; }
}
///
/// AI组件和具体的EntityView绑定,其他Component最好不要尝试获取AI组件
///
public class BulletAI : Component
{
///
/// 已攻击过的目标id
///
private HashSet _lastTargetIds = new HashSet();
private SkillTable _skill;
private SkillVfxsTable _skillVfxs;
protected override void OnDispose()
{
_skill = null;
_skillVfxs = null;
_lastTargetIds.Clear();
}
public void Start(long targetId, Vector3 targetPosition, EEntityType targetType, int delay)
{
_skill = SkillTableRepo.Get(Context.TableId);
_skillVfxs = SkillVfxsTableRepo.Get(Context.TableId);
var bulletType = (ESkillVfxType)_skill.SkillVfxType;
_lastTargetIds.Clear();
// 子弹的移动方向
var direction = Vector3.Normalize(targetPosition - Context.Position);
var moveArgs = new MoveArgs()
{
target = targetPosition,
speed = _skillVfxs.SkillVfxSpeed,
timeScale = 1,
delay = delay,
moveType = (EMoveType)(_skillVfxs.SkillVfxMoveType - 1),
onComplete = () =>
{
if (bulletType == ESkillVfxType.BulletMortar)
{
MortarExplode(targetPosition, targetType);
End();
return;
}
var target = Context.Selector.GetTarget(targetId);
if (target == null || target.IsDead)
{
//Log.Debug($"子弹移动到目标位置, 目标已死亡. Target:{targetId} Bullet:{Context.TableId}");
End();
return;
}
PlayHitVfx(_skillVfxs.HitVfx, targetPosition);
target.Calculation.Damage(-_skill.Damage, Context.MasterId, _skill);
switch (bulletType)
{
case ESkillVfxType.BulletSplit:
// 分裂
_lastTargetIds.Add(targetId);
Split(direction, target.Entity.EntityType);
End();
break;
case ESkillVfxType.BulletJump:
// 弹跳
_lastTargetIds.Add(targetId);
Jump(target.Entity.EntityType, 1);
break;
default:
End();
break;
}
}
};
if (Enum.TryParse(_skillVfxs.SkillVfxMoveEase, true, out var ease))
{
moveArgs.ease = ease;
}
Context.Move.MoveTo(moveArgs);
}
///
/// 受击特效
///
///
///
private void PlayHitVfx(string hitVfx, Vector3 targetPosition)
{
var actPosition = ObjectPool.Acquire();
actPosition.position = targetPosition;
var hitArgs = ObjectPool.Acquire();
hitArgs.vfxName = hitVfx;
hitArgs.duration = _skillVfxs.HitVfxTime;
hitArgs.followType = EVfxFollowType.World;
hitArgs.action = actPosition;
Context.Vfx.Play(hitArgs);
}
///
/// 子弹分裂
/// DamageArgs[数量,夹角(为零则就近原则找目标),缩放万分比]
/// DamageArgs[1]大于零时,以子弹的初始移动方向为中心,按顺序(先左后右)累加角度获取分裂后子弹的方向
/// 怪物在该方向上则命中,否则子弹按直线飞出屏幕
///
/// 子弹的移动方向
/// 目标类型
private void Split(Vector3 direction, EEntityType targetType)
{
var childCount = _skill.DamageArgs[0];
var childAngle = _skill.DamageArgs[1];
var childScale = _skill.DamageArgs[2];
var from = Context.Position;
var finderInfo = new FinderInfo()
{
uid = Context.UID,
position = from,
radius = 0,
};
for (var i = 0; i < childCount; i++)
{
var target = Context.Selector.FindSync(finderInfo, targetType, ETargetFindType.Nearest, (a) =>
{
if (_lastTargetIds.Contains(a.Entity.EntityId))
{
return true;
}
if (childAngle == 0)
return false;
var tarAngle = ((i + 1) / 2) * childAngle;
var tempDir = Vector3.Normalize(a.Position - from);
// angle:[0, 180]
var angle = Vector3.Angle(direction, tempDir);
//Log.Debug($"Bullet Split index:{i} direction:{direction} temp:{tempDir} angle:{angle} tar:{tarAngle}");
if (Mathf.Abs(angle - tarAngle) < 20)
{ //角度范围-5到5
if (i == 0)
return false;
//cross.z > 0 目标向量在方向向量的左侧
var cross = Vector3.Cross(direction, tempDir);
if (i % 2 == 0)
{ // i为偶数在右边
return cross.z > 0;
}
return cross.z < 0;
}
return true;
});
if (target == null && childAngle == 0)
{
continue;
}
Vector3 to;
if (target != null)
{
_lastTargetIds.Add(target.Entity.EntityId);
to = target.Position;
}
else
{
if (i == 0)
{
to = from + direction * 12;
}
else
{
// i为偶数在右边 负数
var sign = i % 2 == 0 ? -1 : 1;
var tarAngle = ((i + 1) / 2) * childAngle;
//var tempDir = direction.RotateLeft(sign * tarAngle);
//var angle = Vector3.Angle(direction, tempDir);
//Log.Debug($"Bullet Split index:{i} direction:{direction} temp:{tempDir} angle:{angle} tar:{tarAngle} {Vector3.Angle(direction, Vector3.up)} {Vector3.Angle(tempDir, Vector3.up)}");
to = from + direction.RotateLeft(sign * tarAngle) * 12;
}
}
//子弹移动
var duration = Mathf.CeilToInt(Vector3.Distance(from, to) * 1000 / _skillVfxs.SkillVfxSpeed);
if (duration == 0)
{ // 位置重合,直接结算伤害
Log.Debug($"子弹分裂目标重合: from:{from} to:{to} speed:{_skillVfxs.SkillVfxSpeed}");
target?.Calculation.Damage(-_skill.Damage, Context.MasterId, _skill);
continue;
}
var actMove = ObjectPool.Acquire();
actMove.args = new MoveActionArgs()
{
from = from,
to = to,
duration = duration
};
//子弹特效
var bulletArgs = ObjectPool.Acquire();
bulletArgs.vfxName = _skillVfxs.SkillVfx;
bulletArgs.duration = duration;
bulletArgs.followType = EVfxFollowType.World;
bulletArgs.action = actMove;
if (target != null)
{
var skill = _skill;
var masterId = Context.MasterId;
bulletArgs.onFinish = () =>
{
if (target == null || target.IsDead)
{
return;
}
target.Calculation.Damage(-skill.Damage, masterId, skill);
};
//受击特效位置
var actPosition = ObjectPool.Acquire();
actPosition.position = to;
//受击特效
var hitArgs = ObjectPool.Acquire();
hitArgs.vfxName = _skillVfxs.HitVfx;
hitArgs.duration = _skillVfxs.HitVfxTime;
hitArgs.followType = EVfxFollowType.World;
hitArgs.action = actPosition;
bulletArgs.next = hitArgs;
}
Context.Vfx.Play(bulletArgs);
}
}
///
/// 弹跳
/// DamageArgs[弹跳次数,伤害递减万分比]
///
/// 目标类型
/// 弹跳次数
private void Jump(EEntityType targetType, int jumpTimes)
{
var jumpLimit = _skill.DamageArgs[0];
var hurtReduce = _skill.DamageArgs[1];
var finderInfo = new FinderInfo()
{
uid = Context.UID,
position = Context.Position,
radius = 0,
};
var target = Context.Selector.FindSync(finderInfo, targetType, ETargetFindType.Nearest, (a) =>
{
if (_lastTargetIds.Contains(a.Entity.EntityId))
{
return true;
}
return false;
});
if (target == null)
{
End();
return;
}
var targetId = target.Entity.EntityId;
_lastTargetIds.Add(targetId);
var tarPosition = target.Position;
var moveArgs = new MoveArgs()
{
target = tarPosition,
speed = _skillVfxs.SkillVfxSpeed * 0.8f,
timeScale = 1,
onComplete = () =>
{
target = Context.Selector.GetTarget(targetId);
if (target == null || target.IsDead)
{
Log.Debug($"子弹移动到目标位置, 目标已死亡. Target:{targetId} Bullet:{Context.TableId}");
End();
return;
}
// 受击特效
PlayHitVfx(_skillVfxs.HitVfx, tarPosition);
// 计算受击伤害
target.Calculation.Damage(-JumpHurt(_skill.Damage, jumpTimes, hurtReduce), Context.MasterId, _skill);
jumpTimes++;
if (jumpTimes > jumpLimit)
{
End();
return;
}
Jump(targetType, jumpTimes);
}
};
Context.Move.MoveTo(moveArgs);
}
private int JumpHurt(int baseVal, int jumpTimes, int hurtReduce)
{
float hurtVal = baseVal;
for (var i = 0; i < jumpTimes; i++)
{
hurtVal *= (1 - hurtReduce * 0.0001f);
}
return Mathf.RoundToInt(hurtVal);
}
///
/// 迫击炮爆炸
///
///
///
private void MortarExplode(Vector3 targetPosition, EEntityType targetType)
{
var hitRadius = _skill.Ranges[0];
var explodeRadius = _skill.Ranges[1];
var explodeHurt = _skill.DamageArgs[0];
PlayHitVfx(_skillVfxs.HitVfx, targetPosition);
// 查找受击目标
var finderInfo = new FinderInfo()
{
uid = Context.UID,
position = Context.Position,
radius = hitRadius,
};
var hitTarget = Context.Selector.FindSync(finderInfo, targetType, ETargetFindType.NearestAlive);
if (hitTarget != null)
{
hitTarget.Calculation.Damage(-_skill.Damage, Context.MasterId, _skill);
}
// 查找爆炸受击目标
finderInfo.radius = explodeRadius;
var targets = new List();
if (Context.Selector.FindTargets(finderInfo, targetType, ETargetFindType.InRangeAlive, null, ref targets))
{
foreach (var explodeTar in targets)
{
explodeTar.Calculation.Damage(-explodeHurt, Context.MasterId, _skill);
}
}
}
///
/// 结束
///
private void End()
{
_skill = null;
_skillVfxs = null;
_lastTargetIds.Clear();
EventSingle.Instance.Notify(EventDefine.GameMainMapRemoveSimpleEntity, Context.UID);
}
}
}