Счёт убийств Некромантии переехал из XML в код

Чинит баг: скилл не засчитывал часть убийств зомби. Причин было две, и обе
закрываются одним ходом.

ПРИЧИНА 1: ОДИН КЛАСС - НЕ ВСЕ ЗОМБИ. Счёт висел append-ом на zombieTemplateMale.
Человекоподобные покрыты (effect_group у entity_class наследуется через extends, и
все шаблоны зомби сходятся к Male), но пять зомби-ЗВЕРЕЙ наследуют животную ветку
и до него не доходят вовсе:

    animalZombieBear   extends animalBear
    animalZombieBoar   extends animalBoar
    animalZombieDog    extends animalWolf
    animalZombieVulture extends animalTemplateHostile
    animalZombieVultureRadiated extends animalZombieVulture

Убийство зомбопса, зомбомедведя, зомбокабана и зомбоворона не считалось никак.
Вороны попадаются постоянно - это и была бОльшая часть "не всегда засчитывает".

ПРИЧИНА 2: target="other" - БУКВАЛЬНЫЙ УБИЙЦА. Триггер onOtherKilledSelf с
требованием EntityTagCompare tags="player" начислял только за убийство своей
рукой. Робомолот, кровотечение и питомцы требование не проходят, хотя опыт игрок
за них получает: ваниль определяет получателя не по убийце, а по DamageSource - в
EntityAlive.AwardKillXPServer, где для этого есть обращение к BuffClass (DoT) и
отдельный флаг bTrapKillXP (ловушки).

Требование было ОДНО на оба эффекта, поэтому недосчитывался и
necroZombieKillsCVar, а это урон Ножа некроманта (items.xml: Damage = CVar / 10).
Баг тихо занижал ещё и нож.

РЕШЕНИЕ: Postfix на EntityPlayer.AddKillXP. Этот метод вызывается ровно из одного
места во всей сборке - из AwardKillXPServer, то есть уже ПОСЛЕ того, как ваниль
разобрала DamageSource и решила, чей это фраг (проверено сканированием IL).
Мы не повторяем её логику и не угадываем владельца турели или автора
кровотечения - забираем готовый ответ. Наш счёт совпадает с опытом на экране по
построению, включая случаи, о которых мы не подумали.

ФИЛЬТР ПО ТЕГУ zombie, А НЕ ПО КЛАССУ. Проверено по данным: zombieBiker,
zombieArlene, zombieBoe, zombieSpider несут "entity,zombie,...", зомби-звери -
"entity,animal,zombie,zombieAnimal,...". Тег есть у всех. Работает это благодаря
тому, что Tags у entity_class НЕ наследуется через extends: каждый реально
спавнящийся зомби выписывает теги сам, а безтеговые шаблоны не спавнятся. Тег
переживёт и новых зомби из патчей игры, и чужие моды.

Повышение уровня воспроизводит MinEventActionAddProgressionLevel.Execute шаг в
шаг по его IL: GetProgressionValue, Level+1, кламп по MaxLevel, для крафтового
скилла AddCraftingSkillNotification и HandleCheckCrafting, затем
bProgressionStatsChanged и bPlayerStatsChanged под !isEntityRemote.
HandleCheckCrafting легко выбросить и дорого потерять - без него рецепты рискуют
не заметить, что открылись. Уведомление с _bAddOnlyIfNotExisting=true, чтобы в
орду не всплывал тост на каждый труп.

Config/entityclasses.xml: append снят ЦЕЛИКОМ, на его месте комментарий, почему
возвращать нельзя - XML-триггер рядом с патчем засчитает убийство своей рукой
ДВАЖДЫ. Это единственная ловушка переезда.

Сборка: 0 ошибок (4 прежних MSB3277). Проверено рефлексией по собранной DLL:
атрибут нацелен верно, перегрузка AddKillXP ровно одна, имя параметра killedEntity
совпадает с ванильным (Harmony инжектит по имени - опечатка дала бы молчаливо
неработающий патч), PatchAll подхватывает файл сам.

НЕ ПРОВЕРЕНО В ИГРЕ. Отдельно: питомцы НЕ гарантированы - патч следует решению
ванили, а не принимает его. Если ваниль не зачисляет владельцу убийство
питомцем, не зачислит и он; это отдельная работа, а не ошибка здесь.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
AlexCube
2026-09-16 21:21:11 +03:00
co-authored by Claude Opus 5
parent e890999391
commit 229b436420
4 changed files with 197 additions and 13 deletions
+25 -13
View File
@@ -1,17 +1,29 @@
<config>
<!-- Step 1: lifetime zombie kill counter.
zombieTemplateMale is the root template every zombie entity_class extends
(directly, or indirectly via zombieTemplateShort), so patching it here covers
every zombie variant in the game without listing them individually. -->
<append xpath="/entity_classes/entity_class[@name='zombieTemplateMale']">
<effect_group>
<requirement name="EntityTagCompare" target="other" tags="player"/>
<triggered_effect trigger="onOtherKilledSelf" action="ModifyCVar" target="other" cvar="necroZombieKillsCVar" operation="add" value="1"/>
<!-- "Некромантия" skill: +1 level per zombie kill, capped by its own max_level.
See progression.xml for why this drives the skill instead of reading books. -->
<triggered_effect trigger="onOtherKilledSelf" action="AddProgressionLevel" target="other" progression_name="craftingNecroNecromancy" level="1"/>
</effect_group>
</append>
<!-- СЧЁТ УБИЙСТВ ПЕРЕЕХАЛ В КОД 2026-09-16. Здесь СОЗНАТЕЛЬНО ничего нет, и вернуть это
обратно нельзя - см. HarmonySrc/NecromancyKillCreditPatch.cs.
Тут стоял append на zombieTemplateMale с двумя onOtherKilledSelf-эффектами
(ModifyCVar necroZombieKillsCVar и AddProgressionLevel craftingNecroNecromancy) под общим
требованием EntityTagCompare target="other" tags="player". Он был сломан дважды:
1. Один класс - не все зомби. Пять зомби-ЗВЕРЕЙ наследуют животную ветку и до
zombieTemplateMale не доходят вовсе (animalZombieBear extends animalBear,
animalZombieBoar extends animalBoar, animalZombieDog extends animalWolf,
animalZombieVulture extends animalTemplateHostile, animalZombieVultureRadiated).
Убийство зомбопса, зомбомедведя, зомбокабана и зомбоворона не считалось никак.
2. target="other" - это БУКВАЛЬНЫЙ убийца, а не тот, кому ваниль зачла убийство.
Робомолот, кровотечение и питомцы требование tags="player" не проходят, хотя опыт
игрок за них получает: ваниль определяет получателя по DamageSource, в
EntityAlive.AwardKillXPServer.
Требование было одно на оба эффекта, поэтому вместе со скиллом недосчитывался и
necroZombieKillsCVar - то есть занижался урон Ножа некроманта (items.xml: Damage =
necroZombieKillsCVar / 10).
ОБА эффекта теперь делает Postfix на EntityPlayer.AddKillXP - единственной точке, где
ваниль уже решила, чей это фраг. ЕСЛИ ВЕРНУТЬ ЭТОТ append НА МЕСТО, убийство своей рукой
будет засчитано ДВАЖДЫ: и здесь, и в патче. Ровно это и проверять, если уровень вдруг
начнёт расти по два за труп. -->
<!-- "Зомбособака" (Zombie Dog pet): BACKLOG.md item 3. Extends the vanilla hostile
animalZombieDog (same prefab/physics/sounds - a real zombie dog model, not a reskinned