Lineage 2 HF Geodata или recast4j для FakePlayerAI

RedZerGG

Путник
Участник
Сообщения
25
Розыгрыши
0
Репутация
0
Реакции
28
Баллы
55
Привет всем , разработка идёт полным ходом создания полноценного мира ИИ игроков против друг друга на ХФ, наткнулся на неприятную ситуацию боты тупят не правильно бегают не правильно строят путь не обходят препятствия улучшал скрипты всё равно но как только интегрировал recast4j всё стало чётко но спустя пару минут началось то же самое, проблема Боты с recast4j обходят препятствия , но доходят только по первых координат дальше тупо стоят и юзают чат, некоторые фармят гремлинов больше не куда не идут, некоторые просто стоят но при этом все юзают чат и так у всех рас, потом заметил если их общее количество при старте 300, то если подойти к главной локации спавна игроков у каждой расы то начинаются жесткие лаги я так понял боты путь не находят и досят, что можно придумать или есть какие либо наработки что бы боты не выглядели как скриптовые а были похожи на реальных людей передвижение поведение , жду ваших идей ответов предложений
 
А можно ещё что ни будь интересное главный скрипт поиска правильного передвижения, то есть допустим бот поднял 6й лвл у меня переключается на файл FakePlayerQuestAI.java он смотрит где Нпс выбирает какой ему нужен квест и обратно переключает на FakePlayerMovement.java приходит или портируется в город переключает на CityNavigation.java ) можно ли без сторонних софтов максимально уменьшить нагрузку от Ботов удалить recast4j, и сделать ботов через ядро л2мобис, залейте кто ни будь хоть одну папку логики ботов как они двигаются как двигаются в городе в катакомбах как берут квест можно у кого ни будь логику посмотреть, если есть возможность поделитесь пожалуйста я просто уже месяц не могу выбрать со стартовой локации и добраться до 1й профы макс 6-7лвл я уже рейты обратно вернул х1, а то ждать х0.05 что то долго походу можно даже х2 поставить что бы побыстрее настроить
ты дипсиком что ли пользуешься? но даже он такие тупые результаты не выдает о которых ты пишешь
 
Неадекватное поведение
ты дипсиком что ли пользуешься? но даже он такие тупые результаты не выдает о которых ты пишешь
Ну и пошел ты [А по щам?] тебя не кто вообще не спрашивает , хули ты свой клюв суёшь в чужие разговоры касающиеся развития своей индустрии ебун тухлый длинноносый Депсик хуик [А по щам?]
 
Ну и пошел ты [А по щам?] тебя не кто вообще не спрашивает , хули ты свой клюв суёшь в чужие разговоры касающиеся развития своей индустрии ебун тухлый длинноносый Депсик хуик [А по щам?]
1788946978916.webp
 
А можно ещё что ни будь интересное главный скрипт поиска правильного передвижения, то есть допустим бот поднял 6й лвл у меня переключается на файл FakePlayerQuestAI.java он смотрит где Нпс выбирает какой ему нужен квест и обратно переключает на FakePlayerMovement.java приходит или портируется в город переключает на CityNavigation.java ) можно ли без сторонних софтов максимально уменьшить нагрузку от Ботов удалить recast4j, и сделать ботов через ядро л2мобис, залейте кто ни будь хоть одну папку логики ботов как они двигаются как двигаются в городе в катакомбах как берут квест можно у кого ни будь логику посмотреть, если есть возможность поделитесь пожалуйста я просто уже месяц не могу выбрать со стартовой локации и добраться до 1й профы макс 6-7лвл я уже рейты обратно вернул х1, а то ждать х0.05 что то долго походу можно даже х2 поставить что бы побыстрее настроить
Тебе уже давали, но к сожалению там боты оказались тупые.
 
Тебе уже давали, но к сожалению там боты оказались тупые.
Да я понял но я инегрировал переписал под мобиус, они у меня вообще жестко тупить начали , то есть они стали появляться убивать одного моба и тупо стоять, отлючились соски чат навигация, то есть они начали залазить в друг друга, а до этого первого кода было я сделал что они у невбие не брали квест а сразу на старте у них 200 соск у Фигтеров Хуманов свои , что они 6й лвл взяли шли в деревню и там бафались и гуарда брали на соски квест, но я всё дальше и дальше удаляюсь от цели эксперементирую с новыми логиками ) вот ИИ что говорит ,
У тебя два независимых пути оптимизации:

Путь finding 247ms → 2.8ms

  • IXYZ убирает аллокации Location и GC-давление
  • Но 2.8ms у Aristo — это ещё и алгоритмическая разница: вероятно, он использует прекалькулированные графы переходов, hierarchical pathfinding или Theta* с кешированием, а не полный перебор на каждом запросе
  • Без исходников GeoEngine и PathFinding Mobius ты вряд ли добьёшься 2.8ms — это работа на уровне C++/native
Боты: стратегия + скиллы

  • Сейчас у тебя FakePlayerQuestAI — линейные стадии, CombatAI — базовый thinkAndAct
  • Качество зависит от:
    • скорости выбора цели (selectTarget в FakePlayerAI)
    • предикатов применения скилов (FakePlayerSkillManager)
    • реакции на ситуацию: HP/MP пороги, количество целей, дистанции
    • координации в пати (если нужно)
 
Да я понял но я инегрировал переписал под мобиус, они у меня вообще жестко тупить начали , то есть они стали появляться убивать одного моба и тупо стоять, отлючились соски чат навигация, то есть они начали залазить в друг друга, а до этого первого кода было я сделал что они у невбие не брали квест а сразу на старте у них 200 соск у Фигтеров Хуманов свои , что они 6й лвл взяли шли в деревню и там бафались и гуарда брали на соски квест, но я всё дальше и дальше удаляюсь от цели эксперементирую с новыми логиками ) вот ИИ что говорит ,
У тебя два независимых пути оптимизации:

Путь finding 247ms → 2.8ms

  • IXYZ убирает аллокации Location и GC-давление
  • Но 2.8ms у Aristo — это ещё и алгоритмическая разница: вероятно, он использует прекалькулированные графы переходов, hierarchical pathfinding или Theta* с кешированием, а не полный перебор на каждом запросе
  • Без исходников GeoEngine и PathFinding Mobius ты вряд ли добьёшься 2.8ms — это работа на уровне C++/native
Боты: стратегия + скиллы

  • Сейчас у тебя FakePlayerQuestAI — линейные стадии, CombatAI — базовый thinkAndAct
  • Качество зависит от:
    • скорости выбора цели (selectTarget в FakePlayerAI)
    • предикатов применения скилов (FakePlayerSkillManager)
    • реакции на ситуацию: HP/MP пороги, количество целей, дистанции
    • координации в пати (если нужно)
Так а ты иишку в браузере чтоль заебуешь, чтобы она тебе ботов сделала?
 
Так а ты иишку в браузере чтоль заебуешь, чтобы она тебе ботов сделала?
Не в браузере, на прямую в , скоро инишки будут игры в UE-5-6 будут уже автоматоп переписывать игры под UE. а мы всё еб....я с этой л2 ))))

IntelliJ IDEA​

 

Вложения

  • Screenshot 2026-09-09 151942.webp
    Screenshot 2026-09-09 151942.webp
    104,9 КБ · Просмотры: 46
