Files
necromants-tome-7d2d-3-2/Config/items.xml
T
Alex CubeandClaude Opus 5 e8f064f5ec Книга некроманта 1.0 — первая публичная версия
Мод для 7 Days to Die 3.2: навык «Некромантия», растущий от счётчика убитых
зомби, тёмное оружие с шестью собственными модами, призывная нежить, пирамида
духов и сюжетный финал через Чёрный портал. Локализация на 13 языках.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MaNro5hAGTzcQ7rJNN2tCX
2026-09-09 21:13:03 +03:00

1347 lines
88 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<config>
<!-- REMOVED: the "books read" counter/skill patch that used to live here (patched
schematicMaster's effect_group) never actually worked. items.xml effect_group does not
inherit through Extends in this game version - the loader parses each item's
effect_group from its own XML node only, never a parent's, regardless of Extends
(confirmed by decompiling ItemClassesFromXml.parseItem). Every one of the ~150 skill
magazine/schematic items would need its own individual append to actually react to being
read - not worth it, so the books-read counter buff and the skill it drove were both
dropped entirely. See progression.xml/entityclasses.xml: the skill now levels from
zombie kills instead, which already worked. -->
<!-- Reusable tin cans: eating canned food leaves you an empty can, which works like the
vanilla mason jar (drinkJarEmpty/drinkJarRiverWater/drinkJarBoiledWater - fill from
water, drink, get the empty can back). Ported from AC-EnergyBow (__NoMods reference
library), fixing one bug in it along the way: it extends the jar items but never
overrides Meshfile, so its "tin can" is visually a mason jar in hand. We use the mesh
from vanilla's own disabled-but-fully-built "drinkCanEmpty" item (items.xml, commented
out under "*** Drinks") instead - a real can model, just never wired up by TFP.
BUG FIXED 2026-09-01 (canned water didn't quench thirst): the initial port only copied
each item's own property/property-class nodes and dropped the effect_group blocks,
on the wrong assumption that Extends would pull those in from drinkJarRiverWater/
drinkJarBoiledWater - it doesn't (see the comment above: effect_group is parsed from
each item's own XML node only, never inherited through Extends). So tinCanRiverWater/
tinCanBoiledWater ran their Eat action (sound, delay, empty-can-back) but never applied
$waterAmountAdd - the can "drank" and did nothing. Restored the effect_group from
AC-EnergyBow verbatim (river water still carries dysentery risk/HP hit without
buffWaterPurifier, same as a jar; boiled water is clean), fixing one more small bug in
the source along the way: its dDysenteryRisk display_value said .16 while the actual
roll it sets is 8 (i.e. .08) - tooltip now matches what the roll does. -->
<append xpath="/items">
<item name="tinCanEmpty">
<property name="Extends" value="drinkJarEmpty"/>
<property name="CustomIcon" value="tinCanEmpty"/>
<property name="DescriptionKey" value="tinCanEmptyDesc"/>
<property name="Meshfile" value="@:Other/Items/Food/can_emptyPrefab.prefab"/>
<property name="HoldType" value="14"/>
<property name="Material" value="Mmetal"/>
<property class="Action1"> <!-- UseAction -->
<property name="Class" value="CollectWater"/>
<property name="Delay" value="3.4"/>
<property name="Change_item_to" value="tinCanRiverWater"/>
<property name="Sound_start" value="bucketfill_water"/>
</property>
</item>
<item name="tinCanRiverWater">
<property name="Extends" value="drinkJarRiverWater"/>
<property name="CustomIcon" value="tinCanRiverWater"/>
<property name="DescriptionKey" value="tinCanRiverWaterDesc"/>
<property name="Meshfile" value="@:Other/Items/Food/can_emptyPrefab.prefab"/>
<property name="HoldType" value="14"/>
<property name="Material" value="Mmetal"/>
<property class="Action0">
<property name="Class" value="Eat"/>
<property name="Delay" value="4"/>
<property name="Sound_start" value="player_drinking"/>
<property name="Create_item" value="tinCanEmpty"/>
<property name="Use_jar_refund" value="false"/>
</property>
<effect_group tiered="false" name="Drink Tier 0">
<requirement name="!HasBuff" buff="buffIsOnFire"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar="$waterAmountAdd" operation="add" value="10"/>
<display_value name="dStaminaRegen" value=".15"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="AddBuff" buff="buffProcessConsumables"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyStats" stat="Health" operation="add" value="-5">
<requirement name="!HasBuff" buff="buffWaterPurifier"/>
</triggered_effect>
<display_value name="foodHealthAmount" value="-5"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar=".DiseaseRoll" operation="set" value="8"/>
<display_value name="dDysenteryRisk" value=".08"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar=".DiseaseRoll" operation="add" value="@$MetabolismResist"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="AddBuff" buff="buffDysenteryCatchDrink">
<requirement name="RandomRoll" seed_type="Random" min_max="1,100" operation="LTE" value="@.DiseaseRoll"/>
</triggered_effect>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="PlaySound" sound="player#vomit">
<requirement name="!HasBuff" buff="buffWaterPurifier"/>
</triggered_effect>
</effect_group>
<effect_group tiered="false">
<requirement name="HasBuff" buff="buffIsOnFire"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="AddBuff" buff="buffExtinguishFire"/>
</effect_group>
</item>
<item name="tinCanBoiledWater">
<property name="Extends" value="drinkJarBoiledWater"/>
<property name="CustomIcon" value="tinCanBoiledWater"/>
<property name="DescriptionKey" value="tinCanBoiledWaterDesc"/>
<property name="Meshfile" value="@:Other/Items/Food/can_emptyPrefab.prefab"/>
<property name="HoldType" value="14"/>
<property name="Material" value="Mmetal"/>
<property class="Action0">
<property name="Class" value="Eat"/>
<property name="Delay" value="4"/>
<property name="Sound_start" value="player_drinking"/>
<property name="Create_item" value="tinCanEmpty"/>
<property name="Use_jar_refund" value="false"/>
</property>
<effect_group tiered="false" name="Drink Tier 0/1">
<requirement name="!HasBuff" buff="buffIsOnFire"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="ModifyCVar" cvar="$waterAmountAdd" operation="add" value="16"/>
<display_value name="dStaminaRegen" value=".15"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="AddBuff" buff="buffProcessConsumables"/>
</effect_group>
<effect_group tiered="false">
<requirement name="HasBuff" buff="buffIsOnFire"/>
<triggered_effect trigger="onSelfPrimaryActionEnd" action="AddBuff" buff="buffExtinguishFire"/>
</effect_group>
</item>
</append>
<!-- Only foodCanBeef needs patching, even though Chicken/Lamb/Catfood should also leave a
can behind: those three Extends="foodCanBeef" without their own Action0 element, so
there's nothing for a foodCanChicken/foodCanLamb/foodCanCatfood xpath to match - but for
the same reason, they'll all inherit foodCanBeef's patched Action0 (Create_item included)
at load time anyway, same as how our zombie kill counter above only had to patch
zombieTemplateMale once. -->
<append xpath="/items/item[@name='foodCanBeef']/property[@class='Action0']">
<property name="Create_item" value="tinCanEmpty"/>
</append>
<!-- "Камень духов" (Spirit Stone): the first necromancer item, replacing the earlier
"Девиатор" (Deviator) placeholder book with a real thrown object - a vanilla small rock,
reused via resourceRockSmall's own mesh/icon, tinted green (TintColor for the 3D mesh,
CustomIconTint for the UI icon - both multiply onto the material's base color, which is
why this works even though we never touch the rock's actual textures).
Back on Molotov Cocktail's own architecture: Class="ItemClassTimeBomb" +
Action0 Class="ThrowAway". An earlier version used Class="ThrownWeapon" (spear-style)
specifically to get a precise "who exactly got hit" target for the buff - that DOES work,
but it drags in ThrownWeaponMoveScript.checkCollision(), which unconditionally registers a
map/compass marker on impact (NavObjectManager.RegisterNavObject) and crashes hard if the
item has no NavObject configured. Molotov never touches any of that, because it doesn't
need a specific target - it applies its buff to everything within a small radius of where
it landed (target="positionAOE") instead of to "whatever I directly hit". We do the same
here with a tight 2m radius: in practice that's just the one zombie you threw it at, and
if it catches a second zombie standing right next to it, that's a fine bonus for a
necromancer item, not a bug. This trades "always exactly one" for "no exotic crash".
The buff it delivers (buffNecroDeviatorCharm) is what actually flips the hit zombie -
see buffs.xml + HarmonySrc/CharmPatch.cs. The buff name itself stays "Deviator" even
though the item that delivers it is now the Spirit Stone - CharmPatch.cs/DamagePatch.cs/
ParticlePatch.cs all key off the buff name, not the item name, so nothing there needs to
change for this rename. -->
<append xpath="/items">
<item name="thrownStoneSpirit">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="explosion"/>
<!-- ICON UPDATED 2026-08-29: real generated art (UIAtlases/ItemIconAtlas/
SpiritStone.png) replaces the reused-resourceRockSmall-icon+tint trick - no
CustomIconTint on top of it per the user's own instruction, real art shouldn't
be recolored. -->
<property name="CustomIcon" value="SpiritStone"/>
<property name="DescriptionKey" value="thrownStoneSpiritDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<property name="Class" value="ItemClassTimeBomb"/>
<!-- HoldType 40 is what resourceRockSmall itself uses - matches how
rock_smallPrefab.prefab was actually authored/rigged to be held. -->
<property name="HoldType" value="40"/>
<property name="Meshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/> <!-- in-flight -->
<property name="HandMeshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/> <!-- held -->
<property name="DropMeshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/> <!-- world-dropped -->
<property name="Material" value="MresourceRockSmall"/>
<property name="TintColor" value="30, 200, 60"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="20"/>
<property name="FuseTime" value="4000"/>
<property name="ExplodeOnHit" value="true"/>
<property name="StickPercent" value="0"/>
<!-- Zero radius/damage here on purpose: this block's own RadiusEntities/EntityDamage
is for native splash DAMAGE (we don't want any), separate from the buff's own
"range" below (confirmed by how Molotov itself splits these: its Explosion block
does real radius damage, while its buff uses its own independent range="2.7").
ParticleIndex=7 is the same index Molotov's own Action0 puff already uses safely
in this same ItemClassTimeBomb context (unlike ThrownWeapon's separate, apparently
broken, particle-index space that crashed above). -->
<property class="Explosion">
<property name="ParticleIndex" value="7"/>
<property name="RadiusBlocks" value="0"/>
<property name="RadiusEntities" value="0"/>
</property>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="UsableUnderwater" value="false"/>
<property name="SoundPickup" value="stone_grab"/>
<property name="SoundPlace" value="stone_place"/>
<!-- Deliberately just Action0, no Action1/FusePrimeOnActivate. Molotov requires that
second "prime" step (RMB to light the rag) before throwing, and mustPrime gates
ItemClassTimeBomb's ExplodeOnHit check in OnDroppedUpdate: `(!mustPrime ||
itemValue.Meta > 0)`. If FusePrimeOnActivate is set but the item is thrown without
ever activating (Meta stays 0), that condition is permanently false, and every
single collision this update produces early-returns without ever firing
onProjectileImpact - which is exactly why every earlier test bounced with no
effect at all, buff or otherwise, regardless of anything else about the item.
Leaving FusePrimeOnActivate unset keeps mustPrime at its C# default (false), so
ExplodeOnHit fires unconditionally on first contact - one throw, no priming ritual. -->
<property class="Action0">
<property name="Class" value="ThrowAway"/>
<property name="Delay" value="1.2"/>
<property name="Throw_strength_default" value="20"/>
<property name="Throw_strength_max" value="50"/>
<property name="Max_strain_time" value="1.25"/>
<property name="Sound_start" value="swoosh"/>
</property>
<effect_group tiered="false">
<triggered_effect trigger="onProjectileImpact" action="AddBuff" target="positionAOE" range="2" buff="buffNecroDeviatorCharm">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
<triggered_effect trigger="onProjectileImpact" action="PlaySound" sound="stonehitorganic"/>
</effect_group>
</item>
</append>
<!-- NAMING CONVENTION 2026-08-29 (user request): every book-type item that gets consumed on
use is now called a "свиток"/"scroll" in its DISPLAY text (Localization.csv), not a
"фолиант"/"tome"/"книга"/"book" any more - applies to this item (renamed "Гримуар
девиации" -> "Свиток девиации"), bookSummonInsectSwarm (description text only, its own
name "Жуки Властелина" didn't say "book" to begin with), and bookBanshee (renamed "Книга
банши" -> "Свиток банши"). bookSummonZombieDog/Bear/Wolf/Griffin stay "книги" (books) -
they're NOT consumed (ConsumesBook=false, see SummonPatch.cs), matching the same rule.
Internal item ids (thrownBookGrimoireDeviation, bookSummonInsectSwarm, bookBanshee) were
deliberately LEFT UNCHANGED - only the user-visible name/description text was renamed,
not the id every recipe/Harmony patch/save file references. Renaming ids too would orphan
any copies already in the user's inventory/world from before this rename. Say if the ids
should be renamed as well despite that risk.
Model/mesh is still the vanilla bookPrefab placeholder for all of these - per the user's
own note ("не представляю пока какую использовать модельку"), swap Meshfile/HandMeshfile/
DropMeshfile (and probably HoldType) once a real scroll model exists; nothing to do here
until then.
"Гримуар девиации" (now "Свиток девиации"): BACKLOG.md item 2. A stronger, later-game
sibling of the Spirit Stone above, not a replacement for it - same underlying charm
(buffNecroDeviatorCharm, same Harmony patches), but hits every zombie caught in a wide
blast instead of just whatever's closest. That's the ENTIRE difference from the Spirit
Stone mechanically: target="positionAOE" already applies to every matching entity within
"range" of the impact point, not just the nearest one - the Spirit Stone's range="2" just
happens to be small enough that it's usually only ever one zombie. Bumping range to "5"
here is the whole AOE upgrade; no new Harmony code needed.
It's a book again (unlike the Spirit Stone, which moved off being a book - see BACKLOG.md),
so it reuses the original Deviator's book meshes/hack: bookPrefab.prefab held/dropped, but
vomitBulbPrefab.prefab in flight, because a flat book mesh tumbling through the air via
ItemClassTimeBomb/ThrowAway didn't read well - the rounded vomit bulb was already proven to
fly fine there. Tinted green the same way as the Spirit Stone (TintColor for the 3D mesh,
CustomIconTint for the UI icon), per user request - same color on both items ties them
together visually as "the same kind of magic".
Gated at Necromancy level 50 via the recipe's necroNecromancyLvl50 tag (see progression.xml
for why this needed its own one-off tag instead of one of the 5 existing groups). -->
<append xpath="/items">
<item name="thrownBookGrimoireDeviation">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="explosion"/>
<!-- ICON UPDATED 2026-08-29: real generated art (ScrollOfDeviation.png), no tint. -->
<property name="CustomIcon" value="ScrollOfDeviation"/>
<property name="DescriptionKey" value="thrownBookGrimoireDeviationDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<property name="Class" value="ItemClassTimeBomb"/>
<!-- 3D MODEL UPDATED 2026-08-29 per direct user request, for the whole "свиток"
family (this item + bookBanshee + bookSummonInsectSwarm): user pointed at the
vanilla first aid kit (medicalFirstAidKit, itself Extends="medicalBandage") -
that item's real Meshfile is @:Other/Items/Health/bandage.FBX (confirmed by
reading medicalBandage directly), a rolled bandage - tinted parchment
yellow-brown per the user's own instruction ("покрасить в жёлтый или
жёлто-коричневый"), it reads as a rolled scroll. HoldType switched to 64
(medicalBandage's own HoldType) to match how that mesh was actually rigged to be
held - keeping HoldType 21 (book pose) with a bandage-shaped mesh would likely
look wrong.
In-flight Meshfile (vomitBulbPrefab) deliberately LEFT UNCHANGED - this item is
thrown (Class="ItemClassTimeBomb"), and the comment on the Spirit Stone above
already documents that a flat/elongated mesh tumbling through the air "didn't
read well" here (that's why vomitBulbPrefab was used in the first place, a
rounded shape proven to fly correctly) - only HandMeshfile/DropMeshfile (held/
on the ground) switch to the bandage roll, matching how the player actually sees
this item most of the time anyway. -->
<property name="HoldType" value="64"/>
<property name="Meshfile" value="@:Other/Items/Weapons/Ranged/Vomit/vomitBulbPrefab.prefab"/> <!-- in-flight -->
<property name="HandMeshfile" value="@:Other/Items/Health/bandage.FBX"/> <!-- held -->
<property name="DropMeshfile" value="@:Other/Items/Health/bandage.FBX"/> <!-- world-dropped -->
<property name="Material" value="Mpaper"/>
<property name="TintColor" value="210, 170, 90"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="20"/>
<property name="FuseTime" value="4000"/>
<property name="ExplodeOnHit" value="true"/>
<property name="StickPercent" value="0"/>
<!-- Zero radius/damage here on purpose, same reasoning as the Spirit Stone above: this
block's own RadiusEntities/EntityDamage is for native splash DAMAGE (we don't want
any), separate from the buff's own "range" below. -->
<property class="Explosion">
<property name="ParticleIndex" value="7"/>
<property name="RadiusBlocks" value="0"/>
<property name="RadiusEntities" value="0"/>
</property>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="UsableUnderwater" value="false"/>
<property name="SoundPickup" value="schematics_grab"/>
<property name="SoundPlace" value="schematics_place"/>
<!-- Deliberately just Action0, no Action1/FusePrimeOnActivate - see the Spirit Stone's
item block above for the full explanation of why (mustPrime/ExplodeOnHit trap). -->
<property class="Action0">
<property name="Class" value="ThrowAway"/>
<property name="Delay" value="1.2"/>
<property name="Throw_strength_default" value="20"/>
<property name="Throw_strength_max" value="50"/>
<property name="Max_strain_time" value="1.25"/>
<property name="Sound_start" value="hulkvomitwarning"/>
</property>
<effect_group tiered="false">
<triggered_effect trigger="onProjectileImpact" action="AddBuff" target="positionAOE" range="5" buff="buffNecroDeviatorCharm">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
<triggered_effect trigger="onProjectileImpact" action="PlaySound" sound="vomitimpact"/>
</effect_group>
</item>
</append>
<!-- Two portal stones, BACKLOG.md item 6 (dictated 2026-08-28, implemented 2026-08-29 "без
вопросов" per user request). Same rock mesh/icon as the Spirit Stone above (reused via
Meshfile/CustomIcon, tinted per-stone) but used IN PLACE, not thrown - no
Class="ItemClassTimeBomb"/Action0 ThrowAway here, just a plain item with a click-to-
start-channel Action0.
REWRITTEN 2026-08-29: the first version's Delay="10" on Class="Eat" turned out NOT to be
a 10-second hold at all (user tested: "срабатывает мгновенно") - re-decompiling
ItemActionEat showed Delay there is really just a re-click cooldown, and the actual
multi-second "eating" duration for a UseAnimation item comes from a fixed-per-HoldType
table that isn't XML-exposed anywhere (checked - no "RayCast"/"AnimationDelay" anywhere in
Data/Config). Class="Eat" is now used ONLY as a reliable "detect a click" trigger -
Delay="1" here is just a debounce against accidental double-fires, not the channel length
at all any more. HarmonySrc/PortalStonePatch.cs Prefixes ItemActionEat.ExecuteAction
itself and, for these two items, skips the vanilla method entirely and opens the game's
own real countdown-timer UI directly (XUiC_Timer.OpenTimer - the same generic primitive
Block.TakeItemWithTimer itself is built on, decompiled to confirm) for the full 10
seconds, with a real visible progress bar and TimerEventData.CloseOnHit=true (cancels
automatically on taking damage - a real engine feature, not hand-rolled). Consume="false"
is now irrelevant (the whole ItemActionEat consumption path is skipped either way) but
left in place as documentation of intent - the stone is a reusable travel tool, unlike the
one-shot thrown Spirit Stone.
Actual teleportation is NOT an XML-reachable action anywhere in this game version
(confirmed - no ItemAction class does it, only console commands/GameEvent sequences do,
both C#-only) - needs Harmony. The same NetPackageTeleportPlayer the game's own
"teleportplayer" console command uses (decompiled from
ConsoleCmdTeleportsAbs.ExecuteTeleport) fires once the timer above completes - not some
invented mechanism.
Blue stone: teleports to the player's own bedroll/respawn point
(EntityPlayer.PersistentPlayerData.BedrollPos, decompiled from PersistentPlayerData - the
same field the game's own respawn-at-bedroll flow reads). No crafting gate specified by
the user beyond "простой ранний рецепт по аналогии с Камнем духов" (guessed, per the
backlog's own note) - available from the start, no craft_area/level tag.
Black stone: gated at necroNecromancyMaster (level 5000, the existing "Мастер" tier -
no new one-off level tag needed, per the user's own explicit instruction). Deliberately
a placeholder per direct user instruction - crafts and can be "used" (same 10s channel,
for UX consistency with the blue stone) but PortalStonePatch.cs's blue/black switch has
no real destination wired up for it yet, just a tooltip explaining that. Not a dead
button by accident - a real placeholder, matching what was asked for.
Sounds/icon tints are guesses (no dedicated "portal"/"teleport" sound exists anywhere in
vanilla sounds.xml - checked directly, not assumed) - "swoosh" for the channel start,
"spawnInStinger" (a real vanilla sound, the cue used when something materializes into the
world) for the actual teleport moment. Both now played directly from
PortalStonePatch.cs's C# code (not Sound_start here) since ItemActionEat's own
ExecuteAction - the method that would normally play Sound_start - is skipped entirely by
the Harmony Prefix, so a Sound_start property here would just never fire. -->
<append xpath="/items">
<item name="thrownStonePortalBlue">
<property name="Tags" value="T0,weapon,attPerception"/>
<!-- ItemTypeIcon="explosion" REMOVED 2026-09-07 (user: "убери explosion у портальных
камней тоже"), same call as the Spatial Vault's bogus "melee" badge just above -
this is the 12x12 corner badge over the item's icon in the recipe list, not the
workstation indicator. The sprite was fine; the meaning wasn't. This stone
teleports, it does not explode. Removed rather than replaced: with no
ItemTypeIcon property the {hasitemtypeicon} gate hides the widget entirely.
Kept on thrownStoneSpirit and thrownBookGrimoireDeviation, which really do go off
as an area burst on impact - only the two portal stones were asked for. -->
<!-- ICON UPDATED 2026-08-29: real generated art (BluePortalStone.png), no tint.
TintColor below is unrelated - it colors the 3D rock mesh (still the plain
rock_smallPrefab), not the 2D icon, so it's left alone. -->
<property name="CustomIcon" value="BluePortalStone"/>
<property name="DescriptionKey" value="thrownStonePortalBlueDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<property name="HoldType" value="40"/>
<property name="Meshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/>
<property name="HandMeshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/>
<property name="DropMeshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/>
<property name="Material" value="MresourceRockSmall"/>
<property name="TintColor" value="30, 100, 255"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="stone_grab"/>
<property name="SoundPlace" value="stone_place"/>
<!-- Delay="1" is just a re-click debounce now, not a channel length - see the big
comment above for why (the real 10s channel is PortalStonePatch.cs's own
XUiC_Timer, not this ItemAction's Delay at all). -->
<property class="Action0">
<property name="Class" value="Eat"/>
<property name="Delay" value="1"/>
<property name="Consume" value="false"/>
</property>
</item>
</append>
<append xpath="/items">
<item name="thrownStonePortalBlack">
<property name="Tags" value="T0,weapon,attPerception"/>
<!-- ItemTypeIcon="explosion" REMOVED 2026-09-07, same reason as the Blue stone above -
see that comment for the full explanation of which widget this actually is. Even
less accurate here than there: this one is a 10-second channelled ritual ending in
a confirmation dialog, a fullscreen video and an exit to the main menu. Nothing
about it is an explosion. -->
<!-- ICON UPDATED 2026-08-29: real generated art (BlackPortalStone.png), no tint. -->
<property name="CustomIcon" value="BlackPortalStone"/>
<property name="DescriptionKey" value="thrownStonePortalBlackDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<property name="HoldType" value="40"/>
<property name="Meshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/>
<property name="HandMeshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/>
<property name="DropMeshfile" value="@:Other/Items/Crafting/rock_smallPrefab.prefab"/>
<property name="Material" value="MresourceRockSmall"/>
<property name="TintColor" value="0, 0, 0"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="stone_grab"/>
<property name="SoundPlace" value="stone_place"/>
<property class="Action0">
<property name="Class" value="Eat"/>
<property name="Delay" value="1"/>
<property name="Consume" value="false"/>
</property>
</item>
</append>
<!-- "Призыв зомбособаки" (Summon Zombie Dog): BACKLOG.md item 3. A book, per user request -
but unlike the Deviator/Grimoire above, it's not thrown - Class="SpawnEntity" (same
ItemAction vanilla's own meleeHandZombieRancher uses to have a zombie spit out an insect
swarm) plays a short cast animation in place and spawns the pet at the player's feet, no
projectile/physics involved. CustomIcon is the user's own hand-drawn art (2026-08-28,
redrawn from the exch/schematicMaster.png reference copy), dropped into
UIAtlases/ItemIconAtlas/schematicDogSummon.png the same way tinCanEmpty.png etc. already
work in this mod - no CustomIconTint on top of it, that would recolor real art.
BUG FIXED 2026-08-28 (book did nothing but play the warn sound in-game): AnimWait was
1.87s, copied from meleeHandZombieRancher's own AnimType/AnimWait without checking what
it actually gates. Confirmed by decompiling ItemActionSpawnEntity.ExecuteAction/
OnHoldingUpdate: the use button has to stay HELD DOWN for the full AnimWait duration -
releasing early (a normal quick click, which is how every other item in this mod works)
resets state back to None and Spawn() never runs at all, silently, no error anywhere -
only the SoundWarn from the initial press ever plays. AI-held items never hit this
because AI code holds the action for its full duration on purpose; a player quick-clicking
does not. Dropped to 0.3s so an ordinary click is long enough to clear it reliably while
still giving a short, visible "cast" beat - raise it back if a deliberate hold-to-cast feel
is wanted later, but document the tradeoff if so.
Ownership/limit/consuming the book on cast is NOT handled by SpawnEntity itself (it's
built for zombie mobs, which have no concept of "owner" or "ammo") - see
HarmonySrc/SummonPatch.cs for the Harmony patch that adds all three, modeled on how
vanilla's own drone (ItemActionSpawnTurret + EntityDrone.isValidForPlayer) behaves: only
one active pet per player PER SPECIES (dog and insect swarm below are tracked
independently - see SummonPatch.cs's LimitedPets table), summoning a second one of the
same species while the first is alive is BLOCKED (not a replace) with a tooltip, same as
"xuiMaxDeployedDronesReached" for the drone.
Gated at Necromancy level 200 (necroNecromancyLvl200, see progression.xml) and crafted at
a workbench specifically (craft_area="workbench" in recipes.xml), per user request - not
available from the player's own personal crafting menu like the Spirit Stone/Grimoire.
BUG FIXED 2026-08-28 (dogs launching the player into the air on spawn): EntityOffset was
"0, -.1, 1" - copied from meleeHandZombieRancher's own tiny insect-swarm offset without
scaling it up for a full-sized quadruped. That's barely below head height and only 1m
forward, easily still overlapping the caster's own collider - Unity's physics shoving two
overlapping colliders apart is exactly what launched the player. This was made much worse
by the SummonPatch.cs "<=0 vs -1" bug (see that file) piling up a dozen undespawned dogs,
all later teleported onto the exact same point by PetFollowPatch's leash every second -
but the underlying spawn-offset was too tight even for a single dog. Widened to
"0, -1, 2.5" (2.5m forward, roughly ground height instead of head height; gravity settles
the rest). -->
<append xpath="/items">
<item name="bookSummonZombieDog">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ICON REPLACED 2026-08-29: swapped the user's earlier hand-drawn
schematicDogSummon.png for the new AI-generated SummonZombieDog.png, to match
the rest of the item set's unified style (same prompt/generator as the other 12
icons). The old file is left in UIAtlases/ItemIconAtlas/ unused, not deleted, in
case this should be reverted. -->
<property name="CustomIcon" value="SummonZombieDog"/>
<property name="DescriptionKey" value="bookSummonZombieDogDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<!-- HoldType 21 is what schematicMaster (the vanilla book item) itself uses - matches
how bookPrefab.prefab was actually authored/rigged to be held. No flight mesh
needed here (unlike the thrown books above) since this item is never thrown. -->
<property name="HoldType" value="21"/>
<property name="Meshfile" value="@:Other/Items/Misc/bookPrefab.prefab"/>
<property name="Material" value="Mpaper"/>
<!-- Yellow-green (per user request), distinct from the pure green of the Spirit
Stone/Grimoire above - keeps the "summon" books visually distinguishable from the
"throw" books at a glance even before real icon art exists. -->
<property name="TintColor" value="160, 220, 40"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="schematics_grab"/>
<property name="SoundPlace" value="schematics_place"/>
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<property name="SoundWarn" value="zombiedogalert"/>
<property name="SoundAttack" value="zombiedogattack"/>
<property name="Entity" value="necroZombieDog"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
<!-- Recall (user's own suggestion 2026-08-28): Action1 - the "power attack"/secondary
click - uses the same SpawnEntity Class/Entity as Action0, but SummonPatch.cs's
Prefix treats index 1 as recall-only and never lets vanilla actually spawn
anything from it - see that file. AnimWait kept short (no real "cast" needed to
dismiss a pet); "swoosh" reused from the Spirit Stone's throw as a quick
"gone" cue instead of the summon warning sound - SoundAttack here never actually
plays either way, since the Prefix always blocks Action1's Spawn() before it
reaches that line. -->
<property class="Action1">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.1"/>
<property name="SoundWarn" value="swoosh"/>
<property name="Entity" value="necroZombieDog"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
</item>
</append>
<!-- Zombie Dog's bite: user request 2026-08-28 ("добавь собаке возможность накладывать баф
замедления"). Same shape as necroMeleeHandInsectSwarm below - Extends the vanilla hand
item so Action0's Delay/Range/Class etc. inherit unchanged (property class="Action0" DOES
merge per-property through Extends), only DamageEntity is unlisted here so even that
inherits (kept at vanilla's own 8 - no request to change damage, only to add the slow). -->
<append xpath="/items">
<item name="necroMeleeHandZombieDog">
<property name="Extends" value="meleeHandAnimalZombieDog"/>
<property name="CreativeMode" value="None"/>
<!-- effect_group does NOT inherit through Extends (see the note at the top of this
file) - meleeHandAnimalZombieDog's own triggers have to be restated from scratch
if wanted; dropped its player-injury-system debuff soup (buffFatiguedTrigger etc.,
the exact same set the Insect Swarm dropped in necroMeleeHandInsectSwarm below)
since it doesn't mean much against a zombie, kept only the slow that was asked
for. Gated to zombies only, same EntityTagCompare pattern as everywhere else in
this mod that adds a buff on hit. -->
<effect_group name="necroMeleeHandZombieDog" tiered="false">
<passive_effect name="ModSlots" operation="base_set" value="0"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffInjurySlow">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
</effect_group>
</item>
</append>
<!-- "Жуки Властелина" (Insect Swarm summon): user request 2026-08-28, added alongside the
Zombie Dog bugfix above since it reuses the exact same summon plumbing (SpawnEntity,
HarmonySrc/SummonPatch.cs ownership/limit).
BEHAVIOR CHANGE 2026-08-28 (user request, replacing the original damage+slow design):
no longer deals meaningful damage or applies buffInjurySlow - a sting now applies
buffNecroDeviatorCharm instead, the SAME charm the Spirit Stone/Grimoire use (see
Config/buffs.xml + HarmonySrc/CharmPatch.cs), converting the stung zombie to fight for the
player exactly like a thrown-book hit would. So the swarm is really a mobile, persistent
version of the Deviator/Grimoire's charm effect - see necroInsectSwarm in entityclasses.xml
and HarmonySrc/SwarmTargetPatch.cs for why targeting zombies at all took a Harmony patch,
not just XML (EntitySwarm's real base class hardcodes player-hunting in C#), and for how it
skips already-charmed zombies so it moves on to the next one instead of camping the first.
Recipe has no craft_area (personal crafting), unlike the Dog's workbench requirement -
the user only asked for the Dog to move to the workbench; this is a guess at matching the
Spirit Stone/Grimoire's default. Say if it should be workbench-gated too. Gated at the
same necroNecromancyLvl200 threshold as the Dog for now, also a guess - it's a comparably
strong summon, but nothing pins it to that exact number. -->
<append xpath="/items">
<item name="necroMeleeHandInsectSwarm">
<property name="Extends" value="meleeHandAnimalInsectSwarm"/>
<property name="CreativeMode" value="None"/>
<!-- DamageEntity reverted to vanilla's own 3 (was bumped to 15 for the old damage-
dealing design, no longer the point - this is a bite that converts, not a weapon).
Delay/Range/Sphere/Class etc. inherit from meleeHandAnimalInsectSwarm's own
Action0 fine (property class="Action0" DOES merge per-property through Extends,
unlike effect_group - see below). -->
<property class="Action0">
<property name="DamageEntity" value="3"/>
</property>
<!-- effect_group does NOT inherit through Extends (see the note at the top of this
file) - meleeHandAnimalInsectSwarm's own triggers have to be restated here from
scratch, not just added to. EntityTagCompare gate matches how the Spirit Stone/
Grimoire's own onProjectileImpact AddBuff is gated in items.xml above - only
charms actual zombies, never anything else the swarm might somehow end up hitting
(e.g. if provoked into retaliating against the player). -->
<effect_group name="necroMeleeHandInsectSwarm" tiered="false">
<passive_effect name="ModSlots" operation="base_set" value="0"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroDeviatorCharm">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
</effect_group>
</item>
</append>
<!-- User request 2026-08-28: "пусть вызов насекомых будет одноразовым" - unlike the Dog, NO
Action1/recall here on purpose. SummonPatch.cs's recall branch only ever runs for
indexInEntityOfAction==1, and with no Action1 block declared at all there's no way to
reach that index for this item - the swarm is cast-and-forget, plumbing-wise identical to
just never wiring recall up for it. Still capped at one active swarm at a time (same
LimitedPets/ownedEntities check as the Dog) - "one-time" was about not being able to call
it back, not about being unlimited.
CustomIcon is the user's own hand-drawn art (2026-08-28, exch/schematicInsectoSummon.png
-> UIAtlases/ItemIconAtlas/), same as bookSummonZombieDog's schematicDogSummon.png above -
no CustomIconTint on top of it either, for the same reason. -->
<append xpath="/items">
<item name="bookSummonInsectSwarm">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ICON REPLACED 2026-08-29: same reasoning as bookSummonZombieDog above - swapped
the earlier hand-drawn schematicInsectoSummon.png for BeetlesOfTheLord.png. -->
<property name="CustomIcon" value="BeetlesOfTheLord"/>
<property name="DescriptionKey" value="bookSummonInsectSwarmDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<!-- 3D MODEL UPDATED 2026-08-29: part of the "свиток" family model swap - see
thrownBookGrimoireDeviation's own comment above for the full reasoning
(medicalBandage's real Meshfile, bandage.FBX, tinted parchment yellow-brown,
HoldType switched to 64 to match). No separate in-flight mesh here (this item
isn't thrown, Class="SpawnEntity"), so one Meshfile covers both held and
dropped. -->
<property name="HoldType" value="64"/>
<property name="Meshfile" value="@:Other/Items/Health/bandage.FBX"/>
<property name="Material" value="Mpaper"/>
<property name="TintColor" value="210, 170, 90"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="schematics_grab"/>
<property name="SoundPlace" value="schematics_place"/>
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<property name="SoundWarn" value="swarmalert"/>
<property name="SoundAttack" value="swarmattack"/>
<property name="Entity" value="necroInsectSwarm"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
</item>
</append>
<!-- Three more summon books, BACKLOG.md item 4a. REPLACED 2026-08-29 - see entityclasses.xml
for why (the previous three, Stripper/Cop/Soldier, came back hostile in testing; new
concept is Зомбогриф/Зомбомедведь/Зомбоволк, extending necroZombieGriffin/Bear/Wolf).
Same plumbing as bookSummonZombieDog above in every respect: Class="SpawnEntity",
Action0=summon/Action1=recall via HarmonySrc/SummonPatch.cs's LimitedPets table,
ConsumesBook=false (book never leaves the inventory, matches "по аналогии с
Зомбособакой"). No hand-drawn icon exists yet for any of these three (CustomIcon
intentionally omitted, same as the Dog/Swarm before the user drew their own art). Gated
at necroNecromancyLvl200 + craft_area="workbench" in recipes.xml, matching the Dog's own
tier. -->
<append xpath="/items">
<item name="bookSummonZombieGriffin">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ICON ADDED 2026-08-29: real generated art (SummonZombieGriffin.png). -->
<property name="CustomIcon" value="SummonZombieGriffin"/>
<property name="DescriptionKey" value="bookSummonZombieGriffinDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<property name="HoldType" value="21"/>
<property name="Meshfile" value="@:Other/Items/Misc/bookPrefab.prefab"/>
<property name="Material" value="Mpaper"/>
<property name="TintColor" value="160, 220, 40"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="schematics_grab"/>
<property name="SoundPlace" value="schematics_place"/>
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<property name="SoundWarn" value="mlionalert"/>
<property name="SoundAttack" value="mlionattack"/>
<property name="Entity" value="necroZombieGriffin"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
<property class="Action1">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.1"/>
<property name="SoundWarn" value="swoosh"/>
<property name="Entity" value="necroZombieGriffin"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
</item>
</append>
<append xpath="/items">
<item name="bookSummonZombieBear">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ICON ADDED 2026-08-29: real generated art (SummonZombieBear.png). -->
<property name="CustomIcon" value="SummonZombieBear"/>
<property name="DescriptionKey" value="bookSummonZombieBearDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<property name="HoldType" value="21"/>
<property name="Meshfile" value="@:Other/Items/Misc/bookPrefab.prefab"/>
<property name="Material" value="Mpaper"/>
<property name="TintColor" value="160, 220, 40"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="schematics_grab"/>
<property name="SoundPlace" value="schematics_place"/>
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<property name="SoundWarn" value="bearalert"/>
<property name="SoundAttack" value="bearattack"/>
<property name="Entity" value="necroZombieBear"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
<property class="Action1">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.1"/>
<property name="SoundWarn" value="swoosh"/>
<property name="Entity" value="necroZombieBear"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
</item>
</append>
<append xpath="/items">
<item name="bookSummonZombieWolf">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ICON ADDED 2026-08-29: real generated art (SummonZombieWolf.png). -->
<property name="CustomIcon" value="SummonZombieWolf"/>
<property name="DescriptionKey" value="bookSummonZombieWolfDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<property name="HoldType" value="21"/>
<property name="Meshfile" value="@:Other/Items/Misc/bookPrefab.prefab"/>
<property name="Material" value="Mpaper"/>
<property name="TintColor" value="160, 220, 40"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="schematics_grab"/>
<property name="SoundPlace" value="schematics_place"/>
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<property name="SoundWarn" value="wolfdirealert"/>
<property name="SoundAttack" value="wolfdireattack"/>
<property name="Entity" value="necroZombieWolf"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
<property class="Action1">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.1"/>
<property name="SoundWarn" value="swoosh"/>
<property name="Entity" value="necroZombieWolf"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
</item>
</append>
<!-- "Книга банши" (Banshee's Book): BACKLOG.md item 7 (dictated 2026-08-28, implemented
2026-08-29 "без вопросов" per user request). Consumed on use, plays a screamer's own
alert sound, spawns a small HOSTILE horde near the caster - see
HarmonySrc/BansheePatch.cs for the full mechanism and why it's a deliberate
simplification of the real AIScoutHordeSpawner system, not a faithful copy of it.
Class="Eat"/Delay="0.3" - same short "ordinary click is long enough" pattern the summon
books above settled on (see bookSummonZombieDog's own comment for why 0.3s, not a longer
hold), reusing the same ItemActionEat.consume() completion hook the portal stones above
already use, just a separate Harmony patch class. Consume defaults to true (not
overridden here, unlike the portal stones) - the book really is spent per the user's own
description ("при применении книга исчезает"), not a reusable tool.
No name/icon/craft/tier given by the user beyond the mechanic - guessed: reuses
schematicMaster's icon (same reused-vanilla-icon trick as the Grimoire), tinted purple to
read as "dangerous/hostile" rather than the green friendly-summon family or the blue/black
portal stones. Gated at necroNecromancyLvl200 (same tier as the Dog/Swarm/three zombie
pets) and workbench-crafted, since it's also an "advanced ritual" item, not an early one -
say if this should be different. -->
<append xpath="/items">
<item name="bookBanshee">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ICON UPDATED 2026-08-29: real generated art (BansheesScroll.png), no tint. -->
<property name="CustomIcon" value="BansheesScroll"/>
<property name="DescriptionKey" value="bookBansheeDesc"/>
<property name="DisplayType" value="ammoGrenadeFire"/>
<!-- 3D MODEL UPDATED 2026-08-29: part of the "свиток" family model swap - see
thrownBookGrimoireDeviation's own comment above for the full reasoning. Tinted
the same parchment yellow-brown as the other two scrolls for a consistent
"свиток" family look (the old purple TintColor was about signaling "dangerous",
not really about material color - dropped in favor of matching the set; say if
Banshee's Scroll should keep a darker/distinct tint instead). -->
<property name="HoldType" value="64"/>
<property name="Meshfile" value="@:Other/Items/Health/bandage.FBX"/>
<property name="Material" value="Mpaper"/>
<property name="TintColor" value="210, 170, 90"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="10"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property name="SoundPickup" value="schematics_grab"/>
<property name="SoundPlace" value="schematics_place"/>
<property class="Action0">
<property name="Class" value="Eat"/>
<property name="Delay" value="0.3"/>
<property name="Sound_start" value="zombiefemalescoutalert"/>
</property>
</item>
</append>
<!-- "Нож некроманта" (Necromancer's Knife): BACKLOG.md item 5, user request 2026-08-28.
Extends the vanilla bone knife (meleeWpnBladeT0BoneKnife) - same swing/harvesting
mechanics (Action0 inherits fine through Extends, unlike effect_group), just:
- Fully black (TintColor="0,0,0").
- Damage = necroZombieKillsCVar / 10, computed continuously in buffs.xml's
buffNecroZombieKillTrackerDisplay (see that file for why it lives there and not here -
a held item has no update tick of its own) and read live via
passive_effect value="@$necroKnifeDamageCVar" - "base_set" REPLACES the base damage
entirely (own effect_group, doesn't inherit the parent's tiered damage - matches this
mod's own established items.xml effect_group-doesn't-inherit-through-Extends rule, so
there's nothing to actually override, just nothing inherited to begin with). At 0 kills
this is 0 damage - a deliberate consequence of the user's own formula, not a bug: the
knife is a payoff weapon for the necromancer playstyle, not a starter blade.
- Lifesteal: heals for half of that same damage value (a second computed CVar,
$necroKnifeHealCVar), applied instantly on the hit via ModifyStats. ORDINARY SWING
ONLY since 2026-09-09 - the power attack no longer heals at all, see the trigger
comment below. It originally went
through vanilla's medicalRegHealthAmount regen queue instead and healed NOTHING at all -
see the long comment on the triggered_effect below for why that never worked and why the
fix moved off that path rather than completing it. This is "half the weapon's listed
damage", not "half of what actually landed after the target's own resistances" - those
should be very close for melee-vs-zombie in practice, and getting the exact
post-mitigation number would need a Harmony patch on DamageEntity's return value instead
of this pure-XML approach.
- Applies buffNecroVictim (buffs.xml) to whatever zombie it hits - see
HarmonySrc/VictimPatch.cs for what that debuff actually does (guaranteed green bag drop
on death, not visible otherwise).
- Damage/heal/victim-debuff triggers all gated to zombies only (EntityTagCompare) so
swinging at a block, crate, or (PvP) another player doesn't heal you or tag anything.
No crafting recipe/level gate specified by the user beyond the mechanics - guessed modest
and early (5 bone + 1 blood + 10 fiber, no craft_area/level tag, personal crafting from
the start like the Spirit Stone), matching how the knife's real power comes from playing
the necromancer build over time, not an expensive unlock. Say if that should change. -->
<append xpath="/items">
<item name="necroWpnBladeNecroKnife">
<property name="Extends" value="meleeWpnBladeT0BoneKnife"/>
<!-- Not for sale, per user request 2026-08-29 ("убери у всех предметов некроманта
цену продажи") - overridden explicitly since this item never had its own
EconomicValue before (just inherited the vanilla bone knife's own sell price). -->
<property name="EconomicValue" value="0"/>
<!-- ICON UPDATED 2026-08-29: real generated art (NecromancersKnife.png) replaces the
old reused-vanilla-icon+black-tint trick entirely - no CustomIconTint needed any
more, the new art is already black. -->
<property name="CustomIcon" value="NecromancersKnife"/>
<!-- TintColor (0,0,0 = pure black multiply) is the same mechanism used successfully
elsewhere in this mod (e.g. the Spirit Stone/Grimoire/summon books), but this is
its first use on an actual weapon mesh rather than a resource/book - unconfirmed
whether melee weapons with ShowQuality="true" (inherited from the base knife)
render TintColor the same way. If it's still not black after this deploy, that's
the next thing to dig into (possibly needs ShowQuality or a cosmetic-slot
workaround), not a guess to make blind right now. -->
<property name="TintColor" value="0, 0, 0"/>
<!-- MOD SLOTS ADDED 2026-09-07 (user request: "добавь в нож слоты для
модификаций... модификации там будут особые, именно для ножа
некроманта, а не для обычного ножа"). Tags has to be restated in full here
rather than appended to - a property DOES inherit through Extends (unlike
effect_group), so this whole list is the vanilla bone knife's own Tags value copied
verbatim, plus the two new ones at the end, MINUS canHaveCosmetic (see below).
Everything else the original had is kept deliberately:
perkDeepCuts/perkFlurryOfAgility/bladeSkill/attAgility are what the agility perks
match on, and corpseRemoval is vanilla plumbing.
canHaveCosmetic REMOVED 2026-09-07 on direct instruction ("хэв косметик убирай").
It was the one remaining hole in "necromancer mods only": the 10 vanilla modDye*
modifiers are the only mods in the game with no blocked_tags at all, so the noMods
tag below does not stop them, and their
installable_tags="weapon,tool,vehicle,drone" matches this knife's own "weapon"
tag. Dyes land in a separate cosmetic slot rather than one of the four real ones,
so they never actually cost the player a mod slot - but they would recolour a
weapon whose entire point is being pure black. That tag is exactly and only what
allocates the slot: decompiled, ItemValue's constructor does
"CosmeticMods = new ItemValue[itemClass.HasAnyTags(ItemClassModifier.CosmeticItemTags) ? 1 : 0]"
and CosmeticItemTags is literally FastTags.Parse("canHaveCosmetic"). No tag, no
slot, nowhere for a dye to go. Same save-compat caveat as the mod slots below: the
array is sized once when the item is created, so a knife already sitting in a save
keeps whatever cosmetic slot it was built with.
The two additions do exactly one job each, and together they are the entire
"necromancer mods only" mechanism:
noMods - blocks EVERY vanilla mod. Confirmed exhaustively: all 87 vanilla
item_modifiers that declare blocked_tags at all list noMods among
them, and blocked_tags maps to ItemClassModifier.DisallowedTags,
which the install UI itself refuses on (decompiled
XUiC_ItemPartStack.CanSwap + XUiM_AssembleItem, both do
"itemClass.HasAnyTags(DisallowedTags) -> return false"). The 24
modifiers with NO blocked_tags cannot reach this knife anyway:
they are the 10 dyes and 7 drone mods (cosmetic slot / drone-only),
two quest items, modGunBowAdminArcheryReloadRecovery
(installable_tags="perkArchery", which this knife lacks) and
modGunButtkick3000/4000 + modMeleeGunToolDecapitizer, all three of
which are CreativeMode="Test"/"Dev" and so unobtainable in normal
play. "noMods" itself has no meaning in code at all - grepping the
decompiled assembly for it finds nothing, it is purely a naming
convention vanilla data uses in blocked_tags.
necroKnife - the positive half: the mod's own three modifiers declare
installable_tags="necroKnife" (see the new Config/item_modifiers.xml),
and no other item in vanilla or this mod carries that tag, so they
fit this knife and nothing else. -->
<property name="Tags" value="T0,knife,melee,grunting,light,perkFlurryOfAgility,weapon,meleeWeapon,attAgility,perkDeepCuts,perkTheHuntsman,bladeSkill,corpseRemoval,noMods,necroKnife"/>
<property class="Action0">
<property name="DamageEntity" value="1"/>
</property>
<!-- tiered="false" REMOVED 2026-09-07 - it would have silently broken every mod
installed here, and this is not a cosmetic detail. ItemClass.HasQuality is not
driven by the ShowQuality property at all; decompiled, it is literally
"Effects.IsOwnerTiered()", and MinEffectController.IsOwnerTiered() returns true
only if at least ONE effect_group has OwnerTiered set - which defaults to true
(MinEffectGroup line 28) and is overridden only when a "tiered" attribute is
actually present. This item had exactly one effect_group and it said
tiered="false", so HasQuality was false. And ItemValue.FireEvent ends with:
if (!HasQuality) { return; }
for (i...) Modifications[i].FireEvent(...)
- i.e. installed mods receive NO triggered_effects whatsoever on an item without
quality, while the slots themselves would still have appeared and still accepted
mods (Modifications is allocated earlier, unconditionally). Exactly the same shape
of silent failure as the lifesteal bug fixed above: everything looks wired up,
nothing fires. Dropping the attribute restores the vanilla default - and note the
real bone knife this item extends has no "tiered" attribute on its own
effect_group either, so this now matches it. Nothing else about this group depends
on being untiered: every passive_effect below is a single value with no per-tier
list, which is exactly what the vanilla bone knife does inside its own tiered
group (AttacksPerMinute/StaminaLoss/MaxRange are all flat there).
ModSlots 0 -> 4 per the user's own choice ("4 фиксированно"): flat, not a
per-quality list like vanilla knives use, since quality is meaningless on this
weapon anyway (its damage is base_set from a CVar, not rolled from a stats table). -->
<effect_group name="necroWpnBladeNecroKnife">
<passive_effect name="ModSlots" operation="base_set" value="4"/>
<passive_effect name="EntityDamage" operation="base_set" value="@$necroKnifeDamageCVar"/>
<!-- User request 2026-08-28 ("еле-еле бьёт" - add speed): the base bone knife's
swing rate comes from its <stats> quality-roll table (Data/Config/items.xml),
not a flat value - base_set here overrides that outright, unconditionally
(no perk tag gate), same mechanism vanilla knives use for their own speed
bonuses (e.g. AttacksPerMinute base_set="120" tags="perkDeepCuts,..." on a
nearby T1 knife) minus the tag requirement, since this should just always be
fast. 120 matches that same reference point. -->
<passive_effect name="AttacksPerMinute" operation="base_set" value="120"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroVictim">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
<!-- ЛЕЧЕНИЕ СНЯТО С СИЛОВОГО УДАРА 2026-09-09 (пользователь: "лучше вообще не
лечить на силовом ударе"). Триггер сменён с onSelfAttackedOther на
onSelfPrimaryActionRayHit, и это единственное изменение по смыслу.
ЗАЧЕМ. onSelfAttackedOther шлётся на ЛЮБОЕ попадание - и обычное, и силовое
(ItemActionAttack.cs:790/838, внутри ItemActionAttack.Hit). Поэтому силовой
удар одновременно снимал 5 HP и лечил на половину урона ножа, то есть на
necroZombieKillsCVar/20. Порядок при этом был "сначала плата, потом лечение":
ItemActionDynamic.cs:541 шлёт onSelfSecondaryActionRayHit ДО вызова
ItemActionAttack.Hit в конце того же метода. Итоговая цена силовой атаки
получалась 5 - убийств/20, то есть:
100 убийств - ровно ноль,
500 убийств - ПЛЮС 20 HP за удар,
5000 убийств - плюс 245 HP за удар.
Силовая атака превращалась в лучший в моде способ лечиться, при том что её
цена прямо описана в Localization.csv. Перестановка порядка (лечить первым,
чтобы лечение упёрлось в потолок - Stat.Value клампится по ModifiedMax,
проверено) чинила бы только вид на полном здоровье: когда игрок реально ранен,
порядок ни на что не влияет и удар всё равно даёт чистый плюс. Поэтому убрано
совсем, а не переставлено.
ПОЧЕМУ ИМЕННО onSelfPrimaryActionRayHit. Это зеркало уже используемого здесь
onSelfSecondaryActionRayHit - та же строка ItemActionDynamic.cs:541, тот же
тернарник по indexInEntityOfAction, только ветка обычного удара. Ванильный,
широко используемый триггер (24 срабатывания в Data/Config/items.xml, в том
числе с target="other" у электродубинки), так что MinEventContext.Other к
этому моменту гарантированно проставлен (строка 532) и гейт на зомби
работает.
Два следствия, оба безобидные:
- Событие летит ДО применения урона, а не после. Сумме лечения это
безразлично: она берётся из CVar $necroKnifeHealCVar, который считает
бафф в buffs.xml, а не из фактически нанесённого урона.
- Событие летит и при попадании по блоку, но там Other пуст, и
EntityTagCompare tags="zombie" такой вызов отсекает - как и раньше.
buffNecroVictim НЕ трогали: он остался на onSelfAttackedOther, то есть метка
Жертвы по-прежнему вешается и обычным, и силовым ударом.
LIFESTEAL BUG FIXED 2026-09-07 (user report: "нож не подлечивает"). The old
version did only "ModifyCVar medicalRegHealthAmount add @$necroKnifeHealCVar",
which is just the FIRST of the three things vanilla's own medical items do -
and that CVar heals nothing by itself, it is only a queue. Every vanilla healer
(aloeCream/bandage/firstAidKit, Data/Config/items.xml ~18614 / ~18747 / ~18854)
follows that add with "ModifyCVar medRegHealthIncSpeed set ..." AND "AddBuff
buffProcessConsumables"; buffProcessConsumables then adds buffHealHealth, and it
is buffHealHealth's own onSelfBuffUpdate (Data/Config/buffs.xml ~8567) that
actually turns the queue into HP - medRegHealthIncSpeed HP per 1s tick while
draining 2 from the queue. The knife did neither of the other two steps, so it
just kept incrementing a number nothing ever read: zero healing, always. And
even adding buffProcessConsumables alone would not have been enough, because
medRegHealthIncSpeed has no default value anywhere in vanilla data (it is only
ever set by a medical item, or reset to 2 when buffHealHealth ends), so on a
player who had never used a bandage the tick would have added 0 HP.
Fixed with a direct ModifyStats rather than by completing the medical-queue
path: lifesteal should land on the swing, not trickle in over the following ten
seconds, and routing it through medicalRegHealthAmount would also let a knife
hit stomp the heal RATE of a first aid kit the player happens to have
regenerating at that moment (medRegHealthIncSpeed is one shared CVar, and a
physician-perked kit sets it well above 2). ModifyStats reading a live CVar via
value="@$cvar" is confirmed vanilla-supported (buffs.xml ~8569 and ~4565), it
targets self by default, and it clamps to max health on its own.
Still HALF the knife's damage, per the original spec - see BACKLOG.md item 5
and the item description in Localization.csv, both of which say "half". -->
<!-- ГЕЙТ НА ТРУПЫ, 2026-09-09. Без "Health GT 0" на цели каждый из эффектов ниже
срабатывал и по трупу: isHitValid берёт цель с _ignoreDead:false
(ItemActionDynamic.cs:673), труп остаётся тем же EntityZombie с тегом zombie, а
EntityAlive.DamageEntity не имеет раннего выхода по IsDead(). То есть разделка
трупа лечила игрока, а силовой удар по трупу брал с него 5 HP впустую.
Почему именно эти эффекты можно так гейтить, а метку Жертвы выше - НЕЛЬЗЯ.
Все они висят на *ActionRayHit, а это событие уходит ДО применения урона
(ItemActionDynamic.cs:541, ItemActionAttack.Hit вызывается в конце того же
метода) - значит у живой цели здоровье в этот момент ещё больше нуля, включая
смертельный удар. А buffNecroVictim сидит на onSelfAttackedOther, которое
уходит ПОСЛЕ DamageEntity (ItemActionAttack.cs:838, сразу за строкой 827): на
смертельном ударе зомби там уже мёртв и его здоровье 0, так что такой же гейт
снял бы метку ровно с добивающего удара - то есть отобрал бы мешок Жертвы у
всех, кого убили с одного удара. Поэтому метка оставлена без гейта; повесить
её на уже мёртвого зомби безвредно - мешок для него всё равно уже разыгран.
AOE-эффекты "Мёртвой бури" гейтить не нужно: World/Chunk.GetLivingEntitiesInBounds
(Chunk.cs:2920) сама отсеивает цели по !IsDead(), так что трупы в выборку не
попадают вовсе.
Требование само по себе безопасно и при попадании по блоку: там Other пуст, а
TargetedCompareRequirementBase.IsValid возвращает false, если цели нет. -->
<triggered_effect trigger="onSelfPrimaryActionRayHit" action="ModifyStats" stat="Health" operation="add" value="@$necroKnifeHealCVar">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
<requirement name="StatCompareCurrent" target="other" stat="Health" operation="GT" value="0"/>
</triggered_effect>
<!-- POWER ATTACK, added 2026-09-07. User: "У него есть основная атака и силовая. Но
силовая по мощности не отличается от основной. Может повесим на силовую
что-нибудь другое?" - then, on a first round of proposals: "Слишком имбалансно.
Нужно что-то другое", and finally "Гарантированное сбивание с ног и отъедание,
например 5 HP персонажа - это очень заманчиво."
WHY THE TWO ATTACKS DEALT IDENTICAL DAMAGE - not an engine bug, a direct
consequence of how this item sets damage. ItemActionAttack.GetDamageEntity
tags the lookup by which action swung:
tmpTag = ((actionIndex == 0) ? PrimaryTag : SecondaryTag); // primary/secondary
return EffectManager.GetValue(PassiveEffects.EntityDamage, ..., tmpTag);
Vanilla leans on that - the iron fireaxe, pickaxes and shovels all carry
"EntityDamage perc_add 1.25 tags=secondary" plus a heavier secondary
StaminaLoss. This knife's own EntityDamage is base_set with NO tag, so it
applies identically to both swings AND overwrites any inherited difference.
Deliberately NOT "fixed" by adding a secondary damage bonus: the user asked for
the power attack to be DIFFERENT, not stronger, and rejected the stronger
options outright as imbalanced. Damage, stamina and swing rate are untouched.
onSelfSecondaryActionRayHit is the clean hook and needs no extra XML plumbing:
ItemActionDynamic.cs line 541 fires
(indexInEntityOfAction == 0) ? onSelfPrimaryActionRayHit
: onSelfSecondaryActionRayHit
so it means "the power swing actually connected", not "the power swing was
started". That is why the 5 HP is charged here rather than on
onSelfSecondaryActionEnd - a whiff costs nothing, only a landed hit does.
(The UsePowerAttackTriggers property exists too, but it REROUTES the primary
triggers onto secondary ones and only three vanilla salvage tools use it -
wrong tool here, it would have broken the primary swing's own effects.)
The Ragdoll shape is copied from vanilla's stun baton (Data/Config/items.xml
~4090), which uses this exact trigger and action; narrowed from its otherAOE to
a single target, since knocking down a whole group would be the kind of power
spike that was already turned down once.
С 2026-09-09 эта плата наконец настоящая: раньше тот же удар вешал ещё и
вампиризм через onSelfAttackedOther и с некоторого уровня перекрывал её с
запасом - см. большой комментарий у лечения выше.
The 5 HP is gated on Health GT 5 so this can never kill the player. Dying to
your own knife mid-horde would read as a bug, not a cost. Note the consequence:
below 5 HP the knockdown becomes free. If that mercy is unwanted, drop the
StatCompareCurrent requirement.
Works on zombie ANIMALS too (user asked directly). Everything here gates on the
"zombie" tag, and animalZombieDog/Bear/Vulture/Boar all carry
"entity,animal,zombie,zombieAnimal,hostile,..." - verified by resolving their
Tags through the extends chain. Note this is NOT true of two other parts of the
mod - the kill counter and the Deviator charm both miss zombie animals for
unrelated reasons; see BACKLOG.md 2026-09-07. -->
<triggered_effect trigger="onSelfSecondaryActionRayHit" action="Ragdoll" target="other" duration="1.5" force="150">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
<requirement name="StatCompareCurrent" target="other" stat="Health" operation="GT" value="0"/>
</triggered_effect>
<triggered_effect trigger="onSelfSecondaryActionRayHit" action="ModifyStats" stat="Health" operation="subtract" value="5">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
<requirement name="StatCompareCurrent" target="other" stat="Health" operation="GT" value="0"/>
<requirement name="StatCompareCurrent" stat="Health" operation="GT" value="5"/>
</triggered_effect>
</effect_group>
</item>
</append>
<!-- "Кожа жертвы" (Victim's Skin): rare drop from the guaranteed green "Жертва" bag (see
HarmonySrc/VictimPatch.cs, recipes/loot.xml groupNecroVictimPack). Per user: an ingredient
for summon books - not wired into the existing Dog/Swarm recipes here (not asked to
retrofit those), just a real, valid crafting resource ready for that or a future recipe. -->
<append xpath="/items">
<item name="resourceVictimSkin">
<property name="Extends" value="resourceLeather"/>
<property name="DescriptionKey" value="resourceVictimSkinDesc"/>
<!-- ICON UPDATED 2026-08-29: real generated art (VictimsSkin.png) replaces the reused
resourceLeather-icon+yellow-tint trick - no CustomIconTint any more. Extends
alone still doesn't give an item a working icon on its own (see the Knife's own
history), so CustomIcon stays explicit even though this item already extends
resourceLeather for its other properties. -->
<property name="CustomIcon" value="VictimsSkin"/>
<!-- 3D MODEL UPDATED 2026-08-29, SECOND ATTEMPT - the bedroll model (first attempt,
same day) was rejected by the user ("не тот префаб") without it ever being
confirmed as the one they remembered; a plain string search of items.xml/
blocks.xml/entityclasses.xml for hide/pelt/leather/roll/rug/tarp/canvas/curled
turned up nothing more convincing (rugBearPrefab/mattressCurledPrefab were the
only other candidates, offered but not chosen). User's own pick instead:
foodShamSandwich's real Meshfile, @:Other/Items/Food/parcelGenericPrefab.prefab
(confirmed by reading that item directly) - a generic wrapped parcel/bundle
shape. HoldType switched to 31 (foodShamSandwich's own HoldType) to match how
that mesh was actually rigged to be held, same reasoning as the bandage/HoldType
64 swap on the "свиток" family above. TintColor (dark blood-red) kept unchanged
from before - still reads fine as a stained wrapped skin/hide bundle. -->
<property name="HoldType" value="31"/>
<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="TintColor" value="60, 10, 10"/>
<property name="EconomicValue" value="0"/>
</item>
</append>
<!-- "Прах зомби" (Zombie Ash): BACKLOG.md item 8, dictated 2026-08-29, implemented
2026-08-29. Replaces foodRottingFlesh/resourceBone in the "Жертва" loot bag (see
loot.xml groupZpackVictim below) and becomes a crafting ingredient across the other
necromancy recipes - but deliberately NOT the Knife's own recipe (see recipes.xml for
why: the knife has to stay craftable from scratch, and ash only exists because the
knife already tagged a zombie as a Victim - adding it to the knife's own recipe would
make it uncraftable from zero).
No dedicated "ash" resource exists anywhere in vanilla items.xml to reuse wholesale
(checked directly - resourceCoal/MresourceCoal is the closest powdery/granular resource,
but it's coal, not ash).
ICON UPDATED AGAIN 2026-08-29 (same day, superseding the gunpowder placeholder above):
real generated art now exists (UIAtlases/ItemIconAtlas/ZombieAsh.png) - no CustomIconTint
needed. TintColor below still colors the 3D mesh (still resourceCoal-based, unrelated to
the 2D icon) - left alone, only the icon changed. -->
<append xpath="/items">
<item name="resourceZombieAsh">
<property name="Extends" value="resourceCoal"/>
<property name="DescriptionKey" value="resourceZombieAshDesc"/>
<property name="CustomIcon" value="ZombieAsh"/>
<property name="TintColor" value="200, 200, 200"/>
<property name="EconomicValue" value="0"/>
</item>
</append>
<!-- "Кровь некроманта" (Necromancer's Blood): dictated 2026-08-30. Extends medicalBloodBag for
its mesh/material/hold/pickup-sound (reused wholesale, thematically it IS a bag of blood,
just a darker/necromantic one) - CustomIcon still set explicitly even though Extends is
used, same lesson as every other item in this mod (Extends alone never gives a working
icon, confirmed originally on the Knife). Reuses the REAL vanilla sprite named
"medicalBloodBag" itself (that item has no CustomIcon of its own, so its sprite name
equals its item name) rather than a generated-art file, per direct instruction ("Иконка
такая же как и у обычной крови, но тинт затемнённый") - only TintColor differs (dark,
near-black red vs. no tint on the vanilla bag).
Crafting rules ("для создания нужна пустая банка и наличие любого ножа. При крафте нужно
отнимать у персонажа 90% имеющегося ХП") - the jar is a normal recipe ingredient (see
recipes.xml), but "any knife present, not consumed" and "cost 90% of current HP" have NO
vanilla XML equivalent (recipes.xml has no per-ingredient "required but not consumed" flag,
and crafting a resource has no HP-cost hook at all) - both enforced in
HarmonySrc/NecromancerBloodPatch.cs instead. See that file for the exact decompiled
mechanism and an important caveat about ingredient-refund timing that's flagged there, not
glossed over. -->
<append xpath="/items">
<item name="resourceNecromancerBlood">
<property name="Extends" value="medicalBloodBag"/>
<property name="DescriptionKey" value="resourceNecromancerBloodDesc"/>
<!-- Custom art delivered 2026-08-30 (exch/NecromantsBlood.png, 160x160, copied to
UIAtlases/ItemIconAtlas/) - replaces the earlier placeholder that reused the
vanilla medicalBloodBag sprite with a darkened tint. No CustomIconTint here,
same reasoning as every other hand-drawn icon in this mod (Dog/Insect summon
books, etc.) - don't recolor finished art. -->
<property name="CustomIcon" value="NecromantsBlood"/>
<property name="TintColor" value="60, 0, 10"/>
<property name="EconomicValue" value="0"/>
</item>
</append>
<!-- "Петля вора" (Thief's Loop) - REMOVED 2026-08-30. Existed briefly (dictated/implemented
2026-08-29, reworked several times through 2026-08-30 chasing load errors), removed per
direct instruction once the real root cause of its last error became clear: its
Action1 zoom (Class="Zoom") needs a genuine weapon-rigged mesh (an "Attachments"
transform, confirmed by decompiling the real NullReferenceException it threw), which
meant giving up the "invisible/seed-like" look the user actually wanted for it - "т.е.
зум завязан на меш? тогда отмена... петлю вора убираем, раз не работает." Its own Harmony
file (HarmonySrc/ThiefLoopPatch.cs) was deleted outright, not just disabled - nothing
else in the mod depended on it. If a remote-loot item is wanted again later, the
raycast/EntityLootContainer-draining approach documented in this session's history is
still valid, just needs a non-Zoom item shell (or a real weapon mesh accepted as the
visual, since that's what Zoom specifically requires).
"Пространственный браслет" (Spatial Bracelet) below is unaffected - it never used
Class="Zoom" at all (both its actions are Class="Eat"), so it never hit this problem. -->
<!-- Second bracelet, dictated 2026-08-30, implemented same day - no explicit name given, so
kept internally descriptive: "Пространственный браслет" (Spatial Bracelet).
- Power attack: opens a large, resizable personal storage window - "как у дрона, но
большой" (like a drone's, but bigger). See HarmonySrc/SpatialVaultPatch.cs for the real
mechanism (XUiC_BagStorageWindowGroup.Open - the exact same API EntityDrone itself uses
to show ITS OWN storage, decompiled directly to confirm, not guessed) - not a
declarative XML feature, Harmony required. Slot count is NOT fixed - user's own later
correction: "1*скилл_некроманта/10 округлённый до целого" - scales with the player's
live Necromancy skill level (same skill the Knife's damage already scales with,
craftingNecroNecromancy, capped at level 5000) divided by 10 and rounded, growing over
time exactly like the Knife's own damage does, same "payoff scales with progression"
family as every other kill-count-tied mechanic in this mod.
- Regular attack: DISABLED 2026-08-30 per direct request ("не срабатывает никак" ->
"пусть тогда обычная атака... не делает ничего") - originally meant to knock back and
slow whatever zombie the crosshair is aimed at (raycast + Entity.SetPosition shove +
buffInjurySlow, see HarmonySrc/SpatialVaultPatch.cs's own ShoveZombieAtCrosshair method,
kept in the file unused rather than deleted in case this gets revisited) - the click is
now just silently absorbed, no effect.
"Seed"-style grip: foodCropYuccaFruit's own HoldType="31" + parcelGenericPrefab.prefab
(per direct correction after an earlier rock-mesh placeholder didn't match what was
meant) - never touches Class="Zoom", so it never needed a real weapon mesh the way
Thief's Loop briefly did.
Not for sale (EconomicValue=0), same as every other necromancer item. TintColor green
(2026-08-30, direct request). Custom art delivered 2026-08-30
(exch/ProstranstvennoeHranilische.png, 160x160, copied to UIAtlases/ItemIconAtlas/) -
CustomIcon set below, no CustomIconTint (same "don't recolor finished art" rule as the
rest of the mod's hand-drawn icons). -->
<append xpath="/items">
<item name="braceletSpatialVault">
<property name="Tags" value="T0,weapon,attPerception"/>
<!-- ItemTypeIcon="melee" REMOVED 2026-09-07 (user report: "поверх пиктограмм некоторых
рецептов стоят странные пиктограммы... то ли факел, то ли спичка"). This was the
small badge drawn in the TOP-LEFT corner over the item's own icon in the recipe
list - a different widget from the workstation indicator the user was expecting to
see. Both live in XUi_InGame/templates.xml's recipe_entry:
<sprite name="itemtypeicon" width="12" height="12"
sprite="ui_game_symbol_{itemtypeicon}" pos="2,-2" .../> <- this one
<sprite name="Unlocked" sprite="{unlockicon}" size="24,24"
pos="352,-10" visible="{isunlockable}" .../> <- workbench/lock
So the value is pasted straight into a sprite name: ItemTypeIcon="melee" asked for
"ui_game_symbol_melee", which really does exist (it is the icon vanilla gives
buffMeleePreferred - a club), and at 12x12 in the corner a club reads exactly like
a match or a torch. Not a missing-sprite bug, a wrong-sprite one.
Wrong on the merits too: this is a bracelet, not a melee weapon, and its regular
melee attack was deliberately disabled on 2026-08-30 (see SpatialVaultPatch.cs and
the BACKLOG entry) - so the badge advertised an attack the item no longer has.
Dropped entirely rather than swapped for another symbol: ItemClass.ItemTypeIcon
defaults to "" and the widget is gated on {hasitemtypeicon}, so with no property at
all the badge simply doesn't draw, leaving the row showing just the item icon plus
the real workbench indicator on the right - which is what was wanted. There is no
sensible "amulet/magic" symbol in the set vanilla actually uses for items anyway
(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. -->
<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="TintColor" value="30, 200, 60"/>
<property name="HoldType" value="31"/>
<property name="Weight" value="0"/>
<property name="Stacknumber" value="1"/>
<property name="EconomicValue" value="0"/>
<property name="Group" value="Ammo/Weapons,Ammo"/>
<property class="Action0">
<property name="Class" value="Eat"/>
<property name="Delay" value="0.3"/>
</property>
<property class="Action1">
<property name="Class" value="Eat"/>
<property name="Delay" value="0.3"/>
</property>
</item>
</append>
</config>