ИИ питомцев переписан, добавлен Дух крысы, пороги разнесены по шкале

Работа по указаниям 2026-09-18. В релиз пока не выходит.

НОВЫЙ ПИТОМЕЦ "Дух крысы" (necroRatSpirit), Некромантия 3 (60 убийств):
модель зомбоволка в масштабе 0.28, урон 5, кроличьи звуки. Сам не нападает
никогда, держится справа-сзади в двух блоках, упёршись в препятствие проходит
СКВОЗЬ него и блоков не ломает. Укус замедляет, оставляет метку духа (видна на
компасе, +25% получаемого урона) и рвёт жилу. Обгрызая труп, лечится.

СВОЯ ЗАДАЧА ИИ. Задачи "иди за сущностью" в игре нет вовсе - проверены все 32
типа EAI*. Написана NecroFollowOwnerTask по образцу EAIApproachSpot: сектор
"3-6 часов" от хозяина, FindPath с canBreak:false, проход сквозь препятствие
через Entity.IsStuck. Вешается в рантайме, минуя Type.GetType.

ПРИКАЗ АТАКОВАТЬ. Повторное применение свитка при живом питомце натравливает
его на цель под прицелом: EntityPlayerLocal.HitInfo + ItemActionAttack.
GetEntityFromHit. Торговцы и игроки отсеяны. Нет цели - "Нет цели для атаки".

ЗОМБОЖИВОТНЫЕ ПРИВЕДЕНЫ К ТОМУ ЖЕ ОБРАЗЦУ. Сняты BreakBlock, Territorial,
ApproachSpot, Wander, BlockingTargetTask и поедание трупов; цель они больше не
выбирают сами. Лестница урона 20/35/45/60 плюс расчленение у Пса, Медведя и
Волка. Кровотечение всем, метка и ослабление - только у крысы.

УБИЙСТВА ПИТОМЦЕМ ЗАСЧИТЫВАЮТСЯ ВЛАДЕЛЬЦУ, включая добивание кровотечением.
Префикс на AwardKillXPServer подменяет убийцу владельцем; для смерти от баффа
заведена память укусов, потому что в DamageSource от баффа нет того, кто его
наложил. Зомби под Камнем духов это не задело - решение от 17.09 в силе.

ГРИФ откачен на летающую ветку EntityVulture и переименован в Могильного
стервятника: модель наконец соответствует имени. Держится у игрока сам, через
собственный механизм "дома" (setHomeArea), на время погони дом отвязывается.
Попытка натянуть птичий префаб на наземный класс провалилась и записана - так
делать нельзя.

ПРОЧЕЕ: призрачный вид распространён с торговцев на питомцев (у Пса, Медведя и
Волка выключен по указанию), у Пса светятся фиолетовые глаза, белая иконка
книги снята со всех свитков призыва, "Жуки Властелина" переименованы в "Рой
фараона" на всех 13 языках.

ПОРОГИ: крыса 60, стервятник 500, пёс 1300, медведь 2000, волк 4000.

