Продолжение #3643. Симптом: маг-дамаг (в т.ч. волхв, каст через obj) почти не проходит по мобам с резистами. Разобрал механику — вопрос балансный, на решение Свентовиту.
Правка: в первой версии я ошибочно написал про «некапнутый NPC-резист ≥200 → иммунитет». Резисты мобов клампятся (см. ниже), 200 недостижимо. Ниже — корректные числа.
Резисты мобов ограничены 95
src/gameplay/classes/recalc_mob_params_by_vnum.cpp:470 (и :488 для MR):
base_value = std::max(0, std::min(base_value, kMaxMobResist)); // kMaxMobResist = 95
И стихийные GET_RESIST, и GET_MR у мобов ограничены 95. Полного иммунитета через данные мобов нет.
Магический урон режется в двух местах
Ступень 1 — бинарный гейт по GET_MR (главный эффект)
CalcTotalSpellDmg, src/gameplay/magic/magic.cpp:338:
if (number(1,100) > std::min(ch->IsNpc() ? kMaxNpcResist : kMaxPcResist, GET_MR(victim))) {
// считаем урон
}
// иначе total_dmg = 0 -- весь каст в ноль
ch — кастер. Игрок (ch->IsNpc()==false) → кап kMaxPcResist = 75.
- Моб с
MR >= 75 → number(1,100) > 75 → каст проходит лишь в 25% случаев, 75% гасятся полностью (всё-или-ничего).
- Тот же принцип у дыхания:
fight_hit.cpp:860. Механика давняя, не регрессия.
Константы: kMaxPcResist = 75 (resist.h), kMaxNpcResist = 100 (structs.h), kMaxMobResist = 95.
Ступень 2 — процентный стихийный резист ApplyResist
src/gameplay/mechanics/resist.cpp:84, применяется в damage.cpp:646 для kMagicDmg:
result = value - (resistance + number(0, resistance)) * value / 200.0; // max(0, ...)
Срез = (R + rand(0,R))/200: в среднем 0.75*R %, максимум R %.
R = 75 → срез в среднем ~56%, максимум 75%.
R = 95 → срез в среднем ~71%, максимум 95%.
Это НЕ иммунитет — часть урона всегда проходит.
Итог
Моб MR≈75–95 + стихийный ≈95: бинарный гейт пропускает ~25% кастов, стихийный дорезает их ещё на ~71% в среднем → сквозь проходит ~7% номинала. Отсюда ощущение «магия не работает». Это суммарный эффект двух ступеней по дизайну, а не баг обнуления.
Про лог [MAG DAMAGE]
log("[MAG DAMAGE] …") (magic.cpp:619) стоит до расчёта урона, поэтому даже зарезиненный каст её пишет (кроме DoT-веток с ранним return). Резистами лог не гасится.
Развилка (нужно решение Свентовита)
- Смягчить бинарный MR-гейт для игроков — главный кандидат. Сейчас это all-or-nothing 75%. Сделать частичным (снижать урон, а не отсекать) либо понизить кап.
- Пересмотреть стек двух резистов (MR-гейт + стихийный) — не слишком ли жёстко вместе.
- Код не трогать — считать, что конкретные мобы перекручены по
MR/резистам в данных, править мир.
Числа и путь — за Свентовитом. По готовности решения сделаю точечный фикс.
Продолжение #3643. Симптом: маг-дамаг (в т.ч. волхв, каст через obj) почти не проходит по мобам с резистами. Разобрал механику — вопрос балансный, на решение Свентовиту.
Резисты мобов ограничены 95
src/gameplay/classes/recalc_mob_params_by_vnum.cpp:470(и :488 для MR):И стихийные
GET_RESIST, иGET_MRу мобов ограничены 95. Полного иммунитета через данные мобов нет.Магический урон режется в двух местах
Ступень 1 — бинарный гейт по
GET_MR(главный эффект)CalcTotalSpellDmg,src/gameplay/magic/magic.cpp:338:ch— кастер. Игрок (ch->IsNpc()==false) → капkMaxPcResist = 75.MR >= 75→number(1,100) > 75→ каст проходит лишь в 25% случаев, 75% гасятся полностью (всё-или-ничего).fight_hit.cpp:860. Механика давняя, не регрессия.Константы:
kMaxPcResist = 75(resist.h),kMaxNpcResist = 100(structs.h),kMaxMobResist = 95.Ступень 2 — процентный стихийный резист
ApplyResistsrc/gameplay/mechanics/resist.cpp:84, применяется вdamage.cpp:646дляkMagicDmg:Срез =
(R + rand(0,R))/200: в среднем0.75*R %, максимумR %.R = 75→ срез в среднем ~56%, максимум 75%.R = 95→ срез в среднем ~71%, максимум 95%.Это НЕ иммунитет — часть урона всегда проходит.
Итог
Моб
MR≈75–95+ стихийный≈95: бинарный гейт пропускает ~25% кастов, стихийный дорезает их ещё на ~71% в среднем → сквозь проходит ~7% номинала. Отсюда ощущение «магия не работает». Это суммарный эффект двух ступеней по дизайну, а не баг обнуления.Про лог
[MAG DAMAGE]log("[MAG DAMAGE] …")(magic.cpp:619) стоит до расчёта урона, поэтому даже зарезиненный каст её пишет (кроме DoT-веток с ранним return). Резистами лог не гасится.Развилка (нужно решение Свентовита)
MR/резистам в данных, править мир.Числа и путь — за Свентовитом. По готовности решения сделаю точечный фикс.