Proper attack hit delay.
Contributed by Liamxroy.
This commit is contained in:
parent
1a1bdae5ad
commit
c22fd24b18
@ -204,8 +204,8 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
double dmg2 = 0;
|
||||
|
||||
// ATTACK speed in milliseconds
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1, null);
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2, null);
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1.getPAtkSpd());
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2.getPAtkSpd());
|
||||
// number of ATTACK per 100 seconds
|
||||
sAtk1 = 100000 / sAtk1;
|
||||
sAtk2 = 100000 / sAtk2;
|
||||
@ -237,7 +237,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss1)
|
||||
{
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, 0, _shld1, _crit1, false);
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, _shld1, _crit1, false);
|
||||
dmg1 += _dmg1;
|
||||
npc1.abortAttack();
|
||||
}
|
||||
@ -270,7 +270,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss2)
|
||||
{
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, 0, _shld2, _crit2, false);
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, _shld2, _crit2, false);
|
||||
dmg2 += _dmg2;
|
||||
npc2.abortAttack();
|
||||
}
|
||||
|
@ -981,7 +981,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
|
||||
// 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();
|
||||
|
||||
// Check if attacker's weapon can attack
|
||||
if (weaponItem != null)
|
||||
@ -1121,7 +1120,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
}
|
||||
|
||||
final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS) || isChargedShot(ShotType.BLESSED_SOULSHOTS); // Verify if soulshots are charged.
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(this, weaponType); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(getPAtkSpd()); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeToHit = timeAtk / 2; // the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
|
||||
final int ssGrade = (weaponItem != null) ? weaponItem.getItemGrade().ordinal() : 0;
|
||||
final Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
|
||||
@ -1264,7 +1263,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
|
||||
// Bows Ranged Damage Formula (Damage gradually decreases when 60% or lower than full hit range, and increases when 60% or higher).
|
||||
// full hit range is 500 which is the base bow range, and the 60% of this is 800.
|
||||
@ -1337,7 +1336,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 1
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 /= 2;
|
||||
}
|
||||
|
||||
@ -1351,7 +1350,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit2 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 2
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 /= 2;
|
||||
}
|
||||
|
||||
@ -1382,7 +1381,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1402,7 +1401,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1452,7 +1451,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
// Create a new hit task with Medium priority
|
||||
@ -1477,7 +1476,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
ThreadPoolManager.schedule(new HitTask(this, surroundTarget, damage, crit, miss, attack.hasSoulshot(), shld), sAtk);
|
||||
|
@ -404,14 +404,13 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate how many milliseconds takes to make an attack.
|
||||
* @param rate
|
||||
* @return the delay between each attack with a minimum of 50 milliseconds (max. 10000 attack speed)
|
||||
* @param attackSpeed the attack speed of the Creature.
|
||||
* @return {@code 500000 / attackSpeed}.
|
||||
*/
|
||||
public static int calcPAtkSpd(double rate)
|
||||
public static int calculateTimeBetweenAttacks(int attackSpeed)
|
||||
{
|
||||
// Measured Nov 2015 by Nik. Formula: atk.spd/500 = hits per second.
|
||||
return (int) Math.max(50, (500000 / rate));
|
||||
return Math.max(50, (500000 / attackSpeed));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1383,13 +1382,12 @@ public final class Formulas
|
||||
* Calculated damage caused by ATTACK of attacker on target.
|
||||
* @param attacker player or NPC that makes ATTACK
|
||||
* @param target player or NPC, target of ATTACK
|
||||
* @param power
|
||||
* @param shld
|
||||
* @param crit if the ATTACK have critical success
|
||||
* @param ss if weapon item was charged by soulshot
|
||||
* @return
|
||||
*/
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, double power, byte shld, boolean crit, boolean ss)
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, byte shld, boolean crit, boolean ss)
|
||||
{
|
||||
final double distance = attacker.calculateDistance(target, true, false);
|
||||
|
||||
@ -1504,33 +1502,61 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param attackType the type of attack. Different attack types have different time between attacks.
|
||||
* @return the Attack Speed of the L2Character (delay (in milliseconds) before next attack).
|
||||
* @param totalAttackTime the time needed to make a full attack.
|
||||
* @param attackType the weapon type used for attack.
|
||||
* @param twoHanded if the weapon is two handed.
|
||||
* @param secondHit calculates the second hit for dual attacks.
|
||||
* @return the time required from the start of the attack until you hit the target.
|
||||
*/
|
||||
public static int calculateTimeBetweenAttacks(L2Character activeChar, WeaponType attackType)
|
||||
public static int calculateTimeToHit(int totalAttackTime, WeaponType attackType, boolean twoHanded, boolean secondHit)
|
||||
{
|
||||
// Gracia Final Retail confirmed:
|
||||
// Time to damage (1 hand, 1 hit): TotalBasicAttackTime * 0.644
|
||||
// Time to damage (2 hand, 1 hit): TotalBasicAttackTime * 0.735
|
||||
// Time to damage (2 hand, 2 hit): TotalBasicAttackTime * 0.2726 and TotalBasicAttackTime * 0.6
|
||||
// Time to damage (bow/xbow): TotalBasicAttackTime * 0.978
|
||||
|
||||
// Measured July 2016 by Nik.
|
||||
// Due to retail packet delay, we are unable to gather too accurate results. Therefore the below formulas are based on original Gracia Final values.
|
||||
// Any original values that appear higher than tested have been replaced with the tested values, because even with packet delay its obvious they are wrong.
|
||||
// All other original values are compared with the test results and differences are considered to be too insignificant and mostly caused due to packet delay.
|
||||
switch (attackType)
|
||||
{
|
||||
case BOW:
|
||||
{
|
||||
return (1500 * 345) / activeChar.getPAtkSpd();
|
||||
}
|
||||
case CROSSBOW:
|
||||
case TWOHANDCROSSBOW:
|
||||
{
|
||||
return (1200 * 345) / activeChar.getPAtkSpd();
|
||||
return (int) (totalAttackTime * 0.95);
|
||||
}
|
||||
case DAGGER:
|
||||
case DUALBLUNT:
|
||||
case DUALDAGGER:
|
||||
case DUAL:
|
||||
case DUALFIST:
|
||||
{
|
||||
// atkSpd /= 1.15;
|
||||
break;
|
||||
if (secondHit)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.6);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.2726);
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (twoHanded)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.735);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.644);
|
||||
}
|
||||
}
|
||||
|
||||
return calcPAtkSpd(activeChar.getPAtkSpd());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param weapon
|
||||
* @return {@code (500_000 millis + 333 * WeaponItemReuseDelay) / PAttackSpeed}
|
||||
*/
|
||||
public static int calculateReuseTime(L2Character activeChar, L2Weapon weapon)
|
||||
{
|
||||
if (weapon == null)
|
||||
@ -1549,9 +1575,9 @@ public final class Formulas
|
||||
}
|
||||
|
||||
reuse *= activeChar.getStat().getWeaponReuseModifier();
|
||||
final double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
|
||||
return (int) ((reuse * 345) / atkSpd); // ((reuse * 312) / atkSpd)) for non ranged?
|
||||
return (int) ((500000 + (333 * reuse)) / atkSpd);
|
||||
}
|
||||
|
||||
public static double calculatePvpPveBonus(L2Character attacker, L2Character target, Skill skill, boolean crit)
|
||||
|
@ -204,8 +204,8 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
double dmg2 = 0;
|
||||
|
||||
// ATTACK speed in milliseconds
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1, null);
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2, null);
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1.getPAtkSpd());
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2.getPAtkSpd());
|
||||
// number of ATTACK per 100 seconds
|
||||
sAtk1 = 100000 / sAtk1;
|
||||
sAtk2 = 100000 / sAtk2;
|
||||
@ -237,7 +237,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss1)
|
||||
{
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, 0, _shld1, _crit1, false);
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, _shld1, _crit1, false);
|
||||
dmg1 += _dmg1;
|
||||
npc1.abortAttack();
|
||||
}
|
||||
@ -270,7 +270,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss2)
|
||||
{
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, 0, _shld2, _crit2, false);
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, _shld2, _crit2, false);
|
||||
dmg2 += _dmg2;
|
||||
npc2.abortAttack();
|
||||
}
|
||||
|
@ -985,7 +985,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
|
||||
// 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();
|
||||
|
||||
// Check if attacker's weapon can attack
|
||||
if (weaponItem != null)
|
||||
@ -1125,7 +1124,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
}
|
||||
|
||||
final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS) || isChargedShot(ShotType.BLESSED_SOULSHOTS); // Verify if soulshots are charged.
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(this, weaponType); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(getPAtkSpd()); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeToHit = timeAtk / 2; // the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
|
||||
final int ssGrade = (weaponItem != null) ? weaponItem.getItemGrade().ordinal() : 0;
|
||||
final Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
|
||||
@ -1268,7 +1267,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
|
||||
// Bows Ranged Damage Formula (Damage gradually decreases when 60% or lower than full hit range, and increases when 60% or higher).
|
||||
// full hit range is 500 which is the base bow range, and the 60% of this is 800.
|
||||
@ -1341,7 +1340,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 1
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 /= 2;
|
||||
}
|
||||
|
||||
@ -1355,7 +1354,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit2 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 2
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 /= 2;
|
||||
}
|
||||
|
||||
@ -1386,7 +1385,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1406,7 +1405,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1456,7 +1455,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
// Create a new hit task with Medium priority
|
||||
@ -1481,7 +1480,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
ThreadPoolManager.schedule(new HitTask(this, surroundTarget, damage, crit, miss, attack.hasSoulshot(), shld), sAtk);
|
||||
|
@ -404,14 +404,13 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate how many milliseconds takes to make an attack.
|
||||
* @param rate
|
||||
* @return the delay between each attack with a minimum of 50 milliseconds (max. 10000 attack speed)
|
||||
* @param attackSpeed the attack speed of the Creature.
|
||||
* @return {@code 500000 / attackSpeed}.
|
||||
*/
|
||||
public static int calcPAtkSpd(double rate)
|
||||
public static int calculateTimeBetweenAttacks(int attackSpeed)
|
||||
{
|
||||
// Measured Nov 2015 by Nik. Formula: atk.spd/500 = hits per second.
|
||||
return (int) Math.max(50, (500000 / rate));
|
||||
return Math.max(50, (500000 / attackSpeed));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1383,13 +1382,12 @@ public final class Formulas
|
||||
* Calculated damage caused by ATTACK of attacker on target.
|
||||
* @param attacker player or NPC that makes ATTACK
|
||||
* @param target player or NPC, target of ATTACK
|
||||
* @param power
|
||||
* @param shld
|
||||
* @param crit if the ATTACK have critical success
|
||||
* @param ss if weapon item was charged by soulshot
|
||||
* @return
|
||||
*/
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, double power, byte shld, boolean crit, boolean ss)
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, byte shld, boolean crit, boolean ss)
|
||||
{
|
||||
final double distance = attacker.calculateDistance(target, true, false);
|
||||
|
||||
@ -1504,33 +1502,61 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param attackType the type of attack. Different attack types have different time between attacks.
|
||||
* @return the Attack Speed of the L2Character (delay (in milliseconds) before next attack).
|
||||
* @param totalAttackTime the time needed to make a full attack.
|
||||
* @param attackType the weapon type used for attack.
|
||||
* @param twoHanded if the weapon is two handed.
|
||||
* @param secondHit calculates the second hit for dual attacks.
|
||||
* @return the time required from the start of the attack until you hit the target.
|
||||
*/
|
||||
public static int calculateTimeBetweenAttacks(L2Character activeChar, WeaponType attackType)
|
||||
public static int calculateTimeToHit(int totalAttackTime, WeaponType attackType, boolean twoHanded, boolean secondHit)
|
||||
{
|
||||
// Gracia Final Retail confirmed:
|
||||
// Time to damage (1 hand, 1 hit): TotalBasicAttackTime * 0.644
|
||||
// Time to damage (2 hand, 1 hit): TotalBasicAttackTime * 0.735
|
||||
// Time to damage (2 hand, 2 hit): TotalBasicAttackTime * 0.2726 and TotalBasicAttackTime * 0.6
|
||||
// Time to damage (bow/xbow): TotalBasicAttackTime * 0.978
|
||||
|
||||
// Measured July 2016 by Nik.
|
||||
// Due to retail packet delay, we are unable to gather too accurate results. Therefore the below formulas are based on original Gracia Final values.
|
||||
// Any original values that appear higher than tested have been replaced with the tested values, because even with packet delay its obvious they are wrong.
|
||||
// All other original values are compared with the test results and differences are considered to be too insignificant and mostly caused due to packet delay.
|
||||
switch (attackType)
|
||||
{
|
||||
case BOW:
|
||||
{
|
||||
return (1500 * 345) / activeChar.getPAtkSpd();
|
||||
}
|
||||
case CROSSBOW:
|
||||
case TWOHANDCROSSBOW:
|
||||
{
|
||||
return (1200 * 345) / activeChar.getPAtkSpd();
|
||||
return (int) (totalAttackTime * 0.95);
|
||||
}
|
||||
case DAGGER:
|
||||
case DUALBLUNT:
|
||||
case DUALDAGGER:
|
||||
case DUAL:
|
||||
case DUALFIST:
|
||||
{
|
||||
// atkSpd /= 1.15;
|
||||
break;
|
||||
if (secondHit)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.6);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.2726);
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (twoHanded)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.735);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.644);
|
||||
}
|
||||
}
|
||||
|
||||
return calcPAtkSpd(activeChar.getPAtkSpd());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param weapon
|
||||
* @return {@code (500_000 millis + 333 * WeaponItemReuseDelay) / PAttackSpeed}
|
||||
*/
|
||||
public static int calculateReuseTime(L2Character activeChar, L2Weapon weapon)
|
||||
{
|
||||
if (weapon == null)
|
||||
@ -1549,9 +1575,9 @@ public final class Formulas
|
||||
}
|
||||
|
||||
reuse *= activeChar.getStat().getWeaponReuseModifier();
|
||||
final double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
|
||||
return (int) ((reuse * 345) / atkSpd); // ((reuse * 312) / atkSpd)) for non ranged?
|
||||
return (int) ((500000 + (333 * reuse)) / atkSpd);
|
||||
}
|
||||
|
||||
public static double calculatePvpPveBonus(L2Character attacker, L2Character target, Skill skill, boolean crit)
|
||||
|
@ -204,8 +204,8 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
double dmg2 = 0;
|
||||
|
||||
// ATTACK speed in milliseconds
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1, null);
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2, null);
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1.getPAtkSpd());
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2.getPAtkSpd());
|
||||
// number of ATTACK per 100 seconds
|
||||
sAtk1 = 100000 / sAtk1;
|
||||
sAtk2 = 100000 / sAtk2;
|
||||
@ -237,7 +237,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss1)
|
||||
{
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, 0, _shld1, _crit1, false);
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, _shld1, _crit1, false);
|
||||
dmg1 += _dmg1;
|
||||
npc1.abortAttack();
|
||||
}
|
||||
@ -270,7 +270,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss2)
|
||||
{
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, 0, _shld2, _crit2, false);
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, _shld2, _crit2, false);
|
||||
dmg2 += _dmg2;
|
||||
npc2.abortAttack();
|
||||
}
|
||||
|
@ -985,7 +985,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
|
||||
// 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();
|
||||
|
||||
// Check if attacker's weapon can attack
|
||||
if (weaponItem != null)
|
||||
@ -1125,7 +1124,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
}
|
||||
|
||||
final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS) || isChargedShot(ShotType.BLESSED_SOULSHOTS); // Verify if soulshots are charged.
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(this, weaponType); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(getPAtkSpd()); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeToHit = timeAtk / 2; // the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
|
||||
final int ssGrade = (weaponItem != null) ? weaponItem.getItemGrade().ordinal() : 0;
|
||||
final Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
|
||||
@ -1268,7 +1267,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
|
||||
// Bows Ranged Damage Formula (Damage gradually decreases when 60% or lower than full hit range, and increases when 60% or higher).
|
||||
// full hit range is 500 which is the base bow range, and the 60% of this is 800.
|
||||
@ -1341,7 +1340,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 1
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 /= 2;
|
||||
}
|
||||
|
||||
@ -1355,7 +1354,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit2 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 2
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 /= 2;
|
||||
}
|
||||
|
||||
@ -1386,7 +1385,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1406,7 +1405,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1456,7 +1455,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
// Create a new hit task with Medium priority
|
||||
@ -1481,7 +1480,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
ThreadPoolManager.schedule(new HitTask(this, surroundTarget, damage, crit, miss, attack.hasSoulshot(), shld), sAtk);
|
||||
|
@ -404,14 +404,13 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate how many milliseconds takes to make an attack.
|
||||
* @param rate
|
||||
* @return the delay between each attack with a minimum of 50 milliseconds (max. 10000 attack speed)
|
||||
* @param attackSpeed the attack speed of the Creature.
|
||||
* @return {@code 500000 / attackSpeed}.
|
||||
*/
|
||||
public static int calcPAtkSpd(double rate)
|
||||
public static int calculateTimeBetweenAttacks(int attackSpeed)
|
||||
{
|
||||
// Measured Nov 2015 by Nik. Formula: atk.spd/500 = hits per second.
|
||||
return (int) Math.max(50, (500000 / rate));
|
||||
return Math.max(50, (500000 / attackSpeed));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1383,13 +1382,12 @@ public final class Formulas
|
||||
* Calculated damage caused by ATTACK of attacker on target.
|
||||
* @param attacker player or NPC that makes ATTACK
|
||||
* @param target player or NPC, target of ATTACK
|
||||
* @param power
|
||||
* @param shld
|
||||
* @param crit if the ATTACK have critical success
|
||||
* @param ss if weapon item was charged by soulshot
|
||||
* @return
|
||||
*/
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, double power, byte shld, boolean crit, boolean ss)
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, byte shld, boolean crit, boolean ss)
|
||||
{
|
||||
final double distance = attacker.calculateDistance(target, true, false);
|
||||
|
||||
@ -1504,33 +1502,61 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param attackType the type of attack. Different attack types have different time between attacks.
|
||||
* @return the Attack Speed of the L2Character (delay (in milliseconds) before next attack).
|
||||
* @param totalAttackTime the time needed to make a full attack.
|
||||
* @param attackType the weapon type used for attack.
|
||||
* @param twoHanded if the weapon is two handed.
|
||||
* @param secondHit calculates the second hit for dual attacks.
|
||||
* @return the time required from the start of the attack until you hit the target.
|
||||
*/
|
||||
public static int calculateTimeBetweenAttacks(L2Character activeChar, WeaponType attackType)
|
||||
public static int calculateTimeToHit(int totalAttackTime, WeaponType attackType, boolean twoHanded, boolean secondHit)
|
||||
{
|
||||
// Gracia Final Retail confirmed:
|
||||
// Time to damage (1 hand, 1 hit): TotalBasicAttackTime * 0.644
|
||||
// Time to damage (2 hand, 1 hit): TotalBasicAttackTime * 0.735
|
||||
// Time to damage (2 hand, 2 hit): TotalBasicAttackTime * 0.2726 and TotalBasicAttackTime * 0.6
|
||||
// Time to damage (bow/xbow): TotalBasicAttackTime * 0.978
|
||||
|
||||
// Measured July 2016 by Nik.
|
||||
// Due to retail packet delay, we are unable to gather too accurate results. Therefore the below formulas are based on original Gracia Final values.
|
||||
// Any original values that appear higher than tested have been replaced with the tested values, because even with packet delay its obvious they are wrong.
|
||||
// All other original values are compared with the test results and differences are considered to be too insignificant and mostly caused due to packet delay.
|
||||
switch (attackType)
|
||||
{
|
||||
case BOW:
|
||||
{
|
||||
return (1500 * 345) / activeChar.getPAtkSpd();
|
||||
}
|
||||
case CROSSBOW:
|
||||
case TWOHANDCROSSBOW:
|
||||
{
|
||||
return (1200 * 345) / activeChar.getPAtkSpd();
|
||||
return (int) (totalAttackTime * 0.95);
|
||||
}
|
||||
case DAGGER:
|
||||
case DUALBLUNT:
|
||||
case DUALDAGGER:
|
||||
case DUAL:
|
||||
case DUALFIST:
|
||||
{
|
||||
// atkSpd /= 1.15;
|
||||
break;
|
||||
if (secondHit)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.6);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.2726);
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (twoHanded)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.735);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.644);
|
||||
}
|
||||
}
|
||||
|
||||
return calcPAtkSpd(activeChar.getPAtkSpd());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param weapon
|
||||
* @return {@code (500_000 millis + 333 * WeaponItemReuseDelay) / PAttackSpeed}
|
||||
*/
|
||||
public static int calculateReuseTime(L2Character activeChar, L2Weapon weapon)
|
||||
{
|
||||
if (weapon == null)
|
||||
@ -1549,9 +1575,9 @@ public final class Formulas
|
||||
}
|
||||
|
||||
reuse *= activeChar.getStat().getWeaponReuseModifier();
|
||||
final double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
|
||||
return (int) ((reuse * 345) / atkSpd); // ((reuse * 312) / atkSpd)) for non ranged?
|
||||
return (int) ((500000 + (333 * reuse)) / atkSpd);
|
||||
}
|
||||
|
||||
public static double calculatePvpPveBonus(L2Character attacker, L2Character target, Skill skill, boolean crit)
|
||||
|
@ -204,8 +204,8 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
double dmg2 = 0;
|
||||
|
||||
// ATTACK speed in milliseconds
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1, null);
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2, null);
|
||||
int sAtk1 = Formulas.calculateTimeBetweenAttacks(npc1.getPAtkSpd());
|
||||
int sAtk2 = Formulas.calculateTimeBetweenAttacks(npc2.getPAtkSpd());
|
||||
// number of ATTACK per 100 seconds
|
||||
sAtk1 = 100000 / sAtk1;
|
||||
sAtk2 = 100000 / sAtk2;
|
||||
@ -237,7 +237,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss1)
|
||||
{
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, 0, _shld1, _crit1, false);
|
||||
final double _dmg1 = Formulas.calcAutoAttackDamage(npc1, npc2, _shld1, _crit1, false);
|
||||
dmg1 += _dmg1;
|
||||
npc1.abortAttack();
|
||||
}
|
||||
@ -270,7 +270,7 @@ public class AdminFightCalculator implements IAdminCommandHandler
|
||||
|
||||
if (!_miss2)
|
||||
{
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, 0, _shld2, _crit2, false);
|
||||
final double _dmg2 = Formulas.calcAutoAttackDamage(npc2, npc1, _shld2, _crit2, false);
|
||||
dmg2 += _dmg2;
|
||||
npc2.abortAttack();
|
||||
}
|
||||
|
@ -985,7 +985,6 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
|
||||
// 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();
|
||||
|
||||
// Check if attacker's weapon can attack
|
||||
if (weaponItem != null)
|
||||
@ -1125,7 +1124,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
}
|
||||
|
||||
final boolean wasSSCharged = isChargedShot(ShotType.SOULSHOTS) || isChargedShot(ShotType.BLESSED_SOULSHOTS); // Verify if soulshots are charged.
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(this, weaponType); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeAtk = Formulas.calculateTimeBetweenAttacks(getPAtkSpd()); // Get the Attack Speed of the L2Character (delay (in milliseconds) before next attack)
|
||||
final int timeToHit = timeAtk / 2; // the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow case
|
||||
final int ssGrade = (weaponItem != null) ? weaponItem.getItemGrade().ordinal() : 0;
|
||||
final Attack attack = new Attack(this, target, wasSSCharged, ssGrade);
|
||||
@ -1268,7 +1267,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
|
||||
// Bows Ranged Damage Formula (Damage gradually decreases when 60% or lower than full hit range, and increases when 60% or higher).
|
||||
// full hit range is 500 which is the base bow range, and the 60% of this is 800.
|
||||
@ -1341,7 +1340,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 1
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 /= 2;
|
||||
}
|
||||
|
||||
@ -1355,7 +1354,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit2 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages of hit 2
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 = (int) Formulas.calcAutoAttackDamage(this, target, shld2, crit2, attack.hasSoulshot());
|
||||
damage2 /= 2;
|
||||
}
|
||||
|
||||
@ -1386,7 +1385,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1406,7 +1405,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
damage /= 2;
|
||||
}
|
||||
|
||||
@ -1456,7 +1455,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
crit1 = Formulas.calcCrit(getStat().getCriticalHit(), this, target, null);
|
||||
|
||||
// Calculate physical damages
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, 0, shld1, crit1, attack.hasSoulshot());
|
||||
damage1 = (int) Formulas.calcAutoAttackDamage(this, target, shld1, crit1, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
// Create a new hit task with Medium priority
|
||||
@ -1481,7 +1480,7 @@ public abstract class L2Character extends L2Object implements ISkillsHolder, IDe
|
||||
{
|
||||
shld = Formulas.calcShldUse(this, surroundTarget);
|
||||
crit = Formulas.calcCrit(getStat().getCriticalHit(), this, surroundTarget, null);
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, 0, shld, crit, attack.hasSoulshot());
|
||||
damage = (int) Formulas.calcAutoAttackDamage(this, surroundTarget, shld, crit, attack.hasSoulshot());
|
||||
}
|
||||
|
||||
ThreadPoolManager.schedule(new HitTask(this, surroundTarget, damage, crit, miss, attack.hasSoulshot(), shld), sAtk);
|
||||
|
@ -404,14 +404,13 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate how many milliseconds takes to make an attack.
|
||||
* @param rate
|
||||
* @return the delay between each attack with a minimum of 50 milliseconds (max. 10000 attack speed)
|
||||
* @param attackSpeed the attack speed of the Creature.
|
||||
* @return {@code 500000 / attackSpeed}.
|
||||
*/
|
||||
public static int calcPAtkSpd(double rate)
|
||||
public static int calculateTimeBetweenAttacks(int attackSpeed)
|
||||
{
|
||||
// Measured Nov 2015 by Nik. Formula: atk.spd/500 = hits per second.
|
||||
return (int) Math.max(50, (500000 / rate));
|
||||
return Math.max(50, (500000 / attackSpeed));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1383,13 +1382,12 @@ public final class Formulas
|
||||
* Calculated damage caused by ATTACK of attacker on target.
|
||||
* @param attacker player or NPC that makes ATTACK
|
||||
* @param target player or NPC, target of ATTACK
|
||||
* @param power
|
||||
* @param shld
|
||||
* @param crit if the ATTACK have critical success
|
||||
* @param ss if weapon item was charged by soulshot
|
||||
* @return
|
||||
*/
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, double power, byte shld, boolean crit, boolean ss)
|
||||
public static double calcAutoAttackDamage(L2Character attacker, L2Character target, byte shld, boolean crit, boolean ss)
|
||||
{
|
||||
final double distance = attacker.calculateDistance(target, true, false);
|
||||
|
||||
@ -1504,33 +1502,61 @@ public final class Formulas
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param attackType the type of attack. Different attack types have different time between attacks.
|
||||
* @return the Attack Speed of the L2Character (delay (in milliseconds) before next attack).
|
||||
* @param totalAttackTime the time needed to make a full attack.
|
||||
* @param attackType the weapon type used for attack.
|
||||
* @param twoHanded if the weapon is two handed.
|
||||
* @param secondHit calculates the second hit for dual attacks.
|
||||
* @return the time required from the start of the attack until you hit the target.
|
||||
*/
|
||||
public static int calculateTimeBetweenAttacks(L2Character activeChar, WeaponType attackType)
|
||||
public static int calculateTimeToHit(int totalAttackTime, WeaponType attackType, boolean twoHanded, boolean secondHit)
|
||||
{
|
||||
// Gracia Final Retail confirmed:
|
||||
// Time to damage (1 hand, 1 hit): TotalBasicAttackTime * 0.644
|
||||
// Time to damage (2 hand, 1 hit): TotalBasicAttackTime * 0.735
|
||||
// Time to damage (2 hand, 2 hit): TotalBasicAttackTime * 0.2726 and TotalBasicAttackTime * 0.6
|
||||
// Time to damage (bow/xbow): TotalBasicAttackTime * 0.978
|
||||
|
||||
// Measured July 2016 by Nik.
|
||||
// Due to retail packet delay, we are unable to gather too accurate results. Therefore the below formulas are based on original Gracia Final values.
|
||||
// Any original values that appear higher than tested have been replaced with the tested values, because even with packet delay its obvious they are wrong.
|
||||
// All other original values are compared with the test results and differences are considered to be too insignificant and mostly caused due to packet delay.
|
||||
switch (attackType)
|
||||
{
|
||||
case BOW:
|
||||
{
|
||||
return (1500 * 345) / activeChar.getPAtkSpd();
|
||||
}
|
||||
case CROSSBOW:
|
||||
case TWOHANDCROSSBOW:
|
||||
{
|
||||
return (1200 * 345) / activeChar.getPAtkSpd();
|
||||
return (int) (totalAttackTime * 0.95);
|
||||
}
|
||||
case DAGGER:
|
||||
case DUALBLUNT:
|
||||
case DUALDAGGER:
|
||||
case DUAL:
|
||||
case DUALFIST:
|
||||
{
|
||||
// atkSpd /= 1.15;
|
||||
break;
|
||||
if (secondHit)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.6);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.2726);
|
||||
}
|
||||
default:
|
||||
{
|
||||
if (twoHanded)
|
||||
{
|
||||
return (int) (totalAttackTime * 0.735);
|
||||
}
|
||||
|
||||
return (int) (totalAttackTime * 0.644);
|
||||
}
|
||||
}
|
||||
|
||||
return calcPAtkSpd(activeChar.getPAtkSpd());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param activeChar
|
||||
* @param weapon
|
||||
* @return {@code (500_000 millis + 333 * WeaponItemReuseDelay) / PAttackSpeed}
|
||||
*/
|
||||
public static int calculateReuseTime(L2Character activeChar, L2Weapon weapon)
|
||||
{
|
||||
if (weapon == null)
|
||||
@ -1549,9 +1575,9 @@ public final class Formulas
|
||||
}
|
||||
|
||||
reuse *= activeChar.getStat().getWeaponReuseModifier();
|
||||
final double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
double atkSpd = activeChar.getStat().getPAtkSpd();
|
||||
|
||||
return (int) ((reuse * 345) / atkSpd); // ((reuse * 312) / atkSpd)) for non ranged?
|
||||
return (int) ((500000 + (333 * reuse)) / atkSpd);
|
||||
}
|
||||
|
||||
public static double calculatePvpPveBonus(L2Character attacker, L2Character target, Skill skill, boolean crit)
|
||||
|
Loading…
Reference in New Issue
Block a user