ИСПРАВЛЕНО ПО ХОДУ ИГРОВЫХ ПРОВЕРОК: питомцы не призывались обычным кликом
(AnimWait требовал удержания), отзыв срабатывал не с первого раза (автомат
состояний ItemActionSpawnEntity), крыса проваливалась сквозь мир (IsStuck
отключает и пол), питомец подбрасывал хозяина (коллайдеры разводились до
появления модели).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-09-18 20:51:42 +03:00
co-authored by Claude Opus 5
parent 15c59dd37a
commit d97f83e972
21 changed files with 2501 additions and 179 deletions
+22 -10
View File
File diff suppressed because one or more lines are too long
+123
View File
@@ -200,5 +200,128 @@
</effect_group>
</buff>
<!-- "Путы духа крысы" - укус Духа крысы, 2026-09-18.
БЫЛО НОКДАУНОМ, СТАЛО ЗАМЕДЛЕНИЕМ (в тот же день, по игровой проверке: "ронять зомби
при каждом ударе смотрится скорее смешно"). Первая версия ставила из кода
bodyDamage.CurrentStun = Prone на 5 секунд - механизм рабочий, но зомби валился на
землю от каждого укуса мелкого духа, и выглядело это комично.
ПОЧЕМУ ТЕПЕРЬ ХВАТАЕТ ЧИСТОГО XML, А РАНЬШЕ НЕ ХВАТАЛО. Открытым был ровно один вопрос:
доходят ли RunSpeed/WalkSpeed до ИИ, или это чисто игроцкие величины. Ответ нашёлся в
EntityAlive:
public virtual float GetMoveSpeedAggro() {
if (IsBloodMoon || world.IsDark())
return EffectManager.GetValue(PassiveEffects.RunSpeed, null, moveSpeedAggroMax, this);
return EffectManager.GetValue(PassiveEffects.WalkSpeed, null, moveSpeedAggro, this);
}
Скорость зомби берётся через EffectManager, а EntityMoveHelper.SetMoveTo читает её
оттуда же. Значит обычный пассивный эффект работает, и весь Harmony-код (RatGripPatch.cs)
оказался не нужен - удалён.
ОБЕ ВЕЛИЧИНЫ ОБЯЗАТЕЛЬНЫ. Ветка в GetMoveSpeedAggro выбирается по времени суток: ночью и
в кровавую луну читается RunSpeed, днём - WalkSpeed. Прописать одну - значит получить
замедление, которое молча отваливается на полсуток. CrouchSpeed добавлен за компанию,
для крадущихся спящих.
ЗАТУХАНИЕ, А НЕ РОВНАЯ ПОЛКА: value="0.7,.1" с duration="0,5" - это -70% в момент укуса,
плавно сходящие к -10% за 5 секунд, тем же приёмом, что у ванильного buffInjurySlow.
stack_type replace: каждый следующий укус начинает отсчёт заново, поэтому пока крыса
грызёт, зомби так и держится возле -70%, а стоит ей отвлечься - он разгоняется обратно.
Кулдаун, который был нужен нокдауну (иначе один питомец держал бы зомби лежачим вечно),
здесь не нужен и удалён вместе с ним: замедление, обновляемое укусами, - это ровно то
поведение, которое и требовалось. -->
<buff name="buffNecroRatGrip" name_key="buffNecroRatGripName" description_key="buffNecroRatGripDesc" icon="ui_game_symbol_twitch_slow" icon_color="140,200,255">
<stack_type value="replace"/>
<duration value="5"/>
<effect_group>
<passive_effect name="RunSpeed" operation="perc_subtract" value="0.7,.1" duration="0,5"/>
<passive_effect name="WalkSpeed" operation="perc_subtract" value="0.7,.1" duration="0,5"/>
<passive_effect name="CrouchSpeed" operation="perc_subtract" value="0.7,.1" duration="0,5"/>
</effect_group>
</buff>
<!-- "Метка духа" - вторая половина укуса Духа крысы, 2026-09-18 (указания "предложи дебаф
поинтереснее" -> выбраны варианты 2 и 3, метка и гниль, вместе).
ДВА ЭФФЕКТА, ОДИН БАФФ, И ЖИВЁТ ОН ДОЛЬШЕ ЗАМЕДЛЕНИЯ. Замедление (buffNecroRatGrip
выше) осмысленно только пока крыса грызёт, поэтому оно короткое и обновляется каждым
укусом. Метка - наоборот: её ценность в том, что она ПЕРЕЖИВАЕТ драку. Тридцать секунд
достаточно, чтобы добежать до помеченного и добить его, и мало, чтобы карта превратилась
в ёлку.
1. ВИДНО. SetNavObject на самого носителя - тот же механизм, которым сделано Тёмное
чутьё (necroModKnifeDarkSense ниже), только там selfAOE по площади, а здесь
target="self": метится ровно тот, кого укусили. Класс метки свой и зелёный, не
красный ванильный, - иначе добыча крысы была бы неотличима от зомби, подсвеченных
чутьём (см. комментарий в nav_objects.xml).
Снимается тремя триггерами, а не одним: по истечении баффа, по смерти носителя и на
всякий случай - тем же способом, что RemoveParticleEffectFromEntity у девиации выше.
Метка на трупе - это метка, которая не уйдёт уже никогда.
2. ГНИЁТ. GeneralDamageResist в МИНУС - помеченный получает на четверть больше урона от
всего подряд. Это не выдумка и не побочный эффект: сопротивление читается со стороны
ЖЕРТВЫ, в EntityAlive.DamageEntity:
float num = Utils.FastMin(1f, EffectManager.GetValue(PassiveEffects.GeneralDamageResist, null, 0f, this));
float num2 = (float)_strength * num + accumulatedDamageResisted;
int num3 = Utils.FastMin(_strength, (int)num2);
_strength -= num3;
при отрицательном сопротивлении num3 отрицателен, и вычитание его УВЕЛИЧИВАЕТ урон.
Ограничение FastMin(1f, ...) режет только верх (100% сопротивления), низ открыт.
Вместе это и есть роль Духа крысы: он не убивает сам (урон 5), он находит добычу и
делает её уязвимой для хозяина. -->
<buff name="buffNecroRatMark" name_key="buffNecroRatMarkName" description_key="buffNecroRatMarkDesc" icon="ui_game_symbol_skull" icon_color="150,220,70">
<stack_type value="replace"/>
<duration value="30"/>
<effect_group>
<passive_effect name="GeneralDamageResist" operation="base_subtract" value="0.25"/>
<triggered_effect trigger="onSelfBuffStart" action="SetNavObject" target="self" nav_object="necroRatSpiritMark" add="true"/>
<triggered_effect trigger="onSelfBuffRemove" action="SetNavObject" target="self" nav_object="necroRatSpiritMark" add="false"/>
<triggered_effect trigger="onSelfDied" action="SetNavObject" target="self" nav_object="necroRatSpiritMark" add="false"/>
</effect_group>
</buff>
<!-- "Кровоточащая рана" - кровотечение от укуса Духа крысы, 2026-09-18.
ВАНИЛЬНОЕ buffInjuryBleeding НА ЗОМБИ НЕ РАБОТАЕТ ВООБЩЕ, и стояло оно тут ошибочно.
Замечено пользователем по игре ("при такой слабой атаке байкера она била минут 5"),
подтверждено чтением Data/Config/buffs.xml. Урон там берётся не из самого баффа, а из
переменной игрока:
<triggered_effect trigger="onSelfBuffStart" action="ModifyCVar" cvar="$bleedAmount"
operation="set" value="@bleedCounter"/>
<passive_effect name="HealthChangeOT" operation="base_subtract" value="@$bleedAmount"/>
<triggered_effect trigger="onSelfBuffUpdate" action="RemoveBuff" buff="buffInjuryBleeding">
<requirement name="CVarCompare" cvar="bleedCounter" operation="Equals" value="0"/>
</triggered_effect>
bleedCounter накручивают ДРУГИЕ баффы-обёртки (buffInjuryBleedingOne/Two,
buffInjuryBleedingBarbedWire), и это часть игроцкой системы травм. Повесить
buffInjuryBleeding напрямую на зомби - значит получить счётчик 0: урон ноль, и бафф
снимает сам себя на первом же обновлении. Ровно ноль эффекта, молча.
Поэтому здесь своё, без единой внешней переменной: сколько написано, столько и капает.
ЧИСЛА. 5 в секунду на 15 секунд. Укус крысы даёт 5 урона и проходит примерно раз в
секунду, то есть кровотечение РОВНО УДВАИВАЕТ её вклад, а stack_type replace означает,
что пока она грызёт, рана не заживает. Отвлеклась - зомби живёт ещё 15 секунд и
перестаёт течь. Если в игре окажется мало или много, править надо эти две цифры и
больше ничего. -->
<buff name="buffNecroRatBleed" name_key="buffNecroRatBleedName" description_key="buffNecroRatBleedDesc" icon="ui_game_symbol_critical" icon_color="200,40,40" icon_blink="true">
<damage_type value="bloodloss"/>
<damage_source value="Internal"/>
<stack_type value="replace"/>
<duration value="15"/>
<update_rate value="1"/>
<effect_group>
<passive_effect name="HealthChangeOT" operation="base_subtract" value="5"/>
</effect_group>
</buff>
</append>
</config>
+161 -48
View File
@@ -58,9 +58,41 @@
originally had before its own rework - see necroMeleeHandZombieDog in items.xml. -->
<property name="HandItem" value="necroMeleeHandZombieDog"/>
<property name="AITask-3" value="ApproachAndAttackTarget" data="class=EntityZombie,20"/>
<property name="AITarget-1" value="SetAsTargetIfHurt" data="class=EntityZombie"/>
<property name="AITarget-4" value="SetNearestEntityAsTarget" data="class=EntityZombie,22,20"/>
<!-- ИИ ПЕРЕПИСАН 2026-09-18 ПОД ОБРАЗЕЦ ДУХА КРЫСЫ, прямое указание: "поведение
других призванных животных зомби тоже подгони под крысу. По сути это те же
крысы, но размер модели оригинальный, урон нарастает от зомбособаки до
зомбоволка". Правка стоит здесь, у necroZombieDog, и этого достаточно:
Зомбомедведь, Зомбоволк и Зомбогриф расширяют именно его и унаследуют всё.
БЫЛО (и разобрано в BACKLOG.md 18.09 как "питомцы унаследовали ИИ враждебной
твари"): AITask-3 переопределял только погоню, а от animalZombieDog оставались
BreakBlock (грыз базу игрока), Territorial (держался точки призыва, а не
хозяина), ApproachSpot и Wander (бродил сам по себе), BlockingTargetTask и
SetNearestCorpseAsTarget (бросал хозяина ради ближайшего трупа), а
SetNearestEntityAsTarget заставлял его самому лезть в драку в 22 метрах.
СТАЛО, ровно как у крысы:
- AITask-3 пустой обрывает перебор, поэтому BreakBlock/Territorial/ApproachSpot/
Wander не грузятся вовсе (CopyPropertiesFromEntityClass идёт по индексам и
встаёт на первом пустом);
- AITarget-2 пустой так же убирает BlockingTargetTask и поедание трупов;
- цель НЕ ВЫБИРАЕТСЯ самостоятельно никогда: SetNearestEntityAsTarget больше
нет. Цель приходит только приказом игрока (PetCommandPatch.cs);
- AITarget-1 остаётся только как ответ на удар, и список классов без
EntityPlayer: питомец огрызается на зомби и враждебных зверей, но никогда на
хозяина;
- class=EntityAlive в AITask-1 - это разрешение задаче подхватить ту цель,
которую ей ДАЛИ, а не "бей всё живое" (EAIApproachAndAttackTarget.CanExecute
сверяет тип цели со списком и без совпадения не запускается). Кого можно
назначать, решает PetCommandPatch.cs, и торговец там отсеян.
Задача следования (NecroFollowOwnerTask) вешается в рантайме из SummonPatch.cs -
всем, у кого UsesFollowTask, то есть теперь и этим четверым. -->
<property name="AITask-1" value="ApproachAndAttackTarget" data="class=EntityAlive,0"/>
<property name="AITask-2" value="Look"/>
<property name="AITask-3" value=""/>
<property name="AITarget-1" value="SetAsTargetIfHurt" data="class=EntityZombie,EntityEnemyAnimal"/>
<property name="AITarget-2" value=""/>
</entity_class>
</append>
@@ -156,6 +188,9 @@
<property name="PrefabCombined" value="true"/>
<property name="PhysicsBody" value="bear"/>
<property name="Mass" value="600"/>
<!-- 80% от ванильного, указание 2026-09-18. У animalZombieBear своего SizeScale нет,
то есть ванильный размер - единица, отсюда 0.8. -->
<property name="SizeScale" value="0.8"/>
<property name="Tags" value="entity,animal,bear"/>
<!-- FIXED 2026-08-30 (user report: "зомбомедведь лает" - swapping the Prefab only
changes the visible model, sound properties are a totally separate set of
@@ -173,7 +208,7 @@
<property name="SoundStepType" value="animalhvystep"/>
<!-- Real vanilla hand item (claw damage 60, vs. the Dog's own bite at 8) - reused
as-is, not customized further (no slow debuff etc. - not asked for). -->
<property name="HandItem" value="meleeHandAnimalZombieBear"/>
<property name="HandItem" value="necroMeleeHandZombieBear"/>
<effect_group name="Base Effects">
<!-- 1500 is a guess between the Dog's 200 and real animalZombieBear's own 4000 -
a tough pet, not necessarily boss-tier tanky. Say if it should be higher/lower. -->
@@ -189,7 +224,8 @@
<property name="PrefabCombined" value="true"/>
<property name="PhysicsBody" value="AWolf"/>
<property name="Mass" value="180"/>
<property name="SizeScale" value="1.4"/>
<!-- 1.12 = 80% от ванильных 1.4 у animalDireWolf, указание 2026-09-18. -->
<property name="SizeScale" value="1.12"/>
<property name="Tags" value="entity,animal,wolf"/>
<!-- FIXED 2026-08-30, same oversight as the Bear above (see its comment) - real
animalDireWolf's own sound set. -->
@@ -202,7 +238,7 @@
<property name="SoundGiveUp" value="wolfdiregiveup"/>
<property name="SoundStepType" value="animalpawstep"/>
<!-- Real vanilla hand item (bite damage 60, vs. the Dog's own 8). -->
<property name="HandItem" value="meleeHandAnimalDireWolf"/>
<property name="HandItem" value="necroMeleeHandZombieWolf"/>
<effect_group name="Base Effects">
<!-- 1200 is a guess between the Dog's 200 and real animalDireWolf's own 3000 -
slightly below the Bear, faster/leaner theme. Say if it should be different. -->
@@ -211,56 +247,133 @@
</entity_class>
</append>
<!-- Griffin CONVERTED 2026-08-29 to the same Dog-reskin trick as Bear/Wolf, after the user
confirmed live in-game that the animalZombieVulture+Harmony-redirect approach genuinely
doesn't work ("летает где-то в небе, и зомби его вообще не интересуют" - flies around
doing EntityVulture's own default Wander behavior, never engaging anything). Not worth
debugging the Harmony redirect further without another live test cycle - switched
straight to the proven-reliable pattern instead, same as Wolf (confirmed working) and
Bear (not yet confirmed, same trick).
<!-- "Зомбогриф" ОТКАЧЕН 2026-09-18 К ЛЕТАЮЩЕЙ ВЕТКЕ.
No real griffin exists in this game and a bird's skeleton/rig was flagged as a real risk
for reusing necroZombieDog's own quadruped rig (see the removed comment this replaces) -
picked animalMountainLion as the substitute model instead of a bird: it EXTENDS
animalWolf (Data/Config/entityclasses.xml, confirmed by reading it directly) - the SAME
immediate parent animalZombieDog itself extends - almost certainly sharing the exact same
quadruped skeleton/rig family the Dog's own model already uses, the lowest-risk pick
available (lower risk than the Bear, which comes from a totally different
animalBear->animalTemplateHostile lineage). Not a literal griffin visually any more (a
mountain lion, not a bird/lion-eagle hybrid) - say if a different substitute or the name
itself should change; kept "Зомбогриф"/"Summon Zombie Griffin" as-is for now since
renaming would touch items.xml/recipes.xml/Localization.csv too and wasn't asked for. -->
История у него длинная, и важно не потерять её конец. До 29.08 он расширял
animalZombieVulture и просто бестолково летал ("летает где-то в небе, и зомби его вообще
не интересуют") - тогда его перевели на necroZombieDog, то есть сделали наземным со
сменённой моделью. 18.09 пользователь заметил результат прямо: "зомбогриф у тебя это серая
пума". Попробовали натянуть на тот же наземный класс префаб стервятника - вышло хуже
всего: "выглядит жутко, прозрачное только туловище и голова, по земле он ползёт на
крыльях". Это и был ответ на висевший с 29.08 вопрос, можно ли подменить ОДИН префаб:
нельзя, птичья модель на аниматоре наземного квадрупа не работает.
Указание: "зомбогрифа откати, пусть ведёт себя как раньше и не делай его прозрачным.
Пусть пока бестолково летает как раньше, но с возможностью нацелить его на противника...
Потом сделаем грифа как надо. Но потом."
Поэтому здесь снова ванильная ЛЕТАЮЩАЯ ветка со всем, что к ней прилагается, и это
сознательный шаг назад до отдельной работы над ним (замысел разведдрона - BACKLOG.md).
ЧТО ПРИШЛОСЬ СНЯТЬ И ПОЧЕМУ:
- EntityFlags и EntityType уходят с "animal,zombie"/"Zombie" на чистое "animal"/"Animal".
Иначе EntityAlive.DamageEntity не даст ему бить зомби вовсе: там жёсткое правило, что
две сущности с флагом Zombie не могут повредить друг другу. Тот же приём и по той же
причине применён к Рою жуков выше.
- Tags теряют zombie/hostile, IsEnemyEntity=false, Faction=none - форма "свой игроку",
как у всех остальных питомцев.
- AITask/AITarget НЕ ЗАДАЮТСЯ ВОВСЕ, и это не упущение: Class="EntityVulture" не
использует систему AITask ни в каком виде, у него собственный захардкоженный поиск
целей на C# (см. HarmonySrc/SwarmTargetPatch.cs, там это разобрано целиком). Писать
сюда задачи - ровно та ошибка, которую уже совершали 28.08 с Роем.
ЧТОБЫ НЕ АТАКОВАЛ ХОЗЯИНА, он добавлен в SwarmTargetPatch.SpeciesByName: префикс на
EntityAlive.SetAttackTarget подменяет игрока ближайшим зомби. Это единственная точка, через
которую проходят все цели EntityVulture.
ПРИКАЗ АТАКОВАТЬ у него остаётся (CommandsAttack в SummonPatch.cs), но с оговоркой:
EntityVulture перебирает цели сам, своим циклом, и может сбросить наш приказ. Задачу
следования ему НЕ вешаем - она наземная и с полётом только подерётся. -->
<append xpath="/entity_classes">
<entity_class name="necroZombieGriffin" extends="necroZombieDog">
<!-- Real vanilla animalMountainLion's own Prefab/PhysicsBody/Mass/SizeScale. -->
<property name="Prefab" value="@:Entities/Animals/Cat/animalMountainLion.prefab"/>
<property name="PrefabCombined" value="true"/>
<property name="PhysicsBody" value="MountainLion"/>
<property name="Mass" value="125"/>
<property name="SizeScale" value="1"/>
<property name="Tags" value="entity,animal,cat"/>
<!-- FIXED 2026-08-30, same oversight as the Bear above (see its comment) - real
animalMountainLion's own sound set. -->
<property name="SoundRandom" value="mlionroam"/>
<property name="SoundAlert" value="mlionalert"/>
<property name="SoundHurt" value="mlionpain"/>
<property name="SoundDeath" value="mliondeath"/>
<property name="SoundAttack" value="mlionattack"/>
<property name="SoundSense" value="mlionsense"/>
<property name="SoundGiveUp" value="mliongiveup"/>
<property name="SoundStepType" value="animalpawstep"/>
<!-- Real vanilla hand item (claw damage 22 - lighter than the Bear/Wolf's 60, a
faster/leaner theme fitting a big cat rather than a heavyweight brawler). -->
<property name="HandItem" value="meleeHandAnimalMountainLion"/>
<entity_class name="necroZombieGriffin" extends="animalZombieVulture">
<property name="EntityFlags" value="animal"/>
<property name="EntityType" value="Animal"/>
<property name="Tags" value="entity,animal,vulture"/>
<property name="IsEnemyEntity" value="false"/>
<property name="Faction" value="none"/>
<property name="HandItem" value="necroMeleeHandZombieGriffin"/>
<effect_group name="Base Effects">
<!-- Real vanilla animalMountainLion's own HealthMax (750) - used as-is, not
scaled further, since this pet is meant to be the "fast/agile" one, not the
tankiest of the three. -->
<!-- У ванильного зомбостервятника 30 HP - для питомца, которого не отозвать до
следующего призыва, это ничто. 750 оставлено с прежней, наземной версии. -->
<passive_effect name="HealthMax" operation="base_set" value="750"/>
</effect_group>
</entity_class>
</append>
<!-- "Дух крысы" (Rat Spirit) - питомец начального уровня, указание 2026-09-18. Первый
питомец, у которого поведение задано НАМЕРЕННО, а не унаследовано: на нём и отлаживается
ИИ питомцев вообще (см. разбор в BACKLOG.md от 2026-09-18).
МОДЕЛЬ. Extends necroZombieWolf, то есть тот же самый ванильный префаб лютоволка, только
SizeScale с 1.4 на 0.28 - "уменьшить в 5 раз". Хитбокс отдельно задавать НЕ НАДО и брать
кроличий НЕЛЬЗЯ: коллайдеры в physicsbodies.xml привязаны к костям по имени пути, а риг
кролика с волчьим не совпадает (у кролика корпус Hips/LowerBack/Spine1 и голова под
Spine2/Neck/Head, у волка Hips/LowerBack/Spine/Spine1/Spine2 и голова под Neck/Neck1/Head)
- кроличье тело нашло бы на волчьей модели только таз и ноги, а корпус и голова остались
бы вообще без хитбокса. Унаследованный PhysicsBody="AWolf" при этом ужимается сам:
Entity.SetScale ставит ModelTransform.localScale, а костяные коллайдеры лежат внутри него.
ЗВУКИ - кроличьи ("он тоже грызун"). На АТАКУ поставлен rabbitpain: это буквально вопль
кролика от боли, и он же остаётся на SoundHurt - крыса вопит и когда кусает, и когда бьют
её. Остальные звуки ГЛУШАТСЯ ПУСТЫМИ НАМЕРЕННО: у animalRabbit нет ни roam, ни alert, ни
sense, ни giveup, и если их не погасить, крыса унаследует волчьи от necroZombieWolf -
ровно те грабли, что дали "зомбомедведь лает" (см. necroZombieBear выше). Дух рыщет молча.
ИИ. Здесь снято ВСЁ, что питомцы до сих пор тащили от враждебного зомбопса:
- нет BreakBlock и BlockingTargetTask - крыса физически не может грызть блоки, и это же
продублировано в коде (EAINecroFollowOwner зовёт FindPath с canBreak:false). Упёршись
в препятствие, дух проходит СКВОЗЬ него - см. Entity.IsStuck в PetFollowTask.cs;
- нет Territorial, ApproachSpot и Wander - крыса не бродит сама, она ходит за хозяином
(задача следования добавляется в рантайме из SummonPatch.cs, отдельным классом, потому
что задачи "иди за сущностью" в игре нет вовсе - все 32 типа EAI* проверены);
- нет SetNearestCorpseAsTarget - она не бросает хозяина ради ближайшего трупа;
- AITarget-1 оставлен ТОЛЬКО как ответ на удар, и список классов без EntityPlayer:
крыса огрызается на зомби и враждебных зверей, но никогда на хозяина, даже если он
заденет её в свалке (прямое указание 2026-09-18).
Сама она цель не ВЫБИРАЕТ никогда: SetNearestEntityAsTarget здесь нет, а AITarget-2
пустой обрывает перебор (CopyPropertiesFromEntityClass идёт по индексам и встаёт на первом
пустом - тем же приёмом ваниль глушит задачи у animalInsectSwarm). Цель приходит снаружи:
по команде игрока из PetCommandPatch.cs.
class=EntityAlive в AITask-1 - это НЕ "бей всё живое" само по себе, а разрешение задаче
подхватить ту цель, которую ей дали (EAIApproachAndAttackTarget.CanExecute сверяет тип
цели с этим списком и без совпадения просто не запускается). Кого можно назначить целью,
решает PetCommandPatch.cs, и торговец там отсеян. -->
<append xpath="/entity_classes">
<entity_class name="necroRatSpirit" extends="necroZombieWolf">
<property name="SizeScale" value="0.28"/>
<property name="Mass" value="33"/>
<property name="Weight" value="11"/>
<property name="Tags" value="entity,animal,rat"/>
<property name="HandItem" value="necroMeleeHandRatSpirit"/>
<property name="SoundRandom" value=""/>
<property name="SoundAlert" value=""/>
<property name="SoundSense" value=""/>
<property name="SoundGiveUp" value=""/>
<property name="SoundHurt" value="rabbitpain"/>
<property name="SoundDeath" value="rabbitdeath"/>
<property name="SoundAttack" value="rabbitpain"/>
<property name="SoundStepType" value="animallightstep"/>
<!-- Жёлтая метка на карте и компасе - указание 2026-09-18. Класс метки наш,
Config/nav_objects.xml; ванильные animaltracking_* не подошли, они завязаны
на перк следопыта, а не на прямое назначение. -->
<property name="NavObject" value="necroRatSpiritPet"/>
<property name="AITask-1" value="ApproachAndAttackTarget" data="class=EntityAlive,0"/>
<property name="AITask-2" value="Look"/>
<property name="AITask-3" value=""/>
<property name="AITarget-1" value="SetAsTargetIfHurt" data="class=EntityZombie,EntityEnemyAnimal"/>
<property name="AITarget-2" value=""/>
<effect_group name="Base Effects">
<!-- Дух-разведчик, а не танк: меньше собаки (200). Урон у неё и так 5. -->
<passive_effect name="HealthMax" operation="base_set" value="120"/>
</effect_group>
</entity_class>
</append>
<!-- "Жертва" loot bag - BACKLOG.md item 5, Necromancer's Knife. Same shape as vanilla's own
EntityLootContainerStrong ("BLUE")/EntityLootContainerBoss ("RED") right above these in
Data/Config/entityclasses.xml - only the Mesh and LootList differ.
+309 -13
View File
@@ -518,7 +518,10 @@
<append xpath="/items">
<item name="bookSummonZombieDog">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ItemTypeIcon="book" УБРАН 2026-09-18 со ВСЕХ свитков и книг призыва разом,
прямое указание: "свитки призыва и книги призыва не должны содержать белую
иконку книги поверх. Они ПРЕДМЕТЫ, а не рецепты." Полный разбор виджета - у
bookSummonRatSpirit ниже и у Пространственного браслета (07.09). -->
<!-- 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
@@ -546,7 +549,22 @@
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<!-- 0.05, а не 0.3 - выровнено со Свитком духа крысы 2026-09-18, после того как
в игре не призвались НИ ГРИФ, НИ ВОЛК, НИ МЕДВЕДЬ, ни пёс, а крыса
призвалась. В логе у всех четверых было только "action index=1" (отзыв) и ни
одного index=0: до Spawn основной клик не доходил вовсе.
ItemActionSpawnEntity.OnHoldingUpdate копит stateTime по 0.05 за тик и
сравнивает с animWait, а ExecuteAction(_bReleased: true) при отпускании
кнопки сбрасывает состояние в None. То есть 0.3 означало "держать кнопку
треть секунды", шесть тиков подряд, и обычный клик до спавна не доживал -
молча, без ошибки и без строки в логе.
Раньше это сходило с рук: призыв делают раз за сессию и кнопку держали. Но
теперь на этом же слоте сидит КОМАНДА АТАКОВАТЬ, которую отдают быстро и
часто, - и требовать под неё удержание нельзя. У крысы это было исправлено
сразу, у остальных - забыто; теперь у всех одинаково. -->
<property name="AnimWait" value="0.05"/>
<property name="SoundWarn" value="zombiedogalert"/>
<property name="SoundAttack" value="zombiedogattack"/>
<property name="Entity" value="necroZombieDog"/>
@@ -587,15 +605,99 @@
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. -->
<!-- УРОН ПОДНЯТ С ВАНИЛЬНЫХ 8 ДО 35, 2026-09-18. Указание: зомбоживотные - это "по
сути те же крысы, но размер модели оригинальный, урон нарастает от зомбособаки до
зомбоволка... они уже реально убивают зомби эффективно", и следом уточнение
"зомбособаке урон 35 и шанс расчленения, как и волку, как и медведю".
Лестница целиком: Дух крысы 5 -> Зомбогриф 20 -> Зомбопёс 35 ->
Зомбомедведь 45 -> Зомбоволк 60. Верхняя ступень - ванильный урон лютоволка,
ниже неё всё выстроено вручную; ванильные значения были 8 / 22 / 60 / 60, то
есть лестницы не было вовсе (медведь и волк совпадали).
ГРИФ НАМЕРЕННО САМЫЙ СЛАБЫЙ ИЗ ЗВЕРЕЙ, и это не просчёт: "для атаки он не особо
подходит", роль у него будет другая - см. BACKLOG.md 18.09, замысел сделать из
него аналог разведдрона (запустить в небо и смотреть его глазами).
РАСЧЛЕНЕНИЕ - у Пса, Медведя и Волка. Ни у Грифа, ни у Духа крысы его нет.
Величина 0.25 одна на всех троих: конкретной цифры названо не было, а разводить
её по видам незачем - они уже разведены уроном. -->
<property class="Action0">
<property name="DamageEntity" value="35"/>
</property>
<effect_group name="necroMeleeHandZombieDog" tiered="false">
<passive_effect name="ModSlots" operation="base_set" value="0"/>
<passive_effect name="DismemberChance" operation="base_add" value="0.25"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffInjurySlow">
<requirement name="EntityTagCompare" target="other" tags="zombie"/>
</triggered_effect>
<!-- Кровотечение добавлено 2026-09-18 всем призванным зомбоживотным. Метку духа
и ослабление (buffNecroRatMark) они НЕ ставят по прямому указанию - это
остаётся особенностью крысы, у которой своего урона почти нет. Замедление
buffInjurySlow выше - НЕ то же самое, что "ослабление": оно стоит здесь с
28.08 по отдельной просьбе ("укус применяет buffInjurySlow") и не трогалось.
Скажите, если его тоже убрать. -->
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroRatBleed"/>
</effect_group>
</item>
</append>
<!-- Оружие ближнего боя трёх остальных зомбоживотных, 2026-09-18. До сих пор они брали
ванильные предметы напрямую (meleeHandAnimalMountainLion / meleeHandAnimalZombieBear /
meleeHandAnimalDireWolf), и своих эффектов у них не было вовсе. Теперь нужны свои: и
чтобы выстроить лестницу урона, и чтобы повесить кровотечение.
effect_group НЕ наследуется через Extends (см. заметку в шапке файла), поэтому у каждого
он свой, а не добавленный к родительскому. Всё остальное - задержка, радиус, звуки удара -
наследуется от ванильного предмета как есть. -->
<append xpath="/items">
<item name="necroMeleeHandZombieGriffin">
<property name="Extends" value="meleeHandAnimalZombieVulture"/>
<property name="CreativeMode" value="None"/>
<!-- 20, а не 35: понижено 18.09 по прямому указанию. Гриф - будущий разведчик, а не
боец (см. комментарий к лестнице выше). Расчленения у него тоже нет. -->
<property class="Action0">
<property name="DamageEntity" value="20"/>
</property>
<effect_group name="necroMeleeHandZombieGriffin" tiered="false">
<passive_effect name="ModSlots" operation="base_set" value="0"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroRatBleed"/>
</effect_group>
</item>
</append>
<append xpath="/items">
<item name="necroMeleeHandZombieBear">
<property name="Extends" value="meleeHandAnimalZombieBear"/>
<property name="CreativeMode" value="None"/>
<property class="Action0">
<property name="DamageEntity" value="45"/>
</property>
<effect_group name="necroMeleeHandZombieBear" tiered="false">
<passive_effect name="ModSlots" operation="base_set" value="0"/>
<passive_effect name="DismemberChance" operation="base_add" value="0.25"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroRatBleed"/>
</effect_group>
</item>
</append>
<append xpath="/items">
<item name="necroMeleeHandZombieWolf">
<property name="Extends" value="meleeHandAnimalDireWolf"/>
<property name="CreativeMode" value="None"/>
<!-- 60 - ванильный урон лютоволка, верхняя ступень лестницы. Оставлен как есть. -->
<property class="Action0">
<property name="DamageEntity" value="60"/>
</property>
<effect_group name="necroMeleeHandZombieWolf" tiered="false">
<passive_effect name="ModSlots" operation="base_set" value="0"/>
<passive_effect name="DismemberChance" operation="base_add" value="0.25"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroRatBleed"/>
</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).
@@ -656,7 +758,10 @@
<append xpath="/items">
<item name="bookSummonInsectSwarm">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ItemTypeIcon="book" УБРАН 2026-09-18 со ВСЕХ свитков и книг призыва разом,
прямое указание: "свитки призыва и книги призыва не должны содержать белую
иконку книги поверх. Они ПРЕДМЕТЫ, а не рецепты." Полный разбор виджета - у
bookSummonRatSpirit ниже и у Пространственного браслета (07.09). -->
<!-- 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"/>
@@ -681,7 +786,22 @@
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<!-- 0.05, а не 0.3 - выровнено со Свитком духа крысы 2026-09-18, после того как
в игре не призвались НИ ГРИФ, НИ ВОЛК, НИ МЕДВЕДЬ, ни пёс, а крыса
призвалась. В логе у всех четверых было только "action index=1" (отзыв) и ни
одного index=0: до Spawn основной клик не доходил вовсе.
ItemActionSpawnEntity.OnHoldingUpdate копит stateTime по 0.05 за тик и
сравнивает с animWait, а ExecuteAction(_bReleased: true) при отпускании
кнопки сбрасывает состояние в None. То есть 0.3 означало "держать кнопку
треть секунды", шесть тиков подряд, и обычный клик до спавна не доживал -
молча, без ошибки и без строки в логе.
Раньше это сходило с рук: призыв делают раз за сессию и кнопку держали. Но
теперь на этом же слоте сидит КОМАНДА АТАКОВАТЬ, которую отдают быстро и
часто, - и требовать под неё удержание нельзя. У крысы это было исправлено
сразу, у остальных - забыто; теперь у всех одинаково. -->
<property name="AnimWait" value="0.05"/>
<property name="SoundWarn" value="swarmalert"/>
<property name="SoundAttack" value="swarmattack"/>
<property name="Entity" value="necroInsectSwarm"/>
@@ -703,7 +823,10 @@
<append xpath="/items">
<item name="bookSummonZombieGriffin">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ItemTypeIcon="book" УБРАН 2026-09-18 со ВСЕХ свитков и книг призыва разом,
прямое указание: "свитки призыва и книги призыва не должны содержать белую
иконку книги поверх. Они ПРЕДМЕТЫ, а не рецепты." Полный разбор виджета - у
bookSummonRatSpirit ниже и у Пространственного браслета (07.09). -->
<!-- ICON ADDED 2026-08-29: real generated art (SummonZombieGriffin.png). -->
<property name="CustomIcon" value="SummonZombieGriffin"/>
<property name="DescriptionKey" value="bookSummonZombieGriffinDesc"/>
@@ -721,9 +844,28 @@
<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"/>
<!-- 0.05, а не 0.3 - выровнено со Свитком духа крысы 2026-09-18, после того как
в игре не призвались НИ ГРИФ, НИ ВОЛК, НИ МЕДВЕДЬ, ни пёс, а крыса
призвалась. В логе у всех четверых было только "action index=1" (отзыв) и ни
одного index=0: до Spawn основной клик не доходил вовсе.
ItemActionSpawnEntity.OnHoldingUpdate копит stateTime по 0.05 за тик и
сравнивает с animWait, а ExecuteAction(_bReleased: true) при отпускании
кнопки сбрасывает состояние в None. То есть 0.3 означало "держать кнопку
треть секунды", шесть тиков подряд, и обычный клик до спавна не доживал -
молча, без ошибки и без строки в логе.
Раньше это сходило с рук: призыв делают раз за сессию и кнопку держали. Но
теперь на этом же слоте сидит КОМАНДА АТАКОВАТЬ, которую отдают быстро и
часто, - и требовать под неё удержание нельзя. У крысы это было исправлено
сразу, у остальных - забыто; теперь у всех одинаково. -->
<property name="AnimWait" value="0.05"/>
<!-- Звуки призыва сменены 2026-09-18: "при появлении слышен крик льва, а не
грифа". Остались с той поры, когда Гриф был горным львом; модель откатили на
стервятника, а свиток забыли - те же грабли, что "зомбомедведь лает", только
теперь на предмете, а не на сущности. -->
<property name="SoundWarn" value="vulturealert"/>
<property name="SoundAttack" value="vultureattack"/>
<property name="Entity" value="necroZombieGriffin"/>
<property name="EntityOffset" value="0, -1, 2.5"/>
</property>
@@ -741,7 +883,10 @@
<append xpath="/items">
<item name="bookSummonZombieBear">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ItemTypeIcon="book" УБРАН 2026-09-18 со ВСЕХ свитков и книг призыва разом,
прямое указание: "свитки призыва и книги призыва не должны содержать белую
иконку книги поверх. Они ПРЕДМЕТЫ, а не рецепты." Полный разбор виджета - у
bookSummonRatSpirit ниже и у Пространственного браслета (07.09). -->
<!-- ICON ADDED 2026-08-29: real generated art (SummonZombieBear.png). -->
<property name="CustomIcon" value="SummonZombieBear"/>
<property name="DescriptionKey" value="bookSummonZombieBearDesc"/>
@@ -759,7 +904,22 @@
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<!-- 0.05, а не 0.3 - выровнено со Свитком духа крысы 2026-09-18, после того как
в игре не призвались НИ ГРИФ, НИ ВОЛК, НИ МЕДВЕДЬ, ни пёс, а крыса
призвалась. В логе у всех четверых было только "action index=1" (отзыв) и ни
одного index=0: до Spawn основной клик не доходил вовсе.
ItemActionSpawnEntity.OnHoldingUpdate копит stateTime по 0.05 за тик и
сравнивает с animWait, а ExecuteAction(_bReleased: true) при отпускании
кнопки сбрасывает состояние в None. То есть 0.3 означало "держать кнопку
треть секунды", шесть тиков подряд, и обычный клик до спавна не доживал -
молча, без ошибки и без строки в логе.
Раньше это сходило с рук: призыв делают раз за сессию и кнопку держали. Но
теперь на этом же слоте сидит КОМАНДА АТАКОВАТЬ, которую отдают быстро и
часто, - и требовать под неё удержание нельзя. У крысы это было исправлено
сразу, у остальных - забыто; теперь у всех одинаково. -->
<property name="AnimWait" value="0.05"/>
<property name="SoundWarn" value="bearalert"/>
<property name="SoundAttack" value="bearattack"/>
<property name="Entity" value="necroZombieBear"/>
@@ -779,7 +939,10 @@
<append xpath="/items">
<item name="bookSummonZombieWolf">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ItemTypeIcon="book" УБРАН 2026-09-18 со ВСЕХ свитков и книг призыва разом,
прямое указание: "свитки призыва и книги призыва не должны содержать белую
иконку книги поверх. Они ПРЕДМЕТЫ, а не рецепты." Полный разбор виджета - у
bookSummonRatSpirit ниже и у Пространственного браслета (07.09). -->
<!-- ICON ADDED 2026-08-29: real generated art (SummonZombieWolf.png). -->
<property name="CustomIcon" value="SummonZombieWolf"/>
<property name="DescriptionKey" value="bookSummonZombieWolfDesc"/>
@@ -797,7 +960,22 @@
<property class="Action0">
<property name="Class" value="SpawnEntity"/>
<property name="AnimType" value="4"/>
<property name="AnimWait" value="0.3"/>
<!-- 0.05, а не 0.3 - выровнено со Свитком духа крысы 2026-09-18, после того как
в игре не призвались НИ ГРИФ, НИ ВОЛК, НИ МЕДВЕДЬ, ни пёс, а крыса
призвалась. В логе у всех четверых было только "action index=1" (отзыв) и ни
одного index=0: до Spawn основной клик не доходил вовсе.
ItemActionSpawnEntity.OnHoldingUpdate копит stateTime по 0.05 за тик и
сравнивает с animWait, а ExecuteAction(_bReleased: true) при отпускании
кнопки сбрасывает состояние в None. То есть 0.3 означало "держать кнопку
треть секунды", шесть тиков подряд, и обычный клик до спавна не доживал -
молча, без ошибки и без строки в логе.
Раньше это сходило с рук: призыв делают раз за сессию и кнопку держали. Но
теперь на этом же слоте сидит КОМАНДА АТАКОВАТЬ, которую отдают быстро и
часто, - и требовать под неё удержание нельзя. У крысы это было исправлено
сразу, у остальных - забыто; теперь у всех одинаково. -->
<property name="AnimWait" value="0.05"/>
<property name="SoundWarn" value="wolfdirealert"/>
<property name="SoundAttack" value="wolfdireattack"/>
<property name="Entity" value="necroZombieWolf"/>
@@ -814,6 +992,121 @@
</item>
</append>
<!-- "Дух крысы" (Rat Spirit): питомец начального уровня, указание 2026-09-18.
УРОН 5 - прямо по указанию, и это почти ничего (у волчьего укуса, от которого предмет
наследуется, 60). Крыса не оружие: она вешает дебафы. Их два, и оба БЕЗ гейта по тегу
"zombie", в отличие от укуса зомбопса выше - крысе разрешено атаковать любое существо,
значит и дебафы должны ложиться на то, что она укусила, а не только на зомби.
ЗАМЕДЛЕНИЕ СДЕЛАНО СВОЁ (buffNecroRatGrip, см. buffs.xml), а не взято из ванили: все
готовые "слоу" - это эффекты игроцкой системы травм, половина из них скрыта и написана
заодно под транспорт. Своё - это ровно RunSpeed/WalkSpeed/CrouchSpeed с затуханием, без
лишнего. Что эти величины вообще доходят до ИИ зомби, подтверждено декомпиляцией
EntityAlive.GetMoveSpeedAggro - разбор целиком в комментарии к самому баффу. -->
<append xpath="/items">
<item name="necroMeleeHandRatSpirit">
<property name="Extends" value="meleeHandAnimalDireWolf"/>
<property name="CreativeMode" value="None"/>
<property class="Action0">
<property name="DamageEntity" value="5"/>
</property>
<!-- effect_group НЕ наследуется через Extends (см. заметку в шапке файла) - триггеры
родителя пришлось бы переписывать целиком, и здесь они и не нужны: у крысы свои. -->
<effect_group name="necroMeleeHandRatSpirit" tiered="false">
<passive_effect name="ModSlots" operation="base_set" value="0"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroRatGrip"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroRatMark"/>
<triggered_effect trigger="onSelfAttackedOther" action="AddBuff" target="other" buff="buffNecroRatBleed"/>
</effect_group>
</item>
</append>
<!-- "Свиток духа крысы": тот же свиток, что у Зомбоволка, но серее - указание 2026-09-18
("выглядит как свиток призыва зомбоволка, но с более серым тинтом"). Иконка
переиспользуется целиком (CustomIcon="SummonZombieWolf"), своей рисовать не пришлось;
CustomIconTint обесцвечивает её, TintColor - 3D-модель в руке. Оба множатся на базовый
цвет, как у Камня духов.
ДВА ДЕЙСТВИЯ, И ОНИ НЕ ТЕ ЖЕ, ЧТО У ОСТАЛЬНЫХ ПИТОМЦЕВ. Action1 (силовая атака) - отзыв,
как везде. А вот Action0 у крысы ТРЁХзначный, и разбирает это HarmonySrc/PetCommandPatch.cs:
если крыса не призвана - призыв; если призвана - КОМАНДА АТАКОВАТЬ того, на кого смотрит
прицел; если под прицелом некого атаковать - "Нет цели для атаки" и больше ничего.
Class="SpawnEntity" в обоих слотах при этом обязателен: наш префикс висит именно на
ItemActionSpawnEntity.Spawn, и без него клик до нас не дойдёт.
Книга НЕ расходуется (ConsumesBook=false в SummonPatch.cs): иначе командовать атакой
означало бы тратить свиток на каждый приказ. -->
<append xpath="/items">
<item name="bookSummonRatSpirit">
<property name="Tags" value="T0,weapon,attPerception"/>
<!-- ItemTypeIcon="book" НЕ СТАВИТСЯ, хотя стоит у всех шести книг призыва выше.
Баг-репорт 2026-09-18: "у книги призыва есть ещё одна белая иконка книги поверх.
Такая обычно появляется на неизученных рецептах, но тут у нас не рецепт и не
книга для изучения. Это предмет."
Это тот же виджет, что уже разбирался 07.09 у Пространственного браслета (см.
его комментарий ниже, там полностью): значок 12x12 в ЛЕВОМ ВЕРХНЕМ углу поверх
иконки предмета, XUi_InGame/templates.xml, recipe_entry:
<sprite name="itemtypeicon" sprite="ui_game_symbol_{itemtypeicon}" .../>
Значение подставляется прямо в имя спрайта, поэтому "book" даёт белую книжку. У
ванили этот значок означает "куда/чем крафтится" (bundle, forge, campfire), и
единственный ванильный предмет с value="book" - это настоящая схема. Наш свиток
схемой не является: его не изучают, им пользуются. Значок вводил в заблуждение.
Убрано целиком, а не заменено: ItemClass.ItemTypeIcon по умолчанию "", виджет
закрыт гейтом {hasitemtypeicon}, и без свойства он просто не рисуется.
У ОСТАЛЬНЫХ ШЕСТИ КНИГ ПРИЗЫВА ЭТОТ ЖЕ ЗНАЧОК ПОКА ОСТАЛСЯ - трогать их без
указания не стал, но замечание к ним относится ровно так же. -->
<property name="CustomIcon" value="SummonZombieWolf"/>
<property name="CustomIconTint" value="8a9a8a"/>
<property name="DescriptionKey" value="bookSummonRatSpiritDesc"/>
<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="120, 135, 120"/>
<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"/>
<!-- 0.05, а НЕ 0.3, как у остальных призывных книг. Исправлено 2026-09-18 по первому же
игровому запуску: в логе шесть нажатий подряд и все с action index=1 (отзыв), ни
одного с index=0 - то есть до Spawn основной клик не доходил вовсе.
Причина в ItemActionSpawnEntity.OnHoldingUpdate: stateTime растёт по 0.05 за тик и
сравнивается с animWait, а ExecuteAction(_bReleased: true) при отпускании кнопки
сбрасывает состояние обратно в None. Значит 0.3 - это "держать кнопку треть
секунды", шесть тиков; обычный клик короче и не доходит до спавна, причём МОЛЧА -
ни ошибки, ни строки в логе.
Для остальных питомцев удержание терпимо (призыв делают раз в сессию), а у крысы
на этом же слоте сидит КОМАНДА АТАКОВАТЬ - приказ посреди боя, который отдают
быстро и часто. Требовать под него удержание нельзя. -->
<property name="AnimWait" value="0.05"/>
<property name="SoundWarn" value="rabbitpain"/>
<property name="Entity" value="necroRatSpirit"/>
<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="necroRatSpirit"/>
<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
@@ -836,7 +1129,10 @@
<append xpath="/items">
<item name="bookBanshee">
<property name="Tags" value="T0,weapon,attPerception"/>
<property name="ItemTypeIcon" value="book"/>
<!-- ItemTypeIcon="book" УБРАН 2026-09-18 со ВСЕХ свитков и книг призыва разом,
прямое указание: "свитки призыва и книги призыва не должны содержать белую
иконку книги поверх. Они ПРЕДМЕТЫ, а не рецепты." Полный разбор виджета - у
bookSummonRatSpirit ниже и у Пространственного браслета (07.09). -->
<!-- ICON UPDATED 2026-08-29: real generated art (BansheesScroll.png), no tint. -->
<property name="CustomIcon" value="BansheesScroll"/>
<property name="DescriptionKey" value="bookBansheeDesc"/>
+87
View File
@@ -0,0 +1,87 @@
<config>
<!-- Метки на карте и компасе для Духа крысы, 2026-09-18.
Две штуки, и это РАЗНЫЕ вещи: одна помечает саму крысу (чтобы игрок видел, где его
питомец), вторая - зомби, которого крыса укусила (метка духа).
Форма скопирована с ванильного nav_object_class "zombie" (Data/Config/nav_objects.xml):
это самый простой из существующих классов - у него НЕТ requirement_type, то есть метка
показывается безусловно, как только её кому-нибудь повесили. Классы с requirement_type
(IsPlayer, IsAlly, IsOwner, Tracking и ещё семь) сами решают, кому показываться, и нам
это только мешало бы: и питомец, и его добыча должны быть видны владельцу всегда.
Ванильные animaltracking_* сюда не годятся: они работают через систему отслеживания
животных (перк следопыта), а не через прямое назначение. -->
<append xpath="/nav_object_classes">
<!-- Сам питомец - ЖЁЛТЫМ, по прямому указанию.
СПРАЙТ: "воскрешение" (пробуем, указание 2026-09-18 - "для крысы попробуй иконку
воскрешения, проверим как смотрится"). Кроличий, стоявший тут сначала, не подошёл по
смыслу. Призрака, духа или души среди иконок игры НЕТ ВООБЩЕ - просмотрены все 359
имён вида ui_game_symbol_* из Data/Config и из самой сборки; ближайшее по названию
было specters_grace (бафф "Призрачная грация", перк Ловкости), но у воскрешения
смысл к некромантии ближе. Если в игре окажется невнятно, запасные - specters_grace
и skull (череп, он же значок самой Некромантии), меняется одна строка. -->
<nav_object_class name="necroRatSpiritPet">
<map_settings>
<property name="sprite_name" value="ui_game_symbol_twitch_resurrect"/>
<property name="min_distance" value="0"/>
<property name="max_distance" value="-1"/>
<property name="color" value="255,220,0,255"/>
<property name="has_pulse" value="false"/>
</map_settings>
<compass_settings>
<property name="sprite_name" value="ui_game_symbol_twitch_resurrect"/>
<property name="min_distance" value="0"/>
<property name="max_distance" value="1024"/>
<property name="max_scale_distance" value="32"/>
<property name="color" value="255,220,0,255"/>
<property name="has_pulse" value="false"/>
<property name="icon_clamped" value="false"/>
</compass_settings>
</nav_object_class>
</append>
<append xpath="/nav_object_classes">
<!-- Помеченная добыча. Спрайт зомбиный, как у ванильной метки, но цвет НЕ красный:
красным в игре уже отмечены зомби из Тёмного чутья (буфф necroModKnifeDarkSense
вешает ванильный nav_object "zombie"), и если бы метка крысы была такой же, две
разные механики стали бы неотличимы на карте.
ЦВЕТ ИСПРАВЛЕН 2026-09-18. Сначала здесь стоял зелёный (150,220,70) - ошибка, и
пользователь поймал её сразу: "игрок может решить, что цель дружелюбна". Зелёный в
интерфейсах значит "свой", а это помеченный ВРАГ.
Взят гнилостно-коричневый, а не ядовито-зелёный (предложены были оба). Причина
простая: сама крыса на карте жёлтая (255,220,0), а ядовито-зелёный - это
жёлто-зелёный, и на значке размером в десяток пикселей эти два цвета слились бы.
Коричневый не спорит ни с жёлтой крысой, ни с красными зомби Тёмного чутья и
читается как "гниёт" - ровно то, что метка и делает (см. GeneralDamageResist в
buffs.xml). Если в игре окажется тускло, ядовито-зелёный - это value="154,185,20",
одна строка.
has_pulse=true намеренно: метка живёт полминуты и должна быть заметна среди прочего,
это её единственная работа, а коричневый на карте сам по себе неяркий. -->
<nav_object_class name="necroRatSpiritMark">
<map_settings>
<property name="sprite_name" value="ui_game_symbol_zombie"/>
<property name="min_distance" value="0"/>
<property name="max_distance" value="-1"/>
<property name="color" value="165,100,35,255"/>
<property name="has_pulse" value="true"/>
</map_settings>
<compass_settings>
<property name="sprite_name" value="ui_game_symbol_zombie"/>
<property name="min_distance" value="0"/>
<property name="max_distance" value="1024"/>
<property name="max_scale_distance" value="32"/>
<property name="color" value="165,100,35,255"/>
<property name="has_pulse" value="true"/>
<property name="icon_clamped" value="false"/>
</compass_settings>
</nav_object_class>
</append>
</config>
+51 -9
View File
@@ -179,7 +179,7 @@
ур. 15 (300 убийств) - чутьё
Тег necroNecromancyLvl30 вместе с этим удалён - им больше никто не
пользуется, вода сидит на necroNecromancyLvl20. -->
<unlock_entry item="necroModKnifeScavengersFeast" unlock_tier="3"/>
<unlock_entry item="necroModKnifeScavengersFeast,bookSummonRatSpirit" unlock_tier="3"/>
<unlock_entry item="necroModKnifeGravesRepose" unlock_tier="4"/>
<unlock_entry item="necroModKnifeDarkSense" unlock_tier="5"/>
</display_entry>
@@ -210,12 +210,43 @@
привязки 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"/>
<!-- ПИТОМЦЫ РАЗНЕСЕНЫ ПО ШКАЛЕ 2026-09-18, прямое указание: "давай разнесём получение
питомцев по шкале скилла. Крыса ок, она доступна почти сразу. Собака пусть
открывается на Подмастерье. Медведь на Ученик. Лютоволк - на 4000 зомби
(200 уровень некромантии)."
До этого трое из четверых лежали в одной куче: Пёс, Жуки и Гриф на Ученике (100),
Медведь, Волк и Банши на Некроманте (150). Стало так:
Дух крысы ур. 3 (60 убийств) - тег necroNecromancyLvl60, Адепт
Зомбопёс ур. 25 (500) - necroNecromancyJourneyman, Подмастерье
Зомбомедведь ур. 100 (2000) - necroNecromancyApprentice, Ученик
Зомбоволк ур. 200 (4000) - necroNecromancyLvl4000, Некромант
ГРИФ И ПЁС ПОМЕНЯНЫ МЕСТАМИ 2026-09-18, после игровой проверки: "пожалуй, стоит
его поменять местами с собакой по скиллу. Он проще собаки, и при этом сейчас
стал полезнее". Гриф встал на 500 убийств (ур. 25), Пёс на 1300 (ур. 65).
Странность, из-за которой это и поменяли: у Грифа урон 20, у Пса 35, но Гриф
летает, сам возвращается к хозяину и разведывает - на практике он оказался
полезнее. Жуки на Ученике и Свиток банши на Некроманте не трогались.
ПРАВИЛО, КОТОРОЕ ЛЕГКО НАРУШИТЬ (уже наступали, см. комментарий про браслет выше):
уровень в теге рецепта и unlock_level того display_entry, где предмет нарисован,
ОБЯЗАНЫ совпадать. display_entry рисует замок сам по себе, по unlock_tier, и про
теги не знает - разойдутся, и на панели будет замок на доступном рецепте. -->
<display_entry icon="ScrollOfDeviation" name_key="craftingNecroNecromancyTier2Name" has_quality="false" unlock_level="25,65,70,85">
<unlock_entry item="thrownBookGrimoireDeviation,bookSummonZombieGriffin" unlock_tier="1"/>
<!-- Зомбогриф, 18.09: "гриф пусть тоже будет на подмастерье, но 1300 (65 уровень
некромантии)". 65 вставлен в unlock_level МЕЖДУ 25 и 70, поэтому unlock_tier
у двух модов ножа ниже сдвинулись на единицу (были 2 и 3, стали 3 и 4).
Список обязан идти по возрастанию: unlock_tier - это номер позиции в нём. -->
<unlock_entry item="bookSummonZombieDog" unlock_tier="2"/>
<unlock_entry item="necroModKnifeDeadMansGrip" unlock_tier="3"/>
<unlock_entry item="necroModKnifeDeadStorm" unlock_tier="4"/>
</display_entry>
<display_entry icon="SummonZombieDog" name_key="craftingNecroNecromancyTier3Name" has_quality="false" unlock_level="100">
<!-- Иконка сменена с SummonZombieDog на SummonZombieBear 18.09: Пёс уехал на
Подмастерье, и группу теперь возглавляет Медведь. -->
<display_entry icon="SummonZombieBear" name_key="craftingNecroNecromancyTier3Name" has_quality="false" unlock_level="100">
<!-- resourceBloodStone добавлен 2026-09-16 (указание «доступен на третьем грейде
некромантии, в самом его начале»). Тир 3 - это и есть третий грейд, группа
«Ученик», а его единственный unlock_level равен 100 уровням (это те же 2000
@@ -228,10 +259,14 @@
себе, по unlock_tier. Поэтому 100 здесь и 100 там ОБЯЗАНЫ совпадать -
иначе замок на панели разойдётся с реальной доступностью рецепта. Совпадают:
один порог, одно значение. -->
<unlock_entry item="bookSummonZombieDog,bookSummonInsectSwarm,bookSummonZombieGriffin,resourceBloodStone" unlock_tier="1"/>
<unlock_entry item="bookSummonZombieBear,bookSummonInsectSwarm,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"/>
<!-- Две ступени вместо одной: у группы появился второй уровень (200), потому что
Лютоволк теперь открывается позже Свитка банши. Иконка сменена на волчью -
Медведь уехал на Ученика. -->
<display_entry icon="SummonZombieWolf" name_key="craftingNecroNecromancyTier4Name" has_quality="false" unlock_level="150,200">
<unlock_entry item="bookBanshee" unlock_tier="1"/>
<unlock_entry item="bookSummonZombieWolf" unlock_tier="2"/>
</display_entry>
<display_entry icon="BlackPortalStone" name_key="craftingNecroNecromancyTier5Name" has_quality="false" unlock_level="250">
<unlock_entry item="thrownStonePortalBlack" unlock_tier="1"/>
@@ -257,10 +292,17 @@
<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"/>
<!-- Зомбогриф, 2026-09-18. 1300 убийств = 65-й уровень. -->
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="65,250" value="1" tags="necroNecromancyLvl1300"/>
<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"/>
<!-- Лютоволк, 2026-09-18. Имя тега В УБИЙСТВАХ (4000), уровень - в уровнях (200):
шкала 20 убийств = 1 уровень, см. шапку файла. Это одноразовый порог внутри
группы "Некромант", а не новая группа - ровно та же схема, что у
necroNecromancyLvl20 и Lvl60 выше. -->
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="200,250" value="1" tags="necroNecromancyLvl4000"/>
<passive_effect name="RecipeTagUnlocked" operation="base_set" level="250,250" value="1" tags="necroNecromancyMaster"/>
</effect_group>
</crafting_skill>
+19 -4
View File
@@ -151,7 +151,7 @@
personal crafting). workbenchCrafting is just the same UI-categorization tag vanilla's
own workbench recipes carry alongside craft_area, not a separate gate. -->
<append xpath="/recipes">
<recipe name="bookSummonZombieDog" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyApprentice">
<recipe name="bookSummonZombieDog" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyLvl1300">
<ingredient name="foodRottingFlesh" count="50"/>
<ingredient name="medicalBloodBag" count="3"/>
<ingredient name="drinkJarBoiledWater" count="4"/>
@@ -234,7 +234,7 @@
resourceFeather/foodRawMeat both verified to exist in vanilla items.xml before use, same
lesson as the earlier wrong guesses. -->
<append xpath="/recipes">
<recipe name="bookSummonZombieGriffin" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyApprentice">
<recipe name="bookSummonZombieGriffin" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyJourneyman">
<ingredient name="resourceFeather" count="30"/>
<ingredient name="resourceBone" count="20"/>
<ingredient name="casinoCoin" count="5"/>
@@ -243,7 +243,7 @@
</recipe>
</append>
<append xpath="/recipes">
<recipe name="bookSummonZombieBear" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyNecromancer">
<recipe name="bookSummonZombieBear" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyApprentice">
<ingredient name="resourceLeather" count="20"/>
<ingredient name="foodRawMeat" count="10"/>
<ingredient name="resourceBone" count="20"/>
@@ -252,7 +252,7 @@
</recipe>
</append>
<append xpath="/recipes">
<recipe name="bookSummonZombieWolf" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyNecromancer">
<recipe name="bookSummonZombieWolf" count="1" craft_area="workbench" tags="learnable,workbenchCrafting,necroNecromancyLvl4000">
<ingredient name="resourceLeather" count="15"/>
<ingredient name="foodRawMeat" count="10"/>
<ingredient name="resourceBone" count="15"/>
@@ -344,6 +344,21 @@
<ingredient name="foodRawMeat" count="5"/>
<ingredient name="resourceYuccaFibers" count="10"/>
</recipe>
<!-- "Свиток духа крысы" - питомец начального уровня, указание 2026-09-18. Состав задан
дословно. craft_area НЕТ намеренно: свиток Зомбоволка требует верстак, но это питомец
ТРЕТЬЕГО уровня, а верстак в первые дни игрок не находит - ровно эта претензия пришла с
Nexus (Derizor, 14.09, про Пространственный браслет). Порог ставится туда, где предмет
нужен, значит и крафт должен быть доступен там же - в личном верстаке.
Тег necroNecromancyLvl60 - это "60 убийств", и он стоит на уровне 3 (progression.xml). -->
<recipe name="bookSummonRatSpirit" count="1" tags="learnable,packMuleCrafting,necroNecromancyLvl60">
<ingredient name="resourceCoal" count="5"/>
<ingredient name="resourceScrapIron" count="2"/>
<ingredient name="resourceVictimSkin" count="1"/>
<ingredient name="resourceZombieAsh" count="2"/>
<ingredient name="resourceCropCottonPlant" count="10"/>
<ingredient name="resourceBone" count="3"/>
<ingredient name="resourceWood" count="1"/>
</recipe>
<recipe name="necroModKnifeDeadMansGrip" count="1" tags="learnable,packMuleCrafting,necroNecromancyLvl1400">
<ingredient name="resourceZombieAsh" count="10"/>
<ingredient name="resourceBone" count="15"/>