Пространственный браслет: в руке пусто, блочный хват

Свёрток-«коробочка на бечёвке» (parcelGenericPrefab + HoldType=31,
унаследованные от Петли вора) для браслета выглядел нелепо. Теперь в
руке не видно ничего, только кулак, повёрнутый вниз.

Связка списана с ванильного vehicleMinibikePlaceable (items.xml:13385),
где те же две строки стоят подряд: HoldType="7" + HoldingItemHidden="true".

Найдено декомпиляцией Assembly-CSharp 3.2.0 через Mono.Cecil, который
лежит прямо в игре (Mods/0_TFP_Harmony/Mono.Cecil.dll):

- HoldType=7 - это и есть блочный хват: его ставит ItemClassBlock..ctor,
  а в blocks.xml свойство HoldType не встречается ни разу, то есть каждый
  блок в игре держится именно этим значением. Костет - это 70, запасной
  вариант не понадобился.
- HoldingItemHidden - штатное свойство ItemClass (PropHoldingItemHidden в
  .cctor, ParseBool в Init), а Inventory.setHoldingItemTransform в конце
  делает SetActive(!HoldingItemHidden). Гаснет только модель в руке -
  иконка инвентаря и предмет на земле не трогаются.
- Пустой меш поставить нельзя: ItemClass.CloneModel подставляет заглушку
  leather.fbx, если ассет не загрузился. Поэтому Meshfile оставлен
  затычкой, а DropMeshfile - ванильный мешок, как у минибайка.
- Действия предмета не задеты: ItemActionEat читает HoldType только ради
  AnimationDelay[HoldType].RayCast, который равен 0f и у 31, и у 7;
  ExecuteAction, за которую держится SpatialVaultPatch, его не читает.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FEXvXg1FSAQJHrvYbeAKqq
