Addition of AutoPlay and AutoUse task pools.
This commit is contained in:
parent
800c42fd4e
commit
99c556eade
@ -38,212 +38,244 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static boolean _working = false;
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return false;
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,212 +38,244 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static boolean _working = false;
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return false;
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,212 +38,244 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static boolean _working = false;
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return false;
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,212 +38,244 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static boolean _working = false;
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return false;
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,212 +38,244 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static boolean _working = false;
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// Iss classes considered fighters.
|
||||
final int classId = player.getActiveClass();
|
||||
if ((classId > 170) && (classId < 176))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return false;
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,205 +38,237 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static boolean _working = false;
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,205 +38,237 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static boolean _working = false;
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,213 +38,245 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final Integer AUTO_ATTACK_ACTION = 2;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,213 +38,245 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final Integer AUTO_ATTACK_ACTION = 2;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,213 +38,245 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final Integer AUTO_ATTACK_ACTION = 2;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,213 +38,245 @@ import org.l2jmobius.gameserver.util.Util;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoPlayTaskManager implements Runnable
|
||||
public class AutoPlayTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final Integer AUTO_ATTACK_ACTION = 2;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoPlayTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoPlay implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
PLAY: for (Player player : PLAYERS)
|
||||
public AutoPlay(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
PLAY: for (Player player : _players)
|
||||
{
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||
{
|
||||
player.setTarget(null);
|
||||
stopAutoPlay(player);
|
||||
continue PLAY;
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
|
||||
if (player.isCastingNow() || (player.getQueuedSkill() != null))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Skip thinking.
|
||||
final WorldObject target = player.getTarget();
|
||||
if ((target != null) && target.isMonster())
|
||||
{
|
||||
final Monster monster = (Monster) target;
|
||||
if (monster.isAlikeDead())
|
||||
{
|
||||
player.setTarget(null);
|
||||
}
|
||||
else if ((monster.getTarget() == player) || (monster.getTarget() == null))
|
||||
{
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Check if actually attacking.
|
||||
if (player.hasAI() && !player.isAttackingNow() && !player.isCastingNow() && !player.isMoving() && !player.isDisabled())
|
||||
{
|
||||
if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, monster);
|
||||
}
|
||||
else if (monster.hasAI() && !monster.getAI().isAutoAttacking())
|
||||
{
|
||||
final Weapon weapon = player.getActiveWeaponItem();
|
||||
if (weapon != null)
|
||||
{
|
||||
final boolean ranged = weapon.getItemType().isRanged();
|
||||
final double angle = Util.calculateHeadingFrom(player, monster);
|
||||
final double radian = Math.toRadians(angle);
|
||||
final double course = Math.toRadians(180);
|
||||
final double distance = (ranged ? player.getCollisionRadius() : player.getCollisionRadius() + monster.getCollisionRadius()) * 2;
|
||||
final int x1 = (int) (Math.cos(Math.PI + radian + course) * distance);
|
||||
final int y1 = (int) (Math.sin(Math.PI + radian + course) * distance);
|
||||
final Location location;
|
||||
if (ranged)
|
||||
{
|
||||
location = new Location(player.getX() + x1, player.getY() + y1, player.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
location = new Location(monster.getX() + x1, monster.getY() + y1, player.getZ());
|
||||
}
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, location);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue PLAY;
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
// Pickup.
|
||||
if (player.getAutoPlaySettings().doPickup())
|
||||
{
|
||||
PICKUP: for (Item droppedItem : World.getInstance().getVisibleObjectsInRange(player, Item.class, 200))
|
||||
{
|
||||
// Check if item is reachable.
|
||||
if ((droppedItem == null) //
|
||||
|| (!droppedItem.isSpawned()) //
|
||||
|| !GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), droppedItem.getX(), droppedItem.getY(), droppedItem.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
continue PICKUP;
|
||||
}
|
||||
|
||||
// Move to item.
|
||||
if (player.calculateDistance2D(droppedItem) > 70)
|
||||
{
|
||||
if (!player.isMoving())
|
||||
{
|
||||
player.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, droppedItem);
|
||||
}
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
// Try to pick it up.
|
||||
if (!droppedItem.isProtected() || (droppedItem.getOwnerId() == player.getObjectId()))
|
||||
{
|
||||
player.doPickupItem(droppedItem);
|
||||
continue PLAY; // Avoid pickup being skipped.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find target.
|
||||
Monster monster = null;
|
||||
double closestDistance = Double.MAX_VALUE;
|
||||
TARGET: for (Monster nearby : World.getInstance().getVisibleObjectsInRange(player, Monster.class, player.getAutoPlaySettings().isShortRange() ? 600 : 1400))
|
||||
{
|
||||
// Skip unavailable monsters.
|
||||
if ((nearby == null) || nearby.isAlikeDead())
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check monster target.
|
||||
if (player.getAutoPlaySettings().isRespectfulHunting() && (nearby.getTarget() != null) && (nearby.getTarget() != player) && !player.getServitors().containsKey(nearby.getTarget().getObjectId()))
|
||||
{
|
||||
continue TARGET;
|
||||
}
|
||||
// Check if monster is reachable.
|
||||
if (nearby.isAutoAttackable(player) //
|
||||
&& GeoEngine.getInstance().canSeeTarget(player, nearby)//
|
||||
&& GeoEngine.getInstance().canMoveToTarget(player.getX(), player.getY(), player.getZ(), nearby.getX(), nearby.getY(), nearby.getZ(), player.getInstanceWorld()))
|
||||
{
|
||||
final double monsterDistance = player.calculateDistance2D(nearby);
|
||||
if (monsterDistance < closestDistance)
|
||||
{
|
||||
monster = nearby;
|
||||
closestDistance = monsterDistance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// New target was assigned.
|
||||
if (monster != null)
|
||||
{
|
||||
player.setTarget(monster);
|
||||
|
||||
// We take granted that mage classes do not auto hit.
|
||||
if (isMageCaster(player))
|
||||
{
|
||||
continue PLAY;
|
||||
}
|
||||
|
||||
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
}
|
||||
}
|
||||
|
||||
public void doAutoPlay(Player player)
|
||||
public synchronized void doAutoPlay(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
player.onActionRequest();
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
player.onActionRequest();
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoPlay(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoPlay(Player player)
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
if (pool.remove(player))
|
||||
{
|
||||
summon.followOwner();
|
||||
// Pets must follow their owner.
|
||||
if (player.hasServitors())
|
||||
{
|
||||
for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
summon.followOwner();
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (player.hasPet())
|
||||
{
|
||||
player.getPet().followOwner();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAutoPlay(Player player)
|
||||
{
|
||||
return PLAYERS.contains(player);
|
||||
}
|
||||
|
||||
private boolean isMageCaster(Player player)
|
||||
{
|
||||
// On Essence auto attack is enabled via the Auto Attack action.
|
||||
if (Config.AUTO_PLAY_ATTACK_ACTION)
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
return !player.getAutoUseSettings().getAutoActions().contains(AUTO_ATTACK_ACTION);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Non Essence like.
|
||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
||||
return false;
|
||||
}
|
||||
|
||||
public static AutoPlayTaskManager getInstance()
|
||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
||||
/**
|
||||
* @author Mobius
|
||||
*/
|
||||
public class AutoUseTaskManager implements Runnable
|
||||
public class AutoUseTaskManager
|
||||
{
|
||||
private static final Set<Player> PLAYERS = ConcurrentHashMap.newKeySet();
|
||||
private static final Set<Set<Player>> POOLS = ConcurrentHashMap.newKeySet();
|
||||
private static final int POOL_SIZE = 300;
|
||||
private static final int TASK_DELAY = 300;
|
||||
private static final int REUSE_MARGIN_TIME = 3;
|
||||
private static boolean _working = false;
|
||||
|
||||
protected AutoUseTaskManager()
|
||||
{
|
||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
private class AutoUse implements Runnable
|
||||
{
|
||||
if (_working)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_working = true;
|
||||
private final Set<Player> _players;
|
||||
|
||||
for (Player player : PLAYERS)
|
||||
public AutoUse(Set<Player> players)
|
||||
{
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
_players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
for (Player player : _players)
|
||||
{
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
if (!player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
stopAutoUseTask(player);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||
|
||||
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
break ITEMS;
|
||||
}
|
||||
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSupplyItems().remove(itemId);
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final ItemTemplate it = item.getTemplate();
|
||||
if (it != null)
|
||||
{
|
||||
if (!it.checkCondition(player, player, false))
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
continue ITEMS;
|
||||
}
|
||||
|
||||
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||
if (skills != null)
|
||||
{
|
||||
for (ItemSkillHolder itemSkillHolder : skills)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
final Skill skill = itemSkillHolder.getSkill();
|
||||
if (player.isAffectedBySkill(skill.getId()) || player.hasSkillReuse(skill.getReuseHashCode()) || !skill.checkCondition(player, player, false))
|
||||
{
|
||||
continue ITEMS;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
|
||||
final int reuseDelay = item.getReuseDelay();
|
||||
if ((reuseDelay <= 0) || (player.getItemRemainingReuseTime(item.getObjectId()) <= 0))
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
final EtcItem etcItem = item.getEtcItem();
|
||||
final IItemHandler handler = ItemHandler.getInstance().getHandler(etcItem);
|
||||
if ((handler != null) && handler.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
if (Config.ENABLE_AUTO_POTION && !isInPeaceZone && (player.getCurrentHpPercent() < player.getAutoPlaySettings().getAutoPotionPercent()))
|
||||
{
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||
{
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
player.getAutoUseSettings().getAutoPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_PET_POTION && !isInPeaceZone)
|
||||
{
|
||||
final Pet pet = player.getPet();
|
||||
if ((pet != null) && !pet.isDead())
|
||||
{
|
||||
final int percent = pet.getCurrentHpPercent();
|
||||
if ((percent < 100) && (percent <= player.getAutoPlaySettings().getAutoPetPotionPercent()))
|
||||
{
|
||||
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPetPotionItems())
|
||||
{
|
||||
final Item item = player.getInventory().getItemByItemId(itemId.intValue());
|
||||
if (item == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoPetPotionItems().remove(itemId);
|
||||
continue POTIONS;
|
||||
}
|
||||
|
||||
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.useItem(player, item, false) && (reuseDelay > 0))
|
||||
{
|
||||
player.addTimeStampItem(item, reuseDelay);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Config.ENABLE_AUTO_SKILL)
|
||||
{
|
||||
BUFFS: for (Integer skillId : player.getAutoUseSettings().getAutoBuffs())
|
||||
{
|
||||
// Fixes start area issue.
|
||||
if (isInPeaceZone)
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break BUFFS;
|
||||
}
|
||||
|
||||
Playable pet = null;
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoBuffs().remove(skillId);
|
||||
continue BUFFS;
|
||||
}
|
||||
}
|
||||
|
||||
final WorldObject target = player.getTarget();
|
||||
if (canCastBuff(player, target, skill))
|
||||
{
|
||||
ATTACH_SEARCH: for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()))
|
||||
{
|
||||
skill = holder.getSkill();
|
||||
break ATTACH_SEARCH;
|
||||
}
|
||||
}
|
||||
|
||||
// Playable target cast.
|
||||
final Playable caster = pet != null ? pet : player;
|
||||
if ((target != null) && target.isPlayable() && (target.getActingPlayer().getPvpFlag() == 0) && (target.getActingPlayer().getReputation() >= 0))
|
||||
{
|
||||
caster.doCast(skill);
|
||||
}
|
||||
else // Target self, cast and re-target.
|
||||
{
|
||||
final WorldObject savedTarget = target;
|
||||
caster.setTarget(caster);
|
||||
caster.doCast(skill);
|
||||
caster.setTarget(savedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
// Continue when auto play is not enabled.
|
||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
continue;
|
||||
}
|
||||
|
||||
SKILLS:
|
||||
{
|
||||
// Already casting.
|
||||
if (player.isCastingNow())
|
||||
{
|
||||
continue ACTIONS;
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Player is teleporting.
|
||||
if (player.isTeleporting())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Acquire next skill.
|
||||
Playable pet = null;
|
||||
final Integer skillId = player.getAutoUseSettings().getNextSkillId();
|
||||
Skill skill = player.getKnownSkill(skillId.intValue());
|
||||
if (skill == null)
|
||||
{
|
||||
if (player.hasServitors())
|
||||
{
|
||||
SUMMON_SEARCH: for (Summon summon : player.getServitors().values())
|
||||
{
|
||||
skill = summon.getKnownSkill(skillId.intValue());
|
||||
if (skill != null)
|
||||
{
|
||||
pet = summon;
|
||||
break SUMMON_SEARCH;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((skill == null) && player.hasPet())
|
||||
{
|
||||
pet = player.getPet();
|
||||
skill = pet.getKnownSkill(skillId.intValue());
|
||||
}
|
||||
if (skill == null)
|
||||
{
|
||||
player.getAutoUseSettings().getAutoSkills().remove(skillId);
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
break SKILLS;
|
||||
}
|
||||
}
|
||||
|
||||
// Casting on self stops movement.
|
||||
final WorldObject target = player.getTarget();
|
||||
if (target == player)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Check bad skill target.
|
||||
if ((target == null) || !target.isAttackable() || ((Creature) target).isDead())
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
// Do not attack guards.
|
||||
if (target instanceof Guard)
|
||||
{
|
||||
break SKILLS;
|
||||
}
|
||||
|
||||
if (!canUseMagic(player, target, skill) || (pet != null ? pet : player).useMagic(skill, null, true, false))
|
||||
{
|
||||
player.getAutoUseSettings().incrementSkillOrder();
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||
if (info != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
for (AbstractEffect effect : info.getEffects())
|
||||
{
|
||||
if (!effect.checkCondition(actionId))
|
||||
{
|
||||
player.sendPacket(SystemMessageId.YOU_HAVE_BEEN_REPORTED_AS_AN_ILLEGAL_PROGRAM_USER_SO_YOUR_ACTIONS_HAVE_BEEN_RESTRICTED);
|
||||
break ACTIONS;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do not allow to do some action if player is transformed.
|
||||
if (player.isTransformed())
|
||||
{
|
||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
||||
{
|
||||
continue ACTIONS;
|
||||
}
|
||||
}
|
||||
|
||||
final ActionDataHolder actionHolder = ActionData.getInstance().getActionData(actionId);
|
||||
if (actionHolder != null)
|
||||
{
|
||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
||||
if (actionHandler != null)
|
||||
{
|
||||
actionHandler.useAction(player, actionHolder, false, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_working = false;
|
||||
}
|
||||
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if (!player.hasServitors())
|
||||
// Summon check.
|
||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
if (!player.hasServitors())
|
||||
{
|
||||
occurrences++;
|
||||
return false;
|
||||
}
|
||||
int occurrences = 0;
|
||||
for (Summon servitor : player.getServitors().values())
|
||||
{
|
||||
if (servitor.isAffectedBySkill(skill.getId()))
|
||||
{
|
||||
occurrences++;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (occurrences == player.getServitors().size())
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
|
||||
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||
if (!canUseMagic(player, playableTarget, skill))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final BuffInfo buffInfo = playableTarget.getEffectList().getBuffInfoBySkillId(skill.getId());
|
||||
final BuffInfo abnormalBuffInfo = playableTarget.getEffectList().getFirstBuffInfoByAbnormalType(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;
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
private boolean canUseMagic(Player player, WorldObject target, Skill skill)
|
||||
{
|
||||
if ((skill.getItemConsumeCount() > 0) && (player.getInventory().getInventoryItemCount(skill.getItemConsumeId(), -1) < skill.getItemConsumeCount()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (AttachSkillHolder holder : skill.getAttachSkills())
|
||||
{
|
||||
if (player.isAffectedBySkill(holder.getRequiredSkillId()) //
|
||||
&& (player.hasSkillReuse(holder.getSkill().getReuseHashCode()) || player.isAffectedBySkill(holder)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return !player.isSkillDisabled(skill) && skill.checkCondition(player, target, false);
|
||||
}
|
||||
}
|
||||
|
||||
public void startAutoUseTask(Player player)
|
||||
public synchronized void startAutoUseTask(Player player)
|
||||
{
|
||||
if (!PLAYERS.contains(player))
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
PLAYERS.add(player);
|
||||
if (pool.contains(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.size() < POOL_SIZE)
|
||||
{
|
||||
pool.add(player);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
final Set<Player> pool = ConcurrentHashMap.newKeySet(POOL_SIZE);
|
||||
pool.add(player);
|
||||
ThreadPool.scheduleAtFixedRate(new AutoUse(pool), TASK_DELAY, TASK_DELAY);
|
||||
POOLS.add(pool);
|
||||
}
|
||||
|
||||
public void stopAutoUseTask(Player player)
|
||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
||||
player.getAutoUseSettings().resetSkillOrder();
|
||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
||||
{
|
||||
PLAYERS.remove(player);
|
||||
for (Set<Player> pool : POOLS)
|
||||
{
|
||||
if (pool.remove(player))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user