Proper attack hit delay.

Contributed by Liamxroy.
This commit is contained in:
MobiusDev 2017-09-23 15:25:27 +00:00
parent 1a1bdae5ad
commit c22fd24b18
12 changed files with 240 additions and 140 deletions

View File

@ -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();
}

View File

@ -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);

View File

@ -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)

View File

@ -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();
}

View File

@ -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);

View File

@ -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)

View File

@ -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();
}

View File

@ -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);

View File

@ -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)

View File

@ -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();
}

View File

@ -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);

View File

@ -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)