Package lineage2.gameserver.stats

Source Code of lineage2.gameserver.stats.Formulas$AttackInfo

/*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation, either version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package lineage2.gameserver.stats;

import lineage2.commons.util.Rnd;
import lineage2.gameserver.Config;
import lineage2.gameserver.cache.Msg;
import lineage2.gameserver.model.Creature;
import lineage2.gameserver.model.Player;
import lineage2.gameserver.model.Skill;
import lineage2.gameserver.model.Skill.SkillType;
import lineage2.gameserver.model.base.BaseStats;
import lineage2.gameserver.model.base.Element;
import lineage2.gameserver.model.base.SkillTrait;
import lineage2.gameserver.model.instances.ReflectionBossInstance;
import lineage2.gameserver.model.items.ItemInstance;
import lineage2.gameserver.network.serverpackets.SystemMessage;
import lineage2.gameserver.network.serverpackets.components.SystemMsg;
import lineage2.gameserver.skills.EffectType;
import lineage2.gameserver.skills.effects.EffectTemplate;
import lineage2.gameserver.tables.AttributeDamageResistTable;
import lineage2.gameserver.templates.item.WeaponTemplate;
import lineage2.gameserver.utils.PositionUtils;

/**
* @author Mobius
* @version $Revision: 1.0 $
*/
public class Formulas
{
  /**
   * Method calcHpRegen.
   * @param cha Creature
   * @return double
   */
 
  public static double calcHpRegen(Creature cha)
  {
    double init;
    if (cha.isPlayer())
    {
      init = cha.getPlayer().getTemplate().getBaseHpReg(cha.getLevel());
    }
    else
    {
      init = cha.getTemplate().getBaseHpReg();
    }
    if (cha.isPlayable())
    {
      init *= BaseStats.CON.calcBonus(cha);
      if (cha.isServitor())
      {
        init *= 2;
      }
    }
    return cha.calcStat(Stats.REGENERATE_HP_RATE, init);
  }
 
  /**
   * Method calcMpRegen.
   * @param cha Creature
   * @return double
   */
  public static double calcMpRegen(Creature cha)
  {
    double init;
    if (cha.isPlayer())
    {
      init = cha.getPlayer().getTemplate().getBaseMpReg(cha.getLevel());
    }
    else
    {
      init = cha.getTemplate().getBaseMpReg();
    }
    if (cha.isPlayable())
    {
      init *= BaseStats.MEN.calcBonus(cha);
      if (cha.isServitor())
      {
        init *= 2;
      }
    }
    return cha.calcStat(Stats.REGENERATE_MP_RATE, init);
  }
 
  /**
   * Method calcCpRegen.
   * @param cha Creature
   * @return double
   */
  public static double calcCpRegen(Creature cha)
  {
    double init = 0.0;
    if (cha.isPlayer())
    {
      init = cha.getPlayer().getTemplate().getBaseCpReg(cha.getLevel()) * BaseStats.CON.calcBonus(cha);
    }
    return cha.calcStat(Stats.REGENERATE_CP_RATE, init);
  }
 
  /**
   * @author Mobius
   */
  public static class AttackInfo
  {
    /**
     * Field reflectableDamage.
     */
    public double reflectableDamage = 0;
    /**
     * Field damage.
     */
    public double damage = 0;
    /**
     * Field defence.
     */
    public double defence = 0;
    /**
     * Field crit_static.
     */
    public double crit_static = 0;
    /**
     * Field death_rcpt.
     */
    public double death_rcpt = 0;
    /**
     * Field lethal1.
     */
    public double lethal1 = 0;
    /**
     * Field lethal2.
     */
    public double lethal2 = 0;
    /**
     * Field lethal_dmg.
     */
    public double lethal_dmg = 0;
    /**
     * Field crit.
     */
    public boolean crit = false;
    /**
     * Field shld.
     */
    public boolean shld = false;
    /**
     * Field lethal.
     */
    public boolean lethal = false;
    /**
     * Field miss.
     */
    public boolean miss = false;
  }
 
