Partial sync with L2jServer HighFive Nov 6th 2015.

This commit is contained in:
MobiusDev
2015-11-07 01:14:51 +00:00
parent 4964c24da9
commit 57dcdb065d
107 changed files with 1331 additions and 266 deletions

View File

@ -267,7 +267,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
/** Represents the time where the attack should end, in nanoseconds. */
private volatile long _attackEndTime;
private int _disableBowAttackEndTime;
private int _disableCrossBowAttackEndTime;
private int _castInterruptTime;
@ -812,6 +811,78 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
teleToLocation(MapRegionManager.getInstance().getTeleToLocation(this, teleportWhere), true);
}
private boolean canUseRangeWeapon()
{
if (isTransformed())
{
return true;
}
L2Weapon weaponItem = getActiveWeaponItem();
if ((weaponItem == null) || !weaponItem.isRange())
{
return false;
}
// Check for arrows and MP
if (isPlayer())
{
// 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(weaponItem.isBow() ? SystemMessageId.YOU_HAVE_RUN_OUT_OF_ARROWS : SystemMessageId.NOT_ENOUGH_BOLTS);
return false;
}
// Verify if the bow can be use
if (_disableBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
{
// 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 false;
}
// 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 false;
}
}
else if (isNpc())
{
if (_disableBowAttackEndTime > GameTimeController.getInstance().getGameTicks())
{
return false;
}
}
return true;
}
/**
* Launch a physical attack against a target (Simple, Bow, Pole or Dual).<br>
* <B><U>Actions</U>:</B>
@ -941,186 +1012,42 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
return;
}
// BOW and CROSSBOW checks
// Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
L2Weapon weaponItem = getActiveWeaponItem();
if ((weaponItem != null) && !isTransformed())
{
if (weaponItem.getItemType() == WeaponType.BOW)
{
// Check for arrows and MP
if (isPlayer())
{
// 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.YOU_HAVE_RUN_OUT_OF_ARROWS);
return;
}
// Verify if the bow can be use
if (_disableBowAttackEndTime <= GameTimeController.getInstance().getGameTicks())
{
// 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;
}
}
}
if (weaponItem.getItemType() == WeaponType.CROSSBOW)
{
// Check for bolts
if (isPlayer())
{
// 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())
{
// 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;
}
}
else if (isNpc())
{
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
L2Weapon weaponItem = getActiveWeaponItem();
final int timeAtk = calculateTimeBetweenAttacks();
final int timeToHit = timeAtk / 2;
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeAtk, TimeUnit.MILLISECONDS);
final int ssGrade = (weaponItem != null) ? weaponItem.getItemGrade().ordinal() : 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)));
Attack attack = new Attack(this, target, isChargedShot(ShotType.SOULSHOTS), (weaponItem != null) ? weaponItem.getItemGrade().ordinal() : 0);
setHeading(Util.calculateHeadingFrom(this, target));
// Get the Attack Reuse Delay of the L2Weapon
int reuse = calculateReuseTime(target, weaponItem);
int reuse = calculateReuseTime(weaponItem);
boolean hitted = false;
switch (getAttackType())
{
case BOW:
{
if (!canUseRangeWeapon())
{
return;
}
_attackEndTime = System.nanoTime() + TimeUnit.NANOSECONDS.convert(timeToHit + (reuse / 2), TimeUnit.MILLISECONDS);
hitted = doAttackHitByBow(attack, target, timeAtk, reuse);
break;
}
case CROSSBOW:
{
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;
}
@ -1136,11 +1063,13 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
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);
break;
}
@ -1193,9 +1122,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
}
// Notify AI with EVT_READY_TO_ACT
// Old method - use in case of bow reuse issues occur.
// ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk);
ThreadPoolManager.getInstance().scheduleAi(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT), timeAtk + reuse);
}
finally
{
@ -1334,7 +1261,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
ThreadPoolManager.getInstance().scheduleAi(new HitTask(this, target, damage1, crit1, miss1, attack.hasSoulshot(), shld1), sAtk);
// Calculate and set the disable delay of the bow in function of the Attack Speed
_disableCrossBowAttackEndTime = ((sAtk + reuse) / GameTimeController.MILLIS_IN_TICK) + GameTimeController.getInstance().getGameTicks();
_disableBowAttackEndTime = ((sAtk + reuse) / GameTimeController.MILLIS_IN_TICK) + GameTimeController.getInstance().getGameTicks();
// Add this hit to the Server-Client packet Attack
attack.addHit(target, damage1, miss1, crit1, shld1);
@ -5311,61 +5238,36 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
}
/**
* @param target
* @param weapon
* @return the Attack Speed of the L2Character (delay (in milliseconds) before next attack).
*/
public int calculateTimeBetweenAttacks(L2Character target, L2Weapon weapon)
public int calculateTimeBetweenAttacks()
{
if ((weapon != null) && !isTransformed())
{
switch (weapon.getItemType())
{
case BOW:
return (int) ((1500 * 345) / getPAtkSpd());
case CROSSBOW:
return (int) ((1200 * 345) / getPAtkSpd());
case DAGGER:
// atkSpd /= 1.15;
break;
}
}
return Formulas.calcPAtkSpd(this, target, getPAtkSpd());
return (int) (500000 / getPAtkSpd());
}
public int calculateReuseTime(L2Character target, L2Weapon weapon)
/**
* @param weapon
* @return the Reuse Time of Attack (used for bow delay)
*/
public int calculateReuseTime(final L2Weapon weapon)
{
if (isTransformed())
{
switch (getAttackType())
{
case BOW:
return (int) ((1500 * 333 * getStat().getWeaponReuseModifier(null)) / getStat().getPAtkSpd());
case CROSSBOW:
return (int) ((517500 * getStat().getWeaponReuseModifier(null)) / getStat().getPAtkSpd());
return (int) ((1200 * 333 * getStat().getWeaponReuseModifier(null)) / getStat().getPAtkSpd());
}
}
if ((weapon == null))
if ((weapon == null) || (weapon.getReuseDelay() == 0))
{
return 0;
}
int reuse = weapon.getReuseDelay();
// only bows should continue for now
if (reuse == 0)
{
return 0;
}
reuse *= getStat().getWeaponReuseModifier(null);
double atkSpd = getStat().getPAtkSpd();
switch (weapon.getItemType())
{
case BOW:
case CROSSBOW:
return (int) ((reuse * 345) / atkSpd);
default:
return (int) ((reuse * 312) / atkSpd);
}
return (int) ((weapon.getReuseDelay() * 333) / getPAtkSpd());
}
/**