@Svjatovit — твоя епархия (issue.affect-migration, dgaffect), поэтому ничего не трогаем без твоего слова.
Симптом
Жалоба игрока (Хендела, воззвание к богам, 2 августа 2026):
закл !расплести чары! - убирает с персонажа в том числе аффект от свитка опыта, свитка силы и т.п.
Проверено по коду и конфигам, воспроизводится логикой, а не случайностью.
Механика
kUnweave («расплести чары») — единственный талант в игре с debuff_only="Y" (spells.xml:6606):
<unaffect affect_flags="kAfDispellable" dispel_bonus="50" debuff_only="Y">
<remove any_of="*" />
</unaffect>
Фильтр в CollectRemovals (src/gameplay/magic/magic.cpp:2058):
return AffectMatchesFlags(aff, flags)
&& !(debuff_only && affects::AffectBuffKind(aff->affect_type) == affects::EBuff::kYes);
То есть debuff_only защищает только явно объявленные баффы (EBuff::kYes); всё неклассифицированное (kAmbiguous) снимается.
APPLY-аффекты от dgaffect получают общую личность kWitchery (dg_misc.cpp:330), а она в lib/cfg/affects.xml:866 описана так:
<affect id="kWitchery">
<flags val="kAfDispellable"/>
</affect>
Атрибута buff= нет → kAmbiguous → под фильтр не попадает → снимается. Награды выдаются именно так: свиток опыта (триггер 200232, зона 2002) делает dgaffect %actor% *множитель 3 20 0.
Почему очевидные правки не годятся
Подсчёт по трекаемому миру (lib/world/zones/*/triggers.yaml, 348 вызовов dgaffect, значения нормализованы с учётом того, что %…% раскрываются до разбора команды):
| Значение модификатора |
Вызовов |
| положительное (награды, баффы мобам) |
154 |
| отрицательное (проклятия боссов, ловушки) |
132 |
| из переменной, знак известен только в рантайме |
62 |
kWitchery → buff="Y" — 132 дебаффа (зоны 339, 610, 822, 972 и другие) станут неснимаемыми через «расплести чары».
- убрать
kAfDispellable у kWitchery — награды перестанет снимать вообще что угодно, включая «развеять магию».
Одна личность обслуживает две противоположные роли — расщеплять придётся в любом случае.
Предложение
1. Разделить личность DG-аффекта по знаку модификатора (do_dg_affect).
af.affect_type = (value >= 0) ? EAffect::kBoon : EAffect::kWitchery;
плюс в affects.xml: kWitchery → buff="N", новая kBoon → buff="Y", обе остаются kAfDispellable.
Решение принимается в момент наложения, поэтому 62 вызова со значением из переменной обрабатываются правильно сами собой. Уже висящие на игроках аффекты останутся kWitchery до перевыдачи — при часовых длительностях это переживаемо.
Оговорка по знаку: у броня и спас-бросков «лучше» не всегда значит «больше». По миру знак совпадает с намерением (броня -20 вешается на %enemy%), но если хочется строгости — нужна таблица направления по EApply, и тогда это уже отдельная задача.
2. Две правки в affects.xml (чистые данные).
Кроме kWitchery под удар попадают ровно два аффекта — это все, кто одновременно kAfDispellable и без buff=:
kEnlarge («увеличение») → buff="Y" — сейчас «расплести чары» сносит и его, это вторая такая же жалоба, просто ещё не пришедшая;
kLessening («уменьшение») → buff="N".
3. Перевернуть направление отказа в debuff_only (одна строка).
&& !(debuff_only && affects::AffectBuffKind(aff->affect_type) != affects::EBuff::kNo);
Из 139 аффектов в affects.xml классификации нет у 46. Сейчас забытая метка означает «срывается с игрока дружественной чисткой», после правки — «не трогается». Для дружественной чистки направление отказа правильно именно такое.
Пункт 3 без пунктов 1–2 делать нельзя: иначе «расплести чары» перестанет снимать DG-дебаффы совсем.
Разделение работ
Пункты 2 и 3 — данные и одна строка, готов сделать сразу.
Пункт 1 добавляет новую сущность в EAffect и лежит внутри issue.affect-migration — за тобой, ничего не трогаем без твоего слова.
@Svjatovit — твоя епархия (
issue.affect-migration,dgaffect), поэтому ничего не трогаем без твоего слова.Симптом
Жалоба игрока (Хендела, воззвание к богам, 2 августа 2026):
Проверено по коду и конфигам, воспроизводится логикой, а не случайностью.
Механика
kUnweave(«расплести чары») — единственный талант в игре сdebuff_only="Y"(spells.xml:6606):Фильтр в
CollectRemovals(src/gameplay/magic/magic.cpp:2058):То есть
debuff_onlyзащищает только явно объявленные баффы (EBuff::kYes); всё неклассифицированное (kAmbiguous) снимается.APPLY-аффекты от
dgaffectполучают общую личностьkWitchery(dg_misc.cpp:330), а она вlib/cfg/affects.xml:866описана так:Атрибута
buff=нет →kAmbiguous→ под фильтр не попадает → снимается. Награды выдаются именно так: свиток опыта (триггер 200232, зона 2002) делаетdgaffect %actor% *множитель 3 20 0.Почему очевидные правки не годятся
Подсчёт по трекаемому миру (
lib/world/zones/*/triggers.yaml, 348 вызововdgaffect, значения нормализованы с учётом того, что%…%раскрываются до разбора команды):kWitchery→buff="Y"— 132 дебаффа (зоны 339, 610, 822, 972 и другие) станут неснимаемыми через «расплести чары».kAfDispellableуkWitchery— награды перестанет снимать вообще что угодно, включая «развеять магию».Одна личность обслуживает две противоположные роли — расщеплять придётся в любом случае.
Предложение
1. Разделить личность DG-аффекта по знаку модификатора (
do_dg_affect).плюс в
affects.xml:kWitchery→buff="N", новаяkBoon→buff="Y", обе остаютсяkAfDispellable.Решение принимается в момент наложения, поэтому 62 вызова со значением из переменной обрабатываются правильно сами собой. Уже висящие на игроках аффекты останутся
kWitcheryдо перевыдачи — при часовых длительностях это переживаемо.Оговорка по знаку: у
броняи спас-бросков «лучше» не всегда значит «больше». По миру знак совпадает с намерением (броня -20вешается на%enemy%), но если хочется строгости — нужна таблица направления поEApply, и тогда это уже отдельная задача.2. Две правки в
affects.xml(чистые данные).Кроме
kWitcheryпод удар попадают ровно два аффекта — это все, кто одновременноkAfDispellableи безbuff=:kEnlarge(«увеличение») →buff="Y"— сейчас «расплести чары» сносит и его, это вторая такая же жалоба, просто ещё не пришедшая;kLessening(«уменьшение») →buff="N".3. Перевернуть направление отказа в
debuff_only(одна строка).&& !(debuff_only && affects::AffectBuffKind(aff->affect_type) != affects::EBuff::kNo);Из 139 аффектов в
affects.xmlклассификации нет у 46. Сейчас забытая метка означает «срывается с игрока дружественной чисткой», после правки — «не трогается». Для дружественной чистки направление отказа правильно именно такое.Пункт 3 без пунктов 1–2 делать нельзя: иначе «расплести чары» перестанет снимать DG-дебаффы совсем.
Разделение работ
Пункты 2 и 3 — данные и одна строка, готов сделать сразу.
Пункт 1 добавляет новую сущность в
EAffectи лежит внутриissue.affect-migration— за тобой, ничего не трогаем без твоего слова.