  /**
   * Method calcPhysDam.
   * @param attacker Creature
   * @param target Creature
   * @param skill Skill
   * @param dual boolean
   * @param blow boolean
   * @param ss boolean
   * @param onCrit boolean
   * @return AttackInfo
   */
  public static AttackInfo calcPhysDam(Creature attacker, Creature target, Skill skill, boolean dual, boolean blow, boolean ss, boolean onCrit)
  {
    AttackInfo info = new AttackInfo();
    info.damage = attacker.getPAtk(target);
    info.defence = target.getPDef(attacker);
    info.crit_static = attacker.calcStat(Stats.CRITICAL_DAMAGE_STATIC, target, skill);
    info.death_rcpt = 0.01 * target.calcStat(Stats.DEATH_VULNERABILITY, attacker, skill);
    info.lethal1 = skill == null ? 0 : skill.getLethal1() * info.death_rcpt;
    info.lethal2 = skill == null ? 0 : skill.getLethal2() * info.death_rcpt;
    info.crit = Rnd.chance(calcCrit(attacker, target, skill, blow));
    info.shld = ((skill == null) || !skill.getShieldIgnore()) && Formulas.calcShldUse(attacker, target);
    info.lethal = false;
    info.miss = false;
    boolean isPvP = attacker.isPlayable() && target.isPlayable();
    if (info.shld)
    {
      info.defence += target.getShldDef();
    }
    info.defence = Math.max(info.defence, 1);
    if (skill != null)
    {
      if (!blow && !target.isLethalImmune())
      {
        if (Rnd.chance(info.lethal1))
        {
          if (target.isPlayer())
          {
            info.lethal = true;
            info.lethal_dmg = target.getCurrentCp();
            target.sendPacket(Msg.CP_DISAPPEARS_WHEN_HIT_WITH_A_HALF_KILL_SKILL);
          }
          else
          {
            info.lethal_dmg = target.getCurrentHp() / 2;
          }
          attacker.sendPacket(Msg.HALF_KILL);
        }
        else if (Rnd.chance(info.lethal2))
        {
          if (target.isPlayer())
          {
            info.lethal = true;
            info.lethal_dmg = (target.getCurrentHp() + target.getCurrentCp()) - 1.1;
            target.sendPacket(SystemMsg.LETHAL_STRIKE);
          }
          else
          {
            info.lethal_dmg = target.getCurrentHp() - 1;
          }
          attacker.sendPacket(SystemMsg.YOUR_LETHAL_STRIKE_WAS_SUCCESSFUL);
        }
      }
      if (skill.getPower(target) == 0)
      {
        info.damage = 0;
        return info;
      }
      if (blow && !skill.isBehind() && ss)
      {
        info.damage *= 2.04;
      }
      info.damage += Math.max(0., skill.getPower(target) * attacker.calcStat(Stats.SKILL_POWER, 1, null, null));
      if (blow && skill.isBehind() && ss)
      {
        info.damage *= 1.5;
      }
      if (!skill.isChargeBoost())
      {
        info.damage *= 1 + (((Rnd.get() * attacker.getRandomDamage() * 2) - attacker.getRandomDamage()) / 100);
      }
      if (blow)
      {
        info.damage *= 0.01 * attacker.calcStat(Stats.CRITICAL_DAMAGE, target, skill);
        info.damage = target.calcStat(Stats.CRIT_DAMAGE_RECEPTIVE, info.damage, attacker, skill);
        info.damage += 6.1 * info.crit_static;
      }
      if (skill.isChargeBoost())
      {
        info.damage *= 0.8 + (0.2 * attacker.getIncreasedForce());
      }
      if (skill.getSkillType() == SkillType.CHARGE)
      {
        info.damage *= 2;
      }
      else if (skill.isSoulBoost())
      {
        info.damage *= 1.0 + (0.06 * Math.min(attacker.getConsumedSouls(), 5));
      }
      info.damage *= 1.10113;
      if (info.crit)
      {
        if (skill.isChargeBoost() || (skill.getSkillType() == SkillType.CHARGE))
        {
          info.damage *= 2.;
        }
        else
        {
          info.damage = 2 * target.calcStat(Stats.CRIT_DAMAGE_RECEPTIVE, info.damage, attacker, skill);
        }
      }
    }
    else
    {
      info.damage *= 1 + (((Rnd.get() * attacker.getRandomDamage() * 2) - attacker.getRandomDamage()) / 100);
      if (dual)
      {
        info.damage /= 2.;
      }
      if (info.crit)
      {
        info.damage *= 0.01 * attacker.calcStat(Stats.CRITICAL_DAMAGE, target, skill);
        info.damage = 2 * target.calcStat(Stats.CRIT_DAMAGE_RECEPTIVE, info.damage, attacker, skill);
        info.damage += info.crit_static;
      }
    }
    if (info.crit)
    {
      int chance = attacker.getSkillLevel(Skill.SKILL_SOUL_MASTERY);
      if (chance > 0)
      {
        if (chance >= 21)
        {
          chance = 30;
        }
        else if (chance >= 15)
        {
          chance = 25;
        }
        else if (chance >= 9)
        {
          chance = 20;
        }
        else if (chance >= 4)
        {
          chance = 15;
        }
        if (Rnd.chance(chance))
        {
          attacker.setConsumedSouls(attacker.getConsumedSouls() + 1, null);
        }
      }
    }
    switch (PositionUtils.getDirectionTo(target, attacker))
    {
      case BEHIND:
        info.damage *= 1.2;
        break;
      case SIDE:
        info.damage *= 1.1;
        break;
      default:
        break;
    }
    if (ss)
    {
      info.damage *= blow ? 1.0 : 2.0;
    }
    info.damage *= 70. / info.defence;
    info.damage = attacker.calcStat(Stats.PHYSICAL_DAMAGE, info.damage, target, skill);
    info.damage += attacker.calcStat(Stats.PHYS_SKILL_DMG_STATIC, 1, target, skill);
    if (info.shld && Rnd.chance(5))
    {
      info.damage = 1;
    }
    if (isPvP)
    {
      if (skill == null)
      {
        info.damage *= attacker.calcStat(Stats.PVP_PHYS_DMG_BONUS, 1, null, null);
        info.damage /= target.calcStat(Stats.PVP_PHYS_DEFENCE_BONUS, 1, null, null);
      }
      else
      {
        info.damage *= attacker.calcStat(Stats.PVP_PHYS_SKILL_DMG_BONUS, 1, null, null);
        info.damage /= target.calcStat(Stats.PVP_PHYS_SKILL_DEFENCE_BONUS, 1, null, null);
      }
    }
    if (skill != null)
    {
      if (info.shld)
      {
        if (info.damage == 1)
        {
          target.sendPacket(SystemMsg.YOUR_EXCELLENT_SHIELD_DEFENSE_WAS_A_SUCCESS);
        }
        else
        {
          target.sendPacket(SystemMsg.YOUR_SHIELD_DEFENSE_HAS_SUCCEEDED);
        }
      }
      if ((info.damage > 1) && !skill.hasEffects() && Rnd.chance(target.calcStat(Stats.PSKILL_EVASION, 0, attacker, skill)))
      {
        attacker.sendPacket(new SystemMessage(SystemMessage.C1S_ATTACK_WENT_ASTRAY).addName(attacker));
        target.sendPacket(new SystemMessage(SystemMessage.C1_HAS_EVADED_C2S_ATTACK).addName(target).addName(attacker));
        info.damage = 0;
      }
      if ((info.damage > 1) && skill.isDeathlink())
      {
        info.damage *= 1.8 * (1.0 - attacker.getCurrentHpRatio());
      }
      if (onCrit && !calcBlow(attacker, target, skill))
      {
        info.miss = true;
        info.damage = 0;
        attacker.sendPacket(new SystemMessage(SystemMessage.C1S_ATTACK_WENT_ASTRAY).addName(attacker));
      }
      if (blow)
      {
        if (Rnd.chance(info.lethal1))
        {
          if (target.isPlayer())
          {
            info.lethal = true;
            info.lethal_dmg = target.getCurrentCp();
            target.sendPacket(Msg.CP_DISAPPEARS_WHEN_HIT_WITH_A_HALF_KILL_SKILL);
          }
          else if (target.isLethalImmune())
          {
            info.damage *= 2;
          }
          else
          {
            info.lethal_dmg = target.getCurrentHp() / 2;
          }
          attacker.sendPacket(Msg.HALF_KILL);
        }
        else if (Rnd.chance(info.lethal2))
        {
          if (target.isPlayer())
          {
            info.lethal = true;
            info.lethal_dmg = (target.getCurrentHp() + target.getCurrentCp()) - 1.1;
            target.sendPacket(SystemMsg.LETHAL_STRIKE);
          }
          else if (target.isLethalImmune())
          {
            info.damage *= 3;
          }
          else
          {
            info.lethal_dmg = target.getCurrentHp() - 1;
          }
          attacker.sendPacket(SystemMsg.YOUR_LETHAL_STRIKE_WAS_SUCCESSFUL);
        }
      }
      if(skill.isPowerModified())
      {
        double percentMod = skill.getWeaponModifiedPower(attacker);
        info.damage *= percentMod;
      }
      if (info.damage > 0)
      {
        attacker.displayGiveDamageMessage(target, (int) info.damage, info.crit || blow, false, false, false);
      }
      if (target.isStunned() && calcStunBreak(info.crit))
      {
        target.getEffectList().stopEffects(EffectType.Stun);
      }
      if (calcCastBreak(target, info.crit))
      {
        target.abortCast(false, true);
      }
    }
    double receiveDamageLimit = target.calcStat(Stats.RECEIVE_DAMAGE_LIMIT, 0, target, skill);
    info.reflectableDamage = info.damage;
    if ((receiveDamageLimit > 0) && (info.damage > receiveDamageLimit))
    {
      info.reflectableDamage = info.damage - receiveDamageLimit;
      info.damage = receiveDamageLimit;
    }
    return info;
  }
 
