/*
* Copyright (C) 2004-2015 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server 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.
*
* L2J Server 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.l2jserver.gameserver.model.actor;
import com.l2jserver.Config;
import com.l2jserver.gameserver.ai.CtrlIntention;
import com.l2jserver.gameserver.ai.L2CharacterAI;
import com.l2jserver.gameserver.ai.L2SummonAI;
import com.l2jserver.gameserver.data.xml.impl.ExperienceData;
import com.l2jserver.gameserver.datatables.ItemTable;
import com.l2jserver.gameserver.enums.InstanceType;
import com.l2jserver.gameserver.enums.Race;
import com.l2jserver.gameserver.enums.ShotType;
import com.l2jserver.gameserver.enums.Team;
import com.l2jserver.gameserver.handler.IItemHandler;
import com.l2jserver.gameserver.handler.ItemHandler;
import com.l2jserver.gameserver.model.AggroInfo;
import com.l2jserver.gameserver.model.L2Object;
import com.l2jserver.gameserver.model.L2Party;
import com.l2jserver.gameserver.model.L2WorldRegion;
import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
import com.l2jserver.gameserver.model.actor.knownlist.SummonKnownList;
import com.l2jserver.gameserver.model.actor.stat.SummonStat;
import com.l2jserver.gameserver.model.actor.status.SummonStatus;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.effects.L2EffectType;
import com.l2jserver.gameserver.model.events.EventDispatcher;
import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerSummonSpawn;
import com.l2jserver.gameserver.model.itemcontainer.PetInventory;
import com.l2jserver.gameserver.model.items.L2EtcItem;
import com.l2jserver.gameserver.model.items.L2Weapon;
import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
import com.l2jserver.gameserver.model.items.type.ActionType;
import com.l2jserver.gameserver.model.olympiad.OlympiadGameManager;
import com.l2jserver.gameserver.model.skills.Skill;
import com.l2jserver.gameserver.model.skills.targets.L2TargetType;
import com.l2jserver.gameserver.model.zone.ZoneId;
import com.l2jserver.gameserver.network.SystemMessageId;
import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
import com.l2jserver.gameserver.network.serverpackets.ExPartyPetWindowAdd;
import com.l2jserver.gameserver.network.serverpackets.ExPartyPetWindowDelete;
import com.l2jserver.gameserver.network.serverpackets.ExPartyPetWindowUpdate;
import com.l2jserver.gameserver.network.serverpackets.ExPetInfo;
import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jserver.gameserver.network.serverpackets.PetDelete;
import com.l2jserver.gameserver.network.serverpackets.PetInfo;
import com.l2jserver.gameserver.network.serverpackets.PetItemList;
import com.l2jserver.gameserver.network.serverpackets.PetStatusUpdate;
import com.l2jserver.gameserver.network.serverpackets.RelationChanged;
import com.l2jserver.gameserver.network.serverpackets.SummonInfo;
import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
import com.l2jserver.gameserver.network.serverpackets.TeleportToLocation;
import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
import com.l2jserver.gameserver.util.Util;
import com.l2jserver.util.Rnd;
public abstract class L2Summon extends L2Playable
{
private L2PcInstance _owner;
private int _attackRange = 36; // Melee range
private boolean _follow = true;
private boolean _previousFollowStatus = true;
protected boolean _restoreSummon = true;
private int _shotsMask = 0;
private int _summonPoints = 0;
// @formatter:off
private static final int[] PASSIVE_SUMMONS =
{
12564, 12621, 14702, 14703, 14704, 14705, 14706, 14707, 14708, 14709, 14710, 14711,
14712, 14713, 14714, 14715, 14716, 14717, 14718, 14719, 14720, 14721, 14722, 14723,
14724, 14725, 14726, 14727, 14728, 14729, 14730, 14731, 14732, 14733, 14734, 14735, 14736
};
// @formatter:on
/**
* Creates an abstract summon.
* @param template the summon NPC template
* @param owner the owner
*/
public L2Summon(L2NpcTemplate template, L2PcInstance owner)
{
super(template);
setInstanceType(InstanceType.L2Summon);
setInstanceId(owner.getInstanceId()); // set instance to same as owner
setShowSummonAnimation(true);
_owner = owner;
getAI();
setXYZInvisible(owner.getX() + Rnd.get(-100, 100), owner.getY() + Rnd.get(-100, 100), owner.getZ());
}
@Override
public void onSpawn()
{
super.onSpawn();
if (Config.SUMMON_STORE_SKILL_COOLTIME && !isTeleporting())
{
restoreEffects();
}
setFollowStatus(true);
updateAndBroadcastStatus(0);
sendPacket(new RelationChanged(this, getOwner().getRelation(getOwner()), false));
for (L2PcInstance player : getOwner().getKnownList().getKnownPlayersInRadius(800))
{
player.sendPacket(new RelationChanged(this, getOwner().getRelation(player), isAutoAttackable(player)));
}
L2Party party = getOwner().getParty();
if (party != null)
{
party.broadcastToPartyMembers(getOwner(), new ExPartyPetWindowAdd(this));
}
setShowSummonAnimation(false); // addVisibleObject created the info packets with summon animation
// if someone comes into range now, the animation shouldn't show any more
_restoreSummon = false;
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnPlayerSummonSpawn(this), this);
}
@Override
public final SummonKnownList getKnownList()
{
return (SummonKnownList) super.getKnownList();
}
@Override
public void initKnownList()
{
setKnownList(new SummonKnownList(this));
}
@Override
public SummonStat getStat()
{
return (SummonStat) super.getStat();
}
@Override
public void initCharStat()
{
setStat(new SummonStat(this));
}
@Override
public SummonStatus getStatus()
{
return (SummonStatus) super.getStatus();
}
@Override
public void initCharStatus()
{
setStatus(new SummonStatus(this));
}
@Override
protected L2CharacterAI initAI()
{
return new L2SummonAI(this);
}
@Override
public L2NpcTemplate getTemplate()
{
return (L2NpcTemplate) super.getTemplate();
}
// this defines the action buttons, 1 for Summon, 2 for Pets
public abstract int getSummonType();
@Override
public final void stopAllEffects()
{
super.stopAllEffects();
updateAndBroadcastStatus(1);
}
@Override
public final void stopAllEffectsExceptThoseThatLastThroughDeath()
{
super.stopAllEffectsExceptThoseThatLastThroughDeath();
updateAndBroadcastStatus(1);
}
@Override
public void updateAbnormalVisualEffects()
{
for (L2PcInstance player : getKnownList().getKnownPlayers().values())
{
if (isPet())
{
player.sendPacket(new ExPetInfo(this, player, 1));
}
else
{
player.sendPacket(new SummonInfo(this, player, 1));
}
}
}
/**
* @return Returns the mountable.
*/
public boolean isMountable()
{
return false;
}
public long getExpForThisLevel()
{
if (getLevel() >= ExperienceData.getInstance().getMaxPetLevel())
{
return 0;
}
return ExperienceData.getInstance().getExpForLevel(getLevel());
}
public long getExpForNextLevel()
{
if (getLevel() >= (ExperienceData.getInstance().getMaxPetLevel() - 1))
{
return 0;
}
return ExperienceData.getInstance().getExpForLevel(getLevel() + 1);
}
@Override
public final int getKarma()
{
return getOwner() != null ? getOwner().getKarma() : 0;
}
@Override
public final byte getPvpFlag()
{
return getOwner() != null ? getOwner().getPvpFlag() : 0;
}
@Override
public final Team getTeam()
{
return getOwner() != null ? getOwner().getTeam() : Team.NONE;
}
public final L2PcInstance getOwner()
{
return _owner;
}
/**
* Gets the summon ID.
* @return the summon ID
*/
@Override
public final int getId()
{
return getTemplate().getId();
}
public short getSoulShotsPerHit()
{
if (getTemplate().getSoulShot() > 0)
{
return (short) getTemplate().getSoulShot();
}
return 1;
}
public short getSpiritShotsPerHit()
{
if (getTemplate().getSpiritShot() > 0)
{
return (short) getTemplate().getSpiritShot();
}
return 1;
}
public void followOwner()
{
setFollowStatus(true);
}
@Override
public boolean doDie(L2Character killer)
{
if (isNoblesseBlessedAffected())
{
stopEffects(L2EffectType.NOBLESSE_BLESSING);
storeEffect(true);
}
else
{
storeEffect(false);
}
if (!super.doDie(killer))
{
return false;
}
final L2PcInstance owner = getOwner();
if (owner != null)
{
for (L2Character TgMob : getKnownList().getKnownCharacters())
{
// get the mobs which have aggro on the this instance
if (TgMob instanceof L2Attackable)
{
if (TgMob.isDead())
{
continue;
}
AggroInfo info = ((L2Attackable) TgMob).getAggroList().get(this);
if (info != null)
{
((L2Attackable) TgMob).addDamageHate(owner, info.getDamage(), info.getHate());
}
}
}
}
DecayTaskManager.getInstance().add(this);
return true;
}
public boolean doDie(L2Character killer, boolean decayed)
{
if (!super.doDie(killer))
{
return false;
}
if (!decayed)
{
DecayTaskManager.getInstance().add(this);
}
return true;
}
public void stopDecay()
{
DecayTaskManager.getInstance().cancel(this);
}
@Override
public void onDecay()
{
deleteMe(_owner);
}
@Override
public void broadcastStatusUpdate()
{
super.broadcastStatusUpdate();
updateAndBroadcastStatus(1);
}
public void deleteMe(L2PcInstance owner)
{
if (owner != null)
{
owner.sendPacket(new PetDelete(getSummonType(), getObjectId()));
final L2Party party = owner.getParty();
if (party != null)
{
party.broadcastToPartyMembers(owner, new ExPartyPetWindowDelete(this));
}
}
// pet will be deleted along with all his items
if (getInventory() != null)
{
getInventory().destroyAllItems("pet deleted", getOwner(), this);
}
decayMe();
getKnownList().removeAllKnownObjects();
if (owner != null)
{
owner.setPet(null);
}
super.deleteMe();
}
public void unSummon(L2PcInstance owner)
{
if (isVisible() && !isDead())
{
getAI().stopFollow();
if (owner != null)
{
owner.sendPacket(new PetDelete(getSummonType(), getObjectId()));
final L2Party party = owner.getParty();
if (party != null)
{
party.broadcastToPartyMembers(owner, new ExPartyPetWindowDelete(this));
}
if ((getInventory() != null) && (getInventory().getSize() > 0))
{
getOwner().setPetInvItems(true);
sendPacket(SystemMessageId.THERE_ARE_ITEMS_IN_YOUR_PET_INVENTORY_RENDERING_YOU_UNABLE_TO_SELL_TRADE_DROP_PET_SUMMONING_ITEMS_PLEASE_EMPTY_YOUR_PET_INVENTORY);
}
else
{
getOwner().setPetInvItems(false);
}
}
abortAttack();
abortCast();
storeMe();
storeEffect(true);
if (owner != null)
{
if (isPet())
{
owner.setPet(null);
}
else
{
owner.removeServitor(getObjectId());
}
}
// Stop AI tasks
if (hasAI())
{
getAI().stopAITask();
}
stopAllEffects();
L2WorldRegion oldRegion = getWorldRegion();
decayMe();
if (oldRegion != null)
{
oldRegion.removeFromZones(this);
}
getKnownList().removeAllKnownObjects();
setTarget(null);
if (owner != null)
{
for (int itemId : owner.getAutoSoulShot())
{
String handler = ((L2EtcItem) ItemTable.getInstance().getTemplate(itemId)).getHandlerName();
if ((handler != null) && handler.contains("Beast"))
{
owner.disableAutoShot(itemId);
}
}
}
}
}
public int getAttackRange()
{
return _attackRange;
}
public void setAttackRange(int range)
{
_attackRange = (range < 36) ? 36 : range;
}
public void setFollowStatus(boolean state)
{
_follow = state;
if (_follow)
{
getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, getOwner());
}
else
{
getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null);
}
}
public boolean getFollowStatus()
{
return _follow;
}
@Override
public boolean isAutoAttackable(L2Character attacker)
{
return (_owner != null) && _owner.isAutoAttackable(attacker);
}
public int getControlObjectId()
{
return 0;
}
public L2Weapon getActiveWeapon()
{
return null;
}
@Override
public PetInventory getInventory()
{
return null;
}
public void setRestoreSummon(boolean val)
{
}
@Override
public L2ItemInstance getActiveWeaponInstance()
{
return null;
}
@Override
public L2Weapon getActiveWeaponItem()
{
return null;
}
@Override
public L2ItemInstance getSecondaryWeaponInstance()
{
return null;
}
@Override
public L2Weapon getSecondaryWeaponItem()
{
return null;
}
/**
* Return True if the L2Summon is invulnerable or if the summoner is in spawn protection.
*/
@Override
public boolean isInvul()
{
return super.isInvul() || getOwner().isSpawnProtected();
}
/**
* Return the L2Party object of its L2PcInstance owner or null.
*/
@Override
public L2Party getParty()
{
if (_owner == null)
{
return null;
}
return _owner.getParty();
}
/**
* Return True if the L2Character has a Party in progress.
*/
@Override
public boolean isInParty()
{
return (_owner != null) && _owner.isInParty();
}
/**
* Check if the active L2Skill can be casted.<br>
* <B><U>Actions</U>:</B>
* <ul>
* <li>Check if the target is correct</li>
* <li>Check if the target is in the skill cast range</li>
* <li>Check if the summon owns enough HP and MP to cast the skill</li>
* <li>Check if all skills are enabled and this skill is enabled</li>
* <li>Check if the skill is active</li>
* <li>Notify the AI with AI_INTENTION_CAST and target</li>
* </ul>
* @param skill The L2Skill to use
* @param forceUse used to force ATTACK on players
* @param dontMove used to prevent movement, if not in range
*/
@Override
public boolean useMagic(Skill skill, boolean forceUse, boolean dontMove)
{
// Null skill, dead summon or null owner are reasons to prevent casting.
if ((skill == null) || isDead() || (getOwner() == null))
{
return false;
}
// Check if the skill is active
if (skill.isPassive())
{
// just ignore the passive skill request. why does the client send it anyway ??
return false;
}
// If a skill is currently being used
if (isCastingNow())
{
return false;
}
// Set current pet skill
getOwner().setCurrentPetSkill(skill, forceUse, dontMove);
// Get the target for the skill
L2Object target = null;
switch (skill.getTargetType())
{
// OWNER_PET should be cast even if no target has been found
case OWNER_PET:
target = getOwner();
break;
// PARTY, AURA, SELF should be cast even if no target has been found
case PARTY:
case AURA:
case FRONT_AURA:
case BEHIND_AURA:
case SELF:
case AURA_CORPSE_MOB:
case COMMAND_CHANNEL:
target = this;
break;
default:
// Get the first target of the list
target = skill.getFirstOfTargetList(this);
break;
}
// Check the validity of the target
if (target == null)
{
sendPacket(SystemMessageId.YOUR_TARGET_CANNOT_BE_FOUND);
return false;
}
// Check if this skill is enabled (e.g. reuse time)
if (isSkillDisabled(skill))
{
sendPacket(SystemMessageId.THAT_PET_SERVITOR_SKILL_CANNOT_BE_USED_BECAUSE_IT_IS_RECHARGING);
return false;
}
// Check if the summon has enough MP
if (getCurrentMp() < (getStat().getMpConsume(skill) + getStat().getMpInitialConsume(skill)))
{
// Send a System Message to the caster
sendPacket(SystemMessageId.NOT_ENOUGH_MP);
return false;
}
// Check if the summon has enough HP
if (getCurrentHp() <= skill.getHpConsume())
{
// Send a System Message to the caster
sendPacket(SystemMessageId.NOT_ENOUGH_HP);
return false;
}
// Check if this is bad magic skill
if (skill.isBad())
{
if (getOwner() == target)
{
return false;
}
// Summons can cast skills on NPCs inside peace zones.
if (isInsidePeaceZone(this, target) && !getOwner().getAccessLevel().allowPeaceAttack())
{
// If summon or target is in a peace zone, send a system message:
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_THIS_TARGET_IN_A_PEACEFUL_ZONE);
return false;
}
// If L2PcInstance is in Olympiad and the match isn't already start, send a Server->Client packet ActionFailed
if (getOwner().isInOlympiadMode() && !getOwner().isOlympiadStart())
{
sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
if ((target.getActingPlayer() != null) && (getOwner().getSiegeState() > 0) && getOwner().isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getOwner().getSiegeState()) && (target.getActingPlayer() != getOwner()) && (target.getActingPlayer().getSiegeSide() == getOwner().getSiegeSide()))
{
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
// Check if the target is attackable
if (target.isDoor())
{
if (!target.isAutoAttackable(getOwner()))
{
return false;
}
}
else
{
// Summons can cast skills on NPCs inside peace zones.
if (!target.canBeAttacked() && !getOwner().getAccessLevel().allowPeaceAttack())
{
return false;
}
// Check if a Forced attack is in progress on non-attackable target
if (!target.isAutoAttackable(this) && !forceUse && !target.isNpc() && (skill.getTargetType() != L2TargetType.AURA) && (skill.getTargetType() != L2TargetType.FRONT_AURA) && (skill.getTargetType() != L2TargetType.BEHIND_AURA) && (skill.getTargetType() != L2TargetType.CLAN) && (skill.getTargetType() != L2TargetType.PARTY) && (skill.getTargetType() != L2TargetType.SELF))
{
return false;
}
}
}
// Notify the AI with AI_INTENTION_CAST and target
getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target);
return true;
}
@Override
public void setIsImmobilized(boolean value)
{
super.setIsImmobilized(value);
if (value)
{
_previousFollowStatus = getFollowStatus();
// if immobilized temporarily disable follow mode
if (_previousFollowStatus)
{
setFollowStatus(false);
}
}
else
{
// if not more immobilized restore previous follow mode
setFollowStatus(_previousFollowStatus);
}
}
public void setOwner(L2PcInstance newOwner)
{
_owner = newOwner;
}
@Override
public void sendDamageMessage(L2Character target, int damage, boolean mcrit, boolean pcrit, boolean miss)
{
if (miss || (getOwner() == null))
{
return;
}
// Prevents the double spam of system messages, if the target is the owning player.
if (target.getObjectId() != getOwner().getObjectId())
{
if (pcrit || mcrit)
{
if (isServitor())
{
sendPacket(SystemMessageId.SUMMONED_MONSTER_S_CRITICAL_HIT);
}
else
{
sendPacket(SystemMessageId.PET_S_CRITICAL_HIT);
}
}
if (getOwner().isInOlympiadMode() && (target instanceof L2PcInstance) && ((L2PcInstance) target).isInOlympiadMode() && (((L2PcInstance) target).getOlympiadGameId() == getOwner().getOlympiadGameId()))
{
OlympiadGameManager.getInstance().notifyCompetitorDamage(getOwner(), damage);
}
final SystemMessage sm;
if (target.isInvul() && !(target instanceof L2NpcInstance))
{
sm = SystemMessage.getSystemMessage(SystemMessageId.THE_ATTACK_HAS_BEEN_BLOCKED);
}
else
{
sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_INFLICTED_S3_DAMAGE_ON_C2_S4);
sm.addNpcName(this);
sm.addCharName(target);
sm.addInt(damage);
sm.addPopup(target.getObjectId(), getObjectId(), (damage * -1));
}
sendPacket(sm);
}
}
@Override
public void reduceCurrentHp(double damage, L2Character attacker, Skill skill)
{
super.reduceCurrentHp(damage, attacker, skill);
if ((getOwner() != null) && (attacker != null))
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_RECEIVED_S3_DAMAGE_FROM_C2);
sm.addNpcName(this);
sm.addCharName(attacker);
sm.addInt((int) damage);
sendPacket(sm);
}
}
@Override
public void doCast(Skill skill)
{
final L2PcInstance actingPlayer = getActingPlayer();
if (!actingPlayer.checkPvpSkill(getTarget(), skill) && !actingPlayer.getAccessLevel().allowPeaceAttack())
{
// Send a System Message to the L2PcInstance
actingPlayer.sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET);
// Send a Server->Client packet ActionFailed to the L2PcInstance
actingPlayer.sendPacket(ActionFailed.STATIC_PACKET);
return;
}
super.doCast(skill);
}
@Override
public boolean isInCombat()
{
return (getOwner() != null) && getOwner().isInCombat();
}
@Override
public L2PcInstance getActingPlayer()
{
return getOwner();
}
@Override
public final void broadcastPacket(L2GameServerPacket mov)
{
if (getOwner() != null)
{
mov.setInvisible(getOwner().isInvisible());
}
super.broadcastPacket(mov);
}
@Override
public final void broadcastPacket(L2GameServerPacket mov, int radiusInKnownlist)
{
if (getOwner() != null)
{
mov.setInvisible(getOwner().isInvisible());
}
super.broadcastPacket(mov, radiusInKnownlist);
}
public void updateAndBroadcastStatus(int val)
{
if (getOwner() == null)
{
return;
}
sendPacket(new PetInfo(this, val));
sendPacket(new PetStatusUpdate(this));
if (isVisible())
{
broadcastNpcInfo(val);
}
L2Party party = getOwner().getParty();
if (party != null)
{
party.broadcastToPartyMembers(getOwner(), new ExPartyPetWindowUpdate(this));
}
updateEffectIcons(true);
}
public void broadcastNpcInfo(int val)
{
for (L2PcInstance player : getKnownList().getKnownPlayers().values())
{
if ((player == null) || (player == getOwner()))
{
continue;
}
if (isPet())
{
player.sendPacket(new ExPetInfo(this, player, val));
}
else
{
player.sendPacket(new SummonInfo(this, player, val));
}
}
}
public boolean isHungry()
{
return false;
}
public int getWeapon()
{
return 0;
}
public int getArmor()
{
return 0;
}
@Override
public void sendInfo(L2PcInstance activeChar)
{
// Check if the L2PcInstance is the owner of the Pet
if (activeChar == getOwner())
{
activeChar.sendPacket(new PetInfo(this, 1));
// The PetInfo packet wipes the PartySpelled (list of active spells' icons). Re-add them
updateEffectIcons(true);
if (isPet())
{
activeChar.sendPacket(new PetItemList(getInventory().getItems()));
}
}
else
{
if (isPet())
{
activeChar.sendPacket(new ExPetInfo(this, activeChar, 0));
}
else
{
activeChar.sendPacket(new SummonInfo(this, activeChar, 0));
}
}
}
@Override
public void onTeleported()
{
super.onTeleported();
sendPacket(new TeleportToLocation(this, getX(), getY(), getZ(), getHeading()));
}
@Override
public String toString()
{
return super.toString() + "(" + getId() + ") Owner: " + getOwner();
}
@Override
public boolean isUndead()
{
return getTemplate().getRace() == Race.UNDEAD;
}
/**
* Change the summon's state.
*/
public void switchMode()
{
// Do nothing.
}
/**
* Cancel the summon's action.
*/
public void cancelAction()
{
if (!isMovementDisabled())
{
getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null);
}
}
/**
* Performs an attack to the owner's target.
*/
public void doAttack()
{
final L2PcInstance owner = getOwner();
final L2Object target = getOwner().getTarget();
if ((owner != null) && (target != null))
{
if (Config.FACTION_SYSTEM_ENABLED && target.isPlayer() && ((owner.isGood() && target.getActingPlayer().isGood()) || (owner.isEvil() && target.getActingPlayer().isEvil())))
{
return;
}
setTarget(target);
getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
}
}
/**
* Verify if the summon can perform an attack.
* @param ctrlPressed {@code true} if Ctrl key is pressed
* @return {@code true} if the summon can attack, {@code false} otherwise
*/
public final boolean canAttack(boolean ctrlPressed)
{
if (getOwner() == null)
{
return false;
}
final L2Object target = getOwner().getTarget();
if ((target == null) || (this == target) || (getOwner() == target))
{
return false;
}
// Sin eater, Big Boom, Wyvern can't attack with attack button.
final int npcId = getId();
if (Util.contains(PASSIVE_SUMMONS, npcId))
{
getOwner().sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
if (isBetrayed())
{
sendPacket(SystemMessageId.YOUR_PET_SERVITOR_IS_UNRESPONSIVE_AND_WILL_NOT_OBEY_ANY_ORDERS);
sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
if (isAttackingDisabled())
{
if (!isAttackingNow())
{
return false;
}
getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
}
if (isPet() && ((getLevel() - getOwner().getLevel()) > 20))
{
sendPacket(SystemMessageId.YOUR_PET_IS_TOO_HIGH_LEVEL_TO_CONTROL);
sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
if (getOwner().isInOlympiadMode() && !getOwner().isOlympiadStart())
{
// If owner is in Olympiad and the match isn't already start, send a Server->Client packet ActionFailed
getOwner().sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
if ((target.getActingPlayer() != null) && (getOwner().getSiegeState() > 0) && getOwner().isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeSide() == getOwner().getSiegeSide()))
{
sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE);
sendPacket(ActionFailed.STATIC_PACKET);
return false;
}
if (!getOwner().getAccessLevel().allowPeaceAttack() && getOwner().isInsidePeaceZone(this, target))
{
sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_THIS_TARGET_IN_A_PEACEFUL_ZONE);
return false;
}
if (isLockedTarget())
{
sendPacket(SystemMessageId.FAILED_TO_CHANGE_ENMITY);
return false;
}
// Summons can attack NPCs even when the owner cannot.
if (!target.isAutoAttackable(getOwner()) && !ctrlPressed && !target.isNpc())
{
setFollowStatus(false);
getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, target);
sendPacket(SystemMessageId.INVALID_TARGET);
return false;
}
// Siege golems AI doesn't support attacking other than doors/walls at the moment.
if (target.isDoor() && (getTemplate().getRace() != Race.SIEGE_WEAPON))
{
return false;
}
return true;
}
@Override
public void sendPacket(L2GameServerPacket mov)
{
if (getOwner() != null)
{
getOwner().sendPacket(mov);
}
}
@Override
public void sendPacket(SystemMessageId id)
{
if (getOwner() != null)
{
getOwner().sendPacket(id);
}
}
@Override
public boolean isSummon()
{
return true;
}
@Override
public boolean isChargedShot(ShotType type)
{
return (_shotsMask & type.getMask()) == type.getMask();
}
@Override
public void setChargedShot(ShotType type, boolean charged)
{
if (charged)
{
_shotsMask |= type.getMask();
}
else
{
_shotsMask &= ~type.getMask();
}
}
@Override
public void rechargeShots(boolean physical, boolean magic)
{
L2ItemInstance item;
IItemHandler handler;
if ((getOwner().getAutoSoulShot() == null) || getOwner().getAutoSoulShot().isEmpty())
{
return;
}
for (int itemId : getOwner().getAutoSoulShot())
{
item = getOwner().getInventory().getItemByItemId(itemId);
if (item != null)
{
if (magic)
{
if (item.getItem().getDefaultAction() == ActionType.SUMMON_SPIRITSHOT)
{
handler = ItemHandler.getInstance().getHandler(item.getEtcItem());
if (handler != null)
{
handler.useItem(getOwner(), item, false);
}
}
}
if (physical)
{
if (item.getItem().getDefaultAction() == ActionType.SUMMON_SOULSHOT)
{
handler = ItemHandler.getInstance().getHandler(item.getEtcItem());
if (handler != null)
{
handler.useItem(getOwner(), item, false);
}
}
}
}
else
{
getOwner().removeAutoSoulShot(itemId);
}
}
}
@Override
public int getClanId()
{
return (getOwner() != null) ? getOwner().getClanId() : 0;
}
@Override
public int getAllyId()
{
return (getOwner() != null) ? getOwner().getAllyId() : 0;
}
public int getFormId()
{
int formId = 0;
final int npcId = getId();
if ((npcId == 16041) || (npcId == 16042))
{
if (getLevel() > 69)
{
formId = 3;
}
else if (getLevel() > 64)
{
formId = 2;
}
else if (getLevel() > 59)
{
formId = 1;
}
}
else if ((npcId == 16025) || (npcId == 16037))
{
if (getLevel() > 69)
{
formId = 3;
}
else if (getLevel() > 64)
{
formId = 2;
}
else if (getLevel() > 59)
{
formId = 1;
}
}
return formId;
}
public void setSummonPoints(int summonPoints)
{
_summonPoints = summonPoints;
}
public int getSummonPoints()
{
return _summonPoints;
}
}