This commit is contained in:
AlexCube
2026-09-13 17:21:20 +03:00
co-authored by Claude Opus 5
parent ab1b3eadaf
commit a645c53ff3
+80 -11
View File
@@ -214,7 +214,31 @@
<triggered_effect trigger="onProjectileImpact" action="AddBuff" target="positionAOE" range="2" buff="buffNecroDeviatorCharm">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
<!-- Generic stone-on-flesh thud, deliberately left UNCONDITIONAL: it fires on every
impact (ground, wall, zombie) so a miss still sounds like something landed. -->
<triggered_effect trigger="onProjectileImpact" action="PlaySound" sound="stonehitorganic"/>
<!-- The charm's OWN cue - fires only on a hit that actually charms, gated by the exact
same "other is a zombie" requirement the AddBuff above uses, so it can never fire on
a miss. Requirements on a PlaySound effect are the vanilla stun baton pattern
(Data/Config/items.xml:4005 - IsAlive/EntityTagCompare on target="other"), not
invented here; onProjectileImpact populating "other" with the hit entity is proven
by the AddBuff right above, which already works in game.
target="other" is meant to play the clip FROM the zombie, i.e. positional at the
point of impact instead of at the thrower's head (the attribute itself is vanilla -
items.xml:5514 uses target="self"). If it turns out silent in game, drop just the
attribute: the effect then plays on self and the gating still holds.
THE MOD'S OWN SOUND, the first one in the whole mod that is not a borrowed vanilla
id: "necroSpiritStoneHit" is defined in this mod's Config/sounds.xml and its clip
lives in Resources/necrosounds, built from the Unity project (see that file's header
for why a plain wav next to the XML cannot work). If the sound is missing in game,
the failure is SILENT - look for "AudioManager LoadAudio failed to load audio clip"
in the game log, and note that the generic stonehitorganic above will still play, so
"I heard something" is not proof this one fired. -->
<triggered_effect trigger="onProjectileImpact" action="PlaySound" target="other" sound="necroSpiritStoneHit">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
</effect_group>
</item>
</append>
@@ -1401,19 +1425,64 @@
(bundle, computer, forge, explosion, campfire, gunsmithing, book). -->
<property name="DescriptionKey" value="braceletSpatialVaultDesc"/>
<property name="CustomIcon" value="ProstranstvennoeHranilische"/>
<!-- Same "seed"-style grip as braceletThiefLoop originally had (see that item's own
comment for the full history) - foodCropYuccaFruit's own HoldType="31" +
parcelGenericPrefab.prefab. Unlike Thief's Loop, this item never touches
Class="Zoom" (both its actions are Class="Eat"), so it never hit the
"Attachments" transform error that forced Thief's Loop onto a real weapon mesh -
no reason to change this one's mesh too. TintColor changed to green 2026-08-30
per direct request. -->
<!-- МЕШ И ХВАТ, 2026-09-13. Просьба в два захода: сперва "пусть будет камень, а
хват давай сделаем как когда пытаешься ставить какой-нибудь блок", затем
уточнение - "браслет это браслет... в идеале меш камня вообще убрать". Было:
свёрток-«семечко» parcelGenericPrefab.prefab (коробочка, перевязанная бечёвкой -
для браслета нелепо) + HoldType="31", и то и другое унаследовано от Петли вора.
ИТОГ: в руке НЕТ НИЧЕГО, только кулак. Пустой префаб собирать не пришлось - в
движке есть готовое свойство, и вся связка целиком списана с ванильного
vehicleMinibikePlaceable (items.xml:13385), у которого стоят ровно те же две
строки подряд: HoldType="7" + HoldingItemHidden="true".
HoldType="7" - это и есть блочный хват ("кулак вниз, как будто держишь руль"),
не угаданный номер. Декомпилировано Mono.Cecil'ом из Assembly-CSharp 3.2.0
(сам Mono.Cecil.dll лежит в Mods/0_TFP_Harmony, отдельный декомпилятор не нужен):
ItemClassBlock..ctor -> HoldType = new DataItem<int>(7)
AnimationDelayData.AnimationDelay[7] =
new AnimationDelays(0, 0f, 0f, .31f, .31f, true) <- последний флаг TwoHanded
В blocks.xml свойства HoldType нет ни разу (0 вхождений), то есть КАЖДЫЙ блок в
игре держится именно семёркой из этого конструктора. Костет
(meleeWpnKnucklesT0LeatherKnuckles) - это HoldType="70", запасной вариант не
понадобился.
HoldingItemHidden="true" - штатное свойство ItemClass, а не трюк:
ItemClass..cctor заводит PropHoldingItemHidden = "HoldingItemHidden",
ItemClass.Init читает его через StringParsers.ParseBool, а
Inventory.setHoldingItemTransform в самом конце делает
holdingItemTransform.gameObject.SetActive(!HoldingItemHidden). Гасится ТОЛЬКО
модель в руке: иконка в инвентаре (своя рисованная ProstranstvennoeHranilische)
и мешок на земле не трогаются, действия предмета живут в ItemActionEat и от
этого GameObject не зависят.
Пустой меш поставить было НЕЛЬЗЯ, и это проверено, а не предположено:
ItemClass.CloneModel, если имя меша пустое и ассет не загрузился, подставляет
заглушку "@:Other/Items/Crafting/leather.fbx" - в руке оказался бы кусок кожи.
Единственный ванильный предмет вообще без Meshfile - meleeHandMaster (голые
руки), и он выкручивается через Canhold="false", что нам не подходит: браслет
надо держать, чтобы им пользоваться.
Meshfile оставлен камнем как безобидная затычка (в руке он скрыт, а для
MeshPurpose World/Local/Preview что-то иметь надо), DropMeshfile - ванильный
мешок sack_droppedPrefab, ровно тем же приёмом и по той же причине, что у
vehicleMinibikePlaceable: выброшенный предмет должно быть видно на земле, а
своей модели у него нет. HandMeshfile убран за ненадобностью.
Про HoldType и действия: единственное место, где ItemActionEat вообще читает
HoldType, - AnimationDelay[HoldType].RayCast (в PercentDone и IsActionRunning),
и он равен 0f и у старого 31, и у нового 7 (InitStatic заполняет все 100 слотов
нулями, ItemClassBlock переписывает слот 7, оставляя RayCast нулём).
ExecuteAction, за которую держится SpatialVaultPatch.cs, HoldType не читает
вовсе - проверено сканом IL по всей сборке. -->
<property name="Material" value="Morganic"/>
<property name="Meshfile" value="@:Other/Items/Food/parcelGenericPrefab.prefab"/>
<property name="HandMeshfile" value="@:Other/Items/Food/parcelGenericPrefab.prefab"/>
<property name="DropMeshfile" value="@:Other/Items/Food/parcelGenericPrefab.prefab"/>
<property name="Meshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/>
<property name="DropMeshfile" value="@:Other/Items/Misc/sack_droppedPrefab.prefab"/>
<property name="TintColor" value="30, 200, 60"/>
<property name="HoldType" value="31"/>
<property name="HoldType" value="7"/>
<property name="HoldingItemHidden" value="true"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="1"/>
<property name="EconomicValue" value="0"/>