Clan Elpy - version 5

Status
Không mở trả lời sau này.
làm nhiều nhiều vứt pé 15 coin thôi còn nhiu chia đều cả clan :)) ko chia bắt nhốt kêu làm cave loại II giờ
 
File công thức tính toán damage/stat của tenkai, xin của cu Pere, cho bà con nghiên cứu mà build.
http://www.mediafire.com/?5nl3z7k9yzqbh6l
Ngâm cứu đi pà con, công thức này không có gì quá phức tạp :8cool_cool:

/*
* 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 com.l2tenkai.gameserver.stats;

import java.util.logging.Level;
import java.util.logging.Logger;

import com.l2tenkai.Config;
import com.l2tenkai.gameserver.datatables.PlayerClassTable;
import com.l2tenkai.gameserver.instancemanager.CastleManager;
import com.l2tenkai.gameserver.instancemanager.ClanHallManager;
import com.l2tenkai.gameserver.instancemanager.FortManager;
import com.l2tenkai.gameserver.instancemanager.SiegeManager;
import com.l2tenkai.gameserver.instancemanager.ZoneManager;
import com.l2tenkai.gameserver.model.Elementals;
import com.l2tenkai.gameserver.model.L2ItemInstance;
import com.l2tenkai.gameserver.model.L2SiegeClan;
import com.l2tenkai.gameserver.model.L2Skill;
import com.l2tenkai.gameserver.model.actor.L2Attackable;
import com.l2tenkai.gameserver.model.actor.L2Character;
import com.l2tenkai.gameserver.model.actor.L2Npc;
import com.l2tenkai.gameserver.model.actor.L2Playable;
import com.l2tenkai.gameserver.model.actor.L2Summon;
import com.l2tenkai.gameserver.model.actor.instance.L2CubicInstance;
import com.l2tenkai.gameserver.model.actor.instance.L2DoorInstance;
import com.l2tenkai.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2tenkai.gameserver.model.actor.instance.L2PcInstance;
import com.l2tenkai.gameserver.model.actor.instance.L2PetInstance;
import com.l2tenkai.gameserver.model.actor.instance.L2RaidBossInstance;
import com.l2tenkai.gameserver.model.actor.instance.L2SummonInstance;
import com.l2tenkai.gameserver.model.base.PlayerClass;
import com.l2tenkai.gameserver.model.base.PlayerState;
import com.l2tenkai.gameserver.model.entity.Castle;
import com.l2tenkai.gameserver.model.entity.ClanHall;
import com.l2tenkai.gameserver.model.entity.Fort;
import com.l2tenkai.gameserver.model.entity.Siege;
import com.l2tenkai.gameserver.model.itemcontainer.Inventory;
import com.l2tenkai.gameserver.model.zone.type.L2CastleZone;
import com.l2tenkai.gameserver.model.zone.type.L2ClanHallZone;
import com.l2tenkai.gameserver.model.zone.type.L2FortZone;
import com.l2tenkai.gameserver.model.zone.type.L2MotherTreeZone;
import com.l2tenkai.gameserver.network.SystemMessageId;
import com.l2tenkai.gameserver.network.serverpackets.SystemMessage;
import com.l2tenkai.gameserver.stats.conditions.ConditionLogicOr;
import com.l2tenkai.gameserver.stats.conditions.ConditionPlayerState;
import com.l2tenkai.gameserver.stats.conditions.ConditionUsingItemType;
import com.l2tenkai.gameserver.stats.funcs.Func;
import com.l2tenkai.gameserver.templates.effects.EffectTemplate;
import com.l2tenkai.gameserver.templates.item.L2Armor;
import com.l2tenkai.gameserver.templates.item.L2ArmorType;
import com.l2tenkai.gameserver.templates.item.L2Item;
import com.l2tenkai.gameserver.templates.item.L2Weapon;
import com.l2tenkai.gameserver.templates.item.L2WeaponType;
import com.l2tenkai.gameserver.templates.skills.L2SkillType;
import com.l2tenkai.gameserver.util.Util;
import com.l2tenkai.util.Rnd;
import com.l2tenkai.util.StringUtil;


/**
* Global calculations, can be modified by server admins
*/
public final class Formulas
{
/** Regen Task period */
protected static final Logger _log = Logger.getLogger(L2Character.class.getName());
private static final int HP_REGENERATE_PERIOD = 3000; // 3 secs

public static final byte SHIELD_DEFENSE_FAILED = 0; // no shield defense
public static final byte SHIELD_DEFENSE_SUCCEED = 1; // normal shield defense
public static final byte SHIELD_DEFENSE_PERFECT_BLOCK = 2; // perfect block

public static final byte SKILL_REFLECT_FAILED = 0; // no reflect
public static final byte SKILL_REFLECT_SUCCEED = 1; // normal reflect, some damage reflected some other not
public static final byte SKILL_REFLECT_VENGEANCE = 2; // 100% of the damage affect both

private static final byte MELEE_ATTACK_RANGE = 40;

static class FuncAddLevel3 extends Func
{
static final FuncAddLevel3[] _instancies = new FuncAddLevel3[Stats.NUM_STATS];

static Func getInstance(Stats stat)
{
int pos = stat.ordinal();
if (_instancies[pos] == null) _instancies[pos] = new FuncAddLevel3(stat);
return _instancies[pos];
}

private FuncAddLevel3(Stats pStat)
{
super(pStat, 0x10, null);
}

@Override
public void calc(Env env)
{
env.value += env.player.getLevel() / 3.0;
}
}

static class FuncMultLevelMod extends Func
{
static final FuncMultLevelMod[] _instancies = new FuncMultLevelMod[Stats.NUM_STATS];

static Func getInstance(Stats stat)
{
int pos = stat.ordinal();
if (_instancies[pos] == null) _instancies[pos] = new FuncMultLevelMod(stat);
return _instancies[pos];
}

private FuncMultLevelMod(Stats pStat)
{
super(pStat, 0x20, null);
}

@Override
public void calc(Env env)
{
env.value *= env.player.getLevelMod();
}
}

static class FuncMultRegenResting extends Func
{
static final FuncMultRegenResting[] _instancies = new FuncMultRegenResting[Stats.NUM_STATS];

/**
* Return the Func object corresponding to the state concerned.<BR><BR>
*/
static Func getInstance(Stats stat)
{
int pos = stat.ordinal();

if (_instancies[pos] == null) _instancies[pos] = new FuncMultRegenResting(stat);

return _instancies[pos];
}

/**
* Constructor of the FuncMultRegenResting.<BR><BR>
*/
private FuncMultRegenResting(Stats pStat)
{
super(pStat, 0x20, null);
setCondition(new ConditionPlayerState(PlayerState.RESTING, true));
}

/**
* Calculate the modifier of the state concerned.<BR><BR>
*/
@Override
public void calc(Env env)
{
if (!cond.test(env)) return;

env.value *= 1.45;
}
}

static class FuncPAtkMod extends Func
{
static final FuncPAtkMod _fpa_instance = new FuncPAtkMod();

static Func getInstance()
{
return _fpa_instance;
}

private FuncPAtkMod()
{
super(Stats.POWER_ATTACK, 0x30, null);
}

@Override
public void calc(Env env)
{
if (env.player instanceof L2PcInstance)
{
env.value *= BaseStats.STR.calcBonus(env.player) * env.player.getLevelMod();
}
else
{
float level = env.player.getLevel();
env.value *= BaseStats.STR.calcBonus(env.player) * ((level + 89) / 100);
}
}
}

static class FuncMAtkMod extends Func
{
static final FuncMAtkMod _fma_instance = new FuncMAtkMod();

static Func getInstance()
{
return _fma_instance;
}

private FuncMAtkMod()
{
super(Stats.MAGIC_ATTACK, 0x20, null);
}

@Override
public void calc(Env env)
{
if (env.player instanceof L2PcInstance)
{
double intb = BaseStats.INT.calcBonus(env.player);
double lvlb = env.player.getLevelMod();
env.value *= (lvlb * lvlb) * (intb * intb);
}
else
{
float level = env.player.getLevel();
double intb = BaseStats.INT.calcBonus(env.player);
float lvlb = ((level + 89) / 100);
env.value *= (lvlb * lvlb) * (intb * intb);
}
}
}

static class FuncMDefMod extends Func
{
static final FuncMDefMod _fmm_instance = new FuncMDefMod();

static Func getInstance()
{
return _fmm_instance;
}

private FuncMDefMod()
{
super(Stats.MAGIC_DEFENCE, 0x20, null);
}

@Override
public void calc(Env env)
{
float level = env.player.getLevel();
if (env.player instanceof L2PcInstance)
{
L2PcInstance p = (L2PcInstance) env.player;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_LFINGER) != null)
env.value -= 5;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_RFINGER) != null)
env.value -= 5;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEAR) != null)
env.value -= 9;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_REAR) != null)
env.value -= 9;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_NECK) != null)
env.value -= 13;
env.value *= BaseStats.MEN.calcBonus(env.player) * env.player.getLevelMod();
}
else if (env.player instanceof L2PetInstance)
{
if (env.player.getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_NECK) != 0)
{
env.value *= BaseStats.MEN.calcBonus(env.player);
return;
}
}
else
{
env.value *= BaseStats.MEN.calcBonus(env.player) * ((level + 89) / 100);
}
}
}

static class FuncPDefMod extends Func
{
static final FuncPDefMod _fmm_instance = new FuncPDefMod();

static Func getInstance()
{
return _fmm_instance;
}

private FuncPDefMod()
{
super(Stats.POWER_DEFENCE, 0x20, null);
}

@Override
public void calc(Env env)
{
if (env.player instanceof L2PcInstance)
{
L2PcInstance p = (L2PcInstance) env.player;
boolean hasMagePDef = (p.getCurrentClass().isMage() || p.getCurrentClass().getId() == 0x31); // orc mystics are a special case
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_HEAD) != null)
env.value -= 12;
L2ItemInstance chest = p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
if (chest != null)
env.value -= hasMagePDef ? 15 : 31;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS) != null || (chest != null && chest.getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR))
env.value -= hasMagePDef ? 8 : 18;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_GLOVES) != null)
env.value -= 8;
if (p.getInventory().getPaperdollItem(Inventory.PAPERDOLL_FEET) != null)
env.value -= 7;
env.value *= env.player.getLevelMod();
}
else
{
float level = env.player.getLevel();
env.value *= ((level + 89) / 100);
}
}
}

static class FuncBowAtkRange extends Func
{
private static final FuncBowAtkRange _fbar_instance = new FuncBowAtkRange();

static Func getInstance()
{
return _fbar_instance;
}

private FuncBowAtkRange()
{
super(Stats.POWER_ATTACK_RANGE, 0x10, null);
setCondition(new ConditionUsingItemType(L2WeaponType.BOW.mask()));
}

@Override
public void calc(Env env)
{
if (!cond.test(env))
return;
// default is 40 and with bow should be 500
env.value += 460;
}
}

static class FuncCrossBowAtkRange extends Func
{
private static final FuncCrossBowAtkRange _fcb_instance = new FuncCrossBowAtkRange();

static Func getInstance()
{
return _fcb_instance;
}

private FuncCrossBowAtkRange()
{
super(Stats.POWER_ATTACK_RANGE, 0x10, null);
ConditionLogicOr or = new ConditionLogicOr();
or.add(new ConditionUsingItemType(L2WeaponType.CROSSBOW.mask()));
or.add(new ConditionUsingItemType(L2WeaponType.CROSSBOWK.mask()));
setCondition(or);
}

@Override
public void calc(Env env)
{
if (!cond.test(env))
return;
// default is 40 and with crossbow should be 400
env.value += 360;
}
}

static class FuncAtkAccuracy extends Func
{
static final FuncAtkAccuracy _faa_instance = new FuncAtkAccuracy();

static Func getInstance()
{
return _faa_instance;
}

private FuncAtkAccuracy()
{
super(Stats.ACCURACY_COMBAT, 0x10, null);
}

@Override
public void calc(Env env)
{
final int level = env.player.getLevel();
env.value += Math.sqrt(env.player.getDEX()) * 6;
env.value += level;
if (level > 77)
env.value += (level - 77) + 1;
if (level > 69)
env.value += (level - 69);
//if (env.player instanceof L2Summon)
//env.value += (level < 60) ? 4 : 5;
}
}

static class FuncMAtkAccuracy extends Func
{
static final FuncMAtkAccuracy _fmaa_instance = new FuncMAtkAccuracy();

static Func getInstance()
{
return _fmaa_instance;
}

private FuncMAtkAccuracy()
{
super(Stats.ACCURACY_MAGIC, 0x10, null);
}

@Override
public void calc(Env env)
{
final int level = env.player.getLevel();
//[Square(DEX)]*6 + lvl + weapon hitbonus;
env.value += Math.sqrt(env.player.getWIT()) * 6;
env.value += level;
if (level > 77)
env.value += (level - 77);
if (level > 69)
env.value += (level - 69);
if (env.player instanceof L2Summon)
env.value += (level < 60) ? 4 : 5;
}
}

static class FuncAtkEvasion extends Func
{
static final FuncAtkEvasion _fae_instance = new FuncAtkEvasion();

static Func getInstance()
{
return _fae_instance;
}

private FuncAtkEvasion()
{
super(Stats.EVASION_RATE, 0x10, null);
}

@Override
public void calc(Env env)
{
final int level = env.player.getLevel();
if (env.player instanceof L2PcInstance)
{
//[Square(DEX)]*6 + lvl;
env.value += Math.sqrt(env.player.getDEX()) * 6;
env.value += level;
if (level > 77)
env.value += (level - 77);
if (level > 69)
env.value += (level - 69);
}
else
{
//[Square(DEX)]*6 + lvl;
env.value += Math.sqrt(env.player.getDEX()) * 6;
env.value += level;
if (level > 69)
env.value += (level - 69) + 2;
}
}
}

static class FuncMAtkEvasion extends Func
{
static final FuncMAtkEvasion _fmae_instance = new FuncMAtkEvasion();

static Func getInstance()
{
return _fmae_instance;
}

private FuncMAtkEvasion()
{
super(Stats.MEVASION_RATE, 0x10, null);
}

@Override
public void calc(Env env)
{
final int level = env.player.getLevel();
if (env.player instanceof L2PcInstance)
{
//[Square(DEX)]*6 + lvl;
env.value += Math.sqrt(env.player.getWIT()) * 6;
env.value += level;
if (level > 77)
env.value += (level - 77);
if (level > 69)
env.value += (level - 69);
}
else
{
//[Square(DEX)]*6 + lvl;
env.value += Math.sqrt(env.player.getWIT()) * 6;
env.value += level;
if (level > 69)
env.value += (level - 69) + 2;
}
}
}

static class FuncAtkCritical extends Func
{
static final FuncAtkCritical _fac_instance = new FuncAtkCritical();

static Func getInstance()
{
return _fac_instance;
}

private FuncAtkCritical()
{
super(Stats.CRITICAL_RATE, 0x09, null);
}

@Override
public void calc(Env env)
{
env.value *= BaseStats.DEX.calcBonus(env.player);
env.value *= 10;
env.baseValue = env.value;
}
}

static class FuncMAtkCritical extends Func
{
static final FuncMAtkCritical _fac_instance = new FuncMAtkCritical();

static Func getInstance()
{
return _fac_instance;
}

private FuncMAtkCritical()
{
super(Stats.MCRITICAL_RATE, 0x09, null);
}

@Override
public void calc(Env env)
{
L2Character p = env.player;
if(p instanceof L2Summon)
env.value = 8; // TODO: needs retail value
else if (p instanceof L2PcInstance && p.getActiveWeaponInstance() != null)
env.value *= BaseStats.WIT.calcBonus(p);
env.value *= 10;
env.baseValue = env.value;
}
}

static class FuncMoveSpeed extends Func
{
static final FuncMoveSpeed _fms_instance = new FuncMoveSpeed();

static Func getInstance()
{
return _fms_instance;
}

private FuncMoveSpeed()
{
super(Stats.RUN_SPEED, 0x30, null);
}

@Override
public void calc(Env env)
{
//env.value *= BaseStats.DEX.calcBonus(env.player);
}
}

static class FuncPAtkSpeed extends Func
{
static final FuncPAtkSpeed _fas_instance = new FuncPAtkSpeed();

static Func getInstance()
{
return _fas_instance;
}

private FuncPAtkSpeed()
{
super(Stats.POWER_ATTACK_SPEED, 0x20, null);
}

@Override
public void calc(Env env)
{
env.value *= BaseStats.DEX.calcBonus(env.player);
}
}

static class FuncMAtkSpeed extends Func
{
static final FuncMAtkSpeed _fas_instance = new FuncMAtkSpeed();

static Func getInstance()
{
return _fas_instance;
}

private FuncMAtkSpeed()
{
super(Stats.MAGIC_ATTACK_SPEED, 0x20, null);
}

@Override
public void calc(Env env)
{
env.value *= BaseStats.WIT.calcBonus(env.player);
}
}

static class FuncHennaSTR extends Func
{
static final FuncHennaSTR _fh_instance = new FuncHennaSTR();

static Func getInstance()
{
return _fh_instance;
}

private FuncHennaSTR()
{
super(Stats.STAT_STR, 0x10, null);
}

@Override
public void calc(Env env)
{
// L2PcTemplate t = (L2PcTemplate)env._player.getTemplate();
L2PcInstance pc = (L2PcInstance) env.player;
if (pc != null) env.value += pc.getHennaStatSTR();
}
}

static class FuncHennaDEX extends Func
{
static final FuncHennaDEX _fh_instance = new FuncHennaDEX();

static Func getInstance()
{
return _fh_instance;
}

private FuncHennaDEX()
{
super(Stats.STAT_DEX, 0x10, null);
}

@Override
public void calc(Env env)
{
// L2PcTemplate t = (L2PcTemplate)env._player.getTemplate();
L2PcInstance pc = (L2PcInstance) env.player;
if (pc != null) env.value += pc.getHennaStatDEX();
}
}

static class FuncHennaINT extends Func
{
static final FuncHennaINT _fh_instance = new FuncHennaINT();

static Func getInstance()
{
return _fh_instance;
}

private FuncHennaINT()
{
super(Stats.STAT_INT, 0x10, null);
}

@Override
public void calc(Env env)
{
// L2PcTemplate t = (L2PcTemplate)env._player.getTemplate();
L2PcInstance pc = (L2PcInstance) env.player;
if (pc != null) env.value += pc.getHennaStatINT();
}
}

static class FuncHennaMEN extends Func
{
static final FuncHennaMEN _fh_instance = new FuncHennaMEN();

static Func getInstance()
{
return _fh_instance;
}

private FuncHennaMEN()
{
super(Stats.STAT_MEN, 0x10, null);
}

@Override
public void calc(Env env)
{
// L2PcTemplate t = (L2PcTemplate)env._player.getTemplate();
L2PcInstance pc = (L2PcInstance) env.player;
if (pc != null) env.value += pc.getHennaStatMEN();
}
}

static class FuncHennaCON extends Func
{
static final FuncHennaCON _fh_instance = new FuncHennaCON();

static Func getInstance()
{
return _fh_instance;
}

private FuncHennaCON()
{
super(Stats.STAT_CON, 0x10, null);
}

@Override
public void calc(Env env)
{
// L2PcTemplate t = (L2PcTemplate)env._player.getTemplate();
L2PcInstance pc = (L2PcInstance) env.player;
if (pc != null) env.value += pc.getHennaStatCON();
}
}

static class FuncHennaWIT extends Func
{
static final FuncHennaWIT _fh_instance = new FuncHennaWIT();

static Func getInstance()
{
return _fh_instance;
}

private FuncHennaWIT()
{
super(Stats.STAT_WIT, 0x10, null);
}

@Override
public void calc(Env env)
{
// L2PcTemplate t = (L2PcTemplate)env._player.getTemplate();
L2PcInstance pc = (L2PcInstance) env.player;
if (pc != null) env.value += pc.getHennaStatWIT();
}
}

static class FuncMaxHpAdd extends Func
{
static final FuncMaxHpAdd _fmha_instance = new FuncMaxHpAdd();

static Func getInstance()
{
return _fmha_instance;
}

private FuncMaxHpAdd()
{
super(Stats.MAX_HP, 0x10, null);
}

@Override
public void calc(Env env)
{
PlayerClass cl = PlayerClassTable.getInstance().getClassById(((L2PcInstance)env.player).getActiveClass());
int lvl = env.player.getLevel() - cl.getLevel();
double hpmod = cl.getHpMod() * lvl;
double hpmax = (cl.getHpAdd() + hpmod) * lvl;
double hpmin = (cl.getHpAdd() * lvl) + hpmod;
env.value += (hpmax + hpmin) / 2;
}
}

static class FuncMaxHpMul extends Func
{
static final FuncMaxHpMul _fmhm_instance = new FuncMaxHpMul();

static Func getInstance()
{
return _fmhm_instance;
}

private FuncMaxHpMul()
{
super(Stats.MAX_HP, 0x20, null);
}

@Override
public void calc(Env env)
{
env.value *= BaseStats.CON.calcBonus(env.player);
}
}

static class FuncMaxCpAdd extends Func
{
static final FuncMaxCpAdd _fmca_instance = new FuncMaxCpAdd();

static Func getInstance()
{
return _fmca_instance;
}

private FuncMaxCpAdd()
{
super(Stats.MAX_CP, 0x10, null);
}

@Override
public void calc(Env env)
{
PlayerClass cl = PlayerClassTable.getInstance().getClassById(((L2PcInstance)env.player).getActiveClass());
int lvl = env.player.getLevel() - cl.getLevel();
double cpmod = cl.getCpMod() * lvl;
double cpmax = (cl.getCpAdd() + cpmod) * lvl;
double cpmin = (cl.getCpAdd() * lvl) + cpmod;
env.value += (cpmax + cpmin) / 2;
}
}

static class FuncMaxCpMul extends Func
{
static final FuncMaxCpMul _fmcm_instance = new FuncMaxCpMul();

static Func getInstance()
{
return _fmcm_instance;
}

private FuncMaxCpMul()
{
super(Stats.MAX_CP, 0x20, null);
}

@Override
public void calc(Env env)
{
env.value *= BaseStats.CON.calcBonus(env.player);
}
}

static class FuncMaxMpAdd extends Func
{
static final FuncMaxMpAdd _fmma_instance = new FuncMaxMpAdd();

static Func getInstance()
{
return _fmma_instance;
}

private FuncMaxMpAdd()
{
super(Stats.MAX_MP, 0x10, null);
}

@Override
public void calc(Env env)
{
PlayerClass cl = PlayerClassTable.getInstance().getClassById(((L2PcInstance)env.player).getActiveClass());
int lvl = env.player.getLevel() - cl.getLevel();
double mpmod = cl.getMpMod() * lvl;
double mpmax = (cl.getMpAdd() + mpmod) * lvl;
double mpmin = (cl.getMpAdd() * lvl) + mpmod;
env.value += (mpmax + mpmin) / 2;
}
}

static class FuncMaxMpMul extends Func
{
static final FuncMaxMpMul _fmmm_instance = new FuncMaxMpMul();

static Func getInstance()
{
return _fmmm_instance;
}

private FuncMaxMpMul()
{
super(Stats.MAX_MP, 0x20, null);
}

@Override
public void calc(Env env)
{
//TODO: Confirm
//env.value *= BaseStats.MEN.calcBonus(env.player);
}
}

/**
* Return the period between 2 regenerations task (3s for L2Character, 5 min for L2DoorInstance).<BR><BR>
*/
public static int getRegeneratePeriod(L2Character cha)
{
if (cha instanceof L2DoorInstance) return HP_REGENERATE_PERIOD * 100; // 5 mins

return HP_REGENERATE_PERIOD; // 3s
}

/**
* Return the standard NPC Calculator set containing ACCURACY_COMBAT and EVASION_RATE.<BR><BR>
*
* <B><U> Concept</U> :</B><BR><BR>
* A calculator is created to manage and dynamically calculate the effect of a character property (ex : MAX_HP, REGENERATE_HP_RATE...).
* In fact, each calculator is a table of Func object in which each Func represents a mathematic function : <BR><BR>
*
* FuncAtkAccuracy -> Math.sqrt(_player.getDEX())*6+_player.getLevel()<BR><BR>
*
* To reduce cache memory use, L2NPCInstances who don't have skills share the same Calculator set called <B>NPC_STD_CALCULATOR</B>.<BR><BR>
*
*/
public static Calculator[] getStdNPCCalculators()
{
Calculator[] std = new Calculator[Stats.NUM_STATS];

std[Stats.MAX_HP.ordinal()] = new Calculator();
std[Stats.MAX_HP.ordinal()].addFunc(FuncMaxHpMul.getInstance());

std[Stats.MAX_MP.ordinal()] = new Calculator();
std[Stats.MAX_MP.ordinal()].addFunc(FuncMaxMpMul.getInstance());

std[Stats.POWER_ATTACK.ordinal()] = new Calculator();
std[Stats.POWER_ATTACK.ordinal()].addFunc(FuncPAtkMod.getInstance());

std[Stats.MAGIC_ATTACK.ordinal()] = new Calculator();
std[Stats.MAGIC_ATTACK.ordinal()].addFunc(FuncMAtkMod.getInstance());

std[Stats.POWER_DEFENCE.ordinal()] = new Calculator();
std[Stats.POWER_DEFENCE.ordinal()].addFunc(FuncPDefMod.getInstance());

std[Stats.MAGIC_DEFENCE.ordinal()] = new Calculator();
std[Stats.MAGIC_DEFENCE.ordinal()].addFunc(FuncMDefMod.getInstance());

std[Stats.CRITICAL_RATE.ordinal()] = new Calculator();
std[Stats.CRITICAL_RATE.ordinal()].addFunc(FuncAtkCritical.getInstance());

std[Stats.MCRITICAL_RATE.ordinal()] = new Calculator();
std[Stats.MCRITICAL_RATE.ordinal()].addFunc(FuncMAtkCritical.getInstance());

std[Stats.ACCURACY_COMBAT.ordinal()] = new Calculator();
std[Stats.ACCURACY_COMBAT.ordinal()].addFunc(FuncAtkAccuracy.getInstance());

std[Stats.ACCURACY_MAGIC.ordinal()] = new Calculator();
std[Stats.ACCURACY_MAGIC.ordinal()].addFunc(FuncAtkAccuracy.getInstance());

std[Stats.EVASION_RATE.ordinal()] = new Calculator();
std[Stats.EVASION_RATE.ordinal()].addFunc(FuncAtkEvasion.getInstance());

std[Stats.MEVASION_RATE.ordinal()] = new Calculator();
std[Stats.MEVASION_RATE.ordinal()].addFunc(FuncAtkEvasion.getInstance());

std[Stats.POWER_ATTACK_SPEED.ordinal()] = new Calculator();
std[Stats.POWER_ATTACK_SPEED.ordinal()].addFunc(FuncPAtkSpeed.getInstance());

std[Stats.MAGIC_ATTACK_SPEED.ordinal()] = new Calculator();
std[Stats.MAGIC_ATTACK_SPEED.ordinal()].addFunc(FuncMAtkSpeed.getInstance());

std[Stats.RUN_SPEED.ordinal()] = new Calculator();
std[Stats.RUN_SPEED.ordinal()].addFunc(FuncMoveSpeed.getInstance());

return std;
}

public static Calculator[] getStdDoorCalculators()
{
Calculator[] std = new Calculator[Stats.NUM_STATS];

// Add the FuncAtkAccuracy to the Standard Calculator of ACCURACY_COMBAT
std[Stats.ACCURACY_COMBAT.ordinal()] = new Calculator();
std[Stats.ACCURACY_COMBAT.ordinal()].addFunc(FuncAtkAccuracy.getInstance());

// Add the FuncAtkEvasion to the Standard Calculator of EVASION_RATE
std[Stats.EVASION_RATE.ordinal()] = new Calculator();
std[Stats.EVASION_RATE.ordinal()].addFunc(FuncAtkEvasion.getInstance());

// Add the FuncAtkEvasion to the Standard Calculator of MEVASION_RATE
std[Stats.MEVASION_RATE.ordinal()] = new Calculator();
std[Stats.MEVASION_RATE.ordinal()].addFunc(FuncAtkEvasion.getInstance());

return std;
}

/**
* Add basics Func objects to L2PcInstance and L2Summon.<BR><BR>
*
* <B><U> Concept</U> :</B><BR><BR>
* A calculator is created to manage and dynamically calculate the effect of a character property (ex : MAX_HP, REGENERATE_HP_RATE...).
* In fact, each calculator is a table of Func object in which each Func represents a mathematic function : <BR><BR>
*
* FuncAtkAccuracy -> Math.sqrt(_player.getDEX())*6+_player.getLevel()<BR><BR>
*
* @param cha L2PcInstance or L2Summon that must obtain basic Func objects
*/
public static void addFuncsToNewCharacter(L2Character cha)
{
if (cha instanceof L2PcInstance)
{
cha.addStatFunc(FuncMaxHpAdd.getInstance());
cha.addStatFunc(FuncMaxHpMul.getInstance());
cha.addStatFunc(FuncMaxCpAdd.getInstance());
cha.addStatFunc(FuncMaxCpMul.getInstance());
cha.addStatFunc(FuncMaxMpAdd.getInstance());
cha.addStatFunc(FuncMaxMpMul.getInstance());
//cha.addStatFunc(FuncMultRegenResting.getInstance(Stats.REGENERATE_HP_RATE));
//cha.addStatFunc(FuncMultRegenResting.getInstance(Stats.REGENERATE_CP_RATE));
//cha.addStatFunc(FuncMultRegenResting.getInstance(Stats.REGENERATE_MP_RATE));
cha.addStatFunc(FuncBowAtkRange.getInstance());
cha.addStatFunc(FuncCrossBowAtkRange.getInstance());
//cha.addStatFunc(FuncMultLevelMod.getInstance(Stats.POWER_ATTACK));
//cha.addStatFunc(FuncMultLevelMod.getInstance(Stats.POWER_DEFENCE));
//cha.addStatFunc(FuncMultLevelMod.getInstance(Stats.MAGIC_DEFENCE));
cha.addStatFunc(FuncPAtkMod.getInstance());
cha.addStatFunc(FuncMAtkMod.getInstance());
cha.addStatFunc(FuncPDefMod.getInstance());
cha.addStatFunc(FuncMDefMod.getInstance());
cha.addStatFunc(FuncAtkCritical.getInstance());
cha.addStatFunc(FuncMAtkCritical.getInstance());
cha.addStatFunc(FuncAtkAccuracy.getInstance());
cha.addStatFunc(FuncMAtkAccuracy.getInstance());
cha.addStatFunc(FuncAtkEvasion.getInstance());
cha.addStatFunc(FuncMAtkEvasion.getInstance());
cha.addStatFunc(FuncPAtkSpeed.getInstance());
cha.addStatFunc(FuncMAtkSpeed.getInstance());
cha.addStatFunc(FuncMoveSpeed.getInstance());

cha.addStatFunc(FuncHennaSTR.getInstance());
cha.addStatFunc(FuncHennaDEX.getInstance());
cha.addStatFunc(FuncHennaINT.getInstance());
cha.addStatFunc(FuncHennaMEN.getInstance());
cha.addStatFunc(FuncHennaCON.getInstance());
cha.addStatFunc(FuncHennaWIT.getInstance());
}
else if (cha instanceof L2PetInstance)
{
cha.addStatFunc(FuncMaxHpMul.getInstance());
cha.addStatFunc(FuncMaxMpMul.getInstance());
cha.addStatFunc(FuncPAtkMod.getInstance());
cha.addStatFunc(FuncMAtkMod.getInstance());
cha.addStatFunc(FuncPDefMod.getInstance());
cha.addStatFunc(FuncMDefMod.getInstance());
cha.addStatFunc(FuncAtkCritical.getInstance());
cha.addStatFunc(FuncMAtkCritical.getInstance());
cha.addStatFunc(FuncAtkAccuracy.getInstance());
cha.addStatFunc(FuncMAtkAccuracy.getInstance());
cha.addStatFunc(FuncAtkEvasion.getInstance());
cha.addStatFunc(FuncMAtkEvasion.getInstance());
cha.addStatFunc(FuncMoveSpeed.getInstance());
cha.addStatFunc(FuncPAtkSpeed.getInstance());
cha.addStatFunc(FuncMAtkSpeed.getInstance());
}
else if (cha instanceof L2Summon)
{
//cha.addStatFunc(FuncMultRegenResting.getInstance(Stats.REGENERATE_HP_RATE));
//cha.addStatFunc(FuncMultRegenResting.getInstance(Stats.REGENERATE_MP_RATE));
cha.addStatFunc(FuncMaxHpMul.getInstance());
cha.addStatFunc(FuncMaxMpMul.getInstance());
cha.addStatFunc(FuncPAtkMod.getInstance());
cha.addStatFunc(FuncMAtkMod.getInstance());
cha.addStatFunc(FuncPDefMod.getInstance());
cha.addStatFunc(FuncMDefMod.getInstance());
cha.addStatFunc(FuncAtkCritical.getInstance());
cha.addStatFunc(FuncMAtkCritical.getInstance());
cha.addStatFunc(FuncAtkAccuracy.getInstance());
cha.addStatFunc(FuncMAtkAccuracy.getInstance());
cha.addStatFunc(FuncAtkEvasion.getInstance());
cha.addStatFunc(FuncMAtkEvasion.getInstance());
cha.addStatFunc(FuncMoveSpeed.getInstance());
cha.addStatFunc(FuncPAtkSpeed.getInstance());
cha.addStatFunc(FuncMAtkSpeed.getInstance());
}
}

/**
* Calculate the HP regen rate (base + modifiers).<BR>
* <BR>
*/
public static final double calcHpRegen(L2Character cha)
{
double init = cha.getTemplate().baseHpReg;
double hpRegenMultiplier = cha.isRaid() ? Config.RAID_HP_REGEN_MULTIPLIER : Config.HP_REGEN_MULTIPLIER;
double hpRegenBonus = 0;

if (Config.L2JMOD_CHAMPION_ENABLE && cha.isChampion())
hpRegenMultiplier *= Config.L2JMOD_CHAMPION_HP_REGEN;

if (cha instanceof L2PcInstance)
{
L2PcInstance player = (L2PcInstance) cha;

// Calculate correct baseHpReg value for certain level of PC
init += (player.getLevel() > 10) ? ((player.getLevel() - 1) / 10.0) : 0.5;

double siegeModifier = calcSiegeRegenModifer(player);
if (siegeModifier > 0)
hpRegenMultiplier *= siegeModifier;

if (player.isInsideZone(L2Character.ZONE_CLANHALL) && player.getClan() != null && player.getClan().getHasHideout() > 0)
{
L2ClanHallZone zone = ZoneManager.getInstance().getZone(player, L2ClanHallZone.class);
int posChIndex = zone == null ? -1 : zone.getClanHallId();
int clanHallIndex = player.getClan().getHasHideout();
if (clanHallIndex > 0 && clanHallIndex == posChIndex)
{
ClanHall clansHall = ClanHallManager.getInstance().getClanHallById(clanHallIndex);
if (clansHall != null)
if (clansHall.getFunction(ClanHall.FUNC_RESTORE_HP) != null)
hpRegenMultiplier *= 1 + (double) clansHall.getFunction(ClanHall.FUNC_RESTORE_HP).getLvl() / 100;
}
}

if (player.isInsideZone(L2Character.ZONE_CASTLE) && player.getClan() != null && player.getClan().getHasCastle() > 0)
{
L2CastleZone zone = ZoneManager.getInstance().getZone(player, L2CastleZone.class);
int posCastleIndex = zone == null ? -1 : zone.getCastleId();
int castleIndex = player.getClan().getHasCastle();
if (castleIndex > 0 && castleIndex == posCastleIndex)
{
Castle castle = CastleManager.getInstance().getCastleById(castleIndex);
if (castle != null)
if (castle.getFunction(Castle.FUNC_RESTORE_HP) != null)
hpRegenMultiplier *= 1 + (double) castle.getFunction(Castle.FUNC_RESTORE_HP).getLvl() / 100;
}
}

if (player.isInsideZone(L2Character.ZONE_FORT) && player.getClan() != null && player.getClan().getHasFort() > 0)
{
L2FortZone zone = ZoneManager.getInstance().getZone(player, L2FortZone.class);
int posFortIndex = zone == null ? -1 : zone.getFortId();
int fortIndex = player.getClan().getHasFort();
if (fortIndex > 0 && fortIndex == posFortIndex)
{
Fort fort = FortManager.getInstance().getFortById(fortIndex);
if (fort != null)
if (fort.getFunction(Fort.FUNC_RESTORE_HP) != null)
hpRegenMultiplier *= 1 + (double) fort.getFunction(Fort.FUNC_RESTORE_HP).getLvl() / 100;
}
}

// Mother Tree effect is calculated at last
if (player.isInsideZone(L2Character.ZONE_MOTHERTREE))
{
L2MotherTreeZone zone = ZoneManager.getInstance().getZone(player, L2MotherTreeZone.class);
int hpBonus = zone == null ? 0 : zone.getHpRegenBonus();
hpRegenBonus += hpBonus;
}

// Calculate Movement bonus
if (player.isSitting())
hpRegenMultiplier *= 1.5; // Sitting
else if (!player.isMoving())
hpRegenMultiplier *= 1.1; // Staying
else if (player.isRunning())
hpRegenMultiplier *= 0.7; // Running

// Add CON bonus
init *= cha.getLevelMod() * BaseStats.CON.calcBonus(cha);
}
else if (cha instanceof L2PetInstance)
init = ((L2PetInstance) cha).getPetLevelData().getPetRegenHP() * Config.PET_HP_REGEN_MULTIPLIER;

if (init < 1)
init = 1;

return cha.calcStat(Stats.REGENERATE_HP_RATE, init, null, null) * hpRegenMultiplier + hpRegenBonus;
}

/**
* Calculate the MP regen rate (base + modifiers).<BR><BR>
*/
public static final double calcMpRegen(L2Character cha)
{
double init = cha.getTemplate().baseMpReg;
double mpRegenMultiplier = cha.isRaid() ? Config.RAID_MP_REGEN_MULTIPLIER : Config.MP_REGEN_MULTIPLIER;
double mpRegenBonus = 0;

if (cha instanceof L2PcInstance)
{
L2PcInstance player = (L2PcInstance) cha;

// Calculate correct baseMpReg value for certain level of PC
init += 0.3 * ((player.getLevel() - 1) / 10.0);

// Mother Tree effect is calculated at last'
if (player.isInsideZone(L2Character.ZONE_MOTHERTREE))
{
L2MotherTreeZone zone = ZoneManager.getInstance().getZone(player, L2MotherTreeZone.class);
int mpBonus = zone == null ? 0 : zone.getMpRegenBonus();
mpRegenBonus += mpBonus;
}

if (player.isInsideZone(L2Character.ZONE_CLANHALL) && player.getClan() != null && player.getClan().getHasHideout() > 0)
{
L2ClanHallZone zone = ZoneManager.getInstance().getZone(player, L2ClanHallZone.class);
int posChIndex = zone == null ? -1 : zone.getClanHallId();
int clanHallIndex = player.getClan().getHasHideout();
if (clanHallIndex > 0 && clanHallIndex == posChIndex)
{
ClanHall clansHall = ClanHallManager.getInstance().getClanHallById(clanHallIndex);
if (clansHall != null)
if (clansHall.getFunction(ClanHall.FUNC_RESTORE_MP) != null)
mpRegenMultiplier *= 1 + (double) clansHall.getFunction(ClanHall.FUNC_RESTORE_MP).getLvl() / 100;
}
}

if (player.isInsideZone(L2Character.ZONE_CASTLE) && player.getClan() != null && player.getClan().getHasCastle() > 0)
{
L2CastleZone zone = ZoneManager.getInstance().getZone(player, L2CastleZone.class);
int posCastleIndex = zone == null ? -1 : zone.getCastleId();
int castleIndex = player.getClan().getHasCastle();
if (castleIndex > 0 && castleIndex == posCastleIndex)
{
Castle castle = CastleManager.getInstance().getCastleById(castleIndex);
if (castle != null)
{
if (castle.getFunction(Castle.FUNC_RESTORE_MP) != null)
mpRegenMultiplier *= 1 + (double) castle.getFunction(Castle.FUNC_RESTORE_MP).getLvl() / 100;
}
}
}

if (player.isInsideZone(L2Character.ZONE_FORT) && player.getClan() != null && player.getClan().getHasFort() > 0)
{
L2FortZone zone = ZoneManager.getInstance().getZone(player, L2FortZone.class);
int posFortIndex = zone == null ? -1 : zone.getFortId();
int fortIndex = player.getClan().getHasFort();
if (fortIndex > 0 && fortIndex == posFortIndex)
{
Fort fort = FortManager.getInstance().getFortById(fortIndex);
if (fort != null)
if (fort.getFunction(Fort.FUNC_RESTORE_MP) != null)
mpRegenMultiplier *= 1 + (double) fort.getFunction(Fort.FUNC_RESTORE_MP).getLvl() / 100;
}
}

// Calculate Movement bonus
if (player.isSitting())
mpRegenMultiplier *= 1.5; // Sitting
else if (!player.isMoving())
mpRegenMultiplier *= 1.1; // Staying
else if (player.isRunning())
mpRegenMultiplier *= 0.7; // Running

if (Config.isServer(Config.PVP) && player.getPvpFlag() == 0)
init *= 10;
}
else if (cha instanceof L2PetInstance)
init = ((L2PetInstance) cha).getPetLevelData().getPetRegenMP() * Config.PET_MP_REGEN_MULTIPLIER;

if (init < 1)
init = 1;

return cha.calcStat(Stats.REGENERATE_MP_RATE, init, null, null) * mpRegenMultiplier + mpRegenBonus;
}

/**
* Calculate the CP regen rate (base + modifiers).<BR><BR>
*/
public static final double calcCpRegen(L2Character cha)
{
double init = cha.getTemplate().baseHpReg;
double cpRegenMultiplier = Config.CP_REGEN_MULTIPLIER;
double cpRegenBonus = 0;

if (cha instanceof L2PcInstance)
{
L2PcInstance player = (L2PcInstance) cha;

// Calculate correct baseHpReg value for certain level of PC
init += (player.getLevel() > 10) ? ((player.getLevel() - 1) / 10.0) : 0.5;

// Calculate Movement bonus
if (player.isSitting())
cpRegenMultiplier *= 1.5; // Sitting
else if (!player.isMoving())
cpRegenMultiplier *= 1.1; // Staying
else if (player.isRunning())
cpRegenMultiplier *= 0.7; // Running
}
else
{
// Calculate Movement bonus
if (!cha.isMoving())
cpRegenMultiplier *= 1.1; // Staying
else if (cha.isRunning())
cpRegenMultiplier *= 0.7; // Running
}

// Apply CON bonus
init *= cha.getLevelMod() * BaseStats.CON.calcBonus(cha);
if (init < 1)
init = 1;

return cha.calcStat(Stats.REGENERATE_CP_RATE, init, null, null) * cpRegenMultiplier + cpRegenBonus;
}

public static final double calcSiegeRegenModifer(L2PcInstance activeChar)
{
if (activeChar == null || activeChar.getClan() == null) return 0;

Siege siege = SiegeManager.getInstance().getSiege(activeChar.getPosition().getX(),
activeChar.getPosition().getY(),
activeChar.getPosition().getZ());
if (siege == null || !siege.getIsInProgress()) return 0;

L2SiegeClan siegeClan = siege.getAttackerClan(activeChar.getClan().getClanId());
if (siegeClan == null || siegeClan.getFlag().isEmpty()
|| !Util.checkIfInRange(200, activeChar, siegeClan.getFlag().get(0), true)) return 0;

return 1.5; // If all is true, then modifer will be 50% more
}

public static double calcBlowDamage(L2Character attacker, L2Character target, L2Skill skill, byte shld, boolean ss)
{
double defence = target.getPDef(attacker);

switch(shld)
{
case Formulas.SHIELD_DEFENSE_SUCCEED:
defence += target.getShldDef();
break;
case Formulas.SHIELD_DEFENSE_PERFECT_BLOCK: // perfect block
return 1;
}

double power = skill.getPower();
//TODO: target instanceof L2Playable && skill.getPvpPower() != 0 ? skill.getPvpPower() : skill.getPower();
double damage = 0;
double proximityBonus = 1;
double graciaPhysSkillBonus = skill.isMagic() ? 1 : 1.10113; // Gracia final physical skill bonus 10.113%
double ssboost = ss ? (skill.getSSBoost() > 0 ? skill.getSSBoost() : 2.04) : 1; // 104% bonus with SS
double pvpBonus = 1;

if ((attacker instanceof L2Playable) && (target instanceof L2Playable))
{
// Dmg bonusses in PvP fight
pvpBonus *= attacker.calcStat(Stats.PVP_PHYS_SKILL_DMG, 1, null, null);
// Def bonusses in PvP fight
defence *= target.calcStat(Stats.PVP_PHYS_SKILL_DEF, 1, null, null);
}

if (isBehind(attacker, target))
proximityBonus = 1.2; // +20% crit dmg when back stabbed
else if (isInFrontOf(attacker, target))
proximityBonus = 1.1; // +10% crit dmg when side stabbed

damage += Formulas.calcValakasAttribute(attacker, target, skill);

double element = calcElemental(attacker, target, skill);

// SSBoost > 0 have different calculation
if (skill.getSSBoost() > 0)
damage += 70. * graciaPhysSkillBonus * (attacker.getPAtk(target) + power) / defence * (attacker.calcStat(Stats.CRITICAL_DAMAGE, 1, target, skill))
* (target.calcStat(Stats.CRIT_VULN, 1, target, skill)) * ssboost * proximityBonus * element * pvpBonus
+ (attacker.calcStat(Stats.CRITICAL_DAMAGE_ADD, 0, target, skill) * 6.1 * 70 / defence * graciaPhysSkillBonus);
else
damage += 70. * graciaPhysSkillBonus * (power + (attacker.getPAtk(target) * ssboost)) / defence * (attacker.calcStat(Stats.CRITICAL_DAMAGE, 1, target, skill))
* (target.calcStat(Stats.CRIT_VULN, 1, target, skill)) * proximityBonus * element * pvpBonus
+ (attacker.calcStat(Stats.CRITICAL_DAMAGE_ADD, 0, target, skill) * 6.1 * 70 / defence * graciaPhysSkillBonus);

damage += target.calcStat(Stats.CRIT_ADD_VULN, 0, target, skill) * 6.1;

if (isInFrontOf(target, attacker))
damage *= attacker.calcStat(Stats.CRITICAL_DMG_FRONT, 1, target, skill);
else if (isBehind(target, attacker))
damage *= attacker.calcStat(Stats.CRITICAL_DMG_BEHIND, 1, target, skill);
else
damage *= attacker.calcStat(Stats.CRITICAL_DMG_SIDE, 1, target, skill);

// get the vulnerability for the instance due to skills (buffs, passives, toggles, etc)
damage = target.calcStat(Stats.DAGGER_WPN_VULN, damage, target, null);
// Random weapon damage
damage *= attacker.getRandomDamageMultiplier();

if (target instanceof L2Attackable && !target.isRaid() && !target.isRaidMinion() && target.getLevel() >= Config.MIN_NPC_LVL_DMG_PENALTY && attacker.getActingPlayer() != null
&& (target.getLevel() - attacker.getActingPlayer().getLevel()) >= 2)
{
int lvlDiff = target.getLevel() - attacker.getActingPlayer().getLevel() - 1;
if (lvlDiff > Config.NPC_SKILL_DMG_PENALTY.size())
damage *= Config.NPC_SKILL_DMG_PENALTY.get(Config.NPC_SKILL_DMG_PENALTY.size());
else
damage *= Config.NPC_SKILL_DMG_PENALTY.get(lvlDiff);

}

//TODO: Formulas.calcStunBreak(target, damage);

return damage < 1 ? 1. : damage;
}

/** Calculated damage caused by ATTACK of attacker on target,
* called separatly for each weapon, if dual-weapon is used.
*
* @param attacker player or NPC that makes ATTACK
* @param target player or NPC, target of ATTACK
* @param miss one of ATTACK_XXX constants
* @param crit if the ATTACK have critical success
* @param dual if dual weapon is used
* @param ss if weapon item was charged by soulshot
* @return damage points
*/
public static final double calcPhysDam(L2Character attacker, L2Character target, L2Skill skill,
byte shld, boolean crit, boolean dual, boolean ss)
{
final boolean isPvP = (attacker instanceof L2Playable) && (target instanceof L2Playable);
final boolean isPvE = (attacker instanceof L2Playable) && (target instanceof L2Attackable);
double damage = attacker.getPAtk(target);
double defence = target.getPDef(attacker);
damage+=calcValakasAttribute(attacker, target, skill);
if (attacker instanceof L2Npc)
{
if(((L2Npc)attacker)._soulshotcharged)
{
ss = true;
}
else
ss = false;
((L2Npc)attacker)._soulshotcharged = false;
}
// Def bonusses in PvP fight
if(isPvP)
{
if(skill == null)
defence *= target.calcStat(Stats.PVP_PHYSICAL_DEF, 1, null, null);
else
defence *= target.calcStat(Stats.PVP_PHYS_SKILL_DEF, 1, null, null);
}

switch (shld)
{
case SHIELD_DEFENSE_SUCCEED:
if (!Config.ALT_GAME_SHIELD_BLOCKS)
defence += target.getShldDef();
break;
case SHIELD_DEFENSE_PERFECT_BLOCK: // perfect block
return 1.;
}

if (ss) damage *= 2;
if (skill != null)
{
double skillpower = skill.getPower(attacker, target, isPvP, isPvE);
float ssboost = skill.getSSBoost();
if (ssboost <= 0)
damage += skillpower;
else if (ssboost > 0)
{
if (ss)
{
skillpower *= ssboost;
damage += skillpower;
}
else
damage += skillpower;
}
}

// defence modifier depending of the attacker weapon
L2Weapon weapon = attacker.getActiveWeaponItem();
Stats stat = null;
boolean isBow = false;
if (weapon != null && !attacker.isTransformed())
{
switch (weapon.getItemType())
{
case BOW:
isBow = true;
stat = Stats.BOW_WPN_VULN;
break;
case CROSSBOW:
case CROSSBOWK:
isBow = true;
stat = Stats.CROSSBOW_WPN_VULN;
break;
case BLUNT:
stat = Stats.BLUNT_WPN_VULN;
break;
case DAGGER:
stat = Stats.DAGGER_WPN_VULN;
break;
case DUAL:
stat = Stats.DUAL_WPN_VULN;
break;
case DUALFIST:
stat = Stats.DUALFIST_WPN_VULN;
break;
case ETC:
stat = Stats.ETC_WPN_VULN;
break;
case FIST:
stat = Stats.FIST_WPN_VULN;
break;
case POLE:
stat = Stats.POLE_WPN_VULN;
break;
case SWORD:
stat = Stats.SWORD_WPN_VULN;
break;
case BIGSWORD:
stat = Stats.BIGSWORD_WPN_VULN;
break;
case BIGBLUNT:
stat = Stats.BIGBLUNT_WPN_VULN;
break;
case DUALDAGGER:
stat = Stats.DUALDAGGER_WPN_VULN;
break;
case RAPIER:
stat = Stats.RAPIER_WPN_VULN;
break;
case ANCIENTSWORD:
stat = Stats.ANCIENT_WPN_VULN;
break;
/*case PET:
stat = Stats.PET_WPN_VULN;
break;*/
}
}

// for summon use pet weapon vuln, since they cant hold weapon
if (attacker instanceof L2SummonInstance)
stat = Stats.PET_WPN_VULN;

if (crit)
{
//Finally retail like formula
damage = 2 * attacker.calcStat(Stats.CRITICAL_DAMAGE, 1, target, skill) * target.calcStat(Stats.CRIT_VULN, target.getTemplate().baseCritVuln, target, null) * (70 * damage / defence);
//Crit dmg add is almost useless in normal hits...
damage += (attacker.calcStat(Stats.CRITICAL_DAMAGE_ADD, 0, target, skill) * 70 / defence);
}
else
damage = 70 * damage / defence;

/** Tenkai customization - Summon boost **/
/*if (Config.isServer(Config.PVP) && (attacker instanceof L2Summon || target instanceof L2Summon))
{
if (target instanceof L2Summon && attacker instanceof L2PcInstance // Player attacks a summon
&& !((L2Summon)target).getOwner().getCurrentClass().equalsOrChildOf(ClassId.darkAvenger) // Summon is not a Dark Panther
&& !((L2Summon)target).getOwner().getCurrentClass().equalsOrChildOf(ClassId.necromancer) // Summon is not a Necro summon
&& !((L2Summon)target).getOwner().getCurrentClass().equalsOrChildOf(ClassId.paladin)) // Summon is not a Imperial Phoenix
{
damage *= 0.5;
}

if (attacker instanceof L2Summon && target instanceof L2PcInstance // Summon attacks a player
&& !((L2Summon)attacker).getOwner().getCurrentClass().equalsOrChildOf(ClassId.darkAvenger) // Summon is not a Dark Panther
&& !((L2Summon)attacker).getOwner().getCurrentClass().equalsOrChildOf(ClassId.necromancer) // Summon is not a Necro summon
&& !((L2Summon)attacker).getOwner().getCurrentClass().equalsOrChildOf(ClassId.paladin)) // Summon is not a Imperial Phoenix
{
damage *= 2;
}
}*/

if (stat != null)
{
// get the vulnerability due to skills (buffs, passives, toggles, etc)
damage = target.calcStat(stat, damage, target, null);
}

// Weapon random damage
damage *= attacker.getRandomDamageMultiplier();

//damage += Rnd.nextDouble() * damage / 10;
// damage += _rnd.nextDouble()* attacker.getRandomDamage(target);
// }
if (shld > 0 && Config.ALT_GAME_SHIELD_BLOCKS)
{
damage -= target.getShldDef();
if (damage < 0) damage = 0;
}

if (target instanceof L2Npc)
{
switch (((L2Npc) target).getTemplate().getRace())
{
case BEAST:
damage *= attacker.getPAtkMonsters(target);
break;
case ANIMAL:
damage *= attacker.getPAtkAnimals(target);
break;
case PLANT:
damage *= attacker.getPAtkPlants(target);
break;
case DRAGON:
damage *= attacker.getPAtkDragons(target);
break;
case BUG:
damage *= attacker.getPAtkInsects(target);
break;
case GIANT:
damage *= attacker.getPAtkGiants(target);
break;
case MAGICCREATURE:
damage *= attacker.getPAtkMagicCreatures(target);
break;
default:
// nothing
break;
}
}

if (damage > 0 && damage < 1)
{
damage = 1;
}
else if (damage < 0)
{
damage = 0;
}

// Dmg bonusses in PvP fight
if(isPvP)
{
if(skill == null)
damage *= attacker.calcStat(Stats.PVP_PHYSICAL_DMG, 1, null, null);
else
damage *= attacker.calcStat(Stats.PVP_PHYS_SKILL_DMG, 1, null, null);
}

// Physical skill dmg boost
if (skill != null)
damage *= attacker.calcStat(Stats.PHYSICAL_SKILL_POWER, 1, null, null);

damage *= calcElemental(attacker, target, skill);
if (target instanceof L2Attackable)
{
if (isBow)
{
if (skill != null)
damage *= attacker.calcStat(Stats.PVE_BOW_SKILL_DMG, 1, null, null);
else
damage *= attacker.calcStat(Stats.PVE_BOW_DMG, 1, null, null);
}
else
damage *= attacker.calcStat(Stats.PVE_PHYSICAL_DMG, 1, null, null);
if (!target.isRaid() && !target.isRaidMinion()
&& target.getLevel() >= Config.MIN_NPC_LVL_DMG_PENALTY && attacker.getActingPlayer() != null
&& (target.getLevel() - attacker.getActingPlayer().getLevel()) >= 2)
{
int lvlDiff = target.getLevel() - attacker.getActingPlayer().getLevel() - 1;
if (skill != null)
{
if (lvlDiff > Config.NPC_SKILL_DMG_PENALTY.size())
damage *= Config.NPC_SKILL_DMG_PENALTY.get(Config.NPC_SKILL_DMG_PENALTY.size());
else
damage *= Config.NPC_SKILL_DMG_PENALTY.get(lvlDiff);
}
else if (crit)
{
if (lvlDiff > Config.NPC_CRIT_DMG_PENALTY.size())
damage *= Config.NPC_CRIT_DMG_PENALTY.get(Config.NPC_CRIT_DMG_PENALTY.size());
else
damage *= Config.NPC_CRIT_DMG_PENALTY.get(lvlDiff);
}
else
{
if (lvlDiff > Config.NPC_DMG_PENALTY.size())
damage *= Config.NPC_DMG_PENALTY.get(Config.NPC_DMG_PENALTY.size());
else
damage *= Config.NPC_DMG_PENALTY.get(lvlDiff);
}
}
}

/** Tenkai custom - apply damage modifiers for PvP on Ceriel **/
//if (Config.isServer(Config.PVP) && attacker instanceof L2PcInstance && target instanceof L2PcInstance)
// damage *= pDamMultiplier((L2PcInstance) attacker);

return damage;
}

public static final double calcMagicDam(L2Character attacker, L2Character target, L2Skill skill, byte shld, boolean ss, boolean bss, boolean mcrit)
{
final boolean isPvP = (attacker instanceof L2Playable) && (target instanceof L2Playable);
final boolean isPvE = (attacker instanceof L2Playable) && (target instanceof L2Attackable);
double mAtk = attacker.getMAtk(target, skill);
double mDef = target.getMDef(attacker, skill);
// AI SpiritShot
if (attacker instanceof L2Npc)
{
if(((L2Npc)attacker)._spiritshotcharged)
{
ss = true;
}
else
ss = false;
((L2Npc)attacker)._spiritshotcharged = false;
}
// --------------------------------
// Pvp bonuses for def
if(isPvP)
{
if(skill.isMagic())
mDef *= target.calcStat(Stats.PVP_MAGICAL_DEF, 1, null, null);
else
mDef *= target.calcStat(Stats.PVP_PHYS_SKILL_DEF, 1, null, null);
}

switch (shld)
{
case SHIELD_DEFENSE_SUCCEED:
mDef += target.getShldDef(); // kamael
break;
case SHIELD_DEFENSE_PERFECT_BLOCK: // perfect block
return 1;
}

if (bss)
mAtk *= 4;
else if (ss)
mAtk *= 2;

double damage = 91 * Math.sqrt(mAtk) / mDef * skill.getPower(attacker, target, isPvP, isPvE);

// Failure calculation
if (Config.ALT_GAME_MAGICFAILURES && !calcMagicSuccess(attacker, target, skill))
{
if (attacker instanceof L2PcInstance)
{
if (calcMagicSuccess(attacker, target, skill)
&& (target.getLevel() - attacker.getLevel()) <= 9)
{
if (skill.getSkillType() == L2SkillType.DRAIN)
attacker.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DRAIN_HALF_SUCCESFUL));
else
attacker.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ATTACK_FAILED));

damage /= 2;
}
else
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_RESISTED_YOUR_S2);
sm.addCharName(target);
sm.addSkillName(skill);
attacker.sendPacket(sm);

damage = 1;
}
}

if (target instanceof L2PcInstance)
{
if (skill.getSkillType() == L2SkillType.DRAIN)
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.RESISTED_C1_DRAIN);
sm.addCharName(attacker);
target.sendPacket(sm);
}
else
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.RESISTED_C1_MAGIC);
sm.addCharName(attacker);
target.sendPacket(sm);
}
}
}
else if (mcrit)
{
if (attacker instanceof L2PcInstance && target instanceof L2PcInstance)
damage *= 2; // Tenkai "custom": from 2.5 to 2
else
damage *= 2.5; // Tenkai "custom": from 3 to 2.5

damage *= attacker.calcStat(Stats.MAGIC_CRIT_DMG, 1, null, null);
damage += attacker.calcStat(Stats.MAGIC_CRIT_DMG_ADD, 0, null, null);
}

/** Tenkai custom - summon boost for Ceriel **/
if (Config.isServer(Config.PVP))
{
/*if (target instanceof L2Summon && attacker instanceof L2PcInstance // Player attacks a summon
&& !((L2Summon)target).getOwner().getCurrentClass().equalsOrChildOf(ClassId.darkAvenger) // Summon is not a Dark Panther
&& !((L2Summon)target).getOwner().getCurrentClass().equalsOrChildOf(ClassId.necromancer) // Summon is not a Necro summon
&& !((L2Summon)target).getOwner().getCurrentClass().equalsOrChildOf(ClassId.paladin)) // Summon is not a Imperial Phoenix
{
damage *= 0.5;
}

if (attacker instanceof L2Summon && target instanceof L2PcInstance // Summon attacks a player
&& !((L2Summon)attacker).getOwner().getCurrentClass().equalsOrChildOf(ClassId.darkAvenger) // Summon is not a Dark Panther
&& !((L2Summon)attacker).getOwner().getCurrentClass().equalsOrChildOf(ClassId.necromancer) // Summon is not a Necro summon
&& !((L2Summon)attacker).getOwner().getCurrentClass().equalsOrChildOf(ClassId.paladin)) // Summon is not a Imperial Phoenix
{
damage *= 2;
}

if (attacker instanceof L2PcInstance && target instanceof L2PcInstance)
damage *= mDamMultiplier((L2PcInstance)attacker);*/

//if (attacker instanceof L2PcInstance)
// damage *= 0.85;
}

// Weapon random damage
damage *= attacker.getRandomDamageMultiplier();

// Pvp bonuses for dmg
if (isPvP)
{
if(skill.isMagic())
damage *= attacker.calcStat(Stats.PVP_MAGICAL_DMG, 1, null, null);
else
damage *= attacker.calcStat(Stats.PVP_PHYS_SKILL_DMG, 1, null, null);
}

damage *= attacker.calcStat(Stats.MAGIC_SKILL_POWER, 1, null, null);
// CT2.3 general magic vuln
damage *= target.calcStat(Stats.MAGIC_DAMAGE_VULN, 1, null, null);

damage *= calcElemental(attacker, target, skill);

if (target instanceof L2Attackable)
{
damage *= attacker.calcStat(Stats.PVE_MAGICAL_DMG, 1, null, null);
if (!target.isRaid() && !target.isRaidMinion()
&& target.getLevel() >= Config.MIN_NPC_LVL_DMG_PENALTY && attacker.getActingPlayer() != null
&& (target.getLevel() - attacker.getActingPlayer().getLevel()) >= 2)
{
int lvlDiff = target.getLevel() - attacker.getActingPlayer().getLevel() - 1;
if (lvlDiff > Config.NPC_SKILL_DMG_PENALTY.size())
damage *= Config.NPC_SKILL_DMG_PENALTY.get(Config.NPC_SKILL_DMG_PENALTY.size());
else
damage *= Config.NPC_SKILL_DMG_PENALTY.get(lvlDiff);
}
}

return damage;
}

public static final double calcMagicDam(L2CubicInstance attacker, L2Character target, L2Skill skill, boolean mcrit, byte shld)
{
// Current info include mAtk in the skill power.
// double mAtk = attacker.getMAtk();
final boolean isPvP = (target instanceof L2Playable);
final boolean isPvE = (target instanceof L2Attackable);
double mDef = target.getMDef(attacker.getOwner(), skill);

switch (shld)
{
case SHIELD_DEFENSE_SUCCEED:
mDef += target.getShldDef(); // kamael
break;
case SHIELD_DEFENSE_PERFECT_BLOCK: // perfect block
return 1;
}

double damage = 91 /* * Math.sqrt(mAtk)*/ / mDef * skill.getPower(isPvP, isPvE);
L2PcInstance owner = attacker.getOwner();
// Failure calculation
if (Config.ALT_GAME_MAGICFAILURES && !calcMagicSuccess(owner, target, skill))
{
if (calcMagicSuccess(owner, target, skill) && (target.getLevel() - skill.getMagicLevel()) <= 9){
if (skill.getSkillType() == L2SkillType.DRAIN)
owner.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DRAIN_HALF_SUCCESFUL));
else
owner.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.ATTACK_FAILED));

damage /= 2;
}
else
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_RESISTED_YOUR_S2);
sm.addCharName(target);
sm.addSkillName(skill);
owner.sendPacket(sm);

damage = 1;
}

if (target instanceof L2PcInstance)
{
if (skill.getSkillType() == L2SkillType.DRAIN)
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.RESISTED_C1_DRAIN);
sm.addCharName(owner);
target.sendPacket(sm);
}
else
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.RESISTED_C1_MAGIC);
sm.addCharName(owner);
target.sendPacket(sm);
}
}
}
else if (mcrit)
damage *= 3;

// CT2.3 general magic vuln
damage *= target.calcStat(Stats.MAGIC_DAMAGE_VULN, 1, null, null);

damage *= calcElemental(owner, target, skill);

if (target instanceof L2Attackable)
{
damage *= attacker.getOwner().calcStat(Stats.PVE_MAGICAL_DMG, 1, null, null);
if (!target.isRaid() && !target.isRaidMinion()
&& target.getLevel() >= Config.MIN_NPC_LVL_DMG_PENALTY && attacker.getOwner() != null
&& (target.getLevel() - attacker.getOwner().getLevel()) >= 2)
{
int lvlDiff = target.getLevel() - attacker.getOwner().getLevel() - 1;
if (lvlDiff > Config.NPC_SKILL_DMG_PENALTY.size())
damage *= Config.NPC_SKILL_DMG_PENALTY.get(Config.NPC_SKILL_DMG_PENALTY.size());
else
damage *= Config.NPC_SKILL_DMG_PENALTY.get(lvlDiff);
}
}

return damage;
}

/** Returns true in case of critical hit */
public static final boolean calcCrit(double rate, L2Character target)
{
final boolean success = rate > Rnd.get(1000);

// support for critical damage evasion
if (success)
{
if (target == null)
return true; // no effect

// little weird, but remember what CRIT_DAMAGE_EVASION > 1 increase chances to _evade_ crit hits
return Rnd.get((int)target.getStat().calcStat(Stats.CRIT_DAMAGE_EVASION, 100, null, null)) < 100;
}
return success;
}
/** Calculate value of blow success */
public static final boolean calcBlow(L2Character activeChar, L2Character target, int chance)
{
return activeChar.calcStat(Stats.BLOW_RATE, chance*(1.0+(activeChar.getDEX()-20)/100), target, null)>Rnd.get(100);
}
/** Calculate value of lethal chance */
public static final double calcLethal(L2Character activeChar, L2Character target, int baseLethal, int magiclvl)
{
double chance = 0;
if (magiclvl > 0)
{
int delta = ((magiclvl + activeChar.getLevel()) / 2) - 1 - target.getLevel();

// delta [-3,infinite)
if (delta >= -3)
{
chance = (baseLethal * ((double) activeChar.getLevel() / target.getLevel()));
}
// delta [-9, -3[
else if (delta < -3 && delta >= -9)
{
// baseLethal
// chance = -1 * -----------
// (delta / 3)
chance = (-3) * (baseLethal / (delta));
}
//delta [-infinite,-9[
else
{
chance = baseLethal / 15;
}
}
else
{
chance = (baseLethal * ((double) activeChar.getLevel() / target.getLevel()));
}
return 10 * activeChar.calcStat(Stats.LETHAL_RATE, chance, target, null);
}

public static final boolean calcLethalHit(L2Character activeChar, L2Character target, L2Skill skill)
{
if (!target.isRaid()
&& !(target instanceof L2DoorInstance)
&& !(target instanceof L2Npc && ((L2Npc) target).getNpcId() == 35062))
{
// 2nd lethal effect activate (cp,hp to 1 or if target is npc then hp to 1)
if (skill.getLethalChance2() > 0 && Rnd.get(1000) < calcLethal(activeChar, target, skill.getLethalChance2(),skill.getMagicLevel()))
{
if (target instanceof L2Npc)
target.reduceCurrentHp(target.getCurrentHp() - 1, activeChar, skill);
else if (target instanceof L2PcInstance) // If is a active player set his HP and CP to 1
{
L2PcInstance player = (L2PcInstance) target;
if (!player.isInvul())
{
if (!(activeChar instanceof L2PcInstance &&
(((L2PcInstance)activeChar).isGM() && !((L2PcInstance)activeChar).getAccessLevel().canGiveDamage())))
{
player.setCurrentHp(1);
player.setCurrentCp(1);
player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.LETHAL_STRIKE_SUCCESSFUL));
}
}
}
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.LETHAL_STRIKE));
}
else if (skill.getLethalChance1() > 0 && Rnd.get(1000) < calcLethal(activeChar, target, skill.getLethalChance1(),skill.getMagicLevel()))
{
if (target instanceof L2PcInstance)
{
L2PcInstance player = (L2PcInstance) target;
if (!player.isInvul())
{
if (!(activeChar instanceof L2PcInstance &&
(((L2PcInstance)activeChar).isGM() && !((L2PcInstance)activeChar).getAccessLevel().canGiveDamage())))
{
player.setCurrentCp(1); // Set CP to 1
player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CP_DISAPPEARS_WHEN_HIT_WITH_A_HALF_KILL_SKILL));
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CP_SIPHON));
}
}
}
//TODO: remove half kill since SYSMsg got changed.
else if (target instanceof L2Npc) // If is a monster remove first damage and after 50% of current hp
{
target.reduceCurrentHp(target.getCurrentHp() / 2, activeChar, skill);
activeChar.sendMessage("Half Kill!");
}
}
else
return false;
}
else
return false;

return true;
}

public static final boolean calcMCrit(double mRate)
{
return mRate > Rnd.get(1000);
}

/** Returns true in case when ATTACK is canceled due to hit */
public static final boolean calcAtkBreak(L2Character target, double dmg)
{
if (target.getFusionSkill() != null)
return true;

double init = 0;

if (Config.ALT_GAME_CANCEL_CAST && target.isCastingNow())
init = 15;
if (Config.ALT_GAME_CANCEL_BOW && target.isAttackingNow())
{
L2Weapon wpn = target.getActiveWeaponItem();
if (wpn != null && wpn.getItemType() == L2WeaponType.BOW)
init = 15;
}

if (target.isRaid() || target.isInvul() || init <= 0)
return false; // No attack break

// Chance of break is higher with higher dmg
//init += Math.sqrt(13 * dmg);
init = Math.sqrt(dmg / target.getMaxVisibleHp()) * 100; // Tenkai custom

// Chance is affected by target MEN
init -= (BaseStats.MEN.calcBonus(target) * 100 - 100);

// Calculate all modifiers for ATTACK_CANCEL
double rate = target.calcStat(Stats.ATTACK_CANCEL, init, null, null);

// Adjust the rate to be between 1 and 99
if (rate > 99)
rate = 99;
else if (rate < 1)
rate = 1;

return Rnd.get(100) < rate;
}

/** Calculate delay (in milliseconds) before next ATTACK */
public static final int calcPAtkSpd(L2Character attacker, L2Character target, double rate)
{
// measured Oct 2006 by Tank6585, formula by Sami
// attack speed 312 equals 1500 ms delay... (or 300 + 40 ms delay?)
if(rate < 2) return 2700;
else return (int)(470000/rate);
}

/** Calculate delay (in milliseconds) for skills cast */
public static final int calcAtkSpd(L2Character attacker, L2Skill skill, double skillTime)
{
if (skill.isMagic()) return (int) (skillTime * 333 / attacker.getMAtkSpd());
return (int) (skillTime * 333 / attacker.getPAtkSpd());
}

/** Returns true if hit missed (target evaded)
* Formula based on http://l2p.l2wh.com/nonskillattacks.html
**/
public static boolean calcHitMiss(L2Character attacker, L2Character target)
{
int chance = (80 + (2 * (attacker.getAccuracy() - target.getEvasionRate(attacker))))*10;

// Get additional bonus from the conditions when you are attacking
chance *= hitConditionBonus.getConditionBonus(attacker, target);

chance = Math.max(chance, 200);
chance = Math.min(chance, 980);

return chance < Rnd.get(1000);
}

public static boolean __calcHitMiss(L2Character attacker, L2Character target)
{
int delta = attacker.getAccuracy() - target.getEvasionRate(attacker);
int chance;
if (delta >= 10) chance = 980;
else
{
switch (delta)
{
case 9: chance = 975; break;
case 8: chance = 970; break;
case 7: chance = 965; break;
case 6: chance = 960; break;
case 5: chance = 955; break;
case 4: chance = 945; break;
case 3: chance = 935; break;
case 2: chance = 925; break;
case 1: chance = 915; break;
case 0: chance = 905; break;
case -1: chance = 890; break;
case -2: chance = 875; break;
case -3: chance = 860; break;
case -4: chance = 845; break;
case -5: chance = 830; break;
case -6: chance = 815; break;
case -7: chance = 800; break;
case -8: chance = 785; break;
case -9: chance = 770; break;
case -10: chance = 755; break;
case -11: chance = 735; break;
case -12: chance = 715; break;
case -13: chance = 695; break;
case -14: chance = 675; break;
case -15: chance = 655; break;
case -16: chance = 625; break;
case -17: chance = 595; break;
case -18: chance = 565; break;
case -19: chance = 535; break;
case -20: chance = 505; break;
case -21: chance = 455; break;
case -22: chance = 405; break;
case -23: chance = 355; break;
case -24: chance = 305; break;
default: chance = 275;
}
if(!attacker.isInFrontOfTarget())
{
if(attacker.isBehindTarget())
chance *= 1.2;
else // side
chance *= 1.1;
if (chance > 980)
chance = 980;
}
}
return chance < Rnd.get(1000);
}

/**
* Returns:<br>
* 0 = shield defense doesn't succeed<br>
* 1 = shield defense succeed<br>
* 2 = perfect block<br>
*
* @param attacker
* @param target
* @param sendSysMsg
* @return
*/
public static byte calcShldUse(L2Character attacker, L2Character target, L2Skill skill, boolean sendSysMsg)
{
if (skill != null && skill.ignoreShield())
return 0;

L2Item item = target.getSecondaryWeaponItem();
if (item == null || !(item instanceof L2Armor) || ((L2Armor)item).getItemType() == L2ArmorType.SIGIL)
return 0;

double shldRate = target.calcStat(Stats.SHIELD_RATE, 0, attacker, null)
* BaseStats.DEX.calcBonus(target);
if (shldRate == 0.0)
return 0;

int degreeside = (int) target.calcStat(Stats.SHIELD_DEFENCE_ANGLE, 0, null, null) + 120;
if (degreeside < 360 && (!target.isFacing(attacker, degreeside)))
return 0;

byte shldSuccess = SHIELD_DEFENSE_FAILED;
// if attacker
// if attacker use bow and target wear shield, shield block rate is multiplied by 1.3 (30%)
L2Weapon at_weapon = attacker.getActiveWeaponItem();
if (at_weapon != null && at_weapon.getItemType() == L2WeaponType.BOW)
shldRate *= 1.3;

if (shldRate > 0 && 100 - Config.ALT_PERFECT_SHLD_BLOCK < Rnd.get(100))
{
shldSuccess = SHIELD_DEFENSE_PERFECT_BLOCK;
}
else if (shldRate > Rnd.get(100))
{
shldSuccess = SHIELD_DEFENSE_SUCCEED;
}

if (sendSysMsg && target instanceof L2PcInstance)
{
L2PcInstance enemy = (L2PcInstance)target;

switch (shldSuccess)
{
case SHIELD_DEFENSE_SUCCEED:
enemy.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.SHIELD_DEFENCE_SUCCESSFULL));
break;
case SHIELD_DEFENSE_PERFECT_BLOCK:
enemy.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_EXCELLENT_SHIELD_DEFENSE_WAS_A_SUCCESS));
break;
}
}

return shldSuccess;
}

public static byte calcShldUse(L2Character attacker, L2Character target, L2Skill skill)
{
return calcShldUse(attacker, target, skill, true);
}

public static byte calcShldUse(L2Character attacker, L2Character target)
{
return calcShldUse(attacker, target, null, true);
}

public static boolean calcMagicAffected(L2Character actor, L2Character target, L2Skill skill)
{
// TODO: CHECK/FIX THIS FORMULA UP!!
L2SkillType type = skill.getSkillType();
double defence = 0;
if (skill.isActive() && skill.isOffensive() && !skill.isNeutral())
defence = target.getMDef(actor, skill);

double attack = 2 * actor.getMAtk(target, skill) * (1 + calcSkillVulnerability(actor, target, skill) / 100);
double d = (attack - defence)/(attack + defence);
if (target.isRaid())
{
switch (type)
{
case CONFUSION:
case MUTE:
case PARALYZE:
case ROOT:
case FEAR:
case SLEEP:
case STUN:
case DEBUFF:
case AGGDEBUFF:
if ( d > 0 && Rnd.get(1000) == 1)
return true;
else
return false;
}
}

d += 0.5 * Rnd.nextGaussian();
return d > 0;
}

public static double calcSkillVulnerability(L2Character attacker, L2Character target, L2Skill skill)
{
double multiplier = 0; // initialize...

// Get the skill type to calculate its effect in function of base stats
// of the L2Character target
if (skill != null)
{
// first, get the natural template vulnerability values for the target
Stats stat = skill.getStat();
if (stat != null)
{
switch (stat)
{
case AGGRESSION:
multiplier = target.getTemplate().baseAggressionVuln;
break;
case BLEED:
multiplier = target.getTemplate().baseBleedVuln;
break;
case POISON:
multiplier = target.getTemplate().basePoisonVuln;
break;
case STUN:
multiplier = target.getTemplate().baseStunVuln;
break;
case ROOT:
multiplier = target.getTemplate().baseRootVuln;
break;
case MOVEMENT:
multiplier = target.getTemplate().baseMovementVuln;
break;
case SLEEP:
multiplier = target.getTemplate().baseSleepVuln;
break;
}
}

// Finally, calculate skilltype vulnerabilities
L2SkillType type = skill.getSkillType();

// For additional effects (like STUN, SHOCK, PARALYZE...) on damage skills
switch (type)
{
case PDAM:
case MDAM:
case BLOW:
case DRAIN:
case CHARGEDAM:
case FATAL:
case DEATHLINK:
case CPDAM:
case MANADAM:
case CPDAMPERCENT:
type = skill.getEffectType();
}

multiplier = calcSkillTypeVulnerability(multiplier, target, type);
}
return multiplier;
}

public static double calcSkillTypeVulnerability(double multiplier, L2Character target, L2SkillType type)
{
if (type != null)
{
switch (type)
{
case BLEED:
multiplier = target.calcStat(Stats.BLEED_VULN, multiplier, target, null);
break;
case POISON:
multiplier = target.calcStat(Stats.POISON_VULN, multiplier, target, null);
break;
case STUN:
multiplier = target.calcStat(Stats.STUN_VULN, multiplier, target, null);
break;
case PARALYZE:
multiplier = target.calcStat(Stats.PARALYZE_VULN, multiplier, target, null);
break;
case ROOT:
multiplier = target.calcStat(Stats.ROOT_VULN, multiplier, target, null);
break;
case SLEEP:
multiplier = target.calcStat(Stats.SLEEP_VULN, multiplier, target, null);
break;
case MUTE:
case FEAR:
case BETRAY:
case AGGDEBUFF:
case ERASE:
case CONFUSION:
case CONFUSE_MOB_ONLY:
multiplier = target.calcStat(Stats.DERANGEMENT_VULN, multiplier, target, null);
break;
case DEBUFF:
multiplier = target.calcStat(Stats.DEBUFF_VULN, multiplier, target, null);
break;
case BUFF:
multiplier = target.calcStat(Stats.BUFF_VULN, multiplier, target, null);
break;
case CANCEL:
multiplier = target.calcStat(Stats.CANCEL_VULN, multiplier, target, null);
break;
default:
}
}

return multiplier;
}

public static double calcSkillProficiency(L2Skill skill, L2Character attacker, L2Character target)
{
double multiplier = 0; // initialize...

if (skill != null)
{
// Calculate skilltype vulnerabilities
L2SkillType type = skill.getSkillType();

// For additional effects (like STUN, SHOCK, PARALYZE...) on damage skills
switch (type)
{
case PDAM:
case MDAM:
case BLOW:
case DRAIN:
case CHARGEDAM:
case FATAL:
case DEATHLINK:
case CPDAM:
case MANADAM:
case CPDAMPERCENT:
type = skill.getEffectType();
}

multiplier = calcSkillTypeProficiency(multiplier, attacker, target, type);
}

return multiplier;
}

public static double calcSkillTypeProficiency(double multiplier, L2Character attacker, L2Character target, L2SkillType type)
{
if (type != null)
{
switch (type)
{
case BLEED:
multiplier = attacker.calcStat(Stats.BLEED_PROF, multiplier, target, null);
break;
case POISON:
multiplier = attacker.calcStat(Stats.POISON_PROF, multiplier, target, null);
break;
case STUN:
multiplier = attacker.calcStat(Stats.STUN_PROF, multiplier, target, null);
break;
case PARALYZE:
multiplier = attacker.calcStat(Stats.PARALYZE_PROF, multiplier, target, null);
break;
case ROOT:
multiplier = attacker.calcStat(Stats.ROOT_PROF, multiplier, target, null);
break;
case SLEEP:
multiplier = attacker.calcStat(Stats.SLEEP_PROF, multiplier, target, null);
break;
case MUTE:
case FEAR:
case BETRAY:
case AGGDEBUFF:
case ERASE:
case CONFUSION:
case CONFUSE_MOB_ONLY:
multiplier = attacker.calcStat(Stats.DERANGEMENT_PROF, multiplier, target, null);
break;
case DEBUFF:
multiplier = attacker.calcStat(Stats.DEBUFF_PROF, multiplier, target, null);
break;
case CANCEL:
multiplier = attacker.calcStat(Stats.CANCEL_PROF, multiplier, target, null);
break;
default:

}
}

return multiplier;
}

public static double calcSkillStatModifier(L2Skill skill, L2Character target)
{
final BaseStats saveVs = skill.getSaveVs();
if (saveVs == null)
return 1;

return 1 / saveVs.calcBonus(target);
}

public static int calcLvlDependModifier(L2Character attacker, L2Character target, L2Skill skill)
{
if (skill.getLevelDepend() == 0)
return 0;

final int attackerMod;
if (skill.getMagicLevel() > 0)
attackerMod = skill.getMagicLevel();
else
attackerMod = attacker.getLevel();

final int delta = attackerMod - target.getLevel();
int deltamod = delta / 5;
deltamod = deltamod * 5;
if (deltamod != delta)
{
if (delta < 0)
deltamod -= 5;
else
deltamod += 5;
}

return deltamod;
}

public static int calcElementModifier(L2Character attacker, L2Character target, L2Skill skill)
{
final byte element = skill.getElement();

if (element == Elementals.NONE)
return 0;

int result = skill.getElementPower();
if (attacker.getAttackElement() == element)
result += attacker.getAttackElementValue(element);

result -= target.getDefenseElementValue(element);

if (result < 0)
return 0;

return Math.round((float)result / 10);
}

public static boolean calcEffectSuccess(L2Character attacker, L2Character target, EffectTemplate effect, L2Skill skill, byte shld, boolean ss, boolean sps, boolean bss)
{
// overriding L2J Formula
return tenkaiCalcEffectSuccess(attacker, target, effect, skill, shld, ss, sps, bss);

/*
final boolean isPvP = (attacker instanceof L2Playable) && (target instanceof L2Playable);

final L2SkillType type = effect.effectType;
final int value = (int)effect.effectPower;
if (type == null)
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" effect ignoring resists");

return Rnd.get(100) < value;
}
else if (type.equals(L2SkillType.CANCEL)) // CANCEL-type effects land always
return true;

if (shld == SHIELD_DEFENSE_PERFECT_BLOCK) // perfect block
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" effect blocked by shield");

return false;
}

double statModifier = calcSkillStatModifier(skill, target);

// Calculate BaseRate.
int rate = (int) (value * statModifier);

// Add Matk/Mdef Bonus
double mAtkModifier = 0;
int ssModifier = 0;
if (skill.isMagic())
{
mAtkModifier = target.getMDef(target, skill);
if (shld == SHIELD_DEFENSE_SUCCEED)
mAtkModifier += target.getShldDef();

// Add Bonus for Sps/SS
if (bss)
ssModifier = 4;
else if (sps)
ssModifier = 2;
else
ssModifier = 1;

mAtkModifier = 14 * Math.sqrt(ssModifier * attacker.getMAtk(target, skill)) / mAtkModifier;

if (Config.isServer(Config.PVP) && attacker instanceof L2PcInstance && !attacker.getActingPlayer().getClassId().isMage())
rate = (int) (rate * (mAtkModifier * 5)); // Compensating fighters' low m.atk on Ceriel to make their MDAM debuffs land
else
rate = (int) (rate * mAtkModifier);
}

// Resists
double vulnModifier = calcSkillTypeVulnerability(0, target, type);
double profModifier = calcSkillTypeProficiency(0, attacker, target, type);
double res = vulnModifier + profModifier;
double resMod = 1;
if (res != 0)
{
if (res < 0)
{
//resMod = 1 - 0.075 * res; LOL
resMod = 1 - 0.03 * res;
resMod = 1 / resMod;
}
else
resMod = 1 + 0.02 * res;

rate *= resMod;
}

int elementModifier = calcElementModifier(attacker, target, skill);
// Way too much impact of element on debuff land rates
if (Config.isServer(Config.PVP))
rate += 0.25 * elementModifier;
else
rate += elementModifier; // TODO: test this impact for Lionna

//lvl modifier.
int deltamod = calcLvlDependModifier(attacker, target, skill);
rate += deltamod;

if (rate > skill.getMaxChance())
rate = skill.getMaxChance();
else if (rate < skill.getMinChance())
rate = skill.getMinChance();

// General debuff cap (allows a maximum of 39% land rate)
if (rate > 25 && isPvP)
rate = 25 + (rate - 25) / 5;

if (attacker.isDebug() || Config.DEVELOPER)
{
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat,
skill.getName(),
" eff.type:", type.toString(),
" power:", String.valueOf(value),
" stat:", String.format("%1.2f", statModifier),
" res:", String.format("%1.2f", resMod), "(",
String.format("%1.2f", profModifier), "/",
String.format("%1.2f", vulnModifier),
") elem:", String.valueOf(elementModifier),
" mAtk:", String.format("%1.2f", mAtkModifier),
" ss:", String.valueOf(ssModifier),
" lvl:", String.valueOf(deltamod),
" total:", String.valueOf(rate)
);
final String result = stat.toString();
if (attacker.isDebug())
attacker.sendDebugMessage(result);
if (Config.DEVELOPER)
_log.info(result);
}

if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isLandRates())
attacker.sendMessage("Your " + skill.getName() + "'s effect had a " + rate + "% chance to land, with a " + skill.getPower() + " base land rate.");
if (target instanceof L2PcInstance && ((L2PcInstance)target).isLandRates())
((L2PcInstance)target).sendMessage("This " + skill.getName() + "'s effect had a " + rate + "% chance to land over you, with a " + skill.getPower() + " base land rate.");

return (Rnd.get(100) < rate);
*/
}

private static boolean tenkaiCalcEffectSuccess(L2Character attacker, L2Character target, EffectTemplate effect, L2Skill skill, byte shld, boolean ss, boolean sps, boolean bss)
{
int rate = 0;

// Determine whether it's a skill used in PvP
final boolean isPvP = (attacker instanceof L2Playable) && (target instanceof L2Playable);
final boolean isPvE = (attacker instanceof L2Playable) && (target instanceof L2Attackable);

// We don't want our raids debuffed by some little player char
if ((target instanceof L2RaidBossInstance || target instanceof L2GrandBossInstance)
&& (effect.effectType == L2SkillType.FEAR
|| effect.effectType == L2SkillType.STUN
|| effect.effectType == L2SkillType.MUTE
|| effect.effectType == L2SkillType.ROOT
|| effect.effectType == L2SkillType.SLEEP))
return false;

// Special case for resist ignoring skills
if (skill.ignoreResists())
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" ignoring resists");

return (Rnd.get(100) < skill.getPower(isPvP, isPvE));
}

// Perfect shield block can protect from debuff
if (shld == SHIELD_DEFENSE_PERFECT_BLOCK) // perfect block
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" blocked by shield");

return false;
}

// Get power of the skill
int power = (int) effect.effectPower;

// Decrease base rate for olympiad games to make matches not too debuff dependent
if ((attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isInOlympiadMode())
|| (attacker instanceof L2SummonInstance && ((L2SummonInstance)attacker).isInOlympiadMode()))
{
power /= 2;
}

// Consider stats' influence
double statMod = calcSkillStatModifier(skill, target);

// Calculate reduction/boost depending on m.atk-m.def-relation for magical skills
double mAtkMod = 1.;
if (skill.isMagic())
{
int ssModifier = 0;

mAtkMod = target.getMDef(target, skill);
if (shld == SHIELD_DEFENSE_SUCCEED)
mAtkMod += target.getShldDef();

// Add Bonus for Sps/SS
if (bss)
ssModifier = 4;
else if (sps)
ssModifier = 2;
else
ssModifier = 1;

mAtkMod = 20 * Math.sqrt(ssModifier * attacker.getMAtk(target, skill)) / mAtkMod;
}

// Resists and proficiency boosts (epics etc.)
double vulnModifier = 0.;
double profModifier = 0.;

vulnModifier = calcSkillTypeVulnerability(0, target, effect.effectType);
profModifier = calcSkillTypeProficiency(0, attacker, target, effect.effectType);

double res = vulnModifier + profModifier;
double resMod = 1;
if (res != 0)
{
if (res < 0)
{
resMod = 1 - 0.02 * res;
resMod = 1 / resMod;
}
else
resMod = 1 + 0.02 * res;
}

// Bonus for skills with element
int eleMod = calcElementModifier(attacker, target, skill);

// Bonus for level difference between target and caster/skill level
int lvlMod = calcLvlDependModifier(attacker, target, skill);

// Finally, the calculation of the land rate in ONE customizable formula, to take some confusion out of this mess
// Factors to consider: power, statMod, resMod, eleMod, lvlMod
rate = (int) ((power * statMod + eleMod + lvlMod) * resMod);

if (skill.isMagic()) // And eventually the influence of m.atk/m.def relation between attacker and target
{
if (mAtkMod < 1)
rate *= Math.max(mAtkMod, 0.8) * 0.6;
else
rate *= Math.min(mAtkMod, 1.5) * 0.6; // This implicitly reduces the land rate of all magical debuffs
}

if (skill.getMaxChance() < rate)
rate = skill.getMaxChance();

if (rate > 90)
rate = 90;
else if (rate < 10 && skill.getMagicLevel() < 85)
rate = 10;

// Feedback for .landrates
if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isLandRates())
attacker.sendMessage("Your " + skill.getName() + " had a " + rate
+ "% chance to land, with a " + effect.effectPower + " base land rate.");
if (target instanceof L2PcInstance && ((L2PcInstance)target).isLandRates())
((L2PcInstance)target).sendMessage("This " + skill.getName() + " had a " + rate
+ "% chance to land over you, with a " + effect.effectPower + " base land rate.");

// For land rate observation by GM
if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isLandrateObservationActive())
{
for (L2PcInstance obs : ((L2PcInstance)attacker).getLandrateObservers())
{
obs.sendMessage("Player:" + attacker.getName() + " Target: " + target.getName() + " Skill:" + skill.getName()
+" Effect:" + effect.effectType +" Rate:" + rate + "%"
+" statMod:" + Math.floor(statMod*1000)/1000 + " eleMod:" + eleMod + " lvlMod:" + lvlMod
+" resMod:" + Math.floor(resMod*1000)/1000 + (skill.isMagic() ? " mAtkMod: " + Math.floor(mAtkMod*1000)/1000 : ""));
}
}

return (Rnd.get(100) < rate);
}

public static boolean calcSkillSuccess(L2Character attacker, L2Character target, L2Skill skill, byte shld, boolean ss, boolean sps, boolean bss)
{
// Overriding L2J formula
return tenkaiCalcSkillSuccess(attacker, target, skill, shld, ss, sps, bss);

/*
final boolean isPvP = (attacker instanceof L2Playable) && (target instanceof L2Playable);
if (skill.ignoreResists())
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" ignoring resists");

return (Rnd.get(100) < skill.getPower(isPvP));
}

if (shld == SHIELD_DEFENSE_PERFECT_BLOCK) // perfect block
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" blocked by shield");

return false;
}

int value = (int) skill.getPower(isPvP);
double statModifier = calcSkillStatModifier(skill, target);

// Calculate BaseRate.
int rate = (int) (value * statModifier);

// Add Matk/Mdef Bonus
double mAtkModifier = 0;
int ssModifier = 0;
if (skill.isMagic())
{
mAtkModifier = target.getMDef(target, skill);
if (shld == SHIELD_DEFENSE_SUCCEED)
mAtkModifier += target.getShldDef();

// Add Bonus for Sps/SS
if (bss)
ssModifier = 4;
else if (sps)
ssModifier = 2;
else
ssModifier = 1;

mAtkModifier = 14 * Math.sqrt(ssModifier * attacker.getMAtk(target, skill)) / mAtkModifier;

if (Config.isServer(Config.PVP) && attacker instanceof L2PcInstance && !attacker.getActingPlayer().getClassId().isMage())
rate = (int) (rate * (mAtkModifier * 1.5)); // Compensating fighters' low m.atk on Ceriel to make their magical debuffs land
else
rate = (int) (rate * mAtkModifier);
}

// Resists
double vulnModifier = calcSkillVulnerability(attacker, target, skill);
double profModifier = calcSkillProficiency(skill, attacker, target);
double res = vulnModifier + profModifier;
double resMod = 1;
if (res != 0)
{
if (res < 0)
{
//resMod = 1 - 0.075 * res; LOL
resMod = 1 - 0.01 * res;
resMod = 1 / resMod;
}
else
resMod = 1 + 0.02 * res;

rate *= resMod;
}

int elementModifier = calcElementModifier(attacker, target, skill);

// Way too much impact of element on debuff land rates
if (Config.isServer(Config.PVP))
rate += 0.25 * elementModifier;
else
rate += elementModifier; // TODO: test this impact for Lionna

//lvl modifier.
int deltamod = calcLvlDependModifier(attacker, target, skill);
rate += deltamod;

if (rate > skill.getMaxChance())
rate = skill.getMaxChance();
else if (rate < skill.getMinChance())
rate = skill.getMinChance();

if (attacker.isDebug() || Config.DEVELOPER)
{
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat,
skill.getName(),
" type:", skill.getSkillType().toString(),
" power:", String.valueOf(value),
" stat:", String.format("%1.2f", statModifier),
" res:", String.format("%1.2f", resMod), "(",
String.format("%1.2f", profModifier), "/",
String.format("%1.2f", vulnModifier),
") elem:", String.valueOf(elementModifier),
" mAtk:", String.format("%1.2f", mAtkModifier),
" ss:", String.valueOf(ssModifier),
" lvl:", String.valueOf(deltamod),
" total:", String.valueOf(rate)
);
final String result = stat.toString();
if (attacker.isDebug())
attacker.sendDebugMessage(result);
if (Config.DEVELOPER)
_log.info(result);
}

// General debuff cap (allows a maximum of 39% land rate)
if (rate > 25 && isPvP)
rate = 25 + (rate - 25) / 5;

if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isLandRates())
attacker.sendMessage("Your " + skill.getName() + " had a " + rate + "% chance to land, with a " + skill.getPower() + " base land rate.");
if (target instanceof L2PcInstance && ((L2PcInstance)target).isLandRates())
((L2PcInstance)target).sendMessage("This " + skill.getName() + " had a " + rate + "% chance to land over you, with a " + skill.getPower() + " base land rate.");
return (Rnd.get(100) < rate);
*/
}

private static boolean tenkaiCalcSkillSuccess(L2Character attacker, L2Character target, L2Skill skill, byte shld, boolean ss, boolean sps, boolean bss)
{
int rate = 0;

// Determine whether it's a skill used in PvP
final boolean isPvP = (attacker instanceof L2Playable) && (target instanceof L2Playable);
final boolean isPvE = (attacker instanceof L2Playable) && (target instanceof L2Attackable);

// We don't want our raids debuffed by some little player char
if ((target instanceof L2RaidBossInstance || target instanceof L2GrandBossInstance)
&& (skill.getSkillType() == L2SkillType.FEAR
|| skill.getSkillType() == L2SkillType.STUN
|| skill.getSkillType() == L2SkillType.MUTE
|| skill.getSkillType() == L2SkillType.ROOT
|| skill.getSkillType() == L2SkillType.SLEEP))
return false;

// Special case for resist ignoring skills
if (skill.ignoreResists())
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" ignoring resists");

return (Rnd.get(100) < skill.getPower(isPvP, isPvE));
}

// Perfect shield block can protect from debuff
if (shld == SHIELD_DEFENSE_PERFECT_BLOCK) // perfect block
{
if (attacker.isDebug())
attacker.sendDebugMessage(skill.getName()+" blocked by shield");

return false;
}

// Get power of the skill
int power = (int) skill.getPower(isPvP, isPvE);

// Decrease base rate for olympiad games to make matches not too debuff dependent
if ((attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isInOlympiadMode())
|| (attacker instanceof L2SummonInstance && ((L2SummonInstance)attacker).isInOlympiadMode()))
{
power /= 2;
}

// Consider stats' influence
double statMod = calcSkillStatModifier(skill, target);

// Calculate reduction/boost depending on m.atk-m.def-relation for magical skills
double mAtkMod = 1.;
if (skill.isMagic())
{
int ssModifier = 0;

mAtkMod = target.getMDef(target, skill);
if (shld == SHIELD_DEFENSE_SUCCEED)
mAtkMod += target.getShldDef();

// Add Bonus for Sps/SS
if (bss)
ssModifier = 4;
else if (sps)
ssModifier = 2;
else
ssModifier = 1;

mAtkMod = 20 * Math.sqrt(ssModifier * attacker.getMAtk(target, skill)) / mAtkMod;
}

// Resists and proficiency boosts (epics etc.)
double vulnModifier = calcSkillVulnerability(attacker, target, skill);
double profModifier = calcSkillProficiency(skill, attacker, target);
double res = vulnModifier + profModifier;
double resMod = 1;
if (res != 0)
{
if (res < 0)
{
resMod = 1 - 0.02 * res;
resMod = 1 / resMod;
}
else
resMod = 1 + 0.02 * res;
}

// Bonus for skills with element
int eleMod = calcElementModifier(attacker, target, skill);

// Bonus for level difference between target and caster/skill level
int lvlMod = calcLvlDependModifier(attacker, target, skill);

// Finally, the calculation of the land rate in ONE customizable formula, to take some confusion out of this mess
// Factors to consider: power, statMod, resMod, eleMod, lvlMod
rate = (int) ((power * statMod + eleMod + lvlMod) * resMod);

if (skill.isMagic()) // And eventually the influence of m.atk/m.def relation between attacker and target
{
if (mAtkMod < 1)
rate *= Math.max(mAtkMod, 0.8) * 0.6;
else
rate *= Math.min(mAtkMod, 1.5) * 0.6; // This implicitly reduces the land rate of all magical debuffs
}

if (skill.getMaxChance() < rate)
rate = skill.getMaxChance();

if (rate > 90)
rate = 90;
else if (rate < 10 && skill.getMagicLevel() < 85)
rate = 10;

// Feedback for .landrates
if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isLandRates())
attacker.sendMessage("Your " + skill.getName() + " had a " + rate
+ "% chance to land, with a " + skill.getPower() + " base land rate.");
if (target instanceof L2PcInstance && ((L2PcInstance)target).isLandRates())
((L2PcInstance)target).sendMessage("This " + skill.getName() + " had a " + rate
+ "% chance to land over you, with a " + skill.getPower() + " base land rate.");

// For land rate observation by GM
if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).isLandrateObservationActive())
{
for (L2PcInstance obs : ((L2PcInstance)attacker).getLandrateObservers())
{
obs.sendMessage("Player:"+attacker.getName()+" Target: "+target.getName()+" Skill:"+skill.getName()
+" Rate:"+rate+"%"
+" statMod:" + Math.floor(statMod*1000)/1000 + " eleMod:" + eleMod + " lvlMod:" + lvlMod
+" resMod:" + Math.floor(resMod*1000)/1000 + (skill.isMagic() ? " mAtkMod: " + Math.floor(mAtkMod*1000)/1000 : ""));
}
}

return (Rnd.get(100) < rate);
}

public static boolean calcCubicSkillSuccess(L2CubicInstance attacker, L2Character target, L2Skill skill, byte shld)
{
if (shld == SHIELD_DEFENSE_PERFECT_BLOCK) // perfect block
return false;
final boolean isPvP = (target instanceof L2Playable);
final boolean isPvE = (target instanceof L2Attackable);

L2SkillType type = skill.getSkillType();

// these skills should not work on RaidBoss
if (target.isRaid())
{
switch (type)
{
case CONFUSION:
case ROOT:
case STUN:
case MUTE:
case FEAR:
case DEBUFF:
case PARALYZE:
case SLEEP:
case AGGDEBUFF:
return false;
}
}

// if target reflect this skill then the effect will fail
if (calcSkillReflect(target, skill) != SKILL_REFLECT_FAILED)
return false;

int value = (int) skill.getPower(isPvP, isPvE);
double statModifier = calcSkillStatModifier(skill, target);
int rate = (int) (value * statModifier);

// Add Matk/Mdef Bonus
double mAtkModifier = 0;
if (skill.isMagic())
{
mAtkModifier = target.getMDef(attacker.getOwner(), skill);
if (shld == SHIELD_DEFENSE_SUCCEED)
mAtkModifier += target.getShldDef();

mAtkModifier = Math.pow(attacker.getMAtk() / mAtkModifier, 0.2);

rate += (int) (mAtkModifier * 100) - 100;
}

// Resists
double vulnModifier = calcSkillVulnerability(attacker.getOwner(), target, skill);
double profModifier = calcSkillProficiency(skill, attacker.getOwner(), target);
double res = vulnModifier + profModifier;
double resMod = 1;
if (res != 0)
{
if (res < 0)
{
resMod = 1 - 0.075 * res;
resMod = 1 / resMod;
}
else
resMod = 1 + 0.02 * res;

rate *= resMod;
}

int elementModifier = calcElementModifier(attacker.getOwner(), target, skill);
rate += elementModifier;

//lvl modifier.
int deltamod = calcLvlDependModifier(attacker.getOwner(), target, skill);
rate += deltamod;

if (rate > skill.getMaxChance())
rate = skill.getMaxChance();
else if (rate < skill.getMinChance())
rate = skill.getMinChance();

if (attacker.getOwner().isDebug() || Config.DEVELOPER)
{
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat,
skill.getName(),
" type:", skill.getSkillType().toString(),
" power:", String.valueOf(value),
" stat:", String.format("%1.2f", statModifier),
" res:", String.format("%1.2f", resMod), "(",
String.format("%1.2f", profModifier), "/",
String.format("%1.2f", vulnModifier),
") elem:", String.valueOf(elementModifier),
" mAtk:", String.format("%1.2f", mAtkModifier),
" lvl:", String.valueOf(deltamod),
" total:", String.valueOf(rate)
);
final String result = stat.toString();
if (attacker.getOwner().isDebug())
attacker.getOwner().sendDebugMessage(result);
if (Config.DEVELOPER)
_log.info(result);
}
return (Rnd.get(100) < rate);
}

public static boolean calcMagicSuccess(L2Character attacker, L2Character target, L2Skill skill)
{
/** Tenkai Customization: no more Wind Strike against full grown chars **/
if (target.getLevel() - skill.getMagicLevel() > 40 && target instanceof L2PcInstance && attacker instanceof L2PcInstance)
return false;

// DS: remove skill magic level dependence from nukes
//int lvlDifference = (target.getLevel() - (skill.getMagicLevel() > 0 ? skill.getMagicLevel() : attacker.getLevel()));
int lvlDifference = (target.getLevel() - (skill.getSkillType() == L2SkillType.SPOIL ? skill.getMagicLevel() : attacker.getLevel()));
double lvlModifier = Math.pow(1.3, lvlDifference);
float targetModifier = 1;
if (target instanceof L2Attackable && !target.isRaid() && !target.isRaidMinion()
&& target.getLevel() >= Config.MIN_NPC_LVL_MAGIC_PENALTY && attacker.getActingPlayer() != null
&& (target.getLevel() - attacker.getActingPlayer().getLevel()) >= 3)
{
int lvlDiff = target.getLevel() - attacker.getActingPlayer().getLevel() - 2;
if (lvlDiff > Config.NPC_SKILL_CHANCE_PENALTY.size())
targetModifier = Config.NPC_SKILL_CHANCE_PENALTY.get(Config.NPC_SKILL_CHANCE_PENALTY.size());
else
targetModifier = Config.NPC_SKILL_CHANCE_PENALTY.get(lvlDiff);
}
// general magic resist
final double resModifier = target.calcStat(Stats.MAGIC_SUCCESS_RES, 1, null, skill);
final double failureModifier = attacker.calcStat(Stats.MAGIC_FAILURE_RATE, 1, target, skill);
int rate = 100 - Math.round((float)(lvlModifier * targetModifier * resModifier * failureModifier));

if (rate > skill.getMaxChance())
rate = skill.getMaxChance();
else if (rate < skill.getMinChance())
rate = skill.getMinChance();

if (attacker.isDebug() || Config.DEVELOPER)
{
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat,
skill.getName(),
" lvlDiff:", String.valueOf(lvlDifference),
" lvlMod:", String.format("%1.2f", lvlModifier),
" res:", String.format("%1.2f", resModifier),
" fail:", String.format("%1.2f", failureModifier),
" tgt:", String.valueOf(targetModifier),
" total:", String.valueOf(rate)
);
final String result = stat.toString();
if (attacker.isDebug())
attacker.sendDebugMessage(result);
if (Config.DEVELOPER)
_log.info(result);
}
return (Rnd.get(100) < rate);
}

public static double calcManaDam(L2Character attacker, L2Character target, L2Skill skill,
boolean ss, boolean bss)
{
// AI SpiritShot
if (attacker instanceof L2Npc)
{
if(((L2Npc)attacker)._spiritshotcharged)
{
ss = true;
}
else
ss = false;
((L2Npc)attacker)._spiritshotcharged = false;
}
//Mana Burnt = (SQR(M.Atk)*Power*(Target Max MP/97))/M.Def
double mAtk = attacker.getMAtk(target, skill);
double mDef = target.getMDef(attacker, skill);
final boolean isPvP = (attacker instanceof L2Playable) && (target instanceof L2Playable);
final boolean isPvE = (attacker instanceof L2Playable) && (target instanceof L2Attackable);
double mp = target.getMaxMp();
if (bss) mAtk *= 4;
else if (ss) mAtk *= 2;

double damage = (Math.sqrt(mAtk) * skill.getPower(attacker, target, isPvP, isPvE) * (mp/97)) / mDef;
damage *= (1 + calcSkillVulnerability(attacker, target, skill) / 100);
if (target instanceof L2Attackable)
{
damage *= attacker.calcStat(Stats.PVE_MAGICAL_DMG, 1, null, null);
if (!target.isRaid() && !target.isRaidMinion()
&& target.getLevel() >= Config.MIN_NPC_LVL_DMG_PENALTY && attacker.getActingPlayer() != null
&& (target.getLevel() - attacker.getActingPlayer().getLevel()) >= 2)
{
int lvlDiff = target.getLevel() - attacker.getActingPlayer().getLevel() - 1;
if (lvlDiff > Config.NPC_SKILL_DMG_PENALTY.size())
damage *= Config.NPC_SKILL_DMG_PENALTY.get(Config.NPC_SKILL_DMG_PENALTY.size());
else
damage *= Config.NPC_SKILL_DMG_PENALTY.get(lvlDiff);
}
}

return damage;
}

public static double calculateSkillResurrectRestorePercent(double baseRestorePercent, L2Character caster)
{
if (baseRestorePercent == 0 || baseRestorePercent == 100)
return baseRestorePercent;

double restorePercent = baseRestorePercent * BaseStats.WIT.calcBonus(caster);
if(restorePercent - baseRestorePercent > 20.0)
restorePercent += 20.0;

restorePercent = Math.max(restorePercent, baseRestorePercent);
restorePercent = Math.min(restorePercent, 90.0);

return restorePercent;
}

public static boolean calcPhysicalSkillEvasion(L2Character target, L2Skill skill)
{
if (skill.isMagic() && skill.getSkillType() != L2SkillType.BLOW || skill.isDebuff())
return false;

return Rnd.get(100) < target.calcStat(Stats.P_SKILL_EVASION, 0, null, skill);
}

public static boolean calcMagicalSkillEvasion(L2Character target, L2Skill skill)
{
if (skill.isDebuff())
return false;

return Rnd.get(100) < target.calcStat(Stats.MEVASION_RATE, 0, null, skill);
}

public static boolean calcSkillMastery(L2Character actor, L2Skill sk)
{
if (sk.getSkillType() == L2SkillType.FISHING)
return false;

double val = actor.getStat().calcStat(Stats.SKILL_MASTERY, 0, null, null);

if (actor instanceof L2PcInstance)
{
if (((L2PcInstance) actor).isMageClass())
val *= BaseStats.INT.calcBonus(actor);
else
val *= BaseStats.STR.calcBonus(actor);
}

return Rnd.get(100) < val;
}

public static double calcValakasAttribute(L2Character attacker, L2Character target, L2Skill skill)
{
double calcPower = 0;
double calcDefen = 0;

if (skill != null && skill.getAttributeName().contains("valakas"))
{
calcPower = attacker.calcStat(Stats.VALAKAS, calcPower, target, skill);
calcDefen = target.calcStat(Stats.VALAKAS_RES, calcDefen, target, skill);
}
else
{
calcPower = attacker.calcStat(Stats.VALAKAS, calcPower, target, skill);
if (calcPower > 0)
{
calcPower = attacker.calcStat(Stats.VALAKAS, calcPower, target, skill);
calcDefen = target.calcStat(Stats.VALAKAS_RES, calcDefen, target, skill);
}
}
return calcPower - calcDefen;
}

public static double calcElemental(L2Character attacker, L2Character target, L2Skill skill)
{
int calcPower = 0;
int calcDefen = 0;
int calcTotal = 0;
double result = 1.0;
byte element;

if (skill != null)
{
element = skill.getElement();
if (element >= 0)
{
calcPower = skill.getElementPower();
calcDefen = target.getDefenseElementValue(element);

if (attacker.getAttackElement() == element)
calcPower += attacker.getAttackElementValue(element);

calcTotal = calcPower - calcDefen;
if (calcTotal > 0)
{
if (calcTotal < 50)
result += calcTotal * 0.003948;
else if (calcTotal < 150)
result = 1.1974;
else if (calcTotal < 300)
result = 1.3973;
else
result = 1.6963;
}

if (Config.DEVELOPER)
_log.info(skill.getName()
+ ": "
+ calcPower
+ ", "
+ calcDefen
+ ", "
+ result);
}
}
else
{
element = attacker.getAttackElement();
if (element >= 0)
{
calcTotal = Math.max(attacker.getAttackElementValue(element)
- target.getDefenseElementValue(element), 0);

if (calcTotal < 50)
result += calcTotal * 0.003948;
else if (calcTotal < 150)
result = 1.1974;
else if (calcTotal < 300)
result = 1.3973;
else
result = 1.6963;

if (Config.DEVELOPER)
_log.info("Hit: "
+ calcPower
+ ", "
+ calcDefen
+ ", "
+ result);
}
}

//return result;
return Math.sqrt(result);
}

/**
* Calculate skill reflection according these three possibilities:
* <li>Reflect failed</li>
* <li>Mormal reflect (just effects). <U>Only possible for skilltypes: BUFF, REFLECT, HEAL_PERCENT,
* MANAHEAL_PERCENT, HOT, CPHOT, MPHOT</U></li>
* <li>vengEance reflect (100% damage reflected but damage is also dealt to actor). <U>This is only possible
* for skills with skilltype PDAM, BLOW, CHARGEDAM, MDAM or DEATHLINK</U></li>
<br><br>
*
@param actor
* @param target
* @param skill
* @return SKILL_REFLECTED_FAILED, SKILL_REFLECT_SUCCEED or SKILL_REFLECT_VENGEANCE
*/
public static byte calcSkillReflect(L2Character target, L2Skill skill)
{
/*
* Neither some special skills (like hero debuffs...) or those skills
* ignoring resistances can be reflected
*/
if (skill.ignoreResists() || !skill.canBeReflected())
return SKILL_REFLECT_FAILED;

// only magic and melee skills can be reflected
if (!skill.isMagic() &&
(skill.getCastRange() == -1 || skill.getCastRange() > MELEE_ATTACK_RANGE))
return SKILL_REFLECT_FAILED;

byte reflect = SKILL_REFLECT_FAILED;
// check for non-reflected skilltypes, need additional retail check
switch (skill.getSkillType())
{
case BUFF:
case HEAL_PERCENT:
case MANAHEAL_PERCENT:
case HOT:
case CPHOT:
case MPHOT:
case UNDEAD_DEFENSE:
case AGGDEBUFF:
case CONT:
return SKILL_REFLECT_FAILED;
// these skill types can deal damage
case PDAM:
case MDAM:
case BLOW:
case DRAIN:
case CHARGEDAM:
case FATAL:
case DEATHLINK:
case CPDAM:
case MANADAM:
case CPDAMPERCENT:
final Stats stat = skill.isMagic() ? Stats.VENGEANCE_SKILL_MAGIC_DAMAGE : Stats.VENGEANCE_SKILL_PHYSICAL_DAMAGE;
final double venganceChance = target.getStat().calcStat(stat, 0, target, skill);
if (venganceChance > Rnd.get(100))
reflect |= SKILL_REFLECT_VENGEANCE;
break;
}

final double reflectChance = target.calcStat(skill.isMagic() ? Stats.REFLECT_SKILL_MAGIC : Stats.REFLECT_SKILL_PHYSIC, 0, null, skill);

if( Rnd.get(100) < reflectChance)
reflect |= SKILL_REFLECT_SUCCEED;

return reflect;
}

/**
* Calculate damage caused by falling
* @param cha
* @param fallHeight
* @return damage
*/
public static double calcFallDam(L2Character cha, int fallHeight)
{
if (!Config.ENABLE_FALLING_DAMAGE || fallHeight < 0)
return 0;
final double damage = cha.calcStat(Stats.FALL, fallHeight * cha.getMaxHp() / 1000, null, null);
return damage;
}

private static double FRONT_MAX_ANGLE = 100;
private static double BACK_MAX_ANGLE = 40;

/**
* Calculates blow success depending on base chance and relative position of attacker and target
* @param activeChar Target that is performing skill
* @param target Target of this skill
* @param skill Skill which will be used to get base value of blowChance and crit condition
* @return Success of blow
*/
public static boolean calcBlowSuccess(L2Character activeChar, L2Character target, L2Skill skill)
{
int blowChance = skill.getBlowChance();

// Skill is blow and it has 0% to make dmg... thats just wrong
if (blowChance == 0)
{
_log.log(Level.WARNING, "Skill " + skill.getId() + " - " + skill.getName() + " has 0 blow land chance, yet its a blow skill!");
//TODO: return false;
//lets add 20 for now, till all skills are corrected
blowChance = 20;
}

if (isBehind(target, activeChar))
blowChance *= 2; //double chance from behind
else if (isInFrontOf(target, activeChar))
{
if ((skill.getCondition() & L2Skill.COND_BEHIND) != 0)
return false;

//base chance from front
}
else
blowChance *= 1.5; //50% better chance from side

return activeChar.calcStat(Stats.BLOW_RATE, blowChance * (1.0 + (activeChar.getDEX()) / 100.), target, null) > Rnd.get(100);
}

/**
* Those are altered formulas for blow lands
* Return True if the target is IN FRONT of the L2Character.<BR><BR>
*/
public static boolean isInFrontOf(L2Character target, L2Character attacker)
{
if (target == null)
return false;

double angleChar, angleTarget, angleDiff;
angleTarget = Util.calculateAngleFrom(target, attacker);
angleChar = Util.convertHeadingToDegree(target.getHeading());
angleDiff = angleChar - angleTarget;
if (angleDiff <= -360 + FRONT_MAX_ANGLE)
angleDiff += 360;
if (angleDiff >= 360 - FRONT_MAX_ANGLE)
angleDiff -= 360;
if (Math.abs(angleDiff) <= FRONT_MAX_ANGLE)
return true;
return false;
}

/**
* Those are altered formulas for blow lands
* Return True if the L2Character is behind the target and can't be seen.<BR><BR>
*/
public static boolean isBehind(L2Character target, L2Character attacker)
{
if (target == null)
return false;

double angleChar, angleTarget, angleDiff;
L2Character target1 = target;
angleChar = Util.calculateAngleFrom(attacker, target1);
angleTarget = Util.convertHeadingToDegree(target1.getHeading());
angleDiff = angleChar - angleTarget;
if (angleDiff <= -360 + BACK_MAX_ANGLE)
angleDiff += 360;
if (angleDiff >= 360 - BACK_MAX_ANGLE)
angleDiff -= 360;
if (Math.abs(angleDiff) <= BACK_MAX_ANGLE)
return true;
return false;
}

/*
private static void landratesVerbose (L2Character actor, boolean isTarget, L2Skill skill, int rate)
{
if (isTarget)
actor.sendMessage("Your " + skill.getName() + " had a " + rate + "% chance to land on target."/*, with a " + skill.getPower() + " base land rate.");
else
actor.sendMessage("This " + skill.getName() + " had a " + rate + "% chance to land over you."/*, with a " + skill.getPower() + " base land rate.");
}

private static void broadcastlandrates(L2Character attacker, L2Character target, L2Skill skill, EffectTemplate effect,
int rate, double statMod, int eleMod, int deltaMod, double resMod, double mAtkMod)
{
for (L2PcInstance obs : ((L2PcInstance)attacker).getLandrateObservers())
{
obs.sendMessage("Player:" + attacker.getName() // Player name
+ " Target: " + target.getName() // Target name
+ " Skill:" + skill.getName() // Skill name
+ " Effect:" + (effect != null ? effect.effectType : null) // Type of abnormal effect (DEBUFF, MDAM, PDAM, etc)
+ " Rate:" + rate + "%" // Rate, like landrates
+ " statMod:" + Math.floor(statMod*1000)/1000 // Stat modifier
+ " eleMod:" + eleMod // Element modifier
+ " lvlMod:" + deltaMod // Level modifier
+ " resMod:" + Math.floor(resMod*1000)/1000 // Resist modifier
+ (skill.isMagic() ? " mAtkMod: " + Math.floor(mAtkMod*1000)/1000 : ""));
}
}

private static void debugVerbose(L2Character attacker, L2Skill skill, L2SkillType type, int value, double statMod, double resMod,
double profMod, double vulnMod, double elementMod, double mAtkMod, int ssMod, int deltamod, int rate)
{
final StringBuilder stat = new StringBuilder(100);
StringUtil.append(stat,
skill.getName(),
" eff.type:", type.toString(),
" power:", String.valueOf(value),
" stat:", String.format("%1.2f", statMod),
" res:", String.format("%1.2f", resMod), "(",
String.format("%1.2f", profMod), "/",
String.format("%1.2f", vulnMod),
") elem:", String.valueOf(elementMod),
" mAtk:", String.format("%1.2f", mAtkMod),
" ss:", String.valueOf(ssMod),
" lvl:", String.valueOf(deltamod),
" total:", String.valueOf(rate)
);
final String result = stat.toString();
if (attacker.isDebug())
attacker.sendDebugMessage(result);
if (Config.DEVELOPER)
_log.info(result);
}
*/
}
 
hjx nguyên cái đó bik đc 1 chữ KHỜ ngoài ra ko bik gì ...... thà đưa newbie cho bé dìm hàng còn lẹ hơn mò cái này :9cool_too_sad:

Đề nghị bớt farm lại 1 chút mà làm party đi trị đám DX này đi, láo quá rồi...

File công thức tính toán damage/stat của tenkai, xin của cu Pere, cho bà con nghiên cứu mà build.
http://www.mediafire.com/?5nl3z7k9yzqbh6l

pé yêu ơi vô đi cave cho pé đi DD nào :))
hay mình ally vs tụi WECAN nhỉ =))
 
Chỉnh sửa cuối:
má ơi bé hông hỉu gì hết trơn

đề nghị mọi người nên post bài trong sáng dễ hỉu, những cái gì "trong tối" pm riêng cho bé
 
Cái file trên là các công thức tính toán Patk, Matk, Def, Eva, accuracy, damage, chance hit, chance debuff của l2tenkai. Ngoại trừ vài cái custom để balance pvp ra thì không thay đổi nhiều so với l2j.
Tóm tắt mấy cái công thức quan trọng:
Physical Damage = 70*Patk/Pdef;
Crit P Damage = 2*Physical Damage* Buff.
Nếu là skill thì thay Patk = Patk + Power skill. Có buff tăng power skill thì nhân thêm vào.
Vì power skill của dagger rất lớn so với Patk của dagger, cho nên co build thêm nhiều Patk cho dagger cũng không thay đổi nhiều damage blow.

Magical damage = 91* căn bậc 2 của Matk / Mdef * Power skill.
Damage magic crit lên player là x2 (custom, gốc là 2,5).

Tỷ lệ crit = Crit rate/1000;
Tỷ lệ blow dính = blow chance *(1+ (DEX)/100); (Tạm thời blow chance 4th được set 20 hết, cho đến khi có thông tin cụ thể về blow chance của skill 4th; blow chance của 3rd thì phụ thuộc crit rate)
Ví dụ 1 con dagger có 50 DEX thì sẽ có 20%*(1+50/100) = 30% cơ hội blow dính. Nếu xăm max 15 DEX thì tăng khoảng 3% tỷ lệ blow dính. blow bên hông tăng 50% và ở sau lưng tăng gấp đôi cơ hội.
Có cả tỷ lệ lethal, nhưng ko cần quan tâm, vì nó chỉ phụ thuộc level, không thay đổi = dye được.

Tỷ lệ hit ở bảng sau : http://l2p.l2wh.com/nonskillattacks.html
Đánh ở sau lưng +20% chance hit và ở bên hông thì +10% chance.

Tỷ lệ debuff khá phức tạp, phụ thuộc vào chênh lệch level, Matk/Mdef, và hệ thống Mesmerize nữa. Không biết cái chỗ tính Mesmerize có MEN không, chứ không thấy MEN trong chỗ resist.

Vụ ally để từ từ nghiên cứu, đám WECAN ở TnS cũng tạm chấp nhận đc, nhưng qua đây thì chưa thấy nó pvp nhiều, ko biết sao
 
mà khỏi cần ally làm gì lắm bỏ castle đi làm cái 4t pvp bless cho sướng ;))
 
Cho hỏi chút , mình có con bow lv 91 set Eliga thì farm ở chổ nào để có Adena nhỉ.
 
Bạn lên Ancient city of Arcan, chạy ra đến chỗ Fairy Settlement mà đánh. Mob yếu, vừa exp vừa adena luôn.
 
pm Babarian,BlueRose để đc tài trợ với kéo lv bé nhé

What Going On .....WHY me :6cool_beat_shot: :2cool_after_boom:
Invite clan thì đc chứ tài trợ thì .... pé hết khả năng rầu ( hết thục két đc ) :9cool_too_sad: :4cool_cold:
@P/s : pm mrsdze, crowfeather, kissing for adena + full items :6cool_sure: :2cool_sexy_girl:
 
Chỉnh sửa cuối:
Pm Babarian, Bluerose, Sagit for tình cảm (Lưu ý, không tiếp phụ nữ...)
 
Status
Không mở trả lời sau này.
Back
Top