Свёрток-«коробочка на бечёвке» (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
1501 lines
103 KiB
XML
1501 lines
103 KiB
XML
<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>
|
||
<!-- 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>
|
||
|
||
<!-- 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.
|
||
|
||
СОМНЕНИЕ СНЯТО 2026-09-10 по самой ванили: meleeWpnBladeT0BoneKnife задаёт себе
|
||
TintColor 107, 107, 71 (Data/Config/items.xml:2419) на том же самом
|
||
boneShivPrefab, то есть слот тинта у этого меша рабочий и на оружии с
|
||
ShowQuality тоже применяется. Копать тут больше нечего; осталось только
|
||
посмотреть глазами, достаточно ли чёрный получается клинок. -->
|
||
<!-- TintColor ОТКЛЮЧЁН 2026-09-10 вместе с переходом на свою текстуру.
|
||
|
||
TintColor - это МНОЖИТЕЛЬ цвета меша, а 0,0,0 множит в чистый чёрный. Он был нужен,
|
||
пока нож носил ванильную бежевую кость: другого способа затемнить клинок не было.
|
||
Теперь цвет несёт своя текстура necroKnife_d.png, и тот же множитель погасил бы
|
||
всё нарисованное в ноль - вместе с пурпурным свечением в альбедо.
|
||
|
||
Иконка от этого не зависит: она отдельный арт (CustomIcon выше), CustomIconTint
|
||
здесь никогда не стоял. И на иконке клинок именно КОСТЯНОЙ, а не чёрный, так что
|
||
чёрный множитель ей вдобавок противоречил.
|
||
|
||
Побочно это снимает неоднозначность теста трубы: красный клинок мог быть виден
|
||
только за счёт эмиссии, если тинт всё-таки давил альбедо нашего меша. Без тинта
|
||
вопроса больше нет.
|
||
<property name="TintColor" value="0, 0, 0"/> -->
|
||
|
||
<!-- СВОЯ МОДЕЛЬ. ВКЛЮЧЕНА 2026-09-10 вместе с бандлом Resources/necroknife (2.19 МБ,
|
||
собран Unity 2022.3.62f2 в batch-режиме; внутри necroKnifePrefab.prefab, три меша
|
||
boneShiv_LOD0/1/2, текстуры boneShiv_d/_n и материал necroKnife.mat - бандл
|
||
самодостаточный, проверено по составу).
|
||
|
||
ВНИМАНИЕ: в бандле сейчас ТЕСТОВЫЙ вид - ярко-красный материал с эмиссией. Это
|
||
проверка трубы, а не финальная модель. Красный выбран потому, что ванильный нож
|
||
бежевый и по нему не отличить, загрузился наш бандл или подставилась ваниль;
|
||
эмиссия - потому что TintColor ниже стоит 0,0,0, и если игра множит тинт на
|
||
материал нашего меша, красный альбедо ушёл бы в чёрный и тест ничего бы не показал.
|
||
Настоящая текстура рисуется поверх _private/Extracted/boneShiv_d.png после того,
|
||
как труба подтвердится.
|
||
|
||
Синтаксис: "#" - грузить из бандла, "@modfolder(NecromancerTome):" - путь от
|
||
корня этого мода, "?" отделяет путь к префабу ВНУТРИ бандла. Форма собрана из
|
||
двух подтверждённых кусков: ваниль пишет "#Entities/Trees?SnakeweedPrefab.prefab"
|
||
(Data/Config/blocks.xml), а поддержка "@modfolder:" лежит в Assembly-CSharp.
|
||
ФОРМА ПОДТВЕРЖДЕНА ЖИВЫМ ПРИМЕРОМ 2026-09-10: с этой строкой тестовый красный
|
||
нож появился в руке. Значит и запись работает, и бандл из папки мода читается,
|
||
и Standard-шейдер из Unity 2022.3.62f2 игра отрисовывает. Больше не гипотеза -
|
||
этой же формой можно подключать любые следующие свои модели.
|
||
|
||
necroknife - имя AssetBundle, заданное префабу в Unity; necroKnifePrefab.prefab -
|
||
имя префаба ВНУТРИ бандла. Оба должны совпасть с тем, что реально собралось, иначе
|
||
предмет останется без модели молча. Пересборка - меню NecromancerTome -> Ctrl+Shift+B
|
||
либо batch: Unity.exe -batchmode -nographics -quit -projectPath <проект>
|
||
-executeMethod NecroKnifeSetup.SetupAndBuild. Игру после каждой пересборки
|
||
перезапускать: моды и их бандлы читаются только при старте.
|
||
|
||
Геометрия остаётся ванильной, меняются материал и текстура, поэтому HoldType,
|
||
посадка в руке и анимации от базового ножа продолжают подходить без правок. -->
|
||
<property name="Meshfile" value="#@modfolder(NecromancerTome):Resources/necroknife?necroKnifePrefab.prefab"/>
|
||
|
||
<!-- 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"/>
|
||
|
||
<!-- СВОЯ БАНКА С КРОВЬЮ, 2026-09-10 (указание: «берём чай из золотарника, и жёлтое
|
||
заменяем на кровавый цвет, с фиолетовыми оттенками»).
|
||
|
||
Заодно чинится расхождение текста и модели: описание предмета
|
||
(resourceNecromancerBloodDesc) с самого начала говорит «Банка, наполненная кровью
|
||
самого некроманта», а наследуемый medicalBloodBag показывает
|
||
@:Other/Items/Misc/sackPrefab.prefab - обычный мешок. Банка вернее и по механике:
|
||
рецепт и так требует пустую банку (recipes.xml, NecromancerBloodPatch.cs).
|
||
|
||
ПОЧЕМУ НЕ ХВАТИЛО ТИНТА - ПРОВЕРЕНО В ИГРЕ. Сначала пробовали дёшево, без бандла:
|
||
ванильный префаб чая плюс TintColor. Проверка 2026-09-10 показала, что банка
|
||
осталась чаем из золотарника - тинт предмета на этот меш НЕ ПОДЕЙСТВОВАЛ ВООБЩЕ.
|
||
У шейдера Game_EntityTintMaskSSS выигрывает собственный _Color материала (у чая
|
||
жёлтый, 166,133,37), и свойство предмета его не перебивает. Поэтому TintColor здесь
|
||
не задаётся совсем: он ничего не даёт и только вводил бы в заблуждение.
|
||
|
||
И по сути: кровь отличается от чая не цветом, а тем, что она непрозрачная, тёмная и
|
||
густая, с плёнкой на стекле. Поэтому жидкость ПЕРЕРИСОВАНА по яркости, а не
|
||
перекрашена множителем - генератор _private/tools/make_necroblood_textures.py.
|
||
|
||
HoldType 3 - хват банки вместо 45 (мешок), Material Mglass - стекло вместо ткани
|
||
(звук удара и осколки при разбитии). Без них банка держалась бы как мешок.
|
||
|
||
ЧТО СМОТРЕТЬ ГЛАЗАМИ. Материал собран на встроенном Standard в режиме Fade: родной
|
||
шейдер переиспользовать нельзя, AssetRipper выгрузил шейдеры заглушками. У Standard
|
||
альфа текстуры - это прозрачность, поэтому она задана осознанно: стекло
|
||
полупрозрачное, жидкость плотная. Банка должна читаться как стекло с густой кровью,
|
||
а не как матовый сосуд. -->
|
||
<property name="Meshfile" value="#@modfolder(NecromancerTome):Resources/necroblood?necroBloodPrefab.prefab"/>
|
||
<property name="HoldType" value="3"/>
|
||
<property name="Material" value="Mglass"/>
|
||
|
||
<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"/>
|
||
<!-- МЕШ И ХВАТ, 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/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="7"/>
|
||
<property name="HoldingItemHidden" value="true"/>
|
||
<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>
|