  /**
   * Method calcMagicDam.
   * @param attacker Creature
   * @param target Creature
   * @param skill Skill
   * @param sps int
   * @return AttackInfo
   */
  public static AttackInfo calcMagicDam(Creature attacker, Creature target, Skill skill, int sps)
  {
    AttackInfo info = new AttackInfo();
    boolean isPvP = attacker.isPlayable() && target.isPlayable();
    info.crit_static = attacker.calcStat(Stats.MCRITICAL_DAMAGE_STATIC, target, skill);
    info.shld = skill.getShieldIgnore() && calcShldUse(attacker, target);
    info.miss = false;
    double mAtk = attacker.getMAtk(target, skill);
    if (sps == 2)
    {
      mAtk *= 4;
    }
    else if (sps == 1)
    {
      mAtk *= 2;
    }
    double mdef = target.getMDef(null, skill);
    if (info.shld)
    {
      mdef += target.getShldDef();
    }
    if (mdef == 0)
    {
      mdef = 1;
    }
    double power = skill.getPower(target);
    info.lethal_dmg = 0;
    if (Rnd.chance(skill.getLethal1()))
    {
      if (target.isPlayer())
      {
        info.lethal = true;
        info.lethal_dmg = target.getCurrentCp();
        target.sendPacket(Msg.CP_DISAPPEARS_WHEN_HIT_WITH_A_HALF_KILL_SKILL);
      }
      else if (!target.isLethalImmune())
      {
        info.lethal = true;
        info.lethal_dmg = target.getCurrentHp() / 2;
      }
      else
      {
        power *= 2;
      }
      attacker.sendPacket(Msg.HALF_KILL);
    }
    else if (Rnd.chance(skill.getLethal2()))
    {
      if (target.isPlayer())
      {
        info.lethal = true;
        info.lethal_dmg = (target.getCurrentHp() + target.getCurrentCp()) - 1.1;
        target.sendPacket(SystemMsg.LETHAL_STRIKE);
      }
      else if (!target.isLethalImmune())
      {
        info.lethal = true;
        info.lethal_dmg = target.getCurrentHp() - 1;
      }
      else
      {
        power *= 3;
      }
      attacker.sendPacket(SystemMsg.YOUR_LETHAL_STRIKE_WAS_SUCCESSFUL);
    }
    if (power == 0)
    {
      if (info.lethal_dmg > 0)
      {
        attacker.displayGiveDamageMessage(target, (int) info.lethal_dmg, false, false, false, false);
      }
      return info;
    }
    if (skill.isSoulBoost())
    {
      power *= 1.0 + (0.06 * Math.min(attacker.getConsumedSouls(), 5));
    }
    info.damage = (91 * power * Math.sqrt(mAtk)) / mdef;
    info.damage *= 1 + (((Rnd.get() * attacker.getRandomDamage() * 2) - attacker.getRandomDamage()) / 100);
    info.crit = calcMCrit(attacker.getMagicCriticalRate(target, skill));
    if (info.crit)
    {
      info.damage *= attacker.getMagicCriticalDmg(target, skill);
      info.damage += info.crit_static;
    }
    info.damage = attacker.calcStat(Stats.MAGIC_DAMAGE, info.damage, target, skill);
    if (info.shld)
    {
      if (Rnd.chance(5))
      {
        info.damage = 0;
        target.sendPacket(SystemMsg.YOUR_EXCELLENT_SHIELD_DEFENSE_WAS_A_SUCCESS);
        attacker.sendPacket(new SystemMessage(SystemMessage.C1_RESISTED_C2S_MAGIC).addName(target).addName(attacker));
      }
      else
      {
        target.sendPacket(SystemMsg.YOUR_SHIELD_DEFENSE_HAS_SUCCEEDED);
        attacker.sendPacket(new SystemMessage(SystemMessage.YOUR_OPPONENT_HAS_RESISTANCE_TO_MAGIC_THE_DAMAGE_WAS_DECREASED));
      }
    }
    int levelDiff = target.getLevel() - attacker.getLevel();
    if ((info.damage > 1) && skill.isDeathlink())
    {
      info.damage *= 1.8 * (1.0 - attacker.getCurrentHpRatio());
    }
    if ((info.damage > 1) && skill.isBasedOnTargetDebuff())
    {
      info.damage *= 1 + (0.05 * target.getEffectList().getAllEffects().size());
    }
    info.damage += info.lethal_dmg;
    if (skill.getSkillType() == SkillType.MANADAM)
    {
      info.damage = Math.max(1, info.damage / 4.);
    }
    if (isPvP && (info.damage > 1))
    {
      info.damage *= attacker.calcStat(Stats.PVP_MAGIC_SKILL_DMG_BONUS, 1, null, null);
      info.damage /= target.calcStat(Stats.PVP_MAGIC_SKILL_DEFENCE_BONUS, 1, null, null);
    }
    double magic_rcpt = target.calcStat(Stats.MAGIC_RESIST, attacker, skill) - attacker.calcStat(Stats.MAGIC_POWER, target, skill);
    double failChance = 4. * Math.max(1., levelDiff) * (1. + (magic_rcpt / 100.));
    if (Rnd.chance(failChance))
    {
      if (levelDiff > 9)
      {
        info.damage = 0;
        SystemMessage msg = new SystemMessage(SystemMessage.C1_RESISTED_C2S_MAGIC).addName(target).addName(attacker);
        attacker.sendPacket(msg);
        target.sendPacket(msg);
      }
      else
      {
        info.damage /= 2;
        SystemMessage msg = new SystemMessage(SystemMessage.DAMAGE_IS_DECREASED_BECAUSE_C1_RESISTED_AGAINST_C2S_MAGIC).addName(target).addName(attacker);
        attacker.sendPacket(msg);
        target.sendPacket(msg);
      }
    }
    if ((info.damage > 1) && skill.isMagic() && calcMagicMiss(attacker,target))
    {
      attacker.sendPacket(new SystemMessage(SystemMessage.C1S_ATTACK_WENT_ASTRAY).addName(attacker));
      target.sendPacket(new SystemMessage(SystemMessage.C1_HAS_EVADED_C2S_ATTACK).addName(target).addName(attacker));
      info.damage = 0;
    }
    if (info.damage > 0)
    {
      attacker.displayGiveDamageMessage(target, (int) info.damage, info.crit, false, false, true);
    }
    if (calcCastBreak(target, info.crit))
    {
      target.abortCast(false, true);
    }
    double receiveDamageLimit = target.calcStat(Stats.RECEIVE_DAMAGE_LIMIT, 0, target, skill);
    info.reflectableDamage = info.damage;
    if ((receiveDamageLimit > 0) && (info.damage > receiveDamageLimit))
    {
      info.reflectableDamage = info.damage - receiveDamageLimit;
      info.damage = receiveDamageLimit;
    }
    if (skill.isMarkDamage())
    {
      int boost = 1;
      //TODO ADD INTO XML LIST OF SKILL TO CHECK
      // name="dependOnTargetEffectId" value="11259,11261,11262"
      int[] list = {11259,11261,11262};
      for (int id : list)
      {
        if (target.getEffectList().containEffectFromSkillId(id, true))
        {
          boost += 1;
        }
      }
      //TODO CHECK RETAIL INFO ABOUT BOOST DAMAGE
      info.damage *= boost;
    }
    return info;
  }
 
  /**
   * Method calcStunBreak.
   * @param crit boolean
   * @return boolean
   */
  public static boolean calcStunBreak(boolean crit)
  {
    return Rnd.chance(crit ? 75 : 10);
  }
 
  /**
   * Method calcBlow.
   * @param activeChar Creature
   * @param target Creature
   * @param skill Skill
   * @return boolean
   */
  public static boolean calcBlow(Creature activeChar, Creature target, Skill skill)
  {
    WeaponTemplate weapon = activeChar.getActiveWeaponItem();
    double base_weapon_crit = weapon == null ? 4. : weapon.getCritical();
    double crit_height_bonus = (0.008 * Math.min(25, Math.max(-25, target.getZ() - activeChar.getZ()))) + 1.1;
    double buffs_mult = activeChar.calcStat(Stats.FATALBLOW_RATE, target, skill);
    double skill_mod = skill.isBehind() ? 5 : 4;
    double chance = base_weapon_crit * buffs_mult * crit_height_bonus * skill_mod;
    if (!target.isInCombat())
    {
      chance *= 1.1;
    }
    switch (PositionUtils.getDirectionTo(target, activeChar))
    {
      case BEHIND:
        chance *= 1.3;
        break;
      case SIDE:
        chance *= 1.1;
        break;
      case FRONT:
        if (skill.isBehind())
        {
          chance = 3.0;
        }
        break;
      default:
        break;
    }
    chance = Math.min(skill.isBehind() ? 100 : 80, chance);
    return Rnd.chance(chance);
  }
 
  /**
   * Method calcCrit.
   * @param attacker Creature
   * @param target Creature
   * @param skill Skill
   * @param blow boolean
   * @return double
   */
  public static double calcCrit(Creature attacker, Creature target, Skill skill, boolean blow)
  {
    if (attacker.isPlayer() && (attacker.getActiveWeaponItem() == null))
    {
      return 0;
    }
    if (skill != null)
    {
      return skill.getCriticalRate() * (blow ? BaseStats.DEX.calcBonus(attacker) : BaseStats.STR.calcBonus(attacker)) * 0.01 * attacker.calcStat(Stats.SKILL_CRIT_CHANCE_MOD, target, skill);
    }
    double rate = attacker.getCriticalHit(target, null) * 0.01 * target.calcStat(Stats.CRIT_CHANCE_RECEPTIVE, attacker, skill);
    switch (PositionUtils.getDirectionTo(target, attacker))
    {
      case BEHIND:
        rate *= 1.4;
        break;
      case SIDE:
        rate *= 1.2;
        break;
      default:
        break;
    }
    return rate / 10;
  }
 
  /**
   * Method calcMCrit.
   * @param mRate double
   * @return boolean
   */
  public static boolean calcMCrit(double mRate)
  {
    return mRate > Rnd.get(1000);
  }
 
  /**
   * Method calcCastBreak.
   * @param target Creature
   * @param crit boolean
   * @return boolean
   */
  public static boolean calcCastBreak(Creature target, boolean crit)
  {
    if ((target == null) || target.isInvul() || target.isRaid() || !target.isCastingNow())
    {
      return false;
    }
    Skill skill = target.getCastingSkill();
    if ((skill != null) && ((skill.getSkillType() == SkillType.TAKECASTLE) || (skill.getSkillType() == SkillType.TAKEFORTRESS)))
    {
      return false;
    }
    return Rnd.chance(target.calcStat(Stats.CAST_INTERRUPT, crit ? 75 : 10, null, skill));
  }
 
  /**
   * Method calcPAtkSpd.
   * @param rate double
   * @return int
   */
  public static int calcPAtkSpd(double rate)
  {
    return (int) (500000 / rate);
  }
 
  /**
   * Method calcMAtkSpd.
   * @param attacker Creature
   * @param skill Skill
   * @param skillTime double
   * @return int
   */
  public static int calcMAtkSpd(Creature attacker, Skill skill, double skillTime)
  {
    if (skill.isMagic())
    {
      return (int) ((skillTime * 333) / Math.max(attacker.getMAtkSpd(), 1));
    }
    return (int) ((skillTime * 333) / Math.max(attacker.getPAtkSpd(), 1));
  }
 
  /**
   * Method calcSkillReuseDelay.
   * @param actor Creature
   * @param skill Skill
   * @return long
   */
  public static long calcSkillReuseDelay(Creature actor, Skill skill)
  {
    long reuseDelay = skill.getReuseDelay();
    if (actor.isMonster())
    {
      reuseDelay = skill.getReuseForMonsters();
    }
    if (skill.isReuseDelayPermanent() || skill.isHandler() || skill.isItemSkill())
    {
      return reuseDelay;
    }
    if (actor.getSkillMastery(skill.getId()) == 1)
    {
      actor.removeSkillMastery(skill.getId());
      return 0;
    }
    if (skill.isMusic())
    {
      return (long) actor.calcStat(Stats.MUSIC_REUSE_RATE, reuseDelay, null, skill);
    }
    if (skill.isMagic())
    {
      return (long) actor.calcStat(Stats.MAGIC_REUSE_RATE, reuseDelay, null, skill);
    }
    return (long) actor.calcStat(Stats.PHYSIC_REUSE_RATE, reuseDelay, null, skill);
  }
 
  /**
   * Method calcHitMiss.
   * @param attacker Creature
   * @param target Creature
   * @return boolean
   */
  public static boolean calcHitMiss(Creature attacker, Creature target)
  {
    int chanceToHit = 88 + (2 * (attacker.getAccuracy() - target.getEvasionRate(attacker)));
    chanceToHit = Math.max(chanceToHit, 28);
    chanceToHit = Math.min(chanceToHit, 98);
    PositionUtils.TargetDirection direction = PositionUtils.getDirectionTo(attacker, target);
    switch (direction)
    {
      case BEHIND:
        chanceToHit *= 1.2;
        break;
      case SIDE:
        chanceToHit *= 1.1;
        break;
      default:
        break;
    }
    return !Rnd.chance(chanceToHit);
  }

  /**
   * Method calcMagicMiss.
   * @param attacker Creature
   * @param target Creature
   * @return boolean
   */
  public static boolean calcMagicMiss(Creature attacker, Creature target)
  {
    int chanceToHit = 88 + (2 * (attacker.getMAccuracy() - target.getMEvasionRate((attacker))));
    chanceToHit = Math.max(chanceToHit, 28);
    chanceToHit = Math.min(chanceToHit, 98);
    return !Rnd.chance(chanceToHit);
  }
 
  /**
   * Method calcShldUse.
   * @param attacker Creature
   * @param target Creature
   * @return boolean
   */
  public static boolean calcShldUse(Creature attacker, Creature target)
  {
    WeaponTemplate template = target.getSecondaryWeaponItem();
    if ((template == null) || (template.getItemType() != WeaponTemplate.WeaponType.NONE))
    {
      return false;
    }
    int angle = (int) target.calcStat(Stats.SHIELD_ANGLE, attacker, null);
    if (!PositionUtils.isFacing(target, attacker, angle))
    {
      return false;
    }
    return Rnd.chance((int) target.calcStat(Stats.SHIELD_RATE, attacker, null));
  }
 
