Одержимые зомби открывают двери и щёлкают выключателями по E (питомцы - нет)

Прицел на блок до 3 м от головы - первая доступная команда open/close/light/activate, как E у персонажа; замок двери проверяет сама дверь. Иначе E - как раньше (лут, приёмы). Подсказка - 13 языков.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-13 17:27:29 +03:00
co-authored by Claude Opus 5.5
parent a8ee6eb5cf
commit b50cd6d008
4 changed files with 78 additions and 0 deletions
+77
View File
@@ -595,6 +595,12 @@ namespace NecromancerTome
// кнопка геймпада (тест 03.10: «подрыва нету»).
detonate = input.Activate.WasPressed || input.PermanentActions.Activate.WasPressed;
}
// E на дверь или выключатель - как у персонажа (указание 13.10: одержимым управляет ГГ - «ему хватает ума не
// ломать дверь, которую можно открыть»); у волка и крысы - нет. Прицел не на двери - E как раньше.
if (detonate && !IsPet && UseDoorOrSwitch(world, z))
{
detonate = false;
}
// Только гуманоиды обычной комплекции (указание 13.10, FpArms.Allows) - у остальных ~/ё ничего не делает.
if (input != null && !gui && FpArms.Allows(z) &&
(input.CameraChange.WasPressed || input.QuickMenu.WasPressed || input.PermanentActions.QuickMenu.WasPressed))
@@ -1592,6 +1598,10 @@ namespace NecromancerTome
{
text += ", " + Localization.Get("necroPossessHintZombieBag");
}
if (!IsPet)
{
text += ", " + Localization.Get("necroPossessHintDoor"); // двери и выключатели (13.10)
}
if (Flying)
{
text += ", " + Localization.Get("necroPossessHintFly");
@@ -2145,6 +2155,73 @@ namespace NecromancerTome
_flyZoomHeld = false;
}
/// <summary>Команды блока, которые одержимый выполняет по E (как первая доступная команда у персонажа): двери
/// (TEFeatureDoor - open/close), выключатели и лампы (light), рычаги квестов (activate). Без take, lock, edit,
/// trigger. Запертую чужую дверь не откроет - замок проверяет сама дверь.</summary>
static readonly string[] UseCommands = { "open", "close", "light", "activate" };
public const float UseReach = 3f;
static bool UseDoorOrSwitch(World _world, EntityAlive _z)
{
Transform cam = _player?.cameraTransform;
if (cam == null || _world == null)
{
return false;
}
int n = Physics.RaycastNonAlloc(cam.position, cam.forward, _aimHits, 50f, -5, QueryTriggerInteraction.Ignore);
Transform self = _z.transform;
float best = float.MaxValue;
RaycastHit hit = default;
for (int i = 0; i < n; i++)
{
RaycastHit h = _aimHits[i];
if (h.distance >= best || h.transform == self || h.transform.IsChildOf(self))
{
continue;
}
best = h.distance;
hit = h;
}
if (best == float.MaxValue)
{
return false;
}
Vector3 at = hit.point + Origin.position;
if ((at - _z.getHeadPosition()).sqrMagnitude > UseReach * UseReach)
{
return false;
}
Vector3i pos = World.worldToBlockPos(at + cam.forward * 0.05f);
BlockValue bv = _world.GetBlock(pos);
if (bv.isair)
{
pos = World.worldToBlockPos(at - cam.forward * 0.05f);
bv = _world.GetBlock(pos);
}
if (bv.ischild)
{
pos += bv.parent;
bv = _world.GetBlock(pos);
}
Block b = bv.Block;
if (bv.isair || b == null)
{
return false;
}
BlockActivationCommand[] cmds = b.GetBlockActivationCommands(_world, bv, pos, _player);
for (int i = 0; cmds != null && i < cmds.Length; i++)
{
if (!cmds[i].enabled || System.Array.IndexOf(UseCommands, cmds[i].text) < 0)
{
continue;
}
b.OnBlockActivated(cmds[i].text, _world, pos, bv, _player);
Debug.Log("[NecromancerTome] Possession: " + _z.EntityName + " - " + cmds[i].text + " " + b.GetBlockName() + " " + pos);
return true;
}
return false;
}
/// <summary>Миг удара рук от первого лица (FpArms): зомби бьёт сразу - его удар начат и сразу «дошёл до точки
/// удара» (ForceImpact в EntityAlive.IsAttackImpact, задержка луча ItemActionMelee снята). Урон, дальность,
/// цель - его, взгляд - на прицел (MeleeAim).</summary>