Труп «Жертвы» открывается сам, как ящик: без мешка

Обыск больше не превращает труп в мешок. Труп получает сумку со списком добычи zPackVictim
и открывается игровым путём (LockRequestLocal -> LootBagOpened -> окно добычи с игровым
таймером обыска). Запрет открывать мёртвых у EntityAlive снят префиксами только для таких
трупов. Открытый и не опустошённый труп лежит час, опустошённый убирается сразу.
После загрузки труп узнаётся по сохранённой сумке (баффы зомби в сейв не пишутся).
LockRequestLocal вызывается из TEPersistence (перегрузка с ReadOnlySpan не компилируется
в основном проекте). Класс EntityLootContainerVictim оставлен ради старых сейвов.
Описание ножа исправлено на всех языках, README - текущее описание.

Проверено в игре на 3.3: открытие, таймер, уборка опустошённых, восстановление после загрузки.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-09-26 19:33:52 +03:00
co-authored by Claude Opus 5.5
parent 1c2e3f2cdf
commit e25544521d
10 changed files with 239 additions and 104 deletions
+7 -43
View File
@@ -5,8 +5,8 @@ namespace NecromancerTome
{
/// <summary>
/// Нож некроманта (BACKLOG.md, п.5): зомби, задетый ножом, носит buffNecroVictim (buffs.xml) до
/// самой смерти. На смерти он роняет мешок «Жертва» (EntityLootContainerVictim в
/// entityclasses.xml).
/// самой смерти. На смерти его труп становится обыскиваемым контейнером с добычей «Жертвы»
/// (CorpseLootPatch.cs; до 1.5.0 обыск превращал труп в мешок EntityLootContainerVictim).
///
/// ПОВЕДЕНИЕ ИСПРАВЛЕНО 2026-09-18, И ЭТО ВОЗВРАТ К ТОМУ, О ЧЁМ ДОГОВАРИВАЛИСЬ. Баг-репорт
/// пользователя: «мы договаривались, что он выпадает только если не должен был выпасть мешок с
@@ -41,8 +41,11 @@ namespace NecromancerTome
/// от этого не изменились: они по-прежнему решают судьбу ОБЫЧНОГО мешка.
///
/// Второй префикс (на DropBagServer) УДАЛЁН ЦЕЛИКОМ - он теперь вреден: подменял бы и тот
/// обычный мешок, который мы как раз хотим оставить. Спавн «Жертвы» переехал сюда, в
/// SpawnVictimBag.
/// обычный мешок, который мы как раз хотим оставить.
///
/// С 1.5.0 мешка «Жертва» нет совсем: сам труп открывается как ящик (CorpseLootPatch.cs), и
/// метод SpawnVictimBag удалён. Класс EntityLootContainerVictim в entityclasses.xml ОСТАВЛЕН
/// ради сейвов: в мирах игроков могут лежать уже выпавшие мешки, и без класса они не загрузятся.
///
/// ПОЧЕМУ ВООБЩЕ НУЖЕН ПРЕФИКС, А НЕ ПОСТФИКС. Узнать «выпал ли мешок» после оригинала нечем:
/// dropItemOnDeath ничего не возвращает и следов не оставляет. Поэтому бросок повторяется
@@ -57,7 +60,6 @@ namespace NecromancerTome
public static class Patch_EntityAlive_dropItemOnDeath_VictimBag
{
public const string VictimBuffName = "buffNecroVictim";
public const string VictimContainerClassName = "EntityLootContainerVictim";
public static bool Prefix(EntityAlive __instance)
{
@@ -122,43 +124,5 @@ namespace NecromancerTome
CorpseLoot.MarkCorpse(__instance);
return false;
}
/// <summary>Спавнит мешок «Жертва». Раньше это жило в префиксе на Entity.DropBagServer и
/// подменяло собой обычный мешок; теперь ничего не подменяет, поэтому тот патч снят.
///
/// _offsetAside - сдвинуть в сторону, потому что рядом уже лёг обычный мешок. Ваниль
/// кладёт свой ровно в GetPosition() + 0.9 по Y (декомпилировано, Entity.DropBagServer),
/// то есть без сдвига два мешка встали бы в одну точку и выглядели как один.</summary>
public static void SpawnVictimBag(EntityAlive _victim, bool _offsetAside)
{
int containerClassId = EntityClass.GetId(VictimContainerClassName);
if (containerClassId == -1)
{
Debug.LogWarning("[NecromancerTome] VictimPatch: класс '" + VictimContainerClassName +
"' не найден, мешок не выпадет");
return;
}
Vector3 pos = _victim.GetPosition();
pos.y += 0.9f;
if (_offsetAside)
{
// Полметра вбок - достаточно, чтобы мешки читались как два и оба брались
// прицелом, и мало, чтобы «Жертва» уехала за стену или в соседнюю комнату.
pos += _victim.qrotation * new Vector3(0.6f, 0f, 0f);
}
Entity spawned = EntityFactory.CreateEntity(containerClassId, pos, Vector3.zero);
if (spawned is EntityLootContainer lootContainer)
{
GameManager.Instance.World.SpawnEntityInWorld(lootContainer);
Debug.Log("[NecromancerTome] VictimPatch: " + _victim.entityId + " уронил «Жертву» " +
lootContainer.entityId + (_offsetAside ? " (вторым мешком, со сдвигом)" : " (единственным мешком)"));
}
else
{
Debug.LogWarning("[NecromancerTome] VictimPatch: созданная сущность для '" +
VictimContainerClassName + "' не оказалась EntityLootContainer");
}
}
}
}