Вот развёрнутый ответ форумчанину — он явно копает по делу, местами прав, местами промахивается. Разложу по фактам, как в ретейле (по дизасму L2Server.exe + движка NpcServer):
Привет. Ты в целом правильно нащупал структуру, но пара мест не так — разложу по IDA, а не по java-сборкам (они как раз в этом месте кто во что горазд).
1. Скил агра — это не 2, а 3 эффекта. В официальном скиле агрессии сидят:
- p_target_me — только на игроков;
- p_berserker — только на не-игроков (мобы);
- i_get_agro — только на NPC (добор хейта/desire).
То есть на игрока и на моба реагируют
разные эффекты одного скила, каждый со своим гардом по типу цели.
2. По игрокам (p_target_me). Ты прав: это не накладывается как «шанс». В IDA PumpStart первым делом IsPC-гард, дальше:
if ( !current_target || caster != current_target )
target->Action(pCaster); // форс выбора кастера
++target->m_SkillDurationMod.m_TargetMeCount;
Никакого Random(100) < 75 — это самопал java-сборок. Форс идёт
условно (только если ты ещё не на кастере) и работает через
счётчик m_TargetMeCount, а не через флаг. Пока счётчик > 0 — смена цели заблокирована (клиент сам это держит). На выходе --m_TargetMeCount. Никакого «эффекта на 3 сек с блоком интентов» в стиле фира там нет — блок держится ровно пока счётчик ненулевой, и это чисто клиентская фиксация цели, а не серверный интент-лок.
3. По петам. Да, для p_target_me пет = то же, что игрок: гард IsPC. Пета форсит так же. А вот p_berserker пета
исключает отдельно — там гард !IsPC && !IsSummonOrPet && !ExCrtEffect, то есть на суммонов/петов берсеркер не вешается.
4. По монстрам — тут главное расхождение с твоей картиной. Ты думаешь, что хейт моба считается формулой power * 150 / (level+7) или effectPoint * 150 / (level+7).
В ретейле такой формулы для агра нет. Это как раз java-выдумка. На оффе агр моба идёт не через «величину хейта по формуле», а через
desire-систему NpcServer. Оба мобских эффекта шлют команды на NpcServer:
- i_get_agro → NS_SET_TOP_DESIRE (146). Логика в движке:
- обычный моб: desire кастера = GetTopDesireValue(ATTACK) + 1.0 — то есть на единицу выше текущего топа очереди, не по формуле от power/level;
- босс (CT_BOSS): 0.0 — не форсит вообще (боссы иммунны).
- p_berserker → NS_ABSOLUTE_DESIRE (161): AddAttackDesire(mob, caster, -1.0) — абсолютное desire (жёсткий лок на кастере на время эффекта), снимается RemoveAbsoluteDesire на выходе.
То есть агр моба — это не «дать N хейта», а «поставить приоритет чуть выше текущего лидера» (мягкий, i_get_agro) + «абсолютный лок» (жёсткий, p_berserker). Никакого * 150 / (level+7).
5. Про effect_point = -4312. Отрицательный effect_point у агра — это НЕ величина хейта. Он определяет
направление реакции AI (что для мобо-AI это враждебное действие → идёт как атака/агрессия), а не число хейта. Хейт/desire формируется в desire-системе, как выше. Так что прогонять -4312 через effectPoint * 150 / (level+7) не нужно — это смешение двух разных вещей: effect_point (маркер типа воздействия для AI) и desire (реальный приоритет цели).
6. Про классы/расы ненависти — тут ты прав. У NPC на оффе действительно есть группы ненависти (HateRace*, HateClass*, HateClassGroup* с бустами), и они модифицируют desire от атак конкретной цели. Большинство java-сборок это игнорят и сводят всё к линейной формуле — отсюда и power * 150 / (level+7), которую ты нашёл. Это аппроксимация, которую кто-то вывел, чтобы хоть как-то заменить desire-модель. В оффе же desire от удара = damage × (модификаторы ненависти к расе/классу цели) × веса, а не одна формула.
Итого, где java-сборки врут:
- искусственный шанс агра на игрока (Random(100)<75) — выдумка, на оффе 100% и через счётчик;
- power/effectPoint * 150 / (level+7) — выдумка, на оффе агр моба идёт через desire (topDesire+1 / абсолютное), а не через формулу хейта;
- effect_point как «величина хейта» — нет, это маркер типа воздействия для AI.
Если хочешь строго — механика держится на NpcServer-командах 146 (SET_TOP_DESIRE) и 161 (ABSOLUTE_DESIRE), а не на арифметике хейта в L2Server. Java-сборки не эмулируют desire-очередь, поэтому и лепят формулы-заменители.