Files
necromants-tome-7d2d-3-2/Config/progression.xml
T
AlexCubeandClaude Opus 5 ed5fea5192 1.3.0: шкала 20 убийств за уровень, два индикатора, мир между подчинёнными
Уровень навыка игра хранит ОДНИМ БАЙТОМ (ProgressionValue.Write/Read), поэтому
старая шкала "одно убийство - один уровень" при max_level=5000 на каждом
сохранении откатывала уровень на 256 назад. Снаружи это выглядело как
самопроизвольно закрывающиеся рецепты (жалоба со стрима: Слёзы мертвеца открыты,
Пир падальщика под замком при 250+ убитых), а тиры 500/2000/3000/5000 были
недостижимы в принципе. Подтверждено на двух живых сейвах: sezon8 - 384 убийства
при уровне 129, test8 - 303 при уровне 48.

Шкала переведена на 20 убийств = 1 уровень, max_level=250 - влезает в байт с
запасом. Уровень больше не накапливается, а вычисляется из necroZombieKillsCVar
(float, сохраняется честно) и на каждом убийстве, и постфиксом на
PlayerDataFile.ToPlayer - последнее чинит старые сейвы само, без команд и новой
игры. Открытое при этом не теряется: в старой шкале уровень всегда был не больше
счётчика, так что пересчёт может только вернуть украденное переполнением.
Отдельно закрыт случай "счётчик пуст, а уровень есть" - счётчик восстанавливается
из уровня по старой шкале.

Все пороги пересчитаны в уровни, числа убийств не тронуты, кроме воды: 30 на
сетку шагом 20 не ложится, по указанию пользователя мод переехал на 20 - туда же,
где браслет и Кровавая сфера. Тег necroNecromancyLvl30 удалён. Сходимость всех 18
рецептов (тег -> RecipeTagUnlocked -> unlock_tier) проверена скриптом.

Два индикатора: череп в статус-баре показывает уровень (раньше - общее число
убийств), фиолетовая шкала над полосой опыта - продвижение внутри уровня, 0..20.
Шкала сделана вёрсткой, а не баффом (бафф умеет число, но не полосу), заполнение
привязано через XUi-выражение cvar(). Закрывающая скобка у выражения - одна "}",
а не "%}": лишний "%" NCalc читает как остаток от деления и ждёт правый операнд,
на чём первая проверка в игре и споткнулась.

Третья правка: подчинённые зомби больше не дерутся между собой. Девиация выдаёт
приказ "бей зомби", а подчинённый сам EntityZombie, и в targetClasses выражается
только тип. Постфикс на EAITarget.check вычёркивает подчинённого из кандидатов
(зомби выбирает следующего, настоящего врага), префикс на
EntityAlive.SetAttackTarget гасит цель на путях мимо выбора - прежде всего месть,
когда подчиняют уже дерущихся.

Тексты на 13 языках, README (RU+EN), описания для сайта и Nexus приведены к новой
шкале.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-17 23:24:25 +03:00

