/* * 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.actor; import java.util.Collection; import com.l2jserver.gameserver.ai.CtrlIntention; import com.l2jserver.gameserver.ai.L2CharacterAI; import com.l2jserver.gameserver.ai.L2SummonAI; import com.l2jserver.gameserver.datatables.ItemTable; import com.l2jserver.gameserver.instancemanager.TerritoryWarManager; import com.l2jserver.gameserver.model.L2ItemInstance; import com.l2jserver.gameserver.model.L2Object; import com.l2jserver.gameserver.model.L2Party; import com.l2jserver.gameserver.model.L2Skill; import com.l2jserver.gameserver.model.L2WorldRegion; import com.l2jserver.gameserver.model.L2Skill.SkillTargetType; import com.l2jserver.gameserver.model.actor.L2Attackable.AggroInfo; import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance; import com.l2jserver.gameserver.model.actor.instance.L2MerchantSummonInstance; import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance; import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; import com.l2jserver.gameserver.model.actor.instance.L2PetInstance; import com.l2jserver.gameserver.model.actor.instance.L2SummonInstance; 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.base.Experience; import com.l2jserver.gameserver.model.itemcontainer.PetInventory; import com.l2jserver.gameserver.model.olympiad.Olympiad; import com.l2jserver.gameserver.network.SystemMessageId; import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo; 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.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.SystemMessage; import com.l2jserver.gameserver.network.serverpackets.TeleportToLocation; import com.l2jserver.gameserver.taskmanager.DecayTaskManager; import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; import com.l2jserver.gameserver.templates.item.L2EtcItem; import com.l2jserver.gameserver.templates.item.L2Weapon; public abstract class L2Summon extends L2Playable { private L2PcInstance _owner; private int _attackRange = 36; //Melee range private boolean _follow = true; private boolean _previousFollowStatus = true; private int _chargedSoulShot; private int _chargedSpiritShot; public class AIAccessor extends L2Character.AIAccessor { protected AIAccessor() {} public L2Summon getSummon() { return L2Summon.this; } public boolean isAutoFollow() { return L2Summon.this.getFollowStatus(); } public void doPickupItem(L2Object object) { L2Summon.this.doPickupItem(object); } } public L2Summon(int objectId, L2NpcTemplate template, L2PcInstance owner) { super(objectId, template); setInstanceType(InstanceType.L2Summon); setInstanceId(owner.getInstanceId()); // set instance to same as owner _showSummonAnimation = true; _owner = owner; _ai = new L2SummonAI(new L2Summon.AIAccessor()); setXYZInvisible(owner.getX()+20, owner.getY()+20, owner.getZ()+100); } @Override public void onSpawn() { super.onSpawn(); if (!(this instanceof L2MerchantSummonInstance)) { this.setFollowStatus(true); updateAndBroadcastStatus(0); getOwner().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 = this.getOwner().getParty(); if (party != null) { party.broadcastToPartyMembers(this.getOwner(), new ExPartyPetWindowAdd(this)); } } setShowSummonAnimation(false); // addVisibleObject created the info packets with summon animation // if someone comes into range now, the animation shouldnt show any more } @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 public L2CharacterAI getAI() { L2CharacterAI ai = _ai; // copy handle if (ai == null) { synchronized(this) { if (_ai == null) _ai = new L2SummonAI(new L2Summon.AIAccessor()); return _ai; } } return ai; } @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 updateAbnormalEffect() { Collection plrs = getKnownList().getKnownPlayers().values(); //synchronized (getKnownList().getKnownPlayers()) { for (L2PcInstance player : plrs) player.sendPacket(new AbstractNpcInfo.SummonInfo(this, player,1)); } } /** * @return Returns the mountable. */ public boolean isMountable() { return false; } public long getExpForThisLevel() { if(getLevel() >= Experience.LEVEL.length) { return 0; } return Experience.LEVEL[getLevel()]; } public long getExpForNextLevel() { if(getLevel() >= Experience.LEVEL.length - 1) { return 0; } return Experience.LEVEL[getLevel()+1]; } public final int getKarma() { return getOwner()!= null ? getOwner().getKarma() : 0; } public final byte getPvpFlag() { return getOwner()!= null ? getOwner().getPvpFlag() : 0; } public final int getTeam() { return getOwner()!= null ? getOwner().getTeam() : 0; } public final L2PcInstance getOwner() { return _owner; } public final int getNpcId() { return getTemplate().npcId; } public int getMaxLoad() { return 0; } public short getSoulShotsPerHit() { if (getTemplate().getAIDataStatic().getSoulShot() > 0) return (short) getTemplate().getAIDataStatic().getSoulShot(); else return 1; } public short getSpiritShotsPerHit() { if (getTemplate().getAIDataStatic().getSpiritShot() > 0) return (short) getTemplate().getAIDataStatic().getSpiritShot(); else return 1; } public void setChargedSoulShot(int shotType) { _chargedSoulShot = shotType; } public void setChargedSpiritShot(int shotType) { _chargedSpiritShot = shotType; } public void followOwner() { setFollowStatus(true); } @Override public boolean doDie(L2Character killer) { if (!super.doDie(killer)) return false; if (this instanceof L2MerchantSummonInstance) return true; L2PcInstance owner = getOwner(); if (owner != null) { Collection KnownTarget = this.getKnownList().getKnownCharacters(); for (L2Character TgMob : KnownTarget) { // get the mobs which have aggro on the this instance if (TgMob instanceof L2Attackable) { if (((L2Attackable) TgMob).isDead()) continue; AggroInfo info = ((L2Attackable) TgMob).getAggroList().get(this); if (info != null) ((L2Attackable) TgMob).addDamageHate(owner, info.getDamage(), info.getHate()); } } } if (isPhoenixBlessed() && (getOwner() != null)) getOwner().reviveRequest(getOwner(), null, true); DecayTaskManager.getInstance().addDecayTask(this); return true; } public boolean doDie(L2Character killer, boolean decayed) { if (!super.doDie(killer)) return false; if (!decayed) { DecayTaskManager.getInstance().addDecayTask(this); } return true; } public void stopDecay() { DecayTaskManager.getInstance().cancelDecayTask(this); } @Override public void onDecay() { deleteMe(_owner); } @Override public void broadcastStatusUpdate() { super.broadcastStatusUpdate(); updateAndBroadcastStatus(1); } public void deleteMe(L2PcInstance owner) { owner.sendPacket(new PetDelete(getSummonType(), getObjectId())); //pet will be deleted along with all his items if (getInventory() != null) { getInventory().destroyAllItems("pet deleted", getOwner(), this); } decayMe(); getKnownList().removeAllKnownObjects(); owner.setPet(null); super.deleteMe(); } public void unSummon(L2PcInstance owner) { if (isVisible() && !isDead()) { getAI().stopFollow(); owner.sendPacket(new PetDelete(getSummonType(),getObjectId())); L2Party party; if ((party = owner.getParty()) != null) { party.broadcastToPartyMembers(owner, new ExPartyPetWindowDelete(this)); } store(); giveAllToOwner(); owner.setPet(null); stopAllEffects(); L2WorldRegion oldRegion = getWorldRegion(); decayMe(); if (oldRegion != null) oldRegion.removeFromZones(this); getKnownList().removeAllKnownObjects(); setTarget(null); for (int itemId : owner.getAutoSoulShot()) { String handler = ((L2EtcItem)ItemTable.getInstance().getTemplate(itemId)).getHandlerName(); if (handler.contains("Beast")) owner.disableAutoShot(itemId); } } } public int getAttackRange() { return _attackRange; } public void setAttackRange(int range) { if (range < 36) range = 36; _attackRange = 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.isAutoAttackable(attacker); } public int getChargedSoulShot() { return _chargedSoulShot; } public int getChargedSpiritShot() { return _chargedSpiritShot; } public int getControlObjectId() { return 0; } public L2Weapon getActiveWeapon() { return null; } @Override public PetInventory getInventory() { return null; } protected void doPickupItem(L2Object object) { } public void giveAllToOwner() { } public void store() { } @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 _isInvul || _isTeleporting || getOwner().isSpawnProtected(); } /** * Return the L2Party object of its L2PcInstance owner or null.

*/ @Override public L2Party getParty() { if (_owner == null) return null; else return _owner.getParty(); } /** * Return True if the L2Character has a Party in progress.

*/ @Override public boolean isInParty() { if (_owner == null) return false; else return _owner.getParty() != null; } /** * Check if the active L2Skill can be casted.

* * Actions :

*
  • Check if the target is correct
  • *
  • Check if the target is in the skill cast range
  • *
  • Check if the summon owns enough HP and MP to cast the skill
  • *
  • Check if all skills are enabled and this skill is enabled


  • *
  • Check if the skill is active


  • *
  • Notify the AI with AI_INTENTION_CAST and target


  • * * @param skill The L2Skill to use * @param forceUse used to force ATTACK on players * @param dontMove used to prevent movement, if not in range * */ public void useMagic(L2Skill skill, boolean forceUse, boolean dontMove) { if (skill == null || isDead()) return; // Check if the skill is active if (skill.isPassive()) { // just ignore the passive skill request. why does the client send it anyway ?? return; } //************************************* Check Casting in Progress ******************************************* // If a skill is currently being used if (isCastingNow()) { return; } // Set current pet skill getOwner().setCurrentPetSkill(skill, forceUse, dontMove); //************************************* Check Target ******************************************* // 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 TARGET_OWNER_PET: target = getOwner(); break; // PARTY, AURA, SELF should be cast even if no target has been found case TARGET_PARTY: case TARGET_AURA: case TARGET_FRONT_AURA: case TARGET_BEHIND_AURA: case TARGET_SELF: 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) { if (getOwner() != null) getOwner().sendPacket(new SystemMessage(SystemMessageId.TARGET_CANT_FOUND)); return; } //************************************* Check skill availability ******************************************* // Check if this skill is enabled (e.g. reuse time) if (isSkillDisabled(skill)) { if (getOwner() != null) getOwner().sendPacket(new SystemMessage(SystemMessageId.PET_SKILL_CANNOT_BE_USED_RECHARCHING)); return; } //************************************* Check Consumables ******************************************* // Check if the summon has enough MP if (getCurrentMp() < getStat().getMpConsume(skill) + getStat().getMpInitialConsume(skill)) { // Send a System Message to the caster if (getOwner() != null) getOwner().sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_MP)); return; } // Check if the summon has enough HP if (getCurrentHp() <= skill.getHpConsume()) { // Send a System Message to the caster if (getOwner() != null) getOwner().sendPacket(new SystemMessage(SystemMessageId.NOT_ENOUGH_HP)); return; } //************************************* Check Summon State ******************************************* // Check if this is offensive magic skill if (skill.isOffensive()) { if (isInsidePeaceZone(this, target) && getOwner() != null && (!getOwner().getAccessLevel().allowPeaceAttack())) { // If summon or target is in a peace zone, send a system message TARGET_IN_PEACEZONE sendPacket(new SystemMessage(SystemMessageId.TARGET_IN_PEACEZONE)); return; } if (getOwner() != null && getOwner().isInOlympiadMode() && !getOwner().isOlympiadStart()){ // if L2PcInstance is in Olympia and the match isn't already start, send a Server->Client packet ActionFailed sendPacket(ActionFailed.STATIC_PACKET); return; } if (target.getActingPlayer() != null && this.getOwner().getSiegeState() > 0 && this.getOwner().isInsideZone(L2Character.ZONE_SIEGE) && target.getActingPlayer().getSiegeState() == this.getOwner().getSiegeState() && target.getActingPlayer() != this.getOwner() && target.getActingPlayer().getSiegeSide() == this.getOwner().getSiegeSide()) { // if (TerritoryWarManager.getInstance().isTWInProgress()) sendPacket(new SystemMessage(SystemMessageId.YOU_CANNOT_ATTACK_A_MEMBER_OF_THE_SAME_TERRITORY)); else sendPacket(new SystemMessage(SystemMessageId.FORCED_ATTACK_IS_IMPOSSIBLE_AGAINST_SIEGE_SIDE_TEMPORARY_ALLIED_MEMBERS)); sendPacket(ActionFailed.STATIC_PACKET); return; } // Check if the target is attackable if (target instanceof L2DoorInstance) { if(!((L2DoorInstance)target).isAttackable(getOwner())) return; } else { if (!target.isAttackable() && getOwner() != null && (!getOwner().getAccessLevel().allowPeaceAttack())) { return; } // Check if a Forced ATTACK is in progress on non-attackable target if (!target.isAutoAttackable(this) && !forceUse && skill.getTargetType() != SkillTargetType.TARGET_AURA && skill.getTargetType() != SkillTargetType.TARGET_FRONT_AURA && skill.getTargetType() != SkillTargetType.TARGET_BEHIND_AURA && skill.getTargetType() != SkillTargetType.TARGET_CLAN && skill.getTargetType() != SkillTargetType.TARGET_ALLY && skill.getTargetType() != SkillTargetType.TARGET_PARTY && skill.getTargetType() != SkillTargetType.TARGET_SELF) { return; } } } // Notify the AI with AI_INTENTION_CAST and target getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target); } @Override public void setIsImmobilized(boolean value) { super.setIsImmobilized(value); if (value) { _previousFollowStatus = getFollowStatus(); // if immobilized temporarly 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 (this instanceof L2SummonInstance) getOwner().sendPacket(new SystemMessage(SystemMessageId.CRITICAL_HIT_BY_SUMMONED_MOB)); else getOwner().sendPacket(new SystemMessage(SystemMessageId.CRITICAL_HIT_BY_PET)); if (getOwner().isInOlympiadMode() && target instanceof L2PcInstance && ((L2PcInstance)target).isInOlympiadMode() && ((L2PcInstance)target).getOlympiadGameId() == getOwner().getOlympiadGameId()) { Olympiad.getInstance().notifyCompetitorDamage(getOwner(), damage, getOwner().getOlympiadGameId()); } final SystemMessage sm; if (target.isInvul() && !(target instanceof L2NpcInstance)) sm = new SystemMessage(SystemMessageId.ATTACK_WAS_BLOCKED); else { sm = new SystemMessage(SystemMessageId.C1_GAVE_C2_DAMAGE_OF_S3); sm.addNpcName(this); sm.addCharName(target); sm.addNumber(damage); } getOwner().sendPacket(sm); } } @Override public void reduceCurrentHp(double damage, L2Character attacker, L2Skill skill) { super.reduceCurrentHp(damage, attacker, skill); if (getOwner() != null && attacker != null) { SystemMessage sm = new SystemMessage(SystemMessageId.C1_RECEIVED_DAMAGE_OF_S3_FROM_C2); sm.addNpcName(this); sm.addCharName(attacker); sm.addNumber((int)damage); getOwner().sendPacket(sm); } } @Override public void doCast(L2Skill skill) { final L2PcInstance actingPlayer = getActingPlayer(); if (!actingPlayer.checkPvpSkill(getTarget(), skill, true) && !actingPlayer.getAccessLevel().allowPeaceAttack()) { // Send a System Message to the L2PcInstance actingPlayer.sendPacket(new SystemMessage(SystemMessageId.TARGET_IS_INCORRECT)); // Send a Server->Client packet ActionFailed to the L2PcInstance actingPlayer.sendPacket(ActionFailed.STATIC_PACKET); return; } super.doCast(skill); } @Override public boolean isInCombat() { return getOwner().isInCombat(); } @Override public L2PcInstance getActingPlayer() { return getOwner(); } @Override public final void broadcastPacket(L2GameServerPacket mov) { mov.setInvisible(getOwner().getAppearance().getInvisible()); super.broadcastPacket(mov); } @Override public final void broadcastPacket(L2GameServerPacket mov, int radiusInKnownlist) { mov.setInvisible(getOwner().getAppearance().getInvisible()); super.broadcastPacket(mov, radiusInKnownlist); } public void updateAndBroadcastStatus(int val) { if (getOwner() == null) return; getOwner().sendPacket(new PetInfo(this,val)); getOwner().sendPacket(new PetStatusUpdate(this)); if (isVisible()) { broadcastNpcInfo(val); } L2Party party = this.getOwner().getParty(); if (party != null) { party.broadcastToPartyMembers(this.getOwner(), new ExPartyPetWindowUpdate(this)); } updateEffectIcons(true); } public void broadcastNpcInfo(int val) { Collection plrs = getKnownList().getKnownPlayers().values(); for (L2PcInstance player : plrs) { if (player == null || (player == getOwner() && !(this instanceof L2MerchantSummonInstance))) continue; player.sendPacket(new AbstractNpcInfo.SummonInfo(this,player, val)); } } public boolean isHungry() { return false; } @Override public final boolean isAttackingNow() { return isInCombat(); } public int getWeapon() { return 0; } public int getArmor() { return 0; } public int getPetSpeed() { return getTemplate().baseRunSpd; } @Override public void sendInfo(L2PcInstance activeChar) { // Check if the L2PcInstance is the owner of the Pet if (activeChar.equals(getOwner()) && !(this instanceof L2MerchantSummonInstance)) { activeChar.sendPacket(new PetInfo(this, 0)); // The PetInfo packet wipes the PartySpelled (list of active spells' icons). Re-add them updateEffectIcons(true); if (this instanceof L2PetInstance) { activeChar.sendPacket(new PetItemList((L2PetInstance) this)); } } else activeChar.sendPacket(new AbstractNpcInfo.SummonInfo(this, activeChar, 0)); } /* (non-Javadoc) * @see com.l2jserver.gameserver.model.actor.L2Character#onTeleported() */ @Override public void onTeleported() { super.onTeleported(); getOwner().sendPacket(new TeleportToLocation(this, getPosition().getX(), getPosition().getY(), getPosition().getZ(), getPosition().getHeading())); } @Override public String toString() { return super.toString()+" Owner: "+getOwner(); } }