- Могильная крыса: своя модель без волчьей шерсти; стоит на земле (центр капсулы); E - осмотр контейнеров без права взять (окно игры, рюкзак скрыт, отказ «инвентарь заполнен», контейнер не разрушается); ПКМ - проход сквозь постройки, полупрозрачная. - Стервятник: курица в когтях - слепок с обвисшими крыльями и шеей, без хитбокса. - Зомбособака: нюх - все существа рядом на компасе и карте. - Медведь: толстая шкура (вдвое меньше урона), рёв-провокация, удар лапой по блокам. - Лютоволк: бросок на цель с опрокидыванием, след крови раненых, сумка на 3 ячейки (лут, выгрузка у тела, выпадает мешком при гибели и отзыве; отдельный файл сейва). - Череп (останки): 2% при обычном обыске красного трупа вместо браслета. - Прицел книги не рисуется в одержимости; подсказки вселения на 13 языках. - Замер EntityLeakProbe: удалённые, но живые в памяти сущности - в лог. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
81 lines
2.8 KiB
C#
81 lines
2.8 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
namespace NecromancerTome
|
|
{
|
|
/// <summary>
|
|
/// Нюх одержимой зомбособаки (указание 09.10: «добавь зомбособаке отображение противников, как в Тёмном чутье.
|
|
/// Типа у собаки нюх»; решено: только в одержимости, чует всех существ). Как Тёмное чутьё змеи
|
|
/// (SnakeControl.Sense): раз в секунду существа ближе SenseRange к собаке получают метку на компасе и карте,
|
|
/// дальше SenseDropRange, мёртвые и выгруженные её теряют. В отличие от чутья - не только нежить: звери,
|
|
/// бандиты, зомби. Не метятся: тело игрока и другие игроки, питомцы мода (призрачные), торговцы.
|
|
/// </summary>
|
|
public static class DogSense
|
|
{
|
|
public const string DogClass = "necroZombieDog";
|
|
static readonly HashSet<int> _sensed = new HashSet<int>();
|
|
static float _check;
|
|
|
|
public static bool Is(EntityAlive _e)
|
|
{
|
|
return _e != null && EntityClass.list.TryGetValue(_e.entityClass, out EntityClass cls) && cls != null &&
|
|
cls.entityClassName == DogClass;
|
|
}
|
|
|
|
static bool Smells(EntityAlive _e, EntityAlive _dog)
|
|
{
|
|
return _e != null && _e != _dog && !_e.IsDead() && !(_e is EntityPlayer) && !(_e is EntityTrader) &&
|
|
!SnakeControl.IsNecroEntity(_e) && !GhostTraderPatch.ShouldBeGhost(_e);
|
|
}
|
|
|
|
public static void Tick(World _world, EntityAlive _dog, float _dt)
|
|
{
|
|
_check -= _dt;
|
|
if (_check > 0f)
|
|
{
|
|
return;
|
|
}
|
|
_check = 1f;
|
|
Vector3 at = _dog.position;
|
|
for (int i = 0; i < _world.EntityAlives.Count; i++)
|
|
{
|
|
EntityAlive e = _world.EntityAlives[i];
|
|
if (e == null)
|
|
{
|
|
continue;
|
|
}
|
|
float d2 = (e.position - at).sqrMagnitude;
|
|
bool smell = Smells(e, _dog);
|
|
if (smell && d2 < SnakeControl.SenseRange * SnakeControl.SenseRange)
|
|
{
|
|
if (_sensed.Add(e.entityId))
|
|
{
|
|
e.AddNavObject(SnakeControl.SenseNavObject, null, null);
|
|
}
|
|
}
|
|
else if (_sensed.Contains(e.entityId) && (!smell || d2 > SnakeControl.SenseDropRange * SnakeControl.SenseDropRange))
|
|
{
|
|
e.RemoveNavObject(SnakeControl.SenseNavObject);
|
|
_sensed.Remove(e.entityId);
|
|
}
|
|
}
|
|
_sensed.RemoveWhere(id => _world.GetEntity(id) == null);
|
|
}
|
|
|
|
/// <summary>Выход из собаки - снять все метки.</summary>
|
|
public static void Clear()
|
|
{
|
|
World world = GameManager.Instance?.World;
|
|
if (world != null)
|
|
{
|
|
foreach (int id in _sensed)
|
|
{
|
|
(world.GetEntity(id) as EntityAlive)?.RemoveNavObject(SnakeControl.SenseNavObject);
|
|
}
|
|
}
|
|
_sensed.Clear();
|
|
_check = 0f;
|
|
}
|
|
}
|
|
}
|