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
|
* @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 boolean _working = false;
|
private static final int POOL_SIZE = 300;
|
||||||
|
private static final int TASK_DELAY = 300;
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMageCaster(Player player)
|
|
||||||
{
|
|
||||||
// Iss classes considered fighters.
|
|
||||||
final int classId = player.getActiveClass();
|
|
||||||
if ((classId > 170) && (classId < 176))
|
|
||||||
{
|
{
|
||||||
return false;
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 boolean _working = false;
|
private static final int POOL_SIZE = 300;
|
||||||
|
private static final int TASK_DELAY = 300;
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMageCaster(Player player)
|
|
||||||
{
|
|
||||||
// Iss classes considered fighters.
|
|
||||||
final int classId = player.getActiveClass();
|
|
||||||
if ((classId > 170) && (classId < 176))
|
|
||||||
{
|
{
|
||||||
return false;
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 boolean _working = false;
|
private static final int POOL_SIZE = 300;
|
||||||
|
private static final int TASK_DELAY = 300;
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMageCaster(Player player)
|
|
||||||
{
|
|
||||||
// Iss classes considered fighters.
|
|
||||||
final int classId = player.getActiveClass();
|
|
||||||
if ((classId > 170) && (classId < 176))
|
|
||||||
{
|
{
|
||||||
return false;
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 boolean _working = false;
|
private static final int POOL_SIZE = 300;
|
||||||
|
private static final int TASK_DELAY = 300;
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMageCaster(Player player)
|
|
||||||
{
|
|
||||||
// Iss classes considered fighters.
|
|
||||||
final int classId = player.getActiveClass();
|
|
||||||
if ((classId > 170) && (classId < 176))
|
|
||||||
{
|
{
|
||||||
return false;
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 boolean _working = false;
|
private static final int POOL_SIZE = 300;
|
||||||
|
private static final int TASK_DELAY = 300;
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isMageCaster(Player player)
|
|
||||||
{
|
|
||||||
// Iss classes considered fighters.
|
|
||||||
final int classId = player.getActiveClass();
|
|
||||||
if ((classId > 170) && (classId < 176))
|
|
||||||
{
|
{
|
||||||
return false;
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 boolean _working = false;
|
private static final int POOL_SIZE = 300;
|
||||||
|
private static final int TASK_DELAY = 300;
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
{
|
||||||
|
if (pool.contains(player))
|
||||||
private boolean isMageCaster(Player player)
|
{
|
||||||
{
|
return true;
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 boolean _working = false;
|
private static final int POOL_SIZE = 300;
|
||||||
|
private static final int TASK_DELAY = 300;
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
{
|
||||||
|
if (pool.contains(player))
|
||||||
private boolean isMageCaster(Player player)
|
{
|
||||||
{
|
return true;
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -53,383 +53,402 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -437,7 +456,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 final Integer AUTO_ATTACK_ACTION = 2;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Non Essence like.
|
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 final Integer AUTO_ATTACK_ACTION = 2;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Non Essence like.
|
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 final Integer AUTO_ATTACK_ACTION = 2;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Non Essence like.
|
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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
|
* @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 final Integer AUTO_ATTACK_ACTION = 2;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoPlayTaskManager()
|
protected AutoPlayTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoPlay implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode() || !Config.ENABLE_AUTO_PLAY)
|
||||||
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())
|
|
||||||
{
|
{
|
||||||
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.
|
// We take granted that mage classes do not auto hit.
|
||||||
if (isMageCaster(player))
|
if (isMageCaster(player))
|
||||||
{
|
{
|
||||||
continue PLAY;
|
continue PLAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if actually attacking.
|
player.sendPacket(ExAutoPlayDoMacro.STATIC_PACKET);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
if (pool.contains(player))
|
||||||
PLAYERS.add(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)
|
public void stopAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
PLAYERS.remove(player);
|
for (Set<Player> pool : POOLS)
|
||||||
|
|
||||||
// Pets must follow their owner.
|
|
||||||
if (player.hasServitors())
|
|
||||||
{
|
{
|
||||||
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)
|
public boolean isAutoPlay(Player player)
|
||||||
{
|
{
|
||||||
return PLAYERS.contains(player);
|
for (Set<Player> pool : POOLS)
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
if (pool.contains(player))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
// Non Essence like.
|
|
||||||
return player.isMageClass() && (player.getRace() != Race.ORC);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static AutoPlayTaskManager getInstance()
|
public static AutoPlayTaskManager getInstance()
|
||||||
|
@ -54,415 +54,434 @@ import org.l2jmobius.gameserver.network.serverpackets.ExBasicActionList;
|
|||||||
/**
|
/**
|
||||||
* @author Mobius
|
* @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 final int REUSE_MARGIN_TIME = 3;
|
||||||
private static boolean _working = false;
|
|
||||||
|
|
||||||
protected AutoUseTaskManager()
|
protected AutoUseTaskManager()
|
||||||
{
|
{
|
||||||
ThreadPool.scheduleAtFixedRate(this, 500, 500);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private class AutoUse implements Runnable
|
||||||
public void run()
|
|
||||||
{
|
{
|
||||||
if (_working)
|
private final Set<Player> _players;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_working = true;
|
|
||||||
|
|
||||||
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);
|
if (!player.isOnline() || player.isInOfflineMode())
|
||||||
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.isTeleporting())
|
stopAutoUseTask(player);
|
||||||
{
|
continue;
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue when auto play is not enabled.
|
if (player.hasBlockActions() || player.isControlBlocked() || player.isAlikeDead() || player.isMounted() || (player.isTransformed() && player.getTransformation().get().isRiding()))
|
||||||
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
SKILLS:
|
final boolean isInPeaceZone = player.isInsideZone(ZoneId.PEACE) || player.isInsideZone(ZoneId.SAYUNE);
|
||||||
|
|
||||||
|
if (Config.ENABLE_AUTO_ITEM && !isInPeaceZone)
|
||||||
{
|
{
|
||||||
// Already casting.
|
ITEMS: for (Integer itemId : player.getAutoUseSettings().getAutoSupplyItems())
|
||||||
if (player.isCastingNow())
|
|
||||||
{
|
{
|
||||||
break SKILLS;
|
if (player.isTeleporting())
|
||||||
}
|
|
||||||
|
|
||||||
// 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())
|
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());
|
continue ITEMS;
|
||||||
if (skill != null)
|
}
|
||||||
|
|
||||||
|
final List<ItemSkillHolder> skills = it.getAllSkills();
|
||||||
|
if (skills != null)
|
||||||
|
{
|
||||||
|
for (ItemSkillHolder itemSkillHolder : skills)
|
||||||
{
|
{
|
||||||
pet = summon;
|
final Skill skill = itemSkillHolder.getSkill();
|
||||||
break SUMMON_SEARCH;
|
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();
|
final EtcItem etcItem = item.getEtcItem();
|
||||||
skill = pet.getKnownSkill(skillId.intValue());
|
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);
|
POTIONS: for (Integer itemId : player.getAutoUseSettings().getAutoPotionItems())
|
||||||
if (info != null)
|
|
||||||
{
|
{
|
||||||
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);
|
player.addTimeStampItem(item, reuseDelay);
|
||||||
break ACTIONS;
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
// Continue when auto play is not enabled.
|
||||||
if (player.isTransformed())
|
if (!AutoPlayTaskManager.getInstance().isAutoPlay(player))
|
||||||
{
|
{
|
||||||
final int[] allowedActions = player.isTransformed() ? ExBasicActionList.ACTIONS_ON_TRANSFORM : ExBasicActionList.DEFAULT_ACTION_LIST;
|
continue;
|
||||||
if (Arrays.binarySearch(allowedActions, actionId) < 0)
|
}
|
||||||
|
|
||||||
|
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);
|
ACTIONS: for (Integer actionId : player.getAutoUseSettings().getAutoActions())
|
||||||
if (actionHolder != null)
|
|
||||||
{
|
{
|
||||||
final IPlayerActionHandler actionHandler = PlayerActionHandler.getInstance().getHandler(actionHolder.getHandler());
|
final BuffInfo info = player.getEffectList().getFirstBuffInfoByAbnormalType(AbnormalType.BOT_PENALTY);
|
||||||
if (actionHandler != null)
|
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)
|
||||||
}
|
|
||||||
|
|
||||||
private boolean canCastBuff(Player player, WorldObject target, Skill skill)
|
|
||||||
{
|
|
||||||
// Summon check.
|
|
||||||
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
|
||||||
{
|
{
|
||||||
if (!player.hasServitors())
|
// Summon check.
|
||||||
|
if ((skill.getAffectScope() == AffectScope.SUMMON_EXCEPT_MASTER) || (skill.getTargetType() == TargetType.SUMMON))
|
||||||
{
|
{
|
||||||
return false;
|
if (!player.hasServitors())
|
||||||
}
|
|
||||||
int occurrences = 0;
|
|
||||||
for (Summon servitor : player.getServitors().values())
|
|
||||||
{
|
|
||||||
if (servitor.isAffectedBySkill(skill.getId()))
|
|
||||||
{
|
{
|
||||||
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
final Playable playableTarget = (target == null) || !target.isPlayable() || (skill.getTargetType() == TargetType.SELF) ? player : (Playable) target;
|
||||||
if ((target != null) && target.isCreature() && ((Creature) target).isAlikeDead() && (skill.getTargetType() != TargetType.SELF) && (skill.getTargetType() != TargetType.NPC_BODY) && (skill.getTargetType() != TargetType.PC_BODY))
|
if (!canUseMagic(player, playableTarget, skill))
|
||||||
{
|
|
||||||
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)))
|
|
||||||
{
|
{
|
||||||
return false;
|
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)
|
public void stopAutoUseTask(Player player)
|
||||||
@ -470,7 +489,13 @@ public class AutoUseTaskManager implements Runnable
|
|||||||
player.getAutoUseSettings().resetSkillOrder();
|
player.getAutoUseSettings().resetSkillOrder();
|
||||||
if (player.getAutoUseSettings().isEmpty() || !player.isOnline() || player.isInOfflineMode())
|
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