Replaced attack and teleport locks with synchronized methods.
This commit is contained in:
parent
1dd47518ad
commit
7d2f14116f
@ -34,7 +34,6 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -224,9 +223,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final Object _attackLock = new Object();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -577,26 +573,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -905,273 +890,266 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAutoAttack(L2Character target)
|
public synchronized void doAutoAttack(L2Character target)
|
||||||
{
|
{
|
||||||
synchronized (_attackLock)
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
if (!target.isTargetable())
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkTransformed(transform -> !transform.canAttack()))
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final WeaponType weaponType = getAttackType();
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isInsidePeaceZone(this, target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
stopEffectsOnAction();
|
|
||||||
|
|
||||||
// GeoData Los Check here (or dz > 1000)
|
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
if (target.isDead())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOW and CROSSBOW checks
|
if (checkTransformed(transform -> !transform.canAttack()))
|
||||||
if (weaponItem != null)
|
|
||||||
{
|
{
|
||||||
if (!weaponItem.isAttackWeapon() && !isGM())
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final WeaponType weaponType = getAttackType();
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOW and CROSSBOW checks
|
||||||
|
if (weaponItem != null)
|
||||||
|
{
|
||||||
|
if (!weaponItem.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
||||||
{
|
{
|
||||||
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged weapon checks.
|
||||||
|
if (weaponItem.getItemType().isRanged())
|
||||||
|
{
|
||||||
|
// Check if bow delay is still active.
|
||||||
|
if (_disableRangedAttackEndTime > System.nanoTime())
|
||||||
|
{
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapon checks.
|
// Check for arrows and MP
|
||||||
if (weaponItem.getItemType().isRanged())
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
// Check if bow delay is still active.
|
// Check if there are arrows to use or else cancel the attack.
|
||||||
if (_disableRangedAttackEndTime > System.nanoTime())
|
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
||||||
{
|
{
|
||||||
if (isPlayer())
|
// Cancel the action because the L2PcInstance have no arrow
|
||||||
{
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for arrows and MP
|
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
||||||
if (isPlayer())
|
// Other melee is checked in movement code and for offensive spells a check is done every time
|
||||||
|
if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
// Check if there are arrows to use or else cancel the attack.
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
||||||
{
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
// Cancel the action because the L2PcInstance have no arrow
|
return;
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
}
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
// Check if player has enough MP to shoot.
|
||||||
return;
|
int mpConsume = weaponItem.getMpConsume();
|
||||||
}
|
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
||||||
|
{
|
||||||
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
mpConsume = weaponItem.getReducedMpConsume();
|
||||||
// Other melee is checked in movement code and for offensive spells a check is done every time
|
}
|
||||||
if (target.isInsidePeaceZone(getActingPlayer()))
|
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
||||||
{
|
if (_status.getCurrentMp() < mpConsume)
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
{
|
||||||
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
// If L2PcInstance doesn't have enough MP, stop the attack
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
return;
|
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
||||||
}
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
// Check if player has enough MP to shoot.
|
}
|
||||||
int mpConsume = weaponItem.getMpConsume();
|
|
||||||
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
// If L2PcInstance have enough MP, the bow consumes it
|
||||||
{
|
if (mpConsume > 0)
|
||||||
mpConsume = weaponItem.getReducedMpConsume();
|
{
|
||||||
}
|
_status.reduceMp(mpConsume);
|
||||||
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
|
||||||
if (_status.getCurrentMp() < mpConsume)
|
|
||||||
{
|
|
||||||
// If L2PcInstance doesn't have enough MP, stop the attack
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
||||||
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If L2PcInstance have enough MP, the bow consumes it
|
|
||||||
if (mpConsume > 0)
|
|
||||||
{
|
|
||||||
_status.reduceMp(mpConsume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final WeaponType attackType = getAttackType();
|
|
||||||
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
// Mobius: Do not move when attack is launched.
|
||||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
if (isMoving())
|
||||||
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
{
|
||||||
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
// Make sure that char is facing selected target
|
|
||||||
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
final WeaponType attackType = getAttackType();
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
||||||
|
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
||||||
// Get the Attack Reuse Delay of the L2Weapon
|
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
||||||
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
||||||
boolean crossbow = false;
|
|
||||||
switch (attackType)
|
// Make sure that char is facing selected target
|
||||||
|
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
|
||||||
|
// Get the Attack Reuse Delay of the L2Weapon
|
||||||
|
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
||||||
|
boolean crossbow = false;
|
||||||
|
switch (attackType)
|
||||||
|
{
|
||||||
|
case CROSSBOW:
|
||||||
|
case TWOHANDCROSSBOW:
|
||||||
{
|
{
|
||||||
case CROSSBOW:
|
crossbow = true;
|
||||||
case TWOHANDCROSSBOW:
|
}
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
||||||
|
|
||||||
|
// Consume arrows
|
||||||
|
final Inventory inventory = getInventory();
|
||||||
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
crossbow = true;
|
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
||||||
}
|
}
|
||||||
case BOW:
|
|
||||||
|
// Check if the L2Character is a L2PcInstance
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
if (crossbow)
|
||||||
|
|
||||||
// Consume arrows
|
|
||||||
final Inventory inventory = getInventory();
|
|
||||||
if (inventory != null)
|
|
||||||
{
|
{
|
||||||
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving())
|
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
||||||
{
|
|
||||||
stopMove(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the L2Character is a L2PcInstance
|
|
||||||
if (isPlayer())
|
|
||||||
{
|
|
||||||
if (crossbow)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
|
||||||
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FIST:
|
|
||||||
{
|
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
||||||
if (!isPlayer())
|
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
||||||
{
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case FIST:
|
||||||
}
|
{
|
||||||
case DUAL:
|
if (!isPlayer())
|
||||||
case DUALFIST:
|
|
||||||
case DUALBLUNT:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
case DUALFIST:
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
case DUALBLUNT:
|
||||||
if (attack.hasHits())
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
broadcastPacket(attack);
|
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
||||||
|
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
player.updatePvPStatus(target);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
|
{
|
||||||
|
broadcastPacket(attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
npc.setScriptValue(1); // in combat
|
||||||
if (!npc.isScriptValue(1))
|
broadcastInfo(); // update flag status
|
||||||
{
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -224,9 +223,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final Object _attackLock = new Object();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -577,26 +573,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -905,273 +890,266 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAutoAttack(L2Character target)
|
public synchronized void doAutoAttack(L2Character target)
|
||||||
{
|
{
|
||||||
synchronized (_attackLock)
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
if (!target.isTargetable())
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkTransformed(transform -> !transform.canAttack()))
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final WeaponType weaponType = getAttackType();
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isInsidePeaceZone(this, target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
stopEffectsOnAction();
|
|
||||||
|
|
||||||
// GeoData Los Check here (or dz > 1000)
|
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
if (target.isDead())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOW and CROSSBOW checks
|
if (checkTransformed(transform -> !transform.canAttack()))
|
||||||
if (weaponItem != null)
|
|
||||||
{
|
{
|
||||||
if (!weaponItem.isAttackWeapon() && !isGM())
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final WeaponType weaponType = getAttackType();
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOW and CROSSBOW checks
|
||||||
|
if (weaponItem != null)
|
||||||
|
{
|
||||||
|
if (!weaponItem.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
||||||
{
|
{
|
||||||
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged weapon checks.
|
||||||
|
if (weaponItem.getItemType().isRanged())
|
||||||
|
{
|
||||||
|
// Check if bow delay is still active.
|
||||||
|
if (_disableRangedAttackEndTime > System.nanoTime())
|
||||||
|
{
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapon checks.
|
// Check for arrows and MP
|
||||||
if (weaponItem.getItemType().isRanged())
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
// Check if bow delay is still active.
|
// Check if there are arrows to use or else cancel the attack.
|
||||||
if (_disableRangedAttackEndTime > System.nanoTime())
|
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
||||||
{
|
{
|
||||||
if (isPlayer())
|
// Cancel the action because the L2PcInstance have no arrow
|
||||||
{
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for arrows and MP
|
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
||||||
if (isPlayer())
|
// Other melee is checked in movement code and for offensive spells a check is done every time
|
||||||
|
if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
// Check if there are arrows to use or else cancel the attack.
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
||||||
{
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
// Cancel the action because the L2PcInstance have no arrow
|
return;
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
}
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
// Check if player has enough MP to shoot.
|
||||||
return;
|
int mpConsume = weaponItem.getMpConsume();
|
||||||
}
|
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
||||||
|
{
|
||||||
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
mpConsume = weaponItem.getReducedMpConsume();
|
||||||
// Other melee is checked in movement code and for offensive spells a check is done every time
|
}
|
||||||
if (target.isInsidePeaceZone(getActingPlayer()))
|
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
||||||
{
|
if (_status.getCurrentMp() < mpConsume)
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
{
|
||||||
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
// If L2PcInstance doesn't have enough MP, stop the attack
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
return;
|
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
||||||
}
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
// Check if player has enough MP to shoot.
|
}
|
||||||
int mpConsume = weaponItem.getMpConsume();
|
|
||||||
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
// If L2PcInstance have enough MP, the bow consumes it
|
||||||
{
|
if (mpConsume > 0)
|
||||||
mpConsume = weaponItem.getReducedMpConsume();
|
{
|
||||||
}
|
_status.reduceMp(mpConsume);
|
||||||
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
|
||||||
if (_status.getCurrentMp() < mpConsume)
|
|
||||||
{
|
|
||||||
// If L2PcInstance doesn't have enough MP, stop the attack
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
||||||
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If L2PcInstance have enough MP, the bow consumes it
|
|
||||||
if (mpConsume > 0)
|
|
||||||
{
|
|
||||||
_status.reduceMp(mpConsume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final WeaponType attackType = getAttackType();
|
|
||||||
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
// Mobius: Do not move when attack is launched.
|
||||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
if (isMoving())
|
||||||
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
{
|
||||||
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
// Make sure that char is facing selected target
|
|
||||||
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
final WeaponType attackType = getAttackType();
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
||||||
|
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
||||||
// Get the Attack Reuse Delay of the L2Weapon
|
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
||||||
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
||||||
boolean crossbow = false;
|
|
||||||
switch (attackType)
|
// Make sure that char is facing selected target
|
||||||
|
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
|
||||||
|
// Get the Attack Reuse Delay of the L2Weapon
|
||||||
|
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
||||||
|
boolean crossbow = false;
|
||||||
|
switch (attackType)
|
||||||
|
{
|
||||||
|
case CROSSBOW:
|
||||||
|
case TWOHANDCROSSBOW:
|
||||||
{
|
{
|
||||||
case CROSSBOW:
|
crossbow = true;
|
||||||
case TWOHANDCROSSBOW:
|
}
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
||||||
|
|
||||||
|
// Consume arrows
|
||||||
|
final Inventory inventory = getInventory();
|
||||||
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
crossbow = true;
|
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
||||||
}
|
}
|
||||||
case BOW:
|
|
||||||
|
// Check if the L2Character is a L2PcInstance
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
if (crossbow)
|
||||||
|
|
||||||
// Consume arrows
|
|
||||||
final Inventory inventory = getInventory();
|
|
||||||
if (inventory != null)
|
|
||||||
{
|
{
|
||||||
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving())
|
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
||||||
{
|
|
||||||
stopMove(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the L2Character is a L2PcInstance
|
|
||||||
if (isPlayer())
|
|
||||||
{
|
|
||||||
if (crossbow)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
|
||||||
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FIST:
|
|
||||||
{
|
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
||||||
if (!isPlayer())
|
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
||||||
{
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case FIST:
|
||||||
}
|
{
|
||||||
case DUAL:
|
if (!isPlayer())
|
||||||
case DUALFIST:
|
|
||||||
case DUALBLUNT:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
case DUALFIST:
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
case DUALBLUNT:
|
||||||
if (attack.hasHits())
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
broadcastPacket(attack);
|
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
||||||
|
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
player.updatePvPStatus(target);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
|
{
|
||||||
|
broadcastPacket(attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
npc.setScriptValue(1); // in combat
|
||||||
if (!npc.isScriptValue(1))
|
broadcastInfo(); // update flag status
|
||||||
{
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -224,9 +223,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final Object _attackLock = new Object();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -577,26 +573,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -905,273 +890,266 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAutoAttack(L2Character target)
|
public synchronized void doAutoAttack(L2Character target)
|
||||||
{
|
{
|
||||||
synchronized (_attackLock)
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
if (!target.isTargetable())
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkTransformed(transform -> !transform.canAttack()))
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final WeaponType weaponType = getAttackType();
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isInsidePeaceZone(this, target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
stopEffectsOnAction();
|
|
||||||
|
|
||||||
// GeoData Los Check here (or dz > 1000)
|
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
if (target.isDead())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOW and CROSSBOW checks
|
if (checkTransformed(transform -> !transform.canAttack()))
|
||||||
if (weaponItem != null)
|
|
||||||
{
|
{
|
||||||
if (!weaponItem.isAttackWeapon() && !isGM())
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final WeaponType weaponType = getAttackType();
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOW and CROSSBOW checks
|
||||||
|
if (weaponItem != null)
|
||||||
|
{
|
||||||
|
if (!weaponItem.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
||||||
{
|
{
|
||||||
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged weapon checks.
|
||||||
|
if (weaponItem.getItemType().isRanged())
|
||||||
|
{
|
||||||
|
// Check if bow delay is still active.
|
||||||
|
if (_disableRangedAttackEndTime > System.nanoTime())
|
||||||
|
{
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapon checks.
|
// Check for arrows and MP
|
||||||
if (weaponItem.getItemType().isRanged())
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
// Check if bow delay is still active.
|
// Check if there are arrows to use or else cancel the attack.
|
||||||
if (_disableRangedAttackEndTime > System.nanoTime())
|
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
||||||
{
|
{
|
||||||
if (isPlayer())
|
// Cancel the action because the L2PcInstance have no arrow
|
||||||
{
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for arrows and MP
|
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
||||||
if (isPlayer())
|
// Other melee is checked in movement code and for offensive spells a check is done every time
|
||||||
|
if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
// Check if there are arrows to use or else cancel the attack.
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
||||||
{
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
// Cancel the action because the L2PcInstance have no arrow
|
return;
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
}
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
// Check if player has enough MP to shoot.
|
||||||
return;
|
int mpConsume = weaponItem.getMpConsume();
|
||||||
}
|
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
||||||
|
{
|
||||||
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
mpConsume = weaponItem.getReducedMpConsume();
|
||||||
// Other melee is checked in movement code and for offensive spells a check is done every time
|
}
|
||||||
if (target.isInsidePeaceZone(getActingPlayer()))
|
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
||||||
{
|
if (_status.getCurrentMp() < mpConsume)
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
{
|
||||||
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
// If L2PcInstance doesn't have enough MP, stop the attack
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
return;
|
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
||||||
}
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
// Check if player has enough MP to shoot.
|
}
|
||||||
int mpConsume = weaponItem.getMpConsume();
|
|
||||||
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
// If L2PcInstance have enough MP, the bow consumes it
|
||||||
{
|
if (mpConsume > 0)
|
||||||
mpConsume = weaponItem.getReducedMpConsume();
|
{
|
||||||
}
|
_status.reduceMp(mpConsume);
|
||||||
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
|
||||||
if (_status.getCurrentMp() < mpConsume)
|
|
||||||
{
|
|
||||||
// If L2PcInstance doesn't have enough MP, stop the attack
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
||||||
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If L2PcInstance have enough MP, the bow consumes it
|
|
||||||
if (mpConsume > 0)
|
|
||||||
{
|
|
||||||
_status.reduceMp(mpConsume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final WeaponType attackType = getAttackType();
|
|
||||||
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
// Mobius: Do not move when attack is launched.
|
||||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
if (isMoving())
|
||||||
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
{
|
||||||
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
// Make sure that char is facing selected target
|
|
||||||
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
final WeaponType attackType = getAttackType();
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
||||||
|
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
||||||
// Get the Attack Reuse Delay of the L2Weapon
|
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
||||||
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
||||||
boolean crossbow = false;
|
|
||||||
switch (attackType)
|
// Make sure that char is facing selected target
|
||||||
|
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
|
||||||
|
// Get the Attack Reuse Delay of the L2Weapon
|
||||||
|
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
||||||
|
boolean crossbow = false;
|
||||||
|
switch (attackType)
|
||||||
|
{
|
||||||
|
case CROSSBOW:
|
||||||
|
case TWOHANDCROSSBOW:
|
||||||
{
|
{
|
||||||
case CROSSBOW:
|
crossbow = true;
|
||||||
case TWOHANDCROSSBOW:
|
}
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
||||||
|
|
||||||
|
// Consume arrows
|
||||||
|
final Inventory inventory = getInventory();
|
||||||
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
crossbow = true;
|
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
||||||
}
|
}
|
||||||
case BOW:
|
|
||||||
|
// Check if the L2Character is a L2PcInstance
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
if (crossbow)
|
||||||
|
|
||||||
// Consume arrows
|
|
||||||
final Inventory inventory = getInventory();
|
|
||||||
if (inventory != null)
|
|
||||||
{
|
{
|
||||||
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving())
|
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
||||||
{
|
|
||||||
stopMove(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the L2Character is a L2PcInstance
|
|
||||||
if (isPlayer())
|
|
||||||
{
|
|
||||||
if (crossbow)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
|
||||||
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FIST:
|
|
||||||
{
|
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
||||||
if (!isPlayer())
|
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
||||||
{
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case FIST:
|
||||||
}
|
{
|
||||||
case DUAL:
|
if (!isPlayer())
|
||||||
case DUALFIST:
|
|
||||||
case DUALBLUNT:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
case DUALFIST:
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
case DUALBLUNT:
|
||||||
if (attack.hasHits())
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
broadcastPacket(attack);
|
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
||||||
|
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
player.updatePvPStatus(target);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
|
{
|
||||||
|
broadcastPacket(attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
npc.setScriptValue(1); // in combat
|
||||||
if (!npc.isScriptValue(1))
|
broadcastInfo(); // update flag status
|
||||||
{
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -224,9 +223,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final Object _attackLock = new Object();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -577,26 +573,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -905,273 +890,266 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAutoAttack(L2Character target)
|
public synchronized void doAutoAttack(L2Character target)
|
||||||
{
|
{
|
||||||
synchronized (_attackLock)
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
if (!target.isTargetable())
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkTransformed(transform -> !transform.canAttack()))
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final WeaponType weaponType = getAttackType();
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isInsidePeaceZone(this, target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
stopEffectsOnAction();
|
|
||||||
|
|
||||||
// GeoData Los Check here (or dz > 1000)
|
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
if (target.isDead())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOW and CROSSBOW checks
|
if (checkTransformed(transform -> !transform.canAttack()))
|
||||||
if (weaponItem != null)
|
|
||||||
{
|
{
|
||||||
if (!weaponItem.isAttackWeapon() && !isGM())
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final WeaponType weaponType = getAttackType();
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOW and CROSSBOW checks
|
||||||
|
if (weaponItem != null)
|
||||||
|
{
|
||||||
|
if (!weaponItem.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
||||||
{
|
{
|
||||||
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged weapon checks.
|
||||||
|
if (weaponItem.getItemType().isRanged())
|
||||||
|
{
|
||||||
|
// Check if bow delay is still active.
|
||||||
|
if (_disableRangedAttackEndTime > System.nanoTime())
|
||||||
|
{
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapon checks.
|
// Check for arrows and MP
|
||||||
if (weaponItem.getItemType().isRanged())
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
// Check if bow delay is still active.
|
// Check if there are arrows to use or else cancel the attack.
|
||||||
if (_disableRangedAttackEndTime > System.nanoTime())
|
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
||||||
{
|
{
|
||||||
if (isPlayer())
|
// Cancel the action because the L2PcInstance have no arrow
|
||||||
{
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for arrows and MP
|
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
||||||
if (isPlayer())
|
// Other melee is checked in movement code and for offensive spells a check is done every time
|
||||||
|
if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
// Check if there are arrows to use or else cancel the attack.
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
||||||
{
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
// Cancel the action because the L2PcInstance have no arrow
|
return;
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
}
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
// Check if player has enough MP to shoot.
|
||||||
return;
|
int mpConsume = weaponItem.getMpConsume();
|
||||||
}
|
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
||||||
|
{
|
||||||
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
mpConsume = weaponItem.getReducedMpConsume();
|
||||||
// Other melee is checked in movement code and for offensive spells a check is done every time
|
}
|
||||||
if (target.isInsidePeaceZone(getActingPlayer()))
|
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
||||||
{
|
if (_status.getCurrentMp() < mpConsume)
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
{
|
||||||
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
// If L2PcInstance doesn't have enough MP, stop the attack
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
return;
|
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
||||||
}
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
// Check if player has enough MP to shoot.
|
}
|
||||||
int mpConsume = weaponItem.getMpConsume();
|
|
||||||
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
// If L2PcInstance have enough MP, the bow consumes it
|
||||||
{
|
if (mpConsume > 0)
|
||||||
mpConsume = weaponItem.getReducedMpConsume();
|
{
|
||||||
}
|
_status.reduceMp(mpConsume);
|
||||||
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
|
||||||
if (_status.getCurrentMp() < mpConsume)
|
|
||||||
{
|
|
||||||
// If L2PcInstance doesn't have enough MP, stop the attack
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
||||||
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If L2PcInstance have enough MP, the bow consumes it
|
|
||||||
if (mpConsume > 0)
|
|
||||||
{
|
|
||||||
_status.reduceMp(mpConsume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final WeaponType attackType = getAttackType();
|
|
||||||
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
// Mobius: Do not move when attack is launched.
|
||||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
if (isMoving())
|
||||||
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
{
|
||||||
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
// Make sure that char is facing selected target
|
|
||||||
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
final WeaponType attackType = getAttackType();
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
||||||
|
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
||||||
// Get the Attack Reuse Delay of the L2Weapon
|
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
||||||
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
||||||
boolean crossbow = false;
|
|
||||||
switch (attackType)
|
// Make sure that char is facing selected target
|
||||||
|
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
|
||||||
|
// Get the Attack Reuse Delay of the L2Weapon
|
||||||
|
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
||||||
|
boolean crossbow = false;
|
||||||
|
switch (attackType)
|
||||||
|
{
|
||||||
|
case CROSSBOW:
|
||||||
|
case TWOHANDCROSSBOW:
|
||||||
{
|
{
|
||||||
case CROSSBOW:
|
crossbow = true;
|
||||||
case TWOHANDCROSSBOW:
|
}
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
||||||
|
|
||||||
|
// Consume arrows
|
||||||
|
final Inventory inventory = getInventory();
|
||||||
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
crossbow = true;
|
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
||||||
}
|
}
|
||||||
case BOW:
|
|
||||||
|
// Check if the L2Character is a L2PcInstance
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
if (crossbow)
|
||||||
|
|
||||||
// Consume arrows
|
|
||||||
final Inventory inventory = getInventory();
|
|
||||||
if (inventory != null)
|
|
||||||
{
|
{
|
||||||
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving())
|
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
||||||
{
|
|
||||||
stopMove(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the L2Character is a L2PcInstance
|
|
||||||
if (isPlayer())
|
|
||||||
{
|
|
||||||
if (crossbow)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
|
||||||
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FIST:
|
|
||||||
{
|
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
||||||
if (!isPlayer())
|
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
||||||
{
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case FIST:
|
||||||
}
|
{
|
||||||
case DUAL:
|
if (!isPlayer())
|
||||||
case DUALFIST:
|
|
||||||
case DUALBLUNT:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
case DUALFIST:
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
case DUALBLUNT:
|
||||||
if (attack.hasHits())
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
broadcastPacket(attack);
|
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
||||||
|
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
player.updatePvPStatus(target);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
|
{
|
||||||
|
broadcastPacket(attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
npc.setScriptValue(1); // in combat
|
||||||
if (!npc.isScriptValue(1))
|
broadcastInfo(); // update flag status
|
||||||
{
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -30,8 +30,6 @@ import java.util.concurrent.CopyOnWriteArrayList;
|
|||||||
import java.util.concurrent.Future;
|
import java.util.concurrent.Future;
|
||||||
import java.util.concurrent.LinkedBlockingDeque;
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.concurrent.locks.StampedLock;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
@ -227,9 +225,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final StampedLock _attackLock = new StampedLock();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -509,26 +504,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
revalidateZone(true);
|
revalidateZone(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -859,259 +843,257 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAttack(L2Character target)
|
public synchronized void doAttack(L2Character target)
|
||||||
{
|
{
|
||||||
final long stamp = _attackLock.tryWriteLock();
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
try
|
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
// Notify to scripts
|
||||||
|
final TerminateReturn attackReturn = EventDispatcher.getInstance().notifyEvent(new OnCreatureAttack(this, target), this, TerminateReturn.class);
|
||||||
// Notify to scripts
|
if ((attackReturn != null) && attackReturn.terminate())
|
||||||
final TerminateReturn attackReturn = EventDispatcher.getInstance().notifyEvent(new OnCreatureAttack(this, target), this, TerminateReturn.class);
|
{
|
||||||
if ((attackReturn != null) && attackReturn.terminate())
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify to scripts
|
||||||
|
final TerminateReturn attackedReturn = EventDispatcher.getInstance().notifyEvent(new OnCreatureAttacked(this, target), target, TerminateReturn.class);
|
||||||
|
if ((attackedReturn != null) && attackedReturn.terminate())
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
// Notify to scripts
|
|
||||||
final TerminateReturn attackedReturn = EventDispatcher.getInstance().notifyEvent(new OnCreatureAttacked(this, target), target, TerminateReturn.class);
|
|
||||||
if ((attackedReturn != null) && attackedReturn.terminate())
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
if (target.isDead())
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final L2PcInstance actor = getActingPlayer();
|
|
||||||
if (actor.isTransformed() && !actor.getTransformation().canAttack())
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if attacker's weapon can attack
|
|
||||||
if (getActiveWeaponItem() != null)
|
|
||||||
{
|
|
||||||
final L2Weapon wpn = getActiveWeaponItem();
|
|
||||||
if (!wpn.isAttackWeapon() && !isGM())
|
|
||||||
{
|
|
||||||
if (wpn.getItemType() == WeaponType.FISHINGROD)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
final L2PcInstance actor = getActingPlayer();
|
||||||
|
if (actor.isTransformed() && !actor.getTransformation().canAttack())
|
||||||
{
|
{
|
||||||
if (TerritoryWarManager.getInstance().isTWInProgress())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOU_CANNOT_FORCE_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (isInsidePeaceZone(this, target))
|
}
|
||||||
|
|
||||||
|
// Check if attacker's weapon can attack
|
||||||
|
if (getActiveWeaponItem() != null)
|
||||||
|
{
|
||||||
|
final L2Weapon wpn = getActiveWeaponItem();
|
||||||
|
if (!wpn.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (wpn.getItemType() == WeaponType.FISHINGROD)
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
if (TerritoryWarManager.getInstance().isTWInProgress())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.YOU_CANNOT_FORCE_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
stopEffectsOnAction();
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
// GeoData Los Check here (or dz > 1000)
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mobius: Do not move when attack is launched.
|
||||||
|
if (isMoving())
|
||||||
|
{
|
||||||
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final int timeAtk = calculateTimeBetweenAttacks();
|
||||||
|
final int timeToHit = timeAtk / 2;
|
||||||
|
|
||||||
|
final Attack attack = new Attack(this, target, isChargedShot(ShotType.SOULSHOTS), (weaponItem != null) ? weaponItem.getCrystalTypePlus().getLevel() : 0);
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
final int reuse = calculateReuseTime(weaponItem);
|
||||||
|
|
||||||
|
boolean hitted = false;
|
||||||
|
switch (getAttackType())
|
||||||
|
{
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
if (!canUseRangeWeapon())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeToHit + (reuse / 2), TimeUnit.MILLISECONDS);
|
||||||
|
hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
case CROSSBOW:
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final int timeAtk = calculateTimeBetweenAttacks();
|
|
||||||
final int timeToHit = timeAtk / 2;
|
|
||||||
|
|
||||||
final Attack attack = new Attack(this, target, isChargedShot(ShotType.SOULSHOTS), (weaponItem != null) ? weaponItem.getCrystalTypePlus().getLevel() : 0);
|
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
|
||||||
final int reuse = calculateReuseTime(weaponItem);
|
|
||||||
|
|
||||||
boolean hitted = false;
|
|
||||||
switch (getAttackType())
|
|
||||||
{
|
{
|
||||||
case BOW:
|
if (!canUseRangeWeapon())
|
||||||
{
|
{
|
||||||
if (!canUseRangeWeapon())
|
return;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeToHit + (reuse / 2), TimeUnit.MILLISECONDS);
|
|
||||||
hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case CROSSBOW:
|
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeToHit + (reuse / 2), TimeUnit.MILLISECONDS);
|
||||||
|
hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case POLE:
|
||||||
|
{
|
||||||
|
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeAtk, TimeUnit.MILLISECONDS);
|
||||||
|
hitted = doAttackHitByPole(attack, target, timeToHit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case FIST:
|
||||||
|
{
|
||||||
|
if (!isPlayer())
|
||||||
{
|
{
|
||||||
if (!canUseRangeWeapon())
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeToHit + (reuse / 2), TimeUnit.MILLISECONDS);
|
|
||||||
hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case POLE:
|
|
||||||
{
|
|
||||||
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeAtk, TimeUnit.MILLISECONDS);
|
|
||||||
hitted = doAttackHitByPole(attack, target, timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case FIST:
|
|
||||||
{
|
|
||||||
if (!isPlayer())
|
|
||||||
{
|
|
||||||
hitted = doAttackHitSimple(attack, target, timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case DUAL:
|
|
||||||
case DUALFIST:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeAtk, TimeUnit.MILLISECONDS);
|
|
||||||
hitted = doAttackHitByDual(attack, target, timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeAtk, TimeUnit.MILLISECONDS);
|
|
||||||
hitted = doAttackHitSimple(attack, target, timeToHit);
|
hitted = doAttackHitSimple(attack, target, timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
case DUALFIST:
|
||||||
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeAtk, TimeUnit.MILLISECONDS);
|
||||||
if (!npc.isScriptValue(1))
|
hitted = doAttackHitByDual(attack, target, timeToHit);
|
||||||
{
|
break;
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeAtk, TimeUnit.MILLISECONDS);
|
||||||
|
hitted = doAttackHitSimple(attack, target, timeToHit);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
|
{
|
||||||
|
npc.setScriptValue(1); // in combat
|
||||||
|
broadcastInfo(); // update flag status
|
||||||
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
if (player.getSummon() != target)
|
||||||
|
{
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if hit isn't missed
|
||||||
|
if (!hitted)
|
||||||
|
{
|
||||||
|
abortAttack(); // Abort the attack of the L2Character and send Server->Client ActionFailed packet
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// If we didn't miss the hit, discharge the shoulshots, if any
|
||||||
|
setChargedShot(ShotType.SOULSHOTS, false);
|
||||||
|
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
if (player != null)
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
if (player.isCursedWeaponEquipped())
|
||||||
if (player.getSummon() != target)
|
|
||||||
{
|
{
|
||||||
player.updatePvPStatus(target);
|
// If hit by a cursed weapon, CP is reduced to 0
|
||||||
}
|
if (!target.isInvul())
|
||||||
}
|
|
||||||
|
|
||||||
// Check if hit isn't missed
|
|
||||||
if (!hitted)
|
|
||||||
{
|
|
||||||
abortAttack(); // Abort the attack of the L2Character and send Server->Client ActionFailed packet
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// If we didn't miss the hit, discharge the shoulshots, if any
|
|
||||||
setChargedShot(ShotType.SOULSHOTS, false);
|
|
||||||
|
|
||||||
if (player != null)
|
|
||||||
{
|
|
||||||
if (player.isCursedWeaponEquipped())
|
|
||||||
{
|
{
|
||||||
// If hit by a cursed weapon, CP is reduced to 0
|
target.setCurrentCp(0);
|
||||||
if (!target.isInvul())
|
|
||||||
{
|
|
||||||
target.setCurrentCp(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (player.isHero())
|
}
|
||||||
|
else if (player.isHero())
|
||||||
|
{
|
||||||
|
// If a cursed weapon is hit by a Hero, CP is reduced to 0
|
||||||
|
if (target.isPlayer() && target.getActingPlayer().isCursedWeaponEquipped())
|
||||||
{
|
{
|
||||||
// If a cursed weapon is hit by a Hero, CP is reduced to 0
|
target.setCurrentCp(0);
|
||||||
if (target.isPlayer() && target.getActingPlayer().isCursedWeaponEquipped())
|
|
||||||
{
|
|
||||||
target.setCurrentCp(0);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
|
||||||
if (attack.hasHits())
|
|
||||||
{
|
|
||||||
broadcastPacket(attack);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Notify AI with EVT_READY_TO_ACT
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
|
|
||||||
}
|
}
|
||||||
finally
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
{
|
{
|
||||||
_attackLock.unlockWrite(stamp);
|
broadcastPacket(attack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Notify AI with EVT_READY_TO_ACT
|
||||||
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -34,7 +34,6 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -224,9 +223,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final Object _attackLock = new Object();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -577,26 +573,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -905,273 +890,266 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAutoAttack(L2Character target)
|
public synchronized void doAutoAttack(L2Character target)
|
||||||
{
|
{
|
||||||
synchronized (_attackLock)
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
if (!target.isTargetable())
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkTransformed(transform -> !transform.canAttack()))
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final WeaponType weaponType = getAttackType();
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isInsidePeaceZone(this, target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
stopEffectsOnAction();
|
|
||||||
|
|
||||||
// GeoData Los Check here (or dz > 1000)
|
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
if (target.isDead())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOW and CROSSBOW checks
|
if (checkTransformed(transform -> !transform.canAttack()))
|
||||||
if (weaponItem != null)
|
|
||||||
{
|
{
|
||||||
if (!weaponItem.isAttackWeapon() && !isGM())
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final WeaponType weaponType = getAttackType();
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOW and CROSSBOW checks
|
||||||
|
if (weaponItem != null)
|
||||||
|
{
|
||||||
|
if (!weaponItem.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
||||||
{
|
{
|
||||||
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged weapon checks.
|
||||||
|
if (weaponItem.getItemType().isRanged())
|
||||||
|
{
|
||||||
|
// Check if bow delay is still active.
|
||||||
|
if (_disableRangedAttackEndTime > System.nanoTime())
|
||||||
|
{
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapon checks.
|
// Check for arrows and MP
|
||||||
if (weaponItem.getItemType().isRanged())
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
// Check if bow delay is still active.
|
// Check if there are arrows to use or else cancel the attack.
|
||||||
if (_disableRangedAttackEndTime > System.nanoTime())
|
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
||||||
{
|
{
|
||||||
if (isPlayer())
|
// Cancel the action because the L2PcInstance have no arrow
|
||||||
{
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for arrows and MP
|
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
||||||
if (isPlayer())
|
// Other melee is checked in movement code and for offensive spells a check is done every time
|
||||||
|
if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
// Check if there are arrows to use or else cancel the attack.
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
||||||
{
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
// Cancel the action because the L2PcInstance have no arrow
|
return;
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
}
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
// Check if player has enough MP to shoot.
|
||||||
return;
|
int mpConsume = weaponItem.getMpConsume();
|
||||||
}
|
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
||||||
|
{
|
||||||
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
mpConsume = weaponItem.getReducedMpConsume();
|
||||||
// Other melee is checked in movement code and for offensive spells a check is done every time
|
}
|
||||||
if (target.isInsidePeaceZone(getActingPlayer()))
|
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
||||||
{
|
if (_status.getCurrentMp() < mpConsume)
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
{
|
||||||
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
// If L2PcInstance doesn't have enough MP, stop the attack
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
return;
|
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
||||||
}
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
// Check if player has enough MP to shoot.
|
}
|
||||||
int mpConsume = weaponItem.getMpConsume();
|
|
||||||
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
// If L2PcInstance have enough MP, the bow consumes it
|
||||||
{
|
if (mpConsume > 0)
|
||||||
mpConsume = weaponItem.getReducedMpConsume();
|
{
|
||||||
}
|
_status.reduceMp(mpConsume);
|
||||||
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
|
||||||
if (_status.getCurrentMp() < mpConsume)
|
|
||||||
{
|
|
||||||
// If L2PcInstance doesn't have enough MP, stop the attack
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
||||||
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If L2PcInstance have enough MP, the bow consumes it
|
|
||||||
if (mpConsume > 0)
|
|
||||||
{
|
|
||||||
_status.reduceMp(mpConsume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final WeaponType attackType = getAttackType();
|
|
||||||
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
// Mobius: Do not move when attack is launched.
|
||||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
if (isMoving())
|
||||||
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
{
|
||||||
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
// Make sure that char is facing selected target
|
|
||||||
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
final WeaponType attackType = getAttackType();
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
||||||
|
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
||||||
// Get the Attack Reuse Delay of the L2Weapon
|
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
||||||
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
||||||
boolean crossbow = false;
|
|
||||||
switch (attackType)
|
// Make sure that char is facing selected target
|
||||||
|
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
|
||||||
|
// Get the Attack Reuse Delay of the L2Weapon
|
||||||
|
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
||||||
|
boolean crossbow = false;
|
||||||
|
switch (attackType)
|
||||||
|
{
|
||||||
|
case CROSSBOW:
|
||||||
|
case TWOHANDCROSSBOW:
|
||||||
{
|
{
|
||||||
case CROSSBOW:
|
crossbow = true;
|
||||||
case TWOHANDCROSSBOW:
|
}
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
||||||
|
|
||||||
|
// Consume arrows
|
||||||
|
final Inventory inventory = getInventory();
|
||||||
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
crossbow = true;
|
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
||||||
}
|
}
|
||||||
case BOW:
|
|
||||||
|
// Check if the L2Character is a L2PcInstance
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
if (crossbow)
|
||||||
|
|
||||||
// Consume arrows
|
|
||||||
final Inventory inventory = getInventory();
|
|
||||||
if (inventory != null)
|
|
||||||
{
|
{
|
||||||
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving())
|
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
||||||
{
|
|
||||||
stopMove(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the L2Character is a L2PcInstance
|
|
||||||
if (isPlayer())
|
|
||||||
{
|
|
||||||
if (crossbow)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
|
||||||
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FIST:
|
|
||||||
{
|
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
||||||
if (!isPlayer())
|
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
||||||
{
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case FIST:
|
||||||
}
|
{
|
||||||
case DUAL:
|
if (!isPlayer())
|
||||||
case DUALFIST:
|
|
||||||
case DUALBLUNT:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
case DUALFIST:
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
case DUALBLUNT:
|
||||||
if (attack.hasHits())
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
broadcastPacket(attack);
|
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
||||||
|
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
player.updatePvPStatus(target);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
|
{
|
||||||
|
broadcastPacket(attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
npc.setScriptValue(1); // in combat
|
||||||
if (!npc.isScriptValue(1))
|
broadcastInfo(); // update flag status
|
||||||
{
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -224,9 +223,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final Object _attackLock = new Object();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -577,26 +573,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -905,273 +890,266 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAutoAttack(L2Character target)
|
public synchronized void doAutoAttack(L2Character target)
|
||||||
{
|
{
|
||||||
synchronized (_attackLock)
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
if (!target.isTargetable())
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkTransformed(transform -> !transform.canAttack()))
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final WeaponType weaponType = getAttackType();
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isInsidePeaceZone(this, target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
stopEffectsOnAction();
|
|
||||||
|
|
||||||
// GeoData Los Check here (or dz > 1000)
|
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
if (target.isDead())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOW and CROSSBOW checks
|
if (checkTransformed(transform -> !transform.canAttack()))
|
||||||
if (weaponItem != null)
|
|
||||||
{
|
{
|
||||||
if (!weaponItem.isAttackWeapon() && !isGM())
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final WeaponType weaponType = getAttackType();
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOW and CROSSBOW checks
|
||||||
|
if (weaponItem != null)
|
||||||
|
{
|
||||||
|
if (!weaponItem.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
||||||
{
|
{
|
||||||
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged weapon checks.
|
||||||
|
if (weaponItem.getItemType().isRanged())
|
||||||
|
{
|
||||||
|
// Check if bow delay is still active.
|
||||||
|
if (_disableRangedAttackEndTime > System.nanoTime())
|
||||||
|
{
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapon checks.
|
// Check for arrows and MP
|
||||||
if (weaponItem.getItemType().isRanged())
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
// Check if bow delay is still active.
|
// Check if there are arrows to use or else cancel the attack.
|
||||||
if (_disableRangedAttackEndTime > System.nanoTime())
|
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
||||||
{
|
{
|
||||||
if (isPlayer())
|
// Cancel the action because the L2PcInstance have no arrow
|
||||||
{
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for arrows and MP
|
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
||||||
if (isPlayer())
|
// Other melee is checked in movement code and for offensive spells a check is done every time
|
||||||
|
if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
// Check if there are arrows to use or else cancel the attack.
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
||||||
{
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
// Cancel the action because the L2PcInstance have no arrow
|
return;
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
}
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
// Check if player has enough MP to shoot.
|
||||||
return;
|
int mpConsume = weaponItem.getMpConsume();
|
||||||
}
|
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
||||||
|
{
|
||||||
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
mpConsume = weaponItem.getReducedMpConsume();
|
||||||
// Other melee is checked in movement code and for offensive spells a check is done every time
|
}
|
||||||
if (target.isInsidePeaceZone(getActingPlayer()))
|
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
||||||
{
|
if (_status.getCurrentMp() < mpConsume)
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
{
|
||||||
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
// If L2PcInstance doesn't have enough MP, stop the attack
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
return;
|
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
||||||
}
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
// Check if player has enough MP to shoot.
|
}
|
||||||
int mpConsume = weaponItem.getMpConsume();
|
|
||||||
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
// If L2PcInstance have enough MP, the bow consumes it
|
||||||
{
|
if (mpConsume > 0)
|
||||||
mpConsume = weaponItem.getReducedMpConsume();
|
{
|
||||||
}
|
_status.reduceMp(mpConsume);
|
||||||
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
|
||||||
if (_status.getCurrentMp() < mpConsume)
|
|
||||||
{
|
|
||||||
// If L2PcInstance doesn't have enough MP, stop the attack
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
||||||
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If L2PcInstance have enough MP, the bow consumes it
|
|
||||||
if (mpConsume > 0)
|
|
||||||
{
|
|
||||||
_status.reduceMp(mpConsume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final WeaponType attackType = getAttackType();
|
|
||||||
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
// Mobius: Do not move when attack is launched.
|
||||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
if (isMoving())
|
||||||
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
{
|
||||||
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
// Make sure that char is facing selected target
|
|
||||||
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
final WeaponType attackType = getAttackType();
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
||||||
|
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
||||||
// Get the Attack Reuse Delay of the L2Weapon
|
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
||||||
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
||||||
boolean crossbow = false;
|
|
||||||
switch (attackType)
|
// Make sure that char is facing selected target
|
||||||
|
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
|
||||||
|
// Get the Attack Reuse Delay of the L2Weapon
|
||||||
|
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
||||||
|
boolean crossbow = false;
|
||||||
|
switch (attackType)
|
||||||
|
{
|
||||||
|
case CROSSBOW:
|
||||||
|
case TWOHANDCROSSBOW:
|
||||||
{
|
{
|
||||||
case CROSSBOW:
|
crossbow = true;
|
||||||
case TWOHANDCROSSBOW:
|
}
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
||||||
|
|
||||||
|
// Consume arrows
|
||||||
|
final Inventory inventory = getInventory();
|
||||||
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
crossbow = true;
|
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
||||||
}
|
}
|
||||||
case BOW:
|
|
||||||
|
// Check if the L2Character is a L2PcInstance
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
if (crossbow)
|
||||||
|
|
||||||
// Consume arrows
|
|
||||||
final Inventory inventory = getInventory();
|
|
||||||
if (inventory != null)
|
|
||||||
{
|
{
|
||||||
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving())
|
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
||||||
{
|
|
||||||
stopMove(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the L2Character is a L2PcInstance
|
|
||||||
if (isPlayer())
|
|
||||||
{
|
|
||||||
if (crossbow)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
|
||||||
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FIST:
|
|
||||||
{
|
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
||||||
if (!isPlayer())
|
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
||||||
{
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case FIST:
|
||||||
}
|
{
|
||||||
case DUAL:
|
if (!isPlayer())
|
||||||
case DUALFIST:
|
|
||||||
case DUALBLUNT:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
case DUALFIST:
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
case DUALBLUNT:
|
||||||
if (attack.hasHits())
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
broadcastPacket(attack);
|
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
||||||
|
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
player.updatePvPStatus(target);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
|
{
|
||||||
|
broadcastPacket(attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
npc.setScriptValue(1); // in combat
|
||||||
if (!npc.isScriptValue(1))
|
broadcastInfo(); // update flag status
|
||||||
{
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,7 +34,6 @@ import java.util.concurrent.LinkedBlockingDeque;
|
|||||||
import java.util.concurrent.ScheduledFuture;
|
import java.util.concurrent.ScheduledFuture;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
@ -224,9 +223,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
private final byte[] _zones = new byte[ZoneId.getZoneCount()];
|
||||||
protected byte _zoneValidateCounter = 4;
|
protected byte _zoneValidateCounter = 4;
|
||||||
|
|
||||||
private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
||||||
private final Object _attackLock = new Object();
|
|
||||||
|
|
||||||
private Team _team = Team.NONE;
|
private Team _team = Team.NONE;
|
||||||
|
|
||||||
protected long _exceptions = 0;
|
protected long _exceptions = 0;
|
||||||
@ -577,26 +573,15 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onTeleported()
|
public synchronized void onTeleported()
|
||||||
{
|
{
|
||||||
if (!_teleportLock.tryLock())
|
if (!_isTeleporting)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
try
|
spawnMe(getX(), getY(), getZ());
|
||||||
{
|
setIsTeleporting(false);
|
||||||
if (!_isTeleporting)
|
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
spawnMe(getX(), getY(), getZ());
|
|
||||||
setIsTeleporting(false);
|
|
||||||
EventDispatcher.getInstance().notifyEventAsync(new OnCreatureTeleported(this), this);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
_teleportLock.unlock();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -905,273 +890,266 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|||||||
* </ul>
|
* </ul>
|
||||||
* @param target The L2Character targeted
|
* @param target The L2Character targeted
|
||||||
*/
|
*/
|
||||||
public void doAutoAttack(L2Character target)
|
public synchronized void doAutoAttack(L2Character target)
|
||||||
{
|
{
|
||||||
synchronized (_attackLock)
|
if ((target == null) || isAttackingDisabled() || !target.isTargetable())
|
||||||
{
|
{
|
||||||
if ((target == null) || isAttackingDisabled())
|
return;
|
||||||
{
|
}
|
||||||
return;
|
|
||||||
}
|
if (!isAlikeDead())
|
||||||
|
{
|
||||||
if (!target.isTargetable())
|
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isAlikeDead())
|
|
||||||
{
|
|
||||||
if ((isNpc() && target.isAlikeDead()) || !isInSurroundingRegion(target))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (isPlayer())
|
|
||||||
{
|
|
||||||
if (target.isDead())
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (checkTransformed(transform -> !transform.canAttack()))
|
|
||||||
{
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
||||||
final L2Weapon weaponItem = getActiveWeaponItem();
|
|
||||||
final WeaponType weaponType = getAttackType();
|
|
||||||
|
|
||||||
if (getActingPlayer() != null)
|
|
||||||
{
|
|
||||||
if (getActingPlayer().inObserverMode())
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checking if target has moved to peace zone
|
|
||||||
else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
||||||
{
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isInsidePeaceZone(this, target))
|
|
||||||
{
|
{
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (isPlayer())
|
||||||
stopEffectsOnAction();
|
|
||||||
|
|
||||||
// GeoData Los Check here (or dz > 1000)
|
|
||||||
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
|
||||||
{
|
{
|
||||||
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
if (target.isDead())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BOW and CROSSBOW checks
|
if (checkTransformed(transform -> !transform.canAttack()))
|
||||||
if (weaponItem != null)
|
|
||||||
{
|
{
|
||||||
if (!weaponItem.isAttackWeapon() && !isGM())
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
||||||
|
final L2Weapon weaponItem = getActiveWeaponItem();
|
||||||
|
final WeaponType weaponType = getAttackType();
|
||||||
|
|
||||||
|
if (getActingPlayer() != null)
|
||||||
|
{
|
||||||
|
if (getActingPlayer().inObserverMode())
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if ((target.getActingPlayer() != null) && (getActingPlayer().getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getActingPlayer().getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getActingPlayer().getSiegeSide()))
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Checking if target has moved to peace zone
|
||||||
|
else if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isInsidePeaceZone(this, target))
|
||||||
|
{
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
stopEffectsOnAction();
|
||||||
|
|
||||||
|
// GeoData Los Check here (or dz > 1000)
|
||||||
|
if (!GeoEngine.getInstance().canSeeTarget(this, target))
|
||||||
|
{
|
||||||
|
if (!target.isDoor() || (target.calculateDistance(this, false, false) > 200)) // fix for big door targeting
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.CANNOT_SEE_TARGET);
|
||||||
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// BOW and CROSSBOW checks
|
||||||
|
if (weaponItem != null)
|
||||||
|
{
|
||||||
|
if (!weaponItem.isAttackWeapon() && !isGM())
|
||||||
|
{
|
||||||
|
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
||||||
{
|
{
|
||||||
if (weaponItem.getItemType() == WeaponType.FISHINGROD)
|
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
||||||
|
}
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ranged weapon checks.
|
||||||
|
if (weaponItem.getItemType().isRanged())
|
||||||
|
{
|
||||||
|
// Check if bow delay is still active.
|
||||||
|
if (_disableRangedAttackEndTime > System.nanoTime())
|
||||||
|
{
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
sendPacket(SystemMessageId.YOU_LOOK_ODDLY_AT_THE_FISHING_POLE_IN_DISBELIEF_AND_REALIZE_THAT_YOU_CAN_T_ATTACK_ANYTHING_WITH_THIS);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.THAT_WEAPON_CANNOT_PERFORM_ANY_ATTACKS);
|
|
||||||
}
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ranged weapon checks.
|
// Check for arrows and MP
|
||||||
if (weaponItem.getItemType().isRanged())
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
// Check if bow delay is still active.
|
// Check if there are arrows to use or else cancel the attack.
|
||||||
if (_disableRangedAttackEndTime > System.nanoTime())
|
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
||||||
{
|
{
|
||||||
if (isPlayer())
|
// Cancel the action because the L2PcInstance have no arrow
|
||||||
{
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
||||||
}
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for arrows and MP
|
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
||||||
if (isPlayer())
|
// Other melee is checked in movement code and for offensive spells a check is done every time
|
||||||
|
if (target.isInsidePeaceZone(getActingPlayer()))
|
||||||
{
|
{
|
||||||
// Check if there are arrows to use or else cancel the attack.
|
getAI().setIntention(AI_INTENTION_ACTIVE);
|
||||||
if (!checkAndEquipAmmunition(weaponItem.getItemType().isCrossbow() ? EtcItemType.BOLT : EtcItemType.ARROW))
|
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
||||||
{
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
// Cancel the action because the L2PcInstance have no arrow
|
return;
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
}
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
sendPacket(SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS);
|
// Check if player has enough MP to shoot.
|
||||||
return;
|
int mpConsume = weaponItem.getMpConsume();
|
||||||
}
|
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
||||||
|
{
|
||||||
// Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
mpConsume = weaponItem.getReducedMpConsume();
|
||||||
// Other melee is checked in movement code and for offensive spells a check is done every time
|
}
|
||||||
if (target.isInsidePeaceZone(getActingPlayer()))
|
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
||||||
{
|
if (_status.getCurrentMp() < mpConsume)
|
||||||
getAI().setIntention(AI_INTENTION_ACTIVE);
|
{
|
||||||
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_IN_A_PEACEFUL_ZONE);
|
// If L2PcInstance doesn't have enough MP, stop the attack
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
||||||
return;
|
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
||||||
}
|
sendPacket(ActionFailed.STATIC_PACKET);
|
||||||
|
return;
|
||||||
// Check if player has enough MP to shoot.
|
}
|
||||||
int mpConsume = weaponItem.getMpConsume();
|
|
||||||
if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
// If L2PcInstance have enough MP, the bow consumes it
|
||||||
{
|
if (mpConsume > 0)
|
||||||
mpConsume = weaponItem.getReducedMpConsume();
|
{
|
||||||
}
|
_status.reduceMp(mpConsume);
|
||||||
mpConsume = isAffected(EffectFlag.CHEAPSHOT) ? 0 : mpConsume;
|
|
||||||
if (_status.getCurrentMp() < mpConsume)
|
|
||||||
{
|
|
||||||
// If L2PcInstance doesn't have enough MP, stop the attack
|
|
||||||
ThreadPool.schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
||||||
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
||||||
sendPacket(ActionFailed.STATIC_PACKET);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If L2PcInstance have enough MP, the bow consumes it
|
|
||||||
if (mpConsume > 0)
|
|
||||||
{
|
|
||||||
_status.reduceMp(mpConsume);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
final WeaponType attackType = getAttackType();
|
|
||||||
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
// Mobius: Do not move when attack is launched.
|
||||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
if (isMoving())
|
||||||
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
{
|
||||||
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
stopMove(getLocation());
|
||||||
|
}
|
||||||
// Make sure that char is facing selected target
|
|
||||||
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
final WeaponType attackType = getAttackType();
|
||||||
setHeading(Util.calculateHeadingFrom(this, target));
|
final boolean isTwoHanded = (weaponItem != null) && (weaponItem.getBodyPart() == L2Item.SLOT_LR_HAND);
|
||||||
|
final int timeAtk = Formulas.calculateTimeBetweenAttacks(_stat.getPAtkSpd());
|
||||||
// Get the Attack Reuse Delay of the L2Weapon
|
final int timeToHit = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, false);
|
||||||
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
_attackEndTime = System.nanoTime() + (TimeUnit.MILLISECONDS.toNanos(timeAtk));
|
||||||
boolean crossbow = false;
|
|
||||||
switch (attackType)
|
// Make sure that char is facing selected target
|
||||||
|
// also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
|
||||||
|
setHeading(Util.calculateHeadingFrom(this, target));
|
||||||
|
|
||||||
|
// Get the Attack Reuse Delay of the L2Weapon
|
||||||
|
final Attack attack = generateAttackTargetData(target, weaponItem, attackType);
|
||||||
|
boolean crossbow = false;
|
||||||
|
switch (attackType)
|
||||||
|
{
|
||||||
|
case CROSSBOW:
|
||||||
|
case TWOHANDCROSSBOW:
|
||||||
{
|
{
|
||||||
case CROSSBOW:
|
crossbow = true;
|
||||||
case TWOHANDCROSSBOW:
|
}
|
||||||
|
case BOW:
|
||||||
|
{
|
||||||
|
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
||||||
|
|
||||||
|
// Consume arrows
|
||||||
|
final Inventory inventory = getInventory();
|
||||||
|
if (inventory != null)
|
||||||
{
|
{
|
||||||
crossbow = true;
|
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
||||||
}
|
}
|
||||||
case BOW:
|
|
||||||
|
// Check if the L2Character is a L2PcInstance
|
||||||
|
if (isPlayer())
|
||||||
{
|
{
|
||||||
final int reuse = Formulas.calculateReuseTime(this, weaponItem);
|
if (crossbow)
|
||||||
|
|
||||||
// Consume arrows
|
|
||||||
final Inventory inventory = getInventory();
|
|
||||||
if (inventory != null)
|
|
||||||
{
|
{
|
||||||
inventory.reduceArrowCount(crossbow ? EtcItemType.BOLT : EtcItemType.ARROW);
|
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isMoving())
|
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
||||||
{
|
|
||||||
stopMove(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the L2Character is a L2PcInstance
|
|
||||||
if (isPlayer())
|
|
||||||
{
|
|
||||||
if (crossbow)
|
|
||||||
{
|
|
||||||
sendPacket(SystemMessageId.YOUR_CROSSBOW_IS_PREPARING_TO_FIRE);
|
|
||||||
}
|
|
||||||
|
|
||||||
sendPacket(new SetupGauge(getObjectId(), SetupGauge.RED, reuse));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
|
||||||
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
case FIST:
|
|
||||||
{
|
// Calculate and set the disable delay of the bow in function of the Attack Speed
|
||||||
if (!isPlayer())
|
_disableRangedAttackEndTime = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(reuse);
|
||||||
{
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
break;
|
||||||
break;
|
}
|
||||||
}
|
case FIST:
|
||||||
}
|
{
|
||||||
case DUAL:
|
if (!isPlayer())
|
||||||
case DUALFIST:
|
|
||||||
case DUALBLUNT:
|
|
||||||
case DUALDAGGER:
|
|
||||||
{
|
|
||||||
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
|
||||||
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
{
|
||||||
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case DUAL:
|
||||||
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
case DUALFIST:
|
||||||
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
case DUALBLUNT:
|
||||||
if (attack.hasHits())
|
case DUALDAGGER:
|
||||||
{
|
{
|
||||||
broadcastPacket(attack);
|
final int timeToHit2 = Formulas.calculateTimeToHit(timeAtk, weaponType, isTwoHanded, true) - timeToHit;
|
||||||
|
_hitTask = ThreadPool.schedule(() -> onFirstHitTimeForDual(weaponItem, attack, timeToHit, timeAtk, timeToHit2), timeToHit);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
default:
|
||||||
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
||||||
final L2PcInstance player = getActingPlayer();
|
|
||||||
if (player != null)
|
|
||||||
{
|
{
|
||||||
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
_hitTask = ThreadPool.schedule(() -> onHitTimeNotDual(weaponItem, attack, timeToHit, timeAtk), timeToHit);
|
||||||
player.updatePvPStatus(target);
|
break;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
|
||||||
|
// If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
|
||||||
|
// to the L2Character AND to all L2PcInstance in the _KnownPlayers of the L2Character
|
||||||
|
if (attack.hasHits())
|
||||||
|
{
|
||||||
|
broadcastPacket(attack);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flag the attacker if it's a L2PcInstance outside a PvP area
|
||||||
|
final L2PcInstance player = getActingPlayer();
|
||||||
|
if (player != null)
|
||||||
|
{
|
||||||
|
AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
||||||
|
player.updatePvPStatus(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFakePlayer() && (target.isPlayable() || target.isFakePlayer()))
|
||||||
|
{
|
||||||
|
final L2Npc npc = ((L2Npc) this);
|
||||||
|
if (!npc.isScriptValue(1))
|
||||||
{
|
{
|
||||||
final L2Npc npc = ((L2Npc) this);
|
npc.setScriptValue(1); // in combat
|
||||||
if (!npc.isScriptValue(1))
|
broadcastInfo(); // update flag status
|
||||||
{
|
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
||||||
npc.setScriptValue(1); // in combat
|
|
||||||
broadcastInfo(); // update flag status
|
|
||||||
QuestManager.getInstance().getQuest("PvpFlaggingStopTask").notifyEvent("FLAG_CHECK" + npc.getObjectId(), npc, null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user