Ну тогда странно, что он тебе сам реализовать их не может.
Или он не умеет работать с файлами проекта?
Вот именно ))) я из за этого тут ищу опытных людей которые подскажу как это старое Г...но ) исправить чтобы вообще нога человека не ступала туда , боты сами себе хозяева ) я выше описывал идею ) я её придумал давно , есть один тип AlexBayev он сначала сделал с3 с ботами как я мечтал до выхода в мир ИИ, сейчас уже полноценный доделывает с4 там что то до интерлюда, но ядро с 25 года не выкладывает всё ищит с кого бабки срубить продать, а с ц3 выложил но там боты дрявые ещё не доконца настроены не та логика что нужно, ХФ и Годес оф дестракшен это болееее новые хроники не на столько деревянные чем до)
 
Готовые решения тебе вряд ли дадут, Аристо тебе дал кусочек золота, но этот кусочек вставить в мобиус не получится, т.к. если положить золото в говно то оно просто будет вонять.

Включи логгер, или попроси иишку написать тебе логгер для отслеживания движения твоих ботов, например. И смотреть, где затыки, где они тупят, почему, итд. Она сама увидит.
 
Не в браузере, на прямую в , скоро инишки будут игры в UE-5-6 будут уже автоматоп переписывать игры под UE. а мы всё еб....я с этой л2 ))))

IntelliJ IDEA​

ну если там такие "инженеры" будут рулить этими ИИшками, то мы будем ждать этих реализаций еще лет 200
Сообщение объединено:
Вот именно ))) я из за этого тут ищу опытных людей которые подскажу как это старое Г...но ) исправить чтобы вообще нога человека не ступала туда , боты сами себе хозяева ) я выше описывал идею ) я её придумал давно , есть один тип AlexBayev он сначала сделал с3 с ботами как я мечтал до выхода в мир ИИ, сейчас уже полноценный доделывает с4 там что то до интерлюда, но ядро с 25 года не выкладывает всё ищит с кого бабки срубить продать, а с ц3 выложил но там боты дрявые ещё не доконца настроены не та логика что нужно, ХФ и Годес оф дестракшен это болееее новые хроники не на столько деревянные чем до)
подозреваю, что ИИшка твоя все умеет, но твое личное косноязычие делает эту идею нежизнеспособной. как сказали выше - сначала научись внятно формулировать мысли, а то судя по треду у тебя с этим проблемы. ну в общем, выздоравливай! всех благ!
 
Готовые решения тебе вряд ли дадут, Аристо тебе дал кусочек золота, но этот кусочек вставить в мобиус не получится, т.к. если положить золото в говно то оно просто будет вонять.

Включи логгер, или попроси иишку написать тебе логгер для отслеживания движения твоих ботов, например. И смотреть, где затыки, где они тупят, почему, итд. Она сама увидит.
Прикол в том что ии может сама переиспользовать постройку маршрута и геодвиг в целом, как делают обычные игроки. И довольно не плохо все это описать в коде. Но этот крендель не может даже тут сформулировать что он хочет, я уже молчу о реализации в коде, я представляю как у llm взрывается мозг от его запросов
 
Вот именно ))) я из за этого тут ищу опытных людей которые подскажу как это старое Г...но ) исправить чтобы вообще нога человека не ступала туда , боты сами себе хозяева ) я выше описывал идею ) я её придумал давно , есть один тип AlexBayev он сначала сделал с3 с ботами как я мечтал до выхода в мир ИИ, сейчас уже полноценный доделывает с4 там что то до интерлюда, но ядро с 25 года не выкладывает всё ищит с кого бабки срубить продать, а с ц3 выложил но там боты дрявые ещё не доконца настроены не та логика что нужно, ХФ и Годес оф дестракшен это болееее новые хроники не на столько деревянные чем до)
Ну так купи подписку клауд или кодекс, они прекрасно умеют работать с файлами проекта, и при составлении четкой по этапной задачи, напишут что угодно.
А еще лучше писать сразу доку по работе, где будет все описанно, чтобы ии потом не начинал с нуля выдумывать.
 
Ну так купи подписку клауд или кодекс, они прекрасно умеют работать с файлами проекта, и при составлении четкой по этапной задачи, напишут что угодно.
А еще лучше писать сразу доку по работе, где будет все описанно, чтобы ии потом не начинал с нуля выдумывать.
Вот вот а то так и происходило до тех пор пока не сделал
 

Вложения

  • Screenshot 2026-09-09 201423.webp
    Screenshot 2026-09-09 201423.webp
    25 КБ · Просмотры: 35
Селекторы скиллов и оркестратор тасков на эвристике. Разделения по профам нет, просто универсальный скрипт, где каждый бот пользуется теми скиллами и предметами, которые у него есть.

Java:
package ru.nts.scripts.player;

import host.anzo.core.service.ThreadPoolService;
import ru.nts.commons.math.Rnd;
import ru.nts.core.network.outgoing.S_PARTY_MEMBER_POSITION;
import ru.nts.core.network.outgoing.S_SYSTEM_MSG;
import ru.nts.gameserver.game.messages.items.AutoSoulShotItemAction;
import ru.nts.gameserver.game.messages.items.UseItemAction;
import ru.nts.gameserver.game.models.points.Location;
import ru.nts.gameserver.game.models.social.party.Party;
import ru.nts.gameserver.game.stats.effects.Effect;
import ru.nts.gameserver.game.stats.effects.sub_effects.impl.*;
import ru.nts.gameserver.game.stats.impl.CreatureStats;
import ru.nts.gameserver.game.stats.skills.Skill;
import ru.nts.gameserver.game.world.World;
import ru.nts.gameserver.game.world.objects.creatures.Creature;
import ru.nts.gameserver.game.world.objects.creatures.npcs.Npc;
import ru.nts.gameserver.game.world.objects.creatures.npcs.Servitor;
import ru.nts.gameserver.game.world.objects.creatures.npcs.types.base.Pet;
import ru.nts.gameserver.game.world.objects.creatures.npcs.types.base.Summon;
import ru.nts.gameserver.game.world.objects.creatures.npcs.types.base.Warrior;
import ru.nts.gameserver.game.world.objects.creatures.players.FakePlayer;
import ru.nts.gameserver.game.world.objects.creatures.players.Player;
import ru.nts.gameserver.game.world.objects.items.Item;
import ru.nts.gameserver.system.data.memory.SkillTable;
import ru.nts.gameserver.system.data.pts.holder.CategoryDataHolder;
import ru.nts.gameserver.system.engines.geo.GeoData;
import ru.nts.gameserver.system.engines.geo.GeoEngine;
import ru.nts.gameserver.system.enums.*;
import ru.nts.gameserver.system.interfaces.IVarsSessionable;
import ru.nts.gameserver.tools.PositionUtils;
import ru.nts.scripts.nasc.EventNotifier;
import ru.nts.scripts.nasc.NASCScript;
import ru.nts.scripts.nasc.interfaces.INASCConstant;
import ru.nts.scripts.nasc.interfaces.shared.ISharedCreature;

