/*
* 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();
}
}