Стрела: защита от NullReference в GetDismemberChance (+ какое звено пустое - в лог); necrourn probe - что урна видит в зоне и что выбрала; necrofire
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
3ffb90cc57
commit
f372660e39
@@ -61,11 +61,29 @@ namespace NecromancerTome
|
||||
|
||||
/// <summary>У перевозки по нити - ящик «откуда» последнего шага (дух летит сначала к нему), иначе null.</summary>
|
||||
public static Vector3i? LastFrom;
|
||||
/// <summary>Что выбрал последний шаг (для necrourn probe, 23.10: «стою в 19 м - не работают»).</summary>
|
||||
public static string LastWhat;
|
||||
|
||||
/// <summary>Пробный шаг урны без изменений: что в зоне и что дух выбрал бы сейчас (команда necrourn x y z probe).</summary>
|
||||
public static string Probe(World _world, TEFeatureCinerarium _urn)
|
||||
{
|
||||
string zone = "";
|
||||
if (!_urn.IsLogist && !_urn.IsWorker)
|
||||
{
|
||||
Zone z = Scan(_world, _urn);
|
||||
zone = "коллекторов " + z.Collectors.Count + ", станций " + z.Stations.Count + ", ящиков " + z.Boxes.Count +
|
||||
", сумка " + (z.Bag != null) + ", спелых " + z.Ripe.Count + (z.Ripe.Count > 0 ? " (" + z.Ripe[0] + ")" : "") +
|
||||
", пустых клеток " + z.Empty.Count + "; ";
|
||||
}
|
||||
Vector3i? at = Step(_world, _urn, out bool stuck, true);
|
||||
return zone + "шаг: " + (at.HasValue ? LastWhat + " в " + at.Value : "нечего делать") + (stuck ? ", класть некуда" : "");
|
||||
}
|
||||
|
||||
static Vector3i? DoStep(World _world, TEFeatureCinerarium _urn, out bool _stuck)
|
||||
{
|
||||
_stuck = false;
|
||||
LastFrom = null;
|
||||
LastWhat = "?";
|
||||
// Урна с нитями - логист (16.10: «занимается только перетаскиванием из ящиков в ящики»).
|
||||
if (_urn.IsLogist)
|
||||
{
|
||||
@@ -88,6 +106,7 @@ namespace NecromancerTome
|
||||
{
|
||||
c.SetModified();
|
||||
}
|
||||
LastWhat = "расходники коллектору";
|
||||
return z.Where[c];
|
||||
}
|
||||
}
|
||||
@@ -103,6 +122,7 @@ namespace NecromancerTome
|
||||
c.Items = items;
|
||||
c.SetModified();
|
||||
}
|
||||
LastWhat = "готовое из коллектора";
|
||||
return z.Where[c];
|
||||
}
|
||||
}
|
||||
@@ -115,6 +135,7 @@ namespace NecromancerTome
|
||||
{
|
||||
w.Output = output;
|
||||
}
|
||||
LastWhat = "готовое со станции";
|
||||
return z.Where[w];
|
||||
}
|
||||
}
|
||||
@@ -123,6 +144,7 @@ namespace NecromancerTome
|
||||
{
|
||||
if (Harvest(_world, p, z, ref blocked))
|
||||
{
|
||||
LastWhat = "сбор урожая";
|
||||
return p;
|
||||
}
|
||||
}
|
||||
@@ -131,6 +153,7 @@ namespace NecromancerTome
|
||||
Vector3i? planted = PlantRandom(_world, z);
|
||||
if (planted.HasValue)
|
||||
{
|
||||
LastWhat = "посадка";
|
||||
return planted;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,51 @@
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// Стрела: NullReferenceException в ItemAction.GetDismemberChance (23.10.2026). Вызывается из ProjectileMoveScript.checkCollision
|
||||
/// аргументом к ItemActionAttack.Hit - поэтому в первых логах исключение выглядело как ошибка «внутри Hit» (метод был встроен).
|
||||
/// Пустое звено цепочки actionData.invData.holdingEntity.inventory / invData.item у стрелы, запомнившей данные лука при выстреле.
|
||||
/// Исключение вылетало раньше, чем стрела сменит состояние, и она «попадала» снова каждый шаг физики - пачки по 6-19 ошибок.
|
||||
/// Здесь: при исключении шанс отстрела конечности 0 (стрела долетает как обычно), в лог - какое звено пустое (не больше
|
||||
/// MaxLogs раз за запуск). Причина звена - выяснить по этим строкам.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(ItemAction), nameof(ItemAction.GetDismemberChance))]
|
||||
public static class Patch_ItemAction_GetDismemberChance_Guard
|
||||
{
|
||||
const int MaxLogs = 5;
|
||||
static int _logged;
|
||||
|
||||
public static Exception Finalizer(Exception __exception, ItemActionData _actionData, WorldRayHitInfo hitInfo, ref float __result)
|
||||
{
|
||||
if (__exception == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
__result = 0f;
|
||||
if (_logged < MaxLogs)
|
||||
{
|
||||
_logged++;
|
||||
try
|
||||
{
|
||||
ItemInventoryData inv = _actionData?.invData;
|
||||
EntityAlive holder = inv?.holdingEntity;
|
||||
Debug.Log("[NecromancerTome] DismemberGuard: " + __exception.GetType().Name + " в GetDismemberChance - actionData " +
|
||||
(_actionData == null ? "NULL" : _actionData.GetType().Name) +
|
||||
", invData " + (inv == null ? "NULL" : "slot " + inv.slotIdx) +
|
||||
", item " + (inv?.item == null ? "NULL" : inv.item.Name) +
|
||||
", holdingEntity " + (holder == null ? "NULL" : holder.GetType().Name + " " + holder.entityId + (holder.IsDead() ? " dead" : "")) +
|
||||
", inventory " + (holder == null ? "-" : holder.inventory == null ? "NULL" : "в руке " + holder.inventory.holdingItem?.Name) +
|
||||
", tag " + (hitInfo?.tag ?? "NULL") + " - шанс отстрела 0");
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] DismemberGuard: не удалось описать - " + e2.Message);
|
||||
}
|
||||
}
|
||||
return null; // проглотить: стрела долетает и попадает как обычно
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
using System;
|
||||
using HarmonyLib;
|
||||
using UnityEngine;
|
||||
|
||||
namespace NecromancerTome
|
||||
{
|
||||
/// <summary>
|
||||
/// Диагностика (23.10.2026): NullReferenceException внутри ItemActionAttack.Hit при попадании стрелы (ProjectileMoveScript.checkCollision)
|
||||
/// - пачками по одной стреле (исключение вылетает раньше, чем стрела сменит состояние, и она «попадает» снова каждый шаг физики).
|
||||
/// Было дважды, рядом с только что догруженными зомби спящих зон. Здесь только запись в лог, во что попала стрела; исключение
|
||||
/// уходит дальше как было. Не больше MaxLogs записей за запуск. Убрать, когда причина найдена.
|
||||
/// </summary>
|
||||
[HarmonyPatch(typeof(ItemActionAttack), nameof(ItemActionAttack.Hit))]
|
||||
public static class Patch_ItemActionAttack_Hit_Diag
|
||||
{
|
||||
const int MaxLogs = 5;
|
||||
static int _logged;
|
||||
|
||||
public static Exception Finalizer(Exception __exception, WorldRayHitInfo hitInfo, int _attackerEntityId, ItemValue damagingItemValue)
|
||||
{
|
||||
if (__exception == null || _logged >= MaxLogs)
|
||||
{
|
||||
return __exception;
|
||||
}
|
||||
_logged++;
|
||||
try
|
||||
{
|
||||
string s = "tag=" + (hitInfo?.tag ?? "null") + ", transform=" + (hitInfo?.transform != null ? hitInfo.transform.name : "NULL") +
|
||||
", collider=" + (hitInfo?.hitCollider != null ? hitInfo.hitCollider.name : "NULL");
|
||||
if (hitInfo != null && hitInfo.tag != null && hitInfo.tag.StartsWith("E_"))
|
||||
{
|
||||
Entity e = ItemActionAttack.FindHitEntityNoTagCheck(hitInfo, out string part);
|
||||
s += ", entity=" + (e == null ? "NULL" : e.GetType().Name + " " + EntityClass.list[e.entityClass].entityClassName + " " + e.entityId +
|
||||
(e is EntityAlive ea ? (ea.IsDead() ? " dead" : " alive") + ", emodel " + (ea.emodel != null) : "")) +
|
||||
", part=" + (part ?? "null");
|
||||
}
|
||||
Entity atk = GameManager.Instance?.World?.GetEntity(_attackerEntityId);
|
||||
s += ", attacker=" + (atk == null ? "NULL " + _attackerEntityId : atk.GetType().Name + " " + atk.entityId) +
|
||||
", item=" + (damagingItemValue?.ItemClass?.Name ?? "null");
|
||||
Debug.Log("[NecromancerTome] HitDiag: исключение в ItemActionAttack.Hit (" + __exception.GetType().Name + "): " + s +
|
||||
"\n" + __exception.StackTrace);
|
||||
}
|
||||
catch (Exception e2)
|
||||
{
|
||||
Debug.Log("[NecromancerTome] HitDiag: не удалось описать попадание - " + e2.Message);
|
||||
}
|
||||
return __exception;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ namespace NecromancerTome
|
||||
/// necroplace блок x y z [поворот 0-3] - поставить блок (как игрок: OnBlockAdded, TE)
|
||||
/// necrohold предмет [число] - положить в текущую ячейку пояса и взять в руку
|
||||
/// necroslot [N] - какая ячейка пояса в руке и что в ячейках; N - взять в руку ячейку N (23.10: проверка Archer's Eye без мыши)
|
||||
/// necrofire press|release - ЛКМ предметом в руке: нажать / отпустить (лук: натянуть / выстрелить; 23.10, ошибка в Hit от стрелы)
|
||||
/// necrobox x y z list|clear|add предмет число - содержимое хранилища (ящик, сумка урны)
|
||||
/// necrourn x y z info|skull 0|1|from|to|out bx by bz|clear|sample предмет число|group все|нет - урна
|
||||
/// necroclick 0|1 - отпущенная ЛКМ/ПКМ ножом для нитей по блоку под прицелом
|
||||
@@ -45,6 +46,13 @@ namespace NecromancerTome
|
||||
case "necroaim": Aim(w, pl); break;
|
||||
case "necroplace": PlaceBlock(w, _params); break;
|
||||
case "necrohold": Hold(pl, _params); break;
|
||||
case "necrofire":
|
||||
{
|
||||
bool rel = _params.Count > 0 && _params[0] == "release";
|
||||
pl.inventory.Execute(0, rel, pl.playerInput);
|
||||
Out("necrofire: " + (rel ? "отпущено" : "нажато") + ", в руке " + pl.inventory.holdingItem?.Name + ", заряжено " + pl.inventory.holdingItemItemValue.Meta);
|
||||
break;
|
||||
}
|
||||
case "necroslot":
|
||||
{
|
||||
if (_params.Count > 0)
|
||||
@@ -470,6 +478,7 @@ namespace NecromancerTome
|
||||
u.SetSample(_p.Count > 5 ? new ItemStack(ItemClass.GetItem(_p[4], true), int.Parse(_p[5])) : ItemStack.Empty.Clone());
|
||||
break;
|
||||
case "contents": CinerariumContents.Open(GameManager.Instance.World.GetPrimaryPlayer(), u); break;
|
||||
case "probe": Out("necrourn: " + pos + " " + CinerariumWork.Probe(GameManager.Instance.World, u)); return;
|
||||
case "run": u.ZoneCommand("zurn_run", GameManager.Instance.World.GetPrimaryPlayer()); break;
|
||||
case "stop": u.ZoneCommand("zurn_stop", null); break;
|
||||
case "size": u.ZoneSide = int.Parse(_p[4]); u.SetModified(); break;
|
||||
@@ -528,6 +537,15 @@ namespace NecromancerTome
|
||||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo) => NecroDev.Run("necrohold", _params);
|
||||
}
|
||||
|
||||
[Preserve]
|
||||
public class ConsoleCmdNecroFire : ConsoleCmdAbstract
|
||||
{
|
||||
public override string[] getCommands() => new[] { "necrofire" };
|
||||
public override string getDescription() => "NecromancerTome (разработка): ЛКМ предметом в руке: press|release";
|
||||
public override bool AllowedInMainMenu => false;
|
||||
public override void Execute(List<string> _params, CommandSenderInfo _senderInfo) => NecroDev.Run("necrofire", _params);
|
||||
}
|
||||
|
||||
[Preserve]
|
||||
public class ConsoleCmdNecroSlot : ConsoleCmdAbstract
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user