269 lines
25 KiB
XML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<config>
<!-- "Некромантия" (Necromancy) skill.
Reuses the vanilla crafting_skill mechanism (same class that powers e.g.
craftingHarvestingTools) parented to the vanilla "attCrafting" virtual
attribute — this makes it auto-level from a game action (not spent perk
points) and show up for free in the game's existing crafting-skills panel.
Originally this leveled from reading books (see git history / earlier
comments), but items.xml effect_group does NOT inherit through Extends in
this game version (confirmed by decompiling ItemClassesFromXml.parseItem -
it always parses effect_group from the item's own XML node, passing null as
the "parent node" argument to MinEffectController.ParseXml regardless of
Extends; entity_class does the equivalent WITH the parent node passed in,
which is why the zombie kill counter below always worked and reading books
never did). Patching every single one of the ~150 skill magazine/schematic
items individually to work around that was judged not worth it - so this
levels from zombie kills instead, via the same entityclasses.xml patch on
zombieTemplateMale that already drives necroZombieKillsCVar (see
entityclasses.xml) - one kill, one level, capped at max_level.
ШКАЛА ПЕРЕДЕЛАНА 2026-09-17: 20 УБИЙСТВ = 1 УРОВЕНЬ, max_level=250.
Было "одно убийство - один уровень" при max_level=5000, и это молча ломалось на КАЖДОМ
СОХРАНЕНИИ, потому что ванильный ProgressionValue хранит уровень ОДНИМ БАЙТОМ:
_writer.Write((byte)level); // ProgressionValue.Write
level = _reader.ReadByte(); // ProgressionValue.Read
Проверено не только декомпиляцией, но и на живом сейве (New Xisema Mountains/sezon8,
17.09.2026): necroZombieKillsCVar = 384, а уровень в файле игрока = 129, то есть 384-256.
Всё выше 255 откатывалось по модулю 256: панель скилла заново закрывала уже открытые
рецепты (именно это было видно на стриме - Слёзы мертвеца открыты, Пир падальщика под
замком), а группы 500/2000/3000/5000 были недостижимы в принципе. В ванили этот предел
не всплывает: атрибуты идут до 10, перки до 5, крафтовые скиллы до 100.
5000 убийств / 20 = 250 уровней - влезает в байт с запасом, поэтому баг чинится самой
шкалой, а не костылём поверх сериализации. Счёт убийств по-прежнему живёт в
necroZombieKillsCVar (float, сохраняется честно) и остаётся ЕДИНСТВЕННЫМ источником
правды: уровень пересчитывается из него в HarmonySrc/NecromancyKillCreditPatch.cs и на
каждом убийстве, и при загрузке игрока - последнее заодно чинит сейвы, испорченные старой
шкалой (тот же sezon8: 384 убийства -> уровень 19 вместо сломанных 129).
ВСЕ ПОРОГИ НИЖЕ - В УРОВНЯХ. Умножь на 20, чтобы получить убийства:
Group 1 "Адепт" - с нулевого уровня, но не вся: рецепты внутри группы
открываются каждый на своей ступени: 0 / 1 / 3 / 5 / 15,
то есть 0 / 20 / 60 / 100 / 300 убийств.
Group 2 "Подмастерье" - уровень 25 (500 убийств)
Group 3 "Ученик" - уровень 100 (2000)
Group 4 "Некромант" - уровень 150 (3000)
Group 5 "Мастер" - уровень 250 (5000)
Recipe unlocks: give a recipe Tags="necroNecromancyAdept" / "necroNecromancyJourneyman"
/ "necroNecromancyApprentice" / "necroNecromancyNecromancer" / "necroNecromancyMaster"
(whichever group it belongs to) and it will unlock automatically once the
player crosses that group's threshold - no further change needed here.
thrownStoneSpirit (Камень духов) carries none of these tags at all, since Group 1
"Адепт" is unlocked from level 1 anyway - a recipe with no unlock tag is just always
available, same effect, no need to spend a tag on it.
One genuine one-off exception: necroNecromancyLvl20 - the Пространственный браслет, per
direct instruction 2026-08-30 ("нож, камень духов и хранилище - это база... хранилище,
когда убито минимум 20 зомби") - it belongs in the Group 1 "Адепт" display bucket (see
below) but needs its own slightly-later unlock level within that same group, which is
what unlock_tier is for (see display_entry below), not a reason to invent a whole
separate group. Имя тега - в УБИЙСТВАХ (20), а RecipeTagUnlocked у него теперь стоит на
УРОВНЕ 1: это те же самые 20 убийств в новой шкале. С 2026-09-17 на этом же теге сидят
Кровавая сфера и мод ножа на воду (Слёзы мертвеца) - три рецепта одной ступени.
UPDATED 2026-08-29: display_entry blocks added below now that real necromancer items/
recipes exist to point them at - per the user's own direct report ("до сих пор нету ни
одного рецепта" in the skill panel).
UPDATED 2026-08-30 (user report: "превью пустые" - the group icons in the skill panel
were blank). Root cause: `icon=` on display_entry needs a real ICON ATLAS SPRITE NAME
(the same string you'd give CustomIcon), not the item's own internal id - fixed by
pointing icon= at each group's actual CustomIcon sprite value.
REVERTED-AND-REDISTRIBUTED AGAIN 2026-08-30, two direct corrections in a row:
1. "Что ещё за 'уровень'? Была же система! А рецепту можно без группы задать уровень
скилла на котором он откроется." - an earlier edit had invented ad-hoc "Level
20/50/200" display groups, abandoning the real 5-tier flavor-name system this
comment already documents above (Адепт/Подмастерье/Ученик/Некромант/Мастер -
craftingNecroNecromancyTier1-5Name, all already localized, unused since). A recipe's
actual unlock level is controlled entirely by its own `tags=` + the
RecipeTagUnlocked effect_group below - display_entry is PURELY the skill-panel
preview widget, the two don't need to be 1:1. Confirmed against real vanilla
precedent (Data/Config/progression.xml's own explosives group): one display_entry
can list several items that reveal at different levels via a comma-list
unlock_level plus multiple unlock_entry elements at increasing unlock_tier.
2. "У тебя получается 5 групп, как и должно было быть. А вот уровень скилла ты
распределил идиотски." - the fix for #1 above had (wrongly) folded EVERYTHING except
the black stone into Group 1 alone (levels 1/20/50/200), leaving the three real
mid-game tiers (Подмастерье@500/Ученик@2000/Некромант@3000) completely empty and
unused - defeating the entire point of having 5 groups spread across the level
range. Fixed by actually spreading the non-base items across all 5 real tiers
instead of clustering them all near the bottom - see the effect_group tags below and
recipes.xml for the final per-item distribution:
Group 1 "Адепт" (ур. 0 = 0 убийств) - Spirit Stone, Knife, Blue Portal
Stone, Pyramid of Spirits; на ур. 1 (20 убийств) -
Spatial Vault, Blood Sphere и мод на воду; дальше три
выживальческих мода ножа на ур. 3 / 5 / 15
(60 / 100 / 300 убийств)
Group 2 "Подмастерье" (ур. 25 = 500) - Grimoire of Deviation, plus the two
combat knife mods на ур. 70 / 85 (1400 / 1700 убийств)
Group 3 "Ученик" (ур. 100 = 2000) - Zombie Dog, Insect Swarm, Zombie Griffin
Group 4 "Некромант" (ур. 150 = 3000) - Zombie Bear, Zombie Wolf, Banshee Scroll
Group 5 "Мастер" (ур. 250 = 5000) - Black Portal Stone
necroNecromancyLvl50/Lvl200 tags removed (no longer used by anything - Grimoire and
the summon books now use the real necroNecromancyJourneyman/Apprentice/Necromancer
tags instead). necroNecromancyLvl30 убран 2026-09-17 вместе с переходом на шкалу
20-за-уровень: 30 убийств на сетке с шагом 20 не выражается, и мод на воду переехал
на necroNecromancyLvl20 - тот самый тег браслета, по прямому указанию ("20
убийств"). necroNecromancyLvl20 stays - still the one legitimate one-off (see
above). -->
<append xpath="/progression/crafting_skills">
<crafting_skill name="craftingNecroNecromancy" max_level="250" parent="attCrafting" name_key="craftingNecroNecromancyName" desc_key="craftingNecroNecromancyDesc" long_desc_key="craftingNecroNecromancyLongDesc" icon="ui_game_symbol_skull">
<!-- display_entry block kept in the SAME append as the crafting_skill itself (unlike
an earlier draft of this edit, which tried appending display_entry via a second,
separate xpath targeting crafting_skill[@name='craftingNecroNecromancy'] BEFORE
this element even exists in load order - same class of bug that already broke
loot.xml once, see that file's own load-order comment. Nesting it here avoids the
ordering question entirely, and matches how vanilla itself writes display_entry -
directly inside the crafting_skill tag, not as a separate append). -->
<display_entry icon="SpiritStone" name_key="craftingNecroNecromancyTier1Name" has_quality="false" unlock_level="0,1,3,5,15">
<!-- necroHeresyPyramid added here 2026-08-31 (user request: "добавь рецепт блока в
скиллы") - unlock_tier="1" alongside the other always-available Tier-1 items,
matching its recipe's own necroNecromancyAdept tag in recipes.xml (see that
file's comment - both express the same "available immediately" intent). -->
<unlock_entry item="thrownStoneSpirit,necroWpnBladeNecroKnife,thrownStonePortalBlue,necroHeresyPyramid" unlock_tier="1"/>
<!-- resourceBloodSphere ПЕРЕЕХАЛА СЮДА ИЗ tier 1, 2026-09-16.
Сначала (15.09) она стояла на tier 1 по указанию «доступна на первом грейде»,
то есть с уровня 1. Но вставляется она в Пространственный браслет, а тот
открывается на tier 2 (уровень 20) - ровно этой же строкой. Девятнадцать
уровней игрок мог крафтить расходник к предмету, которого у него нет.
По записанному правилу мода «пороги - по нужде, а не по силе» порог ставится
туда, где предмет впервые нужен, - здесь это момент появления браслета.
Парная правка в recipes.xml: рецепту добавлены теги learnable и
necroNecromancyLvl20. Оба уровня ОБЯЗАНЫ совпадать: RecipeTagUnlocked для
necroNecromancyLvl20 стоит на level="1,250" ниже, а tier 2 в
unlock_level="0,1,3,5,15" - это тот же уровень 1. Разойдись они, замок на
панели скилла разошёлся бы с реальной доступностью рецепта.
2026-09-17, СМЕНА ШКАЛЫ (20 убийств = 1 уровень, см. шапку файла): «уровень
20» превратился в «уровень 1», убийств за ним по-прежнему 20. Сюда же, на
эту ступень, переехал мод ножа на воду - см. следующий комментарий. -->
<unlock_entry item="braceletSpatialVault,resourceBloodSphere,necroModKnifeTearsOfTheDead" unlock_tier="2"/>
<!-- ЧЕТЫРЕ МОДА НОЖА ПЕРЕЕХАЛИ СЮДА ИЗ ГРУППЫ 2, 2026-09-09. Продиктовано:
"Питьё важно в тот же день. Оно должно быть доступно после 30 убитых зомби.
Еда - 60. Это самые важные для начала выживания модификации. Модификация на
покой - 100 зомби. Модификацию на тёмное чутьё я бы сделал доступной после
300 убитых зомби."
ЧТО БЫЛО СЛОМАНО. Правка 2026-09-07 разложила все шесть модов ножа внутри
группы 2, то есть в диапазоне 500-1700 убийств. Это ошибка баланса, а не
кода (сама тройка "тег рецепта + RecipeTagUnlocked + unlock_tier" сходилась
по всем шести): голод и жажда - проблема ПЕРВЫХ ДНЕЙ, а к 500 убийствам у
игрока давно есть ферма, костёр, банки и фильтр, и +2 воды с трупа ему уже
не нужны. Два самых "выживальческих" мода открывались ровно тогда, когда
переставали быть нужны - мёртвый контент. Сам нож лежит в группе 1 и
доступен с уровня 1, поэтому моды на воду, еду и тепло теперь идут сразу за
ним, в той же группе.
Ступени группы 1 после правки: 1 - база, 20 - браслет, 30 - вода, 60 - еда,
100 - покой, 300 - чутьё. Все шесть порогов ниже 500, то есть на группу 2
они не заезжают. Хватка мертвеца (1400) и Мёртвая буря (1700) остались в
группе 2 - это чисто боевые моды, ранний доступ им не нужен ("дальше уже не
так принципиально").
Порядок вода -> еда не случаен и задан пользователем прямо: пить хочется в
тот же день, есть - позже.
ПЕРЕСЧИТАНО 2026-09-17 ПОД ШКАЛУ 20-ЗА-УРОВЕНЬ (см. шапку файла). Сами
пороги в убийствах не тронуты, кроме воды: 30 убийств на сетке с шагом 20 не
выражается, и по прямому указанию вода уехала ВНИЗ, на 20 - то есть на одну
ступень с браслетом и Кровавой сферой, а не вверх на 40. Ступеней в группе
теперь пять, а не шесть, и мод на воду стоит в строке tier 2 выше:
ур. 0 (0 убийств) - Камень духов, Нож, Синий портал, Пирамида
ур. 1 (20 убийств) - Браслет, Кровавая сфера, ВОДА
ур. 3 (60 убийств) - еда
ур. 5 (100 убийств) - покой
ур. 15 (300 убийств) - чутьё
Тег necroNecromancyLvl30 вместе с этим удалён - им больше никто не
пользуется, вода сидит на necroNecromancyLvl20. -->
<unlock_entry item="necroModKnifeScavengersFeast" unlock_tier="3"/>
<unlock_entry item="necroModKnifeGravesRepose" unlock_tier="4"/>
<unlock_entry item="necroModKnifeDarkSense" unlock_tier="5"/>
</display_entry>
<!-- Ступенчатая разблокировка внутри группы. ПЕРЕРАСПРЕДЕЛЕНО 2026-09-09: четыре из
шести модов ножа (вода/еда/покой/чутьё) уехали отсюда в группу 1 - см. большой
комментарий там. Здесь остались Гримуар и два боевых мода ножа.
КАК ЭТО ЧИТАЕТСЯ (разобрано по декомпиляции, потому что семантика неочевидная):
unlock_level - это список порогов (ProgressionClass.QualityStarts), а unlock_tier
в XML 1-based, при разборе из него вычитается единица
(ProgressionFromXml.cs:396 - "ParseSInt32(...) - 1"). Дальше
GetUnlockItemLocked = GetQualityLevel(level) <= UnlockTier
где GetQualityLevel возвращает индекс первого порога, который БОЛЬШЕ текущего
уровня. В сумме это даёт простое правило: запись с unlock_tier="N" выходит
из-под замка ровно на N-м значении unlock_level, считая с единицы. Поэтому здесь
tier 1 -> ур. 25, tier 2 -> ур. 70, tier 3 -> ур. 85 (500 / 1400 / 1700
убийств), а в группе 1 выше - tier 1 -> ур. 0, tier 2 -> ур. 1, tier 3 -> ур. 3,
tier 4 -> ур. 5, tier 5 -> ур. 15 (0 / 20 / 60 / 100 / 300 убийств).
Нижняя граница ноль - законна и проверена по коду: у группы 1 unlock_level
начинается с 0, и GetQualityLevel(0) возвращает 1 (первый порог БОЛЬШЕ нуля -
это единица), поэтому tier 1 (после -1 это 0) из-под замка выходит сразу. Точно
так же законен level="0,250" у RecipeTagUnlocked ниже: PassiveEffect.InLevelRange
это ровно "_level >= _min && _level <= _max".
Заблокированная запись рисуется греем из АТЛАСА ItemIconAtlasGreyscale плюс
спрайт-замок ui_game_symbol_unlock поверх (XUi_InGame/windows.xml ~2523-2524,
привязки unlock_icon_atlasN / unlock_icon_lockedN). Именно поэтому у мода теперь
есть вторая папка UIAtlases/ItemIconAtlasGreyscale - без неё под замком у иконки
не было бы картинки вообще. -->
<display_entry icon="ScrollOfDeviation" name_key="craftingNecroNecromancyTier2Name" has_quality="false" unlock_level="25,70,85">
<unlock_entry item="thrownBookGrimoireDeviation" unlock_tier="1"/>
<unlock_entry item="necroModKnifeDeadMansGrip" unlock_tier="2"/>
<unlock_entry item="necroModKnifeDeadStorm" unlock_tier="3"/>
</display_entry>
<display_entry icon="SummonZombieDog" name_key="craftingNecroNecromancyTier3Name" has_quality="false" unlock_level="100">
<!-- resourceBloodStone добавлен 2026-09-16 (указание «доступен на третьем грейде
некромантии, в самом его начале»). Тир 3 - это и есть третий грейд, группа
«Ученик», а его единственный unlock_level равен 100 уровням (это те же 2000
убийств в шкале 20-за-уровень), то есть unlock_tier="1" здесь и означает «в
самом начале грейда, без своего смещения».
ЭТА СТРОКА - ТОЛЬКО ОТОБРАЖЕНИЕ. Открытие рецепта делает тег
necroNecromancyApprentice в recipes.xml вместе с RecipeTagUnlocked
level="100,250" ниже; display_entry о тегах не знает и рисует замок сам по
себе, по unlock_tier. Поэтому 100 здесь и 100 там ОБЯЗАНЫ совпадать -
иначе замок на панели разойдётся с реальной доступностью рецепта. Совпадают:
один порог, одно значение. -->
<unlock_entry item="bookSummonZombieDog,bookSummonInsectSwarm,bookSummonZombieGriffin,resourceBloodStone" unlock_tier="1"/>
</display_entry>
<display_entry icon="SummonZombieBear" name_key="craftingNecroNecromancyTier4Name" has_quality="false" unlock_level="150">
<unlock_entry item="bookSummonZombieBear,bookSummonZombieWolf,bookBanshee" unlock_tier="1"/>
</display_entry>
<display_entry icon="BlackPortalStone" name_key="craftingNecroNecromancyTier5Name" has_quality="false" unlock_level="250">
<unlock_entry item="thrownStonePortalBlack" unlock_tier="1"/>
</display_entry>
<effect_group>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="0,250" value="1" tags="necroNecromancyAdept"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="1,250" value="1" tags="necroNecromancyLvl20"/>
<!-- Пороги модов ножа, 2026-09-09 (см. комментарий в recipes.xml и в группе 1
выше). Каждый уровень тут ОБЯЗАН совпадать с соответствующим значением в
unlock_level того display_entry, где лежит мод, иначе замок на панели скилла
разойдётся с реальной доступностью рецепта: display_entry рисует замок сам по
себе, по unlock_tier, и о тегах не знает.
Lvl800 и Lvl1100 удалены вместе с этой правкой - ими больше никто не
пользуется (Тёмное чутьё уехало на 300, Могильный покой на 100).
ЗНАЧЕНИЯ level= ПЕРЕСЧИТАНЫ 2026-09-17 В УРОВНИ (шкала 20 убийств = 1
уровень, см. шапку файла). ИМЕНА ТЕГОВ ОСТАЛИСЬ В УБИЙСТВАХ и менять их не
надо: necroNecromancyLvl60 - это «60 убийств», а стоит он на level="3,250".
Верхняя граница у всех теперь 250, а не 5000 - это max_level скилла, выше
него уровень не поднимется, и диапазон обязан его накрывать. -->
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="3,250" value="1" tags="necroNecromancyLvl60"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="5,250" value="1" tags="necroNecromancyLvl100"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="15,250" value="1" tags="necroNecromancyLvl300"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="25,250" value="1" tags="necroNecromancyJourneyman"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="70,250" value="1" tags="necroNecromancyLvl1400"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="85,250" value="1" tags="necroNecromancyLvl1700"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="100,250" value="1" tags="necroNecromancyApprentice"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="150,250" value="1" tags="necroNecromancyNecromancer"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="250,250" value="1" tags="necroNecromancyMaster"/>
</effect_group>
</crafting_skill>
</append>
</config>