Jump to content

Recommended Posts

  • 2 weeks later...
Posted

So how to build this.

As a concept.

Farming actions should be done by coding.

Target choose AI. Avoid getting stuck.

 

What is AI actually needed for inside a game?.

 

Chatting moving and choosing

Example call for custom town event.

Second example come for pvp at that area.

It Is amazing to have players respond.

 

Is there a way to run something on each players PC? Like what boting programs do? Do it.

To avoid server lag.

 

Second. AI for monster's.

To be able to feel each monster like a pvp. is what makes a game a masterpiece.

Plus insulting. In real Time.

 

AI for npcs. It can help but is not Worth if it causes even a fragment of lag.

 

AI and party. Clan.

Can it be smart enough to have any meaning to use it as a real player? 

If yes it should be added.

AI should also be used on pets.

 

Chatting  with allied AI.

If you can explain to it to focus the healer and it does. it's becoming perfect.

 

It should be like a friend and an assistant. 

 

How this should be done.

Different AI for fake players

Different for monsters.

Why?

Monsters are easy to manage.

Players are not.

You must add also the events rules.

Example.

CTF go and try to score.

 

How to make AI for pvp.

Set rules to avoid feeding.

How? Smart call for friends.

Events go all in unless something is wrong. Overseer needed. 

Should npc try to be a real player?

No. Unless it's a custom server.

Npc must try to buy or auto earn only with fixed prices.

To avoid getting scammed exploited.

Should they farm forever? Yes.

Should they progress? Yes.

Having people on farm zones makes the game alive.

 

Now. 

What is needed.

 

An AI as an overseer.

Check and correct the rest.

An AI for the players.

An AI for the monsters.

Npc really optional not worth.

How it should be done.

 

Make the training data you want.

Via interface assist.

And copy paste them inside the main mother model.

So people can actually use and train the AI for their own unique server.

 

If it is possible to explain as GM char 

And the AI can be asked to save it.

It would be the perfect scenario.

Also option to showcase to you.

 

So one program for the AI.

And another for training.

Can AI be used for low rate servers?

No it will never work.

Can it be used for custom server's?

Yes it will be able to thrive.

 

If there is anything you can't understand in general.

 

Or if you don't understand why everything should be done exactly as explained above.

I can help you with it  in absolute detail.

  • 3 weeks later...
Posted
On 9/21/2025 at 3:58 AM, Elfo said:

I don't really work on L2j anymore, but from time to time, I check up the forums just in case someone built something cool, and I stumbled upon this:

It's actually really easy to implement LLMs (both local, like gpt-oss, and online, like ChatGPT or Gemini) for any sort of action in L2.

Even without training a model explicitly, a good GPU and some basic LLM knowledge can take you very far. 

Here is an example of an auto play bot I made in 10 minutes that uses gpt-oss (which is mega overkill) locally with a 5090 to do some basic farming.
As you can see from the LM Studio responses, it is fairly fast (and uses reasoning too) to think about what's the best course of action for a given situation.
 

 

Basically just a quick proof of concept. If I had time, I would make something like this for auto-play/farm or bots, since LLMs would play really nicely with fake players that can actually think and are not completely pre-programmed. It's quite cool.

 

spacer.png

 

L2jBayev Chronicle 3: Rise of Darkness – AiEngine Edition

In short: this is a C3 build with a full-fledged AI engine, live mercenaries, a built-in quiz, a “personal account” in the Community Board, and server logic neatly distributed across thread pools. The project is about a living world without lags : bots farm, communicate, gather parties, teleport along routes, and the server remains cold and stable.

 

What's inside (the most delicious)

