Откат крови и Кровавая сфера вместо неё
Чинит поломку сейвов, внесённую предыдущим коммитомe362c62, и заменяет её механику на безопасную. ЧТО БЫЛО СЛОМАНО.e362c62перенёс resourceNecromancerBlood из items.xml в item_modifiers.xml, чтобы кровь вставлялась в Пространственный браслет. Класс предмета определяет БАЙТОВУЮ РАСКЛАДКУ каждого его стака в сейве: ItemValue.Read строка 1094: if ((version > 4 || HasQuality) && !(itemClass is ItemClassModifier)) ItemValue.Write строка 1228: if (!(ItemClass is ItemClassModifier)) Обычный предмет пишет байт числа модификаций и байт числа косметических слотов; ItemClassModifier не пишет ни того, ни другого. Сейв, записанный до переноса, после переноса читается со сдвигом - поток съезжает на первом же стаке крови, и PlayerDataFile.Load падает. Бэкап .ttp.bak умирает вместе с основным файлом: он того же формата. В тестовом мире персонаж потерян вместе с бэкапом, игра откатилась на Respawning: NewGame. В коммитеe362c62написано "сейв цел" со ссылкой на assignIdsFromMapping. Айди действительно берутся из сохранённого name->id мэппинга - но ломается не айди, а раскладка байтов, и к мэппингу это отношения не имеет. Была проверена не та вещь. ОТКАТ. Кровь вернулась в items.xml обычным <item>. Набор её свойств сверен с7172681и совпадает посимвольно: убраны и Stacknumber=1, и прочность (ShowQuality + DegradationBreaksAfter + effect_group DegradationMax). Обе правки были безвредны для сейва, но существовали ради отменённой механики - по указанию пользователя откат доведён до "как было", а не до "как было плюс безвредное". Стак снова 15, наследуется от medicalBloodBag. В комментарии у предмета оставлено предупреждение с номерами строк Read/Write - единственное, что вынесено из аварии, и единственное, ради чего стоит читать этот комментарий целиком. КРОВАВАЯ СФЕРА. Расходник браслета теперь отдельный предмет, и это ровно то, что делает правку безопасной: resourceBloodSphere - НОВОЕ имя, в старых сейвах его нет, значит нет и ни одного стака, который читался бы по другой раскладке. Общее правило, выведенное из аварии: предмет, который уже мог попасть в чужой инвентарь, нельзя переводить между ItemClass и ItemClassModifier ни в какую сторону - нужна модификация, заводи новый предмет. Продиктовано: доступна на первом грейде, без станка, 1 кровь некроманта + 5 праха зомби дают две сферы, прочность 500. В руке - камень с алым тинтом. - item_modifiers.xml: installable_tags="necroBracelet", свой modifier_tags, type="attachment", DegradationMax 500 в tiered="false" группе. Extends=modGeneralMaster, а НЕ resourceRockSmall: вместе с камнем приезжали бы Action0 ThrowAway и ThrowableDecoy, то есть сферу можно было бы метать. Меш берётся строкой Meshfile, наследовать ради него весь предмет не нужно. - Вид в руке: HoldType 40 и все три меша (Meshfile/HandMeshfile/DropMeshfile) на rock_smallPrefab плюс TintColor "220, 30, 45". Путь проверенный - на этом же меше с таким же тинтом в моде живут Камень духов и оба портальных камня. - recipes.xml: count=2, без craft_area и без тега разблокировки (группа 1 открыта с уровня 1, рецепт без тега доступен всегда - как у Камня духов). - progression.xml: сфера в unlock_entry группы 1, unlock_tier="1". - SpatialVaultPickupPatch: SpendBlood -> SpendCharge, имя из новой константы ChargeItemName. Гейт пустого слота в Begin снова включён - после отката он был временно закомментирован, потому что вставлять было нечего. ИКОНКИ. Свои, от пользователя: BloodSphere.png и BloodStone.png, 160x160 RGBA, в ItemIconAtlas и ItemIconAtlasGreyscale. Серая копия обязательна - без неё у заблокированной записи в скиллах не будет картинки вообще. Способ её получения подобран сверкой с существующими файлами и совпал ПОБИТОВО: convert('L') на RGB без альфы, альфа приклеивается обратно отдельным каналом. Записано в BACKLOG.md, раньше это нигде не было зафиксировано. Иконка камня положена заранее - самого предмета ещё нет, он запланирован. ПРОВЕРЕНО, ЧТО НИЧЕГО БОЛЬШЕ НЕ ЕДЕТ. Сверка с7172681: набор <item> в items.xml не изменился, в item_modifiers.xml единственное добавление - resourceBloodSphere, и ни у одного существующего предмета не менялись Tags, ShowQuality и Stacknumber. То есть ни один предмет не сменил класс и не сменил раскладку. В игре: мир грузится без ошибок, сфера крафтится и тратится - в логе "sphere in slot 0 now 12/500 used" за 12-секундный канал. --- Revert the blood, and a Blood Sphere in its place Fixes the save corruption introduced bye362c62and replaces the mechanic behind it with a safe one. WHAT WAS BROKEN.e362c62moved resourceNecromancerBlood from items.xml into item_modifiers.xml so it could be installed in the Spatial Bracelet. An item's CLASS decides the BYTE LAYOUT of every stack of it in a save: ItemValue.Read line 1094: if ((version > 4 || HasQuality) && !(itemClass is ItemClassModifier)) ItemValue.Write line 1228: if (!(ItemClass is ItemClassModifier)) A plain item writes a modifications count and a cosmetic-slot count; an ItemClassModifier writes neither. A save written before the move reads out of step after it - the stream slips on the first blood stack and PlayerDataFile.Load throws. The .ttp.bak backup dies with the main file, being the same format. In the test world the character was lost along with its backup and the game fell back to Respawning: NewGame. e362c62's message claimed "saves are safe", citing assignIdsFromMapping. Item ids really do come from the stored name->id mapping - but what breaks is not the id, it is the byte layout, and the mapping has nothing to do with it. The wrong thing was verified. THE REVERT. The blood is a plain <item> in items.xml again. Its property set was diffed against7172681and matches character for character: both Stacknumber=1 and the durability (ShowQuality + DegradationBreaksAfter + the DegradationMax effect_group) are gone. Both were harmless to the save format, but both existed only to serve the cancelled mechanic - on the user's instruction the revert goes back to "as it was", not "as it was plus whatever I judged harmless". The stack is 15 again, inherited from medicalBloodBag. A warning carrying the Read/Write line numbers stays in the item's comment - the one thing worth keeping out of this accident. THE BLOOD SPHERE. The bracelet's charge is its own item now, and that is precisely what makes this safe: resourceBloodSphere is a NEW name, absent from every existing save, so no stack of it can be read under the wrong layout. The general rule the accident produced: an item that may already sit in someone's inventory must never be moved between ItemClass and ItemClassModifier in either direction - if a modifier is wanted, make a new item. Dictated: available at the first grade, no workstation, 1 Necromancer's Blood + 5 zombie ash makes two spheres, durability 500. Held, it is a stone with a scarlet tint. - item_modifiers.xml: installable_tags="necroBracelet", its own modifier_tags, type="attachment", DegradationMax 500 in a tiered="false" group. Extends=modGeneralMaster, NOT resourceRockSmall: the rock would have brought Action0 ThrowAway and ThrowableDecoy with it, making the sphere throwable. The mesh comes from the Meshfile line; inheriting a whole item for it is not needed. - Held look: HoldType 40 and all three meshes (Meshfile/HandMeshfile/ DropMeshfile) on rock_smallPrefab, plus TintColor "220, 30, 45". A proven path - the Spirit Stone and both portal stones already live on that mesh with that same kind of tint. - recipes.xml: count=2, no craft_area and no unlock tag (group 1 opens at level 1, and a recipe with no tag is simply always available, as with the Spirit Stone). - progression.xml: the sphere joins group 1's unlock_entry at unlock_tier="1". - SpatialVaultPickupPatch: SpendBlood -> SpendCharge, the name coming from a new ChargeItemName constant. The empty-slot gate in Begin is switched back on - it was commented out during the revert because nothing could be installed. ICONS. The user's own art: BloodSphere.png and BloodStone.png, 160x160 RGBA, in both ItemIconAtlas and ItemIconAtlasGreyscale. The greyscale copy is mandatory - without it a locked skill entry has no picture at all. How those copies are made was worked out by diffing against the existing files and matched BIT FOR BIT: convert('L') over RGB without the alpha, with the alpha merged back as its own channel. Written up in BACKLOG.md; it had never been recorded anywhere. The stone's icon is filed ahead of the item, which is still only planned. VERIFIED THAT NOTHING ELSE SHIFTS. Diffed against7172681: the set of <item> entries in items.xml is unchanged, the only addition to item_modifiers.xml is resourceBloodSphere, and no existing item had its Tags, ShowQuality or Stacknumber changed. No item changed class, and no item changed layout. In game: the world loads clean, and the sphere crafts and drains - the log shows "sphere in slot 0 now 12/500 used" for a 12-second channel. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MnwP2Dt1vk8bUPJ452EoVL
This commit is contained in:
co-authored by
Claude Opus 5
parent
e362c627e7
commit
896501dcd8
File diff suppressed because one or more lines are too long
+80
-99
@@ -325,121 +325,102 @@
|
||||
|
||||
</append>
|
||||
|
||||
<!-- "Кровь некроманта" (Necromancer's Blood) - ПЕРЕЕХАЛА СЮДА ИЗ items.xml 2026-09-15.
|
||||
<!-- "Кровавая сфера" (Blood Sphere) - расходный заряд Пространственного браслета.
|
||||
Название и рецепт продиктованы 2026-09-15: «Кровавая сфера. Доступна на первом грейде.
|
||||
Станки не нужны. Ингридиенты: Кровь некроманта, 5 праха зомби. По одному рецепту
|
||||
изготавливается две сферы. Прочность сферы 500.»
|
||||
|
||||
Указание: «пусть флакон крови можно будет вставлять как модификацию для пространственного
|
||||
хранилища» (уточнено: именно Крови некроманта).
|
||||
ЗАЧЕМ ОНА ВООБЩЕ СУЩЕСТВУЕТ - и почему это НЕ «кровь, вставляемая в браслет». 15.09.2026
|
||||
кровь перенесли в этот файл, чтобы она вставлялась в браслет, и это уничтожило персонажа в
|
||||
сейве вместе с бэкапом: класс предмета определяет байтовую раскладку его стака
|
||||
(ItemValue.Read:1094 / Write:1228 - обычный предмет пишет байт числа модификаций,
|
||||
ItemClassModifier не пишет), и старый сейв стал нечитаемым. Полный разбор - в BACKLOG.md и
|
||||
в большом предупреждении у крови в items.xml.
|
||||
|
||||
ПОЧЕМУ ПЕРЕЕЗД, А НЕ НОВОЕ СВОЙСТВО. XUiC_ItemPartStack.CanSwap начинается с
|
||||
Сфера обходит это тем, что она НОВЫЙ предмет: в старых сейвах её нет, значит нет и ни
|
||||
одного стака, который читался бы по другой раскладке. Это общее правило, а не уловка:
|
||||
нужна модификация - заводи новый предмет, никогда не переводи существующий.
|
||||
|
||||
if (!(stack.itemValue.ItemClass is ItemClassModifier itemClassModifier)) return false;
|
||||
|
||||
Слот модификации не смотрит ни на теги, ни на свойства, пока предмет не ItemClassModifier -
|
||||
а этот класс создаётся только из <item_modifier>. Свойства вида "CanBeInstalled" в игре нет;
|
||||
остаться ресурсом в items.xml и при этом вставляться в браслет физически нельзя.
|
||||
|
||||
ЧТО ПРИ ЭТОМ НЕ СЛОМАЛОСЬ - проверено по декомпилятору, а не понадеялись:
|
||||
- Рецепты. ItemClassModifier наследует ItemClass, а имена регистрируются в ОДНОМ общем
|
||||
ItemClass.nameToItem (ItemClass.cs:624). Recipe ищет ингредиент через
|
||||
ItemClass.GetItemClass, то есть по тому же словарю - все четыре рецепта на крови
|
||||
(10 на Чёрный портал, 3 на Пирамиду, два по одной) резолвятся как раньше.
|
||||
- Свой рецепт самой крови и Harmony-патч NecromancerBloodPatch.cs - оба по имени
|
||||
"resourceNecromancerBlood", имя не менялось.
|
||||
- Сейв. Айди предметов раздаются при загрузке, но ItemClass.assignIdsFromMapping берёт их
|
||||
из сохранённого в мире name->id мэппинга (nameIdMapping.GetIdForName), так что предмет в
|
||||
старом сейве не превратится в другой из-за перестановки в конфигах.
|
||||
- Локализация. Ключи "resourceNecromancerBlood"/"...Desc" те же, файл не трогали.
|
||||
|
||||
ЧЕГО НЕТ - у этой модификации пока НЕТ НИ ОДНОГО ЭФФЕКТА ДЛЯ БРАСЛЕТА, и это не
|
||||
забывчивость: указание было про "вставлять", а что именно она даёт - не сказано.
|
||||
effect_group ниже существует ради прочности самого флакона, а не ради браслета.
|
||||
|
||||
ЛОВУШКА, КОТОРУЮ ПРИШЛОСЬ ОБОЙТИ: у модификации effect_group применяется К ПРЕДМЕТУ, В
|
||||
КОТОРЫЙ ЕЁ ВСТАВИЛИ. Прочность 1000 (сделана 2026-09-15, разбор в BACKLOG.md) живёт именно
|
||||
в effect_group - и, переехав сюда, она стала бы выдавать 1000 прочности БРАСЛЕТУ. Поэтому
|
||||
пассивка гейтована tags="necroBloodFlask", а сам тег добавлен в Tags флакона:
|
||||
ItemValue.MaxUseTimesBase зовёт GetValue с ItemClass.ItemTags того предмета, для которого
|
||||
считает, так что у флакона совпадение есть, а у браслета
|
||||
(T0,weapon,attPerception,noMods,necroBracelet) - нет. Тот же механизм, что у "Мёртвой бури"
|
||||
с tags="secondary", только фильтр не по действию, а по предмету.
|
||||
|
||||
installable_tags="necroBracelet" - положительная половина, ровно как требует комментарий к
|
||||
Tags браслета в items.xml: без неё модификация лезла бы в ЛЮБОЙ предмет
|
||||
(CanSwap короткозамыкается на InstallableTags.IsEmpty).
|
||||
blocked_tags НЕ задан намеренно: у браслета в тегах есть "noMods", и объявить его здесь
|
||||
значило бы заблокировать самому себе установку.
|
||||
modifier_tags="necroBraceletBlood" - свой, ни с чем не пересекающийся: моды с общим
|
||||
modifier_tags считаются против MaxModsAllowed (по умолчанию 1). Слот у браслета с
|
||||
2026-09-15 всего один, так что слотов это больше не съедает - но тег всё равно должен быть
|
||||
свой: общий сделал бы две модификации браслета взаимоисключающими и там, где интерфейс
|
||||
этого никак не объясняет.
|
||||
type="attachment" - флакон можно вынуть обратно; type="mod" вставляется навсегда. -->
|
||||
Атрибуты - по образцу модов ножа:
|
||||
installable_tags="necroBracelet" - иначе модификация лезла бы в ЛЮБОЙ предмет
|
||||
(CanSwap короткозамыкается на InstallableTags.IsEmpty);
|
||||
modifier_tags="necroBraceletSphere" - свой, чтобы не конкурировать с будущим Кровавым
|
||||
камнем через MaxModsAllowed;
|
||||
blocked_tags НЕ задан - у браслета в тегах "noMods", объявить его тут значило бы
|
||||
заблокировать самому себе установку;
|
||||
type="attachment" - сферу можно вынуть обратно. -->
|
||||
<append xpath="/item_modifiers">
|
||||
<item_modifier name="resourceNecromancerBlood" installable_tags="necroBracelet" modifier_tags="necroBraceletBlood" type="attachment">
|
||||
<property name="Extends" value="medicalBloodBag"/>
|
||||
<property name="DescriptionKey" value="resourceNecromancerBloodDesc"/>
|
||||
<!-- Своя рисованная иконка, 2026-08-30. CustomIcon задаётся явно даже при Extends -
|
||||
тот же урок, что у всех предметов мода: Extends сам по себе рабочей иконки не даёт. -->
|
||||
<property name="CustomIcon" value="NecromantsBlood"/>
|
||||
<item_modifier name="resourceBloodSphere" installable_tags="necroBracelet" modifier_tags="necroBraceletSphere" type="attachment">
|
||||
<!-- Extends на modGeneralMaster - та же база, что у шести модов ножа: даёт Group
|
||||
"Mods", звуки mod_grab/mod_place, Stacknumber 1 и CreativeMode None.
|
||||
param1="CustomIcon" исключает наследование родительского missingIcon.
|
||||
|
||||
<!-- Своя банка с кровью, 2026-09-10 (указание: «берём чай из золотарника, и жёлтое
|
||||
заменяем на кровавый цвет, с фиолетовыми оттенками»). Заодно чинилось расхождение
|
||||
текста и модели: описание говорит «Банка, наполненная кровью», а наследуемый
|
||||
medicalBloodBag показывал sackPrefab - обычный мешок.
|
||||
РАНЬШЕ ЗДЕСЬ СТОЯЛ Extends="resourceRockSmall", И ЭТО БЫЛА ОШИБКА. Камень нужен
|
||||
был только ради вида, а вместе с ним приезжали Action0 Class="ThrowAway",
|
||||
ThrowableDecoy="true" и DistractionTags - то есть сферу можно было бы метать как
|
||||
отвлекающий камень. Меш берётся строкой Meshfile ниже; наследовать ради него
|
||||
весь предмет не нужно. -->
|
||||
<property name="Extends" value="modGeneralMaster" param1="CustomIcon"/>
|
||||
<property name="DescriptionKey" value="resourceBloodSphereDesc"/>
|
||||
|
||||
ПОЧЕМУ НЕ ХВАТИЛО ТИНТА - ПРОВЕРЕНО В ИГРЕ. Сначала пробовали дёшево, без бандла:
|
||||
ванильный префаб чая плюс TintColor. Банка осталась чаем из золотарника - тинт
|
||||
предмета на этот меш НЕ ПОДЕЙСТВОВАЛ ВООБЩЕ: у шейдера Game_EntityTintMaskSSS
|
||||
выигрывает собственный _Color материала. Поэтому TintColor здесь не задаётся
|
||||
совсем - он ничего не даёт и только вводил бы в заблуждение.
|
||||
<!-- В РУКЕ - КАМЕНЬ С АЛЫМ ТИНТОМ (указание 2026-09-15: «в руке и сфера и кровавый
|
||||
камень пусть будут как камень с алым тинтом»).
|
||||
|
||||
И по сути: кровь отличается от чая не цветом, а тем, что она непрозрачная, тёмная
|
||||
и густая, с плёнкой на стекле. Жидкость ПЕРЕРИСОВАНА по яркости, а не перекрашена
|
||||
множителем - генератор _private/tools/make_necroblood_textures.py.
|
||||
Это уже проверенный в этом моде путь, а не догадка: Камень духов, Синий и Чёрный
|
||||
порталы (items.xml) сидят ровно на этом меше с ровно таким тинтом и в игре
|
||||
работают - зелёный, синий и чёрный камни соответственно. Поэтому взят их набор
|
||||
целиком: HoldType 40 плюс ВСЕ ТРИ меша. Три, а не один, потому что это три разные
|
||||
ситуации - Meshfile общий, HandMeshfile в руке, DropMeshfile лежащим на земле, и
|
||||
у тех трёх предметов они выписаны явно именно поэтому.
|
||||
|
||||
HoldType 3 - хват банки вместо 45 (мешок), Material Mglass - стекло вместо ткани
|
||||
(звук удара и осколки при разбитии). -->
|
||||
<property name="Meshfile" value="#@modfolder(NecromancerTome):Resources/necroblood?necroBloodPrefab.prefab"/>
|
||||
<property name="HoldType" value="3"/>
|
||||
<property name="Material" value="Mglass"/>
|
||||
TintColor - ТРИПЛЕТ "R, G, B", а НЕ hex. Это другая ручка, чем CustomIconTint
|
||||
выше (там hex): ItemClass парсит их разными путями - Color32 через запятые против
|
||||
ParseHexColor. Перепутать легко, и на модах ножа это уже стоило круга.
|
||||
|
||||
<!-- Теги medical/medicalSkill - те же, что давал medicalBloodBag через Extends;
|
||||
перечислены явно, потому что своя строка Tags наследуемую перекрывает целиком.
|
||||
necroBloodFlask добавлен ради гейта прочности - см. большой комментарий выше.
|
||||
Внимание на будущее: ItemClassModifier ПЕРЕОПРЕДЕЛЯЕТ HasAnyTags на ModifierTags,
|
||||
так что на вопрос "есть ли у этого предмета тег X" у модификации отвечают
|
||||
modifier_tags, а не эта строка. Здешние теги читает только то, что лезет в
|
||||
ItemTags напрямую - как раз GetValue с прочностью. -->
|
||||
<property name="Tags" value="medical,medicalSkill,necroBloodFlask"/>
|
||||
ПОЧЕМУ ЗДЕСЬ ТИНТ РАБОТАЕТ, А НА БАНКЕ КРОВИ НЕ СРАБОТАЛ. 10.09 тинт предмета на
|
||||
меш чая из золотарника не подействовал вообще: у шейдера Game_EntityTintMaskSSS
|
||||
выигрывает собственный _Color материала. У rock_smallPrefab такого конфликта нет -
|
||||
доказательство лежит в самом моде, три перекрашенных камня в игре видны. -->
|
||||
<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="TintColor" value="220, 30, 45"/>
|
||||
|
||||
<!-- По одной банке на ячейку, указание 2026-09-15. Наследуемый medicalBloodBag даёт
|
||||
15, и это надо перебивать явно: Extends копирует свойство целиком. Дорого и так
|
||||
задумано - Чёрный портал просит десять банок, то есть десять ячеек. -->
|
||||
<property name="Stacknumber" value="1"/>
|
||||
<!-- Своя рисованная иконка, получена 2026-09-15 (exch/bloodSphere.png, 160x160 RGBA,
|
||||
как все остальные 28). Заглушка со спрайтом Камня духов и багровым тинтом,
|
||||
стоявшая тут несколько часов, снята.
|
||||
|
||||
<!-- Прочность 1000, указание 2026-09-15. Две разные ручки, обе обязательны:
|
||||
ShowQuality рисует полоску (XUiC_ItemStack.ShowDurability => ItemClass.ShowQualityBar),
|
||||
а само число идёт пассивным эффектом DegradationMax
|
||||
(ItemValue.MaxUseTimesBase => EffectManager.GetValue(PassiveEffects.DegradationMax)).
|
||||
Без эффекта прочность равна нулю, а полоска при MaxUseTimes == 0 рисуется ПОЛНОЙ -
|
||||
то есть забытый эффект выглядит как "всё работает".
|
||||
CustomIconTint НЕ ЗАДАЁТСЯ, и это тот же принцип, что у всех рисованных иконок
|
||||
мода (шесть модов ножа, Кровь, Жертвенная кожа): тинт существует, чтобы
|
||||
заимствованный ванильный спрайт не читался как предмет, из которого он взят. На
|
||||
готовой работе он бы просто её затемнил. -->
|
||||
<property name="CustomIcon" value="BloodSphere"/>
|
||||
|
||||
tiered="false" обязателен: ItemClass.HasQuality читается как Effects.IsOwnerTiered(),
|
||||
и тированная группа превратила бы банку в предмет с качеством - тиры, рамка.
|
||||
<!-- Прочность 500 (указание). Две ручки, обе обязательны - тот же разбор, что у крови
|
||||
в items.xml: ShowQuality рисует полоску, а само число идёт пассивкой
|
||||
DegradationMax. Без пассивки прочность равна нулю, а полоска при MaxUseTimes == 0
|
||||
рисуется ПОЛНОЙ, то есть забытый эффект выглядит как "всё работает".
|
||||
|
||||
Прочность пока НЕ УБЫВАЕТ: UseTimes растёт только от ItemAction-ов, а у крови их
|
||||
нет. Решение пользователя 2026-09-15: "прочность будет убавляться, но это
|
||||
реализуем позже". -->
|
||||
tiered="false" обязателен: HasQuality читается как Effects.IsOwnerTiered(), и
|
||||
тированная группа превратила бы сферу в предмет с качеством - тиры, рамка.
|
||||
|
||||
Гейта по тегу здесь, в отличие от крови, НЕ НУЖНО. У модификации effect_group
|
||||
применяется к предмету-хозяину, и у крови пассивку приходилось гейтить, чтобы
|
||||
1000 прочности не досталась браслету. Сфера же и есть расходник браслета: пусть
|
||||
он её и тратит. Браслету от DegradationMax ничего не будет - ему эту прочность
|
||||
никто не списывает (SpendCharge трогает только сферу), а полоски у него нет. -->
|
||||
<property name="ShowQuality" value="true"/>
|
||||
<!-- true: опустевшая банка должна исчезать, а не лежать "сломанной" в ожидании
|
||||
ремонта, как топор. Ванильные инструменты ставят false именно потому, что их чинят. -->
|
||||
<!-- true: опустевшая сфера должна исчезать, а не лежать "сломанной" в ожидании
|
||||
ремонта. Само удаление из слота делает SpendCharge - см. SpatialVaultPickupPatch. -->
|
||||
<property name="DegradationBreaksAfter" value="true"/>
|
||||
|
||||
<property name="EconomicValue" value="0"/>
|
||||
|
||||
<effect_group name="resourceNecromancerBlood" tiered="false">
|
||||
<passive_effect name="DegradationMax" operation="base_set" value="1000" tags="necroBloodFlask"/>
|
||||
<effect_group name="resourceBloodSphere" tiered="false">
|
||||
<passive_effect name="DegradationMax" operation="base_set" value="500"/>
|
||||
</effect_group>
|
||||
|
||||
<!-- Stacknumber 1 приходит из modGeneralMaster, своя строка не нужна. -->
|
||||
<property name="EconomicValue" value="0"/>
|
||||
<property name="SellableToTrader" value="false"/>
|
||||
</item_modifier>
|
||||
</append>
|
||||
</config>
|
||||
|
||||
+75
-12
@@ -1284,23 +1284,86 @@
|
||||
</item>
|
||||
</append>
|
||||
|
||||
<!-- "Кровь некроманта" (Necromancer's Blood) ПЕРЕЕХАЛА В Config/item_modifiers.xml 2026-09-15.
|
||||
<!-- "Кровь некроманта" (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).
|
||||
|
||||
Причина - указание «пусть флакон крови можно будет вставлять как модификацию для
|
||||
пространственного хранилища», и это оказалось не настройкой, а сменой класса предмета.
|
||||
XUiC_ItemPartStack.CanSwap открывается строкой
|
||||
ЭТОТ ПРЕДМЕТ НИКОГДА НЕ ДОЛЖЕН СТАТЬ <item_modifier>. ЭТО НЕ СТИЛЬ, ЭТО СЕЙВЫ.
|
||||
15.09.2026 он был перенесён в item_modifiers.xml, чтобы вставляться в Пространственный
|
||||
браслет, и это уничтожило персонажа в тестовом мире - вместе с бэкапом. Разбор целиком в
|
||||
BACKLOG.md, здесь суть, потому что соблазн повторить велик:
|
||||
|
||||
if (!(stack.itemValue.ItemClass is ItemClassModifier itemClassModifier)) return false;
|
||||
ItemValue.Read строка 1094: if ((version > 4 || HasQuality) && !(itemClass is ItemClassModifier))
|
||||
ItemValue.Write строка 1228: if (!(ItemClass is ItemClassModifier))
|
||||
|
||||
- слот модификации не смотрит ни на теги, ни на свойства, пока предмет не ItemClassModifier,
|
||||
а этот класс создаётся ТОЛЬКО из <item_modifier> в item_modifiers.xml. Никакого свойства
|
||||
вида "CanBeInstalled" не существует; остаться в items.xml и стать модификацией нельзя.
|
||||
Класс предмета решает БАЙТОВУЮ РАСКЛАДКУ каждого его стака в сейве: обычный предмет пишет
|
||||
байт числа модификаций, ItemClassModifier - не пишет. Значит любой сейв, записанный до
|
||||
переноса, после переноса читается со сдвигом: поток съезжает, ближайший ReadString()
|
||||
получает мусор, PlayerDataFile.Load падает с "output char buffer is too small", и игра
|
||||
откатывается на NewGame. Бэкап .ttp.bak умирает вместе с основным файлом - он старого
|
||||
формата ровно так же. Кровь лежит в сейвах у всех, кто поставил 1.1.0 с Nexus.
|
||||
|
||||
Предмет при этом остался обычным во всём остальном: ItemClassModifier наследует ItemClass и
|
||||
регистрируется в том же ItemClass.nameToItem, поэтому рецепты, крафт и Harmony-патч
|
||||
(NecromancerBloodPatch.cs) находят его по имени как раньше. Разбор - в BACKLOG.md.
|
||||
Правило на будущее: предмет, который уже мог попасть в чужой инвентарь, нельзя переводить
|
||||
между ItemClass и ItemClassModifier ни в какую сторону. Нужна модификация - это НОВЫЙ
|
||||
предмет с новым именем, которого в старых сейвах нет. Проверка "айди не поедут"
|
||||
(assignIdsFromMapping) к этому отношения не имеет и ничего тут не гарантирует - именно на
|
||||
неё я и посмотрел вместо раскладки.
|
||||
|
||||
Само определение, со всеми старыми комментариями про банку, текстуры и прочность - там. -->
|
||||
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. -->
|
||||
<append xpath="/items">
|
||||
<item name="resourceNecromancerBlood">
|
||||
<property name="Extends" value="medicalBloodBag"/>
|
||||
<property name="DescriptionKey" value="resourceNecromancerBloodDesc"/>
|
||||
<!-- Своя рисованная иконка, 2026-08-30. CustomIcon задаётся явно даже при Extends. -->
|
||||
<property name="CustomIcon" value="NecromantsBlood"/>
|
||||
|
||||
<!-- Своя банка с кровью, 2026-09-10 (указание: «берём чай из золотарника, и жёлтое
|
||||
заменяем на кровавый цвет, с фиолетовыми оттенками»). Заодно чинилось расхождение
|
||||
текста и модели: описание говорит «Банка, наполненная кровью», а наследуемый
|
||||
medicalBloodBag показывал sackPrefab - обычный мешок.
|
||||
|
||||
ПОЧЕМУ НЕ ХВАТИЛО ТИНТА - ПРОВЕРЕНО В ИГРЕ. Ванильный префаб чая плюс TintColor:
|
||||
банка осталась чаем из золотарника, тинт предмета на этот меш НЕ ПОДЕЙСТВОВАЛ
|
||||
ВООБЩЕ - у шейдера Game_EntityTintMaskSSS выигрывает собственный _Color материала.
|
||||
Поэтому TintColor здесь не задаётся совсем.
|
||||
|
||||
И по сути: кровь отличается от чая не цветом, а тем, что она непрозрачная, тёмная
|
||||
и густая, с плёнкой на стекле. Жидкость ПЕРЕРИСОВАНА по яркости, а не перекрашена
|
||||
множителем - генератор _private/tools/make_necroblood_textures.py.
|
||||
|
||||
HoldType 3 - хват банки вместо 45 (мешок), Material Mglass - стекло вместо ткани. -->
|
||||
<property name="Meshfile" value="#@modfolder(NecromancerTome):Resources/necroblood?necroBloodPrefab.prefab"/>
|
||||
<property name="HoldType" value="3"/>
|
||||
<property name="Material" value="Mglass"/>
|
||||
|
||||
<!-- НИ Stacknumber, НИ ПРОЧНОСТИ ЗДЕСЬ НЕТ, И ЭТО НАМЕРЕННО.
|
||||
|
||||
15.09.2026 крови на один заход выдали и то, и другое: стак по одной банке и
|
||||
прочность 1000. Обе правки существовали ради одного - кровь должна была стать
|
||||
расходником, вставляемым в Пространственный браслет. Эта затея откачена (она
|
||||
ломала сейвы, см. предупреждение выше), расходником стала Кровавая сфера, и по
|
||||
прямому указанию пользователя кровь возвращена к тому, чем была:
|
||||
|
||||
- стак снова 15 - наследуется от medicalBloodBag, своей строки Stacknumber
|
||||
больше нет. Своя строка была нужна только чтобы перебить наследуемое 15 на 1;
|
||||
- прочности нет вовсе: ни ShowQuality, ни DegradationBreaksAfter, ни
|
||||
effect_group с DegradationMax. Тратил её браслет; тратить стало некому, а
|
||||
полоска, которая никогда не двигается, хуже, чем её отсутствие.
|
||||
|
||||
Кровь снова то, чем была с самого начала: ингредиент рецептов, и только.
|
||||
Если прочность когда-нибудь понадобится - разбор обеих ручек (пассивка
|
||||
DegradationMax плюс отдельное свойство ShowQuality, и ловушка с полной полоской
|
||||
при MaxUseTimes == 0) лежит в BACKLOG.md, повторно раскапывать не нужно. -->
|
||||
<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
|
||||
|
||||
@@ -101,7 +101,11 @@
|
||||
скиллы") - 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 добавлена 2026-09-15 (указание «доступна на первом
|
||||
грейде»): tier 1 - это уровень 1, то есть с самого начала, как и весь
|
||||
остальной базовый набор. Её рецепт в recipes.xml намеренно без тега
|
||||
разблокировки - для tier 1 тег не нужен, группа открыта сразу. -->
|
||||
<unlock_entry item="thrownStoneSpirit,necroWpnBladeNecroKnife,thrownStonePortalBlue,necroHeresyPyramid,resourceBloodSphere" unlock_tier="1"/>
|
||||
<unlock_entry item="braceletSpatialVault" unlock_tier="2"/>
|
||||
<!-- ЧЕТЫРЕ МОДА НОЖА ПЕРЕЕХАЛИ СЮДА ИЗ ГРУППЫ 2, 2026-09-09. Продиктовано:
|
||||
"Питьё важно в тот же день. Оно должно быть доступно после 30 убитых зомби.
|
||||
|
||||
@@ -376,4 +376,27 @@
|
||||
<ingredient name="resourceYuccaFibers" count="10"/>
|
||||
</recipe>
|
||||
</append>
|
||||
|
||||
<!-- Кровавая сфера, продиктовано 2026-09-15: «Станки не нужны. Ингридиенты: Кровь некроманта,
|
||||
5 праха зомби. По одному рецепту изготавливается две сферы.»
|
||||
|
||||
Ни craft_area, ни тега разблокировки: «первый грейд» - это группа 1 «Адепт», а она открыта
|
||||
с уровня 1, и рецепт БЕЗ тега просто доступен всегда. Ровно так же сделан Камень духов
|
||||
выше в этом файле - см. комментарий в progression.xml, там это правило записано словами.
|
||||
В скиллах сфера показана отдельной строкой группы 1 с unlock_tier="1".
|
||||
|
||||
ЗАМЕЧЕНО, НЕ ИСПРАВЛЕНО: сам Пространственный браслет открывается на 20 убийствах
|
||||
(unlock_tier="2" в группе 1), то есть сферу можно скрафтить на 19 уровней раньше, чем
|
||||
появится предмет, в который её вставляют. По записанному правилу «пороги - по нужде, а не
|
||||
по силе» ей место рядом с браслетом. Оставлено как продиктовано; переезд - это unlock_tier
|
||||
с 1 на 2 в progression.xml плюс тег necroNecromancyLvl20 сюда.
|
||||
|
||||
Цена реальная, а не по списку: одна кровь некроманта стоит ещё и 90% текущего ХП на её
|
||||
собственный крафт (NecromancerBloodPatch.cs). Две сферы за один заход это и учитывают. -->
|
||||
<append xpath="/recipes">
|
||||
<recipe name="resourceBloodSphere" count="2" tags="packMuleCrafting">
|
||||
<ingredient name="resourceNecromancerBlood" count="1"/>
|
||||
<ingredient name="resourceZombieAsh" count="5"/>
|
||||
</recipe>
|
||||
</append>
|
||||
</config>
|
||||
|
||||
Reference in New Issue
Block a user