/* 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; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import client.IItem; import client.MapleCharacter; import client.MapleInventoryType; import constants.ItemConstants; import java.util.ArrayList; import tools.MaplePacketCreator; import tools.Output; /** * * @author Matze */ public class MapleTrade { private MapleTrade partner = null; private List<IItem> items = new ArrayList<IItem>(); private List<IItem> exchangeItems; private int meso = 0; private int exchangeMeso; boolean locked = false; private MapleCharacter chr; private byte number; public MapleTrade(byte number, MapleCharacter c) { chr = c; this.number = number; } private static int getFee(int meso) { int fee = 0; if (meso >= 100000000) { fee = (int) Math.round(0.06 * meso); } else if (meso >= 25000000) { fee = meso / 20; } else if (meso >= 10000000) { fee = meso / 25; } else if (meso >= 5000000) { fee = (int) Math.round(.03 * meso); } else if (meso >= 1000000) { fee = (int) Math.round(.018 * meso); } else if (meso >= 100000) { fee = meso / 125; } return fee; } private void lock() { locked = true; partner.getChr().getClient().getSession().write(MaplePacketCreator.getTradeConfirmation()); } private void complete1() { exchangeItems = partner.getItems(); exchangeMeso = partner.getMeso(); } private void complete2() { items.clear(); meso = 0; for (IItem item : exchangeItems) { if ((item.getFlag() & ItemConstants.KARMA) == ItemConstants.KARMA) item.setFlag((byte) (item.getFlag() ^ ItemConstants.KARMA)); // yay, reset trade flag. else if (item.getType() == IItem.ITEM && (item.getFlag() & ItemConstants.SPIKES) == ItemConstants.SPIKES) item.setFlag((byte) (item.getFlag() ^ ItemConstants.SPIKES)); MapleInventoryManipulator.addFromDrop(chr.getClient(), item, true); } if (exchangeMeso > 0) { chr.gainMeso(exchangeMeso - getFee(exchangeMeso), true, true, true); } exchangeMeso = 0; if (exchangeItems != null) { exchangeItems.clear(); } chr.getClient().getSession().write(MaplePacketCreator.getTradeCompletion(number)); } private void cancel() { for (IItem item : items) { MapleInventoryManipulator.addFromDrop(chr.getClient(), item, true); } if (meso > 0) { chr.gainMeso(meso, true, true, true); } meso = 0; if (items != null) { items.clear(); } exchangeMeso = 0; if (exchangeItems != null) { exchangeItems.clear(); } chr.getClient().getSession().write(MaplePacketCreator.getTradeCancel(number)); } private boolean isLocked() { return locked; } private int getMeso() { return meso; } public void setMeso(int meso) { if (locked) { throw new RuntimeException("Trade is locked."); } if (meso < 0) { Output.print("[MT] " + chr.getName() + " Trying to trade < 0 mesos"); return; } if (chr.getMeso() >= meso) { chr.gainMeso(-meso, false, true, false); this.meso += meso; chr.getClient().getSession().write(MaplePacketCreator.getTradeMesoSet((byte) 0, this.meso)); if (partner != null) { partner.getChr().getClient().getSession().write(MaplePacketCreator.getTradeMesoSet((byte) 1, this.meso)); } } else { } } public void addItem(IItem item) { items.add(item); chr.getClient().getSession().write(MaplePacketCreator.getTradeItemAdd((byte) 0, item)); if (partner != null) { partner.getChr().getClient().getSession().write(MaplePacketCreator.getTradeItemAdd((byte) 1, item)); } } public void chat(String message) { chr.getClient().getSession().write(MaplePacketCreator.getTradeChat(chr, message, true)); if (partner != null) { partner.getChr().getClient().getSession().write(MaplePacketCreator.getTradeChat(chr, message, false)); } } public MapleTrade getPartner() { return partner; } public void setPartner(MapleTrade partner) { if (locked) { return; } this.partner = partner; } public MapleCharacter getChr() { return chr; } public List<IItem> getItems() { return new LinkedList<IItem>(items); } private boolean fitsInInventory() { MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance(); Map<MapleInventoryType, Integer> neededSlots = new LinkedHashMap<MapleInventoryType, Integer>(); for (IItem item : exchangeItems) { MapleInventoryType type = mii.getInventoryType(item.getItemId()); if (neededSlots.get(type) == null) { neededSlots.put(type, 1); } else { neededSlots.put(type, neededSlots.get(type) + 1); } } for (Map.Entry<MapleInventoryType, Integer> entry : neededSlots.entrySet()) { if (chr.getInventory(entry.getKey()).isFull(entry.getValue() - 1)) { return false; } } return true; } public static void completeTrade(MapleCharacter c) { c.getTrade().lock(); MapleTrade local = c.getTrade(); MapleTrade partner = local.getPartner(); if (partner.isLocked()) { local.complete1(); partner.complete1(); if (!local.fitsInInventory() || !partner.fitsInInventory()) { cancelTrade(c); c.message("There is not enough inventory space to complete the trade."); partner.getChr().message("There is not enough inventory space to complete the trade."); return; } if (local.getChr().getLevel() < 15) { if (local.getChr().getMesosTraded() + local.exchangeMeso > 1000000) { cancelTrade(c); local.getChr().getClient().getSession().write(MaplePacketCreator.sendMesoLimit()); return; } else { local.getChr().addMesosTraded(local.exchangeMeso); } } else if (c.getTrade().getChr().getLevel() < 15) { if (c.getMesosTraded() + c.getTrade().exchangeMeso > 1000000) { cancelTrade(c); c.getClient().getSession().write(MaplePacketCreator.sendMesoLimit()); return; } else { c.addMesosTraded(local.exchangeMeso); } } local.complete2(); partner.complete2(); partner.getChr().setTrade(null); c.setTrade(null); } } public static void cancelTrade(MapleCharacter c) { c.getTrade().cancel(); if (c.getTrade().getPartner() != null) { c.getTrade().getPartner().cancel(); c.getTrade().getPartner().getChr().setTrade(null); } c.setTrade(null); } public static void startTrade(MapleCharacter c) { if (c.getTrade() == null) { c.setTrade(new MapleTrade((byte) 0, c)); c.getClient().getSession().write(MaplePacketCreator.getTradeStart(c.getClient(), c.getTrade(), (byte) 0)); } else { c.message("You are already in a trade."); } } public static void inviteTrade(MapleCharacter c1, MapleCharacter c2) { if (c2.getTrade() == null) { c2.setTrade(new MapleTrade((byte) 1, c2)); c2.getTrade().setPartner(c1.getTrade()); c1.getTrade().setPartner(c2.getTrade()); c2.getClient().getSession().write(MaplePacketCreator.getTradeInvite(c1)); } else { c1.message("The other player is already trading with someone else."); cancelTrade(c1); } } public static void visitTrade(MapleCharacter c1, MapleCharacter c2) { if (c1.getTrade() != null && c1.getTrade().getPartner() == c2.getTrade() && c2.getTrade() != null && c2.getTrade().getPartner() == c1.getTrade()) { c2.getClient().getSession().write(MaplePacketCreator.getTradePartnerAdd(c1)); c1.getClient().getSession().write(MaplePacketCreator.getTradeStart(c1.getClient(), c1.getTrade(), (byte) 1)); } else { c1.message("The other player has already closed the trade."); } } public static void declineTrade(MapleCharacter c) { MapleTrade trade = c.getTrade(); if (trade != null) { if (trade.getPartner() != null) { MapleCharacter other = trade.getPartner().getChr(); other.getTrade().cancel(); other.setTrade(null); other.message(c.getName() + " has declined your trade request."); } trade.cancel(); c.setTrade(null); } } }