1) Full-fledged AI engine for characters

  • Behavior types: farming ( FarmAI ), combat ( CombatAI ), party logic ( PartyAI ), trading/walking ( TraderAI / WalkerAI ), support roles (healer, etc.).
  • Class profiles: for mages/archers/daggers, etc., “smart” skill rotations, distance control, sleep/save skills, healing, loot pickup, etc. are implemented (see examples of classes like SpellSingerAI , NecromancerAI , etc.).
  • Self-healing and teleports: when dying, the bot goes through a sequence of steps without sleep()- via AITaskSequence + AITeleportToLocTask , searches for the nearest gatekeeper and teleports via TeleportationManager with routes depending on the level.
  • Auto-support: auto-nipples, arrows/bones, smart auto-proceduring of buffs and auto-banks CP/HP/MP with thresholds - all sewn into the auxiliary EtcPlayersAi .
  • Chat context: ChatManagerAi processes mentions, makes responses with delays (anti-flood), supports party chat and “human” reaction.
  • Understanding: ChatManagerAi system  processes the dialogue, bots remember your aggression and insults, they start to respond less often to modern users, stop accepting or
    inviting to a group (party) and when it goes beyond the peak they will simply merge you, and every time they see you on the PC, there is an opportunity to measure more often, communicate
    respectfully and beautifully, in general, a “human” reaction.

Why a player/admin needs this: bots actually “live”, farm and interact, and don’t just stand on macros. This is a great background for online and PvE action.

 

2) Mercenaries (Mercenary system)

  • Full-fledged companion character : L2MercenaryInstance with its own MercenaryAI (movement, attack, support, consumables, shots).
  • Behavior modes: DEFENDER / SUPPORT / PASSIVE - switchable to suit your playing style.
  • Progress and trust: the mercenary's trust/exp/level grows , skills are learned according to the MercenarySkillTree (conditions are based on the trust or level of the owner).
  • Templates and equipment: via MercenaryTemplateTable and spawner - model/weapon/type are selected.
  • Social: MercenarySpeechManager - a set of speeches; the mercenary "comes to life" in the chat.
  • Premium Link: Premium account owners give the mercenary additional trust (faster progress).

Why: This is not a dummy pet, but a playful companion with modes, training and “character”.

 

3) Quiz (event viktorina )

  • Rounds according to schedule: pre-launch with announcements (minutes/seconds before start), registration .reg, auto-opening of the window.
  • Multiple choice questions: question + set of answer buttons; fair processing, timings, question change.
  • Tops and history: results table, statistics, neat UI via HTML assembly.
  • Flexible control: you can start immediately or set a delayed start (notification package 5/2/1 min, etc.).

Why: regular activity for players, “social entertainment” module right in the build.

 

4) Personal account in Community Board

  • KB managers: buff cabinet, teleports, clans/forums/mail/friends, tops (PK/PvP/wealth/players), character repair, viewing skill trees , etc.
  • Premium logic: some services/mail are limited by premium; premium also affects the visual (nickname color) and bonuses (see effect on mercenary).
  • Single sign-on: all in one place, no team chaos.

Why: conveniently manage your character and services without going into the console or installing third-party mods.

 

Why is the system technically valuable?

Minimum load and stability

  • Separated thread pools:
    AI logic, hunting, teleports, chat - on separate onesScheduledExecutorService ( AI_THREAD_POOL , MONSTER_HUNT_POOL , TELEPORT_POOL , CHAT_POOL ).
  • No "freezing": task sequencers (teleport/recovery) work through the scheduler, not Thread.sleep().
  • Bot limitation: protection against overload via thresholds/counters - “extra” bots do not start.
  • One bot - one sequence: AITaskManager ensures that the character does not have parallel conflicting tasks.
  • Smoothing out peaks: starting tasks with offsets so that there are no simultaneous “ticks” of hundreds of bots.
  • Monitoring/logs: own loggers (separate files for info/errors/processes/chats), CPU load monitoring.

Bottom line: the build is designed for “thick online” and mass activities without TPS failures .

 

Additional Features

  • Auto-alliances for farming: party logic invites suitable players (checking level/equipment/clan flags), there are “human” responses to requests.
  • Sub/class management: out of the box helpers for changing class/subclass, auto-learning of necessary skills and selection of equipment by level.
  • Security/protection: secondary PIN/picture password support (used in KB/voiced commands; optional).
  • Premium accounts: privileges in KB/mail/visual and synergy with mercenary progress.
  • Ready-made services: tops, auctions/mail, teleports from KB, buff rooms, repairs, viewing skill trees, etc.

 

