Прилив: нож пересоздаётся только при свободной руке, сбой не ломает руку

Тест 10.10: NRE в Hand.Unequip оставлял Hand.isSwitching = true - рука
переставала брать что-либо до перезапуска. Теперь: ножи в других слотах
помечаются к пересборке, нож в руке пересоздаётся, когда нет смены
предмета и удара; сбой перехватывается, isSwitching снимается, Reconcile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-10-10 14:09:08 +03:00
co-authored by Claude Opus 5.5
parent 7d87903302
commit 523b9edcd9
3 changed files with 76 additions and 12 deletions
+76 -12
View File
@@ -57,6 +57,7 @@ namespace NecromancerTome
{
Active = Armed = false;
_swapped = false; // классы предметов игра грузит заново с миром
_recreate = false;
_origHoldType = -1;
Heart(false);
Patch_XUiC_HUDStatBar_Update_BloodTide.Forget();
@@ -70,6 +71,7 @@ namespace NecromancerTome
Debug.Log("[NecromancerTome] BloodTide: " + (tide ? "прилив начался" : "прилив схлынул"));
}
SwapKnife(player, Active);
TryRecreate(player);
if (Time.time >= _nextBuffCheck && !player.IsDead())
{
_nextBuffCheck = Time.time + 0.5f;
@@ -108,20 +110,82 @@ namespace NecromancerTome
knife.HoldType.Value = _tide ? TideHoldType : _origHoldType;
knife.MeshFile = _tide && !string.IsNullOrEmpty(TideMeshFile) ? TideMeshFile : _origMesh;
_swapped = _tide;
Inventory inv = _player.inventory;
ItemInventoryData held = inv?.holdingItemData;
if (held != null && held.item == knife)
Hand hand = _player.Hand;
// Ножи в других слотах пояса: «собранная» копия помечается устаревшей - соберутся заново (с новыми хватом и
// моделью), когда их возьмут. Нож в руке - в TryRecreate, когда рука свободна.
if (hand?.slotDatas != null)
{
// NeedsRebuild - «собранный» предмет не совпадает с тем, что в слоте: сбрасываем собранный, и
// повторный выбор того же слота снимает нож и собирает его заново (Hand.SelectHoldingMode).
// Не ItemValue.None (тест 10.10: NRE в Hand.Unequip - он читает класс предмета у собранного): копия того же
// ножа с другим Seed - класс прежний, а EqualsExceptUseTimesAndMeta (по нему NeedsRebuild) уже не совпадёт.
ItemValue pv = (held.physicalItemValue ?? held.itemValue).Clone();
pv.Seed = (ushort)(pv.Seed + 1);
held.physicalItemValue = pv;
inv.SetHoldingItemIdx(inv.holdingItemIdx);
foreach (ItemInventoryData d in hand.slotDatas)
{
if (d != null && d.item == knife && d != hand.equipped && d.physicalItemValue != null)
{
MarkStale(d);
}
}
}
_recreate = true;
Debug.Log("[NecromancerTome] BloodTide: нож - хват " + knife.HoldType.Value);
}
/// <summary>Нож в руке ждёт пересоздания (сменились хват/модель).</summary>
static bool _recreate;
/// <summary>«Собранный» предмет перестаёт совпадать со слотом (NeedsRebuild): копия того же ножа с другим Seed.
/// Не ItemValue.None (тест 10.10): Hand.Unequip читает класс предмета у собранного - NRE.</summary>
static void MarkStale(ItemInventoryData _d)
{
ItemValue pv = (_d.physicalItemValue ?? _d.itemValue).Clone();
pv.Seed = (ushort)(pv.Seed + 1);
_d.physicalItemValue = pv;
}
/// <summary>
/// Пересоздать нож в руке: повторный выбор того же слота снимает его и собирает заново (Hand.SelectHoldingMode).
/// Только когда рука свободна - не идёт смена предмета и удар; иначе ждём кадр. Тест 10.10: при сбое внутри
/// SelectHoldingMode игра оставляла Hand.isSwitching = true навсегда, и рука переставала брать что-либо до
/// перезапуска («после ошибки не получается переключиться на что-либо ещё»). Поэтому сбой перехватываем и
/// возвращаем руку в рабочее состояние.
/// </summary>
static void TryRecreate(EntityPlayerLocal _player)
{
if (!_recreate)
{
return;
}
Inventory inv = _player.inventory;
Hand hand = _player.Hand;
ItemInventoryData held = inv?.holdingItemData;
if (hand == null || held == null || !SnakeMode.IsKnife(held.item))
{
_recreate = false; // ножа в руке нет - возьмут, тогда и соберётся (слоты помечены)
return;
}
if (hand.isSwitching || inv.IsHoldingItemActionRunning() || _player.IsDead())
{
return;
}
_recreate = false;
ItemValue before = held.physicalItemValue;
try
{
MarkStale(held);
inv.SetHoldingItemIdx(inv.holdingItemIdx);
Debug.Log("[NecromancerTome] BloodTide: нож в руке пересоздан, хват " + held.item.HoldType.Value);
}
catch (System.Exception ex)
{
Debug.LogWarning("[NecromancerTome] BloodTide: нож не пересоздан - " + ex.Message + "; рука восстановлена");
held.physicalItemValue = before;
hand.isSwitching = false;
try
{
hand.Reconcile();
}
catch (System.Exception ex2)
{
Debug.LogWarning("[NecromancerTome] BloodTide: Reconcile - " + ex2.Message);
}
}
Debug.Log("[NecromancerTome] BloodTide: нож - хват " + knife.HoldType.Value + (held != null && held.item == knife ? ", в руке пересоздан" : ""));
}
// ---------------------------------------------------------------- сердце
Binary file not shown.
Binary file not shown.