diff --git a/Config/Localization.csv b/Config/Localization.csv
index f99f592..0fd9cc8 100644
--- a/Config/Localization.csv
+++ b/Config/Localization.csv
@@ -261,6 +261,7 @@ necroWolfBagWaypoint,items,Item,,,,Wolf bag,,Wolfstasche,Bolsa del lobo,Sac du l
necroZombieBagWindowTitle,items,Item,,,,Zombie bag,,Zombietasche,Bolsa del zombi,Sac du zombie,Borsa dello zombi,ゾンビの袋,좀비 가방,Torba zombie,Bolsa do zumbi,Сумка зомби,Zombi çantası,僵尸的包,殭屍的包
necroZombieBagWaypoint,items,Item,,,,Zombie bag,,Zombietasche,Bolsa del zombi,Sac du zombie,Borsa dello zombi,ゾンビの袋,좀비 가방,Torba zombie,Bolsa do zumbi,Сумка зомби,Zombi çantası,僵尸的包,殭屍的包
necroPossessHintZombieBag,items,Item,,,,"Tab - bag, E at a container, loot bag or item - into the bag",,"Tab - Tasche, E an Behälter, Beutel oder Gegenstand - in die Tasche","Tab: bolsa, E en un contenedor, saco o objeto: a la bolsa","Tab : sac, E sur un conteneur, un sac ou un objet : dans le sac","Tab - borsa, E su contenitore, sacca o oggetto - nella borsa","Tab: 袋、コンテナ・袋・アイテムにE: 袋へ","Tab - 가방, 상자·자루·물건에 E - 가방으로","Tab - torba, E na pojemniku, worku lub przedmiocie - do torby","Tab - bolsa, E em contêiner, saco ou item - para a bolsa","Tab - сумка, E на ящик, мешок или предмет - в сумку","Tab - çanta, kaba, torbaya veya eşyaya E - çantaya","Tab - 包,对容器、袋子或物品按E - 放入包","Tab - 包,對容器、袋子或物品按E - 放入包"
+necroPossessHintDoor,items,Item,,,,"E at a door or switch - use it",,"E an Tür oder Schalter - benutzen","E en una puerta o interruptor: usar","E sur une porte ou un interrupteur : utiliser","E su porta o interruttore - usare","ドアやスイッチにE: 使う","문이나 스위치에 E - 사용","E na drzwiach lub przełączniku - użyj","E em porta ou interruptor - usar","E на дверь или выключатель - открыть, включить","Kapıya veya şaltere E - kullan","对门或开关按E - 使用","對門或開關按E - 使用"
necroZombieBagTook,items,Item,,,,"Zombie bag ({1}/{2}): {0}",,"Zombietasche ({1}/{2}): {0}","Bolsa del zombi ({1}/{2}): {0}","Sac du zombie ({1}/{2}) : {0}","Borsa dello zombi ({1}/{2}): {0}","ゾンビの袋({1}/{2}):{0}","좀비 가방 ({1}/{2}): {0}","Torba zombie ({1}/{2}): {0}","Bolsa do zumbi ({1}/{2}): {0}","Сумка зомби ({1}/{2}): {0}","Zombi çantası ({1}/{2}): {0}","僵尸的背包({1}/{2}):{0}","殭屍的背包({1}/{2}):{0}"
necroZombieBagFull,items,Item,,,,"The zombie bag is full ({1}/{2}): {0}",,"Die Zombietasche ist voll ({1}/{2}): {0}","La bolsa del zombi está llena ({1}/{2}): {0}","Le sac du zombie est plein ({1}/{2}) : {0}","La borsa dello zombi è piena ({1}/{2}): {0}","ゾンビの袋がいっぱい({1}/{2}):{0}","좀비 가방이 가득 찼습니다 ({1}/{2}): {0}","Torba zombie jest pełna ({1}/{2}): {0}","A bolsa do zumbi está cheia ({1}/{2}): {0}","Сумка зомби полна ({1}/{2}): {0}","Zombi çantası dolu ({1}/{2}): {0}","僵尸的背包已满({1}/{2}):{0}","殭屍的背包已滿({1}/{2}):{0}"
necroZombieBagShow,items,Item,,,,"Zombie bag ({1}/{2}): {0}",,"Zombietasche ({1}/{2}): {0}","Bolsa del zombi ({1}/{2}): {0}","Sac du zombie ({1}/{2}) : {0}","Borsa dello zombi ({1}/{2}): {0}","ゾンビの袋({1}/{2}):{0}","좀비 가방 ({1}/{2}): {0}","Torba zombie ({1}/{2}): {0}","Bolsa do zumbi ({1}/{2}): {0}","Сумка зомби ({1}/{2}): {0}","Zombi çantası ({1}/{2}): {0}","僵尸的背包({1}/{2}):{0}","殭屍的背包({1}/{2}):{0}"
diff --git a/HarmonySrc/Possession.cs b/HarmonySrc/Possession.cs
index 302846c..5561418 100644
--- a/HarmonySrc/Possession.cs
+++ b/HarmonySrc/Possession.cs
@@ -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;
}
+ /// Команды блока, которые одержимый выполняет по E (как первая доступная команда у персонажа): двери
+ /// (TEFeatureDoor - open/close), выключатели и лампы (light), рычаги квестов (activate). Без take, lock, edit,
+ /// trigger. Запертую чужую дверь не откроет - замок проверяет сама дверь.
+ 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;
+ }
+
/// Миг удара рук от первого лица (FpArms): зомби бьёт сразу - его удар начат и сразу «дошёл до точки
/// удара» (ForceImpact в EntityAlive.IsAttackImpact, задержка луча ItemActionMelee снята). Урон, дальность,
/// цель - его, взгляд - на прицел (MeleeAim).
diff --git a/NecromancerHarmony.dll b/NecromancerHarmony.dll
index 469330b..98b9fac 100644
Binary files a/NecromancerHarmony.dll and b/NecromancerHarmony.dll differ
diff --git a/NecromancerHarmony.pdb b/NecromancerHarmony.pdb
index 100bcc7..2e1ed0a 100644
Binary files a/NecromancerHarmony.pdb and b/NecromancerHarmony.pdb differ