/*
* 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.instance;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import silentium.commons.database.DatabaseFactory;
import silentium.commons.utils.Rnd;
import silentium.gameserver.ThreadPoolManager;
import silentium.gameserver.ai.CtrlIntention;
import silentium.gameserver.configs.MainConfig;
import silentium.gameserver.configs.PlayersConfig;
import silentium.gameserver.geo.GeoData;
import silentium.gameserver.handler.IItemHandler;
import silentium.gameserver.handler.ItemHandler;
import silentium.gameserver.idfactory.IdFactory;
import silentium.gameserver.instancemanager.CursedWeaponsManager;
import silentium.gameserver.instancemanager.ItemsOnGroundManager;
import silentium.gameserver.model.L2ItemInstance;
import silentium.gameserver.model.L2Object;
import silentium.gameserver.model.L2PetData;
import silentium.gameserver.model.L2PetData.L2PetLevelData;
import silentium.gameserver.model.L2Skill;
import silentium.gameserver.model.L2World;
import silentium.gameserver.model.actor.L2Character;
import silentium.gameserver.model.actor.L2Npc;
import silentium.gameserver.model.actor.L2Summon;
import silentium.gameserver.model.actor.stat.PetStat;
import silentium.gameserver.model.itemcontainer.Inventory;
import silentium.gameserver.model.itemcontainer.PetInventory;
import silentium.gameserver.network.SystemMessageId;
import silentium.gameserver.network.serverpackets.ActionFailed;
import silentium.gameserver.network.serverpackets.InventoryUpdate;
import silentium.gameserver.network.serverpackets.MoveToPawn;
import silentium.gameserver.network.serverpackets.MyTargetSelected;
import silentium.gameserver.network.serverpackets.PetInventoryUpdate;
import silentium.gameserver.network.serverpackets.PetItemList;
import silentium.gameserver.network.serverpackets.PetStatusShow;
import silentium.gameserver.network.serverpackets.StatusUpdate;
import silentium.gameserver.network.serverpackets.StopMove;
import silentium.gameserver.network.serverpackets.SystemMessage;
import silentium.gameserver.network.serverpackets.ValidateLocation;
import silentium.gameserver.tables.ItemTable;
import silentium.gameserver.tables.PetDataTable;
import silentium.gameserver.tables.SkillTable;
import silentium.gameserver.taskmanager.DecayTaskManager;
import silentium.gameserver.templates.chars.L2NpcTemplate;
import silentium.gameserver.templates.item.L2ArmorType;
import silentium.gameserver.templates.item.L2EtcItemType;
import silentium.gameserver.templates.item.L2Item;
import silentium.gameserver.templates.item.L2Weapon;
import silentium.gameserver.templates.item.L2WeaponType;
import silentium.gameserver.utils.Util;
public class L2PetInstance extends L2Summon
{
protected static final Logger _logPet = LoggerFactory.getLogger(L2PetInstance.class.getName());
private int _curFed;
private final PetInventory _inventory;
private final int _controlItemId;
private boolean _respawned;
private final boolean _mountable;
private Future<?> _feedTask;
private L2PetData _data;
private L2PetLevelData _leveldata;
/** The Experience before the last Death Penalty */
private long _expBeforeDeath = 0;
private int _curWeightPenalty = 0;
public final L2PetLevelData getPetLevelData()
{
if (_leveldata == null)
_leveldata = PetDataTable.getInstance().getPetLevelData(getTemplate().getNpcId(), getStat().getLevel());
return _leveldata;
}
public final void setPetData(L2PetLevelData value)
{
_leveldata = value;
}
public final L2PetData getPetData()
{
if (_data == null)
_data = PetDataTable.getInstance().getPetData(getTemplate().getNpcId());
return _data;
}
/**
* Manage Feeding Task.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <li>Feed or kill the pet depending on hunger level</li> <li>If pet has food in inventory and feed level drops below 55% then consume food
* from inventory</li> <li>Send a broadcastStatusUpdate packet for this L2PetInstance</li><BR>
* <BR>
*/
class FeedTask implements Runnable
{
@Override
public void run()
{
try
{
if (getOwner() == null || getOwner().getPet() == null || getOwner().getPet().getObjectId() != getObjectId())
{
stopFeed();
return;
}
// eat
else if (getCurrentFed() > getFeedConsume())
setCurrentFed(getCurrentFed() - getFeedConsume());
else
setCurrentFed(0);
broadcastStatusUpdate();
int[] foodIds = getPetData().getFood();
if (foodIds.length == 0)
{
if (getCurrentFed() == 0)
{
getOwner().sendPacket(SystemMessageId.STARVING_GRUMPY_AND_FED_UP_YOUR_PET_HAS_LEFT);
deleteMe(getOwner());
}
else if (isHungry())
getOwner().sendPacket(SystemMessageId.YOUR_PET_IS_VERY_HUNGRY);
return;
}
L2ItemInstance food = null;
for (int id : foodIds)
{
food = getInventory().getItemByItemId(id);
if (food != null)
break;
}
if (isRunning() && isHungry())
setWalking();
else if (!isHungry() && !isRunning())
setRunning();
if (food != null && isHungry())
{
IItemHandler handler = ItemHandler.getInstance().getItemHandler(food.getEtcItem());
if (handler != null)
{
getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.PET_TOOK_S1_BECAUSE_HE_WAS_HUNGRY).addItemName(food));
handler.useItem(L2PetInstance.this, food, false);
}
}
else
{
if (getCurrentFed() == 0)
{
getOwner().sendPacket(SystemMessageId.YOUR_PET_IS_VERY_HUNGRY);
if (Rnd.get(100) < 30)
{
stopFeed();
getOwner().sendPacket(SystemMessageId.STARVING_GRUMPY_AND_FED_UP_YOUR_PET_HAS_LEFT);
deleteMe(getOwner());
}
}
else if (getCurrentFed() < (0.10 * getPetLevelData().getPetMaxFeed()))
{
getOwner().sendPacket(SystemMessageId.YOUR_PET_IS_VERY_HUNGRY_PLEASE_BE_CAREFUL);
if (Rnd.get(100) < 3)
{
stopFeed();
getOwner().sendPacket(SystemMessageId.STARVING_GRUMPY_AND_FED_UP_YOUR_PET_HAS_LEFT);
deleteMe(getOwner());
}
}
}
}
catch (Exception e)
{
_logPet.error("Pet [ObjectId: " + getObjectId() + "] a feed task error has occurred", e);
}
}
/**
* @return
*/
private int getFeedConsume()
{
// if pet is attacking
if (isAttackingNow())
return getPetLevelData().getPetFeedBattle();
return getPetLevelData().getPetFeedNormal();
}
}
public synchronized static L2PetInstance spawnPet(L2NpcTemplate template, L2PcInstance owner, L2ItemInstance control)
{
if (L2World.getInstance().getPet(owner.getObjectId()) != null)
return null; // owner has a pet listed in world
L2PetInstance pet = restore(control, template, owner);
// add the pet instance to world
if (pet != null)
{
pet.setTitle(owner.getName());
L2World.getInstance().addPet(owner.getObjectId(), pet);
}
return pet;
}
public L2PetInstance(int objectId, L2NpcTemplate template, L2PcInstance owner, L2ItemInstance control)
{
super(objectId, template, owner);
_controlItemId = control.getObjectId();
if (template.getNpcId() == 12564)
getStat().setLevel((byte) getOwner().getLevel());
else
getStat().setLevel(template.getLevel());
_inventory = new PetInventory(this);
_inventory.restore();
_mountable = PetDataTable.isMountable(template.getNpcId());
}
@Override
public void initCharStat()
{
setStat(new PetStat(this));
}
@Override
public PetStat getStat()
{
return (PetStat) super.getStat();
}
@Override
public double getLevelMod()
{
return (100.0 - 11 + getLevel()) / 100.0;
}
public boolean isRespawned()
{
return _respawned;
}
@Override
public int getSummonType()
{
return 2;
}
@Override
public void onAction(L2PcInstance player)
{
boolean isOwner = player.getObjectId() == getOwner().getObjectId();
if (isOwner && player != getOwner())
updateRefOwner(player);
if (player.getTarget() != this)
{
player.setTarget(this);
player.sendPacket(new ValidateLocation(this));
player.sendPacket(new MyTargetSelected(getObjectId(), player.getLevel() - getLevel()));
}
else if (isOwner && player.getTarget() == this)
{
// 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
{
player.sendPacket(new MoveToPawn(player, this, L2Npc.INTERACTION_DISTANCE));
player.sendPacket(new PetStatusShow(this));
player.sendPacket(ActionFailed.STATIC_PACKET);
}
}
else
{
if (isAutoAttackable(player))
{
if (MainConfig.GEODATA > 0)
{
if (GeoData.getInstance().canSeeTarget(player, this))
{
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
player.onActionRequest();
}
}
else
{
player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
player.onActionRequest();
}
}
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 (MainConfig.GEODATA > 0)
{
if (GeoData.getInstance().canSeeTarget(player, this))
player.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, this);
}
else
player.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, this);
}
}
}
@Override
public int getControlItemId()
{
return _controlItemId;
}
public L2ItemInstance getControlItem()
{
return getOwner().getInventory().getItemByObjectId(_controlItemId);
}
public int getCurrentFed()
{
return _curFed;
}
public void setCurrentFed(int num)
{
_curFed = num > getMaxFed() ? getMaxFed() : num;
}
/**
* Returns the pet's currently equipped weapon instance (if any).
*/
@Override
public L2ItemInstance getActiveWeaponInstance()
{
for (L2ItemInstance item : getInventory().getItems())
if (item.getLocation() == L2ItemInstance.ItemLocation.PET_EQUIP && item.getItem().getBodyPart() == L2Item.SLOT_R_HAND)
return item;
return null;
}
/**
* Returns the pet's currently equipped weapon (if any).
*/
@Override
public L2Weapon getActiveWeaponItem()
{
L2ItemInstance weapon = getActiveWeaponInstance();
if (weapon == null)
return null;
return (L2Weapon) weapon.getItem();
}
@Override
public L2ItemInstance getSecondaryWeaponInstance()
{
// temporary? unavailable
return null;
}
@Override
public L2Weapon getSecondaryWeaponItem()
{
// temporary? unavailable
return null;
}
@Override
public PetInventory getInventory()
{
return _inventory;
}
/**
* Destroys item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
*
* @param process
* : String Identifier of process triggering this action
* @param objectId
* : int Item Instance identifier of the item to be destroyed
* @param count
* : int Quantity of items to be destroyed
* @param reference
* : L2Object Object referencing current action like NPC selling item or previous item in transformation
* @param sendMessage
* : boolean Specifies whether to send message to Client about this action
* @return boolean informing if the action was successfull
*/
@Override
public boolean destroyItem(String process, int objectId, int count, L2Object reference, boolean sendMessage)
{
L2ItemInstance item = _inventory.destroyItem(process, objectId, count, getOwner(), reference);
if (item == null)
{
if (sendMessage)
getOwner().sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
return false;
}
// Send Pet inventory update packet
PetInventoryUpdate petIU = new PetInventoryUpdate();
petIU.addItem(item);
getOwner().sendPacket(petIU);
if (sendMessage)
{
if (count > 1)
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
sm.addItemName(item.getItemId());
sm.addItemNumber(count);
getOwner().sendPacket(sm);
}
else
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
sm.addItemName(item.getItemId());
getOwner().sendPacket(sm);
}
}
return true;
}
/**
* Destroy item from inventory by using its <B>itemId</B> and send a Server->Client InventoryUpdate packet to the L2PcInstance.
*
* @param process
* : String Identifier of process triggering this action
* @param itemId
* : int Item identifier of the item to be destroyed
* @param count
* : int Quantity of items to be destroyed
* @param reference
* : L2Object Object referencing current action like NPC selling item or previous item in transformation
* @param sendMessage
* : boolean Specifies whether to send message to Client about this action
* @return boolean informing if the action was successfull
*/
@Override
public boolean destroyItemByItemId(String process, int itemId, int count, L2Object reference, boolean sendMessage)
{
L2ItemInstance item = _inventory.destroyItemByItemId(process, itemId, count, getOwner(), reference);
if (item == null)
{
if (sendMessage)
getOwner().sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
return false;
}
// Send Pet inventory update packet
PetInventoryUpdate petIU = new PetInventoryUpdate();
petIU.addItem(item);
getOwner().sendPacket(petIU);
if (sendMessage)
{
if (count > 1)
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
sm.addItemName(itemId);
sm.addItemNumber(count);
getOwner().sendPacket(sm);
}
else
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
sm.addItemName(itemId);
getOwner().sendPacket(sm);
}
}
return true;
}
@Override
protected void doPickupItem(L2Object object)
{
if (isDead())
return;
boolean follow = getFollowStatus();
getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
StopMove sm = new StopMove(getObjectId(), getX(), getY(), getZ(), getHeading());
_logPet.debug("Pet pickup pos: " + object.getX() + " " + object.getY() + " " + object.getZ());
broadcastPacket(sm);
if (!(object instanceof L2ItemInstance))
{
// dont try to pickup anything that is not an item :)
_logPet.warn("trying to pickup wrong target." + object);
getOwner().sendPacket(ActionFailed.STATIC_PACKET);
return;
}
L2ItemInstance target = (L2ItemInstance) object;
// Cursed weapons
if (CursedWeaponsManager.getInstance().isCursed(target.getItemId()))
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1);
smsg.addItemName(target.getItemId());
getOwner().sendPacket(smsg);
return;
}
synchronized (target)
{
if (!target.isVisible())
return;
if (!target.getDropProtection().tryPickUp(this))
{
getOwner().sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1).addItemName(target.getItemId()));
return;
}
if (!_inventory.validateCapacity(target))
{
getOwner().sendPacket(SystemMessageId.YOUR_PET_CANNOT_CARRY_ANY_MORE_ITEMS);
return;
}
if (!_inventory.validateWeight(target, target.getCount()))
{
getOwner().sendPacket(SystemMessageId.UNABLE_TO_PLACE_ITEM_YOUR_PET_IS_TOO_ENCUMBERED);
return;
}
if (target.getOwnerId() != 0 && target.getOwnerId() != getOwner().getObjectId() && !getOwner().isInLooterParty(target.getOwnerId()))
{
if (target.getItemId() == 57)
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1_ADENA);
smsg.addNumber(target.getCount());
getOwner().sendPacket(smsg);
}
else if (target.getCount() > 1)
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S2_S1_S);
smsg.addItemName(target.getItemId());
smsg.addNumber(target.getCount());
getOwner().sendPacket(smsg);
}
else
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1);
smsg.addItemName(target.getItemId());
getOwner().sendPacket(smsg);
}
return;
}
if (target.getItemLootShedule() != null && (target.getOwnerId() == getOwner().getObjectId() || getOwner().isInLooterParty(target.getOwnerId())))
target.resetOwnerTimer();
target.pickupMe(this);
if (MainConfig.SAVE_DROPPED_ITEM) // item must be removed from ItemsOnGroundManager if is active
ItemsOnGroundManager.getInstance().removeObject(target);
}
// Herbs
if (target.getItemType() == L2EtcItemType.HERB)
{
IItemHandler handler = ItemHandler.getInstance().getItemHandler(target.getEtcItem());
if (handler != null)
handler.useItem(this, target, false);
ItemTable.getInstance().destroyItem("Consume", target, getOwner(), null);
broadcastStatusUpdate();
}
else
{
// if item is instance of L2ArmorType or L2WeaponType broadcast an "Attention" system message
if (target.getItemType() instanceof L2ArmorType || target.getItemType() instanceof L2WeaponType)
{
if (target.getEnchantLevel() > 0)
{
SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_S1_PET_PICKED_UP_S2_S3);
msg.addPcName(getOwner());
msg.addNumber(target.getEnchantLevel());
msg.addItemName(target.getItemId());
getOwner().broadcastPacket(msg, 1400);
}
else
{
SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_S1_PET_PICKED_UP_S2);
msg.addPcName(getOwner());
msg.addItemName(target.getItemId());
getOwner().broadcastPacket(msg, 1400);
}
}
if (target.getItemId() == 57)
{
SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S1_ADENA);
sm2.addItemNumber(target.getCount());
getOwner().sendPacket(sm2);
}
else if (target.getEnchantLevel() > 0)
{
SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S1_S2);
sm2.addNumber(target.getEnchantLevel());
sm2.addString(target.getName());
getOwner().sendPacket(sm2);
}
else if (target.getCount() > 1)
{
SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S2_S1_S);
sm2.addItemNumber(target.getCount());
sm2.addString(target.getName());
getOwner().sendPacket(sm2);
}
else
{
SystemMessage sm2 = SystemMessage.getSystemMessage(SystemMessageId.PET_PICKED_S1);
sm2.addString(target.getName());
getOwner().sendPacket(sm2);
}
getInventory().addItem("Pickup", target, getOwner(), this);
getOwner().sendPacket(new PetItemList(this));
}
getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
if (follow)
followOwner();
}
@Override
public void deleteMe(L2PcInstance owner)
{
getInventory().transferItemsToOwner();
super.deleteMe(owner);
destroyControlItem(owner); // this should also delete the pet from the db
}
@Override
public boolean doDie(L2Character killer)
{
if (!super.doDie(killer, true))
return false;
stopFeed();
getOwner().sendPacket(SystemMessageId.MAKE_SURE_YOU_RESSURECT_YOUR_PET_WITHIN_20_MINUTES);
DecayTaskManager.getInstance().addDecayTask(this, 1200000);
// Dont decrease exp if killed in duel or arena
L2PcInstance owner = getOwner();
if (owner != null && !owner.isInDuel() && (!isInsideZone(ZONE_PVP) || isInsideZone(ZONE_SIEGE)))
deathPenalty();
return true;
}
@Override
public void doRevive()
{
getOwner().removeReviving();
super.doRevive();
// stopDecay
DecayTaskManager.getInstance().cancelDecayTask(this);
startFeed();
if (!isHungry())
setRunning();
getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null);
}
@Override
public void doRevive(double revivePower)
{
// Restore the pet's lost experience depending on the % return of the skill used
restoreExp(revivePower);
doRevive();
}
/**
* Transfers item to another inventory
*
* @param process
* : String Identifier of process triggering this action
* @param objectId
* : ObjectId of the item to be transfered
* @param count
* : int Quantity of items to be transfered
* @param target
* : The Inventory to target
* @param actor
* : L2PcInstance Player requesting the item transfer
* @param reference
* : L2Object Object referencing current action like NPC selling item or previous item in transformation
* @return L2ItemInstance corresponding to the new item or the updated item in inventory
*/
public L2ItemInstance transferItem(String process, int objectId, int count, Inventory target, L2PcInstance actor, L2Object reference)
{
L2ItemInstance oldItem = getInventory().getItemByObjectId(objectId);
L2ItemInstance playerOldItem = target.getItemByItemId(oldItem.getItemId());
L2ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, actor, reference);
if (newItem == null)
return null;
// Send inventory update packet
PetInventoryUpdate petIU = new PetInventoryUpdate();
if (oldItem.getCount() > 0 && oldItem != newItem)
petIU.addModifiedItem(oldItem);
else
petIU.addRemovedItem(oldItem);
getOwner().sendPacket(petIU);
// Send target update packet
if (!newItem.isStackable())
{
InventoryUpdate iu = new InventoryUpdate();
iu.addNewItem(newItem);
getOwner().sendPacket(iu);
}
else if (playerOldItem != null && newItem.isStackable())
{
InventoryUpdate iu = new InventoryUpdate();
iu.addModifiedItem(newItem);
getOwner().sendPacket(iu);
}
return newItem;
}
/**
* Remove the Pet from DB and its associated item from the player inventory
*
* @param owner
* The owner from whose invenory we should delete the item
*/
public void destroyControlItem(L2PcInstance owner)
{
// remove the pet instance from world
L2World.getInstance().removePet(owner.getObjectId());
// delete from inventory
try
{
L2ItemInstance removedItem = owner.getInventory().destroyItem("PetDestroy", getControlItemId(), 1, getOwner(), this);
if (removedItem == null)
_log.warn("Couldn't destroy petControlItem for " + owner.getName() + ", pet: " + this);
else
{
InventoryUpdate iu = new InventoryUpdate();
iu.addRemovedItem(removedItem);
owner.sendPacket(iu);
StatusUpdate su = new StatusUpdate(owner);
su.addAttribute(StatusUpdate.CUR_LOAD, owner.getCurrentLoad());
owner.sendPacket(su);
owner.broadcastUserInfo();
L2World.getInstance().removeObject(removedItem);
}
}
catch (Exception e)
{
_logPet.warn("Error while destroying control item: " + e.getMessage(), e);
}
// pet control item no longer exists, delete the pet from the db
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?");
statement.setInt(1, getControlItemId());
statement.execute();
statement.close();
}
catch (Exception e)
{
_logPet.error("Failed to delete Pet [ObjectId: " + getObjectId() + "]", e);
}
}
public void dropAllItems()
{
try
{
for (L2ItemInstance item : getInventory().getItems())
dropItemHere(item);
}
catch (Exception e)
{
_logPet.warn("Pet Drop Error: " + e.getMessage(), e);
}
}
public void dropItemHere(L2ItemInstance dropit)
{
dropItemHere(dropit, false);
}
public void dropItemHere(L2ItemInstance dropit, boolean protect)
{
dropit = getInventory().dropItem("Drop", dropit.getObjectId(), dropit.getCount(), getOwner(), this);
if (dropit != null)
{
if (protect)
dropit.getDropProtection().protect(getOwner());
_logPet.debug("Item id to drop: " + dropit.getItemId() + " amount: " + dropit.getCount());
dropit.dropMe(this, getX(), getY(), getZ() + 100);
}
}
/** @return Returns the mountable. */
@Override
public boolean isMountable()
{
return _mountable;
}
private static L2PetInstance restore(L2ItemInstance control, L2NpcTemplate template, L2PcInstance owner)
{
try (Connection con = DatabaseFactory.getConnection())
{
L2PetInstance pet;
if (template.isType("L2BabyPet"))
pet = new L2BabyPetInstance(IdFactory.getInstance().getNextId(), template, owner, control);
else
pet = new L2PetInstance(IdFactory.getInstance().getNextId(), template, owner, control);
PreparedStatement statement = con.prepareStatement("SELECT item_obj_id, name, level, curHp, curMp, exp, sp, fed FROM pets WHERE item_obj_id=?");
statement.setInt(1, control.getObjectId());
ResultSet rset = statement.executeQuery();
if (!rset.next())
{
rset.close();
statement.close();
return pet;
}
pet._respawned = true;
pet.setName(rset.getString("name"));
pet.getStat().setLevel(rset.getByte("level"));
pet.getStat().setExp(rset.getLong("exp"));
pet.getStat().setSp(rset.getInt("sp"));
pet.getStatus().setCurrentHp(rset.getDouble("curHp"));
pet.getStatus().setCurrentMp(rset.getDouble("curMp"));
pet.getStatus().setCurrentCp(pet.getMaxCp());
if (rset.getDouble("curHp") < 0.5)
{
pet.setIsDead(true);
pet.stopHpMpRegeneration();
}
pet.setCurrentFed(rset.getInt("fed"));
rset.close();
statement.close();
return pet;
}
catch (Exception e)
{
_logPet.warn("Could not restore pet data for owner: " + owner + " - " + e.getMessage(), e);
return null;
}
}
@Override
public void store()
{
if (getControlItemId() == 0)
{
// this is a summon, not a pet, don't store anything
return;
}
String req;
if (!isRespawned())
req = "INSERT INTO pets (name,level,curHp,curMp,exp,sp,fed,item_obj_id) VALUES (?,?,?,?,?,?,?,?)";
else
req = "UPDATE pets SET name=?,level=?,curHp=?,curMp=?,exp=?,sp=?,fed=? WHERE item_obj_id = ?";
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement(req);
statement.setString(1, getName());
statement.setInt(2, getStat().getLevel());
statement.setDouble(3, getStatus().getCurrentHp());
statement.setDouble(4, getStatus().getCurrentMp());
statement.setLong(5, getStat().getExp());
statement.setInt(6, getStat().getSp());
statement.setInt(7, getCurrentFed());
statement.setInt(8, getControlItemId());
statement.executeUpdate();
statement.close();
_respawned = true;
}
catch (Exception e)
{
_logPet.error("Failed to store Pet [ObjectId: " + getObjectId() + "] data", e);
}
L2ItemInstance itemInst = getControlItem();
if (itemInst != null && itemInst.getEnchantLevel() != getStat().getLevel())
{
itemInst.setEnchantLevel(getStat().getLevel());
itemInst.updateDatabase();
}
}
public synchronized void stopFeed()
{
if (_feedTask != null)
{
_feedTask.cancel(false);
_feedTask = null;
_logPet.trace("Pet [#" + getObjectId() + "] feed task stop");
}
}
public synchronized void startFeed()
{
// stop feeding task if its active
stopFeed();
if (!isDead() && getOwner().getPet() == this)
_feedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000, 10000);
}
@Override
public synchronized void unSummon(L2PcInstance owner)
{
// First, stop feed task.
stopFeed();
// Then drop inventory.
if (!isDead())
{
if (getInventory() != null)
getInventory().deleteMe();
}
// Finally drop pet itself.
super.unSummon(owner);
// Drop pet from world's pet list.
if (!isDead())
L2World.getInstance().removePet(owner.getObjectId());
}
/**
* Restore the specified % of experience this L2PetInstance has lost.
*
* @param restorePercent
*/
public void restoreExp(double restorePercent)
{
if (_expBeforeDeath > 0)
{
// Restore the specified % of lost experience.
getStat().addExp(Math.round((_expBeforeDeath - getStat().getExp()) * restorePercent / 100));
_expBeforeDeath = 0;
}
}
private void deathPenalty()
{
int lvl = getStat().getLevel();
double percentLost = -0.07 * lvl + 6.5;
// Calculate the Experience loss
long lostExp = Math.round((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost / 100);
// Get the Experience before applying penalty
_expBeforeDeath = getStat().getExp();
// Set the new Experience value of the L2PetInstance
getStat().addExp(-lostExp);
}
@Override
public void addExpAndSp(long addToExp, int addToSp)
{
getStat().addExpAndSp(Math.round(addToExp * ((getNpcId() == 12564) ? MainConfig.SINEATER_XP_RATE : MainConfig.PET_XP_RATE)), addToSp);
}
@Override
public long getExpForThisLevel()
{
return getStat().getExpForLevel(getLevel());
}
@Override
public long getExpForNextLevel()
{
return getStat().getExpForLevel(getLevel() + 1);
}
@Override
public final int getLevel()
{
return getStat().getLevel();
}
public int getMaxFed()
{
return getStat().getMaxFeed();
}
@Override
public int getAccuracy()
{
return getStat().getAccuracy();
}
@Override
public int getCriticalHit(L2Character target, L2Skill skill)
{
return getStat().getCriticalHit(target, skill);
}
@Override
public int getEvasionRate(L2Character target)
{
return getStat().getEvasionRate(target);
}
@Override
public int getRunSpeed()
{
return getStat().getRunSpeed();
}
@Override
public int getPAtkSpd()
{
return getStat().getPAtkSpd();
}
@Override
public int getMAtkSpd()
{
return getStat().getMAtkSpd();
}
@Override
public int getMAtk(L2Character target, L2Skill skill)
{
return getStat().getMAtk(target, skill);
}
@Override
public int getMDef(L2Character target, L2Skill skill)
{
return getStat().getMDef(target, skill);
}
@Override
public int getPAtk(L2Character target)
{
return getStat().getPAtk(target);
}
@Override
public int getPDef(L2Character target)
{
return getStat().getPDef(target);
}
@Override
public final int getSkillLevel(int skillId)
{
// Unknown skill. Return -1.
if (getKnownSkill(skillId) == null)
return -1;
// Max level for pet is 80, max level for pet skills is 12 => ((80 - 8) / 6) = 12.
int lvl = (getLevel() - 8) / 6;
// Take in consideration pets with lower levels ( <= level 8 would lead to negative values).
if (lvl <= 0)
lvl = 1;
// Avoid to read an non existing level. The maximum possible level is retained.
else
{
int maxLvl = SkillTable.getInstance().getMaxLevel(skillId);
if (lvl > maxLvl)
lvl = maxLvl;
}
return lvl;
}
public void updateRefOwner(L2PcInstance owner)
{
int oldOwnerId = getOwner().getObjectId();
setOwner(owner);
L2World.getInstance().removePet(oldOwnerId);
L2World.getInstance().addPet(oldOwnerId, this);
}
public int getCurrentLoad()
{
return _inventory.getTotalWeight();
}
@Override
public final int getMaxLoad()
{
return getPetData().getLoad();
}
public int getInventoryLimit()
{
return PlayersConfig.INVENTORY_MAXIMUM_PET;
}
public void refreshOverloaded()
{
int maxLoad = getMaxLoad();
if (maxLoad > 0)
{
int weightproc = getCurrentLoad() * 1000 / maxLoad;
int newWeightPenalty;
if (weightproc < 500)
newWeightPenalty = 0;
else if (weightproc < 666)
newWeightPenalty = 1;
else if (weightproc < 800)
newWeightPenalty = 2;
else if (weightproc < 1000)
newWeightPenalty = 3;
else
newWeightPenalty = 4;
if (_curWeightPenalty != newWeightPenalty)
{
_curWeightPenalty = newWeightPenalty;
if (newWeightPenalty > 0)
{
addSkill(SkillTable.getInstance().getInfo(4270, newWeightPenalty));
setIsOverloaded(getCurrentLoad() >= maxLoad);
}
else
{
super.removeSkill(getKnownSkill(4270));
setIsOverloaded(false);
}
}
}
}
@Override
public void updateAndBroadcastStatus(int val)
{
refreshOverloaded();
super.updateAndBroadcastStatus(val);
}
/**
* A simple check, made to see if this current pet is hungry.<br>
* <br>
* If the actual amount of food < 55% of the max, the pet is shown as hungry. Both atkspd and cstspd are divided by 2, and deluxe food can be
* used automatically if worn.
**/
@Override
public final boolean isHungry()
{
return (getCurrentFed() < (getMaxFed() * 0.55));
}
public boolean canEatFoodId(int itemId)
{
return Util.contains(_data.getFood(), itemId);
}
public boolean canWear(L2Item item)
{
if (PetDataTable.isHatchling(getNpcId()) && item.getBodyPart() == L2Item.SLOT_HATCHLING)
return true;
if (PetDataTable.isWolf(getNpcId()) && item.getBodyPart() == L2Item.SLOT_WOLF)
return true;
if (PetDataTable.isStrider(getNpcId()) && item.getBodyPart() == L2Item.SLOT_STRIDER)
return true;
if (PetDataTable.isBaby(getNpcId()) && item.getBodyPart() == L2Item.SLOT_BABYPET)
return true;
return false;
}
@Override
public final int getWeapon()
{
L2ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
if (weapon != null)
return weapon.getItemId();
return 0;
}
@Override
public final int getArmor()
{
L2ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
if (weapon != null)
return weapon.getItemId();
return 0;
}
@Override
public void setName(String name)
{
L2ItemInstance controlItem = getControlItem();
if (controlItem.getCustomType2() == (name == null ? 1 : 0))
{
// Name isn't setted yet.
controlItem.setCustomType2(name != null ? 1 : 0);
controlItem.updateDatabase();
InventoryUpdate iu = new InventoryUpdate();
iu.addModifiedItem(controlItem);
getOwner().sendPacket(iu);
}
super.setName(name);
}
}