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

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

189 lines
15 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<config>
<!-- Step 1: lifetime zombie kill counter display.
buffStatusCheck01 is the vanilla hidden buff every player always has,
used here purely as a hook to (re)apply our display buff on game entry/respawn. -->
<append xpath="buffs/buff[@name='buffStatusCheck01']">
<effect_group name="necro zombie kill tracker">
<triggered_effect trigger="onSelfEnteredGame" action="AddBuff" buff="buffNecroZombieKillTrackerDisplay"/>
<triggered_effect trigger="onSelfRespawn" action="AddBuff" buff="buffNecroZombieKillTrackerDisplay"/>
</effect_group>
</append>
<append xpath="/buffs">
<!-- Never expires (duration 0): a persistent lifetime total, not a per-day/session counter. -->
<buff name="buffNecroZombieKillTrackerDisplay" icon="ui_game_symbol_zombie" icon_color="150,0,0" name_key="buffNecroZombieKillTrackerDisplayName" description_key="buffNecroZombieKillTrackerDisplayDesc">
<stack_type value="ignore"/>
<duration value="0"/>
<update_rate value=".1"/>
<display_value value="necroZombieKillsCVar"/>
<!-- Нож некроманта (BACKLOG.md item 5, user request 2026-08-28): "урон умножается на
скилл некроманта ... и делится на 10". Computed here (this buff already ticks
10x/sec on every player via update_rate) rather than on the knife item itself,
since a held item has no update tick of its own - reading a live CVar from a
passive_effect (confirmed working: vanilla's own $PlayerLevelBonus, computed in
buffs.xml and read elsewhere via passive_effect value="@$PlayerLevelBonus") needs
something that keeps recomputing it. $necroKnifeDamageCVar = necroZombieKillsCVar
/ 10, read by necroWpnBladeNecroKnife's own EntityDamage passive_effect in
items.xml. $necroKnifeHealCVar = half of that, read by the same item's lifesteal
trigger. Both "@cvar" (write) and "@$cvar" (write) forms exist in vanilla data for
plain vs. "$"-prefixed cvars - $-prefix here just follows the naming convention
vanilla uses for these player-effect scratch values, not a different mechanism.
BUG FIXED 2026-08-28 (knife dealt no damage, no lifesteal, victim debuff never
applied): these four triggered_effect elements were direct children of <buff>,
with no <effect_group> wrapper - unlike buffNecroDeviatorCharm below or vanilla's
own buffStatusCheck01 (Data/Config/buffs.xml line ~334), which both wrap every
triggered_effect in one. triggered_effect isn't valid straight under <buff> (only
things like display_value are) - the loader didn't throw on this (no "buffs.xml
failed to load" the way loot.xml did for its own ordering bug), it just silently
never ran them, so $necroKnifeDamageCVar/$necroKnifeHealCVar never got computed
and the knife's damage passive_effect read an undefined (0) value the whole time. -->
<effect_group name="necro knife damage tracking">
<triggered_effect trigger="onSelfBuffUpdate" action="ModifyCVar" cvar="$necroKnifeDamageCVar" operation="set" value="@necroZombieKillsCVar"/>
<triggered_effect trigger="onSelfBuffUpdate" action="ModifyCVar" cvar="$necroKnifeDamageCVar" operation="divide" value="10"/>
<triggered_effect trigger="onSelfBuffUpdate" action="ModifyCVar" cvar="$necroKnifeHealCVar" operation="set" value="@$necroKnifeDamageCVar"/>
<triggered_effect trigger="onSelfBuffUpdate" action="ModifyCVar" cvar="$necroKnifeHealCVar" operation="divide" value="2"/>
</effect_group>
</buff>
<!-- "Жертва" (Victim) marker - applied by the Necromancer's Knife on hit (see
necroWpnBladeNecroKnife in items.xml). HarmonySrc/VictimPatch.cs reacts to its
presence at the moment the zombie actually dies (patches Entity.DropBagServer) to
force-spawn the guaranteed green "Жертва" bag instead of the normal loot roll -
nothing in XML alone can make Entity.DropBagServer's LootDropEntityClass choice
conditional on a live buff, confirmed by decompiling it (that property is a static
per-species entity_class value, not CVar-driven like EntityDamage above).
BUG FIXED 2026-08-28 (buff kept vanishing before death-loot checked it, despite being
permanent - duration="0" confirmed via decompiling BuffClass to genuinely mean "never
expires", not the bug): missing remove_on_death="false". EntityAlive.ClientKill()
calls Buffs.OnDeath() BEFORE OnEntityDeath()/dropItemOnDeath() ever run (confirmed by
decompiling both) - EntityBuffs.OnDeath() strips any buff whose BuffClass.RemoveOnDeath
is true, which is the default when remove_on_death isn't specified at all. So the buff
really was being added, really was permanent under normal circumstances, and really
was still getting wiped out from under VictimPatch.cs's HasBuff check by the death
sequence itself, one step before that check ever ran. remove_on_death="false" is a
real, documented buff attribute - vanilla's own buffStatusCheck01 uses it for exactly
this "must survive death" reason.
Visual (user request 2026-08-28, "подсвети бафнутого зомби... фиолетовым"): same
RadiatedParticlesOnMesh glow as buffNecroDeviatorCharm below, reused rather than a
different particle - HarmonySrc/ParticlePatch.cs recolors it purple specifically for
THIS buff (checks which buff is present before deciding tint), since XML's
AttachParticleEffectToEntity has no color attribute at all (confirmed by decompiling
it - same reason the charm's own glow can't be XML-recolored either). Removed on
death too (onSelfDied) even though the buff FLAG itself survives death on purpose
(remove_on_death="false" above) - the glow is cosmetic and shouldn't linger on a
corpse; VictimPatch.cs's own HasBuff check doesn't care whether the particle is gone. -->
<buff name="buffNecroVictim" hidden="true" remove_on_death="false">
<stack_type value="ignore"/>
<duration value="0"/>
<effect_group>
<triggered_effect trigger="onSelfBuffStart" action="AttachParticleEffectToEntity" particle="RadiatedParticlesOnMesh" local_offset="0,0.75,0"/>
<triggered_effect trigger="onSelfBuffRemove" action="RemoveParticleEffectFromEntity" particle="RadiatedParticlesOnMesh"/>
<triggered_effect trigger="onSelfDied" action="RemoveParticleEffectFromEntity" particle="RadiatedParticlesOnMesh"/>
</effect_group>
</buff>
<!-- "Девиатор" charm marker. Purely a flag for HarmonySrc/CharmPatch.cs to react to -
the actual "fight for the player now" AI behavior happens in that Harmony patch, not
here. hidden/no duration: permanent for the charmed zombie's remaining lifetime.
The visible cue is the particle below - reuses the same mesh-hugging glow vanilla
radiated zombies wear (ParticleEffects/RadiatedParticlesOnMesh.prefab), the closest
thing to an actual recolor achievable from XML alone (no way to retint a live
entity's skin material via buff - AttachParticleEffectToEntity is as close as it gets).
It reads green/energy rather than "necromantic purple", but it's a real full-body
glow, not just a floating cloud; RemoveParticleEffectFromEntity on death/removal so it
doesn't linger on the corpse. -->
<buff name="buffNecroDeviatorCharm" hidden="true">
<stack_type value="ignore"/>
<duration value="0"/>
<effect_group>
<triggered_effect trigger="onSelfBuffStart" action="AttachParticleEffectToEntity" particle="RadiatedParticlesOnMesh" local_offset="0,0.75,0"/>
<triggered_effect trigger="onSelfBuffRemove" action="RemoveParticleEffectFromEntity" particle="RadiatedParticlesOnMesh"/>
<triggered_effect trigger="onSelfDied" action="RemoveParticleEffectFromEntity" particle="RadiatedParticlesOnMesh"/>
</effect_group>
</buff>
<!-- "Портальный вихрь" - BACKLOG.md item 6, user request 2026-08-29 ("пусть вокруг
персонажа летают частицы, лучше чёрные, как дым"). Purely a marker/particle-carrier
buff, same shape as buffNecroVictim/buffNecroDeviatorCharm above - NOT added/removed
by any trigger here at all (no onSelf* triggers reference it), because there's
nothing in the buff-trigger vocabulary that means "for the duration of an open
XUiC_Timer window" - HarmonySrc/PortalStonePatch.cs adds it directly
(player.Buffs.AddBuff) the instant the 10s channel opens and removes it
(player.Buffs.RemoveBuff) the instant the channel ends, however it ends (completed,
cancelled by damage, cancelled by power attack) - see that file.
Reuses the same RadiatedParticlesOnMesh glow the two zombie-facing buffs above
already use (same reasoning: no XML color attribute exists on
AttachParticleEffectToEntity, HarmonySrc/ParticlePatch.cs has to recolor it in code
regardless of which buff triggers it) - generalized ParticlePatch.cs to handle
EntityAlive broadly instead of EntityZombie-only, since this buff's target is the
PLAYER, not a zombie. Tinted near-black/smoke grey, per the user's own request. -->
<buff name="buffNecroPortalChannel" hidden="true">
<stack_type value="ignore"/>
<duration value="0"/>
<effect_group>
<triggered_effect trigger="onSelfBuffStart" action="AttachParticleEffectToEntity" particle="RadiatedParticlesOnMesh" local_offset="0,0.9,0"/>
<triggered_effect trigger="onSelfBuffRemove" action="RemoveParticleEffectFromEntity" particle="RadiatedParticlesOnMesh"/>
<triggered_effect trigger="onSelfDied" action="RemoveParticleEffectFromEntity" particle="RadiatedParticlesOnMesh"/>
</effect_group>
</buff>
<!-- "Тёмное чутьё" - постоянный радар зомби, пока Нож некроманта в руке и в нём стоит
модификатор necroModKnifeDarkSense. Продиктовано 2026-09-07: "В квестах на зачистку на
радаре показывает где находятся зомби. Нужен мод который включит подобный радар на
постоянку, пока мод в ноже, а нож в руке."
Да, реально, и целиком на XML - Harmony не нужен. Ключ в том, что метки на компасе и
карте ставятся через NavObject, а для них есть готовое XML-действие
MinEventActionSetNavObject (ваниль зовёт его в buffs.xml ~15281 для твич-эффектов):
entityAlive.AddNavObject(navObjectName, overrideSprite, overrideText)
и оно наследует MinEventActionTargetedBase, то есть умеет target="selfAOE" с range и
target_tags - ровно то, что нужно для "все зомби вокруг игрока".
ПОЧЕМУ nav_object="zombie", А НЕ "clear_sleeper". Квест на зачистку использует
clear_sleeper, но у того класса стоит <property name="requirement_type"
value="QuestBounds"/> - он показывается только внутри границ активного квеста, то есть
вне квеста не работал бы вообще. Класс "zombie" (nav_objects.xml ~431) - без такого
требования: красная пульсирующая иконка ui_game_symbol_zombie и на карте, и на компасе
(max_distance компаса 1024).
ПОЧЕМУ БАФФ, А НЕ ПРЯМО В МОДЕ. У модификатора нет своего тика. onSelfEquipUpdate
существует (Inventory.OnUpdate шлёт его на держимый предмет), но он идёт каждый
апдейт, а тут на каждом тике делается пространственная выборка сущностей - слишком
часто. Бафф даёт честный троттлинг через update_rate, поэтому мод только вешает и
снимает этот бафф по onSelfEquipStart/onSelfEquipStop (ванильный паттерн, так сделаны
эффекты кирок в items.xml ~158/175).
ПОЧЕМУ КАЖДЫЙ ТИК СНАЧАЛА СНИМАЕТ, ПОТОМ ВЕШАЕТ. Метка живёт на самой сущности
(RegisterNavObject(name, this) - она ездит за зомби), и сама собой не пропадает, когда
зомби отошёл. NavObjectManager.Update чистит только те, у которых TrackedEntity стал
null, то есть уже уничтоженный объект - мёртвый, но ещё не исчезнувший труп метку
сохраняет. Поэтому радиус снятия (60) заведомо больше радиуса добавления
(30): всё, что вышло из зоны показа, но ещё рядом, чистится само.
AddNavObjectClass дедуплицирует (NavObject.cs ~346: "if (!NavObjectClassList.Contains)"),
так что повторное добавление одному и тому же зомби безвредно.
ИЗВЕСТНЫЙ РИСК, ПРОВЕРИТЬ В ИГРЕ: снятие-и-добавление в одном тике для зомби, который
остаётся в зоне, технически пересоздаёт NavObject (RemoveNavObjectClass опустошает
список -> UnRegisterNavObject -> следующий Add регистрирует заново). Итоговое
состояние кадра верное, но пульсация иконки может подёргиваться раз в секунду. Если
будет заметно - убрать строку снятия из onSelfBuffUpdate и оставить чистку только на
onSelfBuffRemove, ценой залипших меток на ушедших зомби. -->
<buff name="buffNecroDarkSense" name_key="buffNecroDarkSenseName" description_key="buffNecroDarkSenseDesc" icon="ui_game_symbol_zombie" icon_color="150,0,255">
<stack_type value="replace"/>
<duration value="0"/>
<update_rate value="1"/>
<effect_group name="necro dark sense radar">
<triggered_effect trigger="onSelfBuffUpdate" action="SetNavObject" target="selfAOE" range="60" target_tags="zombie" nav_object="zombie" add="false"/>
<triggered_effect trigger="onSelfBuffUpdate" action="SetNavObject" target="selfAOE" range="30" target_tags="zombie" nav_object="zombie" add="true"/>
<triggered_effect trigger="onSelfBuffRemove" action="SetNavObject" target="selfAOE" range="60" target_tags="zombie" nav_object="zombie" add="false"/>
</effect_group>
</buff>
</append>
</config>