Skip to content

«Расплести чары» снимает награды от dgaffect (свиток опыта, свиток силы) #3675

Description

@bylins

@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
  • kWitcherybuff="Y" — 132 дебаффа (зоны 339, 610, 822, 972 и другие) станут неснимаемыми через «расплести чары».
  • убрать kAfDispellable у kWitchery — награды перестанет снимать вообще что угодно, включая «развеять магию».

Одна личность обслуживает две противоположные роли — расщеплять придётся в любом случае.

Предложение

1. Разделить личность DG-аффекта по знаку модификатора (do_dg_affect).

af.affect_type = (value >= 0) ? EAffect::kBoon : EAffect::kWitchery;

плюс в affects.xml: kWitcherybuff="N", новая kBoonbuff="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 — за тобой, ничего не трогаем без твоего слова.

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions