/*
* 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 .
*/
package com.l2jserver.gameserver.model.items;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import javolution.util.FastList;
import com.l2jserver.gameserver.handler.ISkillHandler;
import com.l2jserver.gameserver.handler.SkillHandler;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.L2Character;
import com.l2jserver.gameserver.model.actor.L2Npc;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.conditions.Condition;
import com.l2jserver.gameserver.model.conditions.ConditionGameChance;
import com.l2jserver.gameserver.model.effects.L2Effect;
import com.l2jserver.gameserver.model.holders.SkillHolder;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.items.type.L2WeaponType;
import com.l2jserver.gameserver.model.quest.Quest;
import com.l2jserver.gameserver.model.quest.Quest.QuestEventType;
import com.l2jserver.gameserver.model.skills.L2Skill;
import com.l2jserver.gameserver.model.skills.funcs.Func;
import com.l2jserver.gameserver.model.skills.funcs.FuncTemplate;
import com.l2jserver.gameserver.model.stats.Env;
import com.l2jserver.gameserver.model.stats.Formulas;
import com.l2jserver.util.StringUtil;
/**
* This class is dedicated to the management of weapons.
*/
public final class L2Weapon extends L2Item
{
private final L2WeaponType _type;
private final boolean _isMagicWeapon;
private final int _rndDam;
private final int _soulShotCount;
private final int _spiritShotCount;
private final int _mpConsume;
/**
* Skill that activates when item is enchanted +4 (for duals).
*/
private SkillHolder _enchant4Skill = null;
private final int _changeWeaponId;
// Attached skills for Special Abilities
private SkillHolder _skillsOnMagic;
private Condition _skillsOnMagicCondition = null;
private SkillHolder _skillsOnCrit;
private Condition _skillsOnCritCondition = null;
private final int _reducedSoulshot;
private final int _reducedSoulshotChance;
private final int _reducedMpConsume;
private final int _reducedMpConsumeChance;
private final boolean _isForceEquip;
private final boolean _isAttackWeapon;
private final boolean _useWeaponSkillsOnly;
/**
* Constructor for Weapon.
* @param set the StatsSet designating the set of couples (key,value) characterizing the weapon.
*/
public L2Weapon(StatsSet set)
{
super(set);
_type = L2WeaponType.valueOf(set.getString("weapon_type", "none").toUpperCase());
_type1 = L2Item.TYPE1_WEAPON_RING_EARRING_NECKLACE;
_type2 = L2Item.TYPE2_WEAPON;
_isMagicWeapon = set.getBool("is_magic_weapon", false);
_soulShotCount = set.getInteger("soulshots", 0);
_spiritShotCount = set.getInteger("spiritshots", 0);
_rndDam = set.getInteger("random_damage", 0);
_mpConsume = set.getInteger("mp_consume", 0);
String[] reduced_soulshots = set.getString("reduced_soulshot", "").split(",");
_reducedSoulshotChance = (reduced_soulshots.length == 2) ? Integer.parseInt(reduced_soulshots[0]) : 0;
_reducedSoulshot = (reduced_soulshots.length == 2) ? Integer.parseInt(reduced_soulshots[1]) : 0;
String[] reduced_mpconsume = set.getString("reduced_mp_consume", "").split(",");
_reducedMpConsumeChance = (reduced_mpconsume.length == 2) ? Integer.parseInt(reduced_mpconsume[0]) : 0;
_reducedMpConsume = (reduced_mpconsume.length == 2) ? Integer.parseInt(reduced_mpconsume[1]) : 0;
String skill = set.getString("enchant4_skill", null);
if (skill != null)
{
String[] info = skill.split("-");
if ((info != null) && (info.length == 2))
{
int id = 0;
int level = 0;
try
{
id = Integer.parseInt(info[0]);
level = Integer.parseInt(info[1]);
}
catch (Exception nfe)
{
// Incorrect syntax, dont add new skill
_log.info(StringUtil.concat("> Couldnt parse ", skill, " in weapon enchant skills! item ", toString()));
}
if ((id > 0) && (level > 0))
{
_enchant4Skill = new SkillHolder(id, level);
}
}
}
skill = set.getString("onmagic_skill", null);
if (skill != null)
{
String[] info = skill.split("-");
final int chance = set.getInteger("onmagic_chance", 100);
if ((info != null) && (info.length == 2))
{
int id = 0;
int level = 0;
try
{
id = Integer.parseInt(info[0]);
level = Integer.parseInt(info[1]);
}
catch (Exception nfe)
{
// Incorrect syntax, don't add new skill
_log.info(StringUtil.concat("> Couldnt parse ", skill, " in weapon onmagic skills! item ", toString()));
}
if ((id > 0) && (level > 0) && (chance > 0))
{
_skillsOnMagic = new SkillHolder(id, level);
_skillsOnMagicCondition = new ConditionGameChance(chance);
}
}
}
skill = set.getString("oncrit_skill", null);
if (skill != null)
{
String[] info = skill.split("-");
final int chance = set.getInteger("oncrit_chance", 100);
if ((info != null) && (info.length == 2))
{
int id = 0;
int level = 0;
try
{
id = Integer.parseInt(info[0]);
level = Integer.parseInt(info[1]);
}
catch (Exception nfe)
{
// Incorrect syntax, don't add new skill
_log.info(StringUtil.concat("> Couldnt parse ", skill, " in weapon oncrit skills! item ", toString()));
}
if ((id > 0) && (level > 0) && (chance > 0))
{
_skillsOnCrit = new SkillHolder(id, level);
_skillsOnCritCondition = new ConditionGameChance(chance);
}
}
}
_changeWeaponId = set.getInteger("change_weaponId", 0);
_isForceEquip = set.getBool("isForceEquip", false);
_isAttackWeapon = set.getBool("isAttackWeapon", true);
_useWeaponSkillsOnly = set.getBool("useWeaponSkillsOnly", false);
}
/**
* @return the type of Weapon
*/
@Override
public L2WeaponType getItemType()
{
return _type;
}
/**
* @return the ID of the Etc item after applying the mask.
*/
@Override
public int getItemMask()
{
return getItemType().mask();
}
/**
* @return {@code true} if the weapon is magic, {@code false} otherwise.
*/
public boolean isMagicWeapon()
{
return _isMagicWeapon;
}
/**
* @return the quantity of SoulShot used.
*/
public int getSoulShotCount()
{
return _soulShotCount;
}
/**
* @return the quantity of SpiritShot used.
*/
public int getSpiritShotCount()
{
return _spiritShotCount;
}
/**
* @return the reduced quantity of SoultShot used.
*/
public int getReducedSoulShot()
{
return _reducedSoulshot;
}
/**
* @return the chance to use Reduced SoultShot.
*/
public int getReducedSoulShotChance()
{
return _reducedSoulshotChance;
}
/**
* @return the random damage inflicted by the weapon.
*/
public int getRandomDamage()
{
return _rndDam;
}
/**
* @return the MP consumption with the weapon.
*/
public int getMpConsume()
{
return _mpConsume;
}
/**
* @return the reduced MP consumption with the weapon.
*/
public int getReducedMpConsume()
{
return _reducedMpConsume;
}
/**
* @return the chance to use getReducedMpConsume()
*/
public int getReducedMpConsumeChance()
{
return _reducedMpConsumeChance;
}
/**
* @return the skill that player get when has equipped weapon +4 or more (for duals SA).
*/
public L2Skill getEnchant4Skill()
{
if (_enchant4Skill == null)
{
return null;
}
return _enchant4Skill.getSkill();
}
/**
* @return the Id in which weapon this weapon can be changed.
*/
public int getChangeWeaponId()
{
return _changeWeaponId;
}
/**
* @return {@code true} if the weapon is force equip, {@code false} otherwise.
*/
public boolean isForceEquip()
{
return _isForceEquip;
}
/**
* @return {@code true} if the weapon is attack weapon, {@code false} otherwise.
*/
public boolean isAttackWeapon()
{
return _isAttackWeapon;
}
/**
* @return {@code true} if the weapon is skills only, {@code false} otherwise.
*/
public boolean useWeaponSkillsOnly()
{
return _useWeaponSkillsOnly;
}
/**
* @param item the L2ItemInstance pointing out the weapon.
* @param player the L2Character pointing out the player.
* @return an array of Func objects containing the list of functions used by the weapon.
*/
@Override
public Func[] getStatFuncs(L2ItemInstance item, L2Character player)
{
if ((_funcTemplates == null) || (_funcTemplates.length == 0))
{
return _emptyFunctionSet;
}
ArrayList funcs = new ArrayList<>(_funcTemplates.length);
Env env = new Env();
env.setCharacter(player);
env.setItem(item);
Func f;
for (FuncTemplate t : _funcTemplates)
{
f = t.getFunc(env, item);
if (f != null)
{
funcs.add(f);
}
}
return funcs.toArray(new Func[funcs.size()]);
}
/**
* @param caster the L2Character pointing out the caster
* @param target the L2Character pointing out the target
* @param crit the boolean tells whether the hit was critical
* @return the effects of skills associated with the item to be triggered onHit.
*/
public L2Effect[] getSkillEffects(L2Character caster, L2Character target, boolean crit)
{
if ((_skillsOnCrit == null) || !crit)
{
return _emptyEffectSet;
}
final List effects = new FastList<>();
final L2Skill onCritSkill = _skillsOnCrit.getSkill();
if (_skillsOnCritCondition != null)
{
Env env = new Env();
env.setCharacter(caster);
env.setTarget(target);
env.setSkill(onCritSkill);
if (!_skillsOnCritCondition.test(env))
{
// Chance not met
return _emptyEffectSet;
}
}
if (!onCritSkill.checkCondition(caster, target, false))
{
// Skill condition not met
return _emptyEffectSet;
}
final byte shld = Formulas.calcShldUse(caster, target, onCritSkill);
if (!Formulas.calcSkillSuccess(caster, target, onCritSkill, shld, false, false, false))
{
// These skills should not work on RaidBoss
return _emptyEffectSet;
}
if (target.getFirstEffect(onCritSkill.getId()) != null)
{
target.getFirstEffect(onCritSkill.getId()).exit();
}
for (L2Effect e : onCritSkill.getEffects(caster, target, new Env(shld, false, false, false)))
{
effects.add(e);
}
if (effects.isEmpty())
{
return _emptyEffectSet;
}
return effects.toArray(new L2Effect[effects.size()]);
}
/**
* @param caster the L2Character pointing out the caster
* @param target the L2Character pointing out the target
* @param trigger the L2Skill pointing out the skill triggering this action
* @return the effects of skills associated with the item to be triggered onMagic.
*/
public L2Effect[] getSkillEffects(L2Character caster, L2Character target, L2Skill trigger)
{
if (_skillsOnMagic == null)
{
return _emptyEffectSet;
}
final L2Skill onMagicSkill = _skillsOnMagic.getSkill();
// No Trigger if Offensive Skill
if (trigger.isOffensive() && onMagicSkill.isOffensive())
{
return _emptyEffectSet;
}
// No Trigger if not Magic Skill
if (!trigger.isMagic() && !onMagicSkill.isMagic())
{
return _emptyEffectSet;
}
if (_skillsOnMagicCondition != null)
{
Env env = new Env();
env.setCharacter(caster);
env.setTarget(target);
env.setSkill(onMagicSkill);
if (!_skillsOnMagicCondition.test(env))
{
// Chance not met
return _emptyEffectSet;
}
}
if (!onMagicSkill.checkCondition(caster, target, false))
{
// Skill condition not met
return _emptyEffectSet;
}
final byte shld = Formulas.calcShldUse(caster, target, onMagicSkill);
if (onMagicSkill.isOffensive() && !Formulas.calcSkillSuccess(caster, target, onMagicSkill, shld, false, false, false))
{
return _emptyEffectSet;
}
L2Character[] targets =
{
target
};
// Launch the magic skill and calculate its effects
// Get the skill handler corresponding to the skill type
final ISkillHandler handler = SkillHandler.getInstance().getHandler(onMagicSkill.getSkillType());
if (handler != null)
{
handler.useSkill(caster, onMagicSkill, targets);
}
else
{
onMagicSkill.useSkill(caster, targets);
}
// notify quests of a skill use
if (caster instanceof L2PcInstance)
{
// Mobs in range 1000 see spell
Collection objs = caster.getKnownList().getKnownObjects().values();
for (L2Object spMob : objs)
{
if (spMob instanceof L2Npc)
{
L2Npc npcMob = (L2Npc) spMob;
if (npcMob.getTemplate().getEventQuests(QuestEventType.ON_SKILL_SEE) != null)
{
for (Quest quest : npcMob.getTemplate().getEventQuests(QuestEventType.ON_SKILL_SEE))
{
quest.notifySkillSee(npcMob, caster.getActingPlayer(), onMagicSkill, targets, false);
}
}
}
}
}
return _emptyEffectSet;
}
}