/*
* 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 silentium.gameserver.model.actor;
import static silentium.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import java.text.DateFormat;
import java.util.Collection;
import java.util.List;
import javolution.util.FastList;
import silentium.commons.utils.Rnd;
import silentium.commons.utils.StringUtil;
import silentium.gameserver.ThreadPoolManager;
import silentium.gameserver.ai.CtrlIntention;
import silentium.gameserver.configs.CustomConfig;
import silentium.gameserver.configs.EventsConfig;
import silentium.gameserver.configs.MainConfig;
import silentium.gameserver.configs.NPCConfig;
import silentium.gameserver.configs.PlayersConfig;
import silentium.gameserver.data.html.HtmCache;
import silentium.gameserver.data.html.StaticHtmPath;
import silentium.gameserver.data.xml.HelperBuffData;
import silentium.gameserver.idfactory.IdFactory;
import silentium.gameserver.instancemanager.CastleManager;
import silentium.gameserver.instancemanager.DimensionalRiftManager;
import silentium.gameserver.instancemanager.QuestManager;
import silentium.gameserver.instancemanager.TownManager;
import silentium.gameserver.instancemanager.games.Lottery;
import silentium.gameserver.model.L2Clan;
import silentium.gameserver.model.L2ItemInstance;
import silentium.gameserver.model.L2Multisell;
import silentium.gameserver.model.L2NpcAIData;
import silentium.gameserver.model.L2Object;
import silentium.gameserver.model.L2Skill;
import silentium.gameserver.model.L2Spawn;
import silentium.gameserver.model.L2World;
import silentium.gameserver.model.L2WorldRegion;
import silentium.gameserver.model.actor.instance.L2FestivalGuideInstance;
import silentium.gameserver.model.actor.instance.L2FishermanInstance;
import silentium.gameserver.model.actor.instance.L2MerchantInstance;
import silentium.gameserver.model.actor.instance.L2NpcInstance;
import silentium.gameserver.model.actor.instance.L2PcInstance;
import silentium.gameserver.model.actor.instance.L2TeleporterInstance;
import silentium.gameserver.model.actor.instance.L2WarehouseInstance;
import silentium.gameserver.model.actor.knownlist.NpcKnownList;
import silentium.gameserver.model.actor.stat.NpcStat;
import silentium.gameserver.model.actor.status.NpcStatus;
import silentium.gameserver.model.entity.Castle;
import silentium.gameserver.model.entity.sevensigns.SevenSigns;
import silentium.gameserver.model.quest.Quest;
import silentium.gameserver.model.quest.QuestState;
import silentium.gameserver.model.quest.State;
import silentium.gameserver.model.zone.type.L2TownZone;
import silentium.gameserver.network.L2GameClient;
import silentium.gameserver.network.SystemMessageId;
import silentium.gameserver.network.clientpackets.Say2;
import silentium.gameserver.network.serverpackets.AbstractNpcInfo.NpcInfo;
import silentium.gameserver.network.serverpackets.*;
import silentium.gameserver.tables.ClanTable;
import silentium.gameserver.tables.ItemTable;
import silentium.gameserver.tables.SkillTable;
import silentium.gameserver.tables.SkillTable.FrequentSkill;
import silentium.gameserver.taskmanager.DecayTaskManager;
import silentium.gameserver.templates.L2HelperBuff;
import silentium.gameserver.templates.chars.L2NpcTemplate;
import silentium.gameserver.templates.chars.L2NpcTemplate.AIType;
import silentium.gameserver.templates.item.L2Item;
import silentium.gameserver.templates.item.L2Weapon;
import silentium.gameserver.templates.skills.L2SkillType;
import silentium.gameserver.utils.Broadcast;
/**
* This class represents a Non-Player-Character in the world. It can be a monster or a friendly character. It also uses a template to fetch some
* static values. The templates are hardcoded in the client, so we can rely on them.<BR>
* <BR>
* L2Character :<BR>
* <BR>
* <li>L2Attackable</li> <li>L2BoxInstance</li> <li>L2NpcInstance</li>
*/
public class L2Npc extends L2Character
{
public static final int INTERACTION_DISTANCE = 150;
private L2Spawn _spawn;
private boolean _isBusy = false;
volatile boolean _isDecayed = false;
private boolean _isSpoil = false;
private boolean _hasSpoken = false;
private int _castleIndex = -2;
private boolean _isInTown = false;
private String _busyMessage = "";
private int _isSpoiledBy = 0;
protected RandomAnimationTask _rAniTask = null;
private long _lastSocialBroadcast = 0;
private final int _minimalSocialInterval = 6000;
private int _currentLHandId;
private int _currentRHandId;
private int _currentEnchant;
private int _currentCollisionHeight; // used for npc grow effect skills
private int _currentCollisionRadius; // used for npc grow effect skills
public boolean _soulshotcharged = false;
public boolean _spiritshotcharged = false;
private int _soulshotamount = 0;
private int _spiritshotamount = 0;
public boolean _ssrecharged = true;
public boolean _spsrecharged = true;
private final L2NpcAIData _staticAIData = getTemplate().getAIDataStatic();
// AI Recall
public final L2NpcAIData getAIData()
{
return _staticAIData;
}
public int getSoulShot()
{
return _staticAIData.getSoulShot();
}
public int getSpiritShot()
{
return _staticAIData.getSpiritShot();
}
public int getSoulShotChance()
{
return _staticAIData.getSoulShotChance();
}
public int getSpiritShotChance()
{
return _staticAIData.getSpiritShotChance();
}
public boolean useSoulShot()
{
if (_soulshotcharged)
return true;
if (_ssrecharged)
{
_soulshotamount = getSoulShot();
_ssrecharged = false;
}
else if (_soulshotamount > 0)
{
if (Rnd.get(100) <= getSoulShotChance())
{
_soulshotamount = _soulshotamount - 1;
Broadcast.toSelfAndKnownPlayersInRadiusSq(this, new MagicSkillUse(this, this, 2154, 1, 0, 0), 360000);
_soulshotcharged = true;
}
}
else
return false;
return _soulshotcharged;
}
public boolean useSpiritShot()
{
if (_spiritshotcharged)
return true;
if (_spsrecharged)
{
_spiritshotamount = getSpiritShot();
_spsrecharged = false;
}
else if (_spiritshotamount > 0)
{
if (Rnd.get(100) <= getSpiritShotChance())
{
_spiritshotamount = _spiritshotamount - 1;
Broadcast.toSelfAndKnownPlayersInRadiusSq(this, new MagicSkillUse(this, this, 2061, 1, 0, 0), 360000);
_spiritshotcharged = true;
}
}
else
return false;
return _spiritshotcharged;
}
public int getAggroRange()
{
return _staticAIData.getAggroRange();
}
public int getEnemyRange()
{
return _staticAIData.getEnemyRange();
}
public String getEnemyClan()
{
return _staticAIData.getEnemyClan();
}
public int getClanRange()
{
return _staticAIData.getClanRange();
}
public String getClan()
{
return _staticAIData.getClan();
}
public int getPrimaryAttack()
{
return _staticAIData.getPrimaryAttack();
}
public int getMinSkillChance()
{
return _staticAIData.getMinSkillChance();
}
public int getMaxSkillChance()
{
return _staticAIData.getMaxSkillChance();
}
public int getCanMove()
{
return _staticAIData.getCanMove();
}
public int getIsChaos()
{
return _staticAIData.getIsChaos();
}
public int getShortRangeSkillChance()
{
return _staticAIData.getShortRangeChance();
}
public int getLongRangeSkillChance()
{
return _staticAIData.getLongRangeChance();
}
public int getSwitchRangeChance()
{
return _staticAIData.getSwitchRangeChance();
}
public boolean hasLongRangeSkill()
{
return _staticAIData.getLongRangeSkill() != 0;
}
public boolean hasShortRangeSkill()
{
return _staticAIData.getShortRangeSkill() != 0;
}
public FastList<L2Skill> getLongRangeSkill()
{
final FastList<L2Skill> skilldata = new FastList<>();
if (_staticAIData == null || _staticAIData.getLongRangeSkill() == 0)
return skilldata;
switch (_staticAIData.getLongRangeSkill())
{
case -1:
{
L2Skill[] skills = null;
skills = getAllSkills();
if (skills != null)
{
for (L2Skill sk : skills)
{
if (sk == null || sk.isPassive() || sk.getTargetType() == L2Skill.SkillTargetType.TARGET_SELF)
continue;
if (sk.getCastRange() >= 200)
skilldata.add(sk);
}
}
break;
}
case 1:
{
if (getTemplate().getUniversalSkills() != null)
{
for (L2Skill sk : getTemplate().getUniversalSkills())
{
if (sk.getCastRange() >= 200)
skilldata.add(sk);
}
}
break;
}
default:
{
for (L2Skill sk : getAllSkills())
{
if (sk.getId() == _staticAIData.getLongRangeSkill())
skilldata.add(sk);
}
}
}
return skilldata;
}
public FastList<L2Skill> getShortRangeSkill()
{
final FastList<L2Skill> skilldata = new FastList<>();
if (_staticAIData == null || _staticAIData.getShortRangeSkill() == 0)
return skilldata;
switch (_staticAIData.getShortRangeSkill())
{
case -1:
{
L2Skill[] skills = null;
skills = getAllSkills();
if (skills != null)
{
for (L2Skill sk : skills)
{
if (sk == null || sk.isPassive() || sk.getTargetType() == L2Skill.SkillTargetType.TARGET_SELF)
continue;
if (sk.getCastRange() <= 200)
skilldata.add(sk);
}
}
break;
}
case 1:
{
if (getTemplate().getUniversalSkills() != null)
{
for (L2Skill sk : getTemplate().getUniversalSkills())
{
if (sk.getCastRange() <= 200)
skilldata.add(sk);
}
}
break;
}
default:
{
for (L2Skill sk : getAllSkills())
{
if (sk.getId() == _staticAIData.getShortRangeSkill())
skilldata.add(sk);
}
}
}
return skilldata;
}
/**
* Task launching the function onRandomAnimation()
*/
protected class RandomAnimationTask implements Runnable
{
@Override
public void run()
{
try
{
if (this != _rAniTask)
return; // Shouldn't happen, but who knows... just to make sure every active npc has only one timer.
if (isMob())
{
// Cancel further animation timers until intention is changed to ACTIVE again.
if (getAI().getIntention() != AI_INTENTION_ACTIVE)
return;
}
else
{
if (!isInActiveRegion()) // NPCs in inactive region don't run this task
return;
}
if (!(isDead() || isStunned() || isSleeping() || isParalyzed()))
onRandomAnimation(Rnd.get(2, 3));
startRandomAnimationTimer();
}
catch (Exception e)
{
_log.warn(e.getLocalizedMessage(), e);
}
}
}
/**
* Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2Npc and create a new RandomAnimation Task.
*
* @param animationId
* the animation id.
*/
public void onRandomAnimation(int animationId)
{
// Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2Npc
long now = System.currentTimeMillis();
if (now - _lastSocialBroadcast > _minimalSocialInterval)
{
_lastSocialBroadcast = now;
broadcastPacket(new SocialAction(this, animationId));
}
}
/**
* Create a RandomAnimation Task that will be launched after the calculated delay.
*/
public void startRandomAnimationTimer()
{
if (!hasRandomAnimation())
return;
// Calculate the delay before the next animation
int interval = 1000 * (isMob() ? Rnd.get(NPCConfig.MIN_MONSTER_ANIMATION, NPCConfig.MAX_MONSTER_ANIMATION) : Rnd.get(NPCConfig.MIN_NPC_ANIMATION, NPCConfig.MAX_NPC_ANIMATION));
// Create a RandomAnimation Task that will be launched after the calculated delay
_rAniTask = new RandomAnimationTask();
ThreadPoolManager.getInstance().scheduleGeneral(_rAniTask, interval);
}
/**
* @return true if the server allows Random Animation, false if not or the AItype is a corpse.
*/
public boolean hasRandomAnimation()
{
return (NPCConfig.MAX_NPC_ANIMATION > 0 && !getAiType().equals(AIType.CORPSE));
}
/**
* Constructor of L2Npc (use L2Character constructor).<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Call the L2Character constructor to set the _template of the L2Character (copy skills from template to object and link _calculators to
* NPC_STD_CALCULATOR)</li> <li>Set the name of the L2Character</li> <li>Create a RandomAnimation Task that will be launched after the
* calculated delay if the server allow it</li><BR>
* <BR>
*
* @param objectId
* Identifier of the object to initialized
* @param template
* The L2NpcTemplate to apply to the NPC
*/
public L2Npc(int objectId, L2NpcTemplate template)
{
// Call the L2Character constructor to set the _template of the L2Character, copy skills from template to object and link
// _calculators to NPC_STD_CALCULATOR
super(objectId, template);
initCharStatusUpdateValues();
// initialize the "current" equipment
_currentLHandId = getTemplate().getLeftHand();
_currentRHandId = getTemplate().getRightHand();
_currentEnchant = getTemplate().getEnchantEffect();
// initialize the "current" collisions
_currentCollisionHeight = getTemplate().getCollisionHeight();
_currentCollisionRadius = getTemplate().getCollisionRadius();
if (template == null)
{
_log.error("No template for Npc. Please check your datapack is setup correctly.");
return;
}
// Set the name of the L2Character
setName(template.getName());
}
@Override
public void initKnownList()
{
setKnownList(new NpcKnownList(this));
}
@Override
public NpcKnownList getKnownList()
{
return (NpcKnownList) super.getKnownList();
}
@Override
public void initCharStat()
{
setStat(new NpcStat(this));
}
@Override
public NpcStat getStat()
{
return (NpcStat) super.getStat();
}
@Override
public void initCharStatus()
{
setStatus(new NpcStatus(this));
}
@Override
public NpcStatus getStatus()
{
return (NpcStatus) super.getStatus();
}
/**
* Return the L2NpcTemplate of the L2Npc.
*/
@Override
public final L2NpcTemplate getTemplate()
{
return (L2NpcTemplate) super.getTemplate();
}
/**
* @return the generic Identifier of this L2Npc contained in the L2NpcTemplate.
*/
public int getNpcId()
{
return getTemplate().getNpcId();
}
@Override
public boolean isAttackable()
{
return true;
}
/**
* Return the Level of this L2Npc contained in the L2NpcTemplate.
*/
@Override
public final int getLevel()
{
return getTemplate().getLevel();
}
/**
* @return True if the L2Npc is agressive (ex : L2MonsterInstance in function of aggroRange).
*/
public boolean isAggressive()
{
return false;
}
/**
* Return True if this L2Npc is undead in function of the L2NpcTemplate.
*/
@Override
public boolean isUndead()
{
return getTemplate().isUndead();
}
/**
* Send a packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2Npc.
*/
@Override
public void updateAbnormalEffect()
{
// Send a Server->Client packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the
// L2Npc
Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values();
for (L2PcInstance player : plrs)
{
if (player == null)
continue;
if (getRunSpeed() == 0)
player.sendPacket(new ServerObjectInfo(this, player));
else
player.sendPacket(new NpcInfo(this, player));
}
}
/**
* <B><U> Values </U> :</B>
* <ul>
* <li>object is a L2NpcInstance or isn't a L2Character : 0 (don't remember it)</li>
* <li>object is a L2Playable : 1500</li>
* <li>others : 500</li>
* </ul>
*
* @param object
* The Object to add to _knownObject
* @return the distance under which the object must be add to _knownObject in function of the object type.
*/
public int getDistanceToWatchObject(L2Object object)
{
if (object instanceof L2Playable)
return 1500;
if (object instanceof L2NpcInstance || !(object instanceof L2Character))
return 0;
if (object instanceof L2FestivalGuideInstance)
return 10000;
return 500;
}
/**
* <B><U> Overriden in </U> :</B>
* <ul>
* <li>L2MonsterInstance : Check if the attacker is not another L2MonsterInstance</li>
* <li>L2PcInstance</li>
* </ul>
*/
@Override
public boolean isAutoAttackable(L2Character attacker)
{
return false;
}
/**
* @return the Identifier of the item in the left hand of this L2Npc contained in the L2NpcTemplate.
*/
public int getLeftHandItem()
{
return _currentLHandId;
}
/**
* @return the Identifier of the item in the right hand of this L2Npc contained in the L2NpcTemplate.
*/
public int getRightHandItem()
{
return _currentRHandId;
}
public int getEnchantEffect()
{
return _currentEnchant;
}
/**
* @return True if this L2Npc has drops that can be sweeped.
*/
public boolean isSpoil()
{
return _isSpoil;
}
/**
* Set the spoil state of this L2Npc.
*
* @param isSpoil
* boolean value.
*/
public void setSpoil(boolean isSpoil)
{
_isSpoil = isSpoil;
}
public final int getIsSpoiledBy()
{
return _isSpoiledBy;
}
public final void setIsSpoiledBy(int value)
{
_isSpoiledBy = value;
}
/**
* @return True if this L2Npc has spoken (used for SpeakingNPCs script).
*/
public boolean hasSpoken()
{
return _hasSpoken;
}
/**
* Set the speak state of this L2Npc.
*
* @param hasSpoken
* boolean value.
*/
public void setHasSpoken(boolean hasSpoken)
{
_hasSpoken = hasSpoken;
}
/**
* @return the busy status of this L2Npc.
*/
public final boolean isBusy()
{
return _isBusy;
}
/**
* Set the busy status of this L2Npc.
*
* @param isBusy
* boolean value.
*/
public void setBusy(boolean isBusy)
{
_isBusy = isBusy;
}
/**
* @return the busy message of this L2Npc.
*/
public final String getBusyMessage()
{
return _busyMessage;
}
/**
* Set the busy message of this L2Npc.
*
* @param message
* String message to send to this L2Npc.
*/
public void setBusyMessage(String message)
{
_busyMessage = message;
}
/**
* Overidden in L2CastleWarehouse, L2ClanHallManager and L2Warehouse.
*
* @return true if this L2Npc instance can be warehouse manager.
*/
public boolean isWarehouse()
{
return false;
}
/**
* Manage actions when a player click on the L2Npc.<BR>
* <BR>
* <B><U> Actions on first click on the L2Npc (Select it)</U> :</B><BR>
* <BR>
* <li>Set the L2Npc as target of the L2PcInstance player (if necessary)</li> <li>Send a Server->Client packet MyTargetSelected to the
* L2PcInstance player (display the select window)</li> <li>If L2Npc is autoAttackable, send a Server->Client packet StatusUpdate to the
* L2PcInstance in order to update L2Npc HP bar</li> <li>Send a Server->Client packet ValidateLocation to correct the L2Npc position and
* heading on the client</li><BR>
* <BR>
* <B><U> Actions on second click on the L2Npc (Attack it/Intercat with it)</U> :</B><BR>
* <BR>
* <li>Send a Server->Client packet MyTargetSelected to the L2PcInstance player (display the select window)</li> <li>If L2Npc is
* autoAttackable, notify the L2PcInstance AI with AI_INTENTION_ATTACK (after a height verification)</li> <li>If L2Npc is NOT autoAttackable,
* notify the L2PcInstance AI with AI_INTENTION_INTERACT (after a distance verification) and show message</li> <BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid
* that client wait an other packet</B></FONT><BR>
* <BR>
* <B><U> Example of use </U> :</B><BR>
* <BR>
* <li>Client packet : Action, AttackRequest</li><BR>
* <BR>
* <B><U> Overriden in </U> :</B><BR>
* <BR>
* <li>L2ArtefactInstance : Manage only fisrt click to select Artefact</li><BR>
* <BR>
* <li>L2GuardInstance :</li><BR>
* <BR>
*
* @param player
* The L2PcInstance that start an action on the L2Npc
*/
@Override
public void onAction(L2PcInstance player)
{
if (!player.canTarget())
return;
// Check if the L2PcInstance already target the L2Npc
if (this != player.getTarget())
{
// Set the target of the L2PcInstance player
player.setTarget(this);
// Check if the player is attackable (without a forced attack)
if (isAutoAttackable(player))
{
getAI(); // wake up ai
// Send MyTargetSelected to the L2PcInstance player
player.sendPacket(new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()));
// Send StatusUpdate of the L2Npc to the L2PcInstance to update its HP bar
StatusUpdate su = new StatusUpdate(this);
su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
player.sendPacket(su);
}
// Send MyTargetSelected to the L2PcInstance player
else
player.sendPacket(new MyTargetSelected(getObjectId(), 0));
// Send a Server->Client packet ValidateLocation to correct the L2Npc position and heading on the client
player.sendPacket(new ValidateLocation(this));
}
else
{
// Check if the player is attackable (without a forced attack) and isn't dead
if (isAutoAttackable(player) && !isAlikeDead())
{
// Check the height difference
if (Math.abs(player.getZ() - getZ()) < 400) // this max heigth difference might need some tweaking
{
// Set the L2PcInstance Intention to AI_INTENTION_ATTACK
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
}
else
{
// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another
// packet
player.sendPacket(ActionFailed.STATIC_PACKET);
}
}
else if (!isAutoAttackable(player))
{
// Calculate the distance between the L2PcInstance and the L2Npc
if (!canInteract(player))
{
// Notify the L2PcInstance AI with AI_INTENTION_INTERACT
player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this);
}
else
{
// Rotate the player to face the instance
player.sendPacket(new MoveToPawn(player, this, L2Npc.INTERACTION_DISTANCE));
// Send ActionFailed to the player in order to avoid he stucks
player.sendPacket(ActionFailed.STATIC_PACKET);
if (hasRandomAnimation())
onRandomAnimation(Rnd.get(8));
Quest[] qlsa = getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
if ((qlsa != null) && qlsa.length > 0)
player.setLastQuestNpcObject(getObjectId());
Quest[] qlst = getTemplate().getEventQuests(Quest.QuestEventType.ON_FIRST_TALK);
if ((qlst != null) && qlst.length == 1)
qlst[0].notifyFirstTalk(this, player);
else
showChatWindow(player);
}
}
}
}
/**
* Manage and Display the GM console to modify the L2Npc (GM only).<BR>
* <BR>
* <B><U> Actions (If the L2PcInstance is a GM only)</U> :</B><BR>
* <BR>
* <li>Set the L2Npc as target of the L2PcInstance player (if necessary)</li> <li>Send a Server->Client packet MyTargetSelected to the
* L2PcInstance player (display the select window)</li> <li>If L2Npc is autoAttackable, send a Server->Client packet StatusUpdate to the
* L2PcInstance in order to update L2Npc HP bar</li> <li>Send a Server->Client NpcHtmlMessage() containing the GM console about this L2Npc</li>
* <BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Each group of Server->Client packet must be terminated by a ActionFailed packet in order to avoid
* that client wait an other packet</B></FONT><BR>
* <BR>
* <B><U> Example of use </U> :</B><BR>
* <BR>
* <li>Client packet : Action</li><BR>
* <BR>
*
* @param client
* The thread that manage the player that pessed Shift and click on the L2Npc
*/
@Override
public void onActionShift(L2GameClient client)
{
// Get the L2PcInstance corresponding to the thread
L2PcInstance player = client.getActiveChar();
if (player == null)
return;
// Check if the L2PcInstance is a GM
if (player.isGM())
{
// Set the target of the L2PcInstance player
player.setTarget(this);
// Send a Server->Client packet MyTargetSelected to the L2PcInstance player
player.sendPacket(new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()));
// Check if the player is attackable (without a forced attack)
if (isAutoAttackable(player))
{
// Send a Server->Client packet StatusUpdate of the L2Npc to the L2PcInstance to update its HP bar
StatusUpdate su = new StatusUpdate(this);
su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
su.addAttribute(StatusUpdate.MAX_HP, getMaxHp());
player.sendPacket(su);
}
NpcHtmlMessage html = new NpcHtmlMessage(0);
html.setFile(StaticHtmPath.AdminHtmPath + "npcinfo.htm", player);
html.replace("%objid%", String.valueOf(getObjectId()));
html.replace("%class%", getClass().getSimpleName());
html.replace("%id%", String.valueOf(getTemplate().getNpcId()));
html.replace("%lvl%", String.valueOf(getTemplate().getLevel()));
html.replace("%name%", String.valueOf(getTemplate().getName()));
html.replace("%tmplid%", String.valueOf(getTemplate().getNpcId()));
html.replace("%aggro%", String.valueOf((this instanceof L2Attackable) ? ((L2Attackable) this).getAggroRange() : 0));
html.replace("%corpse%", String.valueOf(getTemplate().getCorpseDecayTime()));
html.replace("%enchant%", String.valueOf(getTemplate().getEnchantEffect()));
html.replace("%hp%", String.valueOf((int) ((L2Character) this).getCurrentHp()));
html.replace("%hpmax%", String.valueOf(((L2Character) this).getMaxHp()));
html.replace("%mp%", String.valueOf((int) ((L2Character) this).getCurrentMp()));
html.replace("%mpmax%", String.valueOf(((L2Character) this).getMaxMp()));
html.replace("%patk%", String.valueOf(((L2Character) this).getPAtk(null)));
html.replace("%matk%", String.valueOf(((L2Character) this).getMAtk(null, null)));
html.replace("%pdef%", String.valueOf(((L2Character) this).getPDef(null)));
html.replace("%mdef%", String.valueOf(((L2Character) this).getMDef(null, null)));
html.replace("%accu%", String.valueOf(((L2Character) this).getAccuracy()));
html.replace("%evas%", String.valueOf(((L2Character) this).getEvasionRate(null)));
html.replace("%crit%", String.valueOf(((L2Character) this).getCriticalHit(null, null)));
html.replace("%rspd%", String.valueOf(((L2Character) this).getRunSpeed()));
html.replace("%aspd%", String.valueOf(((L2Character) this).getPAtkSpd()));
html.replace("%cspd%", String.valueOf(((L2Character) this).getMAtkSpd()));
html.replace("%str%", String.valueOf(((L2Character) this).getSTR()));
html.replace("%dex%", String.valueOf(((L2Character) this).getDEX()));
html.replace("%con%", String.valueOf(((L2Character) this).getCON()));
html.replace("%int%", String.valueOf(((L2Character) this).getINT()));
html.replace("%wit%", String.valueOf(((L2Character) this).getWIT()));
html.replace("%men%", String.valueOf(((L2Character) this).getMEN()));
html.replace("%loc%", String.valueOf(getX() + " " + getY() + " " + getZ()));
html.replace("%dist%", String.valueOf((int) Math.sqrt(player.getDistanceSq(this))));
// byte attackAttribute = ((L2Character)this).getAttackElement();
html.replace("%ele_atk_value%", "%todo%" /* String.valueOf(((L2Character)this).getAttackElementValue(attackAttribute)) */);
html.replace("%ele_dfire%", String.valueOf(((L2Character) this).getDefenseElementValue((byte) 2)));
html.replace("%ele_dwater%", String.valueOf(((L2Character) this).getDefenseElementValue((byte) 3)));
html.replace("%ele_dwind%", String.valueOf(((L2Character) this).getDefenseElementValue((byte) 1)));
html.replace("%ele_dearth%", String.valueOf(((L2Character) this).getDefenseElementValue((byte) 4)));
html.replace("%ele_dholy%", String.valueOf(((L2Character) this).getDefenseElementValue((byte) 5)));
html.replace("%ele_ddark%", String.valueOf(((L2Character) this).getDefenseElementValue((byte) 6)));
if (getSpawn() != null)
{
html.replace("%spawn%", getSpawn().getLocx() + " " + getSpawn().getLocy() + " " + getSpawn().getLocz());
html.replace("%loc2d%", String.valueOf((int) Math.sqrt(((L2Character) this).getPlanDistanceSq(getSpawn().getLocx(), getSpawn().getLocy()))));
html.replace("%loc3d%", String.valueOf((int) Math.sqrt(((L2Character) this).getDistanceSq(getSpawn().getLocx(), getSpawn().getLocy(), getSpawn().getLocz()))));
html.replace("%resp%", String.valueOf(getSpawn().getRespawnDelay() / 1000));
}
else
{
html.replace("%spawn%", "<font color=FF0000>null</font>");
html.replace("%loc2d%", "<font color=FF0000>--</font>");
html.replace("%loc3d%", "<font color=FF0000>--</font>");
html.replace("%resp%", "<font color=FF0000>--</font>");
}
if (hasAI())
{
html.replace("%ai_intention%", "<tr><td><table width=270 border=0><tr><td width=100><font color=FFAA00>Intention:</font></td><td align=right width=170>" + String.valueOf(getAI().getIntention().name()) + "</td></tr></table></td></tr>");
html.replace("%ai%", "<tr><td><table width=270 border=0><tr><td width=100><font color=FFAA00>AI</font></td><td align=right width=170>" + getAI().getClass().getSimpleName() + "</td></tr></table></td></tr>");
html.replace("%ai_type%", "<tr><td><table width=270 border=0><tr><td width=100><font color=FFAA00>AIType</font></td><td align=right width=170>" + String.valueOf(getAiType()) + "</td></tr></table></td></tr>");
html.replace("%ai_clan%", "<tr><td><table width=270 border=0><tr><td width=100><font color=FFAA00>Clan & Range:</font></td><td align=right width=170>" + String.valueOf(getClan()) + " " + String.valueOf(getClanRange()) + "</td></tr></table></td></tr>");
html.replace("%ai_enemy_clan%", "<tr><td><table width=270 border=0><tr><td width=100><font color=FFAA00>Enemy & Range:</font></td><td align=right width=170>" + String.valueOf(getEnemyClan()) + " " + String.valueOf(getEnemyRange()) + "</td></tr></table></td></tr>");
}
else
{
html.replace("%ai_intention%", "");
html.replace("%ai%", "");
html.replace("%ai_type%", "");
html.replace("%ai_clan%", "");
html.replace("%ai_enemy_clan%", "");
}
if (this instanceof L2MerchantInstance)
html.replace("%butt%", "<button value=\"Shop\" action=\"bypass -h admin_showShop " + String.valueOf(getTemplate().getNpcId()) + "\" width=65 height=19 back=\"L2UI_ch3.smallbutton2_over\" fore=\"L2UI_ch3.smallbutton2\">");
else
html.replace("%butt%", "");
player.sendPacket(html);
}
// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
player.sendPacket(ActionFailed.STATIC_PACKET);
}
/**
* @return the L2Castle this L2Npc belongs to.
*/
public final Castle getCastle()
{
// Get castle this NPC belongs to (excluding L2Attackable)
if (_castleIndex < 0)
{
L2TownZone town = TownManager.getTown(getX(), getY(), getZ());
if (town != null)
_castleIndex = CastleManager.getInstance().getCastleIndex(town.getTaxById());
if (_castleIndex < 0)
_castleIndex = CastleManager.getInstance().findNearestCastleIndex(this);
else
_isInTown = true; // Npc was spawned in town
}
if (_castleIndex < 0)
return null;
return CastleManager.getInstance().getCastles().get(_castleIndex);
}
public final boolean getIsInTown()
{
if (_castleIndex < 0)
getCastle();
return _isInTown;
}
/**
* Open a quest or chat window on client with the text of the L2Npc in function of the command.
*
* @param player
* The player to test
* @param command
* The command string received from client
*/
public void onBypassFeedback(L2PcInstance player, String command)
{
if (canInteract(player))
{
if (isBusy() && getBusyMessage().length() > 0)
{
player.sendPacket(ActionFailed.STATIC_PACKET);
NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(StaticHtmPath.NpcHtmPath + "npcbusy.htm", player);
html.replace("%busymessage%", getBusyMessage());
html.replace("%npcname%", getName());
html.replace("%playername%", player.getName());
player.sendPacket(html);
}
else if (command.equalsIgnoreCase("TerritoryStatus"))
{
NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (getCastle().getOwnerId() > 0)
{
html.setFile(StaticHtmPath.NpcHtmPath + "territorystatus.htm", player);
L2Clan clan = ClanTable.getInstance().getClan(getCastle().getOwnerId());
html.replace("%clanname%", clan.getName());
html.replace("%clanleadername%", clan.getLeaderName());
}
else
html.setFile(StaticHtmPath.NpcHtmPath + "territorynoclan.htm", player);
html.replace("%castlename%", getCastle().getName());
html.replace("%taxpercent%", "" + getCastle().getTaxPercent());
html.replace("%objectId%", String.valueOf(getObjectId()));
if (getCastle().getCastleId() > 6)
html.replace("%territory%", "The Kingdom of Elmore");
else
html.replace("%territory%", "The Kingdom of Aden");
player.sendPacket(html);
}
else if (command.startsWith("Quest"))
{
String quest = "";
try
{
quest = command.substring(5).trim();
}
catch (IndexOutOfBoundsException ioobe)
{
}
if (quest.length() == 0)
showQuestWindow(player, this);
else
showQuestWindow(player, this, quest);
}
else if (command.startsWith("Chat"))
{
int val = 0;
try
{
val = Integer.parseInt(command.substring(5));
}
catch (IndexOutOfBoundsException ioobe)
{
}
catch (NumberFormatException nfe)
{
}
showChatWindow(player, val);
}
else if (command.startsWith("Link"))
{
String path = command.substring(5).trim();
if (path.indexOf("..") != -1)
return;
String filename = StaticHtmPath.NpcHtmPath + path;
NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(filename, player);
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
}
else if (command.startsWith("Loto"))
{
int val = 0;
try
{
val = Integer.parseInt(command.substring(5));
}
catch (IndexOutOfBoundsException ioobe)
{
}
catch (NumberFormatException nfe)
{
}
if (val == 0)
{
// new loto ticket
for (int i = 0; i < 5; i++)
player.setLoto(i, 0);
}
showLotoWindow(player, val);
}
else if (command.startsWith("CPRecovery"))
{
makeCPRecovery(player);
}
else if (command.startsWith("SupportMagic"))
{
makeSupportMagic(player);
}
else if (command.startsWith("multisell"))
{
L2Multisell.getInstance().separateAndSend(Integer.parseInt(command.substring(9).trim()), player, false, getCastle().getTaxRate());
}
else if (command.startsWith("exc_multisell"))
{
L2Multisell.getInstance().separateAndSend(Integer.parseInt(command.substring(13).trim()), player, true, getCastle().getTaxRate());
}
else if (command.startsWith("Augment"))
{
int cmdChoice = Integer.parseInt(command.substring(8, 9).trim());
switch (cmdChoice)
{
case 1:
player.sendPacket(SystemMessageId.SELECT_THE_ITEM_TO_BE_AUGMENTED);
player.sendPacket(ExShowVariationMakeWindow.STATIC_PACKET);
break;
case 2:
player.sendPacket(SystemMessageId.SELECT_THE_ITEM_FROM_WHICH_YOU_WISH_TO_REMOVE_AUGMENTATION);
player.sendPacket(ExShowVariationCancelWindow.STATIC_PACKET);
break;
}
}
else if (command.startsWith("EnterRift"))
{
try
{
Byte b1 = Byte.parseByte(command.substring(10)); // Selected Area: Recruit, Soldier etc
DimensionalRiftManager.getInstance().start(player, b1, this);
}
catch (Exception e)
{
}
}
else if (command.startsWith("ChangeRiftRoom"))
{
if (player.isInParty() && player.getParty().isInDimensionalRift())
{
player.getParty().getDimensionalRift().manualTeleport(player, this);
}
else
{
DimensionalRiftManager.getInstance().handleCheat(player, this);
}
}
else if (command.startsWith("ExitRift"))
{
if (player.isInParty() && player.getParty().isInDimensionalRift())
{
player.getParty().getDimensionalRift().manualExitRift(player, this);
}
else
{
DimensionalRiftManager.getInstance().handleCheat(player, this);
}
}
}
}
/**
* Return null (regular NPCs don't have weapons instancies).<BR>
* <BR>
*/
@Override
public L2ItemInstance getActiveWeaponInstance()
{
return null;
}
/**
* Return the weapon item equipped in the right hand of the L2Npc or null.<BR>
* <BR>
*/
@Override
public L2Weapon getActiveWeaponItem()
{
// Get the weapon identifier equipped in the right hand of the L2Npc
int weaponId = getTemplate().getRightHand();
if (weaponId < 1)
return null;
// Get the weapon item equipped in the right hand of the L2Npc
L2Item item = ItemTable.getInstance().getTemplate(weaponId);
if (!(item instanceof L2Weapon))
return null;
return (L2Weapon) item;
}
/**
* Return null (regular NPCs don't have weapons instancies).<BR>
* <BR>
*/
@Override
public L2ItemInstance getSecondaryWeaponInstance()
{
return null;
}
/**
* Return the item equipped in the left hand of the L2Npc or null.<BR>
* <BR>
*/
@Override
public L2Item getSecondaryWeaponItem()
{
// Get the weapon identifier equipped in the right hand of the L2Npc
int itemId = getTemplate().getLeftHand();
if (itemId < 1)
return null;
// Return the item equipped in the left hand of the L2Npc
return ItemTable.getInstance().getTemplate(itemId);
}
/**
* Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2Npc.<BR>
* <BR>
*
* @param player
* The L2PcInstance who talks with the L2Npc
* @param content
* The text of the L2NpcMessage
*/
public void insertObjectIdAndShowChatWindow(L2PcInstance player, String content)
{
// Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2Npc
content = content.replaceAll("%objectId%", String.valueOf(getObjectId()));
NpcHtmlMessage npcReply = new NpcHtmlMessage(getObjectId());
npcReply.setHtml(content);
player.sendPacket(npcReply);
}
/**
* <B><U> Format of the pathfile </U> :</B><BR>
* <BR>
* <li>if the file exists on the server (page number = 0) : <B>../npc/default/12006.htm</B> (npcId-page number)</li> <li>if the file exists
* on the server (page number > 0) : <B>../npc/default/12006-1.htm</B> (npcId-page number)</li> <li>if the file doesn't exist on the server :
* <B>../npc/npcdefault.htm</B> (message : "I have nothing to say to you")</li><BR>
* <BR>
* <B><U> Overriden in </U> :</B><BR>
* <BR>
* <li>L2GuardInstance : Set the pathfile to ../npc/guard/12006-1.htm (npcId-page number)</li><BR>
* <BR>
*
* @param npcId
* The Identifier of the L2Npc whose text must be display
* @param val
* The number of the page to display
* @return the pathfile of the selected HTML file in function of the npcId and of the page number.
*/
public String getHtmlPath(int npcId, int val)
{
final String pom = val == 0 ? String.valueOf(npcId) : npcId + "-" + val;
final String temp = StaticHtmPath.DefaultHtmPath + pom + ".htm";
if (HtmCache.isLoadable(temp))
return temp;
return StaticHtmPath.NpcHtmPath + "npcdefault.htm";
}
/**
* Open a choose quest window on client with all quests available of the L2NpcInstance.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li><BR>
* <BR>
*
* @param player
* The L2PcInstance that talk with the L2NpcInstance
* @param npc
* The L2Npc instance.
* @param quests
* The table containing quests of the L2NpcInstance
*/
public static void showQuestChooseWindow(L2PcInstance player, L2Npc npc, Quest[] quests)
{
final StringBuilder sb = StringUtil.startAppend(150, "<html><body>");
for (Quest q : quests)
{
if (q == null)
continue;
StringUtil.append(sb, "<a action=\"bypass -h npc_", String.valueOf(npc.getObjectId()), "_Quest ", q.getName(), "\">[", q.getDisplayName());
QuestState qs = player.getQuestState(q.getScriptName());
if (qs != null)
{
if (qs.isStarted() && (qs.getInt("cond") > 0))
sb.append(" (In Progress)");
else if (qs.isCompleted())
sb.append(" (Done)");
}
sb.append("]</a><br>");
}
sb.append("</body></html>");
// Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
npc.insertObjectIdAndShowChatWindow(player, sb.toString());
}
/**
* Open a quest window on client with the text of the L2NpcInstance.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Get the text of the quest state in the folder ../quests/questId/stateId.htm</li> <li>Send a Server->Client NpcHtmlMessage containing
* the text of the L2NpcInstance to the L2PcInstance</li> <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that
* the client wait another packet</li><BR>
* <BR>
*
* @param player
* The L2PcInstance that talk with the L2NpcInstance
* @param npc
* The L2Npc instance.
* @param questId
* The Identifier of the quest to display the message
*/
public static void showQuestWindow(L2PcInstance player, L2Npc npc, String questId)
{
String content = null;
Quest q = QuestManager.getInstance().getQuest(questId);
// Get the state of the selected quest
QuestState qs = player.getQuestState(questId);
if (q != null)
{
if ((q.getQuestIntId() >= 1 && q.getQuestIntId() < 20000) && (player.getWeightPenalty() >= 3 || !player.isInventoryUnder80(true)))
{
player.sendPacket(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT);
return;
}
if (qs == null)
{
if (q.getQuestIntId() >= 1 && q.getQuestIntId() < 20000)
{
if (player.getAllActiveQuests().length > 40) // if too many ongoing quests, don't show window and send message
{
player.sendPacket(SystemMessageId.TOO_MANY_QUESTS);
return;
}
}
// check for start point
Quest[] qlst = npc.getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
if (qlst != null && qlst.length > 0)
{
for (Quest temp : qlst)
{
if (temp == q)
{
qs = q.newQuestState(player);
break;
}
}
}
}
}
else
content = Quest.getNoQuestMsg(); // no quests found
if (qs != null)
{
// If the quest is alreday started, no need to show a window
if (!qs.getQuest().notifyTalk(npc, qs))
return;
questId = qs.getQuest().getName();
String stateId = State.getStateName(qs.getState());
String path = StaticHtmPath.QuestHtmPath + questId + "/" + stateId + ".htm";
content = HtmCache.getInstance().getHtm(path); // TODO path for quests html
if (_log.isTraceEnabled())
{
if (content != null)
_log.trace("Showing quest window for quest " + questId + " html path: " + path);
else
_log.trace("File not exists for quest " + questId + " html path: " + path);
}
}
// Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
if (content != null)
npc.insertObjectIdAndShowChatWindow(player, content);
// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
player.sendPacket(ActionFailed.STATIC_PACKET);
}
/**
* Collect awaiting quests/start points and display a QuestChooseWindow (if several available) or QuestWindow.<BR>
* <BR>
*
* @param player
* The L2PcInstance that talk with the L2NpcInstance
* @param npc
* The L2Npc instance.
*/
public static void showQuestWindow(L2PcInstance player, L2Npc npc)
{
// collect awaiting quests and start points
List<Quest> options = new FastList<>();
QuestState[] awaits = player.getQuestsForTalk(npc.getTemplate().getNpcId());
Quest[] starts = npc.getTemplate().getEventQuests(Quest.QuestEventType.QUEST_START);
// Quests are limited between 1 and 999 because those are the quests that are supported by the client.
// By limiting them there, we are allowed to create custom quests at higher IDs without interfering
if (awaits != null)
{
for (QuestState x : awaits)
{
if (!options.contains(x.getQuest()))
if ((x.getQuest().getQuestIntId() > 0) && (x.getQuest().getQuestIntId() < 20000))
options.add(x.getQuest());
}
}
if (starts != null)
{
for (Quest x : starts)
{
if (!options.contains(x))
if ((x.getQuestIntId() > 0) && (x.getQuestIntId() < 20000))
options.add(x);
}
}
// Display a QuestChooseWindow (if several quests are available) or QuestWindow
if (options.size() > 1)
showQuestChooseWindow(player, npc, options.toArray(new Quest[options.size()]));
else if (options.size() == 1)
showQuestWindow(player, npc, options.get(0).getName());
else
showQuestWindow(player, npc, "");
}
/**
* Make the NPC speaks to his current knownlist.
*
* @param message
* The String message to send.
*/
public void broadcastNpcSay(String message)
{
broadcastPacket(new NpcSay(getObjectId(), Say2.ALL, getNpcId(), message));
}
/**
* Open a Loto window on client with the text of the L2Npc.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Get the text of the selected HTML file in function of the npcId and of the page number</li> <li>Send a Server->Client NpcHtmlMessage
* containing the text of the L2Npc to the L2PcInstance</li> <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid
* that the client wait another packet</li><BR>
*
* @param player
* The L2PcInstance that talk with the L2Npc
* @param val
* The number of the page of the L2Npc to display
*/
// 0 - first buy lottery ticket window
// 1-20 - buttons
// 21 - second buy lottery ticket window
// 22 - selected ticket with 5 numbers
// 23 - current lottery jackpot
// 24 - Previous winning numbers/Prize claim
// >24 - check lottery ticket by item object id
public void showLotoWindow(L2PcInstance player, int val)
{
int npcId = getTemplate().getNpcId();
String filename;
SystemMessage sm;
NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
if (val == 0) // 0 - first buy lottery ticket window
{
filename = (getHtmlPath(npcId, 1));
html.setFile(filename, player);
}
else if (val >= 1 && val <= 21) // 1-20 - buttons, 21 - second buy lottery ticket window
{
if (!Lottery.getInstance().isStarted())
{
// tickets can't be sold
player.sendPacket(SystemMessageId.NO_LOTTERY_TICKETS_CURRENT_SOLD);
return;
}
if (!Lottery.getInstance().isSellableTickets())
{
// tickets can't be sold
player.sendPacket(SystemMessageId.NO_LOTTERY_TICKETS_AVAILABLE);
return;
}
filename = (getHtmlPath(npcId, 5));
html.setFile(filename, player);
int count = 0;
int found = 0;
// counting buttons and unsetting button if found
for (int i = 0; i < 5; i++)
{
if (player.getLoto(i) == val)
{
// unsetting button
player.setLoto(i, 0);
found = 1;
}
else if (player.getLoto(i) > 0)
{
count++;
}
}
// if not rearched limit 5 and not unseted value
if (count < 5 && found == 0 && val <= 20)
for (int i = 0; i < 5; i++)
if (player.getLoto(i) == 0)
{
player.setLoto(i, val);
break;
}
// setting pusshed buttons
count = 0;
for (int i = 0; i < 5; i++)
if (player.getLoto(i) > 0)
{
count++;
String button = String.valueOf(player.getLoto(i));
if (player.getLoto(i) < 10)
button = "0" + button;
String search = "fore=\"L2UI.lottoNum" + button + "\" back=\"L2UI.lottoNum" + button + "a_check\"";
String replace = "fore=\"L2UI.lottoNum" + button + "a_check\" back=\"L2UI.lottoNum" + button + "\"";
html.replace(search, replace);
}
if (count == 5)
{
String search = "0\">Return";
String replace = "22\">The winner selected the numbers above.";
html.replace(search, replace);
}
}
else if (val == 22) // 22 - selected ticket with 5 numbers
{
if (!Lottery.getInstance().isStarted())
{
// tickets can't be sold
player.sendPacket(SystemMessageId.NO_LOTTERY_TICKETS_CURRENT_SOLD);
return;
}
if (!Lottery.getInstance().isSellableTickets())
{
// tickets can't be sold
player.sendPacket(SystemMessageId.NO_LOTTERY_TICKETS_AVAILABLE);
return;
}
int price = EventsConfig.ALT_LOTTERY_TICKET_PRICE;
int lotonumber = Lottery.getInstance().getId();
int enchant = 0;
int type2 = 0;
for (int i = 0; i < 5; i++)
{
if (player.getLoto(i) == 0)
return;
if (player.getLoto(i) < 17)
enchant += Math.pow(2, player.getLoto(i) - 1);
else
type2 += Math.pow(2, player.getLoto(i) - 17);
}
if (player.getAdena() < price)
{
sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
player.sendPacket(sm);
return;
}
if (!player.reduceAdena("Loto", price, this, true))
return;
Lottery.getInstance().increasePrize(price);
sm = SystemMessage.getSystemMessage(SystemMessageId.ACQUIRED_S1_S2);
sm.addNumber(lotonumber);
sm.addItemName(4442);
player.sendPacket(sm);
L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), 4442);
item.setCount(1);
item.setCustomType1(lotonumber);
item.setEnchantLevel(enchant);
item.setCustomType2(type2);
player.getInventory().addItem("Loto", item, player, this);
InventoryUpdate iu = new InventoryUpdate();
iu.addItem(item);
L2ItemInstance adenaupdate = player.getInventory().getItemByItemId(57);
iu.addModifiedItem(adenaupdate);
player.sendPacket(iu);
filename = (getHtmlPath(npcId, 3));
html.setFile(filename, player);
}
else if (val == 23) // 23 - current lottery jackpot
{
filename = (getHtmlPath(npcId, 3));
html.setFile(filename, player);
}
else if (val == 24) // 24 - Previous winning numbers/Prize claim
{
filename = (getHtmlPath(npcId, 4));
html.setFile(filename, player);
int lotonumber = Lottery.getInstance().getId();
String message = "";
for (L2ItemInstance item : player.getInventory().getItems())
{
if (item == null)
continue;
if (item.getItemId() == 4442 && item.getCustomType1() < lotonumber)
{
message = message + "<a action=\"bypass -h npc_%objectId%_Loto " + item.getObjectId() + "\">" + item.getCustomType1() + " Event Number ";
int[] numbers = Lottery.decodeNumbers(item.getEnchantLevel(), item.getCustomType2());
for (int i = 0; i < 5; i++)
{
message += numbers[i] + " ";
}
int[] check = Lottery.checkTicket(item);
if (check[0] > 0)
{
switch (check[0])
{
case 1:
message += "- 1st Prize";
break;
case 2:
message += "- 2nd Prize";
break;
case 3:
message += "- 3th Prize";
break;
case 4:
message += "- 4th Prize";
break;
}
message += " " + check[1] + "a.";
}
message += "</a><br>";
}
}
if (message.isEmpty())
message += "There is no winning lottery ticket...<br>";
html.replace("%result%", message);
}
else if (val > 24) // >24 - check lottery ticket by item object id
{
int lotonumber = Lottery.getInstance().getId();
L2ItemInstance item = player.getInventory().getItemByObjectId(val);
if (item == null || item.getItemId() != 4442 || item.getCustomType1() >= lotonumber)
return;
int[] check = Lottery.checkTicket(item);
sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
sm.addItemName(4442);
player.sendPacket(sm);
int adena = check[1];
if (adena > 0)
player.addAdena("Loto", adena, this, true);
player.destroyItem("Loto", item, this, false);
return;
}
html.replace("%objectId%", String.valueOf(getObjectId()));
html.replace("%race%", "" + Lottery.getInstance().getId());
html.replace("%adena%", "" + Lottery.getInstance().getPrize());
html.replace("%ticket_price%", "" + EventsConfig.ALT_LOTTERY_TICKET_PRICE);
html.replace("%prize5%", "" + (EventsConfig.ALT_LOTTERY_5_NUMBER_RATE * 100));
html.replace("%prize4%", "" + (EventsConfig.ALT_LOTTERY_4_NUMBER_RATE * 100));
html.replace("%prize3%", "" + (EventsConfig.ALT_LOTTERY_3_NUMBER_RATE * 100));
html.replace("%prize2%", "" + EventsConfig.ALT_LOTTERY_2_AND_1_NUMBER_PRIZE);
html.replace("%enddate%", "" + DateFormat.getDateInstance().format(Lottery.getInstance().getEndDate()));
player.sendPacket(html);
// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
player.sendPacket(ActionFailed.STATIC_PACKET);
}
public void makeCPRecovery(L2PcInstance player)
{
if (getNpcId() != 31225 && getNpcId() != 31226)
return;
if (player.isCursedWeaponEquipped())
{
player.sendMessage("Go away, you're not welcome here.");
return;
}
// Consume 100 adenas
if (player.reduceAdena("RestoreCP", 100, player.getCurrentFolkNPC(), true))
{
setTarget(player);
doCast(FrequentSkill.ARENA_CP_RECOVERY.getSkill());
player.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_CP_WILL_BE_RESTORED).addPcName(player));
}
else
player.sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
}
/**
* Add Newbie helper buffs to L2Player according to its level.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Get the range level in wich player must be to obtain buff</li> <li>If player level is out of range, display a message and return</li>
* <li>According to player level cast buff</li><BR>
* <BR>
* <FONT COLOR=#FF0000><B> Newbie Helper Buff list is define in sql table helper_buff_list</B></FONT><BR>
* <BR>
*
* @param player
* The L2PcInstance that talk with the L2Npc
*/
public void makeSupportMagic(L2PcInstance player)
{
if (player == null)
return;
// Prevent a cursed weapon weilder of being buffed
if (player.isCursedWeaponEquipped())
return;
int player_level = player.getLevel();
int lowestLevel = 0;
int higestLevel = 0;
// Select the player
setTarget(player);
// Calculate the min and max level between wich the player must be to obtain buff
if (player.isMageClass())
{
lowestLevel = HelperBuffData.getInstance().getMagicClassLowestLevel();
higestLevel = HelperBuffData.getInstance().getMagicClassHighestLevel();
}
else
{
lowestLevel = HelperBuffData.getInstance().getPhysicClassLowestLevel();
higestLevel = HelperBuffData.getInstance().getPhysicClassHighestLevel();
}
// If the player is too high level, display a message and return
if (player_level > higestLevel || !player.isNewbie())
{
String content = "<html><body>Newbie Guide:<br>Only a <font color=\"LEVEL\">novice character of level " + higestLevel + " or less</font> can receive my support magic.<br>Your novice character is the first one that you created and raised in this world.</body></html>";
insertObjectIdAndShowChatWindow(player, content);
return;
}
// If the player is too low level, display a message and return
if (player_level < lowestLevel)
{
String content = "<html><body>Come back here when you have reached level " + lowestLevel + ". I will give you support magic then.</body></html>";
insertObjectIdAndShowChatWindow(player, content);
return;
}
L2Skill skill = null;
// Go through the Helper Buff list define in sql table helper_buff_list and cast skill
for (L2HelperBuff helperBuffItem : HelperBuffData.getInstance().getHelperBuffData())
{
if (helperBuffItem.isMagicClassBuff() == player.isMageClass())
{
if (player_level >= helperBuffItem.getLowerLevel() && player_level <= helperBuffItem.getUpperLevel())
{
skill = SkillTable.getInstance().getInfo(helperBuffItem.getSkillID(), helperBuffItem.getSkillLevel());
if (skill.getSkillType() == L2SkillType.SUMMON)
player.doCast(skill);
else
doCast(skill);
}
}
}
}
/**
* Returns true if html exists
*
* @param player
* @param type
* @return boolean
*/
private boolean showPkDenyChatWindow(L2PcInstance player, String type)
{
String html = HtmCache.getInstance().getHtm(StaticHtmPath.NpcHtmPath + type + "/" + getNpcId() + "-pk.htm");
if (html != null)
{
NpcHtmlMessage pkDenyMsg = new NpcHtmlMessage(getObjectId());
pkDenyMsg.setHtml(html);
player.sendPacket(pkDenyMsg);
player.sendPacket(ActionFailed.STATIC_PACKET);
return true;
}
return false;
}
/**
* Open a chat window on client with the text of the L2Npc.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Get the text of the selected HTML file in function of the npcId and of the page number</li> <li>Send a Server->Client NpcHtmlMessage
* containing the text of the L2Npc to the L2PcInstance</li> <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid
* that the client wait another packet</li><BR>
*
* @param player
* The L2PcInstance that talk with the L2Npc
*/
public void showChatWindow(L2PcInstance player)
{
showChatWindow(player, 0);
}
public void showChatWindow(L2PcInstance player, int val)
{
if (player.getKarma() > 0)
{
if (!PlayersConfig.KARMA_PLAYER_CAN_SHOP && this instanceof L2MerchantInstance)
{
if (showPkDenyChatWindow(player, "merchant"))
return;
}
else if (!PlayersConfig.KARMA_PLAYER_CAN_USE_GK && this instanceof L2TeleporterInstance)
{
if (showPkDenyChatWindow(player, "teleporter"))
return;
}
else if (!PlayersConfig.KARMA_PLAYER_CAN_USE_WH && this instanceof L2WarehouseInstance)
{
if (showPkDenyChatWindow(player, "warehouse"))
return;
}
else if (!PlayersConfig.KARMA_PLAYER_CAN_SHOP && this instanceof L2FishermanInstance)
{
if (showPkDenyChatWindow(player, "fisherman"))
return;
}
}
final int npcId = getNpcId();
String filename;
if (npcId >= 31865 && npcId <= 31918)
filename = SevenSigns.SEVEN_SIGNS_HTML_PATH + "rift/GuardianOfBorder.htm";
else
filename = getHtmlPath(npcId, val);
// Send a Server->Client NpcHtmlMessage containing the text of the L2Npc to the L2PcInstance
NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(filename, player);
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
player.sendPacket(ActionFailed.STATIC_PACKET);
}
/**
* Open a chat window on client with the text specified by the given file name and path,<BR>
* relative to the datapack root. <BR>
* <BR>
* Added by Tempy
*
* @param player
* The L2PcInstance that talk with the L2Npc
* @param filename
* The filename that contains the text to send
*/
public void showChatWindow(L2PcInstance player, String filename)
{
// Send a Server->Client NpcHtmlMessage containing the text of the L2Npc to the L2PcInstance
NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
html.setFile(filename, player);
html.replace("%objectId%", String.valueOf(getObjectId()));
player.sendPacket(html);
// Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
player.sendPacket(ActionFailed.STATIC_PACKET);
}
/**
* @return the Exp Reward of this L2Npc contained in the L2NpcTemplate (modified by RATE_XP).
*/
public int getExpReward(int isPremium)
{
if (isPremium == 1)
return (int) (getTemplate().getRewardExp() * CustomConfig.PREMIUM_RATE_XP);
else
return (int) (getTemplate().getRewardExp() * MainConfig.RATE_XP);
}
/**
* @return the SP Reward of this L2Npc contained in the L2NpcTemplate (modified by RATE_SP).
*/
public int getSpReward(int isPremium)
{
if (isPremium == 1)
return (int) (getTemplate().getRewardSp() * CustomConfig.PREMIUM_RATE_SP);
else
return (int) (getTemplate().getRewardSp() * MainConfig.RATE_SP);
}
/**
* Kill the L2Npc (the corpse disappeared after 7 seconds).<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Create a DecayTask to remove the corpse of the L2Npc after 7 seconds</li> <li>Set target to null and cancel Attack or Cast</li> <li>
* Stop movement</li> <li>Stop HP/MP/CP Regeneration task</li> <li>Stop all active skills effects in progress on the L2Character</li> <li>
* Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform</li> <li>Notify L2Character AI</li><BR>
* <BR>
* <B><U> Overriden in </U> :</B><BR>
* <BR>
* <li>L2Attackable</li><BR>
* <BR>
*
* @param killer
* The L2Character who killed it
*/
@Override
public boolean doDie(L2Character killer)
{
if (!super.doDie(killer))
return false;
// normally this wouldn't really be needed, but for those few exceptions,
// we do need to reset the weapons back to the initial templated weapon.
_currentLHandId = getTemplate().getLeftHand();
_currentRHandId = getTemplate().getRightHand();
_currentCollisionHeight = getTemplate().getCollisionHeight();
_currentCollisionRadius = getTemplate().getCollisionRadius();
DecayTaskManager.getInstance().addDecayTask(this);
return true;
}
/**
* Set the spawn of the L2Npc.<BR>
* <BR>
*
* @param spawn
* The L2Spawn that manage the L2Npc
*/
public void setSpawn(L2Spawn spawn)
{
_spawn = spawn;
}
@Override
public void onSpawn()
{
super.onSpawn();
if (getTemplate().getEventQuests(Quest.QuestEventType.ON_SPAWN) != null)
for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_SPAWN))
quest.notifySpawn(this);
}
/**
* Remove the L2Npc from the world and update its spawn object (for a complete removal use the deleteMe method).<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Remove the L2Npc from the world when the decay task is launched</li> <li>Decrease its spawn counter</li> <li>Manage Siege task
* (killFlag, killCT)</li><BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T REMOVE the object from _allObjects of L2World </B></FONT><BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR>
* <BR>
*/
@Override
public void onDecay()
{
if (isDecayed())
return;
setDecayed(true);
// Remove the L2Npc from the world when the decay task is launched
super.onDecay();
// Decrease its spawn counter
if (_spawn != null)
_spawn.decreaseCount(this);
}
/**
* Remove PROPERLY the L2Npc from the world.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Remove the L2Npc from the world and update its spawn object</li> <li>Remove all L2Object from _knownObjects and _knownPlayer of the
* L2Npc then cancel Attak or Cast and notify AI</li> <li>Remove L2Object object from _allObjects of L2World</li><BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR>
* <BR>
*/
@Override
public void deleteMe()
{
// Decay
try
{
onDecay();
}
catch (Exception e)
{
_log.warn("Failed decayMe().", e);
}
try
{
if (_fusionSkill != null)
abortCast();
for (L2Character character : getKnownList().getKnownCharacters())
if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
character.abortCast();
}
catch (Exception e)
{
_log.warn(e.getLocalizedMessage(), e);
}
L2WorldRegion oldRegion = getWorldRegion();
if (oldRegion != null)
oldRegion.removeFromZones(this);
// Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI
try
{
getKnownList().removeAllKnownObjects();
}
catch (Exception e)
{
_log.warn("Failed removing cleaning knownlist.", e);
}
// Remove L2Object object from _allObjects of L2World
L2World.getInstance().removeObject(this);
super.deleteMe();
}
/**
* @return the L2Spawn object that manage this L2Npc.
*/
public L2Spawn getSpawn()
{
return _spawn;
}
@Override
public String toString()
{
return getTemplate().getName();
}
public boolean isDecayed()
{
return _isDecayed;
}
public void setDecayed(boolean decayed)
{
_isDecayed = decayed;
}
public void endDecayTask()
{
if (!isDecayed())
{
DecayTaskManager.getInstance().cancelDecayTask(this);
onDecay();
}
}
/**
* Used for animation timers, overridden in L2Attackable.
*
* @return true if L2Attackable, false otherwise.
*/
public boolean isMob()
{
return false; // This means we use MAX_NPC_ANIMATION instead of MAX_MONSTER_ANIMATION
}
public void setLHandId(int newWeaponId)
{
_currentLHandId = newWeaponId;
}
public void setRHandId(int newWeaponId)
{
_currentRHandId = newWeaponId;
}
public void setCollisionHeight(int height)
{
_currentCollisionHeight = height;
}
public int getCollisionHeight()
{
return _currentCollisionHeight;
}
public void setCollisionRadius(int radius)
{
_currentCollisionRadius = radius;
}
public int getCollisionRadius()
{
return _currentCollisionRadius;
}
public int getCorpseDecayTime()
{
return getTemplate().getCorpseDecayTime() * 1000;
}
public L2Npc scheduleDespawn(long delay)
{
ThreadPoolManager.getInstance().scheduleGeneral(this.new DespawnTask(), delay);
return this;
}
protected class DespawnTask implements Runnable
{
@Override
public void run()
{
if (!isDecayed())
deleteMe();
}
}
@Override
protected final void notifyQuestEventSkillFinished(L2Skill skill, L2Object target)
{
try
{
if (getTemplate().getEventQuests(Quest.QuestEventType.ON_SPELL_FINISHED) != null)
{
L2PcInstance player = null;
if (target != null)
player = target.getActingPlayer();
for (Quest quest : getTemplate().getEventQuests(Quest.QuestEventType.ON_SPELL_FINISHED))
{
quest.notifySpellFinished(this, player, skill);
}
}
}
catch (Exception e)
{
_log.warn(e.getLocalizedMessage(), e);
}
}
/*
* (non-Javadoc)
* @see silentium.gameserver.model.actor.L2Character#isMovementDisabled()
*/
@Override
public boolean isMovementDisabled()
{
return super.isMovementDisabled() || getCanMove() == 0 || getAiType().equals(AIType.CORPSE);
}
public AIType getAiType()
{
return _staticAIData.getAiType();
}
@Override
public void sendInfo(L2PcInstance activeChar)
{
if (getRunSpeed() == 0)
activeChar.sendPacket(new ServerObjectInfo(this, activeChar));
else
activeChar.sendPacket(new NpcInfo(this, activeChar));
}
}