  /**
   * Method calcSkillSuccess.
   * @param env Env
   * @param et EffectTemplate
   * @param spiritshot int
   * @return boolean
   */
  public static boolean calcSkillSuccess(Env env, EffectTemplate et, int spiritshot)
  {
    if (env.value == -1)
    {
      return true;
    }
    env.value = Math.max(Math.min(env.value, 100), 1);
    final double base = env.value;
    final Skill skill = env.skill;
    if (!skill.isOffensive())
    {
      return Rnd.chance(env.value);
    }
    final Creature caster = env.character;
    final Creature target = env.target;
    boolean debugCaster = false;
    boolean debugTarget = false;
    boolean debugGlobal = false;
    if (Config.ALT_DEBUG_ENABLED)
    {
      debugCaster = (caster.getPlayer() != null) && caster.getPlayer().isDebug();
      debugTarget = (target.getPlayer() != null) && target.getPlayer().isDebug();
      final boolean debugPvP = Config.ALT_DEBUG_PVP_ENABLED && (debugCaster && debugTarget) && (!Config.ALT_DEBUG_PVP_DUEL_ONLY || (caster.getPlayer().isInDuel() && target.getPlayer().isInDuel()));
      debugGlobal = debugPvP || (Config.ALT_DEBUG_PVE_ENABLED && ((debugCaster && target.isMonster()) || (debugTarget && caster.isMonster())));
    }
    double statMod = 1.;
    if (skill.getSaveVs() != null)
    {
      statMod = skill.getSaveVs().calcChanceMod(target);
      env.value *= statMod;
    }
    env.value = Math.max(env.value, 1);
    double mAtkMod = 1.;
    int ssMod = 0;
    if (skill.isMagic())
    {
      int mdef = Math.max(1, target.getMDef(target, skill));
      double matk = caster.getMAtk(target, skill);
      if (skill.isSSPossible())
      {
        switch (spiritshot)
        {
          case ItemInstance.CHARGED_BLESSED_SPIRITSHOT:
            ssMod = 4;
            break;
          case ItemInstance.CHARGED_SPIRITSHOT:
            ssMod = 2;
            break;
          default:
            ssMod = 1;
        }
        matk *= ssMod;
      }
      mAtkMod = (Config.SKILLS_CHANCE_MOD * Math.pow(matk, Config.SKILLS_CHANCE_POW)) / mdef;
      env.value *= mAtkMod;
      env.value = Math.max(env.value, 1);
    }
    double lvlDependMod = skill.getLevelModifier();
    if (lvlDependMod != 0)
    {
      final int attackLevel = skill.getMagicLevel() > 0 ? skill.getMagicLevel() : caster.getLevel();
      lvlDependMod = 1. + ((attackLevel - target.getLevel()) * 0.03 * lvlDependMod);
      if (lvlDependMod < 0)
      {
        lvlDependMod = 0;
      }
      else if (lvlDependMod > 2)
      {
        lvlDependMod = 2;
      }
      env.value *= lvlDependMod;
    }
    double vulnMod = 0;
    double profMod = 0;
    double resMod = 1.;
    double debuffMod = 1.;
    if (!skill.isIgnoreResists())
    {
      debuffMod = 1. - (target.calcStat(Stats.DEBUFF_RESIST, caster, skill) / 120.);
      if (debuffMod != 1)
      {
        if (debuffMod == Double.NEGATIVE_INFINITY)
        {
          if (debugGlobal)
          {
            if (debugCaster)
            {
              caster.getPlayer().sendMessage("Full debuff immunity");
            }
            if (debugTarget)
            {
              target.getPlayer().sendMessage("Full debuff immunity");
            }
          }
          return false;
        }
        if (debuffMod == Double.POSITIVE_INFINITY)
        {
          if (debugGlobal)
          {
            if (debugCaster)
            {
              caster.getPlayer().sendMessage("Full debuff vulnerability");
            }
            if (debugTarget)
            {
              target.getPlayer().sendMessage("Full debuff vulnerability");
            }
          }
          return true;
        }
        debuffMod = Math.max(debuffMod, 0);
        env.value *= debuffMod;
      }
      SkillTrait trait = skill.getTraitType();
      if (trait != null)
      {
        vulnMod = trait.calcVuln(env);
        profMod = trait.calcProf(env);
        final double maxResist = 90 + (profMod * 0.85);
        resMod = (maxResist - vulnMod) / 60.;
      }
      if (resMod != 1)
      {
        if (resMod == Double.NEGATIVE_INFINITY)
        {
          if (debugGlobal)
          {
            if (debugCaster)
            {
              caster.getPlayer().sendMessage("Full immunity");
            }
            if (debugTarget)
            {
              target.getPlayer().sendMessage("Full immunity");
            }
          }
          return false;
        }
        if (resMod == Double.POSITIVE_INFINITY)
        {
          if (debugGlobal)
          {
            if (debugCaster)
            {
              caster.getPlayer().sendMessage("Full vulnerability");
            }
            if (debugTarget)
            {
              target.getPlayer().sendMessage("Full vulnerability");
            }
          }
          return true;
        }
        resMod = Math.max(resMod, 0);
        env.value *= resMod;
      }
    }
    double elementMod = 0;
    final Element element = skill.getElement();
    if (element != Element.NONE)
    {
      elementMod = skill.getElementPower();
      Element attackElement = getAttackElement(caster, target);
      if (attackElement == element)
      {
        elementMod += caster.calcStat(element.getAttack(), 0);
      }
      elementMod -= target.calcStat(element.getDefence(), 0);
      elementMod = Math.round(elementMod / 10);
      env.value += elementMod;
    }
    env.value = Math.max(env.value, Math.min(base, Config.SKILLS_CHANCE_MIN));
    env.value = Math.max(Math.min(env.value, Config.SKILLS_CHANCE_CAP), 1);
    final boolean result = Rnd.chance((int) env.value);
    if (debugGlobal)
    {
      StringBuilder stat = new StringBuilder(100);
      if (et == null)
      {
        stat.append(skill.getName());
      }
      else
      {
        stat.append(et._effectType.name());
      }
      stat.append(" AR:");
      stat.append((int) base);
      stat.append(' ');
      if (skill.getSaveVs() != null)
      {
        stat.append(skill.getSaveVs().name());
        stat.append(':');
        stat.append(String.format("%1.1f", statMod));
      }
      if (skill.isMagic())
      {
        stat.append(' ');
        stat.append(" mAtk:");
        stat.append(String.format("%1.1f", mAtkMod));
      }
      if (skill.getTraitType() != null)
      {
        stat.append(' ');
        stat.append(skill.getTraitType().name());
      }
      stat.append(' ');
      stat.append(String.format("%1.1f", resMod));
      stat.append('(');
      stat.append(String.format("%1.1f", profMod));
      stat.append('/');
      stat.append(String.format("%1.1f", vulnMod));
      if (debuffMod != 0)
      {
        stat.append('+');
        stat.append(String.format("%1.1f", debuffMod));
      }
      stat.append(") lvl:");
      stat.append(String.format("%1.1f", lvlDependMod));
      stat.append(" elem:");
      stat.append((int) elementMod);
      stat.append(" Chance:");
      stat.append(String.format("%1.1f", env.value));
      if (!result)
      {
        stat.append(" failed");
      }
      if (debugCaster)
      {
        caster.getPlayer().sendMessage(stat.toString());
      }
      if (debugTarget)
      {
        target.getPlayer().sendMessage(stat.toString());
      }
    }
    return result;
  }
 
