|
@@ -227,7 +227,8 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|
|
|
|
|
private L2Character _debugger = null;
|
|
|
|
|
|
- private final ReentrantLock _teleportLock;
|
|
|
+ private final ReentrantLock _teleportLock = new ReentrantLock();
|
|
|
+ private final ReentrantLock _attackLock = new ReentrantLock();
|
|
|
|
|
|
private Team _team = Team.NONE;
|
|
|
|
|
@@ -506,7 +507,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|
|
}
|
|
|
|
|
|
setIsInvul(true);
|
|
|
- _teleportLock = new ReentrantLock();
|
|
|
}
|
|
|
|
|
|
protected void initCharStatusUpdateValues()
|
|
@@ -829,366 +829,377 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
|
|
* </ul>
|
|
|
* @param target The L2Character targeted
|
|
|
*/
|
|
|
- protected synchronized void doAttack(L2Character target)
|
|
|
+ protected void doAttack(L2Character target)
|
|
|
{
|
|
|
- if ((target == null) || isAttackingDisabled())
|
|
|
+ if (!_attackLock.tryLock())
|
|
|
{
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- // Notify to scripts
|
|
|
- EventDispatcher.getInstance().notifyEventAsync(new OnCreatureAttack(this, target), this);
|
|
|
-
|
|
|
- if (!isAlikeDead())
|
|
|
+ try
|
|
|
{
|
|
|
- if ((isNpc() && target.isAlikeDead()) || !getKnownList().knowsObject(target))
|
|
|
+ if ((target == null) || isAttackingDisabled())
|
|
|
{
|
|
|
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
return;
|
|
|
}
|
|
|
- else if (isPlayer())
|
|
|
+
|
|
|
+ // Notify to scripts
|
|
|
+ EventDispatcher.getInstance().notifyEventAsync(new OnCreatureAttack(this, target), this);
|
|
|
+
|
|
|
+ if (!isAlikeDead())
|
|
|
{
|
|
|
- if (target.isDead())
|
|
|
+ if ((isNpc() && target.isAlikeDead()) || !getKnownList().knowsObject(target))
|
|
|
{
|
|
|
getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
return;
|
|
|
}
|
|
|
-
|
|
|
- final L2PcInstance actor = getActingPlayer();
|
|
|
- if (actor.isTransformed() && !actor.getTransformation().canAttack())
|
|
|
+ else if (isPlayer())
|
|
|
{
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- return;
|
|
|
+ if (target.isDead())
|
|
|
+ {
|
|
|
+ getAI().setIntention(CtrlIntention.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)
|
|
|
- {
|
|
|
- L2Weapon wpn = getActiveWeaponItem();
|
|
|
- if (!wpn.isAttackWeapon() && !isGM())
|
|
|
+
|
|
|
+ // Check if attacker's weapon can attack
|
|
|
+ if (getActiveWeaponItem() != null)
|
|
|
{
|
|
|
- if (wpn.getItemType() == WeaponType.FISHINGROD)
|
|
|
- {
|
|
|
- sendPacket(SystemMessageId.CANNOT_ATTACK_WITH_FISHING_POLE);
|
|
|
- }
|
|
|
- else
|
|
|
+ L2Weapon wpn = getActiveWeaponItem();
|
|
|
+ if (!wpn.isAttackWeapon() && !isGM())
|
|
|
{
|
|
|
- sendPacket(SystemMessageId.THAT_WEAPON_CANT_ATTACK);
|
|
|
+ if (wpn.getItemType() == WeaponType.FISHINGROD)
|
|
|
+ {
|
|
|
+ sendPacket(SystemMessageId.CANNOT_ATTACK_WITH_FISHING_POLE);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ sendPacket(SystemMessageId.THAT_WEAPON_CANT_ATTACK);
|
|
|
+ }
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
}
|
|
|
- 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 (getActingPlayer() != null)
|
|
|
{
|
|
|
- if (TerritoryWarManager.getInstance().isTWInProgress())
|
|
|
+ if (getActingPlayer().inObserverMode())
|
|
|
{
|
|
|
- sendPacket(SystemMessageId.YOU_CANNOT_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY);
|
|
|
+ sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
}
|
|
|
- else
|
|
|
+
|
|
|
+ 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.FORCED_ATTACK_IS_IMPOSSIBLE_AGAINST_SIEGE_SIDE_TEMPORARY_ALLIED_MEMBERS);
|
|
|
+ if (TerritoryWarManager.getInstance().isTWInProgress())
|
|
|
+ {
|
|
|
+ sendPacket(SystemMessageId.YOU_CANNOT_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ sendPacket(SystemMessageId.FORCED_ATTACK_IS_IMPOSSIBLE_AGAINST_SIEGE_SIDE_TEMPORARY_ALLIED_MEMBERS);
|
|
|
+ }
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
}
|
|
|
+
|
|
|
+ // Checking if target has moved to peace zone
|
|
|
+ else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
|
+ {
|
|
|
+ getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (isInsidePeaceZone(this, target))
|
|
|
+ {
|
|
|
+ getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // Checking if target has moved to peace zone
|
|
|
- else if (target.isInsidePeaceZone(getActingPlayer()))
|
|
|
+ stopEffectsOnAction();
|
|
|
+
|
|
|
+ // Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
|
+ L2Weapon weaponItem = getActiveWeaponItem();
|
|
|
+
|
|
|
+ // GeoData Los Check here (or dz > 1000)
|
|
|
+ if (!GeoData.getInstance().canSeeTarget(this, target))
|
|
|
{
|
|
|
+ sendPacket(SystemMessageId.CANT_SEE_TARGET);
|
|
|
getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
return;
|
|
|
}
|
|
|
- }
|
|
|
- else if (isInsidePeaceZone(this, target))
|
|
|
- {
|
|
|
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- stopEffectsOnAction();
|
|
|
-
|
|
|
- // Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
|
|
|
- L2Weapon weaponItem = getActiveWeaponItem();
|
|
|
-
|
|
|
- // GeoData Los Check here (or dz > 1000)
|
|
|
- if (!GeoData.getInstance().canSeeTarget(this, target))
|
|
|
- {
|
|
|
- sendPacket(SystemMessageId.CANT_SEE_TARGET);
|
|
|
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // BOW and CROSSBOW checks
|
|
|
- if ((weaponItem != null) && !isTransformed())
|
|
|
- {
|
|
|
- if (weaponItem.getItemType() == WeaponType.BOW)
|
|
|
+
|
|
|
+ // BOW and CROSSBOW checks
|
|
|
+ if ((weaponItem != null) && !isTransformed())
|
|
|
{
|
|
|
- // Check for arrows and MP
|
|
|
- if (isPlayer())
|
|
|
+ if (weaponItem.getItemType() == WeaponType.BOW)
|
|
|
{
|
|
|
- // Checking if target has moved to peace zone - only for player-bow attacks at the moment
|
|
|
- // Other melee is checked in movement code and for offensive spells a check is done every time
|
|
|
- if (target.isInsidePeaceZone(getActingPlayer()))
|
|
|
- {
|
|
|
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2PcInstance then return True
|
|
|
- if (!checkAndEquipArrows())
|
|
|
- {
|
|
|
- // Cancel the action because the L2PcInstance have no arrow
|
|
|
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- sendPacket(SystemMessageId.NOT_ENOUGH_ARROWS);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Verify if the bow can be use
|
|
|
- if (_disableBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
|
|
|
+ // Check for arrows and MP
|
|
|
+ if (isPlayer())
|
|
|
{
|
|
|
- // Verify if L2PcInstance owns enough MP
|
|
|
- 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
|
|
|
+ // Other melee is checked in movement code and for offensive spells a check is done every time
|
|
|
+ if (target.isInsidePeaceZone(getActingPlayer()))
|
|
|
{
|
|
|
- mpConsume = weaponItem.getReducedMpConsume();
|
|
|
+ getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
}
|
|
|
- mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
|
|
|
|
|
|
- if (getCurrentMp() < mpConsume)
|
|
|
+ // Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2PcInstance then return True
|
|
|
+ if (!checkAndEquipArrows())
|
|
|
{
|
|
|
- // If L2PcInstance doesn't have enough MP, stop the attack
|
|
|
- ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
|
- sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
|
+ // Cancel the action because the L2PcInstance have no arrow
|
|
|
+ getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
|
|
sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ sendPacket(SystemMessageId.NOT_ENOUGH_ARROWS);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // If L2PcInstance have enough MP, the bow consumes it
|
|
|
- if (mpConsume > 0)
|
|
|
+ // Verify if the bow can be use
|
|
|
+ if (_disableBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
|
|
|
{
|
|
|
- getStatus().reduceMp(mpConsume);
|
|
|
+ // Verify if L2PcInstance owns enough MP
|
|
|
+ int mpConsume = weaponItem.getMpConsume();
|
|
|
+ if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
|
|
+ {
|
|
|
+ mpConsume = weaponItem.getReducedMpConsume();
|
|
|
+ }
|
|
|
+ mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
|
|
|
+
|
|
|
+ if (getCurrentMp() < mpConsume)
|
|
|
+ {
|
|
|
+ // If L2PcInstance doesn't have enough MP, stop the attack
|
|
|
+ ThreadPoolManager.getInstance().scheduleAi(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)
|
|
|
+ {
|
|
|
+ getStatus().reduceMp(mpConsume);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the period of bow no re-use
|
|
|
+ _disableBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Cancel the action because the bow can't be re-use at this moment
|
|
|
+ ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
}
|
|
|
-
|
|
|
- // Set the period of bow no re-use
|
|
|
- _disableBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // Cancel the action because the bow can't be re-use at this moment
|
|
|
- ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- return;
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
- if (weaponItem.getItemType() == WeaponType.CROSSBOW)
|
|
|
- {
|
|
|
- // Check for bolts
|
|
|
- if (isPlayer())
|
|
|
+ if (weaponItem.getItemType() == WeaponType.CROSSBOW)
|
|
|
{
|
|
|
- // Checking if target has moved to peace zone - only for player-crossbow attacks at the moment
|
|
|
- // Other melee is checked in movement code and for offensive spells a check is done every time
|
|
|
- if (target.isInsidePeaceZone(getActingPlayer()))
|
|
|
- {
|
|
|
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True
|
|
|
- if (!checkAndEquipBolts())
|
|
|
- {
|
|
|
- // Cancel the action because the L2PcInstance have no arrow
|
|
|
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- sendPacket(SystemMessageId.NOT_ENOUGH_BOLTS);
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // Verify if the crossbow can be use
|
|
|
- if (_disableCrossBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
|
|
|
+ // Check for bolts
|
|
|
+ if (isPlayer())
|
|
|
{
|
|
|
- // Verify if L2PcInstance owns enough MP
|
|
|
- int mpConsume = weaponItem.getMpConsume();
|
|
|
- if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
|
|
+ // Checking if target has moved to peace zone - only for player-crossbow attacks at the moment
|
|
|
+ // Other melee is checked in movement code and for offensive spells a check is done every time
|
|
|
+ if (target.isInsidePeaceZone(getActingPlayer()))
|
|
|
{
|
|
|
- mpConsume = weaponItem.getReducedMpConsume();
|
|
|
+ getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
}
|
|
|
- mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
|
|
|
|
|
|
- if (getCurrentMp() < mpConsume)
|
|
|
+ // Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True
|
|
|
+ if (!checkAndEquipBolts())
|
|
|
{
|
|
|
- // If L2PcInstance doesn't have enough MP, stop the attack
|
|
|
- ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
|
- sendPacket(SystemMessageId.NOT_ENOUGH_MP);
|
|
|
+ // Cancel the action because the L2PcInstance have no arrow
|
|
|
+ getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
|
|
|
sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ sendPacket(SystemMessageId.NOT_ENOUGH_BOLTS);
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- // If L2PcInstance have enough MP, the bow consumes it
|
|
|
- if (mpConsume > 0)
|
|
|
+ // Verify if the crossbow can be use
|
|
|
+ if (_disableCrossBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
|
|
|
{
|
|
|
- getStatus().reduceMp(mpConsume);
|
|
|
+ // Verify if L2PcInstance owns enough MP
|
|
|
+ int mpConsume = weaponItem.getMpConsume();
|
|
|
+ if ((weaponItem.getReducedMpConsume() > 0) && (Rnd.get(100) < weaponItem.getReducedMpConsumeChance()))
|
|
|
+ {
|
|
|
+ mpConsume = weaponItem.getReducedMpConsume();
|
|
|
+ }
|
|
|
+ mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);
|
|
|
+
|
|
|
+ if (getCurrentMp() < mpConsume)
|
|
|
+ {
|
|
|
+ // If L2PcInstance doesn't have enough MP, stop the attack
|
|
|
+ ThreadPoolManager.getInstance().scheduleAi(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)
|
|
|
+ {
|
|
|
+ getStatus().reduceMp(mpConsume);
|
|
|
+ }
|
|
|
+
|
|
|
+ // Set the period of crossbow no re-use
|
|
|
+ _disableCrossBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // Cancel the action because the crossbow can't be re-use at this moment
|
|
|
+ ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
|
+ sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
+ return;
|
|
|
}
|
|
|
-
|
|
|
- // Set the period of crossbow no re-use
|
|
|
- _disableCrossBowAttackEndTime = (5 * GameTimeController.TICKS_PER_SECOND) + GameTimeController.getInstance().getGameTicks();
|
|
|
- }
|
|
|
- else
|
|
|
- {
|
|
|
- // Cancel the action because the crossbow can't be re-use at this moment
|
|
|
- ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), 1000);
|
|
|
- sendPacket(ActionFailed.STATIC_PACKET);
|
|
|
- return;
|
|
|
}
|
|
|
- }
|
|
|
- else if (isNpc())
|
|
|
- {
|
|
|
- if (_disableCrossBowAttackEndTime > GameTimeController.getInstance().getGameTicks())
|
|
|
+ else if (isNpc())
|
|
|
{
|
|
|
- return;
|
|
|
+ if (_disableCrossBowAttackEndTime > GameTimeController.getInstance().getGameTicks())
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // Add the L2PcInstance to _knownObjects and _knownPlayer of the target
|
|
|
- target.getKnownList().addKnownObject(this);
|
|
|
-
|
|
|
- // Reduce the current CP if TIREDNESS configuration is activated
|
|
|
- if (Config.ALT_GAME_TIREDNESS)
|
|
|
- {
|
|
|
- setCurrentCp(getCurrentCp() - 10);
|
|
|
- }
|
|
|
-
|
|
|
- // Verify if soulshots are charged.
|
|
|
- final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS);
|
|
|
- // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
|
|
- final int timeAtk = calculateTimeBetweenAttacks(target, weaponItem);
|
|
|
- // the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
|
|
|
- final int timeToHit = timeAtk / 2;
|
|
|
- _attackEndTime = System.currentTimeMillis() + timeAtk;
|
|
|
- final int ssGrade = (weaponItem != null) ? weaponItem.getItemGradeSPlus().getId() : 0;
|
|
|
- // Create a Server->Client packet Attack
|
|
|
- Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
|
|
|
-
|
|
|
- // 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
|
|
|
- int reuse = calculateReuseTime(target, weaponItem);
|
|
|
-
|
|
|
- boolean hitted = false;
|
|
|
- switch (getAttackType())
|
|
|
- {
|
|
|
- case BOW:
|
|
|
- {
|
|
|
- hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
|
|
|
- break;
|
|
|
- }
|
|
|
- case CROSSBOW:
|
|
|
- {
|
|
|
- hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);
|
|
|
- break;
|
|
|
- }
|
|
|
- case POLE:
|
|
|
+
|
|
|
+ // Add the L2PcInstance to _knownObjects and _knownPlayer of the target
|
|
|
+ target.getKnownList().addKnownObject(this);
|
|
|
+
|
|
|
+ // Reduce the current CP if TIREDNESS configuration is activated
|
|
|
+ if (Config.ALT_GAME_TIREDNESS)
|
|
|
{
|
|
|
- hitted = doAttackHitByPole(attack, target, timeToHit);
|
|
|
- break;
|
|
|
+ setCurrentCp(getCurrentCp() - 10);
|
|
|
}
|
|
|
- case FIST:
|
|
|
+
|
|
|
+ // Verify if soulshots are charged.
|
|
|
+ final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS);
|
|
|
+ // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
|
|
+ final int timeAtk = calculateTimeBetweenAttacks(target, weaponItem);
|
|
|
+ // the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
|
|
|
+ final int timeToHit = timeAtk / 2;
|
|
|
+ _attackEndTime = System.currentTimeMillis() + timeAtk;
|
|
|
+ final int ssGrade = (weaponItem != null) ? weaponItem.getItemGradeSPlus().getId() : 0;
|
|
|
+ // Create a Server->Client packet Attack
|
|
|
+ Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
|
|
|
+
|
|
|
+ // 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
|
|
|
+ int reuse = calculateReuseTime(target, weaponItem);
|
|
|
+
|
|
|
+ boolean hitted = false;
|
|
|
+ switch (getAttackType())
|
|
|
{
|
|
|
- if (!isPlayer())
|
|
|
+ case BOW:
|
|
|
+ {
|
|
|
+ hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case CROSSBOW:
|
|
|
+ {
|
|
|
+ hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case POLE:
|
|
|
+ {
|
|
|
+ hitted = doAttackHitByPole(attack, target, timeToHit);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ case FIST:
|
|
|
+ {
|
|
|
+ if (!isPlayer())
|
|
|
+ {
|
|
|
+ hitted = doAttackHitSimple(attack, target, timeToHit);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case DUAL:
|
|
|
+ case DUALFIST:
|
|
|
+ case DUALDAGGER:
|
|
|
+ {
|
|
|
+ hitted = doAttackHitByDual(attack, target, timeToHit);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ default:
|
|
|
{
|
|
|
hitted = doAttackHitSimple(attack, target, timeToHit);
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
- case DUAL:
|
|
|
- case DUALFIST:
|
|
|
- case DUALDAGGER:
|
|
|
- {
|
|
|
- hitted = doAttackHitByDual(attack, target, timeToHit);
|
|
|
- break;
|
|
|
- }
|
|
|
- default:
|
|
|
+
|
|
|
+ // Flag the attacker if it's a L2PcInstance outside a PvP area
|
|
|
+ final L2PcInstance player = getActingPlayer();
|
|
|
+ if (player != null)
|
|
|
{
|
|
|
- hitted = doAttackHitSimple(attack, target, timeToHit);
|
|
|
- break;
|
|
|
+ AttackStanceTaskManager.getInstance().addAttackStanceTask(player);
|
|
|
+ if (player.getSummon() != target)
|
|
|
+ {
|
|
|
+ player.updatePvPStatus(target);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
-
|
|
|
- // 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)
|
|
|
+ // Check if hit isn't missed
|
|
|
+ if (!hitted)
|
|
|
{
|
|
|
- player.updatePvPStatus(target);
|
|
|
+ abortAttack(); // Abort the attack of the L2Character and send Server->Client ActionFailed packet
|
|
|
}
|
|
|
- }
|
|
|
- // 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)
|
|
|
+ else
|
|
|
{
|
|
|
- if (player.isCursedWeaponEquipped())
|
|
|
+ // If we didn't miss the hit, discharge the shoulshots, if any
|
|
|
+ setChargedShot(ShotType.SOULSHOTS, false);
|
|
|
+
|
|
|
+ if (player != null)
|
|
|
{
|
|
|
- // If hit by a cursed weapon, CP is reduced to 0
|
|
|
- if (!target.isInvul())
|
|
|
+ if (player.isCursedWeaponEquipped())
|
|
|
{
|
|
|
- target.setCurrentCp(0);
|
|
|
+ // If hit by a cursed weapon, CP is reduced to 0
|
|
|
+ if (!target.isInvul())
|
|
|
+ {
|
|
|
+ target.setCurrentCp(0);
|
|
|
+ }
|
|
|
}
|
|
|
- }
|
|
|
- else if (player.isHero())
|
|
|
- {
|
|
|
- // If a cursed weapon is hit by a Hero, CP is reduced to 0
|
|
|
- if (target.isPlayer() && target.getActingPlayer().isCursedWeaponEquipped())
|
|
|
+ else if (player.isHero())
|
|
|
{
|
|
|
- target.setCurrentCp(0);
|
|
|
+ // If a cursed weapon is hit by a Hero, CP is reduced to 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
|
|
|
+ ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
|
|
|
}
|
|
|
-
|
|
|
- // 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())
|
|
|
+ finally
|
|
|
{
|
|
|
- broadcastPacket(attack);
|
|
|
+ _attackLock.unlock();
|
|
|
}
|
|
|
-
|
|
|
- // Notify AI with EVT_READY_TO_ACT
|
|
|
- ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
|
|
|
}
|
|
|
|
|
|
/**
|