using System.Collections.Generic;
using UnityEngine;
namespace NecromancerTome
{
///
/// Одержимый лютоволк - охотник (решено 09.10, BACKLOG «Медведь и лютоволк в одержимости»):
/// - бросок на ПКМ: враг под прицелом не дальше LeapRange - волк разворачивается и прыгает прямо на
/// него (прыжок самой игры: SetJumpDistance на точную дальность и перепад высоты); приземлился рядом -
/// цель падает на LeapStun секунд;
/// - след крови: раненые враги (здоровье ниже BloodHealth) отмечены на компасе и карте до BloodRange,
/// пока не умрут или не залечатся, - отличие от нюха собаки: та чует всех рядом, волк - подранков издалека.
///
public static class WolfControl
{
public const string WolfClass = "necroZombieWolf";
public const float LeapRange = 10f;
public const float LeapMin = 2f;
public const float LeapConeDeg = 10f;
public const float LeapCooldown = 3f;
public const float LeapHit = 2.5f;
public const float LeapStun = 2.5f;
public const float BloodHealth = 0.5f;
public const float BloodRange = 150f;
public const float BloodDropRange = 200f;
static EntityAlive _leapTarget;
static float _leapUntil, _leapStart, _nextLeap;
static readonly HashSet _blood = new HashSet();
static float _bloodCheck;
public static bool Is(EntityAlive _e)
{
return _e != null && EntityClass.list.TryGetValue(_e.entityClass, out EntityClass cls) && cls != null &&
cls.entityClassName == WolfClass;
}
/// Каждый кадр одержимости волком.
public static void Tick(World _world, EntityPlayerLocal _player, EntityAlive _wolf, bool _leap, ref float _bodyYaw)
{
if (_leap && _leapTarget == null && Time.time >= _nextLeap && _wolf.onGround && !_wolf.Jumping)
{
EntityAlive t = Target(_world, _player, _wolf);
if (t != null)
{
Vector3 to = t.position - _wolf.position;
Vector3 flat = new Vector3(to.x, 0f, to.z);
_bodyYaw = Mathf.Atan2(flat.x, flat.z) * Mathf.Rad2Deg;
_wolf.SetRotation(new Vector3(0f, _bodyYaw, 0f));
_wolf.Jumping = true;
_wolf.SetJumpDistance(flat.magnitude, Mathf.Max(0.5f, to.y + 0.5f));
_leapTarget = t;
_leapStart = Time.time;
_leapUntil = Time.time + 2f;
_nextLeap = Time.time + LeapCooldown;
Debug.Log("[NecromancerTome] WolfControl: бросок на " + t.EntityName + " (" + t.entityId + "), " + flat.magnitude.ToString("F1") + " м");
}
}
if (_leapTarget != null)
{
bool landed = Time.time - _leapStart > 0.3f && _wolf.onGround && !_wolf.Jumping;
if (_leapTarget.IsDead() || Time.time > _leapUntil)
{
_leapTarget = null;
}
else if (landed || (_leapTarget.getChestPosition() - _wolf.getHeadPosition()).sqrMagnitude < LeapHit * LeapHit * 0.5f)
{
if ((_leapTarget.position - _wolf.position).sqrMagnitude < LeapHit * LeapHit)
{
KnockDown(_leapTarget, _wolf);
}
_leapTarget = null;
}
}
BloodTick(_world, _wolf);
}
static void KnockDown(EntityAlive _t, EntityAlive _wolf)
{
_wolf.emodel?.avatarController?.StartAnimationAttack();
if (_t.bodyDamage.CurrentStun == EnumEntityStunType.None && _t.emodel?.avatarController != null)
{
_t.emodel.avatarController.BeginStun(EnumEntityStunType.Prone, EnumBodyPartHit.Torso, Utils.EnumHitDirection.Front, false, 0.5f);
_t.SetStun(EnumEntityStunType.Prone);
_t.bodyDamage.StunDuration = LeapStun;
}
Debug.Log("[NecromancerTome] WolfControl: " + _t.EntityName + " сбит с ног");
}
/// Враг ближе всех к лучу прицела: не дальше LeapRange от волка и в конусе LeapConeDeg.
static EntityAlive Target(World _world, EntityPlayerLocal _player, EntityAlive _wolf)
{
Transform cam = _player.cameraTransform;
if (cam == null)
{
return null;
}
Vector3 eye = cam.position + Origin.position;
EntityAlive best = null;
float bestAngle = LeapConeDeg;
for (int i = 0; i < _world.EntityAlives.Count; i++)
{
EntityAlive e = _world.EntityAlives[i];
if (!SnakeControl.IsFoe(e, _wolf))
{
continue;
}
float d = (e.position - _wolf.position).magnitude;
if (d > LeapRange || d < LeapMin)
{
continue;
}
float angle = Vector3.Angle(cam.forward, e.getChestPosition() - eye);
if (angle < bestAngle)
{
bestAngle = angle;
best = e;
}
}
return best;
}
static void BloodTick(World _world, EntityAlive _wolf)
{
_bloodCheck -= Time.deltaTime;
if (_bloodCheck > 0f)
{
return;
}
_bloodCheck = 1f;
for (int i = 0; i < _world.EntityAlives.Count; i++)
{
EntityAlive e = _world.EntityAlives[i];
if (e == null)
{
continue;
}
float d2 = (e.position - _wolf.position).sqrMagnitude;
bool bleeding = SnakeControl.IsFoe(e, _wolf) && e.Health < e.GetMaxHealth() * BloodHealth;
if (bleeding && d2 < BloodRange * BloodRange)
{
if (_blood.Add(e.entityId))
{
e.AddNavObject(SnakeControl.SenseNavObject, null, null);
}
}
else if (_blood.Contains(e.entityId) && (!bleeding || d2 > BloodDropRange * BloodDropRange))
{
e.RemoveNavObject(SnakeControl.SenseNavObject);
_blood.Remove(e.entityId);
}
}
_blood.RemoveWhere(id => _world.GetEntity(id) == null);
}
/// Выход из волка - снять метки, забыть бросок.
public static void Clear()
{
World world = GameManager.Instance?.World;
if (world != null)
{
foreach (int id in _blood)
{
(world.GetEntity(id) as EntityAlive)?.RemoveNavObject(SnakeControl.SenseNavObject);
}
}
_blood.Clear();
_bloodCheck = 0f;
_leapTarget = null;
}
}
}