Who is this build for?

  • Freeshare/project admins who want a living world “from the pack”: bots and mercenaries provide a constant background of activity.
  • Players who value convenience: personal account, premium services, events and a mercenary companion.
  • Developers who want a clean, predictable backend with thread pools and a neat task model without “magic”.

 

How it differs from standard assemblies

  • Not macros - AI profiles with “brains”: rotations, positioning, healing, decision making.
  • Not a decoration pet - a mercenary with his own modes, progress, skill tree and lines.
  • Not a faceless gamemod - an event quiz with UI, schedule, tops.
  • No chaos in flows - strict pools, planning and task managers designed for online and growth.
  • No separate scripts - a single personal account in KB for most activities.

 

TL;DR (one paragraph for the project card)

AiEngine C3 is a build with live AI, smart bots, mercenaries (modes/progress/skills), built-in quiz, premium logic and a convenient personal account in KB. Under the hood are distributed thread pools and task managers without sleep(), so even with a dense online the server remains stable and responsive.

 

Additionally

add - there is still a lot of interesting things command .assassin or shift+target (order murder), shift+target for admins on AI characters for control, admin panel is completely
rewritten, many additional functions, mercenaries change their appearance depending on trust, deepseek and chatGPT system is connected for communication of characters like real players,
GPT - for newer java, there is still a very large list of fixes after the last versions, a lot has been fixed, including height coordinates (Z) geo-Squares, pathfinding,
visibility through obstacles, fix pet summons, trade packages, shop packages, many effects, quests (including the original ones like nipples, etc.), Ai behavior of NPC and RB monsters,
absolutely all epics have been transferred to AiLoader no longer in python scripts.

Attention! The server is suitable for both classic mode and PvP format, as well as with various mods. Absolutely everything is configured in the configurations to suit your taste and purposes of use.

It is recommended to launch the server through L2ServerControl (simplifies management and control of processes).


Download Servers:

Chronicle 3 Server

Chronicle 4 Test Upgraded Server


Full Desc & screens:

Post & Screens c3

Post & Desc c4

 

 

  • 1 month later...
  • 4 months later...
Posted (edited)

I’ve been playing with LLM-driven autofarm bots too, and giving them some visual cues made a big difference. I ended up using art pieces from https://allcorrectgames.com/service/game-art/ as placeholders while training my detection prompts, and it actually helped the models parse scenes more reliably. If you add a bit of lightweight state tracking on top, your fake players start behaving way more naturally.

Edited by Meissgenry
  • 1 month later...
Posted
On 9/22/2025 at 4:12 PM, Mobius said:

别误会,我对AI并没有什么偏见。大语言模型在游戏领域的潜力是巨大的,尤其是在创建动态NPC对话或复杂、自适应的游戏大师方面。我上一篇帖子的重点并不是全盘否定AI,而是质疑它在解决这个特定、已解决的问题上的实际应用。
 

然而,所举的例子(基础农耕)是展示这种潜力最糟糕的用例。这相当于用核聚变反应堆来为台灯供电。与任务本身相比,其开销是天文数字。


我论点的核心在于效率和选择合适的工具。对于自动农耕这种可预测的、基于循环的行为,状态机不仅足够,而且更胜一筹。它轻量、极速、可靠,且消耗的资源微乎其微。
 

为了证明这无关乎代码量,而在于执行效率,
这是我服务器自动播放逻辑的全部内容:
AutoPlayTaskManager400行代码:
AutoUseTaskManager470行代码:


这段代码为所有职业提供了完整的、类似零售版的自动播放支持,包括离线模式。
它可以在任何标准VPS上运行,无需专用GPU,仅占用极少量的CPU资源。


针对同一任务的基于大语言模型的解决方案,即使是“较弱”的模型,也会:
-为每个决策引入显著的延迟(响应时间)。
-需要昂贵的GPU硬件进行本地运行,或为云服务支付API费用。
-为将自然语言回复解析回游戏动作增加了极大的复杂性。
-本质上不如简单的“如果怪物死亡 -> 战利品”检查那样可靠。


