/*
* This file is part of aion-unique <aion-unique.com>.
*
* aion-unique 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.
*
* aion-unique 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 aion-unique. If not, see <http://www.gnu.org/licenses/>.
*/
package com.aionemu.gameserver.services;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.log4j.Logger;
import com.aionemu.commons.database.dao.DAOManager;
import com.aionemu.gameserver.dao.ItemStoneListDAO;
import com.aionemu.gameserver.dataholders.DataManager;
import com.aionemu.gameserver.model.gameobjects.Item;
import com.aionemu.gameserver.model.gameobjects.PersistentState;
import com.aionemu.gameserver.model.gameobjects.player.Player;
import com.aionemu.gameserver.model.gameobjects.player.Storage;
import com.aionemu.gameserver.model.gameobjects.player.StorageType;
import com.aionemu.gameserver.model.items.GodStone;
import com.aionemu.gameserver.model.items.ItemId;
import com.aionemu.gameserver.model.items.ManaStone;
import com.aionemu.gameserver.model.templates.item.GodstoneInfo;
import com.aionemu.gameserver.model.templates.item.ItemTemplate;
import com.aionemu.gameserver.model.templates.quest.QuestItems;
import com.aionemu.gameserver.network.aion.serverpackets.SM_DELETE_ITEM;
import com.aionemu.gameserver.network.aion.serverpackets.SM_DELETE_WAREHOUSE_ITEM;
import com.aionemu.gameserver.network.aion.serverpackets.SM_INVENTORY_UPDATE;
import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE;
import com.aionemu.gameserver.network.aion.serverpackets.SM_UPDATE_ITEM;
import com.aionemu.gameserver.network.aion.serverpackets.SM_UPDATE_WAREHOUSE_ITEM;
import com.aionemu.gameserver.network.aion.serverpackets.SM_WAREHOUSE_UPDATE;
import com.aionemu.gameserver.utils.PacketSendUtility;
import com.aionemu.gameserver.utils.idfactory.IDFactory;
import com.aionemu.gameserver.utils.idfactory.IDFactoryAionObject;
import com.google.inject.Inject;
/**
* @author ATracer
* This class is used for Item manipulations (creation, disposing, modification)
* Can be used as a factory for Item objects
*/
public class ItemService
{
private static Logger log = Logger.getLogger(ItemService.class);
@Inject
@IDFactoryAionObject
private IDFactory aionObjectsIDFactory;
/**
* @param itemId
* @param count
* @return
*
* Creates new Item instance.
* If count is greater than template maxStackCount, count value will be cut to maximum allowed
* This method will return null if ItemTemplate for itemId was not found.
*/
public Item newItem(int itemId, int count)
{
ItemTemplate itemTemplate = DataManager.ITEM_DATA.getItemTemplate(itemId);
if(itemTemplate == null)
{
log.error("Item was not populated correctly. Item template is missing for item id: " + itemId);
return null;
}
int maxStackCount = itemTemplate.getMaxStackCount();
if(count > maxStackCount && maxStackCount != 0)
{
count = maxStackCount;
}
//TODO if Item object will contain ownerId - item can be saved to DB before return
return new Item(aionObjectsIDFactory.nextId(), itemTemplate, count, false, 0);
}
/**
* Loads item stones from DB for each item in a list if item is ARMOR or WEAPON
* Godstones will be laoded for WEAPON items
*
* @param itemList
*/
public void loadItemStones(List<Item> itemList)
{
if(itemList == null)
return;
for(Item item : itemList)
{
if(item.getItemTemplate().isArmor() || item.getItemTemplate().isWeapon())
{
DAOManager.getDAO(ItemStoneListDAO.class).load(item);
}
if(item.getItemTemplate().isWeapon())
{
GodStone gs = DAOManager.getDAO(ItemStoneListDAO.class).loadGodstone(item.getObjectId());
if(gs != null)
{
item.setGoodStone(gs);
}
}
}
}
/**
* Used to split item into 2 items
*
* @param player
* @param itemObjId
* @param splitAmount
* @param slotNum
* @param sourceStorageType
* @param desetinationStorageType
*/
public void splitItem (Player player, int itemObjId, int splitAmount, int slotNum, int sourceStorageType, int destinationStorageType)
{
Storage sourceStorage = player.getStorage(sourceStorageType);
Storage destinationStorage = player.getStorage(destinationStorageType);
Item itemToSplit = sourceStorage.getItemByObjId(itemObjId);
if(itemToSplit == null)
{
itemToSplit = sourceStorage.getKinahItem();
if(itemToSplit.getObjectId() != itemObjId || itemToSplit == null)
{
log.warn(String.format("CHECKPOINT: attempt to split null item %d %d %d", itemObjId, splitAmount, slotNum));
return;
}
}
// To move kinah from inventory to warehouse and vise versa client using split item packet
if(itemToSplit.getItemTemplate().isKinah())
{
moveKinah(player, sourceStorage, splitAmount);
return;
}
int oldItemCount = itemToSplit.getItemCount() - splitAmount;
if(itemToSplit.getItemCount()<splitAmount || oldItemCount == 0)
return;
Item newItem = newItem(itemToSplit.getItemTemplate().getTemplateId(), splitAmount);
newItem.setEquipmentSlot(slotNum);
if(destinationStorage.putToBag(newItem) != null)
{
sourceStorage.decreaseItemCount(itemToSplit, splitAmount);
List<Item> itemsToUpdate = new ArrayList<Item>();
itemsToUpdate.add(newItem);
sendStorageUpdatePacket(player, destinationStorageType, itemsToUpdate.get(0));
sendUpdateItemPacket(player, sourceStorageType, itemToSplit);
}
else
{
releaseItemId(newItem);
}
}
public void moveKinah(Player player, Storage source, int splitAmount)
{
if(source.getKinahItem().getItemCount() < splitAmount)
return;
switch(source.getStorageType())
{
case 0:
{
Storage destination = player.getStorage(StorageType.ACCOUNT_WAREHOUSE.getId());
int chksum = (source.getKinahItem().getItemCount() - splitAmount) + (destination.getKinahItem().getItemCount() + splitAmount);
if(chksum != source.getKinahItem().getItemCount() + destination.getKinahItem().getItemCount())
return;
source.decreaseKinah(splitAmount);
destination.increaseKinah(splitAmount);
break;
}
case 2:
{
Storage destination = player.getStorage(StorageType.CUBE.getId());
int chksum = (source.getKinahItem().getItemCount() - splitAmount) + (destination.getKinahItem().getItemCount() + splitAmount);
if(chksum != source.getKinahItem().getItemCount() + destination.getKinahItem().getItemCount())
return;
source.decreaseKinah(splitAmount);
destination.increaseKinah(splitAmount);
break;
}
default:
break;
}
}
/**
* Used to merge 2 items in inventory
*
* @param player
* @param sourceItemObjId
* @param itemAmount
* @param destinationObjId
*/
public void mergeItems (Player player, int sourceItemObjId, int itemAmount, int destinationObjId, int sourceStorageType, int destinationStorageType)
{
if(itemAmount == 0)
return;
Storage sourceStorage = player.getStorage(sourceStorageType);
Storage destinationStorage = player.getStorage(destinationStorageType);
Item sourceItem = sourceStorage.getItemByObjId(sourceItemObjId);
Item destinationItem = destinationStorage.getItemByObjId(destinationObjId);
if(sourceItem == null || destinationItem == null)
return; //Invalid object id provided
if(sourceItem.getItemTemplate().getTemplateId() != destinationItem.getItemTemplate().getTemplateId())
return; //Invalid item type
if(sourceItem.getItemCount() < itemAmount)
return; //Invalid item amount
if(sourceItem.getItemCount() == itemAmount)
{
destinationStorage.increaseItemCount(destinationItem, itemAmount);
sourceStorage.removeFromBag(sourceItem, true);
sendDeleteItemPacket(player, sourceStorageType, sourceItem.getObjectId());
sendUpdateItemPacket(player, destinationStorageType, destinationItem);
}
else if(sourceItem.getItemCount() > itemAmount)
{
sourceStorage.decreaseItemCount(sourceItem, itemAmount);
destinationStorage.increaseItemCount(destinationItem, itemAmount);
sendUpdateItemPacket(player, sourceStorageType, sourceItem);
sendUpdateItemPacket(player, destinationStorageType, destinationItem);
}
else return; // cant happen in theory, but...
}
public void switchStoragesItems(Player player, int sourceStorageType, int sourceItemObjId, int replaceStorageType, int replaceItemObjId)
{
Storage sourceStorage = player.getStorage(sourceStorageType);
Storage replaceStorage = player.getStorage(replaceStorageType);
Item sourceItem = sourceStorage.getItemByObjId(sourceItemObjId);
if(sourceItem == null)
return;
Item replaceItem = replaceStorage.getItemByObjId(replaceItemObjId);
if(replaceItem == null)
return;
int sourceSlot = sourceItem.getEquipmentSlot();
int replaceSlot = replaceItem.getEquipmentSlot();
sourceItem.setEquipmentSlot(replaceSlot);
replaceItem.setEquipmentSlot(sourceSlot);
sourceStorage.removeFromBag(sourceItem, false);
replaceStorage.removeFromBag(replaceItem, false);
Item newSourceItem = sourceStorage.putToBag(replaceItem);
Item newReplaceItem = replaceStorage.putToBag(sourceItem);
sendDeleteItemPacket(player, sourceStorageType, sourceItemObjId);
sendStorageUpdatePacket(player, sourceStorageType, newSourceItem);
sendDeleteItemPacket(player, replaceStorageType, replaceItemObjId);
sendStorageUpdatePacket(player, replaceStorageType, newReplaceItem);
}
/**
* Adds item count to player inventory
* I moved this method to service cause right implementation of it is critical to server
* operation and could cause starvation of object ids.
*
* This packet will send necessary packets to client (initialize used only from quest engine
*
* @param player
* @param itemId
* @param count - amount of item that were not added to player's inventory
*/
public int addItem(Player player, int itemId, int count)
{
log.info(String.format("[ITEM] ID/Count - %d/%d to player %s.", itemId, count, player.getName()));
return this.addItemWithStones(player, itemId, count, null, null);
}
/**
* @param buyer
* @param itemId
* @param count
* @param isQuestItem
* @param manastones
* @param godStone
*/
public int addItemWithStones(Player player, int itemId, int count, Set<ManaStone> manastones, GodStone godStone)
{
Storage inventory = player.getInventory();
ItemTemplate itemTemplate = DataManager.ITEM_DATA.getItemTemplate(itemId);
if(itemTemplate == null)
return count;
int maxStackCount = itemTemplate.getMaxStackCount();
if (itemId == ItemId.KINAH.value())
{
inventory.increaseKinah(count);
return 0;
}
else
{
/**
* Increase count of existing items
*/
List<Item> existingItems = inventory.getAllItemsByItemId(itemId); // look for existing in equipment. need for power shards.
for(Item existingItem : existingItems)
{
if(count == 0)
break;
int freeCount = maxStackCount - existingItem.getItemCount();
if(count <= freeCount)
{
inventory.increaseItemCount(existingItem, count);
count = 0;
}
else
{
inventory.increaseItemCount(existingItem, freeCount);
count -= freeCount;
}
updateItem(player, existingItem, false);
}
/**
* Create new stacks
*/
while(!inventory.isFull() && count > 0)
{
// item count still more than maxStack value
if(count > maxStackCount)
{
Item item = newItem(itemId, maxStackCount);
count -= maxStackCount;
inventory.putToBag(item);
updateItem(player, item, true);
}
else
{
Item item = newItem(itemId, count);
//add item stones if available
//1. manastones
if(manastones != null)
{
for(ManaStone manaStone : manastones)
{
addManaStone(item, manaStone.getItemId());
}
}
//2. godstone
if(godStone != null)
{
item.addGodStone(godStone.getItemId());
}
inventory.putToBag(item);
updateItem(player, item, true);
count = 0;
}
}
return count;
}
}
/**
*
* @param player
* @param itemObjId
* @param sourceStorageType
* @param destinationStorageType
* @param slot
*/
public void moveItem(Player player, int itemObjId, int sourceStorageType, int destinationStorageType, int slot)
{
Storage sourceStorage = player.getStorage(sourceStorageType);
Item item = player.getStorage(sourceStorageType).getItemByObjId(itemObjId);
if(item == null)
return;
item.setEquipmentSlot(slot);
if (sourceStorageType == destinationStorageType)
{
sourceStorage.setPersistentState(PersistentState.UPDATE_REQUIRED);
return;
}
Storage destinationStorage = player.getStorage(destinationStorageType);
List<Item> existingItems = destinationStorage.getItemsByItemId(item.getItemTemplate().getTemplateId());
int count = item.getItemCount();
int maxStackCount = item.getItemTemplate().getMaxStackCount();
for(Item existingItem : existingItems)
{
if(count == 0)
break;
int freeCount = maxStackCount - existingItem.getItemCount();
if(count <= freeCount)
{
destinationStorage.increaseItemCount(existingItem, count);
count = 0;
sendDeleteItemPacket(player, sourceStorageType, item.getObjectId());
sourceStorage.removeFromBag(item, true);
}
else
{
destinationStorage.increaseItemCount(existingItem, freeCount);
count -= freeCount;
}
sendStorageUpdatePacket(player, destinationStorageType, existingItem);
}
while(!destinationStorage.isFull() && count > 0)
{
// item count still more than maxStack value
if(count > maxStackCount)
{
count -= maxStackCount;
Item newitem = newItem(item.getItemTemplate().getTemplateId(), maxStackCount);
newitem = destinationStorage.putToBag(newitem);
sendStorageUpdatePacket(player, destinationStorageType, newitem);
}
else
{
item.setItemCount(count);
sourceStorage.removeFromBag(item, false);
sendDeleteItemPacket(player, sourceStorageType, item.getObjectId());
Item newitem = destinationStorage.putToBag(item);
sendStorageUpdatePacket(player, destinationStorageType, newitem);
count = 0;
}
}
if(count > 0) // if storage is full and some items left
{
item.setItemCount(count);
sendUpdateItemPacket(player, sourceStorageType, item);
}
}
public void updateItem(Player player, Item item, boolean isNew)
{
if(isNew)
PacketSendUtility.sendPacket(player, new SM_INVENTORY_UPDATE(Collections.singletonList(item)));
else
PacketSendUtility.sendPacket(player, new SM_UPDATE_ITEM(item));
}
public void sendDeleteItemPacket(Player player, int storageType, int itemObjId)
{
if(storageType == StorageType.CUBE.getId())
PacketSendUtility.sendPacket(player, new SM_DELETE_ITEM(itemObjId));
else
PacketSendUtility.sendPacket(player, new SM_DELETE_WAREHOUSE_ITEM(storageType, itemObjId));
}
public void sendStorageUpdatePacket(Player player, int storageType, Item item)
{
if(storageType == StorageType.CUBE.getId())
PacketSendUtility.sendPacket(player, new SM_INVENTORY_UPDATE(Collections.singletonList(item)));
else
PacketSendUtility.sendPacket(player, new SM_WAREHOUSE_UPDATE(item, storageType));
}
public void sendUpdateItemPacket(Player player, int storageType, Item item)
{
if(storageType == StorageType.CUBE.getId())
PacketSendUtility.sendPacket(player, new SM_UPDATE_ITEM(item));
else
PacketSendUtility.sendPacket(player, new SM_UPDATE_WAREHOUSE_ITEM(item, storageType));
}
/**
* Releases item id if item was not used by caller
*
* @param item
*/
public void releaseItemId(Item item)
{
aionObjectsIDFactory.releaseId(item.getObjectId());
}
/**
*
* @param itemId
*/
public ManaStone addManaStone(Item item, int itemId)
{
if(item == null)
return null;
Set<ManaStone> manaStones = item.getItemStones();
//temp fix for manastone spam till templates are updated
if(manaStones.size() > 6)
return null;
int nextSlot = 0;
boolean slotFound = false;
Iterator<ManaStone> iterator = manaStones.iterator();
while(iterator.hasNext())
{
ManaStone manaStone = iterator.next();
if(nextSlot != manaStone.getSlot())
{
slotFound = true;
break;
}
nextSlot++;
}
if(!slotFound)
nextSlot = manaStones.size();
ManaStone stone = new ManaStone(item.getObjectId(), itemId,
nextSlot, PersistentState.NEW);
manaStones.add(stone);
return stone;
}
/**
* @param player
* @param itemObjId
* @param slotNum
*/
public void removeManastone(Player player, int itemObjId, int slotNum)
{
Storage inventory = player.getInventory();
Item item = inventory.getItemByObjId(itemObjId);
if(item == null)
{
log.warn("Item not found during manastone remove");
return;
}
if(!item.hasManaStones())
{
log.warn("Item stone list is empty");
return;
}
Set<ManaStone> itemStones = item.getItemStones();
if(itemStones.size() <= slotNum)
return;
int counter = 0;
Iterator<ManaStone> iterator = itemStones.iterator();
while(iterator.hasNext())
{
ManaStone manaStone = iterator.next();
if(counter == slotNum)
{
manaStone.setPersistentState(PersistentState.DELETED);
iterator.remove();
DAOManager.getDAO(ItemStoneListDAO.class).store(Collections.singleton(manaStone));
break;
}
counter++;
}
PacketSendUtility.sendPacket(player, new SM_UPDATE_ITEM(item));
}
/**
* @param player
* @param weaponId
* @param stoneId
*/
public void socketGodstone(Player player, int weaponId, int stoneId)
{
Item kinahItem = player.getInventory().getKinahItem();
if(kinahItem.getItemCount() < 100000)
return;
Item weaponItem = player.getInventory().getItemByObjId(weaponId);
Item godstone = player.getInventory().getItemByObjId(stoneId);
int godStoneItemId = godstone.getItemTemplate().getTemplateId();
ItemTemplate itemTemplate = DataManager.ITEM_DATA.getItemTemplate(godStoneItemId);
GodstoneInfo godstoneInfo = itemTemplate.getGodstoneInfo();
if(godstoneInfo == null)
{
PacketSendUtility.sendMessage(player, "Cannot socket this godstone");
log.warn("Godstone info missing for itemid " + godStoneItemId);
return;
}
weaponItem.addGodStone(godStoneItemId);
player.getInventory().removeFromBagByObjectId(stoneId, 1);
player.getInventory().decreaseKinah(100000);
PacketSendUtility.sendPacket(player, new SM_UPDATE_ITEM(weaponItem));
PacketSendUtility.sendPacket(player, new SM_UPDATE_ITEM(kinahItem));
}
public boolean addItems(Player player, List<QuestItems> questItems)
{
int needSlot = 0;
for (QuestItems qi : questItems)
{
if (qi.getItemId() != ItemId.KINAH.value() && qi.getCount()!= 0)
{
int stackCount = DataManager.ITEM_DATA.getItemTemplate(qi.getItemId()).getMaxStackCount();
int count = qi.getCount()/stackCount;
if (qi.getCount() % stackCount != 0)
count++;
needSlot += count;
}
}
if (needSlot > player.getInventory().getNumberOfFreeSlots())
{
PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.MSG_FULL_INVENTORY);
return false;
}
for (QuestItems qi : questItems)
addItem(player, qi.getItemId(), qi.getCount());
return true;
}
/**
* @param player
*/
public void restoreKinah(Player player)
{
// if kinah was deleted by some reason it should be restored with 0 count
if(player.getStorage(StorageType.CUBE.getId()).getKinahItem() == null)
{
Item kinahItem = newItem(182400001, 0);
player.getStorage(StorageType.CUBE.getId()).onLoadHandler(kinahItem);
}
if(player.getStorage(StorageType.ACCOUNT_WAREHOUSE.getId()).getKinahItem() == null)
{
Item kinahItem = newItem(182400001, 0);
kinahItem.setItemLocation(StorageType.ACCOUNT_WAREHOUSE.getId());
player.getStorage(StorageType.ACCOUNT_WAREHOUSE.getId()).onLoadHandler(kinahItem);
}
}
}