  /**
   * Method calcSkillSuccess.
   * @param player Creature
   * @param target Creature
   * @param skill Skill
   * @param activateRate int
   * @return boolean
   */
  public static boolean calcSkillSuccess(Creature player, Creature target, Skill skill, int activateRate)
  {
    Env env = new Env();
    env.character = player;
    env.target = target;
    env.skill = skill;
    env.value = activateRate;
    return calcSkillSuccess(env, null, player.getChargedSpiritShot());
  }
 
  /**
   * Method calcSkillMastery.
   * @param skill Skill
   * @param activeChar Creature
   */
  public static void calcSkillMastery(Skill skill, Creature activeChar)
  {
    if (skill.isHandler())
    {
      return;
    }
    if (((activeChar.getSkillLevel(331) > 0) && (activeChar.calcStat(Stats.SKILL_MASTERY, activeChar.getINT(), null, skill) >= Rnd.get(1000))) || (((activeChar.getSkillLevel(330) > 0) || (activeChar.getSkillLevel(10501) > 0)) && (activeChar.calcStat(Stats.SKILL_MASTERY, activeChar.getSTR(), null, skill) >= Rnd.get(1000))))
    {
      int masteryLevel;
      Skill.SkillType type = skill.getSkillType();
      if (skill.isMusic() || (type == Skill.SkillType.BUFF) || (type == Skill.SkillType.HOT) || (type == Skill.SkillType.HEAL_PERCENT))
      {
        masteryLevel = 2;
      }
      else if ((type == Skill.SkillType.HEAL) || (type == Skill.SkillType.HEAL_HP_CP))
      {
        masteryLevel = 3;
      }
      else
      {
        masteryLevel = 1;
      }
      if (masteryLevel > 0)
      {
        activeChar.setSkillMastery(skill.getId(), masteryLevel);
      }
    }
  }
 
  /**
   * Method calcDamageResists.
   * @param skill Skill
   * @param attacker Creature
   * @param defender Creature
   * @param value double
   * @return double
   */
  public static double calcDamageResists(Skill skill, Creature attacker, Creature defender, double value)
  {
    if (attacker == defender)
    {
      return value;
    }
    if (attacker.isBoss())
    {
      value *= Config.RATE_EPIC_ATTACK;
    }
    else if (attacker.isRaid() || (attacker instanceof ReflectionBossInstance))
    {
      value *= Config.RATE_RAID_ATTACK;
    }
    if (defender.isBoss())
    {
      value /= Config.RATE_EPIC_DEFENSE;
    }
    else if (defender.isRaid() || (defender instanceof ReflectionBossInstance))
    {
      value /= Config.RATE_RAID_DEFENSE;
    }
    Player pAttacker = attacker.getPlayer();
    int diff = defender.getLevel() - (pAttacker != null ? pAttacker.getLevel() : attacker.getLevel());
    if (attacker.isPlayable() && defender.isMonster() && (defender.getLevel() >= 78) && (diff > 2))
    {
      value *= .7 / Math.pow(diff - 2, .25);
    }
    Element element = Element.NONE;
    double power = 0.;
    if (skill != null)
    {
      element = skill.getElement();
      power = skill.getElementPower();
    }
    else
    {
      element = getAttackElement(attacker, defender);
    }
    if (element == Element.NONE)
    {
      return value;
    }
    Double attDiff = attacker.calcStat(element.getAttack(), power) - defender.calcStat(element.getDefence(), 0.);
    if ((pAttacker != null) && pAttacker.isDebug())
    {
      pAttacker.sendMessage("Element: " + element.name());
      pAttacker.sendMessage("Attack: " + attacker.calcStat(element.getAttack(), power));
      pAttacker.sendMessage("Defence: " + defender.calcStat(element.getDefence(), 0.));
      pAttacker.sendMessage("Modifier: " + (attDiff < 0 ? "On defense " : "On attack ") + AttributeDamageResistTable.getInstance().getAttributeBonus(attDiff));
    }
    if(attDiff < 0)
    {
      return value / AttributeDamageResistTable.getInstance().getAttributeBonus(attDiff);
    }
    else
    {
      return value * AttributeDamageResistTable.getInstance().getAttributeBonus(attDiff);
    }
  }
  /**
   * Method getAttackElement.
   * @param attacker Creature
   * @param target Creature
   * @return Element
   */
  public static Element getAttackElement(Creature attacker, Creature target)
  {
    double val, max = Double.MIN_VALUE, maxElementDefenseVal = Double.MIN_VALUE;
    Element maxElementDefense = Element.NONE;   
    Element result = Element.NONE;
    for (Element e : Element.VALUES)
    {
      val = attacker.calcStat(e.getAttack(), 0., null, null);
      if (val <= 0.)
      {
        if(target != null && ((target.calcStat(e.getDefence(), 0., null, null)) > maxElementDefenseVal))
        {
          maxElementDefenseVal = target.calcStat(e.getDefence(), 0., null, null);
          maxElementDefense = e;
        }
        continue;
      }
      if(val > max)
      {
        max = val;
        result = e;
     
    }
    if(result == Element.NONE && target != null)
    {
      return maxElementDefense;
    }
    return result;
  }
}
TOP

Related Classes of lineage2.gameserver.stats.Formulas$AttackInfo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.