因此,虽然我同意这项研究作为概念验证“相当酷”,但将其推崇为自动耕作的实用解决方案,这正是“AI炒作”标签的适用之处。真正的创新应该是将大语言模型的能力应用于状态机难以解决的问题,而不是它已经完美解决的问题。

/*
 * Copyright (c) 2013 L2jMobius
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be
 * included in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
 * IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */
package org.l2jmobius.gameserver.taskmanagers;

import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.l2jmobius.commons.threads.ThreadPool;
import org.l2jmobius.gameserver.config.custom.AutoPlayConfig;
import org.l2jmobius.gameserver.data.xml.PetSkillData;
import org.l2jmobius.gameserver.handler.IItemHandler;
import org.l2jmobius.gameserver.handler.ItemHandler;
import org.l2jmobius.gameserver.model.WorldObject;
import org.l2jmobius.gameserver.model.actor.Playable;
import org.l2jmobius.gameserver.model.actor.Player;
import org.l2jmobius.gameserver.model.actor.Summon;
import org.l2jmobius.gameserver.model.actor.instance.Guard;
import org.l2jmobius.gameserver.model.item.EtcItem;
import org.l2jmobius.gameserver.model.item.ItemTemplate;
import org.l2jmobius.gameserver.model.item.instance.Item;
import org.l2jmobius.gameserver.model.skill.AbnormalType;
import org.l2jmobius.gameserver.model.skill.BuffInfo;
import org.l2jmobius.gameserver.model.skill.Skill;
import org.l2jmobius.gameserver.model.skill.holders.SkillHolder;
import org.l2jmobius.gameserver.model.skill.targets.TargetType;
import org.l2jmobius.gameserver.model.zone.ZoneId;

/**
 * @author Mobius (optimized by AI)
 */
public class AutoUseTaskManager
{
    private static final Logger LOG = Logger.getLogger(AutoUseTaskManager.class.getName());
    
    private static final Set<AutoUse> POOLS = ConcurrentHashMap.newKeySet();
    private static final int POOL_SIZE = 200;
    private static final int TASK_DELAY = 300;
    private static final int REUSE_MARGIN_TIME = 3;
    
    protected AutoUseTaskManager()
    {
    }
    
    /**
     * 内部任务类,每个池绑定一个独立任务
     */
    private class AutoUse implements Runnable
    {
        private final Set<Player> _players;
        private volatile ScheduledFuture<?> _future;
        private volatile boolean _cancelled = false;
        
        public AutoUse(Set<Player> players)
        {
            _players = players;
        }
        
        public void setFuture(ScheduledFuture<?> future)
        {
            _future = future;
        }
        
        public Set<Player> getPlayers()
        {
            return _players;
        }
        
        /**
         * 取消任务并从全局池中移除自身
         */
        public void cancelTask()
        {
            if (_cancelled)
            {
                return;
            }
            _cancelled = true;
            if (_future != null)
            {
                _future.cancel(false);
            }
            POOLS.remove(this);
        }
        
        @Override
        public void run()
        {
            // 防止任务被取消后仍执行
            if (_cancelled)
            {
                return;
            }
            
            try
            {
                if (_players.isEmpty())
                {
                    cancelTask();
                    return;
                }
                
                // 使用迭代器安全遍历
                for (Iterator<Player> it = _players.iterator(); it.hasNext();)
                {
                    Player player = it.next();
                    if (!player.isOnline() || (player.isInOfflineMode() && !player.isOfflinePlay()))
                    {
                        it.remove();
                        continue;
                    }
                    
                    // 跳过不可操作状态
                    if (player.isSitting() || player.isStunned() || player.isSleeping() || player.isParalyzed() ||
                        player.isAfraid() || player.isAlikeDead() || player.isMounted() ||
                        (player.isTransformed() && player.getTransformation().isRiding()))
                    {
                        continue;
                    }
                    
                    final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE);
                    
                    // 自动物品
                    if (AutoPlayConfig.ENABLE_AUTO_ITEM && !isInPeaceZone)
                    {
                        processAutoItems(player);
                    }
                    
                    // 自动药水
                    if (AutoPlayConfig.ENABLE_AUTO_POTION && !isInPeaceZone)
                    {
                        processAutoPotion(player);
                    }
                    
                    // 自动技能(Buff 和攻击)
                    if (AutoPlayConfig.ENABLE_AUTO_SKILL)
                    {
                        processAutoBuffs(player, isInPeaceZone);
                        if (player.isAutoPlaying())
                        {
                            processAutoSkills(player);
                        }
                    }
                }
                
                // 若池已空,则取消任务
                if (_players.isEmpty())
                {
                    cancelTask();
                }
            }
            catch (Throwable t)
            {
                LOG.log(Level.SEVERE, "Error in AutoUse task for pool: " + _players, t);
            }
        }
        
