/*
OrpheusMS: MapleStory Private Server based on OdinMS
Copyright (C) 2012 Aaron Weiss <aaron@deviant-core.net>
Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package server.maps;
import java.sql.PreparedStatement;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import client.IItem;
import client.ItemFactory;
import client.ItemInventoryEntry;
import client.MapleCharacter;
import client.MapleClient;
import client.MapleInventoryType;
import com.mysql.jdbc.Statement;
import constants.ItemConstants;
import java.sql.SQLException;
import java.util.ArrayList;
import tools.DatabaseConnection;
import net.MaplePacket;
import net.server.Server;
import server.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MaplePlayerShopItem;
import server.TimerManager;
import tools.MaplePacketCreator;
/**
*
* @author XoticStory
*/
public class HiredMerchant extends AbstractMapleMapObject {
private int ownerId, itemId, mesos = 0;
private byte channel, world;
private long start;
private String ownerName = "";
private String description = "";
private MapleCharacter[] visitors = new MapleCharacter[3];
private List<MaplePlayerShopItem> items = new LinkedList<MaplePlayerShopItem>();
private List<HiredMerchantMessage> messages = new LinkedList<HiredMerchantMessage>();
private List<SoldItem> sold = new LinkedList<SoldItem>();
private boolean open;
public ScheduledFuture<?> schedule = null;
private MapleMap map;
public HiredMerchant(final MapleCharacter owner, int itemId, String desc) {
this.setPosition(owner.getPosition());
this.start = System.currentTimeMillis();
this.ownerId = owner.getId();
this.channel = owner.getClient().getChannel();
this.world = owner.getWorld();
this.itemId = itemId;
this.ownerName = owner.getName();
this.description = desc;
this.map = owner.getMap();
this.schedule = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
HiredMerchant.this.closeShop(owner.getClient(), true);
}
}, 1000 * 60 * 60 * 24);
}
public void broadcastToVisitors(MaplePacket packet) {
for (MapleCharacter visitor : visitors) {
if (visitor != null) {
visitor.getClient().getSession().write(packet);
}
}
}
public void addVisitor(MapleCharacter visitor) {
int i = this.getFreeSlot();
if (i > -1) {
visitors[i] = visitor;
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorAdd(visitor, i + 1));
}
}
public void removeVisitor(MapleCharacter visitor) {
int slot = getVisitorSlot(visitor);
if (visitors[slot] == visitor) {
visitors[slot] = null;
if (slot != -1) {
broadcastToVisitors(MaplePacketCreator.hiredMerchantVisitorLeave(slot + 1));
}
}
}
public int getVisitorSlot(MapleCharacter visitor) {
for (int i = 0; i < 3; i++) {
if (visitors[i] == visitor) {
return i;
}
}
return -1; // Actually 0 because of the +1's.
}
public void removeAllVisitors(String message) {
for (int i = 0; i < 3; i++) {
if (visitors[i] != null) {
visitors[i].setHiredMerchant(null);
visitors[i].getClient().getSession().write(MaplePacketCreator.leaveHiredMerchant(i + 1, 0x11));
if (message.length() > 0) {
visitors[i].dropMessage(1, message);
}
visitors[i] = null;
}
}
}
public void buy(MapleClient c, int item, short quantity) {
MaplePlayerShopItem pItem = items.get(item);
synchronized (items) {
IItem newItem = pItem.getItem().copy();
newItem.setQuantity((short) ((pItem.getItem().getQuantity() * quantity)));
if ((newItem.getFlag() & ItemConstants.KARMA) == ItemConstants.KARMA) {
newItem.setFlag((byte) (newItem.getFlag() ^ ItemConstants.KARMA));
}
if (newItem.getType() == IItem.ITEM && (newItem.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES) {
newItem.setFlag((byte) (newItem.getFlag() ^ ItemConstants.SPIKES));
}
if (quantity < 1 || pItem.getBundles() < 1 || !pItem.isExist() || pItem.getBundles() < quantity) {
c.announce(MaplePacketCreator.enableActions());
return;
} else if (newItem.getType() == 1 && newItem.getQuantity() > 1) {
c.announce(MaplePacketCreator.enableActions());
return;
} else if (!pItem.isExist()) {
c.announce(MaplePacketCreator.enableActions());
return;
}
int price = pItem.getPrice() * quantity;
if (c.getPlayer().getMeso() >= price) {
if (MapleInventoryManipulator.addFromDrop(c, newItem, true)) {
c.getPlayer().gainMeso(-price, false);
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), quantity, price));
pItem.setBundles((short) (pItem.getBundles() - quantity));
if (pItem.getBundles() < 1) {
pItem.setDoesExist(false);
}
MapleCharacter owner = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterByName(ownerName);
if (owner != null) {
owner.addMerchantMesos(price);
} else {
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE characters SET MerchantMesos = MerchantMesos + " + price + " WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, ownerId);
ps.executeUpdate();
ps.close();
} catch (Exception e) {
}
}
} else {
c.getPlayer().dropMessage(1, "Your inventory is full. Please clean a slot before buying this item.");
}
} else {
c.getPlayer().dropMessage(1, "You do not have enough mesos.");
}
try {
this.saveItems(false);
} catch (Exception e) {
}
}
}
public void forceClose() {
if (schedule != null) {
schedule.cancel(false);
}
try {
saveItems(true);
} catch (SQLException ex) {
}
Server.getInstance().getChannel(world, channel).removeHiredMerchant(ownerId);
map.broadcastMessage(MaplePacketCreator.destroyHiredMerchant(getOwnerId()));
map.removeMapObject(this);
map = null;
schedule = null;
}
public void closeShop(MapleClient c, boolean timeout) {
map.removeMapObject(this);
map.broadcastMessage(MaplePacketCreator.destroyHiredMerchant(ownerId));
c.getChannelServer().removeHiredMerchant(ownerId);
try {
PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE characters SET HasMerchant = 0 WHERE id = ?", Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, ownerId);
ps.executeUpdate();
ps.close();
if (check(c.getPlayer(), getItems()) && !timeout) {
for (MaplePlayerShopItem mpsi : getItems()) {
if (mpsi.isExist() && (mpsi.getItem().getType() == IItem.EQUIP)) {
MapleInventoryManipulator.addFromDrop(c, mpsi.getItem(), false);
} else if (mpsi.isExist()) {
MapleInventoryManipulator.addById(c, mpsi.getItem().getItemId(), (short) (mpsi.getBundles() * mpsi.getItem().getQuantity()), null, -1, mpsi.getItem().getExpiration());
}
}
items.clear();
}
try {
this.saveItems(false);
} catch (Exception e) {
}
items.clear();
} catch (Exception e) {
}
schedule.cancel(false);
}
public String getOwner() {
return ownerName;
}
public int getOwnerId() {
return ownerId;
}
public String getDescription() {
return description;
}
public MapleCharacter[] getVisitors() {
return visitors;
}
public List<MaplePlayerShopItem> getItems() {
return Collections.unmodifiableList(items);
}
public void addItem(MaplePlayerShopItem item) {
items.add(item);
try {
this.saveItems(false);
} catch (SQLException ex) {
}
}
public void removeFromSlot(int slot) {
items.remove(slot);
try {
this.saveItems(false);
} catch (SQLException ex) {
}
}
public int getFreeSlot() {
for (int i = 0; i < 3; i++) {
if (visitors[i] == null) {
return i;
}
}
return -1;
}
public void setDescription(String description) {
this.description = description;
}
public boolean isOpen() {
return open;
}
public void setOpen(boolean set) {
this.open = set;
}
public int getItemId() {
return itemId;
}
public boolean isOwner(MapleCharacter chr) {
return chr.getId() == ownerId;
}
public void saveItems(boolean shutdown) throws SQLException {
List<ItemInventoryEntry> itemsWithType = new ArrayList<ItemInventoryEntry>();
for (MaplePlayerShopItem pItems : items) {
IItem newItem = pItems.getItem();
if (shutdown) {
newItem.setQuantity((short) (pItems.getItem().getQuantity() * pItems.getBundles()));
} else {
newItem.setQuantity(pItems.getItem().getQuantity());
}
if (pItems.getBundles() > 0) {
itemsWithType.add(new ItemInventoryEntry(newItem, MapleInventoryType.getByType(newItem.getType())));
}
}
ItemFactory.MERCHANT.saveItems(itemsWithType, this.ownerId);
}
private static boolean check(MapleCharacter chr, List<MaplePlayerShopItem> items) {
byte eq = 0, use = 0, setup = 0, etc = 0, cash = 0;
List<MapleInventoryType> li = new LinkedList<MapleInventoryType>();
for (MaplePlayerShopItem item : items) {
final MapleInventoryType invtype = MapleItemInformationProvider.getInstance().getInventoryType(item.getItem().getItemId());
if (!li.contains(invtype)) {
li.add(invtype);
}
if (invtype == MapleInventoryType.EQUIP) {
eq++;
} else if (invtype == MapleInventoryType.USE) {
use++;
} else if (invtype == MapleInventoryType.SETUP) {
setup++;
} else if (invtype == MapleInventoryType.ETC) {
etc++;
} else if (invtype == MapleInventoryType.CASH) {
cash++;
}
}
for (MapleInventoryType mit : li) {
if (mit == MapleInventoryType.EQUIP) {
if (chr.getInventory(MapleInventoryType.EQUIP).getNumFreeSlot() <= eq) {
return false;
}
} else if (mit == MapleInventoryType.USE) {
if (chr.getInventory(MapleInventoryType.USE).getNumFreeSlot() <= use) {
return false;
}
} else if (mit == MapleInventoryType.SETUP) {
if (chr.getInventory(MapleInventoryType.SETUP).getNumFreeSlot() <= setup) {
return false;
}
} else if (mit == MapleInventoryType.ETC) {
if (chr.getInventory(MapleInventoryType.ETC).getNumFreeSlot() <= etc) {
return false;
}
} else if (mit == MapleInventoryType.CASH) {
if (chr.getInventory(MapleInventoryType.CASH).getNumFreeSlot() <= cash) {
return false;
}
}
}
return true;
}
public byte getChannel() {
return channel;
}
public int getTimeLeft() {
return (int) ((System.currentTimeMillis() - start) / 1000);
}
public List<HiredMerchantMessage> getMessages() {
return messages;
}
public int getMapId() {
return map.getId();
}
public List<SoldItem> getSold() {
return sold;
}
public int getMesos() {
return mesos;
}
@Override
public void sendDestroyData(MapleClient client) {
return;
}
@Override
public MapleMapObjectType getType() {
return MapleMapObjectType.HIRED_MERCHANT;
}
@Override
public void sendSpawnData(MapleClient client) {
client.getSession().write(MaplePacketCreator.spawnHiredMerchant(this));
}
public class SoldItem {
int itemid, mesos;
short quantity;
String buyer;
public SoldItem(String buyer, int itemid, short quantity, int mesos) {
this.buyer = buyer;
this.itemid = itemid;
this.quantity = quantity;
this.mesos = mesos;
}
public String getBuyer() {
return buyer;
}
public int getItemId() {
return itemid;
}
public short getQuantity() {
return quantity;
}
public int getMesos() {
return mesos;
}
}
}