import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class player_base extends NASCScript {

    private static final int CHECK_SPAWN_AFK = -2551;

    public player_base() {}

    @Override
    public void DEBUG_AI(ISharedCreature sCreature, int reply) {
        if (getNASCActor() instanceof Player player_actor) {
            if (sCreature instanceof Player player && player.isGM()) {
                switch (reply) {
                    case 0 -> player_actor.whisperTo("Debug", player);
                }
            }
        }
    }

    @Override
    public void CHECK_CMD_FOLLOW() { //Проверяем, не пора ли бросить все и бежать за хозяином
        if (getNASCActor() instanceof Player player_actor) {
            Party party = player_actor.getParty();
            if (party != null && (player_actor.isReal() || player_actor instanceof FakePlayer fake_actor && fake_actor.getMasterUserID() == party.getPartyLeaderOID())) {
                Player leader = party.getPartyLeader();
                if (leader.distance3D(player_actor) >= 1000) {
                    EventNotifier.getInstance().notify(player_actor, 1000, ENASCEvent.CMD_FOLLOW_ME);
                }
            }
        }
    }

    @Override
    public void CHECK_SWEEP() {
        TrySweep();
    }

    @Override
    public void CHECK_SELF_BUFF() {
        TrySelfBuff(EDesire.get.extra_high());
    }

    @Override
    public void CHECK_SELF_HEAL() {
        if (getNASCActor() instanceof Player player_actor) {
            final CreatureStats stats = player_actor.getStats();
            if (player_actor.isInCombat()) {
                if (CategoryDataHolder.isInCategory(heal_master2_3, player_actor)) {
                    if (stats.getCurrentHpPercents() <= 33) {
                        TryHeal(player_actor, 5, EDesire.get.extra_high());
                    } else if (stats.getCurrentHpPercents() <= 70) {
                        TryHeal(player_actor, 3, EDesire.get.high() + 50000);
                    } else {
                        if (stats.getCurrentHpPercents() <= 90) {
                            TryHeal(player_actor, 1, EDesire.get.normal(20000));
                        }
                    }
                } else if (CategoryDataHolder.isInCategory(buff_master2_3, player_actor) && stats.getCurrentHpPercents() <= 50) {
                    TryHeal(player_actor, 5, EDesire.get.high() + 50000);
                } else {
                    if (CategoryDataHolder.isInCategory(attribute_group_summoner, player_actor) && player_actor.getServitor() instanceof Summon summon && summon.getStats().getCurrentHpPercents() <= 90) {
                        TryHeal(summon, 3, EDesire.get.high() + 10000);
                    }
                }
            } else {
                if (stats.getCurrentHpPercents() < 100) {
                    TryHeal(player_actor, 1, EDesire.get.normal(1000));
                } else {
                    if (player_actor.getServitor() instanceof Summon summon && summon.getStats().getCurrentHpPercents() < 100) {
                        TryHeal(summon, 1, EDesire.get.normal(-1000));
                    }
                }
            }
        }
    }

    @Override
    public void CHECK_DISPEL() {
        if (getNASCActor() instanceof Player player_actor) {
            for (Effect effect : player_actor.getAllEffectsSnapshot()) {
                if (effect.getSkill() instanceof Skill skill && skill.isDebuff()) {
                    if (!TryDispellDebuff(player_actor, skill.getAbnormalType(), skill.getAbnormalLevel(), EDesire.get.high())) {
                        RequestPartyDispell(skill.getAbnormalType(), skill.getAbnormalLevel());
                        break;
                    }
                }
            }
        }
    }

    @Override
    public void CREATED() {
        if (getNASCActor() instanceof Player player_actor) {
            RemoveAllPCHateInfoIF(COND_IS_INVALID, 0);
            RemoveAllPCHateInfoIF(COND_HAS_HATE_LESS_THAN, 100);
            RemoveAllPCHateInfoIF(COND_IS_FAR_AWAY, 3000);

            TrySummonServitor();
            TrySummonCubics();

            if (player_actor instanceof FakePlayer fake_actor && fake_actor.isWild()) {
                AddTimerEx(CHECK_SPAWN_AFK, 60000);
            }
        }
    }

    @Override
    public void RECHECK_TARGET() {
        if (getNASCActor() instanceof Player player_actor) {

            RemoveAllPCHateInfoIF(COND_IS_INVALID, 0);

            TrySweep();

            if (player_actor.isCastingNow() || player_actor.isAttackingNow()) {
                EventNotifier.getInstance().notify(player_actor, 1000, ENASCEvent.RECHECK_TARGET);
                return;
            }

            Creature current_target = player_actor.getTargetAsCreature();
            if (current_target == null || current_target.isDead() || current_target == player_actor || current_target.isInArea(EAreaType.peace_zone) || player_actor.getAI().target_black_list.contains(current_target.getObjectId()) || !GeoEngine.CanSee(player_actor, current_target)) {
                if (current_target != null && current_target != player_actor) {
                    RemovePCAttackDesire(current_target.getObjectId());
                }
                player_actor.setTarget(null, EChangeTargetReason.CTR_BY_USER);
                current_target = null;

                //Сначала известные цели в границах прямой видимости.
                for (Creature creature : player_actor.getHateList().getMostHated(10)) {
                    if (player_actor.getHateList().getHate(creature) > 100 && !player_actor.getAI().target_black_list.contains(creature.getObjectId()) && player_actor.canSee(creature) && creature.isAlive()) {
                        current_target = creature;
                        break;
                    }
                }

                //Потом известные цели к которым можно пройти.
                if (current_target == null) {
                    for (Creature creature : player_actor.getHateList().getMostHated(10)) {
                        if (player_actor.getHateList().getHate(creature) > 100 && !player_actor.getAI().target_black_list.contains(creature.getObjectId()) && player_actor.canMove(creature) && creature.isAlive()) {
                            current_target = creature;
                            break;
                        }
                    }
                }

                //Если нет приоритетной цели по хейту, то ищем ближайшую(Только дикие боты)
                if (current_target == null && player_actor instanceof FakePlayer fake_actor && fake_actor.isWild()) {

                    List<Creature> around = fake_actor.getAI().lookAround(fake_actor.sight_distance() / 3, 500);

                    //Сначала ищем чаров вокруг.
                    //Порабощенных ботов ненавидим и агримся сразу же
                    //Если слишком близко подошел, с кармой или флагом, ну или просто не понравился)
                    current_target = around.stream().filter(creature -> !creature.isInArea(EAreaType.peace_zone)).filter(creature -> !fake_actor.getAI().target_black_list.contains(creature.getObjectId())).filter(creature -> fake_actor.canSee(creature) || fake_actor.canMove(creature)).filter(creature -> {
                        if (creature instanceof Player player) {
                            //Порабощенных ботов ненавидим и агримся сразу же
                            if (player instanceof FakePlayer fake) {
                                return fake.isSlave();
                            }
                            //Если слишком близко подошел, с кармой или флагом, ну или просто не понравился)
                            if (fake_actor.distance3D(player) < 100 || (player.isGuilty() > 0 || player.getKarma() > 0) && Rnd.chance(15 - fake_actor.getDifficulty().ordinal() * 2)) {
                                return true;
                            }
                        }
                        return false;
                    }).min(fake_actor.getRangeComparator()).orElse(null);

                    //Если чаров нет, то ищем мобов
                    if (current_target == null) {
                        current_target = around.stream().filter(creature -> !creature.isInArea(EAreaType.peace_zone)).filter(creature -> !fake_actor.getAI().target_black_list.contains(creature.getObjectId())).filter(creature -> fake_actor.canSee(creature) || fake_actor.canMove(creature)).filter(creature -> {
                            if (creature instanceof Warrior monster && Math.abs(monster.getLevel() - fake_actor.getLevel()) <= 20) {
                                return true;
                            }
                            return false;
                        }).min(fake_actor.getRangeComparator()).orElse(null);
                    }
                }
            }

            if (current_target != null) {
                DoAttack(current_target, Math.max(200, player_actor.getHateList().getHate(current_target)), false);
            }
        }
    }

    @Override
    public void NO_DESIRE() {
        if (getNASCActor() instanceof Player player_actor) {
            //Рабы отрабатывают социальный контракт.
            if (player_actor instanceof FakePlayer fake_actor && (fake_actor.isSlave() || fake_actor.isWildPrivate())) {
                //В этом методе, мы чинимся. Если потеряли мастера, ищем. Если не нашли вблизи - бежим на дальнюю дистанцию.
                if (fake_actor.lost_master()) {
                    List<Player> list = World.getInstance().getAroundPlayers(fake_actor, 1500, fake_actor.getReflectionID(),false);
                    for (Player player : list) {
                        if (player != null && fake_actor.isMyMaster(player)) {
                            //Если увидели родителя, обновляем таск следования за ним.
                            AddFollowDesire2(player, 100, eAFD2_FT_DIST_AND_DEGREE, 120 + Rnd.getInt(0, 50, true), fake_actor.follow_angle);
                            return;
                        }
                    }
                    Creature master = fake_actor.getMasterUser();
                    if (master != null && fake_actor.isSlave() && TryTeleportWithCommunityBoard(master.getLoc(), -1)) {
                        AddMovePartialDesire(master.getLoc(), 5000, 90);
                        return;
                    }
                }

                if (fake_actor.getNextLoc() != null && fake_actor.getNextLoc().distance2D(fake_actor) > 1000) {
                    Location temp = fake_actor.getNextLoc();
                    if (TryTeleportWithCommunityBoard(temp, -1)) {
                        fake_actor.setNextLoc(null);
                    }
                    AddMovePartialDesire(temp, 5000, 90);
                    return;
                }

                //Только если нет потребностей в починке состояния следования за мастером, то тогда чекаем, можно ли кого-то подлечить.
                if (CategoryDataHolder.isInCategory(recom_magic_group, fake_actor)) {
                    if (fake_actor.getParty() != null && fake_actor.getStats().getCurrentMpPercents() > 50) {
                        for (Player member : fake_actor.getParty().getPartyMembers()) {
                            if (member != null && member.getStats().getCurrentHpPercents() < 100) {
                                Skill skill = SelectSkillForHeal(member, 0);
                                if (skill != null) {
                                    AddUseSkillDesireEx(skill.getAimingTarget(fake_actor, member), skill, ST_HEAL, AMT_MOVE_TO_TARGET, FALSE, EDesire.get.normal(), 0);
                                }
                            }
                        }
                    }
                }
            }

            EventNotifier.getInstance().notify(player_actor, ENASCEvent.RECHECK_TARGET);
        }
    }

    @Override
    public void SEE_CREATURE(ISharedCreature sCreature) {
        if (getNASCActor() instanceof Player player_actor) {
            if (sCreature instanceof Creature creature) {

                if (player_actor.isDead()) {
                    return;
                }

                if (player_actor instanceof FakePlayer fake && !fake.isWild()) {
                    // Смотреть по сторонам и анализировать обстановку могут только дикие мобы.
                    return;
                }



                if (creature.isInArea(EAreaType.peace_zone)) {
                    // Если цель в мирной зоне, то не трогаем ее.
                    return;
                }

                if (player_actor.getHateList().getHate(creature) > 0) {
                    // Если на существо уже есть хейт, то новый не добавляем.
                    return;
                }

                double hate = 0;
                double range_add = player_actor.sight_distance() / Math.max(1, player_actor.distance3D(creature));
                // В целом, в SEE_CREATURE может попасть либо моб, либо чар.
                if (creature instanceof Warrior monster && Math.abs(monster.getLevel() - player_actor.getLevel()) <= 20) {
                    hate = 100 + range_add;
                } else if (creature instanceof Player player) {
                    if (player.isReal() && player.getKarma() > 0) {
                        return;
                    }
                    if (player instanceof FakePlayer fake) {
                        if (fake.isWild() && (fake.getFakeFactionId() == -1 || player_actor.isFactionMember(fake))) {
                            return;
                        }
                        if (fake.isSlave() || player_actor.isFactionEnemy(fake)) {
                            hate = 2000 + range_add;
                        }
                    }
                    if (player_actor.distance3D(player) < 100) {
                        hate = 200;
                    }
                } else {
                    return;
                }

                if (hate > 0) {
                    AddPCHate(creature, hate, true, 60);
                }
            }
        }
    }

    @Override
    public void DISAPPEAR_CREATURE(ISharedCreature sCreature, int objectId) {
        if (sCreature instanceof Creature creature) {
            RemovePCAttackDesire(creature.getObjectId());
        }
    }

    @Override
    public void ATTACKED(ISharedCreature sAttacker, int damage, long skill_name_id, int skill_id) {
        if (getNASCActor() instanceof Player player_actor) {
            if (sAttacker instanceof Creature attacker) {
                if (damage > 0) {
                    RequestPartyHeal(); //Всегда проверяем, нужно ли слать запрос на хил в пати.
                }

                if (attacker instanceof FakePlayer fake && fake.isWild() && !player_actor.isFactionEnemy(fake)) {
                    return;
                }

                if (damage != 0 && !(attacker instanceof FakePlayer)) {
                    AddPCHate(attacker, damage, true, 300);
                }
                if (damage == 0 && skill_name_id != 0 && !(attacker instanceof FakePlayer)) {
                    AddPCHate(attacker, 100, true, 300);
                }
                if (damage != 0 || skill_name_id != 0) {
                    EquipWeaponPC(); //Всегда проверяем надета ли пушка, когда нас бьют. Если нет, надеваем.
                    SummonServitor(1000); //Всегда проверяем, нужно ли суммонить сервитора.
                }

                double desire = EDesire.get.byDamage(damage, player_actor);

                if (attacker.isBlockMove() && (player_actor.getAI().battle_distance.get() > attacker.getStats().getPhysicalAttackRange() || player_actor.can_away) || player_actor.isClanMember(attacker) || player_actor.isPartyMember(attacker) || Chance(player_actor.getAI().regroup_chance.get()) && DistancePC(attacker, false) < player_actor.getAI().battle_distance.get() && (player_actor.can_away || attacker.getStats().calcCurrentMoveSpeed() <= player_actor.getStats().calcCurrentMoveSpeed())) {
                    if (!TryAway(attacker, EDesire.get.extra_high())) {
                        AddMoveToDesire(PositionUtils.findAroundPosition(player_actor, player_actor.getAI().battle_distance.get() - 50, player_actor.getAI().battle_distance.get() + 50, player_actor.getReflectionID()), EDesire.get.high());
                    }

                    if (!(player_actor.isClanMember(attacker) || player_actor.isPartyMember(attacker))) {
                        DoAttack(attacker, desire - 1, false);
                    }
                    return;
                }

                if (attacker.distance3D(player_actor) > player_actor.getAI().battle_distance.get() && player_actor.getHateList().getHate(attacker) > 5000 || attacker.getStats().calcMoveSpeed(EMoveType.MT_FAST) > player_actor.getStats().calcMoveSpeed(EMoveType.MT_FAST)) {
                    TryCatch(attacker, desire * 2);
                }
                DoAttack(attacker, desire, false);
            }
        }
    }

    @Override
    public void ABNORMAL_STATUS_CHANGED(ISharedCreature speller, long skill_name_id, int skill_id, int skill_level) {
        if (getNASCActor() instanceof Player player_actor) {
            Skill skill = GetSkill(skill_name_id);
            if (skill == null) {
                return;
            }

            //Дебафы из SOA, которые означают, что бот попытался убить РБ и тот его проклял. Это гг и бота надо убирать.
            //s_seed_of_annihilation_bestacon_silence1
            //s_seed_of_annihilation_cocracon_silence1
            //s_seed_of_annihilation_torum_ba_turn_stone1
            if ((skill_name_id== 435814401 || skill_name_id == 435879937 || skill_name_id ==  419823617) && player_actor instanceof FakePlayer fake_actor && fake_actor.isWild()) {
                player_actor.logout(ELogoutType.KICK);
                return;
            }

            //Цикличные дебафы, которые вызывают сами себя. Ботов они не убивают, а висят бесконечно.
            //s_npc_deadly_poison
            //s_npc_deadly_bleed
            if ((skill_id == 6815 || skill_id == 6816) && player_actor instanceof FakePlayer fake_actor && fake_actor.isWild()) {
                if (player_actor.getEffectBySkillId(skill_id) instanceof Effect effect) {
                    effect.stop(true);
                }
                return;
            }

            //Если положили дебаф, смотрим, есть ли у нас что-то, что может его снять.
            if (skill.isDebuff() && skill_level > 0) {
                if (!TryDispellDebuff(player_actor, skill.getAbnormalType(), skill.getAbnormalLevel(), EDesire.get.high())) {
                    RequestPartyDispell(skill.getAbnormalType(), skill.getAbnormalLevel());
                }
            }

            //Если кончился дизарм, то шедулим надевание пухи
            if (Skill_ContainSubEffect(skill, p_disarm.class) && skill_level == 0) {
                EquipWeaponPC();
            }

            if (skill.containSubEffect(p_transform.class)) {
                player_actor.doSelfAnalysis();
                player_actor.getAI().changeTactics();
            }

            if (skill.containSubEffect(p_block_move.class) && CategoryDataHolder.isInCategory(INASCConstant.fighter_group, player_actor) && CategoryDataHolder.isInCategory(INASCConstant.mage_group, speller)) {
                player_actor.getAI().changeWeapon(skill_level > 0, skill_level <= 0);
            }
        }
    }

    @Override
    public void USE_SKILL_FINISHED(long skill_name_id, ISharedCreature sTarget, int success) {
        if (getNASCActor() instanceof Player player_actor) {
            if (sTarget instanceof Creature cTarget) {
                RequestPartyRecharge();

                if ((player_actor.isClanMember(cTarget) || player_actor.isPartyMember(cTarget))) {
                    StopPCHate(cTarget);
                    return;
                }

                if (cTarget.isDead()) {
                    StopPCHate(cTarget);
                    return;
                }

                if (player_actor.getHateList().getHate(cTarget) <= 0) {
                    return;
                }

                Skill skill = SkillTable.getInstance().getInfo(skill_name_id);

                if (CategoryDataHolder.isInCategory(heal_master2_3, player_actor)) {
                    if (skill != null && skill.isGood()) {
                        return;
                    }
                }

                DoAttack(cTarget, EDesire.get.normal(5000), skill != null && skill.getNextAction() == ENextSkillAction.attack);
            }
        }
    }

    @Override
    public void ATTACK_FINISHED(ISharedCreature sTarget) {
        if (getNASCActor() instanceof Player player_actor) {
            if (sTarget instanceof Creature cTarget) {
                if (player_actor.isClanMember(cTarget) || player_actor.isPartyMember(cTarget)) {
                    StopPCHate(cTarget);
                    return;
                }

                if (cTarget.isDead()) {
                    StopPCHate(cTarget);
                    return;
                }

                if (player_actor.getHateList().getHate(cTarget) <= 0) {
                    return;
                }

                DoAttack(cTarget, EDesire.get.normal(5000), true);
            }
        }
    }

    //Кастомные методы
    @Override
    public void PARTIAL_MOVE_FINISH(int x, int y, int z){
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            Location target_loc = new Location(x, y, z);
            if (!target_loc.isEmptyPoint(GeoData.WORLD_MAP_MIN_X, GeoData.WORLD_MAP_MAX_X, GeoData.WORLD_MAP_MIN_Y, GeoData.WORLD_MAP_MAX_Y) && target_loc.distance2D(fake_actor) > 200) {
                AddMovePartialDesire(target_loc, 5000, EDesire.get.small());
            } else if (!target_loc.isEmptyPoint(GeoData.WORLD_MAP_MIN_X, GeoData.WORLD_MAP_MAX_X, GeoData.WORLD_MAP_MIN_Y, GeoData.WORLD_MAP_MAX_Y) && target_loc.distance2D(fake_actor) <= 200) {
                fake_actor.setNextLoc(null);
            }
        }
    }

    @Override
    public void MOVE_TO_NPC_FINISH(int target_object_id, int next_action_id, Object object1, Object object2){
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            Creature target = World.getObjects().getWorldObjectAs(target_object_id, Creature.class);
            if (target != null) {
                try {
                    switch (next_action_id) {
                        case MTN_TELEPORT -> { //Teleport, after move finishing
                            if (fake_actor.isAlive() && object1 instanceof Location temp_loc) {
                                if (fake_actor.getMasterUser() instanceof Player master && master.isGM()) {
                                    fake_actor.whisperTo("[NPC: " + target.getName() + "][Телепорт: " + temp_loc.toString() + "]", master);
                                }
                                fake_actor.teleToLocation(temp_loc.getX(), temp_loc.getY(), temp_loc.getZ(), temp_loc.getReflectionID());
                            }
                        }
                        case MTN_BYPASS_NPC -> { //Request Bypass, after move finishing
                            if (fake_actor.isAlive() && object1 instanceof String bypass) {
                                try {
                                    if (target instanceof Npc npc) {
                                        fake_actor.setLastNpc(npc);
                                        fake_actor.setSessionVar(IVarsSessionable.FAKE_BYPASS_NPC, target_object_id);
                                        if (fake_actor.getMasterUser() instanceof Player master && master.isGM()) {
                                            fake_actor.whisperTo("[NPC: " + npc.getName() + "][Bypass: " + bypass + "]", master);
                                        }
                                    }
                                    fake_actor.getBypassHandler().checkBypass(bypass, false);
                                } finally {
                                    fake_actor.unsetSessionVar(IVarsSessionable.FAKE_BYPASS_NPC);
                                }
                            }
                        }
                    }
                } catch (Exception ignored) {
                }
            }
        }
    }

    @Override
    public void CHAT(int sender_object_id, int chat_type, String message) {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            Player sender = World.getObjects().getPlayerByObjectID(sender_object_id);
            if (sender != null && fake_actor.isMyMaster(sender)) {
                switch (chat_type) {
                    case 3 -> { //ESayPacketType.PARTY
                        String answer = "";
                        if (message.equalsIgnoreCase("ближе")) {
                            answer = "+";
                            fake_actor.close = true;
                        } else if (message.equalsIgnoreCase("дальше")) {
                            answer = "+";
                            fake_actor.close = false;
                        }
                        if (!answer.isEmpty() && !answer.isBlank()) {
                            fake_actor.sayToParty(answer);
                        }
                    }
                }
            }
        }
    }

    @Override
    public void PARTY_DIED(ISharedCreature party_private, ISharedCreature killer) {
        if (getNASCActor() instanceof Player player_actor) {
            if (party_private instanceof Servitor servitor && servitor.getPlayer() == player_actor) {
                SummonServitor(TimeUnit.SECONDS.toMillis(servitor.getTemplate().corpse_time + 1));
            }
        }
    }

    @Override
    public void PARTY_JOIN() {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            if (fake_actor.isSlave() && fake_actor.getMasterUser() instanceof Player master && master.isReal()) {

                Player requestor = fake_actor.getTransactionRequester();

                fake_actor.setTransactionRequester(null);

                if (requestor == null) {
                    return;
                }

                if (requestor.isFake()) {
                    return;
                }

                if (requestor.isCursedWeaponEquipped() || fake_actor.isCursedWeaponEquipped()) {
                    return;
                }

                if (!requestor.equals(master) && !requestor.isPartyMember(master)) {
                    return;
                }

                requestor.setTransactionRequester(null);

                if (requestor.getParty() == null) {
                    return;
                }

                if (fake_actor.getTransactionType() != ETransactionType.PARTY || fake_actor.getTransactionType() != requestor.getTransactionType() && requestor != master && !requestor.isPartyMember(master)) {
                    return;
                }

                if (fake_actor.getSessionVar("event_team_pvp", String.class) != null && fake_actor.getTeam() != requestor.getTeam()) {
                    return;
                }

                if (requestor.getParty().member_count() >= 9) {
                    requestor.sendSysMessage(S_SYSTEM_MSG.m155_THE_PARTY_IS_FULL);
                    return;
                }

                fake_actor.joinParty(requestor.getParty());
                fake_actor.getParty().broadcastToPartyMembersExclude(fake_actor, new S_PARTY_MEMBER_POSITION(fake_actor));

                for (Player member : fake_actor.getParty().getPartyMembers()) {
                    StopPCHate(member);
                    RemovePCAttackDesire(member.getObjectId());
                    if (member instanceof FakePlayer fake) {
                        StopPCHate(fake_actor);
                        RemovePCAttackDesire(fake_actor.getObjectId());
                    }
                    if (fake_actor.isMyMaster(member)) {
                        AddFollowDesire2(member, 100, eAFD2_FT_DIST_AND_DEGREE, 120 + Rnd.getInt(0, 50, true), fake_actor.follow_angle);
                    }
                }

                fake_actor.getTargetSelector().block();
            } else if (fake_actor.isWildPrivate() && fake_actor.getMasterUser() instanceof FakePlayer master && master.isWild()) {

                Player requestor = fake_actor.getTransactionRequester();

                fake_actor.setTransactionRequester(null);

                if (requestor == null) {
                    return;
                }

                if (requestor.isReal()) {
                    return;
                }

                if (requestor.isCursedWeaponEquipped() || fake_actor.isCursedWeaponEquipped()) {
                    return;
                }

                if (!requestor.equals(master)) {
                    return;
                }

                requestor.setTransactionRequester(null);

                if (requestor.getParty() == null) {
                    return;
                }

                if (fake_actor.getTransactionType() != ETransactionType.PARTY || fake_actor.getTransactionType() != requestor.getTransactionType() && requestor != master && !requestor.isPartyMember(master)) {
                    return;
                }

                if (fake_actor.getSessionVar("event_team_pvp", String.class) != null && fake_actor.getTeam() != requestor.getTeam()) {
                    return;
                }

                if (requestor.getParty().member_count() >= 9) {
                    return;
                }

                fake_actor.joinParty(requestor.getParty());

                for (Player member : fake_actor.getParty().getPartyMembers()) {
                    StopPCHate(member);
                    RemovePCAttackDesire(member.getObjectId());
                    if (member instanceof FakePlayer fake) {
                        StopPCHate(fake_actor);
                        RemovePCAttackDesire(fake_actor.getObjectId());
                    }
                    if (fake_actor.isMyMaster(member)) {
                        AddFollowDesire2(member, 100, eAFD2_FT_DIST_AND_DEGREE, 120 + Rnd.getInt(0, 50, true), fake_actor.follow_angle);
                    }
                }

                fake_actor.getTargetSelector().block();
            }
        }
    }

    @Override
    public void PARTY_LEFT() {
        if (getNASCActor() instanceof Player player_actor) {
            player_actor.getTargetSelector().unblock();
            RemovePCDesire(EPState.PSTATE_FOLLOW);
        }
    }

    @Override
    public void CMD_FOLLOW_ME() {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            if (fake_actor.getMasterUserID() != 0) {
                Party party = fake_actor.getParty();
                if (party != null && party.getPartyLeaderOID() == fake_actor.getMasterUserID()) {
                    Player leader = party.getPartyLeader();
                    if (leader != null) {
                        RemoveAllPCHateInfoIF(COND_ALL, 0);
                        RemoveAllPCDesire();
                        AddMoveToDesire(leader.getX(), leader.getY(), leader.getZ(), EDesire.get.max());
                    }
                }
            }
        }
    }

    @Override
    public void PARTY_MEMBER_LEFT() {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            if (fake_actor.getParty() instanceof Party party) {
                for (Player member : party.getPartyMembers()) {
                    if (member.getObjectId() == fake_actor.getMasterUserID()) { //Мы не выходим из пати, пока наш мастер в ней.
                        return;
                    }
                }
                party.tryLeaveParty(fake_actor);
            }
        }
    }

    @Override
    public void REVIVE_REQUEST(int requester_object_id, int pet) {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            Creature reviever = World.getObjects().getWorldObjectAs(requester_object_id, Creature.class);
            if (fake_actor.death_count.get() <= 0) {
                if (reviever != null) {
                    fake_actor.whisperTo("Даже не пытайся!", reviever);
                }
                return;
            }
            if (reviever != null && (fake_actor.isPartyMember(reviever) || fake_actor.isClanMember(reviever))) {
                fake_actor.stopTask(EFutureType.PLAYER_AI_REQUEST_RESURRECT_TASK);
                fake_actor.stopTask(EFutureType.FAKE_PLAYER_LOGOUT_TASK);

                fake_actor.reviveAnswer(1);
                if (pet == 1) {
                    ThreadPoolService.getInstance().schedule(() -> fake_actor.rebuff(true, true), 3000);
                }
            } else {
                fake_actor.reviveAnswer(0);
            }
        }
    }

    @Override
    public void PARTY_ACTION_REQUEST(int help_type, int requester_object_id, Object object1, Object object2) {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            Creature creature = World.getObjects().getWorldObjectAs(requester_object_id, Creature.class);
            if (creature instanceof Player requester && fake_actor.isPartyMember(requester)) {
                if (help_type == AT_ATTACK && object1 instanceof Creature target_creature && object2 instanceof Number desire) {
                    if (target_creature.getObjectId() == fake_actor.getAI().current_attacked_target.get()) {
                        return;
                    }

                    if (fake_actor.getAI().af_protect_master.get() && !fake_actor.isMyMaster(requester)) {
                        return;
                    }

                    double chance = 0;
                    if (EDesire.get.normal() < desire.doubleValue()) {
                        chance = 75;
                    } else {
                        chance = 30;
                    }
                    if (fake_actor.isMyMaster(requester)) {
                        chance += 20;
                    }
                    if (!fake_actor.isInCombat()) {
                        chance += 20;
                    } else {
                        if (fake_actor.getStats().getCurrentHpPercents() < 30) {
                            chance -= 30;
                        } else {
                            chance -= 10;
                        }
                    }
                    if (fake_actor.distance3D(target_creature) > 1000) {
                        chance -= 15;
                    } else {
                        if (fake_actor.distance3D(target_creature) > 700) {
                            chance -= 5;
                        } else {
                            chance += (1000 - fake_actor.distance3D(target_creature)) / 100;
                        }
                    }

                    if (Rnd.chance(chance)) {
                        DoAttack(target_creature, desire.doubleValue(), false);
                    }
                } else if (help_type == AT_PROTECT && object1 instanceof Creature attacker && object2 instanceof Number desire) {
                    if (attacker.getObjectId() == fake_actor.getAI().current_attacked_target.get()) {
                        return;
                    }

                    if (fake_actor.getAI().af_protect_master.get() && !fake_actor.isMyMaster(requester)) {
                        return;
                    }

                    double chance = 0;
                    if (EDesire.get.normal() < desire.doubleValue()) {
                        chance = 75;
                    } else {
                        chance = 30;
                    }
                    if (fake_actor.isMyMaster(requester) || (CategoryDataHolder.isInCategory(heal_master2_3, requester))) {
                        chance += 30;
                    }
                    if (!fake_actor.isInCombat()) {
                        chance += 20;
                    } else {
                        if (fake_actor.getStats().getCurrentHpPercents() < 30) {
                            chance -= 30;
                        } else {
                            chance -= 10;
                        }
                    }
                    if (fake_actor.distance3D(requester) > 1000) {
                        chance -= 15;
                    } else {
                        if (fake_actor.distance3D(requester) > 700) {
                            chance -= 5;
                        } else {
                            chance += (1000 - fake_actor.distance3D(requester)) / 100;
                        }
                    }

                    if (Rnd.chance(chance)) {
                        DoAttack(attacker, desire.doubleValue(), false);
                    }
                }

                switch (help_type) {
                    case AT_HEAL -> {
                        if (object1 instanceof Number hp_percent && object2 instanceof Number counter) {
                            double actual_hp_percent = requester.getStats().getCurrentHpPercents();
                            if (actual_hp_percent > 30 && !CategoryDataHolder.isInCategory(heal_master2_3, fake_actor)) {
                                return;
                            }
                            if (actual_hp_percent < hp_percent.doubleValue() - 5 || Rnd.chance(100 - hp_percent.doubleValue()) || counter.intValue() > 0) {
                                TryHeal(requester, counter.intValue(), EDesire.get.high());
                            }
                        }
                    }
                    case AT_RECHARGE -> {
                        if (object1 instanceof Number mp_percent && object2 instanceof Number counter) {
                            double actual_mp_percent = requester.getStats().getCurrentMpPercents();
                            if (actual_mp_percent < mp_percent.doubleValue() - 5 || Rnd.chance(100 - mp_percent.doubleValue()) || counter.intValue() > 0) {
                                TryRecharge(requester, counter.intValue(), EDesire.get.high());
                            }
                        }
                    }
                    case AT_DISPELL -> {
                        if (object1 instanceof EAbnormalStatusSlotType abnormal && object2 instanceof Number level) {
                            TryDispellDebuff(requester, abnormal, level.intValue(), EDesire.get.high());
                        }
                    }
                    case AT_BUFF -> {
                    }
                    case AT_RESURRECT -> {
                        int chance = switch (fake_actor.getDifficulty()) {
                            case EASY -> 10;
                            case NORMAL -> 20;
                            case MANUAL, HARD -> 30;
                            case EXTRA_HARD -> 50;
                            default -> 0;
                        };

                        if (!TryResurrect(requester, chance * 2, EDesire.get.high())) {
                            if (Rnd.chance(chance)) {
                                Item item = fake_actor.getInventory().getItemBySkillSubEffect(i_resurrection.class);
                                if (item != null) {
                                    if (fake_actor.distance3D(requester) <= 100) {
                                        fake_actor.executeMessage(new UseItemAction(fake_actor, requester, item, false));
                                    } else {
                                        AddMoveToTargetDesire(requester,90,-1, EDesire.get.extra_high());
                                    }
                                }
                            }
                        }
                    }
                    case AT_SUMMON -> {
                    }
                    case AT_ESCAPE -> {
                        final Item item = fake_actor.getInventory().getItemBySkillSubEffect(i_escape.class);
                        if (item != null) {
                            fake_actor.executeMessage(new UseItemAction(fake_actor, fake_actor, item, false));
                        }
                    }
                    case AT_CMD_ATTACK -> {
                        if (fake_actor.getAI().af_protect_master.get()) {
                            return;
                        }
                        if (fake_actor.isMyMaster(requester) && object1 instanceof Number number) {
                            int target_object_id = number.intValue();
                            if (target_object_id == fake_actor.getAI().current_attacked_target.get()) {
                                return;
                            }
                            Creature target = World.getObjects().getWorldObjectAs(target_object_id, Creature.class);
                            if (target != null) {
                                fake_actor.getAI().high_priority_target = target_object_id;
                                RemoveAllPCAttackDesire();
                                DoAttack(target, EDesire.get.extra_high(500), false);
                            }
                        }
                    }
                    case AT_CMD_STOP -> {
                        if (fake_actor.isMyMaster(requester)) {
                            try {
                                fake_actor.setTarget(null, EChangeTargetReason.CTR_BY_USER);
                                fake_actor.getAI().high_priority_target = 0;
                                fake_actor.getAI().current_attacked_target.set(0);
                                RemoveAllPCHateInfoIF(COND_ALL, 0);
                                RemoveAllPCDesire();
                                RemoveAllPCAttackDesire();
                                AddFollowDesire2(requester, 100, eAFD2_FT_DIST_AND_DEGREE, 120 + Rnd.getInt(0, 50, true), fake_actor.follow_angle);
                                AddMoveToDesire(PositionUtils.getPointInRadius(fake_actor.getLoc(), requester.getLoc(), Rnd.getInt(50, 75, true), fake_actor.follow_angle), 200);
                                if (fake_actor.getServitor() instanceof Pet pet) {
                                    MakeServitorAction(requester, 100, 17, -2);
                                } else if (fake_actor.getServitor() instanceof Summon summon) {
                                    MakeServitorAction(requester, 100, 23, -2);
                                }
                            } finally {
                                fake_actor.setTarget(null, EChangeTargetReason.CTR_BY_USER);
                            }
                        }
                    }
                    case AT_CMD_PICKUP -> {
                        if (fake_actor.isMyMaster(requester) && object1 instanceof Number number) {
                            Item item_to_pickup = World.getObjects().getWorldObjectAs(number.intValue(), Item.class);
                            if (item_to_pickup != null) {
                                AddGetItemDesireEx(item_to_pickup, EDesire.get.extra_high());
                            }
                        }
                    }
                }
            }
        }
    }

    @Override
    public void CUBIC_DISAPPEAR(int cubic_id, int cubic_level, long skill_index) {
        if (getNASCActor() instanceof Player player_actor) {
            long[] cubics = SelectSkillsForSummonCubic(cubic_id);
            if (cubics.length > 0) {
                AddUseSkillDesireEx(player_actor, cubics[0], ST_HEAL, AMT_STAND, TRUE, EDesire.get.normal(), 0);
            }
        }
    }

    @Override
    public void BUFFER_EFFECT_REMOVED(){
        if (getNASCActor() instanceof Player player_actor) {
            player_actor.getAI().buff_removed_count.incrementAndGet();
        }
    }

    @Override
    public void TIMER_FIRED_EX(int timer_id, Object arg1, Object arg2) {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            if (timer_id == CHECK_SPAWN_AFK) {
                boolean near = fake_actor.getController() != null && fake_actor.distance3D(fake_actor.getController()) < 300;
                if (near && fake_actor.isAlive() && !fake_actor.isInCombat()) {
                    Location loc = PositionUtils.findAroundPosition(fake_actor.getLoc(), 500, 1000, fake_actor.getReflectionID());
                    if (fake_actor.getZ() > loc.getZ()) {
                        fake_actor.teleToLocation(loc);
                    } else {
                        AddTimerEx(CHECK_SPAWN_AFK, 250);
                    }
                }
            }
        }
        super.TIMER_FIRED_EX(timer_id, arg1, arg2);
    }

    @Override
    public void SERVITOR_SUMMON_FINISHED() {
        if (getNASCActor() instanceof Player player_actor) {
            player_actor.rebuff(true, true);
            if (player_actor.hasComponent(EComponentType.ITEM_SELECTOR)) {
                final List<Item> snapshot = player_actor.getInventory().getItemsSnapshot();
                player_actor.getItemSelector().items.stream()
                        .map(id -> player_actor.getInventory().getItemByItemId(id, snapshot))
                        .filter(Objects::nonNull)
                        .filter(item -> item.getTemplate().getAction().isSummonSoulShot())
                        .forEach(item -> new AutoSoulShotItemAction(player_actor, item.getItemId(), true));
            }
        }
    }

    @Override
    public void MY_DYING(ISharedCreature sLast_attacker) {
        if (getNASCActor() instanceof FakePlayer fake_actor) {
            fake_actor.addTaskIfAbsent(EFutureType.PLAYER_AI_REQUEST_RESURRECT_TASK, this::RequestPartyResurrect, 2000, 5000);
            if (fake_actor.isWild() && sLast_attacker instanceof Player player) {
                if (Rnd.chance(fake_actor.getDifficulty().ordinal() * 15)) {
                    World.getInstance().getAroundObjects(fake_actor, FakePlayer.class, false, 500, 50)
                            .stream()
                            .filter(Creature::isAlive)
                            .filter(FakePlayer::isWild)
                            .forEach(fake -> {
                        AddPCHate(player, 10000 * fake_actor.getDifficulty().ordinal(), true, 60);
                        EventNotifier.getInstance().notify(fake, ENASCEvent.RECHECK_TARGET);
                    });
                }
            }
        }
    }

    @Override
    public void DIE_SET(ISharedCreature sDied) {
        if (getNASCActor() instanceof Player player_actor) {
            if (sDied instanceof Creature died && died.isDead()) {
                if (died instanceof Warrior mob) {
                    for (ISharedCreature creature : mob.getMostDamage(5)) {
                        if (creature instanceof Player player && player.isReal() && player_actor.distance3D(player) <= 2000) {
                            AddPCHate(player, 50000, true, 60);
                        }
                    }
                }
                if (player_actor.getHateList().getHate(died) > 0) {
                    StopPCHate(died);
                }
                RemovePCAttackDesire(died.getObjectId());
                EventNotifier.getInstance().notify(player_actor, ENASCEvent.RECHECK_TARGET);
            }
        }
    }

    @Override
    public void START_COMBAT() {
        if (getNASCActor() instanceof Player player_actor) {
            if (player_actor.isAlive()) {
                TryToogles(true);
            }
            player_actor.getHateList().clearAllSnapshots();
            player_actor.addTaskIfAbsent(EFutureType.FAKE_PLAYER_HATELIST_SNAPSHOTS_TASK, player_actor.getAI()::doHateSnapshot, 0, 30, TimeUnit.SECONDS);
        }
    }

    @Override
    public void STOP_COMBAT() {
        if (getNASCActor() instanceof Player player_actor) {
            if (player_actor.isAlive()) {
                TryToogles(false);
            }
            player_actor.stopTask(EFutureType.FAKE_PLAYER_HATELIST_SNAPSHOTS_TASK);
        }
    }
}
Сообщение объединено:
Эта ошибка означает, что вы в рекасте задали нестандартное число вершин для полигонов. Если Watershed используете, то там от 3 до 6 вершин на полигон. Детур соответственно тоже должен ожидать нужное количество вершин. Это в спецификации бинарных файлов навмеша прописано.