        // ---------- 各处理逻辑拆分 ----------
        
        private void processAutoItems(Player player)
        {
            Set<Integer> supplyItems = player.getAutoUseSettings().getAutoSupplyItems();
            if (supplyItems == null || supplyItems.isEmpty())
            {
                return;
            }
            
            for (Iterator<Integer> it = supplyItems.iterator(); it.hasNext();)
            {
                Integer itemId = it.next();
                if (player.isTeleporting())
                {
                    break; // 传送中停止处理物品
                }
                
                final Item item = player.getInventory().getItemByItemId(itemId.intValue());
                if (item == null)
                {
                    it.remove();
                    continue;
                }
                
                final ItemTemplate template = item.getTemplate();
                if ((template == null) || !template.checkCondition(player, player, false))
                {
                    continue;
                }
                
                final SkillHolder[] skills = template.getSkills();
                if (skills != null)
                {
                    boolean canUse = true;
                    for (SkillHolder holder : skills)
                    {
                        Skill skill = holder.getSkill();
                        if (!skill.isActive())
                        {
                            continue;
                        }
                        if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) ||
                            !skill.checkCondition(player, player, false))
                        {
                            canUse = false;
                            break;
                        }
                    }
                    if (!canUse)
                    {
                        continue;
                    }
                }
                
