using System;
using System.Collections.Generic;
using UnityEngine;
using XGame;
using XGame.Framework;
namespace FL.Battle
{
///
/// 目标选择组件
///
public class TargetSelector : ITargetSelector
{
struct FindTargetArgs
{
public EEntityType targetType;
public ETargetFindType findType;
///
/// 目标选择回调函数,同步查找目标时该方法无效
/// <选中的目标>
///
public Action onSelected;
}
private Dictionary> _typeToTargetsMap;
private Dictionary _idToTargetMap;
private Dictionary _waitingMap;
private List _tempLst = new List();
private long _playerEntityId;
public void Register(ITarget target)
{
if (_typeToTargetsMap == null)
{
_typeToTargetsMap = new Dictionary>();
_idToTargetMap = new Dictionary();
}
var type = target.Entity.EntityType;
if (!_typeToTargetsMap.TryGetValue(type, out var targets))
{
targets = new HashSet();
_typeToTargetsMap.Add(type, targets);
}
targets.Add(target);
_idToTargetMap.Add(target.Entity.EntityId, target);
if (target.Entity.EntityType == EEntityType.Player)
{
_playerEntityId = target.Entity.EntityId;
}
}
public void Unregister(ITarget target)
{
if (_typeToTargetsMap == null) return;
foreach (var targets in _typeToTargetsMap.Values)
{
if (targets.Contains(target))
{
targets.Remove(target);
break;
}
}
_idToTargetMap.Remove(target.Entity.EntityId);
_waitingMap?.Remove(target);
if (target.Entity.EntityId == _playerEntityId)
{
_playerEntityId = 0;
}
}
public ITarget GetTarget(long entityId)
{
ITarget target = null;
_idToTargetMap?.TryGetValue(entityId, out target);
return target;
}
public ITarget GetPlayer()
{
return GetTarget(_playerEntityId);
}
ITarget ITargetSelector.FindSync(ITarget finder, EEntityType targetType, ETargetFindType findType, Func customFilter)
{
var info = new FinderInfo()
{
uid = finder.Entity.EntityId,
position = finder.Position,
radius = finder.Entity.Attr.Radius
};
return FindTarget(info, targetType, findType, customFilter);
}
ITarget ITargetSelector.FindSync(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func customFilter)
{
return FindTarget(finder, targetType, findType, customFilter);
}
bool ITargetSelector.FindTargets(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func customFilter, ref List targets)
{
if (!TryGetTargets(targetType, out var preTargets))
{
Log.Debug($"没有可选目标 Finder:{finder.uid} TargetType:{targetType}");
return false;
}
if (targets == null)
targets = new List();
foreach (var target in preTargets)
{
if (!IsCanSelect(target, findType is ETargetFindType.NearestAlive or ETargetFindType.InRangeAlive))
continue;
if (customFilter.SafeInvoke(target))
{
continue;
}
if (IsInRange(finder, target, out var distance))
{
targets.Add(target);
}
}
return targets.Count > 0;
}
bool ITargetSelector.FindTargets(Rect rect, EEntityType targetType, ETargetFindType findType, Func customFilter, ref List targets)
{
if (!TryGetTargets(targetType, out var preTargets))
{
Log.Debug($"没有可选目标 Rect:{rect} TargetType:{targetType}");
return false;
}
if (targets == null)
targets = new List();
foreach (var target in preTargets)
{
if (!IsCanSelect(target, findType is ETargetFindType.NearestAlive or ETargetFindType.InRangeAlive))
continue;
if (customFilter.SafeInvoke(target))
{
continue;
}
if (rect.Contains(target.Position))
{
targets.Add(target);
}
}
return targets.Count > 0;
}
void ITargetSelector.FindAsync(ITarget finder, EEntityType targetType, ETargetFindType findType, Action onSelected)
{
if (_waitingMap == null)
{
_waitingMap = new Dictionary();
}
_waitingMap[finder] = new FindTargetArgs()
{
targetType = targetType,
findType = findType,
onSelected = onSelected
};
}
public Vector3 RandomPosition(EEntityType targetType, bool isCloseToTarget, Func customFilter = null)
{
if (isCloseToTarget)
{
var tmpTarget = RandomTarget(targetType, customFilter);
if (tmpTarget != null)
{ // 目标坐标(-1, 1)内随机
var result = tmpTarget.Position;
var radius = tmpTarget.Entity.Attr.Radius;
result.x += UnityEngine.Random.Range(-radius, radius);
result.y += UnityEngine.Random.Range(-radius, radius);
//Log.Debug($"RandomPosition Target:{tmpTarget.Position} Result:{result}");
return result;
}
}
var tarPosition = GetPlayer().Position; //玩家的坐标和本节战斗的位置重叠
if (targetType == EEntityType.Monster)
{// 目标是怪物,怪物的战斗位置在玩家坐标上移8
// TODO 转成配置
tarPosition.y += 8;
}
tarPosition.x += UnityEngine.Random.Range(-4, 5);
tarPosition.y += UnityEngine.Random.Range(-2, 2);
return tarPosition;
}
public void OnUpdate(int times)
{
var count = _waitingMap?.Count ?? 0;
if (count == 0)
return;
_tempLst.AddRange(_waitingMap.Keys);
for (var i = 0; i < count; i++)
{
var finder = _tempLst[i];
if (!IsCanSelect(finder, false))
continue;
if (!_waitingMap.TryGetValue(finder, out var args))
{
continue;
}
var info = new FinderInfo()
{
uid = finder.Entity.EntityId,
position = finder.Position,
radius = finder.Entity.Attr.Radius
};
var target = FindTarget(info, args.targetType, args.findType, null);
if (target != null)
{ // 找到目标了才回调,并删除数据
args.onSelected.SafeInvoke(target);
_waitingMap.Remove(finder);
}
}
_tempLst.Clear();
}
///
/// 查找目标
///
///
///
///
/// 过滤器,不能选择时返回true
///
private ITarget FindTarget(FinderInfo finder, EEntityType targetType, ETargetFindType findType, Func customFilter)
{
if (!TryGetTargets(targetType, out var targets))
{
Log.Debug($"没有可选目标 Finder:{finder.uid} TargetType:{targetType}");
return null;
}
ITarget result = null;
var distanceMin = float.MaxValue;
foreach (var target in targets)
{
if (!IsCanSelect(target, findType is ETargetFindType.NearestAlive or ETargetFindType.InRangeAlive))
continue;
if (customFilter.SafeInvoke(target))
{
continue;
}
var isInRange = IsInRange(finder, target, out var distance);
if (findType is ETargetFindType.Nearest or ETargetFindType.NearestAlive)
{
if (result == null || distanceMin > distance)
{
result = target;
distanceMin = distance;
}
}
else if (isInRange)
{
result = target;
break;
}
}
return result;
}
private ITarget RandomTarget(EEntityType targetType, Func customFilter)
{
if (!TryGetTargets(targetType, out var targets))
{
Log.Debug($"没有可选目标 TargetType: {targetType}");
return null;
}
var list = ListPool.Acquire();
foreach (var target in targets)
{
var random = UnityEngine.Random.Range(0, 1000) % 2;
if (random == 0)
list.Insert(0, target);
else
list.Add(target);
}
ITarget result = null;
foreach (var target in list)
{
if (!IsCanSelect(target, true))
continue;
if (customFilter.SafeInvoke(target))
{
continue;
}
result = target;
break;
}
ListPool.Recycle(list);
return result;
}
///
/// 目标是否可选择
///
///
/// True: 除了死亡状态,其他状态都可选
///
private bool IsCanSelect(ITarget target, bool isAlive)
{
if (target.IsDead) return false;
if (target.Entity.IsState(EEntityState.Moving))
return isAlive;
return isAlive || target.IsMoving == false;
//return target.Entity.State switch
//{
// EEntityState.Idle or EEntityState.Battle => isAlive || target.IsMoving == false,
// EEntityState.Dead => false,
// _ => isAlive,
//};
}
private bool TryGetTargets(EEntityType entityType, out HashSet targets)
{
if (_typeToTargetsMap != null && _typeToTargetsMap.TryGetValue(entityType, out targets))
{
return true;
}
targets = null;
return false;
}
private bool IsInRange(FinderInfo finder, ITarget target, out float distance)
{
distance = Vector3.Distance(target.Position, finder.position);
distance -= (finder.radius + target.Entity.Attr.Radius); // 去掉双方的半径
return distance <= 0;
}
public void Clear()
{
_typeToTargetsMap?.Clear();
_idToTargetMap?.Clear();
_waitingMap?.Clear();
_tempLst?.Clear();
_playerEntityId = 0;
}
}
}