Рекаст не работает с форматом L2j. Он работает с треугольниками. Чтобы корректно сбилдить навмеши для карты L2, вам нужно спарсить коллизионную модель из клиента, а не просто загрузить туда суп из триангулированных ячеек геодаты) Но это не покроет BSP геометрию, которую невозможно корректно выразить через треугольники. Поэтому вам придется искать способ, как корректно подгружать BSP структуру в рекаст ДО вокселизации.
Чисто из интереса: а что это за код-стайл такой?
 
Чисто из интереса: а что это за код-стайл такой?
Когда оч долго реверсишь ПТСку, на автомате начинают проскакивать snake_case, там, где этого не ожидаешь) А по именам методов если, то это сохранение семантики NASC.
 
Вот именно ))) я из за этого тут ищу опытных людей которые подскажу как это старое Г...но ) исправить чтобы вообще нога человека не ступала туда , боты сами себе хозяева ) я выше описывал идею ) я её придумал давно , есть один тип AlexBayev он сначала сделал с3 с ботами как я мечтал до выхода в мир ИИ, сейчас уже полноценный доделывает с4 там что то до интерлюда, но ядро с 25 года не выкладывает всё ищит с кого бабки срубить продать, а с ц3 выложил но там боты дрявые ещё не доконца настроены не та логика что нужно, ХФ и Годес оф дестракшен это болееее новые хроники не на столько деревянные чем до)
чел, ты там если качал чет от этого мудня - загляни в тему, ознакомься https://mmo-dev.info/threads/Осторо...евре»-l2jbayev-c3-c4.38422/page-2#post-301024
 
это работа на уровне C++/native
Ах Aristo подлец! Пади на блейзинг фаст расте написал все! Думаю вас спасет только больше оперативки и ядер, чтобы "каждому боту по ядру". Обязательно проигнорируйте подсказки к производительности со сниппетов, они для лохов, нормальные пацаны дрочат мобиус не меняя его, а надстройками
 
Назад
Сверху Снизу