                final int reuseDelay = item.getReuseDelay();
                if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
                {
                    final IItemHandler handler = ItemHandler.getInstance().getHandler(item.getEtcItem());
                    if ((handler != null) && handler.onItemUse(player, item, false) && (reuseDelay > 0))
                    {
                        player.addTimeStampItem(item, reuseDelay);
                    }
                }
            }
        }
        
        private void processAutoPotion(Player player)
        {
            final int itemId = player.getAutoUseSettings().getAutoPotionItem();
            if (itemId <= 0)
            {
                return;
            }
            
            if (player.getCurrentHpPercent() >= player.getAutoPlaySettings().getAutoPotionPercent())
            {
                return;
            }
            
            final Item item = player.getInventory().getItemByItemId(itemId);
            if (item == null)
            {
                player.getAutoUseSettings().setAutoPotionItem(0);
                return;
            }
            
            final int reuseDelay = item.getReuseDelay();
            if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
            {
                final EtcItem etcItem = item.getEtcItem();
                final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
                if ((handler != null) && handler.onItemUse(player, item, false) && (reuseDelay > 0))
                {
                    player.addTimeStampItem(item, reuseDelay);
                }
            }
        }
        
        private void processAutoBuffs(Player player, boolean isInPeaceZone)
        {
            Set<Integer> autoBuffs = player.getAutoUseSettings().getAutoBuffs();
            if (autoBuffs == null || autoBuffs.isEmpty())
            {
                return;
            }
            
            for (Iterator<Integer> it = autoBuffs.iterator(); it.hasNext();)
            {
                Integer skillId = it.next();
                
                // 处于和平区域则跳过当前 Buff(而不是 break)
                if (isInPeaceZone)
                {
                    continue;
                }
                
                if (player.isCastingNow() || player.isAttackingNow() || player.isTeleporting())
                {
                    continue;
                }
                
                Playable pet = null;
                Skill skill = player.getKnownSkill(skillId);
                if (skill == null)
                {
                    if (player.hasServitor() || player.hasPet())
                    {
                        final Summon summon = player.getSummon();
                        skill = summon.getKnownSkill(skillId);
                        if (skill == null)
                        {
                            skill = PetSkillData.getInstance().getKnownSkill(summon, skillId);
                        }
                        if (skill != null)
                        {
                            pet = summon;
                        }
                    }
                    if (skill == null)
                    {
                        it.remove(); // 技能不存在则移除
                        continue;
                    }
                }
                
                final WorldObject target = player.getTarget();
                if (!canCastBuff(player, target, skill))
                {
                    continue;
                }
                
                final Playable caster = pet != null ? pet : player;
                if ((target != null) && target.isPlayable())
                {
                    final Player targetPlayer = target.asPlayer();
                    if (((targetPlayer.getPvpFlag() == 0) && (targetPlayer.getKarma() <= 0)) || (targetPlayer.getParty() == caster.getParty()))
                    {
                        caster.doCast(skill);
                    }
                    else
                    {
                        if (!caster.getEffectList().isAffectedBySkill(skill.getId()))
                        {
                            final WorldObject savedTarget = target;
                            caster.setTarget(caster);
                            caster.doCast(skill);
                            caster.setTarget(savedTarget);
                        }
                    }
                }
                else
                {
                    final WorldObject savedTarget = target;
                    caster.setTarget(caster);
                    caster.doCast(skill);
                    caster.setTarget(savedTarget);
                }
            }
        }
        
        private void processAutoSkills(Player player)
        {
            final int count = player.getAutoUseSettings().getAutoSkills().size();
            if (count == 0)
            {
                return;
            }
            
            for (int i = 0; i < count; i++)
            {
                if (player.isCastingNow() || player.isTeleporting())
                {
                    return; // 正在施法或传送则停止本轮技能尝试
                }
                
                Playable pet = null;
                final WorldObject target = player.getTarget();
                final Integer skillId = player.getAutoUseSettings().getNextSkillId();
                if (skillId == null)
                {
                    break;
                }
                
                Skill skill = player.getKnownSkill(skillId);
                if (skill == null)
                {
                    if (player.hasServitor() || player.hasPet())
                    {
                        final Summon summon = player.getSummon();
                        skill = summon.getKnownSkill(skillId);
                        if (skill == null)
                        {
                            skill = PetSkillData.getInstance().getKnownSkill(summon, skillId);
                        }
                        if (skill != null)
                        {
                            pet = summon;
                            pet.setTarget(target);
                        }
                    }
                    if (skill == null)
                    {
                        // 移除无效技能
                        player.getAutoUseSettings().getAutoSkills().remove(skillId);
                        player.getAutoUseSettings().resetSkillOrder();
                        break; // 列表变化,重置顺序并退出本轮
                    }
                }
                
                // 如果目标是玩家自身,则跳过该技能(继续下一个)
                if (target == player)
                {
                    player.getAutoUseSettings().incrementSkillOrder();
                    continue;
                }
                
                // 检查目标有效性
                if ((target == null) || target.asCreature().isDead())
                {
                    // 清除队列技能
                    if (player.getQueuedSkill() != null)
                    {
                        player.setQueuedSkill(null, false, false);
                    }
                    player.getAutoUseSettings().incrementSkillOrder();
                    continue; // 跳过当前技能,尝试下一个
                }
                
                if (target.isInsideZone(ZoneId.PEACE) || !target.isAutoAttackable(player))
                {
                    player.getAutoUseSettings().incrementSkillOrder();
                    continue;
                }
                
                if (target instanceof Guard)
                {
                    final int targetMode = player.getAutoPlaySettings().getNextTargetMode();
                    if ((targetMode != 3 /* NPC */) && (targetMode != 0 /* Any Target */))
                    {
                        player.getAutoUseSettings().incrementSkillOrder();
                        continue;
                    }
                }
                
                // 前进到下一个技能(默认+1,若实际施法则在后续可能因冷却等跳过)
                player.getAutoUseSettings().incrementSkillOrder();
                
                final Playable caster = pet != null ? pet : player;
                if (!canUseMagic(caster, target, skill))
                {
                    continue;
                }
                
                caster.useMagic(skill, true, false);
                break; // 成功施法后退出本轮循环
            }
        }
        
        // ---------- 条件判断方法 ----------
        
        private boolean canCastBuff(Player player, WorldObject target, Skill skill)
        {
            if ((target != null) && target.isCreature() && target.asCreature().isAlikeDead() &&
                (skill.getTargetType() != TargetType.SELF) &&
                (skill.getTargetType() != TargetType.CORPSE) &&
                (skill.getTargetType() != TargetType.PC_BODY))
            {
                return false;
            }
            
            final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : target.asPlayable();
            if ((player != playableTarget) && (player.calculateDistance3D(playableTarget) > skill.getCastRange()))
            {
                return false;
            }
            
            if (!canUseMagic(player, playableTarget, skill))
            {
                return false;
            }
            
            final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
            final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getBuffInfoByAbnormalType(skill.getAbnormalType());
            if (abnormalBuffInfo != null)
            {
                if (buffInfo != null)
                {
                    return (abnormalBuffInfo.getSkill().getId() == buffInfo.getSkill().getId()) &&
                        ((buffInfo.getTime() <= REUSE_MARGIN_TIME) || (buffInfo.getSkill().getLevel() < skill.getLevel()));
                }
                return (abnormalBuffInfo.getSkill().getAbnormalLevel() < skill.getAbnormalLevel()) ||
                    abnormalBuffInfo.isAbnormalType(AbnormalType.NONE);
            }
            return buffInfo == null;
        }
        
        private boolean canUseMagic(Playable playable, WorldObject target, Skill skill)
        {
            if ((skill.getItemConsumeCount() > 0) &&
                (playable.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
            {
                return false;
            }
            if (playable.isPlayer() && (playable.asPlayer().getCharges() < skill.getChargeConsumeCount()))
            {
                return false;
            }
            final int mpConsume = skill.getMpInitialConsume() + skill.getMpConsume();
            if ((mpConsume > 0) && (playable.getCurrentMp() < mpConsume))
            {
                return false;
            }
            if ((skill.getId() == 254) && (target != null) && target.isMonster() && target.asMonster().isSpoiled())
            {
                return false;
            }
            return !playable.isSkillDisabled(skill) && skill.checkCondition(playable, target, false);
        }
    }
    
    // ---------- 公开管理方法 ----------
    
    public synchronized void startAutoUseTask(Player player)
    {
        // 检查玩家是否已存在
        for (AutoUse au : POOLS)
        {
            if (au.getPlayers().contains(player))
            {
                return;
            }
        }
        
        // 尝试加入已有池
        for (AutoUse au : POOLS)
        {
            if (au.getPlayers().size() < POOL_SIZE)
            {
                au.getPlayers().add(player);
                return;
            }
        }
        
        // 创建新池
        final Set<Player> newPool = ConcurrentHashMap.newKeySet(POOL_SIZE);
        newPool.add(player);
        final AutoUse task = new AutoUse(newPool);
        final ScheduledFuture<?> future = ThreadPool.schedulePriorityTaskAtFixedRate(task, TASK_DELAY, TASK_DELAY);
        task.setFuture(future);
        POOLS.add(task);
    }
    
    public synchronized void stopAutoUseTask(Player player)
    {
        player.getAutoUseSettings().resetSkillOrder();
        for (Iterator<AutoUse> it = POOLS.iterator(); it.hasNext();)
        {
            AutoUse au = it.next();
            if (au.getPlayers().remove(player))
            {
                if (au.getPlayers().isEmpty())
                {
                    au.cancelTask(); // 内部会从 POOLS 移除自身
                }
                return;
            }
        }
    }
    
    public static AutoUseTaskManager getInstance()
    {
        return SingletonHolder.INSTANCE;
    }
    
    private static class SingletonHolder
    {
        protected static final AutoUseTaskManager INSTANCE = new AutoUseTaskManager();
    }
}

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now


×
×
  • Create New...

Important Information

This community uses essential cookies to function properly. Non-essential cookies and third-party services are used only with your consent. Read our Privacy Policy and We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue..