Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

Database error, column count (v40b)

Junior Spellweaver
Joined
Oct 30, 2010
Messages
148
Reaction score
8
[STRIKE]Hey, just tried setting up a server to play around with.

Got this error even though I should've set up everything correctly and recompiled, any idea what it might be? (RMI problem - fixed)

EDIT: Now I have another problem while trying to fix the DB:

It required me to add the column 'petid' to the table 'inventoryitems' for the character to save properly when making. However when I added that column, it gave me this error. Any idea where to find where the column count is specified or what I have done wrong? /:

EDIT 2: Fixed that problem by adding all columns according to the .java file.. (petid and ringid)

But when I try to enter the game now I get this Null Pointer Exception error:

Apr 18, 2017 8:51:06 PM net.sf.odinms.net.MapleServerHandler messageReceived
SEVERE: <admin (cid: 30013)> (Account: admin) Exception during processing packet: net.sf.odinms.net.channel.handler.PlayerLoggedinHandler: null
java.lang.NullPointerException
at net.sf.odinms.tools.MaplePacketCreator.getCharInfo(MaplePacketCreator.java:619)
at net.sf.odinms.net.channel.handler.PlayerLoggedinHandler.handlePacket(PlayerLoggedinHandler.java:92)
at net.sf.odinms.net.MapleServerHandler.messageReceived(MapleServerHandler.java:147)
at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.messageReceived(AbstractIoFilterChain.java:570)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput.flush(SimpleProtocolDecoderOutput.java:58)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:180)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.executor.ExecutorFilter.processEvent(ExecutorFilter.java:220)
at org.apache.mina.filter.executor.ExecutorFilter$ProcessEventsRunnable.run(ExecutorFilter.java:264)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51)
at java.lang.Thread.run(Unknown Source)

Looks like it has something to do with loading saved maps in VIP rocks, but I can't seem to understand what's wrong. Here's my MaplePacketCreator.java:

Code:
/*
	This file is part of the OdinMS Maple Story Server
    Copyright (C) 2008 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 version 3
    as published by the Free Software Foundation. You may not use, modify
    or distribute this program under any other version of the
    GNU Affero General Public License.

    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 net.sf.odinms.tools;

import java.awt.Point;
import java.awt.Rectangle;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import net.sf.odinms.client.BuddylistEntry;
import net.sf.odinms.client.IEquip;
import net.sf.odinms.client.IEquip.ScrollResult;
import net.sf.odinms.client.IItem;
import net.sf.odinms.client.ISkill;
import net.sf.odinms.client.Item;
import net.sf.odinms.client.MapleBuffStat;
import net.sf.odinms.client.MapleCharacter;
import net.sf.odinms.client.MapleClient;
import net.sf.odinms.client.MapleInventory;
import net.sf.odinms.client.MapleInventoryType;
import net.sf.odinms.client.MapleKeyBinding;
import net.sf.odinms.client.MaplePet;
import net.sf.odinms.client.MapleQuestStatus;
import net.sf.odinms.client.MapleRing;
import net.sf.odinms.client.MapleStat;
import net.sf.odinms.client.status.MonsterStatus;
import net.sf.odinms.constants.ServerConstants;
import net.sf.odinms.database.DatabaseConnection;
import net.sf.odinms.net.ByteArrayMaplePacket;
import net.sf.odinms.net.LongValueHolder;
import net.sf.odinms.net.MaplePacket;
import net.sf.odinms.net.SendPacketOpcode;
import net.sf.odinms.net.channel.handler.AbstractDealDamageHandler.SummonAttackEntry;
import net.sf.odinms.net.world.MapleParty;
import net.sf.odinms.net.world.MaplePartyCharacter;
import net.sf.odinms.net.world.PartyOperation;
import net.sf.odinms.server.CashItemInfo;
import net.sf.odinms.server.MapleItemInformationProvider;
import net.sf.odinms.server.MaplePlayerShop;
import net.sf.odinms.server.MaplePlayerShopItem;
import net.sf.odinms.server.MapleShopItem;
import net.sf.odinms.server.MapleSnowball;
import net.sf.odinms.server.MapleTrade;
import net.sf.odinms.server.PlayerInteraction.IPlayerInteractionManager;
import net.sf.odinms.server.PlayerInteraction.MapleMiniGame;
import net.sf.odinms.server.life.MapleMonster;
import net.sf.odinms.server.life.MapleNPC;
import net.sf.odinms.server.maps.MapleMap;
import net.sf.odinms.server.maps.MapleReactor;
import net.sf.odinms.server.maps.SummonMovementType;
import net.sf.odinms.server.movement.LifeMovementFragment;
import net.sf.odinms.tools.data.input.SeekableLittleEndianAccessor;
import net.sf.odinms.tools.data.output.LittleEndianWriter;
import net.sf.odinms.tools.data.output.MaplePacketLittleEndianWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Provides all MapleStory packets needed in one place.
 * 
 * @author Frz
 *  [USER=302236]Sin[/USER]ce Revision 259
 *  [USER=316612]Version[/USER] 1.0
 */
public class MaplePacketCreator {
	private static Logger log = LoggerFactory.getLogger(MaplePacketCreator.class);

	private final static byte[] CHAR_INFO_MAGIC = new byte[] { (byte) 0xff, (byte) 0xc9, (byte) 0x9a, 0x3b };
	private final static byte[] ITEM_MAGIC = new byte[] { (byte) 0x80, 5 };
	public static final List<Pair<MapleStat, Integer>> EMPTY_STATUPDATE = Collections.emptyList();
        private final static long FT_UT_OFFSET = 116444592000000000L; // EDT

        private static long getKoreanTimestamp(long realTimestamp) {
            long time = (realTimestamp / 1000 / 60); // convert to minutes
            return ((time * 600000000) + FT_UT_OFFSET);
        }
        
	/**
	 * Sends a hello packet.
	 * 
	 *  [USER=2000183830]para[/USER]m mapleVersion The maple client version.
	 *  [USER=2000183830]para[/USER]m sendIv the IV used by the server for sending
	 *  [USER=2000183830]para[/USER]m recvIv the IV used by the server for receiving
	 */
	//* GMS v40 Beta MapleStory
        public static MaplePacket getHello(short mapleVersion, byte[] sendIv, byte[] recvIv, boolean testServer) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.CLIENT_HELLO.getValue());
            mplew.writeShort(40);
            mplew.writeShort(1);
            mplew.write(0);
            mplew.write(recvIv);
            mplew.write(sendIv);
            mplew.write(5);
            return mplew.getPacket();
	}
        //*/
        
        /* KMS v1 Beta MapleStory
        public static MaplePacket getHello(short mapleVersion, byte[] sendIv, byte[] recvIv, boolean testServer) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(0x0E);
            mplew.writeShort(223);
            mplew.writeMapleAsciiString("");
            mplew.write(recvIv);
            mplew.write(sendIv);
            mplew.write(1); // 1 = KMS, 2 = KMST
            return mplew.getPacket();
	}
        //*/

	/**
	 * Sends a ping packet.
	 * 
	 *  [USER=850422]return[/USER] The packet.
	 */
	public static MaplePacket getPing() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PING.getValue());
            return mplew.getPacket();
	}

	/**
	 * Gets a login failed packet.
	 * 
	 * Possible values for <code>reason</code>:<br>
	 * 3: ID deleted or blocked<br>
	 * 4: Incorrect password<br>
	 * 5: Not a registered id<br>
	 * 6: System error<br>
	 * 7: Already logged in<br>
	 * 8: System error<br>
	 * 9: System error<br>
	 * 10: Cannot process so many connections<br>
	 * 11: Only users older than 20 can use this channel
	 * 
	 *  [USER=2000183830]para[/USER]m reason The reason logging in failed.
	 *  [USER=850422]return[/USER] The login failed packet.
	 */
	public static MaplePacket getLoginFailed(int reason) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(16);
            mplew.write(SendPacketOpcode.LOGIN_STATUS.getValue());
            mplew.write(reason);
            mplew.write(0);
            mplew.writeInt(0);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a successful authentication and PIN Request packet.
	 * 
	 *  [USER=2000183830]para[/USER]m c The client.
	 *  [USER=850422]return[/USER] The PIN request packet.
	 */
	public static MaplePacket OnCheckPasswordResult(MapleClient c) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.LOGIN_STATUS.getValue());
            mplew.write(0); // 0 or 1? o.o
            mplew.write(0);
            mplew.writeInt(0);
            mplew.writeInt(c.getAccID());
            mplew.write(0);
            mplew.write(c.isGm() ? 1 : 0);
            mplew.write(1);
            mplew.writeMapleAsciiString(c.getAccountName());
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            return mplew.getPacket();
	}
        
        public static MaplePacket getServerIP(int charid, byte[] ip, short port) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x05);
            mplew.writeShort(0); // ?
            mplew.write(ip);
            mplew.writeShort(port); //login or world or channel o.o
            mplew.writeInt(charid); // charid
            mplew.write(0);
            return mplew.getPacket();
        }
        
        public static MaplePacket getServerList() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            int channels = ServerConstants.Channels;
            mplew.write(SendPacketOpcode.SERVERLIST.getValue());
            mplew.write(0);
            mplew.writeMapleAsciiString(ServerConstants.WORLD_NAME); // Tespia is the only world
            mplew.write((byte)channels); // channels
            for (byte i = 0; i < (byte)channels; i++) {
                mplew.writeMapleAsciiString(ServerConstants.WORLD_NAME + "-" + (i + 1));
                mplew.writeInt(100); // server load, get connected size
                mplew.write(0); // Tespia is the only world
                mplew.write((byte)i);
                mplew.write(0); // unk
            }   
            return mplew.getPacket();
        }
        
        /**
	 * Gets a packet saying that the server list is over.
	 * 
	 *  [USER=850422]return[/USER] The end of server list packet.
	 */
	public static MaplePacket getEndOfServerList() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SERVERLIST.getValue());
            mplew.write(0xFF);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a packet detailing a server status message.
	 * 
	 * Possible values for <code>status</code>:<br>
	 * 0 - Normal<br>
	 * 1 - Highly populated<br>
	 * 2 - Full
	 * 
	 *  [USER=2000183830]para[/USER]m status The server status.
	 *  [USER=850422]return[/USER] The server status packet.
	 */
	public static MaplePacket getServerStatus(byte status) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SERVERSTATUS.getValue());
            mplew.write(status);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a packet with a list of characters.
	 * 
	 *  [USER=2000183830]para[/USER]m c The MapleClient to load characters of.
	 *  [USER=2000183830]para[/USER]m serverId The ID of the server requested.
	 *  [USER=850422]return[/USER] The character list packet.
	 */
	public static MaplePacket getCharList(MapleClient c, int serverId) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CHARLIST.getValue());
		mplew.write(0);
		List<MapleCharacter> chars = c.loadCharacters(serverId);
		mplew.write((byte) chars.size());
		for (MapleCharacter chr : chars) {
                    addCharData(mplew, chr);
		}
                mplew.writeLong(0); 
		return mplew.getPacket();
	}
        
        /**
	 * Adds an entry for a character to an existing
	 * MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWrite instance to write the stats
	 *            to.
	 *  [USER=2000183830]para[/USER]m chr The character to add.
	 */
	private static void addCharData(MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
            mplew.writeInt(chr.getId());
            mplew.writeAsciiString(chr.getName(), 13);
            mplew.write(chr.getGender());
            mplew.write(chr.getSkinColor().getId());
            mplew.writeInt(chr.getFace());
            mplew.writeInt(chr.getHair());
            
            mplew.writeLong(0); // Pet SN 
            
            mplew.write(chr.getLevel());
            mplew.writeShort(chr.getJob().getId());
            mplew.writeShort(chr.getStr());
            mplew.writeShort(chr.getDex());
            mplew.writeShort(chr.getInt());
            mplew.writeShort(chr.getLuk());
            mplew.writeShort(chr.getHp());
            mplew.writeShort(chr.getMaxHp());
            mplew.writeShort(chr.getMp());
            mplew.writeShort(chr.getMaxMp());
            mplew.writeShort(chr.getRemainingAp());
            mplew.writeShort(chr.getRemainingSp());
            mplew.writeInt(chr.getExp());
            mplew.writeShort(chr.getFame());
            
            mplew.writeInt(chr.getMapId());
            mplew.write(chr.getInitialSpawnpoint());
            
            mplew.writeLong(0);
            mplew.writeInt(0);
            mplew.writeInt(0);
            
            MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
            Map<Byte, Integer> myEquip = new LinkedHashMap<Byte, Integer>();
            Map<Byte, Integer> maskedEquip = new LinkedHashMap<Byte, Integer>();
            for (IItem item : iv.list()) {
                byte pos = (byte) (item.getPosition() * -1);
                if (pos < 100 && myEquip.get(pos) == null) {
                    maskedEquip.put(pos, item.getItemId()); 
                } else if (pos > 100) { // lol odinms. walao eh
                    pos -= 100;
                    if (myEquip.get(pos) != null) {
                        maskedEquip.put(pos, myEquip.get(pos));
                    }
                    myEquip.put(pos, item.getItemId());
                } else if (myEquip.get(pos) != null) {
                    maskedEquip.put(pos, item.getItemId());
                }
            }
            for (Entry<Byte, Integer> entry : maskedEquip.entrySet()) {
                mplew.write(entry.getKey());
                mplew.writeInt(entry.getValue());
            }
            mplew.write(0);
            for (Entry<Byte, Integer> entry : myEquip.entrySet()) {
                mplew.write(entry.getKey());
                mplew.writeInt(entry.getValue());
            }
            mplew.write(0);
            if ((chr.getJob().getId() / 100) != 5) {
                mplew.write(1);
                mplew.writeInt(chr.getRank());
                mplew.writeInt(chr.getRankMove()); // might be reverted
                mplew.writeInt(chr.getJobRank());
                mplew.writeInt(chr.getJobRankMove());
            } else {
                mplew.write(0);
            }
	}
        
        public static MaplePacket charNameResponse(String charname, boolean nameUsed) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.CHAR_NAME_RESPONSE.getValue());
            mplew.writeMapleAsciiString(charname);
            mplew.write(nameUsed ? 1 : 0);
            return mplew.getPacket();
	}
        
        public static MaplePacket addNewCharEntry(MapleCharacter chr, boolean worked) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.ADD_NEW_CHAR_ENTRY.getValue());
            mplew.write(worked ? 0 : 1);
            if (worked) {
                addCharData(mplew, chr);
            }
            mplew.writeLong(0);
            mplew.writeLong(0);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a packet telling the client the IP of the channel server.
	 * 
	 *  [USER=2000183830]para[/USER]m ip The ip of the requested channel server.
	 *  [USER=2000183830]para[/USER]m port The port the channel is on.
	 *  [USER=2000183830]para[/USER]m clientId The ID of the client.
	 *  [USER=850422]return[/USER] The server IP packet.
	 */
	public static MaplePacket getServerIP(byte[] ip, short port, int clientId) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SERVER_IP.getValue());
		mplew.writeShort(0);
		mplew.write(ip);
		mplew.writeShort(port);
		mplew.writeInt(clientId);
		mplew.write(0);
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet telling the client the IP of the new channel.
	 * 
	 *  [USER=2000183830]para[/USER]m ip The ip of the requested channel server.
	 *  [USER=2000183830]para[/USER]m port The port the channel is on.
	 *  [USER=850422]return[/USER] The server IP packet.
	 */
	public static MaplePacket getChannelChange(byte[] ip, int port) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CHANGE_CHANNEL.getValue());
		mplew.write(1);
		mplew.write(ip);
		mplew.writeShort(port);
		return mplew.getPacket();
	}
        
        /**
	 * Adds item info to existing MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to write to.
	 *  [USER=2000183830]para[/USER]m item The item to add info about.
	 *  [USER=2000183830]para[/USER]m zeroPosition Is the position zero?
	 *  [USER=2000183830]para[/USER]m leaveOut Leave out the item if position is zero?
	 */
        private static void addItemInfo(MaplePacketLittleEndianWriter mplew, IItem item, boolean shortPos) {
            addItemInfo(mplew, item, shortPos, false);
        }
        
	private static void addItemInfo(MaplePacketLittleEndianWriter mplew, IItem item, boolean shortPos, boolean zeroPosition) {
            Random rand = new Random();
            boolean cash = false;
            boolean ring = false;
            IEquip equip = null;
            if (item.getType() == IItem.EQUIP) {
                equip = (IEquip) item;
                if (equip.getRingId() > -1) {
                    ring = true;
                }
            }
            byte pos = zeroPosition ? 0 : item.getPosition();
            if (pos != 0) {
                if (shortPos) {
                    mplew.writeShort(pos);
                } else {
                    pos = (byte)Math.abs(pos);
                    if (pos > 100 || (item.getType() == MapleInventoryType.EQUIPPED.getType() && ring)) pos -= 100;
                    mplew.write(pos);
                }
            }
            boolean isEquip = (item.getItemId() / 1000000 == 1);
            boolean isPet = (item.getItemId() / 1000000 == 5);
            mplew.writeInt(item.getItemId());
            if (cash) {
                mplew.write(1);
                mplew.writeLong(rand.nextLong()); // Cash Item ID/SN
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            } else if (ring) {
                mplew.write(1);
                mplew.writeLong(equip.getRingId());
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            } else if (isPet) {
                mplew.write(1); // we use unique id like rings here
                mplew.writeLong(item.getPetId());
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
                MaplePet pet = MaplePet.loadFromDb(item.getItemId(), item.getPosition(), item.getPetId());
                String petname = pet.getName();
                mplew.writeAsciiString(petname, 13);
                mplew.write(pet.getLevel());
                mplew.writeShort(pet.getCloseness());
                mplew.write(pet.getFullness());
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2))); // TODO: expirations
                return;
            } else {
                mplew.write(0);
                mplew.writeLong(rand.nextLong()); // Non-NX don't expire, so.. random 8 byte long here
            }
            if (isEquip) {
                mplew.write(equip.getUpgradeSlots());
                mplew.write(equip.getLevel());
                mplew.writeShort(equip.getStr());
                mplew.writeShort(equip.getDex());
                mplew.writeShort(equip.getInt());
                mplew.writeShort(equip.getLuk());
                mplew.writeShort(equip.getHp());
                mplew.writeShort(equip.getMp());
                mplew.writeShort(equip.getWatk());
                mplew.writeShort(equip.getMatk());
                mplew.writeShort(equip.getWdef());
                mplew.writeShort(equip.getMdef());
                mplew.writeShort(equip.getAcc());
                mplew.writeShort(equip.getAvoid());
                mplew.writeShort(equip.getHands());
                mplew.writeShort(equip.getSpeed());
                mplew.writeShort(equip.getJump());
            } else {
                mplew.writeShort(item.getQuantity());
            }
	}
        
        /**
	 * Gets character info for a character.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The character to get info about.
	 *  [USER=850422]return[/USER] The character info packet.
	 */
        public static MaplePacket getCharInfo(MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WARP_TO_MAP.getValue());
                
		mplew.writeInt(chr.getClient().getChannel() - 1); //channel
		mplew.write(0); // portals
		mplew.write(1); // connecting
                
                Random random = new Random();
                mplew.writeInt(random.nextInt()); // 3 random ints here
                mplew.writeInt(random.nextInt());
                mplew.writeInt(random.nextInt());
                
                mplew.writeInt(0);
                
                mplew.writeShort(-1); //flags
                mplew.writeInt(chr.getId());
                mplew.writeAsciiString(chr.getName(), 13);
                mplew.write(chr.getGender());
                mplew.write(chr.getSkinColor().getId());
                mplew.writeInt(chr.getFace());
                mplew.writeInt(chr.getHair());
                
                mplew.writeLong(0); // Pet SN
                
                mplew.write(chr.getLevel());
                mplew.writeShort(chr.getJob().getId());
                mplew.writeShort(chr.getStr());
                mplew.writeShort(chr.getDex());
                mplew.writeShort(chr.getInt());
                mplew.writeShort(chr.getLuk());
                mplew.writeShort(chr.getHp());
                mplew.writeShort(chr.getMaxHp());
                mplew.writeShort(chr.getMp());
                mplew.writeShort(chr.getMaxMp());
                mplew.writeShort(chr.getRemainingAp());
                mplew.writeShort(chr.getRemainingSp());
                mplew.writeInt(chr.getExp());
                mplew.writeShort(chr.getFame());
                
                mplew.writeInt(chr.getMapId());
                mplew.write(chr.getInitialSpawnpoint());
                
                mplew.writeLong(0); // dont know what this is lul
                mplew.writeInt(0);
                mplew.writeInt(0);
                
                mplew.write(chr.getBuddylist().getCapacity());
                mplew.writeInt(chr.getMeso());
                
                MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
		Collection<IItem> equippedList = iv.list();
                Item[] equipped = new Item[17];
                Item[] equippedCash = new Item[17];
		for (IItem item : equippedList) {
                    byte pos = item.getPosition();
                    if (pos < 0) {
                        pos = (byte)Math.abs(pos);
                        if (pos > 100) {
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
                    if (pos < 0) {
                        if (pos < -100) {
                            pos += 100;
                            pos = (byte)Math.abs(pos);
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            pos = (byte)Math.abs(pos);
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
		}
		for (Item item : equipped) {
                    if (item != null) {
                        addItemInfo(mplew, item, false);
                    }
		}
		mplew.write(0);
                for (Item item : equippedCash) {
                    if (item != null) {
                        addItemInfo(mplew, item, false);
                    }
		}
                mplew.write(0);
                for (byte i = 1; i < 6; i++) {
                    mplew.write(100); // TODO: REAL maxslots and not forced 100 to all players + fix the CS operation to increase
                    iv = chr.getInventory(MapleInventoryType.getByType((byte)i));
                    for (IItem item : iv.list()) {
                        if (item != null && item.getPosition() > 0) {
                            addItemInfo(mplew, item, false);
                        }
                    }
                    mplew.write(0);
                }
                Map<ISkill, MapleCharacter.SkillEntry> skills = chr.getSkills();
		mplew.writeShort(skills.size());
		for (Entry<ISkill, MapleCharacter.SkillEntry> skill : skills.entrySet()) {
                    mplew.writeInt(skill.getKey().getId());
                    mplew.writeInt((byte)skill.getValue().skillevel);
		}
                mplew.writeShort(0); // TODO: Quests
		mplew.writeShort(0); // TODO: Mini Games
                
                List<MapleRing> rings = getRing(chr);
                mplew.writeShort(rings.size());
                for (MapleRing ring : rings) {
                    mplew.writeInt(ring.getPartnerChrId());
                    mplew.writeAsciiString(StringUtil.getRightPaddedStr(ring.getPartnerName(), '\0', 13));
                    mplew.writeInt(ring.getRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getPartnerRingId());
                    mplew.writeInt(0);
                }
                
                for (int map : chr.getVIPRockMaps()) {
                    mplew.writeInt(map);
                }
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet spawning a player as a mapobject to other clients.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The character to spawn to other clients.
	 *  [USER=850422]return[/USER] The spawn player packet.
	 */
	public static MaplePacket spawnPlayerMapobject(MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_PLAYER.getValue());
		mplew.writeInt(chr.getId());
		mplew.writeMapleAsciiString(chr.getName());

                int buffmask = 0;
		Integer buffvalue = null;
                
                if (chr.getBuffedValue(MapleBuffStat.DARKSIGHT) != null && !chr.isHidden()) {
                    buffmask |= MapleBuffStat.DARKSIGHT.getValue();
		}
                if (chr.getBuffedValue(MapleBuffStat.COMBO) != null) {
                    buffmask |= MapleBuffStat.COMBO.getValue();
                    buffvalue = Integer.valueOf(chr.getBuffedValue(MapleBuffStat.COMBO).intValue());
		}
                if (chr.getBuffedValue(MapleBuffStat.SHADOWPARTNER) != null) {
                    buffmask |= MapleBuffStat.SHADOWPARTNER.getValue();
		}
                
		mplew.writeInt(buffmask); // TODO: Finish buffmasks
                if (buffvalue != null) {
                    mplew.write(buffvalue.byteValue());
		}
                
                addCharLook(mplew, chr, true);
                mplew.writeInt(0); // [10/28/2014 8:25:40 PM] Justin: IncMaxMemberNumCost
                mplew.writeInt(chr.getItemEffect()); // TODO: Fix Chocolate Hearts (This is Cash Effect not Hearts)
                mplew.writeInt(chr.getChair());
                mplew.writeShort(chr.getPosition().x);
                mplew.writeShort(chr.getPosition().y);
                mplew.write(chr.getStance());
                mplew.writeShort(0); // FH
                
                // Pets
                if (chr.getPet(0) != null) {
                    mplew.write(1);
                    MaplePet pet = chr.getPet(0);
                    mplew.writeInt(pet.getItemId());
                    mplew.writeMapleAsciiString(pet.getName());
                    mplew.writeLong(pet.getUniqueId());
                    mplew.writeShort(pet.getPos().x);
                    mplew.writeShort(pet.getPos().y);
                    mplew.write(pet.getStance());
                    mplew.writeShort(pet.getFh());
                } else {
                    mplew.write(0);
                }
                
                // Mini Game & Interaction Boxes
                if (chr.getPlayerShop() != null && chr.getPlayerShop().isOwner(chr)) {
                    addShopBox(mplew, chr.getPlayerShop());
		} else if ((MapleMiniGame) chr.getInteraction() != null) {
                    addAnnounceBox(mplew, chr.getInteraction());
                } else {
                    mplew.write(0);
		}
                
                // Rings
                List<MapleRing> rings = getRing(chr);
                mplew.write(rings.size());
                for (MapleRing ring : rings) { 
                    mplew.writeInt(ring.getRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getPartnerRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getItemId());
                }
		return mplew.getPacket();
	}
        
        private static List<MapleRing> getRing(MapleCharacter chr) {
                MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED); 
                Collection<IItem> equippedC = iv.list(); 
                List<Item> equipped = new ArrayList<Item>(equippedC.size()); 
                for (IItem item : equippedC) { 
                    equipped.add((Item) item); 
                } 
                Collections.sort(equipped); 
                List<MapleRing> rings = new ArrayList<MapleRing>(); 
                for (Item item : equipped) { 
                        if (((IEquip) item).getRingId() > -1) { 
                            rings.add(MapleRing.loadFromDb(((IEquip) item).getRingId())); 
                        } 
                } 
                Collections.sort(rings); 
		return rings;
	}
        
        /**
	 * Gets a server message packet.
	 * 
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=850422]return[/USER] The server message packet.
	 */
	public static MaplePacket serverMessage(String message) {
		return serverMessage(4, 0, message, true, false);
	}

	/**
	 * Gets a server notice packet.
	 * 
	 * Possible values for <code>type</code>:<br>
	 * 0: [Notice]<br>
	 * 1: Popup<br>
	 * 2: Light blue background and lolwhut<br>
	 * 4: Scrolling message at top<br>
	 * 5: Pink Text<br>
	 * 6: Lightblue Text
	 * 
	 *  [USER=2000183830]para[/USER]m type The type of the notice.
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=850422]return[/USER] The server notice packet.
	 */
	public static MaplePacket serverNotice(int type, String message) {
            return serverMessage(type, 0, message, false, false);
	}

	/**
	 * Gets a server notice packet.
	 * 
	 * Possible values for <code>type</code>:<br>
	 * 0: [Notice]<br>
	 * 1: Popup<br>
	 * 2: Light blue background and lolwhut<br>
	 * 4: Scrolling message at top<br>
	 * 5: Pink Text<br>
	 * 6: Lightblue Text
	 * 
	 *  [USER=2000183830]para[/USER]m type The type of the notice.
	 *  [USER=2000183830]para[/USER]m channel The channel this notice was sent on.
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=850422]return[/USER] The server notice packet.
	 */
	public static MaplePacket serverNotice(int type, int channel, String message) {
		return serverMessage(type, channel, message, false, false);
	}

	/**
	 * Gets a server message packet.
	 * 
	 * Possible values for <code>type</code>:<br>
	 * 0: [Notice]<br>
	 * 1: Popup<br>
	 * 2: Light blue background and lolwhut<br>
	 * 4: Scrolling message at top<br>
	 * 5: Pink Text<br>
	 * 6: Lightblue Text
	 * 
	 *  [USER=2000183830]para[/USER]m type The type of the notice.
	 *  [USER=2000183830]para[/USER]m channel The channel this notice was sent on.
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=2000183830]para[/USER]m servermessage Is this a scrolling ticker?
	 *  [USER=850422]return[/USER] The server notice packet.
	 */
	private static MaplePacket serverMessage(int type, int channel, String message, boolean servermessage, boolean whisper) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SERVERMESSAGE.getValue());
		mplew.write(type > 5 ? type == 6 ? 0 : 5 : type); // v40 beta only has 0: [Notice], 1: Popup, 2: Mega, 3: Smega, 4: Header, and 5: Red text
		if (servermessage) {
                    mplew.write(1);
		}
		mplew.writeMapleAsciiString(message);
		if (type == 3) {
                    mplew.write(channel - 1); // channel
                    mplew.write(whisper ? 1 : 0); // boolean whisper byte is used on super megaphones
		}
		return mplew.getPacket();
	}
        
        public static MaplePacket facialExpression(MapleCharacter from, int expression) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FACIAL_EXPRESSION.getValue());
		mplew.writeInt(from.getId());
		mplew.writeInt(expression);
		return mplew.getPacket();
	}

        public static MaplePacket movePlayer(int cid, SeekableLittleEndianAccessor slea) {//List<LifeMovementFragment> moves
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_PLAYER.getValue());
		mplew.writeInt(cid);
                mplew.write(slea.read((int)slea.available()));
		return mplew.getPacket();
	}
        
        /**
	 * Gets a general chat packet.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The character who sent the chat.
	 *  [USER=2000183830]para[/USER]m text The text of the chat.
	 *  [USER=850422]return[/USER] The general chat packet.
	 */
	public static MaplePacket getChatText(MapleCharacter chr, String text) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CHATTEXT.getValue());
		mplew.writeInt(chr.getId());
		mplew.write(chr.isGM() ? 1 : 0); // gms have this set to != 0, gives them white
		mplew.writeMapleAsciiString(text);
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet telling the client to change maps.
	 * 
	 *  [USER=2000183830]para[/USER]m to The <code>MapleMap</code> to warp to.
	 *  [USER=2000183830]para[/USER]m spawnPoint The spawn portal number to spawn at.
	 *  [USER=2000183830]para[/USER]m chr The character warping to <code>to</code>
	 *  [USER=850422]return[/USER] The map change packet.
	 */
	public static MaplePacket getWarpToMap(MapleMap to, int spawnPoint, MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WARP_TO_MAP.getValue()); // 0x49
		mplew.writeInt(chr.getClient().getChannel() - 1);
		mplew.write(to.getPortals().size());
                mplew.write(0);
		mplew.writeInt(to.getId());
		mplew.write(spawnPoint);
		mplew.writeShort(chr.getHp()); // hp (???)
		return mplew.getPacket();
	}
        
        public static MaplePacket charInfo(MapleCharacter chr) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.CHAR_INFO.getValue());
                mplew.writeInt(chr.getId());
                mplew.write(chr.getLevel());
                mplew.writeShort(chr.getJob().getId());
                mplew.writeShort(chr.getFame());
                mplew.writeMapleAsciiString(chr.isGM() ? chr.getName().equalsIgnoreCase("<3") || chr.getName().equalsIgnoreCase("Eric") ? "The Boss" : "Eric's witch" : ""); //hehe
                if (chr.getPet(0) != null) {
                    MaplePet pet = chr.getPet(0);
                    mplew.write(1);
                    mplew.writeInt(pet.getItemId());
                    mplew.writeMapleAsciiString(pet.getName());
                    mplew.write(pet.getLevel());
                    mplew.writeShort(pet.getCloseness());
                    mplew.write(pet.getFullness());
                    mplew.writeInt(0); // Pet Equip ID? I can't find the ID in v40 so i'm not sure wtf this is
                } else {
                    mplew.write(0);
                }
                mplew.write(0); // Wishlist: while true, 4bytes (int)
                mplew.writeLong(0); // Unknown: Rings (?)
                return mplew.getPacket();
	}
        
        /**
	 * Gets a NPC spawn packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The NPC to spawn.
	 *  [USER=850422]return[/USER] The NPC spawn packet.
	 */
	
        public static MaplePacket spawnNPC(MapleNPC life) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.SPAWN_NPC.getValue());
                mplew.writeInt(life.getObjectId());
		mplew.writeInt(life.getId());
		mplew.writeShort(life.getPosition().x);
		mplew.writeShort(life.getCy());
		mplew.write(life.isFacingLeft() ? 0 : 1); // faces left
		mplew.writeShort(life.getFh());
		mplew.writeShort(life.getRx0());
		mplew.writeShort(life.getRx1());
		return mplew.getPacket();
        }
        
        public static MaplePacket spawnNPCRequest(MapleNPC life) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.SPAWN_NPC_REQUEST_CONTROLLER.getValue());
                mplew.write(0x01);
                mplew.writeInt(life.getObjectId());
		mplew.writeInt(life.getId());
		mplew.writeShort(life.getPosition().x);
		mplew.writeShort(life.getCy());
		mplew.write(life.isFacingLeft() ? 0 : 1); // faces left
		mplew.writeShort(life.getFh());
		mplew.writeShort(life.getRx0());
		mplew.writeShort(life.getRx1());
		return mplew.getPacket();
        }
        
        public static MaplePacket NPCAnimation(SeekableLittleEndianAccessor slea) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x7F);
            byte[] data = slea.read((int)slea.available());
            if (data.length == 5) {
                try {
                    mplew.writeInt(BitTools.toInt32(data, 0));
                    mplew.writeShort(BitTools.toInt16(data, 4));
                } catch (Exception e) {
                }
            } else {
                mplew.write(data);
            }
            return mplew.getPacket();
        }
        
        /**
	 * Adds the aesthetic aspects of a character to an existing
	 * MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWrite instance to write the stats
	 *            to.
	 *  [USER=2000183830]para[/USER]m chr The character to add the looks of.
	 *  [USER=2000183830]para[/USER]m mega Unknown
	 */
	private static void addCharLook(MaplePacketLittleEndianWriter mplew, MapleCharacter chr, boolean mega) {
                if (mega) {
                    mplew.write(chr.getGender());
                }
		mplew.write(chr.getSkinColor().getId()); // skin color
		mplew.writeInt(chr.getFace()); // face
		mplew.write(0); // OdinMS: mega ? 1 : 0
		mplew.writeInt(chr.getHair()); // hair

                MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
		Collection<IItem> equippedList = iv.list();
                Item[] equipped = new Item[17];
                Item[] equippedCash = new Item[17];
		for (IItem item : equippedList) {
                    byte pos = item.getPosition();
                    if (pos < 0) {
                        pos = (byte)Math.abs(pos);
                        if (pos > 100) {
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
                    if (pos < 0) {
                        if (pos < -100) {
                            pos += 100;
                            pos = (byte)Math.abs(pos);
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            pos = (byte)Math.abs(pos);
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
		}
                Map<Byte, Integer> items = new LinkedHashMap<>();
                for (Item item : equippedCash) {
                    if (item != null) {
                        byte slotuse = (byte)Math.abs(item.getPosition());
                        if (slotuse > 100) slotuse -= 100;
                        items.put(slotuse, item.getItemId());
                    }
                }
                for (Item item : equipped) {
                    if (item != null && !items.containsKey((byte)Math.abs(item.getPosition()))) {
                        items.put((byte)Math.abs(item.getPosition()), item.getItemId());
                    }
                }
                for (Entry<Byte, Integer> entry : items.entrySet()) {
                    mplew.write(entry.getKey());
                    mplew.writeInt(entry.getValue());
                }
		mplew.write(0xFF);
                
		if (chr.getPet(0) != null) {
                    mplew.writeInt(chr.getPet(0).getItemId());
		} else {
                    mplew.writeInt(0); // Pet
		}
	}
        
        public static MaplePacket reportReply(byte type) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.REPORT.getValue());
            mplew.write(type);
            return mplew.getPacket();
        }

	/**
	 * Gets an empty stat update.
	 * 
	 *  [USER=850422]return[/USER] The empy stat update packet.
	 */
	public static MaplePacket enableActions2() {
		return updatePlayerStats(EMPTY_STATUPDATE, true);
	}

	/**
	 * Gets an update for specified stats.
	 * 
	 *  [USER=2000183830]para[/USER]m stats The stats to update.
	 *  [USER=850422]return[/USER] The stat update packet.
	 */
	public static MaplePacket updatePlayerStats(List<Pair<MapleStat, Integer>> stats) {
            return updatePlayerStats(stats, false);
	}

	/**
	 * Gets an update for specified stats.
	 * 
	 *  [USER=2000183830]para[/USER]m stats The list of stats to update.
	 *  [USER=2000183830]para[/USER]m itemReaction Result of an item reaction(?)
	 *  [USER=850422]return[/USER] The stat update packet.
	 */
	public static MaplePacket updatePlayerStats(List<Pair<MapleStat, Integer>> stats, boolean itemReaction) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_STATS.getValue());
		if (itemReaction) {
                    mplew.write(1);
		} else {
                    mplew.write(0);
		}
		int updateMask = 0;
		for (Pair<MapleStat, Integer> statupdate : stats) {
			updateMask |= statupdate.getLeft().getValue();
		}
		List<Pair<MapleStat, Integer>> mystats = stats;
		if (mystats.size() > 1) {
			Collections.sort(mystats, new Comparator<Pair<MapleStat, Integer>>() {

				@Override
				public int compare(Pair<MapleStat, Integer> o1, Pair<MapleStat, Integer> o2) {
					int val1 = o1.getLeft().getValue();
					int val2 = o2.getLeft().getValue();
					return (val1 < val2 ? -1 : (val1 == val2 ? 0 : 1));
				}
			});
		}
		mplew.writeInt(updateMask);
		for (Pair<MapleStat, Integer> statupdate : mystats) {
			if (statupdate.getLeft().getValue() >= 1) {
				if (statupdate.getLeft().getValue() == 0x1) {
                                    mplew.writeShort(statupdate.getRight().shortValue());
				} else if (statupdate.getLeft().getValue() <= 0x4) {
					mplew.writeInt(statupdate.getRight());
				} else if (statupdate.getLeft().getValue() < 0x20) {
					mplew.write(statupdate.getRight().shortValue());
				} else if (statupdate.getLeft().getValue() < 0xFFFF) {
					mplew.writeShort(statupdate.getRight().shortValue());
				} else {
					mplew.writeInt(statupdate.getRight().intValue());
				}
			}
		}
		return mplew.getPacket();
	}
        
        /**
	 * Gets a spawn monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The monster to spawn.
	 *  [USER=2000183830]para[/USER]m newSpawn Is it a new spawn?
	 *  [USER=850422]return[/USER] The spawn monster packet.
	 */
	public static MaplePacket spawnMonster(MapleMonster life, boolean newSpawn) {
		return spawnMonsterInternal(life, false, newSpawn, false, 0);
	}

	/**
	 * Gets a spawn monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The monster to spawn.
	 *  [USER=2000183830]para[/USER]m newSpawn Is it a new spawn?
	 *  [USER=2000183830]para[/USER]m effect The spawn effect.
	 *  [USER=850422]return[/USER] The spawn monster packet.
	 */
	public static MaplePacket spawnMonster(MapleMonster life, boolean newSpawn, int effect) {
		return spawnMonsterInternal(life, false, newSpawn, false, effect);
	}

	/**
	 * Gets a control monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The monster to give control to.
	 *  [USER=2000183830]para[/USER]m newSpawn Is it a new spawn?
	 *  [USER=2000183830]para[/USER]m aggro Aggressive monster?
	 *  [USER=850422]return[/USER] The monster control packet.
	 */
	public static MaplePacket controlMonster(MapleMonster life, boolean newSpawn, boolean aggro) {
		return spawnMonsterInternal(life, true, newSpawn, aggro, 0);
	}

	/**
	 * Internal function to handler monster spawning and controlling.
	 * 
	 *  [USER=2000183830]para[/USER]m life The mob to perform operations with.
	 *  [USER=2000183830]para[/USER]m requestController Requesting control of mob?
	 *  [USER=2000183830]para[/USER]m newSpawn New spawn (fade in?)
	 *  [USER=2000183830]para[/USER]m aggro Aggressive mob?
	 *  [USER=2000183830]para[/USER]m effect The spawn effect to use.
	 *  [USER=850422]return[/USER] The spawn/control packet.
	 */
	private static MaplePacket spawnMonsterInternal(MapleMonster life, boolean requestController, boolean newSpawn, boolean aggro, int effect) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		if (requestController) {
                    mplew.write(SendPacketOpcode.SPAWN_MONSTER_CONTROL.getValue());
                    mplew.write(1);
		} else {
                    mplew.write(SendPacketOpcode.SPAWN_MONSTER.getValue());
		}
		mplew.writeInt(life.getObjectId());
		mplew.writeInt(life.getId());
		mplew.writeShort(life.getPosition().x);
		mplew.writeShort(life.getPosition().y);
                if (requestController) {
                    mplew.write(2);
                } else {
                    mplew.write((life.getController() != null ? 0x08 : 0x02));
                }
                mplew.writeShort(life.getFh()); // seems to be left and right
                mplew.writeShort(life.getStance());
		if (effect > 0) {
                    mplew.write(effect);
                    mplew.writeInt(life.getObjectId());
		}
		if (newSpawn) {
                    mplew.write(-2);
		} else {
                    mplew.write(-1);
		}
                mplew.write(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	/**
	 * Gets a stop control monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The ObjectID of the monster to stop controlling.
	 *  [USER=850422]return[/USER] The stop control monster packet.
	 */
	public static MaplePacket stopControllingMonster(int oid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_MONSTER_CONTROL.getValue());
		mplew.write(0);
                mplew.writeInt(0);// ?
		return mplew.getPacket();
	}

	/**
	 * Gets a response to a move monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m objectid The ObjectID of the monster being moved.
	 *  [USER=2000183830]para[/USER]m moveid The movement ID.
	 *  [USER=2000183830]para[/USER]m currentMp The current MP of the monster.
	 *  [USER=2000183830]para[/USER]m useSkills Can the monster use skills?
         *  [USER=2000183830]para[/USER]m skill The skill used
         *  [USER=2000183830]para[/USER]m level The level of the skill used
	 *  [USER=850422]return[/USER] The move response packet.
	 */
	public static MaplePacket moveMonsterResponse(int objectid, short moveid, int currentMp, boolean useSkills, byte skill, byte level) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_MONSTER_RESPONSE.getValue());
		mplew.writeInt(objectid);
                mplew.writeShort(moveid);
                mplew.write(useSkills ? 1 : 0);
		mplew.writeShort(currentMp);
		mplew.write(skill); // skill
                mplew.write(level); // level
		return mplew.getPacket();
	}
        
        public static MaplePacket giveFameResponse(int mode, String charname, int newfame) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FAME_RESPONSE.getValue());
		mplew.write(0);
		mplew.writeMapleAsciiString(charname);
		mplew.write(mode);
		mplew.writeInt(newfame);
		return mplew.getPacket();
	}

	/**
	 * status can be: <br>
	 * 0: ok, use giveFameResponse<br>
	 * 1: the username is incorrectly entered<br>
	 * 2: users under level 15 are unable to toggle with fame.<br>
	 * 3: can't raise or drop fame anymore today.<br>
	 * 4: can't raise or drop fame for this character for this month anymore.<br>
	 * 5: received fame, use receiveFame()<br>
	 * 6: level of fame neither has been raised nor dropped due to an unexpected
	 * error
	 * 
	 *  [USER=2000183830]para[/USER]m status
	 *  [USER=2000183830]para[/USER]m mode
	 *  [USER=2000183830]para[/USER]m charname
	 *  [USER=2000183830]para[/USER]m newfame
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket giveFameErrorResponse(int status) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FAME_RESPONSE.getValue());
		mplew.writeInt(status);
		return mplew.getPacket();
	}

	public static MaplePacket receiveFame(int mode, String charnameFrom) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FAME_RESPONSE.getValue());
		mplew.write(5);
		mplew.writeMapleAsciiString(charnameFrom);
		mplew.write(mode);
		return mplew.getPacket();
	}

	public static MaplePacket partyCreated() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(7);
                for (int i = 0; i < 5; i++) {
                    if (i >= 0 && i <= 2)
                        mplew.writeInt(1);
                    else if (i >= 3 && i <= 4)
                        mplew.writeShort(1);
                }
		return mplew.getPacket();
	}

	public static MaplePacket partyInvite(MapleCharacter from) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(4);
		mplew.writeInt(from.getParty().getId());
		mplew.writeMapleAsciiString(from.getName());
		return mplew.getPacket();
	}

	/**
	 * 9: a beginner can't create a party<br>
	 * 10/13/18: your request for a party didn't work due to an unexpected error<br>
	 * 12: you have yet to join a party<br>
	 * 15: already have joined a party<br>
	 * 16: the party you are trying to join is already at full capacity<br>
	 * 17: unable to find the requested character in this channel<br>
	 * 
	 *  [USER=2000183830]para[/USER]m message
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket partyStatusMessage(int message) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(message);
		return mplew.getPacket();
	}

	/**
	 * 21: has denied the invitation<br>
	 * 
	 *  [USER=2000183830]para[/USER]m message
	 *  [USER=2000183830]para[/USER]m charname
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket partyStatusMessage(int message, String charname) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(message);
		mplew.writeMapleAsciiString(charname);
		return mplew.getPacket();
	}

	private static void addPartyStatus(int forchannel, MapleParty party, LittleEndianWriter lew, boolean leaving) {
		List<MaplePartyCharacter> partymembers = new ArrayList<MaplePartyCharacter>(party.getMembers());
		while (partymembers.size() < 6) {
                    partymembers.add(new MaplePartyCharacter());
		}
		for (MaplePartyCharacter partychar : partymembers) {
                    lew.writeInt(partychar.getId());
		}
		for (MaplePartyCharacter partychar : partymembers) {
                    lew.writeAsciiString(StringUtil.getRightPaddedStr(partychar.getName(), '\0', 13));
                }
                for (MaplePartyCharacter partychar : partymembers) {
                    if (partychar.isOnline()) {
                        // This handles the Map IDs, but in v40 beta if you're
                        // not on the same map, the player appears offline. We
                        // have to eventually find a workaround for this.
                        int id = partychar.getMapid();
                        lew.writeInt(id);
                    } else {
                        lew.writeInt(-2);
                    }
                }
                lew.writeInt(party.getLeader().getId());
                for (MaplePartyCharacter partychar : partymembers) {
                    if (partychar.isOnline()) {
                        lew.writeInt(partychar.getChannel() - 1);
                    } else {
                        lew.writeInt(-2);
                    }
                }
                for (MaplePartyCharacter partychar : partymembers) {
                    if (partychar.getChannel() == forchannel && !leaving) {
                        lew.writeInt(partychar.getDoorTown());
                        lew.writeInt(partychar.getDoorTarget());
                        lew.writeInt(partychar.getDoorPosition().x);
                        lew.writeInt(partychar.getDoorPosition().y);
                    } else {
                        lew.writeInt(999999999);
                        lew.writeInt(999999999);
                        lew.writeInt(-1);
                        lew.writeInt(-1);
                    }
                }
	}

        public static MaplePacket updateParty(int forChannel, MapleParty party, PartyOperation op, MaplePartyCharacter target) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		switch (op) {
                        case DISBAND:
			case EXPEL:
			case LEAVE:
				mplew.write(0xB);
                                mplew.writeInt(party.getId());
                                mplew.writeInt(target.getId());
				if (op == PartyOperation.DISBAND) {
                                    mplew.write(0);
				} else {
                                    mplew.write(1);
                                    if (op == PartyOperation.EXPEL) {
                                        mplew.write(1);
                                    } else {
                                        mplew.write(0);
                                    }
                                    mplew.writeMapleAsciiString(target.getName());
                                    addPartyStatus(forChannel, party, mplew, false);
				}

				break;
			case JOIN:
				mplew.write(0xE);
				mplew.writeInt(1);
				mplew.writeMapleAsciiString(target.getName());
				addPartyStatus(forChannel, party, mplew, false);
				break;
			case SILENT_UPDATE:
			case LOG_ONOFF: // for some reason this has problems updating when logging off lol idk
				mplew.write(0x06);
                                mplew.writeInt(party.getId());
                                addPartyStatus(forChannel, party, mplew, false);
				break;

		}
		return mplew.getPacket();
	}

	public static MaplePacket partyPortal(int townId, int targetId, Point position) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.writeShort(0x1A); // 0x22 v55
		mplew.writeInt(townId);
		mplew.writeInt(targetId);
		mplew.writeShort(position.x);
		mplew.writeShort(position.y);
		return mplew.getPacket();
	}

	public static MaplePacket updatePartyMemberHP(int cid, int curhp, int maxhp) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_PARTYMEMBER_HP.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(curhp);
		mplew.writeInt(maxhp);
		return mplew.getPacket();
	}

	/**
	 * mode: 0 buddychat; 1 partychat; 2 guildchat
	 * 
	 *  [USER=2000183830]para[/USER]m name
	 *  [USER=2000183830]para[/USER]m chattext
	 *  [USER=2000183830]para[/USER]m mode
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket multiChat(String name, String chattext, int mode) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MULTICHAT.getValue());
		mplew.write(mode);
		mplew.writeMapleAsciiString(name);
		mplew.writeMapleAsciiString(chattext);
		return mplew.getPacket();
	}
        
        public static MaplePacket damageSummon(int cid, int summonSkillId, int damage, int unkByte, int monsterIdFrom) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DAMAGE_SUMMON.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(summonSkillId);
		mplew.write(unkByte);
                mplew.writeInt(monsterIdFrom);
		mplew.writeInt(damage);
		mplew.write(0);
		return mplew.getPacket();
	}

	public static MaplePacket damageMonster(int oid, int damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DAMAGE_MONSTER.getValue());
		mplew.writeInt(oid);
		mplew.write(0);
		mplew.writeInt(damage);
		return mplew.getPacket();
	}

        public static MaplePacket updateBuddylist(Collection<BuddylistEntry> buddylist) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(7);
		mplew.write(buddylist.size());
		for (BuddylistEntry buddy : buddylist) {
                    if (buddy.isVisible()) {
                        mplew.writeInt(buddy.getCharacterId()); // cid
                        mplew.writeAsciiString(StringUtil.getRightPaddedStr(buddy.getName(), '\0', 13));
                        mplew.write(0);
                        mplew.writeInt(buddy.getChannel() - 1);
                    }
		}
		for (int x = 0; x < buddylist.size(); x++) {
                    mplew.writeInt(0);
		}
		return mplew.getPacket();
	}

	public static MaplePacket requestBuddylistAdd(int cidFrom, String nameFrom) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(9);
		mplew.writeInt(cidFrom);
		mplew.writeMapleAsciiString(nameFrom);
		mplew.writeInt(cidFrom);
		mplew.writeAsciiString(StringUtil.getRightPaddedStr(nameFrom, '\0', 13));
		mplew.write(1);
		mplew.writeInt(0);//?
                mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket updateBuddyChannel(int characterid, int channel) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(0x14);
		mplew.writeInt(characterid);
		mplew.write(0);
		mplew.writeInt(channel);
		return mplew.getPacket();
	}

	public static MaplePacket updateBuddyCapacity(int capacity) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(0x15);
		mplew.write(capacity);
		return mplew.getPacket();
	}
        
        public static MaplePacket buddylistMessage(byte message) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(message);
		return mplew.getPacket();
	}

	public static MaplePacket showChair(short itemid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_CHAIR.getValue());
		mplew.write(itemid == -1 ? 0 : 1);
		if (itemid != -1) {
                    mplew.writeShort(itemid);
                }
		return mplew.getPacket();
	}

	public static MaplePacket musicChange(String song) {
		return environmentChange(song, 6);
	}

	public static MaplePacket showEffect(String effect) {
		return environmentChange(effect, 3);
	}

	public static MaplePacket playSound(String sound) {
		return environmentChange(sound, 4);
	}

	public static MaplePacket environmentChange(String env, int mode) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BOSS_ENV.getValue());
		mplew.write(mode);
		mplew.writeMapleAsciiString(env);
		return mplew.getPacket();
	}

	public static MaplePacket startMapEffect(String msg, int itemid, boolean active) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MAP_EFFECT.getValue());
		mplew.write(active ? 0 : 1);
		mplew.writeInt(itemid);
		if (active)
                    mplew.writeMapleAsciiString(msg);
		return mplew.getPacket();
	}
        
        public static MaplePacket startKiteEffect(MapleCharacter chr, int itemid, String message) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(0x88);
                mplew.writeInt(chr.getId());
                mplew.writeInt(itemid);
                mplew.writeMapleAsciiString(message);
                mplew.writeMapleAsciiString(chr.getName());
                mplew.writeShort(chr.getPosition().x);
                mplew.writeShort(chr.getPosition().y);
                return mplew.getPacket();
        }
        
        public static MaplePacket startJukebox(int itemid, String name) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(0x32);
                mplew.writeInt(itemid);
                mplew.writeMapleAsciiString(name);
                return mplew.getPacket();
        }

	public static MaplePacket removeMapEffect() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MAP_EFFECT.getValue());
		mplew.write(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}
        
        public static MaplePacket removeKiteEffect(int id) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(0x89);
		mplew.write(0);
		mplew.writeInt(id); // id or 0?
		return mplew.getPacket();
	}
        
        public static MaplePacket showBuffeffect(int cid, int skillid, int effectid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_FOREIGN_EFFECT.getValue());
		mplew.writeInt(cid); // ?
                mplew.write(effectid);
		mplew.writeInt(skillid);
                mplew.writeInt(1); // probably buff level but we don't know it and it doesn't really matter
		return mplew.getPacket();
	}

	public static MaplePacket showOwnBuffEffect(int skillid, int effectid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_ITEM_GAIN_INCHAT.getValue());
                mplew.write(effectid);
                mplew.writeInt(skillid);
                mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket getClock(int time) { // time in seconds
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CLOCK.getValue());
		mplew.write(2);
		mplew.writeInt(time);
		return mplew.getPacket();
	}

	public static MaplePacket getClockTime(int hour, int min, int sec) { // Current Time
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CLOCK.getValue());
		mplew.write(1); //Clock-Type
		mplew.write(hour);
		mplew.write(min);
		mplew.write(sec);
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet to spawn a special map object.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The MapleCharacter who spawned the object.
	 *  [USER=2000183830]para[/USER]m skill The skill used.
	 *  [USER=2000183830]para[/USER]m skillLevel The level of the skill used.
	 *  [USER=2000183830]para[/USER]m pos Where the object was spawned.
	 *  [USER=2000183830]para[/USER]m movementType Movement type of the object.
	 *  [USER=2000183830]para[/USER]m animated Animated spawn?
	 *  [USER=850422]return[/USER] The spawn packet for the map object.
	 */
	public static MaplePacket spawnSpecialMapObject(MapleCharacter chr, int skill, int skillLevel, Point pos, SummonMovementType movementType, boolean animated) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_SPECIAL_MAPOBJECT.getValue());
		mplew.writeInt(chr.getId());
		mplew.writeInt(skill);
		mplew.write(skillLevel);
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		mplew.write(0); // ?
		mplew.writeShort(0);
		mplew.write(movementType.getValue()); // 0 = don't move, 1 = follow
		mplew.write(animated ? 0 : 1);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet to remove a special map object.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The MapleCharacter who removed the object.
	 *  [USER=2000183830]para[/USER]m skill The skill used to create the object.
	 *  [USER=2000183830]para[/USER]m animated Animated removal?
	 *  [USER=850422]return[/USER] The packet removing the object.
	 */
	public static MaplePacket removeSpecialMapObject(MapleCharacter chr, int skill, boolean animated) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_SPECIAL_MAPOBJECT.getValue());
		mplew.writeInt(chr.getId());
		mplew.writeInt(skill);
		mplew.write(animated ? 4 : 1); // ?
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show an EXP increase.
	 * 
	 *  [USER=2000183830]para[/USER]m gain The amount of EXP gained.
	 *  [USER=2000183830]para[/USER]m inChat In the chat box?
	 *  [USER=2000183830]para[/USER]m white White text or yellow?
	 *  [USER=850422]return[/USER] The exp gained packet.
	 */
	public static MaplePacket getShowExpGain(int gain, boolean inChat, boolean white) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(3); // 3 = exp, 4 = fame, 5 = mesos
		mplew.write(white ? 1 : 0);
		mplew.writeInt(gain);
		mplew.write(inChat ? 1 : 0);
                mplew.writeInt(0);
                mplew.writeInt(0);
                mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show a meso gain.
	 * 
	 *  [USER=2000183830]para[/USER]m gain How many mesos gained.
	 *  [USER=850422]return[/USER] The meso gain packet.
	 */
	public static MaplePacket getShowMesoGain(int gain) {
		return getShowMesoGain(gain, false);
	}

	/**
	 * Gets a packet telling the client to show a meso gain.
	 * 
	 *  [USER=2000183830]para[/USER]m gain How many mesos gained.
	 *  [USER=2000183830]para[/USER]m inChat Show in the chat window?
	 *  [USER=850422]return[/USER] The meso gain packet.
	 */
	public static MaplePacket getShowMesoGain(int gain, boolean inChat) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		if (!inChat) {
                    mplew.write(0);
                    mplew.write(1);
		} else {
                    mplew.write(5);
		}
		mplew.writeInt(gain);
		mplew.writeShort(0);
		return mplew.getPacket();
	}
        
        public static MaplePacket enableActions() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(0x12);
		mplew.write(1);
                mplew.write(0);
                mplew.write(0);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show a item gain.
	 * 
	 *  [USER=2000183830]para[/USER]m itemId The ID of the item gained.
	 *  [USER=2000183830]para[/USER]m quantity How many items gained.
	 *  [USER=850422]return[/USER] The item gain packet.
	 */
	public static MaplePacket getShowItemGain(int itemId, short quantity) {
		return getShowItemGain(itemId, quantity, false);
	}

	/**
	 * Gets a packet telling the client to show an item gain.
	 * 
	 *  [USER=2000183830]para[/USER]m itemId The ID of the item gained.
	 *  [USER=2000183830]para[/USER]m quantity The number of items gained.
	 *  [USER=2000183830]para[/USER]m inChat Show in the chat window?
	 *  [USER=850422]return[/USER] The item gain packet.
	 */
	public static MaplePacket getShowItemGain(int itemId, short quantity, boolean inChat) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		if (inChat) {
			mplew.write(SendPacketOpcode.SHOW_ITEM_GAIN_INCHAT.getValue());
			mplew.write(3);
			mplew.write(1);
			mplew.writeInt(itemId);
			mplew.writeInt(quantity);
		} else {
			mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
			mplew.writeShort(0);
			mplew.writeInt(itemId);
			mplew.writeInt(quantity);
			mplew.writeInt(0);
			mplew.writeInt(0);
		}
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client that a monster was killed.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The objectID of the killed monster.
	 *  [USER=2000183830]para[/USER]m animation Show killed animation?
	 *  [USER=850422]return[/USER] The kill monster packet.
	 */
	public static MaplePacket killMonster(int oid, boolean animation) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.KILL_MONSTER.getValue());
		mplew.writeInt(oid);
		if (animation) {
                    mplew.write(1);
		} else {
                    mplew.write(0);
		}
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show mesos coming out of a map
	 * object.
	 * 
	 *  [USER=2000183830]para[/USER]m amount The amount of mesos.
	 *  [USER=2000183830]para[/USER]m itemoid The ObjectID of the dropped mesos.
	 *  [USER=2000183830]para[/USER]m dropperoid The OID of the dropper.
	 *  [USER=2000183830]para[/USER]m ownerid The ID of the drop owner.
	 *  [USER=2000183830]para[/USER]m dropfrom Where to drop from.
	 *  [USER=2000183830]para[/USER]m dropto Where the drop lands.
	 *  [USER=2000183830]para[/USER]m mod ?
	 *  [USER=850422]return[/USER] The drop mesos packet.
	 */
	public static MaplePacket dropMesoFromMapObject(int amount, int itemoid, int dropperoid, int ownerid, Point dropfrom, Point dropto, byte mod) {
            return dropItemFromMapObjectInternal(amount, itemoid, dropperoid, ownerid, dropfrom, dropto, mod, true);
	}

	/**
	 * Gets a packet telling the client to show an item coming out of a map
	 * object.
	 * 
	 *  [USER=2000183830]para[/USER]m itemid The ID of the dropped item.
	 *  [USER=2000183830]para[/USER]m itemoid The ObjectID of the dropped item.
	 *  [USER=2000183830]para[/USER]m dropperoid The OID of the dropper.
	 *  [USER=2000183830]para[/USER]m ownerid The ID of the drop owner.
	 *  [USER=2000183830]para[/USER]m dropfrom Where to drop from.
	 *  [USER=2000183830]para[/USER]m dropto Where the drop lands.
	 *  [USER=2000183830]para[/USER]m mod ?
	 *  [USER=850422]return[/USER] The drop mesos packet.
	 */
	public static MaplePacket dropItemFromMapObject(int itemid, int itemoid, int dropperoid, int ownerid, Point dropfrom, Point dropto, byte mod) {
		return dropItemFromMapObjectInternal(itemid, itemoid, dropperoid, ownerid, dropfrom, dropto, mod, false);
	}

	/**
	 * Internal function to get a packet to tell the client to drop an item onto
	 * the map.
	 * 
	 *  [USER=2000183830]para[/USER]m itemid The ID of the item to drop.
	 *  [USER=2000183830]para[/USER]m itemoid The ObjectID of the dropped item.
	 *  [USER=2000183830]para[/USER]m dropperoid The OID of the dropper.
	 *  [USER=2000183830]para[/USER]m ownerid The ID of the drop owner.
	 *  [USER=2000183830]para[/USER]m dropfrom Where to drop from.
	 *  [USER=2000183830]para[/USER]m dropto Where the drop lands.
	 *  [USER=2000183830]para[/USER]m mod ?
	 *  [USER=2000183830]para[/USER]m mesos Is the drop mesos?
	 *  [USER=850422]return[/USER] The item drop packet.
	 */
	public static MaplePacket dropItemFromMapObjectInternal(int itemid, int itemoid, int dropperoid, int ownerid, Point dropfrom, Point dropto, byte mod, boolean mesos) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DROP_ITEM_FROM_MAPOBJECT.getValue());
		mplew.write(mod); // type
		mplew.writeInt(itemoid);
		mplew.write(mesos ? 1 : 0); // 1 = mesos, 0 =item
		mplew.writeInt(itemid);
		mplew.writeInt(ownerid);
		mplew.write(0); // drop.Type
		mplew.writeShort(dropto.x);
		mplew.writeShort(dropto.y);
                mplew.writeInt(dropperoid); 
		if (mod != 2) {
                    mplew.writeShort(dropfrom.x);
                    mplew.writeShort(dropfrom.y);
                    mplew.writeShort(0);
		}
		if (!mesos) {
                    addExpirationTime(mplew, System.currentTimeMillis(), false);
		}
                mplew.write(0); // player dropped
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket moveSummon(int cid, int summonSkill, SeekableLittleEndianAccessor slea) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_SUMMON.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(summonSkill);
		mplew.write(slea.read((int)slea.available()));
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket moveMonster(int mobid, boolean useSkill, byte skill, Point startPos, SeekableLittleEndianAccessor slea) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_MONSTER.getValue());
		mplew.writeInt(mobid);
                mplew.write(useSkill ? 1 : 0);
		mplew.write(skill);
		mplew.writeInt(0);
		mplew.write(slea.read((int)slea.available()));
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket summonAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, List<Pair<Integer, List<Integer>>> damage) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SUMMON_ATTACK.getValue());
		addAttackSummon(mplew, cid, skill, stance, damage);
		return mplew.getPacket();
	}

	public static MaplePacket closeRangeAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, List<Pair<Integer, List<Integer>>> damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CLOSE_RANGE_ATTACK.getValue());
		if (skill == 4211006) {
                    addMesoExplosion(mplew, cid, skill, stance, direction, numAttackedAndDamage, 0, damage);
		} else {
                    addAttackBody(mplew, cid, skill, stance, direction, numAttackedAndDamage, 0, damage);
		}
		return mplew.getPacket();
	}

	public static MaplePacket rangedAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, int projectile, List<Pair<Integer, List<Integer>>> damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.RANGED_ATTACK.getValue()); // 0x54 in v40 tooo
		addAttackBody(mplew, cid, skill, stance, direction, numAttackedAndDamage, projectile, damage);
		return mplew.getPacket();
	}

	public static MaplePacket magicAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, List<Pair<Integer, List<Integer>>> damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MAGIC_ATTACK.getValue());
		addAttackBody(mplew, cid, skill, stance, direction, numAttackedAndDamage, 0, damage);
		return mplew.getPacket();
	}

	private static void addAttackBody(MaplePacketLittleEndianWriter lew, int cid, int skill, int stance, int direction, int numAttackedAndDamage, int projectile, List<Pair<Integer, List<Integer>>> damage) {
            lew.writeInt(cid);
            lew.write(numAttackedAndDamage);
            if (skill > 0) {
                lew.write(0xFF); // skill level, too low and some skills don't work (?)
                lew.writeInt(skill);
            } else {
                lew.write(0);
            }
            lew.write(stance);
            lew.write(direction);
            lew.write(0); // mastery.. 
            lew.writeInt(projectile); // 00
            for (Pair<Integer, List<Integer>> oned : damage) {
                if (oned.getRight() != null) {
                    lew.writeInt(oned.getLeft().intValue());
                    lew.write(0x06);
                    for (Integer eachd : oned.getRight()) {
                        lew.writeInt(eachd.intValue());
                    }
                }
            }
            lew.writeLong(0);
	}
        
        private static void addAttackSummon(MaplePacketLittleEndianWriter lew, int cid, int skill, int stance, List<Pair<Integer, List<Integer>>> damage) {
            lew.writeInt(cid);
            lew.writeInt(skill); // 2311006 (Bishop - Summon Dragon) : SkillID == SummonID
            lew.write(stance); // ?
            lew.write(1); // numAttacked == 1
            for (Pair<Integer, List<Integer>> oned : damage) {
                if (oned.getRight() != null) {
                    lew.writeInt(oned.getLeft().intValue());
                    lew.write(0x06);
                    for (Integer eachd : oned.getRight()) {
                        lew.writeInt(eachd.intValue());
                    }
                }
            }
            lew.writeLong(0);
	}

	private static void addMesoExplosion(LittleEndianWriter lew, int cid, int skill, int stance, int direction, int numAttackedAndDamage, int projectile, List<Pair<Integer, List<Integer>>> damage) {
		lew.writeInt(cid);
		lew.write(numAttackedAndDamage);
		lew.write(0x1E);
		lew.writeInt(skill);
		lew.write(stance);
                lew.write(direction);
                lew.write(0); // mastery
		lew.writeInt(projectile); // 00
		for (Pair<Integer, List<Integer>> oned : damage) {
                    if (oned.getRight() != null) {
                        lew.writeInt(oned.getLeft().intValue());
                        lew.write(0xFF);
                        lew.write(oned.getRight().size());
                        for (Integer eachd : oned.getRight()) {
                            lew.writeInt(eachd.intValue());
                        }
                    }
		}
	}
        
        public static MaplePacket applyMonsterStatus(int oid, Map<MonsterStatus, Integer> stats, int skill, boolean monsterSkill, int delay) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.APPLY_MONSTER_STATUS.getValue());
		mplew.writeInt(oid);
		int mask = 0;
		for (MonsterStatus stat : stats.keySet()) {
                    mask |= stat.getValue();
		}
		mplew.writeInt(mask);
		for (Integer val : stats.values()) {
			mplew.writeShort(val);
			if (monsterSkill) {
                            mplew.writeShort(skill);
                            mplew.writeShort(1);
			} else {
                            mplew.writeInt(skill);
			}
			mplew.writeShort(100); // buffTime, this needs to be coded properly -- as a workaround, we'll use 100.
		}
                
		mplew.writeShort(delay); // delay in ms
		return mplew.getPacket();
	}

	public static MaplePacket cancelMonsterStatus(int oid, Map<MonsterStatus, Integer> stats) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CANCEL_MONSTER_STATUS.getValue());
		mplew.writeInt(oid);
		int mask = 0;
		for (MonsterStatus stat : stats.keySet()) {
                    mask |= stat.getValue();
		}
		mplew.writeInt(mask);
		mplew.write(1);
		return mplew.getPacket();
	}

        public static MaplePacket getNPCShop(int sid, List<MapleShopItem> items) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.OPEN_NPC_SHOP.getValue());
		mplew.writeInt(sid);
                mplew.writeShort(items.size()); // item count
		for (MapleShopItem item : items) {
                        mplew.writeInt(item.getItemId());
                        mplew.writeInt(item.getPrice());
                        if (item.getItemId() / 10000 == 207) {
                            mplew.writeLong(5);
                        }
                        mplew.writeShort(item.getBuyable());
		}
		return mplew.getPacket();
	}
        
        /**
	 * code (8 = sell, 0 = buy, 0x20 = due to an error the trade did not happen
	 * o.o)
	 * 
	 *  [USER=2000183830]para[/USER]m code
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket confirmShopTransaction(byte code) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CONFIRM_SHOP_TRANSACTION.getValue());
		mplew.write(code); // recharge == 8?
		return mplew.getPacket();
	}
        
        /*
	 * 19 reference 00 01 00 = new while adding 01 01 00 = add from drop 00 01 01 = update count 00 01 03 = clear slot
	 * 01 01 02 = move to empty slot 01 02 03 = move and merge 01 02 01 = move and merge with rest
	 */
	public static MaplePacket addInventorySlot(MapleInventoryType type, IItem item) {
		return addInventorySlot(type, item, false);
	}

	public static MaplePacket addInventorySlot(MapleInventoryType type, IItem item, boolean fromDrop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		if (fromDrop) {
                    mplew.write(1);
		} else {
                    mplew.write(1); // 0
		}
                mplew.write(1);
                mplew.write(0);
		mplew.write(type.getType()); // iv type
		addItemInfo(mplew, item, true);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket updateInventorySlot(MapleInventoryType type, IItem item) {
            return updateInventorySlot(type, item, false);
	}

	public static MaplePacket updateInventorySlot(MapleInventoryType type, IItem item, boolean fromDrop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		if (fromDrop) {
                    mplew.write(1);
		} else {
                    mplew.write(0);
		}
		mplew.write(HexTool.getByteArrayFromHexString("01 01")); // update mode
		mplew.write(type.getType()); // iv type
		mplew.write(item.getPosition()); // slot id
		mplew.write(0); // ?
		mplew.writeShort(item.getQuantity());
		return mplew.getPacket();
	}

	public static MaplePacket moveInventoryItem(MapleInventoryType type, byte src, byte dst) {
		return moveInventoryItem(type, src, dst, (byte) -1);
	}

	public static MaplePacket moveInventoryItem(MapleInventoryType type, byte src, byte dst, byte equipIndicator) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 01 02"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		mplew.writeShort(dst);
		if (equipIndicator != -1) {
                    mplew.write(equipIndicator);
		}
		return mplew.getPacket();
	}

	public static MaplePacket moveAndMergeInventoryItem(MapleInventoryType type, byte src, byte dst, short total) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 02 03"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		mplew.write(1); // merge mode?

		mplew.write(type.getType());
		mplew.writeShort(dst);
		mplew.writeShort(total);
		return mplew.getPacket();
	}

	public static MaplePacket moveAndMergeWithRestInventoryItem(MapleInventoryType type, byte src, byte dst, short srcQ, short dstQ) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 02 01"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		mplew.writeShort(srcQ);
		mplew.write(HexTool.getByteArrayFromHexString("01"));
		mplew.write(type.getType());
		mplew.writeShort(dst);
		mplew.writeShort(dstQ);
		return mplew.getPacket();
	}

	public static MaplePacket clearInventoryItem(MapleInventoryType type, byte slot, boolean fromDrop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(fromDrop ? 1 : 0);
		mplew.write(HexTool.getByteArrayFromHexString("01 03"));
		mplew.write(type.getType());
		mplew.writeShort(slot);
		return mplew.getPacket();
	}

	public static MaplePacket removePlayerFromMap(int cid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_PLAYER_FROM_MAP.getValue());
		mplew.writeInt(cid);
		return mplew.getPacket();
	}

        /**
         * animation: 0 - expire<br/> 1 - without animation<br/> 2 - pickup<br/>
         * 4 - explode<br/> cid is ignored for 0 and 1
         * 
         *  [USER=2000183830]para[/USER]m oid
         *  [USER=2000183830]para[/USER]m animation
         *  [USER=2000183830]para[/USER]m cid
         *  [USER=850422]return[/USER]
        */
        public static MaplePacket removeItemFromMap(int oid, int animation, int cid) {
            return removeItemFromMap(oid, animation, cid, false, 0);
        }
        
	/**
	 * animation: 0 - expire<br/> 1 - without animation<br/> 2 - pickup<br/>
	 * 4 - explode<br/> cid is ignored for 0 and 1
	 * 
	 *  [USER=2000183830]para[/USER]m oid
	 *  [USER=2000183830]para[/USER]m animation
	 *  [USER=2000183830]para[/USER]m cid
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket removeItemFromMap(int oid, int animation, int cid, boolean pet, int slot) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_ITEM_FROM_MAP.getValue());
		mplew.write(animation); // expire
		mplew.writeInt(oid);
		if (animation >= 2) {
                    mplew.writeInt(cid);
                    if (pet) {
                        mplew.write(slot);
                    }
		}
		return mplew.getPacket();
	}

	public static MaplePacket updateCharLook(MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_CHAR_LOOK.getValue());
		mplew.writeInt(chr.getId());
                mplew.writeInt(0);
		mplew.write(1);
		
		addCharLook(mplew, chr, false);
                
                mplew.writeInt(0); // Unknown: 4bytes (int)
                
                mplew.write(0); // Unknown: 8bytes (int, int)
                mplew.write(0); // Unknown: 1bytes (byte)
                
                List<MapleRing> rings = getRing(chr);
		mplew.write(rings.size());  // Rings: 16 bytes (long, long)
                for (MapleRing ring : rings) { 
                    mplew.writeInt(ring.getRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getPartnerRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getItemId());
                }
		return mplew.getPacket();
	}

	public static MaplePacket dropInventoryItem(MapleInventoryType type, short src) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 01 03"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		if (src < 0) {
                    mplew.write(1);
		}
		return mplew.getPacket();
	}

	public static MaplePacket dropInventoryItemUpdate(MapleInventoryType type, IItem item) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 01 01"));
		mplew.write(type.getType());
		mplew.writeShort(item.getPosition());
		mplew.writeShort(item.getQuantity());
		return mplew.getPacket();
	}

	public static MaplePacket damagePlayer(int skill, int monsteridfrom, int cid, int damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DAMAGE_PLAYER.getValue());
		mplew.writeInt(cid);
                mplew.write(0xFE);
		mplew.writeInt(damage);
		mplew.writeInt(damage);
                mplew.writeInt(damage);
		mplew.writeLong(0);
                mplew.writeLong(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	private static <E extends LongValueHolder> int getIntMask(List<Pair<E, Integer>> statups) {
		int mask = 0x0;
		for (Pair<E, Integer> statup : statups) {
                    mask += statup.getLeft().getValue();
		}
		return mask;
	}

	private static <E extends LongValueHolder> int getIntMaskFromList(List<E> statups) {
		int mask = 0;
		for (E statup : statups) {
                    mask |= statup.getValue();
		}
		return mask;
	}

	/**
	 * It is important that statups is in the correct order (see decleration
	 * order in MapleBuffStat) since this method doesn't do automagical
	 * reordering.
	 * 
	 *  [USER=2000183830]para[/USER]m buffid
	 *  [USER=2000183830]para[/USER]m bufflength
	 *  [USER=2000183830]para[/USER]m statups
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket giveBuff(int buffid, int bufflength, List<Pair<MapleBuffStat, Integer>> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.GIVE_BUFF.getValue());
		int mask = getIntMask(statups);
		mplew.writeInt(mask); // :|
		for (Pair<MapleBuffStat, Integer> statup : statups) {
                    mplew.writeShort(statup.getRight().shortValue());
                    mplew.writeInt(buffid);
                    mplew.writeShort(bufflength);
		}
                mplew.writeLong(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket giveForeignBuff(int cid, List<Pair<MapleBuffStat, Integer>> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.GIVE_FOREIGN_BUFF.getValue());
		mplew.writeInt(cid);
		int mask = getIntMask(statups);
		mplew.writeInt(mask);
		for (Pair<MapleBuffStat, Integer> statup : statups) {
                    mplew.writeShort(statup.getRight().byteValue());
		}
		mplew.writeShort(0); // same as give_buff
		return mplew.getPacket();
	}

	public static MaplePacket cancelForeignBuff(int cid, List<MapleBuffStat> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CANCEL_FOREIGN_BUFF.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(getIntMaskFromList(statups));
		return mplew.getPacket();
	}

	public static MaplePacket cancelBuff(List<MapleBuffStat> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CANCEL_BUFF.getValue());
		mplew.writeInt(getIntMaskFromList(statups));
		mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalk(int npc, String talk, boolean prev, boolean next, boolean simple, boolean yesno) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
                mplew.write((simple ? 4 : yesno ? 1 : 0));
		mplew.writeMapleAsciiString(talk);
                if ((prev || next) && (!simple || !yesno)) { // dont send next,previous,or ok with yesno/simple else crash
                    mplew.write(prev ? 1 : 0);
                    mplew.write(next ? 1 : 0);
                }
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalkStyle(int npc, String talk, int styles[]) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
		mplew.write(5);
		mplew.writeMapleAsciiString(talk);
		mplew.write(styles.length);
		for (int i = 0; i < styles.length; i++) {
                    mplew.writeInt(styles[i]);
		}
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalkNum(int npc, String talk, int def, int min, int max) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
		mplew.write(3);
		mplew.writeMapleAsciiString(talk);
		mplew.writeInt(def);
		mplew.writeInt(min);
		mplew.writeInt(max);
		mplew.writeLong(0); // prob int but w/e
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalkText(int npc, String talk) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
		mplew.write(2);
		mplew.writeMapleAsciiString(talk);
                mplew.writeMapleAsciiString(""); // def
		mplew.writeShort(0); // min 
                mplew.writeShort(0); // max
		mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket showLevelup(int cid) {
		return showForeignEffect(cid, 0);
	}

	public static MaplePacket showJobChange(int cid) {
		return showForeignEffect(cid, 8);
	}

	public static MaplePacket showForeignEffect(int cid, int effect) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_FOREIGN_EFFECT.getValue());
		mplew.writeInt(cid); // ?
		mplew.write(effect);
                mplew.writeLong(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket updateSkill(int skillid, int level, int masterlevel) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_SKILLS.getValue());
		mplew.write(1);
		mplew.writeShort(1);
		mplew.writeInt(skillid);
		mplew.writeInt(level);
		mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket updateQuestMobKills(MapleQuestStatus status) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_QUEST_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(status.getQuest().getId());
		mplew.write(1);
		String killStr = "";
		for (int kills : status.getMobKills().values()) {
			killStr += StringUtil.getLeftPaddedStr(String.valueOf(kills), '0', 3);
		}
		mplew.writeMapleAsciiString(killStr);
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	public static MaplePacket getShowQuestCompletion(int id) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_QUEST_COMPLETION.getValue());
		mplew.writeShort(id);
		return mplew.getPacket();
	}
        
        public static MaplePacket getWhisper(String sender, int channel, String text) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(0x12);
		mplew.writeMapleAsciiString(sender);
		mplew.write(channel - 1); // I guess this is the channel
		mplew.writeMapleAsciiString(text);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m target name of the target character
	 *  [USER=2000183830]para[/USER]m reply error code: 0x0 = cannot find char, 0x1 = success
	 *  [USER=850422]return[/USER] the MaplePacket
	 */
	public static MaplePacket getWhisperReply(String target, byte reply) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(0x0A); // whisper?
		mplew.writeMapleAsciiString(target);
		mplew.write(reply);
		return mplew.getPacket();
	}

	public static MaplePacket getFindReplyWithMap(String target, int mapid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(9);
		mplew.writeMapleAsciiString(target);
		mplew.write(1);
		mplew.writeInt(mapid);
		mplew.writeInt(0); // ?? official doesn't send zeros here but whatever
		return mplew.getPacket();
	}

	public static MaplePacket getFindReply(String target, int channel) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(9);
		mplew.writeMapleAsciiString(target);
		mplew.write(3);
		mplew.writeInt(channel - 1);
                mplew.writeInt(0);
		return mplew.getPacket();
	}

	public static MaplePacket getInventoryFull() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(1);
		mplew.write(0);
                mplew.write(0);//?
		return mplew.getPacket();
	}
        
        public static MaplePacket messengerInvite(String from, int messengerid) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(3);
            mplew.writeMapleAsciiString(from);
            mplew.write(0);
            mplew.writeInt(messengerid);
            mplew.write(0);
            return mplew.getPacket();
        }

        public static MaplePacket addMessengerPlayer(String from, MapleCharacter chr, int position, int channel) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(0);
            mplew.write(position);
            addCharLook(mplew, chr, true);
            mplew.writeMapleAsciiString(from);
            mplew.write(channel);
            mplew.write(0);
            return mplew.getPacket();
        }

        public static MaplePacket removeMessengerPlayer(int position) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(2);
            mplew.write(position);
            return mplew.getPacket();
        }

        public static MaplePacket updateMessengerPlayer(String from, MapleCharacter chr, int position, int channel) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(7);
            mplew.write(position);
            addCharLook(mplew, chr, true);
            mplew.writeMapleAsciiString(from);
            mplew.write(channel);
            mplew.write(0);
            return mplew.getPacket();
        }

        public static MaplePacket joinMessenger(int position) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(1);
            mplew.write(position);
            return mplew.getPacket();
        }

        public static MaplePacket messengerChat(String text) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(6);
            mplew.writeMapleAsciiString(text);
            return mplew.getPacket();
        }

        public static MaplePacket messengerNote(String text, int mode, int mode2) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(mode);
            mplew.writeMapleAsciiString(text);
            mplew.write(mode2);
            return mplew.getPacket();
        }
        
        // CASH SHOP PACKETS -- Credits to WvsBeta and OdinMS.
        public static MaplePacket sendWishList(int characterid, boolean update) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            if (update) {
                mplew.write(0x20);
            } else {
                mplew.write(0x1E);
            }
            Connection con = DatabaseConnection.getConnection();
            int i = 10;

            try {
                PreparedStatement ps = con.prepareStatement("SELECT sn FROM wishlist WHERE charid = ? LIMIT 10");
                ps.setInt(1, characterid);
                ResultSet rs = ps.executeQuery();
                while (rs.next()) {
                    mplew.writeInt(rs.getInt("sn"));
                    i--;
                }
                rs.close();
                ps.close();
            } catch (SQLException se) {
                log.info("Error getting wishlist data:", se);
            }
            while (i > 0) {
                mplew.writeInt(0);
                i--;
            }
            return mplew.getPacket();
        }

        public static MaplePacket warpCS(MapleClient c) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            MapleCharacter chr = c.getPlayer();
            mplew.write(0x27);
            
            mplew.writeShort(-1); //flags
            mplew.writeInt(chr.getId());
            mplew.writeAsciiString(chr.getName(), 13);
            mplew.write(chr.getGender());
            mplew.write(chr.getSkinColor().getId());
            mplew.writeInt(chr.getFace());
            mplew.writeInt(chr.getHair());

            mplew.writeLong(0); // pet serial number

            mplew.write(chr.getLevel());
            mplew.writeShort(chr.getJob().getId());
            mplew.writeShort(chr.getStr());
            mplew.writeShort(chr.getDex());
            mplew.writeShort(chr.getInt());
            mplew.writeShort(chr.getLuk());
            mplew.writeShort(chr.getHp());
            mplew.writeShort(chr.getMaxHp());
            mplew.writeShort(chr.getMp());
            mplew.writeShort(chr.getMaxMp());
            mplew.writeShort(chr.getRemainingAp());
            mplew.writeShort(chr.getRemainingSp());
            mplew.writeInt(chr.getExp());
            mplew.writeShort(chr.getFame());

            mplew.writeInt(chr.getMapId());
            mplew.write(chr.getInitialSpawnpoint());

            mplew.writeLong(0); // who knows
            mplew.writeInt(13);
            mplew.writeInt(12);

            mplew.write(chr.getBuddylist().getCapacity());
            mplew.writeInt(chr.getMeso());

            MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
            Collection<IItem> equippedList = iv.list();
            Item[] equipped = new Item[17];
            Item[] equippedCash = new Item[17];
            for (IItem item : equippedList) {
                byte pos = item.getPosition();
                if (pos < 0) {
                    pos = (byte)Math.abs(pos);
                    if (pos > 100) {
                        equippedCash[(byte)(pos - 100)] = (Item)item;
                    } else {
                        equipped[(byte)pos] = (Item)item;
                    }
                }
                if (pos < 0) {
                    if (pos < -100) {
                        pos += 100;
                        pos = (byte)Math.abs(pos);
                        equippedCash[(byte)(pos - 100)] = (Item)item;
                    } else {
                        pos = (byte)Math.abs(pos);
                        equipped[(byte)pos] = (Item)item;
                    }
                }
            }
            for (Item item : equipped) {
                if (item != null) {
                    addItemInfo(mplew, item, false);
                }
            }
            mplew.write(0);
            for (Item item : equippedCash) {
                if (item != null) {
                    addItemInfo(mplew, item, false);
                }
            }
            mplew.write(0);
            for (byte i = 1; i < 6; i++) {
                mplew.write(100); // TODO: custom max slots for GMS-like cash shop purposes
                iv = chr.getInventory(MapleInventoryType.getByType((byte)i));
                for (IItem item : iv.list()) {
                    if (item != null && item.getPosition() > 0) {
                        addItemInfo(mplew, item, false);
                    }
                }
                mplew.write(0);
            }

            Map<ISkill, MapleCharacter.SkillEntry> skills = chr.getSkills();
            mplew.writeShort(skills.size());
            for (Entry<ISkill, MapleCharacter.SkillEntry> skill : skills.entrySet()) {
                mplew.writeInt(skill.getKey().getId());
                mplew.writeInt((byte)skill.getValue().skillevel);
            }

            mplew.writeShort(0); // Quests
            mplew.writeShort(0); // Mini Games
            mplew.writeShort(0); // Rings
            
            for (int i = 0; i < 5; i++) { // Teleport rocks isn't short, it's 4 ints T_T
                mplew.writeInt(910000000 + i);
            }

            mplew.write(1); // 0 = beta or someshit lol
            mplew.writeMapleAsciiString(c.getAccountName()); 
            
            mplew.writeShort(0); // wishlist i think.. o.o
            
            //int[] itemz = new int[]{10000281, 10000282, 10000283, 10000284, 10000285};
            for (byte i = 1; i <= 8; i++) {
                for (byte j = 0; j <= 1; j++) {
                        mplew.writeInt(10000281); // best items, these are just first id's in Commodity
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000282);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000283);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000284);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000285);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                }
            }
            
            mplew.writeShort(5); // Stock 
            mplew.writeInt(-1); // 1 = Sold Out, 2 = Not Sold      
            mplew.writeInt(20900028);
            mplew.writeInt(0); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900027);
            mplew.writeInt(2); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900026);
            mplew.writeInt(4); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900026);
            mplew.writeInt(5); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900026);
                    
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            
            return mplew.getPacket();
        }
        
        public static MaplePacket showBoughtCSItem(MapleCharacter chr, CashItemInfo item) {
            MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x22);
            
            mplew.writeLong(item.hashCode()); // Cash ID
            mplew.writeInt(chr.getId()); // Player ID
            mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01"));
            mplew.writeInt(item.getId());
            mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01"));
            mplew.writeShort(item.getCount()); // quantity is always 1?
            mplew.writeAsciiString(ii.getName(item.getId()), 13);
            mplew.writeLong(0); // Expiration.. items never expire so we shouldnt have a problem.
            mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01"));
            return mplew.getPacket();
        }
        
        public static MaplePacket sendItemInventory(CashItemInfo item, short slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x2F);
            
            mplew.writeShort(slot);
            mplew.write(2);//ServerConstants.getInventoryType(item.getId()).getType());
            Item i = new Item(2090000, (byte)slot, (short)1);
            addItemInfo(mplew, i, false, true);
            return mplew.getPacket();
        }
        
        public static MaplePacket showNXMapleTokens(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBA);
            mplew.writeInt(chr.getCSPoints(1)); // Paypal/PayByCash NX
            mplew.writeInt(chr.getCSPoints(2)); // Maple Points
            return mplew.getPacket();
        }
        
        // TODO, the players inventory of the items bought
        public static MaplePacket sendItems(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x1C);
            
            mplew.write(0);
            // for each byte 
            mplew.writeShort(10);

            return mplew.getPacket();
        }
        
        // TODO: Gifting
        public static MaplePacket showGifts(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x1E);
            mplew.writeAsciiString("fasfsa", 13);
            mplew.writeAsciiString("asfas", 73);
            return mplew.getPacket();
        }
        
        // END OF CASH SHOP PACKETS
        
        public static MaplePacket getStorage(int npcId, byte slots, Collection<IItem> items, int meso) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.OPEN_STORAGE.getValue());
		mplew.writeInt(npcId);
		mplew.write(slots);
		mplew.writeShort(0x7E);
		mplew.writeInt(meso);
                byte size1 = 0, size2 = 0, size3 = 0, size4 = 0, size5 = 0;
                for (IItem item : items) {
                    switch(item.getItemId() / 1000000) {
                        case 1: size1++; break;
                        case 2: size2++; break;
                        case 3: size3++; break;
                        case 4: size4++; break;
                        case 5: size5++; break;
                        default: System.out.println("Unknown type found!"); break;
                    }
                }
                mplew.write(size1);
		for (IItem item : items) { // Equip
                    if (item.getItemId() / 1000000 == 1) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
		mplew.write(size2); 
		for (IItem item : items) { // Use
                    if (item.getItemId() / 1000000 == 2) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size3); // Setup
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 3) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size4); // Etc
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 4) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size5); // Pets
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 5) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket getStorageFull() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1));
		mplew.write(0x0C);
		return mplew.getPacket();
	}

	public static MaplePacket mesoStorage(byte slots, int meso) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1)); // 0xA8
		mplew.write(0x0E);
		mplew.write(slots);
		mplew.writeShort(2);
		mplew.writeInt(meso);
		return mplew.getPacket();
	}

	public static MaplePacket storeStorage(byte slots, MapleInventoryType type, Collection<IItem> items) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1));
		mplew.write(0x09);
		mplew.write(slots);
		mplew.writeShort(type.getBitfieldEncoding());
		byte size1 = 0, size2 = 0, size3 = 0, size4 = 0, size5 = 0;
                for (IItem item : items) {
                    switch(item.getItemId() / 1000000) {
                        case 1: size1++; break;
                        case 2: size2++; break;
                        case 3: size3++; break;
                        case 4: size4++; break;
                        case 5: size5++; break;
                        default: System.out.println("Unknown type found!"); break;
                    }
                }
                mplew.write(size1);
		for (IItem item : items) { // Equip
                    if (item.getItemId() / 1000000 == 1) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
		mplew.write(size2); 
		for (IItem item : items) { // Use
                    if (item.getItemId() / 1000000 == 2) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size3); // Setup
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 3) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size4); // Etc
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 4) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size5); // Pets
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 5) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket takeOutStorage(byte slots, MapleInventoryType type, Collection<IItem> items) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1));
		mplew.write(0x07);
		mplew.write(slots);
		mplew.writeShort(type.getBitfieldEncoding());
		byte size1 = 0, size2 = 0, size3 = 0, size4 = 0, size5 = 0;
                for (IItem item : items) {
                    switch(item.getItemId() / 1000000) {
                        case 1: size1++; break;
                        case 2: size2++; break;
                        case 3: size3++; break;
                        case 4: size4++; break;
                        case 5: size5++; break;
                        default: System.out.println("Unknown type found!"); break;
                    }
                }
                mplew.write(size1);
		for (IItem item : items) { // Equip
                    if (item.getItemId() / 1000000 == 1) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
		mplew.write(size2); 
		for (IItem item : items) { // Use
                    if (item.getItemId() / 1000000 == 2) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size3); // Setup
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 3) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size4); // Etc
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 4) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size5); // Pets
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 5) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.writeLong(0);
		return mplew.getPacket();
	}
        
        public static MaplePacket spawnMist(int oid, int ownerCid, int skillId, Rectangle mistPosition) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_MIST.getValue());
		mplew.writeInt(oid); // maybe this should actually be the "mistid" - seems to always be 1 with only one mist in the map...
		mplew.write(0); // from mob? o.O
		mplew.writeInt(skillId);
		mplew.write(1); // skill level
		mplew.writeShort(7); // delay i guess
		mplew.writeInt(mistPosition.x); // left position
		mplew.writeInt(mistPosition.y); // bottom position
		mplew.writeInt(mistPosition.x + mistPosition.width); // left position
		mplew.writeInt(mistPosition.y + mistPosition.height); // upper position
		return mplew.getPacket();
	}

	public static MaplePacket removeMist(int oid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_MIST.getValue());
		mplew.writeInt(oid);
		return mplew.getPacket();
	}
        
        // is there a way to spawn reactors non-animated?
	public static MaplePacket spawnReactor(MapleReactor reactor) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		Point pos = reactor.getPosition();
		mplew.write(SendPacketOpcode.REACTOR_SPAWN.getValue());
		mplew.writeShort(reactor.getObjectId());
                mplew.write(reactor.getState());
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		mplew.write(2); // Rx0?
                mplew.write(2); // Rx1?
		return mplew.getPacket();
	}
	
	public static MaplePacket triggerReactor(MapleReactor reactor, int stance) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		Point pos = reactor.getPosition();
		mplew.write(SendPacketOpcode.REACTOR_HIT.getValue());
		mplew.writeShort(reactor.getObjectId());
		mplew.write(reactor.getState());
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		mplew.writeShort(stance);
		mplew.write(0);
		//frame delay, set to 5 since there doesn't appear to be a fixed formula for it
		mplew.write(5);
		return mplew.getPacket();
	}
	
	public static MaplePacket destroyReactor(MapleReactor reactor) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REACTOR_DESTROY.getValue());
		mplew.writeShort(reactor.getObjectId());
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet to spawn a portal.
	 * 
	 *  [USER=2000183830]para[/USER]m townId The ID of the town the portal goes to.
	 *  [USER=2000183830]para[/USER]m targetId The ID of the target.
	 *  [USER=2000183830]para[/USER]m pos Where to put the portal.
	 *  [USER=850422]return[/USER] The portal spawn packet.
	 */
	public static MaplePacket spawnPortal(int townId, int targetId, Point pos) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_PORTAL.getValue());
		mplew.writeInt(townId);
		mplew.writeInt(targetId);
		if (pos != null) {
                    mplew.writeShort(pos.x);
                    mplew.writeShort(pos.y);
		}
		return mplew.getPacket();
	}

	/**
	 * Gets a packet to spawn a door.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The door's object ID.
	 *  [USER=2000183830]para[/USER]m pos The position of the door.
	 *  [USER=2000183830]para[/USER]m town
	 *  [USER=850422]return[/USER] The remove door packet.
	 */
	public static MaplePacket spawnDoor(int oid, Point pos, boolean town) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_DOOR.getValue());
		mplew.write(town ? 1 : 0);
		mplew.writeInt(oid);//?
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet to remove a door.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The door's ID.
	 *  [USER=2000183830]para[/USER]m town
	 *  [USER=850422]return[/USER] The remove door packet.
	 */
	public static MaplePacket removeDoor(int oid, boolean town) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		if (town) {
			mplew.write(SendPacketOpcode.SPAWN_PORTAL.getValue());
			mplew.writeInt(999999999);
			mplew.writeInt(999999999);
		} else {
			mplew.write(SendPacketOpcode.REMOVE_DOOR.getValue());
			mplew.write(1); // or is it 0? lol
			mplew.writeInt(oid);
		}
		return mplew.getPacket();
	}
        
        /**
         * 
         *  [USER=2000183830]para[/USER]m oid object id of boss
         *  [USER=2000183830]para[/USER]m currHP current hp of boss
         *  [USER=2000183830]para[/USER]m maxHP max hp of boss
         *  [USER=2000183830]para[/USER]m tagColor the color of the hpbar
         *  [USER=2000183830]para[/USER]m tagBgColor the color of the background for the hpbar
         *  [USER=850422]return[/USER] 
         */
	public static MaplePacket showBossHP(int oid, int currHP, int maxHP, byte tagColor, byte tagBgColor) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BOSS_ENV.getValue());
		mplew.write(5);
		mplew.writeInt(currHP);
		mplew.writeInt(maxHP);
		mplew.writeInt(tagColor);
		mplew.writeInt(tagBgColor);
		return mplew.getPacket();
	}
        
        public static MaplePacket updatePet(MaplePet pet, boolean alive) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
            mplew.write(0);
            mplew.write(2);
            mplew.write(3);
            mplew.write(5);
            mplew.write(pet.getPosition());
            mplew.writeShort(0);
            mplew.write(5);
            mplew.write(pet.getPosition());
            mplew.write(0);
            mplew.writeInt(pet.getItemId());
            mplew.write(1);
            mplew.writeLong(pet.getUniqueId());
            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            String petname = pet.getName();
            mplew.writeAsciiString(petname, 13);
            mplew.write(pet.getLevel());
            mplew.writeShort(pet.getCloseness());
            mplew.write(pet.getFullness());
            if (alive) {
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
                mplew.writeInt(0);
            } else {
                mplew.writeLong(0);
            }
            return mplew.getPacket();
        }

        public static MaplePacket showPet(MapleCharacter chr, MaplePet pet, boolean remove) {
            return showPet(chr, pet, remove, false);
        }

        public static MaplePacket showPet(MapleCharacter chr, MaplePet pet, boolean remove, boolean hunger) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SPAWN_PET.getValue());
            mplew.writeInt(chr.getId());
            if (remove) {
                mplew.write(0);
                mplew.write(hunger ? 1 : 0);
            } else {
                mplew.write(1);
                mplew.writeInt(pet.getItemId());
                mplew.writeMapleAsciiString(pet.getName());
                mplew.writeLong(pet.getUniqueId());
                mplew.writeShort(pet.getPos().x);
                mplew.writeShort(pet.getPos().y - 12);
                mplew.write(pet.getStance());
                mplew.writeInt(pet.getFh());
            }
            return mplew.getPacket();
        }

        public static MaplePacket movePet(int cid, SeekableLittleEndianAccessor slea) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MOVE_PET.getValue());
            mplew.writeInt(cid);
            mplew.write(slea.read((int)slea.available()));
            return mplew.getPacket();
        }

        public static MaplePacket petChat(int cid, int un, String text, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PET_CHAT.getValue());
            mplew.writeInt(cid);
            mplew.write(slot);
            mplew.write(un);
            mplew.writeMapleAsciiString(text);
            return mplew.getPacket();
        }

        public static MaplePacket commandResponse(int cid, byte command, boolean success, boolean food) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PET_COMMAND.getValue());
            mplew.writeInt(cid);
            mplew.write(command == 1 ? 1 : 0);
            mplew.write(command);
            mplew.write(success ? 1 : 0);
            return mplew.getPacket();
        }

        public static MaplePacket showOwnPetLevelUp(int index) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SHOW_ITEM_GAIN_INCHAT.getValue());
            mplew.write(4);
            mplew.write(0);
            mplew.write(index); // Pet Index? o.O
            return mplew.getPacket();
        }

        public static MaplePacket showPetLevelUp(MapleCharacter chr, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SHOW_FOREIGN_EFFECT.getValue());
            mplew.write(4);
            mplew.write(0);
            mplew.write(slot);
            return mplew.getPacket();
        }

        public static MaplePacket changePetName(MapleCharacter chr, String newname, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PET_NAMECHANGE.getValue());
            mplew.writeInt(chr.getId());
            mplew.writeMapleAsciiString(newname);
            mplew.write(slot); // Pet index o.O
            return mplew.getPacket();
        }

        public static MaplePacket petStatUpdate(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.UPDATE_STATS.getValue());
            int mask = 0;
            mask |= MapleStat.PET.getValue();
            mplew.write(1);
            mplew.writeInt(mask);
            MaplePet pet = chr.getPet(0);
            if (pet != null) {
                mplew.writeInt(pet.getUniqueId());
                mplew.writeInt(0);
            } else {
                mplew.writeLong(0);
            }
            mplew.write(1);
            return mplew.getPacket();
        }
        
        /**
	 * state 0 = del ok state 12 = invalid bday
	 * 
	 *  [USER=2000183830]para[/USER]m cid
	 *  [USER=2000183830]para[/USER]m state
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket deleteCharResponse(int cid, int state) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DELETE_CHAR_RESPONSE.getValue());
		mplew.writeInt(cid);
		mplew.write(state);
		return mplew.getPacket();
	}
        
        public static MaplePacket refreshVIPRockMapList(int[] maps, byte type) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(0x1C);
                mplew.write(type);
                for (int map : maps) {
                    mplew.writeInt(map);
                }
                return mplew.getPacket();
        }
        
        public static MaplePacket showNotes(ResultSet notes, int count) throws SQLException {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x1B); // OnMemoResult
            mplew.write(1);
            mplew.write(count);
            for (int i = 0; i < count; i++) {
                mplew.writeInt(notes.getInt("id"));
                mplew.writeMapleAsciiString(notes.getString("from"));
                mplew.writeMapleAsciiString(notes.getString("message"));
                mplew.writeLong(getKoreanTimestamp(notes.getLong("timestamp")));
                mplew.write(0);
                notes.next();
            }
            return mplew.getPacket();
        }
        
        /**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m shop
	 *  [USER=2000183830]para[/USER]m owner
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket getPlayerShop(MapleClient c, MaplePlayerShop shop, boolean owner) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("05 04 04"));
		mplew.write(owner ? 0 : 1);
		mplew.write(0);
		addCharLook(mplew, shop.getOwner(), true);
		mplew.writeMapleAsciiString(shop.getOwner().getName());
		MapleCharacter[] visitors = shop.getVisitors();
		for (int i = 0; i < visitors.length; i++) {
                    if (visitors[i] != null) {
                        mplew.write(i + 1);
                        addCharLook(mplew, visitors[i], true);
                        mplew.writeMapleAsciiString(visitors[i].getName());
                    }
		}
		mplew.write(0xFF);
		mplew.writeMapleAsciiString(shop.getDescription());
		List<MaplePlayerShopItem> items = shop.getItems();
		mplew.write(0x10);
		mplew.write(items.size());
		for (MaplePlayerShopItem item : items) {
                    mplew.writeShort(item.getBundles());
                    mplew.writeShort(item.getItem().getQuantity());
                    mplew.writeInt(item.getPrice());
                    addItemInfo(mplew, item.getItem(), true);
		}
		return mplew.getPacket();
	}

	public static MaplePacket getTradeStart(MapleClient c, MapleTrade trade, byte number) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("05 03 02"));
		mplew.write(number);
		if (number == 1) {
                    mplew.write(0);
                    addCharLook(mplew, trade.getPartner().getChr(), true);
                    mplew.writeMapleAsciiString(trade.getPartner().getChr().getName());
		}
		mplew.write(number);
		addCharLook(mplew, c.getPlayer(), true);
		mplew.writeMapleAsciiString(c.getPlayer().getName());
		mplew.write(0xFF);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeConfirmation() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xF);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeCompletion(byte number, boolean success) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xA);
		mplew.write(number);
		mplew.write(success ? 5 : 6);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeCancel(byte number) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xA);
		mplew.write(number);
		mplew.write(2);
		return mplew.getPacket();
	}
        
        public static MaplePacket getTradePartnerAdd(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("04 01"));
		addCharLook(mplew, c, true);
		mplew.writeMapleAsciiString(c.getName());
		return mplew.getPacket();
	}

	public static MaplePacket getTradeInvite(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("02 03"));
		mplew.writeMapleAsciiString(c.getName());
		mplew.write(HexTool.getByteArrayFromHexString("B7 50 00 00")); // the room id, int
		return mplew.getPacket();
	}

	public static MaplePacket getTradeMesoSet(byte number, int meso) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xE);
		mplew.write(number);
		mplew.writeInt(meso);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeItemAdd(byte number, IItem item) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xD);
                mplew.write(number);
                mplew.write(item.getPosition());
                mplew.write(item.getType()); // 1 equip, 2 use(?), 5 pet
		addItemInfo(mplew, item, false, true);
		return mplew.getPacket();
	}
        
        public static MaplePacket sendInteractionBox(MapleCharacter c) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.UPDATE_CHAR_BOX.getValue()); // 0x40
                mplew.writeInt(c.getId());
                addAnnounceBox(mplew, c.getInteraction());
                return mplew.getPacket();
        }

        public static MaplePacket removeCharBox(MapleCharacter c) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.UPDATE_CHAR_BOX.getValue());
                mplew.writeInt(c.getId());
                mplew.writeInt(0);
                return mplew.getPacket();
        }

	public static MaplePacket getPlayerShopChat(MapleCharacter c, String chat, boolean owner) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("06 08"));
		mplew.write(owner ? 0 : 1);
		mplew.writeMapleAsciiString(c.getName() + " : " + chat);
		return mplew.getPacket();
	}
        
        public static MaplePacket itemEffect(int characterid, int itemid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_ITEM_EFFECT.getValue());
		mplew.writeInt(characterid);
		mplew.writeInt(itemid);
		return mplew.getPacket();
	}
        
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////

	public static MaplePacket getPermBan(byte reason) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.LOGIN_STATUS.getValue());
		mplew.writeShort(0x02); // Account is banned
		mplew.write(0x0);
		mplew.write(reason);
		mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01 00"));
		return mplew.getPacket();
	}

	public static MaplePacket getTempBan(long timestampTill, byte reason) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.LOGIN_STATUS.getValue());
		mplew.write(0x02);
		mplew.write(HexTool.getByteArrayFromHexString("00 00 00 00 00")); // Account is banned
		mplew.write(reason);
		mplew.writeLong(timestampTill); // Tempban date is handled as a 64-bit long, number of 100NS intervals since 1/1/1601. Lulz.
		return mplew.getPacket();
	}

	/**
	 * Adds a quest info entry for a character to an existing
	 * MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWrite instance to write the stats
	 *            to.
	 *  [USER=2000183830]para[/USER]m chr The character to add quest info about.
	 */
	private static void addQuestInfo(MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
		List<MapleQuestStatus> started = chr.getStartedQuests();
		mplew.writeShort(started.size());
		for (MapleQuestStatus q : started) {
			mplew.writeInt(q.getQuest().getId());
		}
		List<MapleQuestStatus> completed = chr.getCompletedQuests();
		mplew.writeShort(completed.size());
		for (MapleQuestStatus q : completed) {
			mplew.writeShort(q.getQuest().getId());
			mplew.writeInt(KoreanDateUtil.getQuestTimestamp(q.getCompletionTime()));
			mplew.writeInt(KoreanDateUtil.getQuestTimestamp(q.getCompletionTime()));
		}
	}

	/**
	 * Adds expiration time info to an existing MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to write to.
	 *  [USER=2000183830]para[/USER]m time The expiration time.
	 *  [USER=2000183830]para[/USER]m showexpirationtime Show the expiration time?
	 */
	private static void addExpirationTime(MaplePacketLittleEndianWriter mplew, long time, boolean showexpirationtime) {
		mplew.writeInt(KoreanDateUtil.getItemTimestamp(time));
		mplew.write(showexpirationtime ? 1 : 2);
	}
        
        public static MaplePacket scrolledItem(IItem scroll, IItem item, boolean destroyed) {
		// 18 00 01 02 03 02 08 00 03 01 F7 FF 01
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(1); // fromdrop always true
		if (destroyed) {
                    mplew.write(2);
		} else {
                    mplew.write(3); // is this 1? xD
		}
		if (scroll.getQuantity() > 0) {
                    mplew.write(1);
		} else {
                    mplew.write(3);
		}
		mplew.write(MapleInventoryType.USE.getType());
		mplew.writeShort(scroll.getPosition());
		if (scroll.getQuantity() > 0) {
                    mplew.writeShort(scroll.getQuantity());
		}
		mplew.write(3);
		if (!destroyed) {
                    mplew.write(MapleInventoryType.EQUIP.getType());
                    mplew.writeShort(item.getPosition());
                    mplew.write(0);
		}
		mplew.write(MapleInventoryType.EQUIP.getType());
		mplew.writeShort(item.getPosition());
		if (!destroyed) {
                    addItemInfo(mplew, item, true);
		}
		mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket getScrollEffect(ScrollResult scrollSuccess) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(6);
		switch (scrollSuccess) {
                    case SUCCESS:
                        mplew.write(2); // 1: Nothing | 2: Nothing? wtf..
                        break;
                    case FAIL:
                        mplew.write(0);
                        break;
                    case CURSE:
                        mplew.write(1);
                        break;
                    default:
                        throw new IllegalArgumentException("effects don't even work in v40 beta and we found a new one? wut");
		}
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket startQuest(MapleCharacter c, short quest) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(quest);
		mplew.writeShort(1);
		mplew.write(0);
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket forfeitQuest(MapleCharacter c, short quest) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_QUEST_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(quest);
		mplew.writeShort(0);
		mplew.write(0);
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket completeQuest(MapleCharacter c, short quest) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_QUEST_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(quest);
		mplew.write(HexTool.getByteArrayFromHexString("02 A0 67 B9 DA 69 3A C8 01"));
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=2000183830]para[/USER]m npc
	 *  [USER=2000183830]para[/USER]m progress
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket updateQuestInfo(MapleCharacter c, short quest, int npc, byte progress) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(0x72); 
		mplew.write(progress);
		mplew.writeShort(quest);
		mplew.writeInt(npc);
		mplew.writeInt(0);
		return mplew.getPacket();
	}
        
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////

	/**
	 * Adds a announcement box to an existing MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to add an announcement box
	 *            to.
	 *  [USER=2000183830]para[/USER]m shop The shop to announce.
	 */
	private static void addAnnounceBox(MaplePacketLittleEndianWriter mplew, IPlayerInteractionManager interaction) {
                mplew.write(interaction.getShopType());
                if (interaction.getShopType() == 4) {
                    mplew.writeInt(((MaplePlayerShop) interaction).getObjectId());
                } else {
                    mplew.writeInt(((MapleMiniGame) interaction).getObjectId());
                }
                mplew.writeMapleAsciiString(interaction.getDescription()); // desc
                mplew.write(0); // TODO: passwords
                mplew.write(interaction.getShopType() == 2 ? ((MapleMiniGame) interaction).getMatchesToWin() == 6 ? 2 : ((MapleMiniGame) interaction).getMatchesToWin() == 10 ? 1 : 0 : 0);
                mplew.write(1);
                mplew.write(interaction.getFreeSlot() > -1 ? 4 : 1);
                if (interaction.getShopType() == 4) {
                    mplew.write(0);
                } else {
                    mplew.write(((MapleMiniGame) interaction).getStarted() ? 1 : 0);
                }
        }
        
        private static void addShopBox(MaplePacketLittleEndianWriter mplew, MaplePlayerShop shop) {
		// 00: no game
		// 01: omok game
		// 02: card game
		// 04: shop
		mplew.write(4);
		mplew.writeInt(shop.getObjectId()); // gameid/shopid
		mplew.writeMapleAsciiString(shop.getDescription()); // desc
		// 00: public
		// 01: private
		mplew.write(0);
		// 00: red 4x3
		// 01: green 5x4
		// 02: blue 6x5
		// omok:
		// 00: normal
		mplew.write(0);
		// first slot: 1/2/3/4
		// second slot: 1/2/3/4
		mplew.write(1);
		mplew.write(4);
		// 0: open
		// 1: in progress
		mplew.write(0);
	}

	public static MaplePacket getPlayerShopNewVisitor(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("04 02"));
		addCharLook(mplew, c, true);
		mplew.writeMapleAsciiString(c.getName());
		return mplew.getPacket();
	}

	public static MaplePacket getPlayerShopItemUpdate(MaplePlayerShop shop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0x15);
		mplew.write(shop.getItems().size());
		for (MaplePlayerShopItem item : shop.getItems()) {
			mplew.writeShort(item.getBundles());
			mplew.writeShort(item.getItem().getQuantity());
			mplew.writeInt(item.getPrice());
                        mplew.write(item.getItem().getType());
                        addItemInfo(mplew, item.getItem(), false, true);
		}
		return mplew.getPacket();
	}

	public static MaplePacket updateCharBox(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.UPDATE_CHAR_BOX.getValue());
		mplew.writeInt(c.getId());
		if (c.getPlayerShop() != null) {
                    addShopBox(mplew, c.getPlayerShop());
		} else {
                    mplew.writeInt(0);
		}
		return mplew.getPacket();
	}
        
        public static MaplePacket shopItemUpdate(IPlayerInteractionManager shop) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();

            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x17);
            if (shop.getShopType() == 1) {
                mplew.writeInt(0);
            }
            mplew.write(shop.getItems().size());
            for (net.sf.odinms.server.PlayerInteraction.MaplePlayerShopItem item : shop.getItems()) { // LULUL
                mplew.writeShort(item.getBundles());
                mplew.writeShort(item.getItem().getQuantity());
                mplew.writeInt(item.getPrice());
                addItemInfo(mplew, item.getItem(), true, true);
            }
            return mplew.getPacket();
        }
        
        public static MaplePacket addVisitor(MapleCharacter chr, boolean firstTime) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue()); 
            mplew.write(4);
            mplew.write(1);
            addCharLook(mplew, chr, true);
            mplew.writeMapleAsciiString(chr.getName());
            
            mplew.writeInt(1); // TODO: get stats
            mplew.writeInt(1);
            mplew.writeInt(1);
            mplew.writeInt(1);
            mplew.writeInt(2000);
            
            return mplew.getPacket();
        }
        
        public static MaplePacket getInteraction(MapleCharacter chr, boolean firstTime) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue()); // header.
            IPlayerInteractionManager ips = chr.getInteraction();
            int type = ips.getShopType();
            if (type == 1) {
                mplew.write(HexTool.getByteArrayFromHexString("05 01 04"));
            } else if (type == 2) {
                mplew.write(HexTool.getByteArrayFromHexString("05 02 02"));
            } else if (type == 4) {
                mplew.write(HexTool.getByteArrayFromHexString("05 04 04")); // might be wrong
            }
            mplew.write(ips.isOwner(chr) ? 0 : 1);
            mplew.write(0);
            if (type == 1 || type == 2) {
                addCharLook(mplew, ((MapleMiniGame) ips).getOwner(), true);
                mplew.writeMapleAsciiString(ips.getOwnerName());
            } else {
                addCharLook(mplew, ((net.sf.odinms.server.PlayerInteraction.MaplePlayerShop) ips).getMCOwner(), false);
                mplew.writeMapleAsciiString(ips.getOwnerName());
            }
            for (int i = 0; i < 3; i++) {
                if (ips.getVisitors()[i] != null) {
                    mplew.write(i + 1);
                    addCharLook(mplew, ips.getVisitors()[i], true);
                    mplew.writeMapleAsciiString(ips.getVisitors()[i].getName());
                }
            }
            mplew.write(0xFF);
            if (type == 1 || type == 2) {
                MapleMiniGame minigame = (MapleMiniGame) ips;
                mplew.write(0);
                if (type == 1 || type == 2) {
                    mplew.writeInt(1);
                } else {
                    mplew.writeInt(2);
                }
                mplew.writeInt(minigame.getOmokPoints("wins", true));
                mplew.writeInt(minigame.getOmokPoints("ties", true));
                mplew.writeInt(minigame.getOmokPoints("losses", true));
                mplew.writeInt(2000);
                if (ips.getVisitors()[0] != null) {
                    mplew.write(1);
                    if (type == 1 || type == 2) {
                        mplew.writeInt(1);
                    } else {
                        mplew.writeInt(2);
                    }
                    mplew.writeInt(minigame.getOmokPoints("wins", false));
                    mplew.writeInt(minigame.getOmokPoints("ties", false));
                    mplew.writeInt(minigame.getOmokPoints("losses", false));
                    mplew.writeInt(2000);
                }
                mplew.write(0xFF);
            }
            mplew.writeMapleAsciiString(ips.getDescription());
            if (type == 1 || type == 2) {
                mplew.write(ips.getItemType());
                mplew.write(0);
                mplew.write(0);
            }
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGame(MapleCharacter chr, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            MapleMiniGame game = (MapleMiniGame) chr.getInteraction();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(5); 
            mplew.write(chr.getInteraction().getShopType()); 
            mplew.write(4); // max slots is actually 4 in v40 but ya
            mplew.write(slot);
            if (slot == 0) {
                mplew.write(0);
                addCharLook(mplew, chr, true);
                mplew.writeMapleAsciiString(chr.getName());
            } else {
                MapleCharacter owner = game.getOwner();
                mplew.write(0);
                addCharLook(mplew, owner, true);
                mplew.writeMapleAsciiString(owner.getName());
                mplew.write(1);
                addCharLook(mplew, chr, true);
                mplew.writeMapleAsciiString(chr.getName());
            }
            mplew.write(0xFF);
            
            mplew.write(0); // Slot 0 -- Owner
            mplew.writeInt(game.getShopType()); // Game Type (1 = Omok)
            mplew.writeInt(game.getOmokPoints("wins", true)); // Wins
            mplew.writeInt(game.getOmokPoints("ties", true)); // Ties
            mplew.writeInt(game.getOmokPoints("losses", true)); // Losses
            mplew.writeInt(2000); // Score

            if (slot >= 1) {
                mplew.write(1); // Slot 1 -- Opponent
                mplew.writeInt(game.getShopType()); // Game Type (1 = Omok)
                mplew.writeInt(game.getOmokPoints("wins", false)); // Wins
                mplew.writeInt(game.getOmokPoints("ties", false)); // Ties
                mplew.writeInt(game.getOmokPoints("losses", false)); // Losses
                mplew.writeInt(2000); // Score
            }
            mplew.write(0xFF);
            
            mplew.writeMapleAsciiString(game.getDescription());
            mplew.write(0); // Piece type
            
            mplew.write(0);
            mplew.write(0);
            return mplew.getPacket();
        }
        
        public static MaplePacket shopChat(String message, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("06 08"));
            mplew.write(slot);
            mplew.writeMapleAsciiString(message);
            return mplew.getPacket();
        }

        public static MaplePacket shopErrorMessage(int error, int type) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x0A);
            mplew.write(type);
            mplew.write(error);
            return mplew.getPacket();
        }
        
        public static MaplePacket shopVisitorLeave(int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x0A);
            if (slot > 0) { // owner != visitor
                mplew.write(slot);
            }
            return mplew.getPacket();
        }
        
        public static MaplePacket getMiniBoxFull() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(3);
            mplew.write(2);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameStart(int loser) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x23);
            mplew.write(loser == 0 ? 1 : 0);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameSkipTurn(int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x39);
            mplew.write(slot);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameReady() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x20);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameUnReady() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x21);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameRequestTie() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x18);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameDenyTie() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x19);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameMoveOmok(int move1, int move2, int move3) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x26);
            mplew.writeInt(move1);
            mplew.writeInt(move2);
            mplew.write(move3);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameWin(MapleMiniGame game, int person) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("38 00"));
            mplew.write(person);
            mplew.writeInt(1); // start of owner; unknown
            mplew.writeInt(game.getOmokPoints("wins", true)); // wins
            mplew.writeInt(game.getOmokPoints("ties", true)); // ties
            mplew.writeInt(game.getOmokPoints("losses", true) + 1); // losses
            mplew.writeInt(2000); // points
            mplew.writeInt(1); // start of visitor; unknown
            mplew.writeInt(game.getOmokPoints("wins", false) + 1); // wins
            mplew.writeInt(game.getOmokPoints("ties", false)); // ties
            mplew.writeInt(game.getOmokPoints("losses", false)); // losses
            mplew.writeInt(2000); // points
            game.setOmokPoints(person + 1);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameTie(MapleMiniGame game) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("38 01"));
            mplew.writeInt(1); // unknown
            mplew.writeInt(game.getOmokPoints("wins", true)); // wins
            mplew.writeInt(game.getOmokPoints("ties", true) + 1); // ties
            mplew.writeInt(game.getOmokPoints("losses", true)); // losses
            mplew.writeInt(2000); // points
            mplew.writeInt(1); // start of visitor; unknown
            mplew.writeInt(game.getOmokPoints("wins", false)); // wins
            mplew.writeInt(game.getOmokPoints("ties", false) + 1); // ties
            mplew.writeInt(game.getOmokPoints("losses", false)); // losses
            mplew.writeInt(2000); // points
            game.setMatchCardPoints(3);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameForfeit(MapleMiniGame game, int person) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("38 02"));
            mplew.write(person);
            mplew.writeInt(1); // start of owner; unknown
            mplew.writeInt(game.getOmokPoints("wins", true)); // wins
            mplew.writeInt(game.getOmokPoints("ties", true)); // ties
            mplew.writeInt(game.getOmokPoints("losses", true) + 1); // losses
            mplew.writeInt(2000); // points
            mplew.writeInt(1); // start of visitor; unknown
            mplew.writeInt(game.getOmokPoints("wins", false) + 1); // wins
            mplew.writeInt(game.getOmokPoints("ties", false)); // ties
            mplew.writeInt(game.getOmokPoints("losses", false)); // losses
            mplew.writeInt(2000); // points
            game.setOmokPoints(person + 1);
            return mplew.getPacket();
        }

        public static MaplePacket getMatchCardStart(MapleMiniGame game) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x23);
            mplew.write(game.getLoser() == 1 ? 0 : 1);
            int times;
            if (game.getMatchesToWin() > 10) {
                times = 30;
            } else if (game.getMatchesToWin() > 6) {
                times = 20;
            } else {
                times = 12;
            }
            mplew.write(times);
            for (int i = 1; i <= times; i++) {
                mplew.writeInt(game.getCardId(i));
            }
            return mplew.getPacket();
        }

        public static MaplePacket getMatchCardSelect(int turn, int slot, int firstslot, int type) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x3E);
            mplew.write(turn);
            if (turn == 1) {
                mplew.write(slot);
            } else if (turn == 0) {
                mplew.write(slot);
                mplew.write(firstslot);
                mplew.write(type);
            }
            return mplew.getPacket();
        }
        
        // Maple Events
        
        public static MaplePacket leftKnockBack() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9D); // LEFT_KNOCK_BACK
            return mplew.getPacket();
        }
        
        public static MaplePacket rollSnowball(int type, int rollDistance0, int rollDistance1, int startPoint0, int startPoint1) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9A); // ROLL_SNOWBALL
            mplew.write(type); // 0 = normal, 1 = rolls from start to end, 2 = btm invis, 3 = top invis
            if (type == 2) {
                MapleSnowball.isInvis0 = true;
            } else if (type == 3) {
                MapleSnowball.isInvis1 = true;
            }
            mplew.write(rollDistance0); // bottom snowball
            mplew.write(startPoint0); // stage
            mplew.write(0);
            mplew.write(rollDistance1); // top snowball
            mplew.write(startPoint1); // stage
            mplew.writeLong(0);
            return mplew.getPacket(); 
        }

        public static MaplePacket hitSnowBall(int team, int damage) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9B); // HIT_SNOWBALL
            mplew.write(team);// 0 is down, 1 is up
            mplew.writeInt(damage); // fixedDamage = 10
            return mplew.getPacket();
        }

        public static MaplePacket snowballMessage(int team, int message) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9C); // SNOWBALL_MESSAGE
            mplew.write(team);// 0 is down, 1 is up
            mplew.writeInt(message);
            return mplew.getPacket();
        }
        
        public static MaplePacket showOXQuiz(int questionSet, int questionId, boolean askQuestion) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x34); // OX_QUIZ
            mplew.write(askQuestion ? 1 : 0);
            mplew.write(questionSet);
            mplew.writeInt(questionId);
            return mplew.getPacket();
        }
        
        public static MaplePacket getEventInstructions() { // same as v117.2
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x35); // SHOW_EVENT_INSTRUCTIONS
            mplew.write(0);
            return mplew.getPacket();
        }
        
        public static MaplePacket showForcedEquip(byte team) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x2C); // FORCED_MAP_EQUIP
            if (team > -1) {
                mplew.writeShort(team); // 0x00 = red, 0x01 = blue
            }
            return mplew.getPacket();
        }
        
        public static MaplePacket coconutScore(int team1, int team2) {
           MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
           mplew.write(0x9D); // COCONUT_SCORE
           mplew.writeShort(team1);
           mplew.writeShort(team2);
           return mplew.getPacket();
       }

       public static MaplePacket hitCoconut(boolean spawn, int id, int type) {
           MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
           mplew.write(0x9C); // HIT_COCONUT
           if (spawn) {
                mplew.write(HexTool.getByteArrayFromHexString("00 80 00 00 00"));
            } else {
                mplew.writeInt(id);
                mplew.write(type); // What action to do for the coconut.
            }
            return mplew.getPacket();
        }
        
        // TODO: Fix inv full packet and remove mob hp packet
        
	public static MaplePacket getShowInventoryFull() { // TODO: fix if inventory is full, we haven't tested this yet.
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(0);
		mplew.write(0xFF);
		mplew.writeLong(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m oid
	 *  [USER=2000183830]para[/USER]m remhp in %
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket showMonsterHP(int oid, int remhppercentage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.SHOW_MONSTER_HP.getValue()); // not in v40b. TODO: remove (;
		mplew.writeInt(oid);
		mplew.write(remhppercentage);
		return mplew.getPacket();
	}
}
[/STRIKE]

EDIT 3: Fixed. Found the missing table 'trocklocations' by looking in MapleCharacter.java.
 
Last edited:
Junior Spellweaver
Joined
Oct 30, 2010
Messages
148
Reaction score
8
Re: Database error?

What source are you using? The error is with RMI

Ohhh. I'm using the v40b Source that Eric released and now that you say it, I think I read that RMI had to be removed somewhere. What does that mean and is it possible to get it running?
 
Upvote 0
Junior Spellweaver
Joined
Jun 29, 2009
Messages
139
Reaction score
12
Re: Database error?

Hmm I see. I've never worked with that source, and although I know what RMI is, I've never used it personally. The purpose of RMI is to execute methods cross java process. To remove it you would need to get the entire server running in one Java instance.
 
Upvote 0
Junior Spellweaver
Joined
Oct 30, 2010
Messages
148
Reaction score
8
Re: Database error?

Hmm I see. I've never worked with that source, and although I know what RMI is, I've never used it personally. The purpose of RMI is to execute methods cross java process. To remove it you would need to get the entire server running in one Java instance.

You can see the release thread here:

http://forum.ragezone.com/f427/development-v40-beta-edition-source-1043426/

The to-do list says "- Remove RMI and clean out a lot of v55 useless clutter" so I guess it's something that was planned. He must've been able to run the source himself though, so any idea how to fix the problem without removing RMI? :L

- Fred
 
Upvote 0
Junior Spellweaver
Joined
Oct 30, 2010
Messages
148
Reaction score
8
Re: Database error?

Nevermind, got it working now. I had to uninstall Java 8 as well after installing Java 7... <_<
 
Upvote 0
Junior Spellweaver
Joined
Oct 30, 2010
Messages
148
Reaction score
8
Re: Database error?

Yeah, I forgot about that :D Long time since I worked with MapleStory.

Now I have another problem while trying to fix the DB:

It required me to add the column 'petid' to the table 'inventoryitems' for the character to save properly when making. However when I added that column, it gave me this error. Any idea where to find where the column count is specified or what I have done wrong? /:

Apr 17, 2017 2:31:54 PM net.sf.odinms.client.MapleCharacter saveToDB
SEVERE: (Account: admin) [charsave] Error saving character data
java.sql.SQLException: Column count doesn't match value count at row 1
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1055)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:956)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3491)
at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:3423)
at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:1936)
at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2060)
at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2542)
at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:1734)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2019)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1937)
at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:1922)
at net.sf.odinms.client.MapleCharacter.saveToDB(MapleCharacter.java:672)
at net.sf.odinms.net.login.handler.CreateCharHandler.handlePacket(CreateCharHandler.java:117)
at net.sf.odinms.net.MapleServerHandler.messageReceived(MapleServerHandler.java:147)
at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.messageReceived(AbstractIoFilterChain.java:570)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput.flush(SimpleProtocolDecoderOutput.java:58)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:180)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.executor.ExecutorFilter.processEvent(ExecutorFilter.java:220)
at org.apache.mina.filter.executor.ExecutorFilter$ProcessEventsRunnable.run(ExecutorFilter.java:264)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51)
at java.lang.Thread.run(Unknown Source)
 
Upvote 0
Junior Spellweaver
Joined
Oct 30, 2010
Messages
148
Reaction score
8
Solved other problems.

But when I try to enter the game now I get this Null Pointer Exception error:

Apr 18, 2017 8:51:06 PM net.sf.odinms.net.MapleServerHandler messageReceived
SEVERE: <admin (cid: 30013)> (Account: admin) Exception during processing packet: net.sf.odinms.net.channel.handler.PlayerLoggedinHandler: null
java.lang.NullPointerException
at net.sf.odinms.tools.MaplePacketCreator.getCharInfo(MaplePacketCreator.java:619)
at net.sf.odinms.net.channel.handler.PlayerLoggedinHandler.handlePacket(PlayerLoggedinHandler.java:92)
at net.sf.odinms.net.MapleServerHandler.messageReceived(MapleServerHandler.java:147)
at org.apache.mina.common.support.AbstractIoFilterChain$TailFilter.messageReceived(AbstractIoFilterChain.java:570)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.codec.support.SimpleProtocolDecoderOutput.flush(SimpleProtocolDecoderOutput.java:58)
at org.apache.mina.filter.codec.ProtocolCodecFilter.messageReceived(ProtocolCodecFilter.java:180)
at org.apache.mina.common.support.AbstractIoFilterChain.callNextMessageReceived(AbstractIoFilterChain.java:299)
at org.apache.mina.common.support.AbstractIoFilterChain.access$1100(AbstractIoFilterChain.java:53)
at org.apache.mina.common.support.AbstractIoFilterChain$EntryImpl$1.messageReceived(AbstractIoFilterChain.java:648)
at org.apache.mina.filter.executor.ExecutorFilter.processEvent(ExecutorFilter.java:220)
at org.apache.mina.filter.executor.ExecutorFilter$ProcessEventsRunnable.run(ExecutorFilter.java:264)
at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at org.apache.mina.util.NamePreservingRunnable.run(NamePreservingRunnable.java:51)
at java.lang.Thread.run(Unknown Source)

Looks like it has something to do with loading saved maps in VIP rocks, but I can't seem to understand what's wrong. Here's my MaplePacketCreator.java:

Code:
/*
	This file is part of the OdinMS Maple Story Server
    Copyright (C) 2008 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 version 3
    as published by the Free Software Foundation. You may not use, modify
    or distribute this program under any other version of the
    GNU Affero General Public License.

    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 net.sf.odinms.tools;

import java.awt.Point;
import java.awt.Rectangle;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Random;
import net.sf.odinms.client.BuddylistEntry;
import net.sf.odinms.client.IEquip;
import net.sf.odinms.client.IEquip.ScrollResult;
import net.sf.odinms.client.IItem;
import net.sf.odinms.client.ISkill;
import net.sf.odinms.client.Item;
import net.sf.odinms.client.MapleBuffStat;
import net.sf.odinms.client.MapleCharacter;
import net.sf.odinms.client.MapleClient;
import net.sf.odinms.client.MapleInventory;
import net.sf.odinms.client.MapleInventoryType;
import net.sf.odinms.client.MapleKeyBinding;
import net.sf.odinms.client.MaplePet;
import net.sf.odinms.client.MapleQuestStatus;
import net.sf.odinms.client.MapleRing;
import net.sf.odinms.client.MapleStat;
import net.sf.odinms.client.status.MonsterStatus;
import net.sf.odinms.constants.ServerConstants;
import net.sf.odinms.database.DatabaseConnection;
import net.sf.odinms.net.ByteArrayMaplePacket;
import net.sf.odinms.net.LongValueHolder;
import net.sf.odinms.net.MaplePacket;
import net.sf.odinms.net.SendPacketOpcode;
import net.sf.odinms.net.channel.handler.AbstractDealDamageHandler.SummonAttackEntry;
import net.sf.odinms.net.world.MapleParty;
import net.sf.odinms.net.world.MaplePartyCharacter;
import net.sf.odinms.net.world.PartyOperation;
import net.sf.odinms.server.CashItemInfo;
import net.sf.odinms.server.MapleItemInformationProvider;
import net.sf.odinms.server.MaplePlayerShop;
import net.sf.odinms.server.MaplePlayerShopItem;
import net.sf.odinms.server.MapleShopItem;
import net.sf.odinms.server.MapleSnowball;
import net.sf.odinms.server.MapleTrade;
import net.sf.odinms.server.PlayerInteraction.IPlayerInteractionManager;
import net.sf.odinms.server.PlayerInteraction.MapleMiniGame;
import net.sf.odinms.server.life.MapleMonster;
import net.sf.odinms.server.life.MapleNPC;
import net.sf.odinms.server.maps.MapleMap;
import net.sf.odinms.server.maps.MapleReactor;
import net.sf.odinms.server.maps.SummonMovementType;
import net.sf.odinms.server.movement.LifeMovementFragment;
import net.sf.odinms.tools.data.input.SeekableLittleEndianAccessor;
import net.sf.odinms.tools.data.output.LittleEndianWriter;
import net.sf.odinms.tools.data.output.MaplePacketLittleEndianWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


/**
 * Provides all MapleStory packets needed in one place.
 * 
 * @author Frz
 *  [USER=302236]Sin[/USER]ce Revision 259
 *  [USER=316612]Version[/USER] 1.0
 */
public class MaplePacketCreator {
	private static Logger log = LoggerFactory.getLogger(MaplePacketCreator.class);

	private final static byte[] CHAR_INFO_MAGIC = new byte[] { (byte) 0xff, (byte) 0xc9, (byte) 0x9a, 0x3b };
	private final static byte[] ITEM_MAGIC = new byte[] { (byte) 0x80, 5 };
	public static final List<Pair<MapleStat, Integer>> EMPTY_STATUPDATE = Collections.emptyList();
        private final static long FT_UT_OFFSET = 116444592000000000L; // EDT

        private static long getKoreanTimestamp(long realTimestamp) {
            long time = (realTimestamp / 1000 / 60); // convert to minutes
            return ((time * 600000000) + FT_UT_OFFSET);
        }
        
	/**
	 * Sends a hello packet.
	 * 
	 *  [USER=2000183830]para[/USER]m mapleVersion The maple client version.
	 *  [USER=2000183830]para[/USER]m sendIv the IV used by the server for sending
	 *  [USER=2000183830]para[/USER]m recvIv the IV used by the server for receiving
	 */
	//* GMS v40 Beta MapleStory
        public static MaplePacket getHello(short mapleVersion, byte[] sendIv, byte[] recvIv, boolean testServer) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.CLIENT_HELLO.getValue());
            mplew.writeShort(40);
            mplew.writeShort(1);
            mplew.write(0);
            mplew.write(recvIv);
            mplew.write(sendIv);
            mplew.write(5);
            return mplew.getPacket();
	}
        //*/
        
        /* KMS v1 Beta MapleStory
        public static MaplePacket getHello(short mapleVersion, byte[] sendIv, byte[] recvIv, boolean testServer) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(0x0E);
            mplew.writeShort(223);
            mplew.writeMapleAsciiString("");
            mplew.write(recvIv);
            mplew.write(sendIv);
            mplew.write(1); // 1 = KMS, 2 = KMST
            return mplew.getPacket();
	}
        //*/

	/**
	 * Sends a ping packet.
	 * 
	 *  [USER=850422]return[/USER] The packet.
	 */
	public static MaplePacket getPing() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PING.getValue());
            return mplew.getPacket();
	}

	/**
	 * Gets a login failed packet.
	 * 
	 * Possible values for <code>reason</code>:<br>
	 * 3: ID deleted or blocked<br>
	 * 4: Incorrect password<br>
	 * 5: Not a registered id<br>
	 * 6: System error<br>
	 * 7: Already logged in<br>
	 * 8: System error<br>
	 * 9: System error<br>
	 * 10: Cannot process so many connections<br>
	 * 11: Only users older than 20 can use this channel
	 * 
	 *  [USER=2000183830]para[/USER]m reason The reason logging in failed.
	 *  [USER=850422]return[/USER] The login failed packet.
	 */
	public static MaplePacket getLoginFailed(int reason) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(16);
            mplew.write(SendPacketOpcode.LOGIN_STATUS.getValue());
            mplew.write(reason);
            mplew.write(0);
            mplew.writeInt(0);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a successful authentication and PIN Request packet.
	 * 
	 *  [USER=2000183830]para[/USER]m c The client.
	 *  [USER=850422]return[/USER] The PIN request packet.
	 */
	public static MaplePacket OnCheckPasswordResult(MapleClient c) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.LOGIN_STATUS.getValue());
            mplew.write(0); // 0 or 1? o.o
            mplew.write(0);
            mplew.writeInt(0);
            mplew.writeInt(c.getAccID());
            mplew.write(0);
            mplew.write(c.isGm() ? 1 : 0);
            mplew.write(1);
            mplew.writeMapleAsciiString(c.getAccountName());
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            return mplew.getPacket();
	}
        
        public static MaplePacket getServerIP(int charid, byte[] ip, short port) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x05);
            mplew.writeShort(0); // ?
            mplew.write(ip);
            mplew.writeShort(port); //login or world or channel o.o
            mplew.writeInt(charid); // charid
            mplew.write(0);
            return mplew.getPacket();
        }
        
        public static MaplePacket getServerList() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            int channels = ServerConstants.Channels;
            mplew.write(SendPacketOpcode.SERVERLIST.getValue());
            mplew.write(0);
            mplew.writeMapleAsciiString(ServerConstants.WORLD_NAME); // Tespia is the only world
            mplew.write((byte)channels); // channels
            for (byte i = 0; i < (byte)channels; i++) {
                mplew.writeMapleAsciiString(ServerConstants.WORLD_NAME + "-" + (i + 1));
                mplew.writeInt(100); // server load, get connected size
                mplew.write(0); // Tespia is the only world
                mplew.write((byte)i);
                mplew.write(0); // unk
            }   
            return mplew.getPacket();
        }
        
        /**
	 * Gets a packet saying that the server list is over.
	 * 
	 *  [USER=850422]return[/USER] The end of server list packet.
	 */
	public static MaplePacket getEndOfServerList() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SERVERLIST.getValue());
            mplew.write(0xFF);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a packet detailing a server status message.
	 * 
	 * Possible values for <code>status</code>:<br>
	 * 0 - Normal<br>
	 * 1 - Highly populated<br>
	 * 2 - Full
	 * 
	 *  [USER=2000183830]para[/USER]m status The server status.
	 *  [USER=850422]return[/USER] The server status packet.
	 */
	public static MaplePacket getServerStatus(byte status) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SERVERSTATUS.getValue());
            mplew.write(status);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a packet with a list of characters.
	 * 
	 *  [USER=2000183830]para[/USER]m c The MapleClient to load characters of.
	 *  [USER=2000183830]para[/USER]m serverId The ID of the server requested.
	 *  [USER=850422]return[/USER] The character list packet.
	 */
	public static MaplePacket getCharList(MapleClient c, int serverId) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CHARLIST.getValue());
		mplew.write(0);
		List<MapleCharacter> chars = c.loadCharacters(serverId);
		mplew.write((byte) chars.size());
		for (MapleCharacter chr : chars) {
                    addCharData(mplew, chr);
		}
                mplew.writeLong(0); 
		return mplew.getPacket();
	}
        
        /**
	 * Adds an entry for a character to an existing
	 * MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWrite instance to write the stats
	 *            to.
	 *  [USER=2000183830]para[/USER]m chr The character to add.
	 */
	private static void addCharData(MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
            mplew.writeInt(chr.getId());
            mplew.writeAsciiString(chr.getName(), 13);
            mplew.write(chr.getGender());
            mplew.write(chr.getSkinColor().getId());
            mplew.writeInt(chr.getFace());
            mplew.writeInt(chr.getHair());
            
            mplew.writeLong(0); // Pet SN 
            
            mplew.write(chr.getLevel());
            mplew.writeShort(chr.getJob().getId());
            mplew.writeShort(chr.getStr());
            mplew.writeShort(chr.getDex());
            mplew.writeShort(chr.getInt());
            mplew.writeShort(chr.getLuk());
            mplew.writeShort(chr.getHp());
            mplew.writeShort(chr.getMaxHp());
            mplew.writeShort(chr.getMp());
            mplew.writeShort(chr.getMaxMp());
            mplew.writeShort(chr.getRemainingAp());
            mplew.writeShort(chr.getRemainingSp());
            mplew.writeInt(chr.getExp());
            mplew.writeShort(chr.getFame());
            
            mplew.writeInt(chr.getMapId());
            mplew.write(chr.getInitialSpawnpoint());
            
            mplew.writeLong(0);
            mplew.writeInt(0);
            mplew.writeInt(0);
            
            MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
            Map<Byte, Integer> myEquip = new LinkedHashMap<Byte, Integer>();
            Map<Byte, Integer> maskedEquip = new LinkedHashMap<Byte, Integer>();
            for (IItem item : iv.list()) {
                byte pos = (byte) (item.getPosition() * -1);
                if (pos < 100 && myEquip.get(pos) == null) {
                    maskedEquip.put(pos, item.getItemId()); 
                } else if (pos > 100) { // lol odinms. walao eh
                    pos -= 100;
                    if (myEquip.get(pos) != null) {
                        maskedEquip.put(pos, myEquip.get(pos));
                    }
                    myEquip.put(pos, item.getItemId());
                } else if (myEquip.get(pos) != null) {
                    maskedEquip.put(pos, item.getItemId());
                }
            }
            for (Entry<Byte, Integer> entry : maskedEquip.entrySet()) {
                mplew.write(entry.getKey());
                mplew.writeInt(entry.getValue());
            }
            mplew.write(0);
            for (Entry<Byte, Integer> entry : myEquip.entrySet()) {
                mplew.write(entry.getKey());
                mplew.writeInt(entry.getValue());
            }
            mplew.write(0);
            if ((chr.getJob().getId() / 100) != 5) {
                mplew.write(1);
                mplew.writeInt(chr.getRank());
                mplew.writeInt(chr.getRankMove()); // might be reverted
                mplew.writeInt(chr.getJobRank());
                mplew.writeInt(chr.getJobRankMove());
            } else {
                mplew.write(0);
            }
	}
        
        public static MaplePacket charNameResponse(String charname, boolean nameUsed) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.CHAR_NAME_RESPONSE.getValue());
            mplew.writeMapleAsciiString(charname);
            mplew.write(nameUsed ? 1 : 0);
            return mplew.getPacket();
	}
        
        public static MaplePacket addNewCharEntry(MapleCharacter chr, boolean worked) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.ADD_NEW_CHAR_ENTRY.getValue());
            mplew.write(worked ? 0 : 1);
            if (worked) {
                addCharData(mplew, chr);
            }
            mplew.writeLong(0);
            mplew.writeLong(0);
            return mplew.getPacket();
	}
        
        /**
	 * Gets a packet telling the client the IP of the channel server.
	 * 
	 *  [USER=2000183830]para[/USER]m ip The ip of the requested channel server.
	 *  [USER=2000183830]para[/USER]m port The port the channel is on.
	 *  [USER=2000183830]para[/USER]m clientId The ID of the client.
	 *  [USER=850422]return[/USER] The server IP packet.
	 */
	public static MaplePacket getServerIP(byte[] ip, short port, int clientId) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SERVER_IP.getValue());
		mplew.writeShort(0);
		mplew.write(ip);
		mplew.writeShort(port);
		mplew.writeInt(clientId);
		mplew.write(0);
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet telling the client the IP of the new channel.
	 * 
	 *  [USER=2000183830]para[/USER]m ip The ip of the requested channel server.
	 *  [USER=2000183830]para[/USER]m port The port the channel is on.
	 *  [USER=850422]return[/USER] The server IP packet.
	 */
	public static MaplePacket getChannelChange(byte[] ip, int port) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CHANGE_CHANNEL.getValue());
		mplew.write(1);
		mplew.write(ip);
		mplew.writeShort(port);
		return mplew.getPacket();
	}
        
        /**
	 * Adds item info to existing MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to write to.
	 *  [USER=2000183830]para[/USER]m item The item to add info about.
	 *  [USER=2000183830]para[/USER]m zeroPosition Is the position zero?
	 *  [USER=2000183830]para[/USER]m leaveOut Leave out the item if position is zero?
	 */
        private static void addItemInfo(MaplePacketLittleEndianWriter mplew, IItem item, boolean shortPos) {
            addItemInfo(mplew, item, shortPos, false);
        }
        
	private static void addItemInfo(MaplePacketLittleEndianWriter mplew, IItem item, boolean shortPos, boolean zeroPosition) {
            Random rand = new Random();
            boolean cash = false;
            boolean ring = false;
            IEquip equip = null;
            if (item.getType() == IItem.EQUIP) {
                equip = (IEquip) item;
                if (equip.getRingId() > -1) {
                    ring = true;
                }
            }
            byte pos = zeroPosition ? 0 : item.getPosition();
            if (pos != 0) {
                if (shortPos) {
                    mplew.writeShort(pos);
                } else {
                    pos = (byte)Math.abs(pos);
                    if (pos > 100 || (item.getType() == MapleInventoryType.EQUIPPED.getType() && ring)) pos -= 100;
                    mplew.write(pos);
                }
            }
            boolean isEquip = (item.getItemId() / 1000000 == 1);
            boolean isPet = (item.getItemId() / 1000000 == 5);
            mplew.writeInt(item.getItemId());
            if (cash) {
                mplew.write(1);
                mplew.writeLong(rand.nextLong()); // Cash Item ID/SN
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            } else if (ring) {
                mplew.write(1);
                mplew.writeLong(equip.getRingId());
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            } else if (isPet) {
                mplew.write(1); // we use unique id like rings here
                mplew.writeLong(item.getPetId());
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
                MaplePet pet = MaplePet.loadFromDb(item.getItemId(), item.getPosition(), item.getPetId());
                String petname = pet.getName();
                mplew.writeAsciiString(petname, 13);
                mplew.write(pet.getLevel());
                mplew.writeShort(pet.getCloseness());
                mplew.write(pet.getFullness());
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2))); // TODO: expirations
                return;
            } else {
                mplew.write(0);
                mplew.writeLong(rand.nextLong()); // Non-NX don't expire, so.. random 8 byte long here
            }
            if (isEquip) {
                mplew.write(equip.getUpgradeSlots());
                mplew.write(equip.getLevel());
                mplew.writeShort(equip.getStr());
                mplew.writeShort(equip.getDex());
                mplew.writeShort(equip.getInt());
                mplew.writeShort(equip.getLuk());
                mplew.writeShort(equip.getHp());
                mplew.writeShort(equip.getMp());
                mplew.writeShort(equip.getWatk());
                mplew.writeShort(equip.getMatk());
                mplew.writeShort(equip.getWdef());
                mplew.writeShort(equip.getMdef());
                mplew.writeShort(equip.getAcc());
                mplew.writeShort(equip.getAvoid());
                mplew.writeShort(equip.getHands());
                mplew.writeShort(equip.getSpeed());
                mplew.writeShort(equip.getJump());
            } else {
                mplew.writeShort(item.getQuantity());
            }
	}
        
        /**
	 * Gets character info for a character.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The character to get info about.
	 *  [USER=850422]return[/USER] The character info packet.
	 */
        public static MaplePacket getCharInfo(MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WARP_TO_MAP.getValue());
                
		mplew.writeInt(chr.getClient().getChannel() - 1); //channel
		mplew.write(0); // portals
		mplew.write(1); // connecting
                
                Random random = new Random();
                mplew.writeInt(random.nextInt()); // 3 random ints here
                mplew.writeInt(random.nextInt());
                mplew.writeInt(random.nextInt());
                
                mplew.writeInt(0);
                
                mplew.writeShort(-1); //flags
                mplew.writeInt(chr.getId());
                mplew.writeAsciiString(chr.getName(), 13);
                mplew.write(chr.getGender());
                mplew.write(chr.getSkinColor().getId());
                mplew.writeInt(chr.getFace());
                mplew.writeInt(chr.getHair());
                
                mplew.writeLong(0); // Pet SN
                
                mplew.write(chr.getLevel());
                mplew.writeShort(chr.getJob().getId());
                mplew.writeShort(chr.getStr());
                mplew.writeShort(chr.getDex());
                mplew.writeShort(chr.getInt());
                mplew.writeShort(chr.getLuk());
                mplew.writeShort(chr.getHp());
                mplew.writeShort(chr.getMaxHp());
                mplew.writeShort(chr.getMp());
                mplew.writeShort(chr.getMaxMp());
                mplew.writeShort(chr.getRemainingAp());
                mplew.writeShort(chr.getRemainingSp());
                mplew.writeInt(chr.getExp());
                mplew.writeShort(chr.getFame());
                
                mplew.writeInt(chr.getMapId());
                mplew.write(chr.getInitialSpawnpoint());
                
                mplew.writeLong(0); // dont know what this is lul
                mplew.writeInt(0);
                mplew.writeInt(0);
                
                mplew.write(chr.getBuddylist().getCapacity());
                mplew.writeInt(chr.getMeso());
                
                MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
		Collection<IItem> equippedList = iv.list();
                Item[] equipped = new Item[17];
                Item[] equippedCash = new Item[17];
		for (IItem item : equippedList) {
                    byte pos = item.getPosition();
                    if (pos < 0) {
                        pos = (byte)Math.abs(pos);
                        if (pos > 100) {
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
                    if (pos < 0) {
                        if (pos < -100) {
                            pos += 100;
                            pos = (byte)Math.abs(pos);
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            pos = (byte)Math.abs(pos);
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
		}
		for (Item item : equipped) {
                    if (item != null) {
                        addItemInfo(mplew, item, false);
                    }
		}
		mplew.write(0);
                for (Item item : equippedCash) {
                    if (item != null) {
                        addItemInfo(mplew, item, false);
                    }
		}
                mplew.write(0);
                for (byte i = 1; i < 6; i++) {
                    mplew.write(100); // TODO: REAL maxslots and not forced 100 to all players + fix the CS operation to increase
                    iv = chr.getInventory(MapleInventoryType.getByType((byte)i));
                    for (IItem item : iv.list()) {
                        if (item != null && item.getPosition() > 0) {
                            addItemInfo(mplew, item, false);
                        }
                    }
                    mplew.write(0);
                }
                Map<ISkill, MapleCharacter.SkillEntry> skills = chr.getSkills();
		mplew.writeShort(skills.size());
		for (Entry<ISkill, MapleCharacter.SkillEntry> skill : skills.entrySet()) {
                    mplew.writeInt(skill.getKey().getId());
                    mplew.writeInt((byte)skill.getValue().skillevel);
		}
                mplew.writeShort(0); // TODO: Quests
		mplew.writeShort(0); // TODO: Mini Games
                
                List<MapleRing> rings = getRing(chr);
                mplew.writeShort(rings.size());
                for (MapleRing ring : rings) {
                    mplew.writeInt(ring.getPartnerChrId());
                    mplew.writeAsciiString(StringUtil.getRightPaddedStr(ring.getPartnerName(), '\0', 13));
                    mplew.writeInt(ring.getRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getPartnerRingId());
                    mplew.writeInt(0);
                }
                
                for (int map : chr.getVIPRockMaps()) {
                    mplew.writeInt(map);
                }
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet spawning a player as a mapobject to other clients.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The character to spawn to other clients.
	 *  [USER=850422]return[/USER] The spawn player packet.
	 */
	public static MaplePacket spawnPlayerMapobject(MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_PLAYER.getValue());
		mplew.writeInt(chr.getId());
		mplew.writeMapleAsciiString(chr.getName());

                int buffmask = 0;
		Integer buffvalue = null;
                
                if (chr.getBuffedValue(MapleBuffStat.DARKSIGHT) != null && !chr.isHidden()) {
                    buffmask |= MapleBuffStat.DARKSIGHT.getValue();
		}
                if (chr.getBuffedValue(MapleBuffStat.COMBO) != null) {
                    buffmask |= MapleBuffStat.COMBO.getValue();
                    buffvalue = Integer.valueOf(chr.getBuffedValue(MapleBuffStat.COMBO).intValue());
		}
                if (chr.getBuffedValue(MapleBuffStat.SHADOWPARTNER) != null) {
                    buffmask |= MapleBuffStat.SHADOWPARTNER.getValue();
		}
                
		mplew.writeInt(buffmask); // TODO: Finish buffmasks
                if (buffvalue != null) {
                    mplew.write(buffvalue.byteValue());
		}
                
                addCharLook(mplew, chr, true);
                mplew.writeInt(0); // [10/28/2014 8:25:40 PM] Justin: IncMaxMemberNumCost
                mplew.writeInt(chr.getItemEffect()); // TODO: Fix Chocolate Hearts (This is Cash Effect not Hearts)
                mplew.writeInt(chr.getChair());
                mplew.writeShort(chr.getPosition().x);
                mplew.writeShort(chr.getPosition().y);
                mplew.write(chr.getStance());
                mplew.writeShort(0); // FH
                
                // Pets
                if (chr.getPet(0) != null) {
                    mplew.write(1);
                    MaplePet pet = chr.getPet(0);
                    mplew.writeInt(pet.getItemId());
                    mplew.writeMapleAsciiString(pet.getName());
                    mplew.writeLong(pet.getUniqueId());
                    mplew.writeShort(pet.getPos().x);
                    mplew.writeShort(pet.getPos().y);
                    mplew.write(pet.getStance());
                    mplew.writeShort(pet.getFh());
                } else {
                    mplew.write(0);
                }
                
                // Mini Game & Interaction Boxes
                if (chr.getPlayerShop() != null && chr.getPlayerShop().isOwner(chr)) {
                    addShopBox(mplew, chr.getPlayerShop());
		} else if ((MapleMiniGame) chr.getInteraction() != null) {
                    addAnnounceBox(mplew, chr.getInteraction());
                } else {
                    mplew.write(0);
		}
                
                // Rings
                List<MapleRing> rings = getRing(chr);
                mplew.write(rings.size());
                for (MapleRing ring : rings) { 
                    mplew.writeInt(ring.getRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getPartnerRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getItemId());
                }
		return mplew.getPacket();
	}
        
        private static List<MapleRing> getRing(MapleCharacter chr) {
                MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED); 
                Collection<IItem> equippedC = iv.list(); 
                List<Item> equipped = new ArrayList<Item>(equippedC.size()); 
                for (IItem item : equippedC) { 
                    equipped.add((Item) item); 
                } 
                Collections.sort(equipped); 
                List<MapleRing> rings = new ArrayList<MapleRing>(); 
                for (Item item : equipped) { 
                        if (((IEquip) item).getRingId() > -1) { 
                            rings.add(MapleRing.loadFromDb(((IEquip) item).getRingId())); 
                        } 
                } 
                Collections.sort(rings); 
		return rings;
	}
        
        /**
	 * Gets a server message packet.
	 * 
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=850422]return[/USER] The server message packet.
	 */
	public static MaplePacket serverMessage(String message) {
		return serverMessage(4, 0, message, true, false);
	}

	/**
	 * Gets a server notice packet.
	 * 
	 * Possible values for <code>type</code>:<br>
	 * 0: [Notice]<br>
	 * 1: Popup<br>
	 * 2: Light blue background and lolwhut<br>
	 * 4: Scrolling message at top<br>
	 * 5: Pink Text<br>
	 * 6: Lightblue Text
	 * 
	 *  [USER=2000183830]para[/USER]m type The type of the notice.
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=850422]return[/USER] The server notice packet.
	 */
	public static MaplePacket serverNotice(int type, String message) {
            return serverMessage(type, 0, message, false, false);
	}

	/**
	 * Gets a server notice packet.
	 * 
	 * Possible values for <code>type</code>:<br>
	 * 0: [Notice]<br>
	 * 1: Popup<br>
	 * 2: Light blue background and lolwhut<br>
	 * 4: Scrolling message at top<br>
	 * 5: Pink Text<br>
	 * 6: Lightblue Text
	 * 
	 *  [USER=2000183830]para[/USER]m type The type of the notice.
	 *  [USER=2000183830]para[/USER]m channel The channel this notice was sent on.
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=850422]return[/USER] The server notice packet.
	 */
	public static MaplePacket serverNotice(int type, int channel, String message) {
		return serverMessage(type, channel, message, false, false);
	}

	/**
	 * Gets a server message packet.
	 * 
	 * Possible values for <code>type</code>:<br>
	 * 0: [Notice]<br>
	 * 1: Popup<br>
	 * 2: Light blue background and lolwhut<br>
	 * 4: Scrolling message at top<br>
	 * 5: Pink Text<br>
	 * 6: Lightblue Text
	 * 
	 *  [USER=2000183830]para[/USER]m type The type of the notice.
	 *  [USER=2000183830]para[/USER]m channel The channel this notice was sent on.
	 *  [USER=2000183830]para[/USER]m message The message to convey.
	 *  [USER=2000183830]para[/USER]m servermessage Is this a scrolling ticker?
	 *  [USER=850422]return[/USER] The server notice packet.
	 */
	private static MaplePacket serverMessage(int type, int channel, String message, boolean servermessage, boolean whisper) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SERVERMESSAGE.getValue());
		mplew.write(type > 5 ? type == 6 ? 0 : 5 : type); // v40 beta only has 0: [Notice], 1: Popup, 2: Mega, 3: Smega, 4: Header, and 5: Red text
		if (servermessage) {
                    mplew.write(1);
		}
		mplew.writeMapleAsciiString(message);
		if (type == 3) {
                    mplew.write(channel - 1); // channel
                    mplew.write(whisper ? 1 : 0); // boolean whisper byte is used on super megaphones
		}
		return mplew.getPacket();
	}
        
        public static MaplePacket facialExpression(MapleCharacter from, int expression) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FACIAL_EXPRESSION.getValue());
		mplew.writeInt(from.getId());
		mplew.writeInt(expression);
		return mplew.getPacket();
	}

        public static MaplePacket movePlayer(int cid, SeekableLittleEndianAccessor slea) {//List<LifeMovementFragment> moves
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_PLAYER.getValue());
		mplew.writeInt(cid);
                mplew.write(slea.read((int)slea.available()));
		return mplew.getPacket();
	}
        
        /**
	 * Gets a general chat packet.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The character who sent the chat.
	 *  [USER=2000183830]para[/USER]m text The text of the chat.
	 *  [USER=850422]return[/USER] The general chat packet.
	 */
	public static MaplePacket getChatText(MapleCharacter chr, String text) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CHATTEXT.getValue());
		mplew.writeInt(chr.getId());
		mplew.write(chr.isGM() ? 1 : 0); // gms have this set to != 0, gives them white
		mplew.writeMapleAsciiString(text);
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet telling the client to change maps.
	 * 
	 *  [USER=2000183830]para[/USER]m to The <code>MapleMap</code> to warp to.
	 *  [USER=2000183830]para[/USER]m spawnPoint The spawn portal number to spawn at.
	 *  [USER=2000183830]para[/USER]m chr The character warping to <code>to</code>
	 *  [USER=850422]return[/USER] The map change packet.
	 */
	public static MaplePacket getWarpToMap(MapleMap to, int spawnPoint, MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WARP_TO_MAP.getValue()); // 0x49
		mplew.writeInt(chr.getClient().getChannel() - 1);
		mplew.write(to.getPortals().size());
                mplew.write(0);
		mplew.writeInt(to.getId());
		mplew.write(spawnPoint);
		mplew.writeShort(chr.getHp()); // hp (???)
		return mplew.getPacket();
	}
        
        public static MaplePacket charInfo(MapleCharacter chr) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.CHAR_INFO.getValue());
                mplew.writeInt(chr.getId());
                mplew.write(chr.getLevel());
                mplew.writeShort(chr.getJob().getId());
                mplew.writeShort(chr.getFame());
                mplew.writeMapleAsciiString(chr.isGM() ? chr.getName().equalsIgnoreCase("<3") || chr.getName().equalsIgnoreCase("Eric") ? "The Boss" : "Eric's witch" : ""); //hehe
                if (chr.getPet(0) != null) {
                    MaplePet pet = chr.getPet(0);
                    mplew.write(1);
                    mplew.writeInt(pet.getItemId());
                    mplew.writeMapleAsciiString(pet.getName());
                    mplew.write(pet.getLevel());
                    mplew.writeShort(pet.getCloseness());
                    mplew.write(pet.getFullness());
                    mplew.writeInt(0); // Pet Equip ID? I can't find the ID in v40 so i'm not sure wtf this is
                } else {
                    mplew.write(0);
                }
                mplew.write(0); // Wishlist: while true, 4bytes (int)
                mplew.writeLong(0); // Unknown: Rings (?)
                return mplew.getPacket();
	}
        
        /**
	 * Gets a NPC spawn packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The NPC to spawn.
	 *  [USER=850422]return[/USER] The NPC spawn packet.
	 */
	
        public static MaplePacket spawnNPC(MapleNPC life) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.SPAWN_NPC.getValue());
                mplew.writeInt(life.getObjectId());
		mplew.writeInt(life.getId());
		mplew.writeShort(life.getPosition().x);
		mplew.writeShort(life.getCy());
		mplew.write(life.isFacingLeft() ? 0 : 1); // faces left
		mplew.writeShort(life.getFh());
		mplew.writeShort(life.getRx0());
		mplew.writeShort(life.getRx1());
		return mplew.getPacket();
        }
        
        public static MaplePacket spawnNPCRequest(MapleNPC life) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.SPAWN_NPC_REQUEST_CONTROLLER.getValue());
                mplew.write(0x01);
                mplew.writeInt(life.getObjectId());
		mplew.writeInt(life.getId());
		mplew.writeShort(life.getPosition().x);
		mplew.writeShort(life.getCy());
		mplew.write(life.isFacingLeft() ? 0 : 1); // faces left
		mplew.writeShort(life.getFh());
		mplew.writeShort(life.getRx0());
		mplew.writeShort(life.getRx1());
		return mplew.getPacket();
        }
        
        public static MaplePacket NPCAnimation(SeekableLittleEndianAccessor slea) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x7F);
            byte[] data = slea.read((int)slea.available());
            if (data.length == 5) {
                try {
                    mplew.writeInt(BitTools.toInt32(data, 0));
                    mplew.writeShort(BitTools.toInt16(data, 4));
                } catch (Exception e) {
                }
            } else {
                mplew.write(data);
            }
            return mplew.getPacket();
        }
        
        /**
	 * Adds the aesthetic aspects of a character to an existing
	 * MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWrite instance to write the stats
	 *            to.
	 *  [USER=2000183830]para[/USER]m chr The character to add the looks of.
	 *  [USER=2000183830]para[/USER]m mega Unknown
	 */
	private static void addCharLook(MaplePacketLittleEndianWriter mplew, MapleCharacter chr, boolean mega) {
                if (mega) {
                    mplew.write(chr.getGender());
                }
		mplew.write(chr.getSkinColor().getId()); // skin color
		mplew.writeInt(chr.getFace()); // face
		mplew.write(0); // OdinMS: mega ? 1 : 0
		mplew.writeInt(chr.getHair()); // hair

                MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
		Collection<IItem> equippedList = iv.list();
                Item[] equipped = new Item[17];
                Item[] equippedCash = new Item[17];
		for (IItem item : equippedList) {
                    byte pos = item.getPosition();
                    if (pos < 0) {
                        pos = (byte)Math.abs(pos);
                        if (pos > 100) {
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
                    if (pos < 0) {
                        if (pos < -100) {
                            pos += 100;
                            pos = (byte)Math.abs(pos);
                            equippedCash[(byte)(pos - 100)] = (Item)item;
                        } else {
                            pos = (byte)Math.abs(pos);
                            equipped[(byte)pos] = (Item)item;
                        }
                    }
		}
                Map<Byte, Integer> items = new LinkedHashMap<>();
                for (Item item : equippedCash) {
                    if (item != null) {
                        byte slotuse = (byte)Math.abs(item.getPosition());
                        if (slotuse > 100) slotuse -= 100;
                        items.put(slotuse, item.getItemId());
                    }
                }
                for (Item item : equipped) {
                    if (item != null && !items.containsKey((byte)Math.abs(item.getPosition()))) {
                        items.put((byte)Math.abs(item.getPosition()), item.getItemId());
                    }
                }
                for (Entry<Byte, Integer> entry : items.entrySet()) {
                    mplew.write(entry.getKey());
                    mplew.writeInt(entry.getValue());
                }
		mplew.write(0xFF);
                
		if (chr.getPet(0) != null) {
                    mplew.writeInt(chr.getPet(0).getItemId());
		} else {
                    mplew.writeInt(0); // Pet
		}
	}
        
        public static MaplePacket reportReply(byte type) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.REPORT.getValue());
            mplew.write(type);
            return mplew.getPacket();
        }

	/**
	 * Gets an empty stat update.
	 * 
	 *  [USER=850422]return[/USER] The empy stat update packet.
	 */
	public static MaplePacket enableActions2() {
		return updatePlayerStats(EMPTY_STATUPDATE, true);
	}

	/**
	 * Gets an update for specified stats.
	 * 
	 *  [USER=2000183830]para[/USER]m stats The stats to update.
	 *  [USER=850422]return[/USER] The stat update packet.
	 */
	public static MaplePacket updatePlayerStats(List<Pair<MapleStat, Integer>> stats) {
            return updatePlayerStats(stats, false);
	}

	/**
	 * Gets an update for specified stats.
	 * 
	 *  [USER=2000183830]para[/USER]m stats The list of stats to update.
	 *  [USER=2000183830]para[/USER]m itemReaction Result of an item reaction(?)
	 *  [USER=850422]return[/USER] The stat update packet.
	 */
	public static MaplePacket updatePlayerStats(List<Pair<MapleStat, Integer>> stats, boolean itemReaction) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_STATS.getValue());
		if (itemReaction) {
                    mplew.write(1);
		} else {
                    mplew.write(0);
		}
		int updateMask = 0;
		for (Pair<MapleStat, Integer> statupdate : stats) {
			updateMask |= statupdate.getLeft().getValue();
		}
		List<Pair<MapleStat, Integer>> mystats = stats;
		if (mystats.size() > 1) {
			Collections.sort(mystats, new Comparator<Pair<MapleStat, Integer>>() {

				@Override
				public int compare(Pair<MapleStat, Integer> o1, Pair<MapleStat, Integer> o2) {
					int val1 = o1.getLeft().getValue();
					int val2 = o2.getLeft().getValue();
					return (val1 < val2 ? -1 : (val1 == val2 ? 0 : 1));
				}
			});
		}
		mplew.writeInt(updateMask);
		for (Pair<MapleStat, Integer> statupdate : mystats) {
			if (statupdate.getLeft().getValue() >= 1) {
				if (statupdate.getLeft().getValue() == 0x1) {
                                    mplew.writeShort(statupdate.getRight().shortValue());
				} else if (statupdate.getLeft().getValue() <= 0x4) {
					mplew.writeInt(statupdate.getRight());
				} else if (statupdate.getLeft().getValue() < 0x20) {
					mplew.write(statupdate.getRight().shortValue());
				} else if (statupdate.getLeft().getValue() < 0xFFFF) {
					mplew.writeShort(statupdate.getRight().shortValue());
				} else {
					mplew.writeInt(statupdate.getRight().intValue());
				}
			}
		}
		return mplew.getPacket();
	}
        
        /**
	 * Gets a spawn monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The monster to spawn.
	 *  [USER=2000183830]para[/USER]m newSpawn Is it a new spawn?
	 *  [USER=850422]return[/USER] The spawn monster packet.
	 */
	public static MaplePacket spawnMonster(MapleMonster life, boolean newSpawn) {
		return spawnMonsterInternal(life, false, newSpawn, false, 0);
	}

	/**
	 * Gets a spawn monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The monster to spawn.
	 *  [USER=2000183830]para[/USER]m newSpawn Is it a new spawn?
	 *  [USER=2000183830]para[/USER]m effect The spawn effect.
	 *  [USER=850422]return[/USER] The spawn monster packet.
	 */
	public static MaplePacket spawnMonster(MapleMonster life, boolean newSpawn, int effect) {
		return spawnMonsterInternal(life, false, newSpawn, false, effect);
	}

	/**
	 * Gets a control monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m life The monster to give control to.
	 *  [USER=2000183830]para[/USER]m newSpawn Is it a new spawn?
	 *  [USER=2000183830]para[/USER]m aggro Aggressive monster?
	 *  [USER=850422]return[/USER] The monster control packet.
	 */
	public static MaplePacket controlMonster(MapleMonster life, boolean newSpawn, boolean aggro) {
		return spawnMonsterInternal(life, true, newSpawn, aggro, 0);
	}

	/**
	 * Internal function to handler monster spawning and controlling.
	 * 
	 *  [USER=2000183830]para[/USER]m life The mob to perform operations with.
	 *  [USER=2000183830]para[/USER]m requestController Requesting control of mob?
	 *  [USER=2000183830]para[/USER]m newSpawn New spawn (fade in?)
	 *  [USER=2000183830]para[/USER]m aggro Aggressive mob?
	 *  [USER=2000183830]para[/USER]m effect The spawn effect to use.
	 *  [USER=850422]return[/USER] The spawn/control packet.
	 */
	private static MaplePacket spawnMonsterInternal(MapleMonster life, boolean requestController, boolean newSpawn, boolean aggro, int effect) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		if (requestController) {
                    mplew.write(SendPacketOpcode.SPAWN_MONSTER_CONTROL.getValue());
                    mplew.write(1);
		} else {
                    mplew.write(SendPacketOpcode.SPAWN_MONSTER.getValue());
		}
		mplew.writeInt(life.getObjectId());
		mplew.writeInt(life.getId());
		mplew.writeShort(life.getPosition().x);
		mplew.writeShort(life.getPosition().y);
                if (requestController) {
                    mplew.write(2);
                } else {
                    mplew.write((life.getController() != null ? 0x08 : 0x02));
                }
                mplew.writeShort(life.getFh()); // seems to be left and right
                mplew.writeShort(life.getStance());
		if (effect > 0) {
                    mplew.write(effect);
                    mplew.writeInt(life.getObjectId());
		}
		if (newSpawn) {
                    mplew.write(-2);
		} else {
                    mplew.write(-1);
		}
                mplew.write(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	/**
	 * Gets a stop control monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The ObjectID of the monster to stop controlling.
	 *  [USER=850422]return[/USER] The stop control monster packet.
	 */
	public static MaplePacket stopControllingMonster(int oid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_MONSTER_CONTROL.getValue());
		mplew.write(0);
                mplew.writeInt(0);// ?
		return mplew.getPacket();
	}

	/**
	 * Gets a response to a move monster packet.
	 * 
	 *  [USER=2000183830]para[/USER]m objectid The ObjectID of the monster being moved.
	 *  [USER=2000183830]para[/USER]m moveid The movement ID.
	 *  [USER=2000183830]para[/USER]m currentMp The current MP of the monster.
	 *  [USER=2000183830]para[/USER]m useSkills Can the monster use skills?
         *  [USER=2000183830]para[/USER]m skill The skill used
         *  [USER=2000183830]para[/USER]m level The level of the skill used
	 *  [USER=850422]return[/USER] The move response packet.
	 */
	public static MaplePacket moveMonsterResponse(int objectid, short moveid, int currentMp, boolean useSkills, byte skill, byte level) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_MONSTER_RESPONSE.getValue());
		mplew.writeInt(objectid);
                mplew.writeShort(moveid);
                mplew.write(useSkills ? 1 : 0);
		mplew.writeShort(currentMp);
		mplew.write(skill); // skill
                mplew.write(level); // level
		return mplew.getPacket();
	}
        
        public static MaplePacket giveFameResponse(int mode, String charname, int newfame) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FAME_RESPONSE.getValue());
		mplew.write(0);
		mplew.writeMapleAsciiString(charname);
		mplew.write(mode);
		mplew.writeInt(newfame);
		return mplew.getPacket();
	}

	/**
	 * status can be: <br>
	 * 0: ok, use giveFameResponse<br>
	 * 1: the username is incorrectly entered<br>
	 * 2: users under level 15 are unable to toggle with fame.<br>
	 * 3: can't raise or drop fame anymore today.<br>
	 * 4: can't raise or drop fame for this character for this month anymore.<br>
	 * 5: received fame, use receiveFame()<br>
	 * 6: level of fame neither has been raised nor dropped due to an unexpected
	 * error
	 * 
	 *  [USER=2000183830]para[/USER]m status
	 *  [USER=2000183830]para[/USER]m mode
	 *  [USER=2000183830]para[/USER]m charname
	 *  [USER=2000183830]para[/USER]m newfame
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket giveFameErrorResponse(int status) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FAME_RESPONSE.getValue());
		mplew.writeInt(status);
		return mplew.getPacket();
	}

	public static MaplePacket receiveFame(int mode, String charnameFrom) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.FAME_RESPONSE.getValue());
		mplew.write(5);
		mplew.writeMapleAsciiString(charnameFrom);
		mplew.write(mode);
		return mplew.getPacket();
	}

	public static MaplePacket partyCreated() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(7);
                for (int i = 0; i < 5; i++) {
                    if (i >= 0 && i <= 2)
                        mplew.writeInt(1);
                    else if (i >= 3 && i <= 4)
                        mplew.writeShort(1);
                }
		return mplew.getPacket();
	}

	public static MaplePacket partyInvite(MapleCharacter from) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(4);
		mplew.writeInt(from.getParty().getId());
		mplew.writeMapleAsciiString(from.getName());
		return mplew.getPacket();
	}

	/**
	 * 9: a beginner can't create a party<br>
	 * 10/13/18: your request for a party didn't work due to an unexpected error<br>
	 * 12: you have yet to join a party<br>
	 * 15: already have joined a party<br>
	 * 16: the party you are trying to join is already at full capacity<br>
	 * 17: unable to find the requested character in this channel<br>
	 * 
	 *  [USER=2000183830]para[/USER]m message
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket partyStatusMessage(int message) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(message);
		return mplew.getPacket();
	}

	/**
	 * 21: has denied the invitation<br>
	 * 
	 *  [USER=2000183830]para[/USER]m message
	 *  [USER=2000183830]para[/USER]m charname
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket partyStatusMessage(int message, String charname) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.write(message);
		mplew.writeMapleAsciiString(charname);
		return mplew.getPacket();
	}

	private static void addPartyStatus(int forchannel, MapleParty party, LittleEndianWriter lew, boolean leaving) {
		List<MaplePartyCharacter> partymembers = new ArrayList<MaplePartyCharacter>(party.getMembers());
		while (partymembers.size() < 6) {
                    partymembers.add(new MaplePartyCharacter());
		}
		for (MaplePartyCharacter partychar : partymembers) {
                    lew.writeInt(partychar.getId());
		}
		for (MaplePartyCharacter partychar : partymembers) {
                    lew.writeAsciiString(StringUtil.getRightPaddedStr(partychar.getName(), '\0', 13));
                }
                for (MaplePartyCharacter partychar : partymembers) {
                    if (partychar.isOnline()) {
                        // This handles the Map IDs, but in v40 beta if you're
                        // not on the same map, the player appears offline. We
                        // have to eventually find a workaround for this.
                        int id = partychar.getMapid();
                        lew.writeInt(id);
                    } else {
                        lew.writeInt(-2);
                    }
                }
                lew.writeInt(party.getLeader().getId());
                for (MaplePartyCharacter partychar : partymembers) {
                    if (partychar.isOnline()) {
                        lew.writeInt(partychar.getChannel() - 1);
                    } else {
                        lew.writeInt(-2);
                    }
                }
                for (MaplePartyCharacter partychar : partymembers) {
                    if (partychar.getChannel() == forchannel && !leaving) {
                        lew.writeInt(partychar.getDoorTown());
                        lew.writeInt(partychar.getDoorTarget());
                        lew.writeInt(partychar.getDoorPosition().x);
                        lew.writeInt(partychar.getDoorPosition().y);
                    } else {
                        lew.writeInt(999999999);
                        lew.writeInt(999999999);
                        lew.writeInt(-1);
                        lew.writeInt(-1);
                    }
                }
	}

        public static MaplePacket updateParty(int forChannel, MapleParty party, PartyOperation op, MaplePartyCharacter target) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		switch (op) {
                        case DISBAND:
			case EXPEL:
			case LEAVE:
				mplew.write(0xB);
                                mplew.writeInt(party.getId());
                                mplew.writeInt(target.getId());
				if (op == PartyOperation.DISBAND) {
                                    mplew.write(0);
				} else {
                                    mplew.write(1);
                                    if (op == PartyOperation.EXPEL) {
                                        mplew.write(1);
                                    } else {
                                        mplew.write(0);
                                    }
                                    mplew.writeMapleAsciiString(target.getName());
                                    addPartyStatus(forChannel, party, mplew, false);
				}

				break;
			case JOIN:
				mplew.write(0xE);
				mplew.writeInt(1);
				mplew.writeMapleAsciiString(target.getName());
				addPartyStatus(forChannel, party, mplew, false);
				break;
			case SILENT_UPDATE:
			case LOG_ONOFF: // for some reason this has problems updating when logging off lol idk
				mplew.write(0x06);
                                mplew.writeInt(party.getId());
                                addPartyStatus(forChannel, party, mplew, false);
				break;

		}
		return mplew.getPacket();
	}

	public static MaplePacket partyPortal(int townId, int targetId, Point position) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PARTY_OPERATION.getValue());
		mplew.writeShort(0x1A); // 0x22 v55
		mplew.writeInt(townId);
		mplew.writeInt(targetId);
		mplew.writeShort(position.x);
		mplew.writeShort(position.y);
		return mplew.getPacket();
	}

	public static MaplePacket updatePartyMemberHP(int cid, int curhp, int maxhp) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_PARTYMEMBER_HP.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(curhp);
		mplew.writeInt(maxhp);
		return mplew.getPacket();
	}

	/**
	 * mode: 0 buddychat; 1 partychat; 2 guildchat
	 * 
	 *  [USER=2000183830]para[/USER]m name
	 *  [USER=2000183830]para[/USER]m chattext
	 *  [USER=2000183830]para[/USER]m mode
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket multiChat(String name, String chattext, int mode) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MULTICHAT.getValue());
		mplew.write(mode);
		mplew.writeMapleAsciiString(name);
		mplew.writeMapleAsciiString(chattext);
		return mplew.getPacket();
	}
        
        public static MaplePacket damageSummon(int cid, int summonSkillId, int damage, int unkByte, int monsterIdFrom) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DAMAGE_SUMMON.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(summonSkillId);
		mplew.write(unkByte);
                mplew.writeInt(monsterIdFrom);
		mplew.writeInt(damage);
		mplew.write(0);
		return mplew.getPacket();
	}

	public static MaplePacket damageMonster(int oid, int damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DAMAGE_MONSTER.getValue());
		mplew.writeInt(oid);
		mplew.write(0);
		mplew.writeInt(damage);
		return mplew.getPacket();
	}

        public static MaplePacket updateBuddylist(Collection<BuddylistEntry> buddylist) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(7);
		mplew.write(buddylist.size());
		for (BuddylistEntry buddy : buddylist) {
                    if (buddy.isVisible()) {
                        mplew.writeInt(buddy.getCharacterId()); // cid
                        mplew.writeAsciiString(StringUtil.getRightPaddedStr(buddy.getName(), '\0', 13));
                        mplew.write(0);
                        mplew.writeInt(buddy.getChannel() - 1);
                    }
		}
		for (int x = 0; x < buddylist.size(); x++) {
                    mplew.writeInt(0);
		}
		return mplew.getPacket();
	}

	public static MaplePacket requestBuddylistAdd(int cidFrom, String nameFrom) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(9);
		mplew.writeInt(cidFrom);
		mplew.writeMapleAsciiString(nameFrom);
		mplew.writeInt(cidFrom);
		mplew.writeAsciiString(StringUtil.getRightPaddedStr(nameFrom, '\0', 13));
		mplew.write(1);
		mplew.writeInt(0);//?
                mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket updateBuddyChannel(int characterid, int channel) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(0x14);
		mplew.writeInt(characterid);
		mplew.write(0);
		mplew.writeInt(channel);
		return mplew.getPacket();
	}

	public static MaplePacket updateBuddyCapacity(int capacity) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(0x15);
		mplew.write(capacity);
		return mplew.getPacket();
	}
        
        public static MaplePacket buddylistMessage(byte message) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BUDDYLIST.getValue());
		mplew.write(message);
		return mplew.getPacket();
	}

	public static MaplePacket showChair(short itemid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_CHAIR.getValue());
		mplew.write(itemid == -1 ? 0 : 1);
		if (itemid != -1) {
                    mplew.writeShort(itemid);
                }
		return mplew.getPacket();
	}

	public static MaplePacket musicChange(String song) {
		return environmentChange(song, 6);
	}

	public static MaplePacket showEffect(String effect) {
		return environmentChange(effect, 3);
	}

	public static MaplePacket playSound(String sound) {
		return environmentChange(sound, 4);
	}

	public static MaplePacket environmentChange(String env, int mode) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BOSS_ENV.getValue());
		mplew.write(mode);
		mplew.writeMapleAsciiString(env);
		return mplew.getPacket();
	}

	public static MaplePacket startMapEffect(String msg, int itemid, boolean active) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MAP_EFFECT.getValue());
		mplew.write(active ? 0 : 1);
		mplew.writeInt(itemid);
		if (active)
                    mplew.writeMapleAsciiString(msg);
		return mplew.getPacket();
	}
        
        public static MaplePacket startKiteEffect(MapleCharacter chr, int itemid, String message) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(0x88);
                mplew.writeInt(chr.getId());
                mplew.writeInt(itemid);
                mplew.writeMapleAsciiString(message);
                mplew.writeMapleAsciiString(chr.getName());
                mplew.writeShort(chr.getPosition().x);
                mplew.writeShort(chr.getPosition().y);
                return mplew.getPacket();
        }
        
        public static MaplePacket startJukebox(int itemid, String name) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(0x32);
                mplew.writeInt(itemid);
                mplew.writeMapleAsciiString(name);
                return mplew.getPacket();
        }

	public static MaplePacket removeMapEffect() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MAP_EFFECT.getValue());
		mplew.write(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}
        
        public static MaplePacket removeKiteEffect(int id) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(0x89);
		mplew.write(0);
		mplew.writeInt(id); // id or 0?
		return mplew.getPacket();
	}
        
        public static MaplePacket showBuffeffect(int cid, int skillid, int effectid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_FOREIGN_EFFECT.getValue());
		mplew.writeInt(cid); // ?
                mplew.write(effectid);
		mplew.writeInt(skillid);
                mplew.writeInt(1); // probably buff level but we don't know it and it doesn't really matter
		return mplew.getPacket();
	}

	public static MaplePacket showOwnBuffEffect(int skillid, int effectid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_ITEM_GAIN_INCHAT.getValue());
                mplew.write(effectid);
                mplew.writeInt(skillid);
                mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket getClock(int time) { // time in seconds
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CLOCK.getValue());
		mplew.write(2);
		mplew.writeInt(time);
		return mplew.getPacket();
	}

	public static MaplePacket getClockTime(int hour, int min, int sec) { // Current Time
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CLOCK.getValue());
		mplew.write(1); //Clock-Type
		mplew.write(hour);
		mplew.write(min);
		mplew.write(sec);
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet to spawn a special map object.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The MapleCharacter who spawned the object.
	 *  [USER=2000183830]para[/USER]m skill The skill used.
	 *  [USER=2000183830]para[/USER]m skillLevel The level of the skill used.
	 *  [USER=2000183830]para[/USER]m pos Where the object was spawned.
	 *  [USER=2000183830]para[/USER]m movementType Movement type of the object.
	 *  [USER=2000183830]para[/USER]m animated Animated spawn?
	 *  [USER=850422]return[/USER] The spawn packet for the map object.
	 */
	public static MaplePacket spawnSpecialMapObject(MapleCharacter chr, int skill, int skillLevel, Point pos, SummonMovementType movementType, boolean animated) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_SPECIAL_MAPOBJECT.getValue());
		mplew.writeInt(chr.getId());
		mplew.writeInt(skill);
		mplew.write(skillLevel);
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		mplew.write(0); // ?
		mplew.writeShort(0);
		mplew.write(movementType.getValue()); // 0 = don't move, 1 = follow
		mplew.write(animated ? 0 : 1);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet to remove a special map object.
	 * 
	 *  [USER=2000183830]para[/USER]m chr The MapleCharacter who removed the object.
	 *  [USER=2000183830]para[/USER]m skill The skill used to create the object.
	 *  [USER=2000183830]para[/USER]m animated Animated removal?
	 *  [USER=850422]return[/USER] The packet removing the object.
	 */
	public static MaplePacket removeSpecialMapObject(MapleCharacter chr, int skill, boolean animated) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_SPECIAL_MAPOBJECT.getValue());
		mplew.writeInt(chr.getId());
		mplew.writeInt(skill);
		mplew.write(animated ? 4 : 1); // ?
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show an EXP increase.
	 * 
	 *  [USER=2000183830]para[/USER]m gain The amount of EXP gained.
	 *  [USER=2000183830]para[/USER]m inChat In the chat box?
	 *  [USER=2000183830]para[/USER]m white White text or yellow?
	 *  [USER=850422]return[/USER] The exp gained packet.
	 */
	public static MaplePacket getShowExpGain(int gain, boolean inChat, boolean white) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(3); // 3 = exp, 4 = fame, 5 = mesos
		mplew.write(white ? 1 : 0);
		mplew.writeInt(gain);
		mplew.write(inChat ? 1 : 0);
                mplew.writeInt(0);
                mplew.writeInt(0);
                mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show a meso gain.
	 * 
	 *  [USER=2000183830]para[/USER]m gain How many mesos gained.
	 *  [USER=850422]return[/USER] The meso gain packet.
	 */
	public static MaplePacket getShowMesoGain(int gain) {
		return getShowMesoGain(gain, false);
	}

	/**
	 * Gets a packet telling the client to show a meso gain.
	 * 
	 *  [USER=2000183830]para[/USER]m gain How many mesos gained.
	 *  [USER=2000183830]para[/USER]m inChat Show in the chat window?
	 *  [USER=850422]return[/USER] The meso gain packet.
	 */
	public static MaplePacket getShowMesoGain(int gain, boolean inChat) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		if (!inChat) {
                    mplew.write(0);
                    mplew.write(1);
		} else {
                    mplew.write(5);
		}
		mplew.writeInt(gain);
		mplew.writeShort(0);
		return mplew.getPacket();
	}
        
        public static MaplePacket enableActions() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(0x12);
		mplew.write(1);
                mplew.write(0);
                mplew.write(0);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show a item gain.
	 * 
	 *  [USER=2000183830]para[/USER]m itemId The ID of the item gained.
	 *  [USER=2000183830]para[/USER]m quantity How many items gained.
	 *  [USER=850422]return[/USER] The item gain packet.
	 */
	public static MaplePacket getShowItemGain(int itemId, short quantity) {
		return getShowItemGain(itemId, quantity, false);
	}

	/**
	 * Gets a packet telling the client to show an item gain.
	 * 
	 *  [USER=2000183830]para[/USER]m itemId The ID of the item gained.
	 *  [USER=2000183830]para[/USER]m quantity The number of items gained.
	 *  [USER=2000183830]para[/USER]m inChat Show in the chat window?
	 *  [USER=850422]return[/USER] The item gain packet.
	 */
	public static MaplePacket getShowItemGain(int itemId, short quantity, boolean inChat) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		if (inChat) {
			mplew.write(SendPacketOpcode.SHOW_ITEM_GAIN_INCHAT.getValue());
			mplew.write(3);
			mplew.write(1);
			mplew.writeInt(itemId);
			mplew.writeInt(quantity);
		} else {
			mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
			mplew.writeShort(0);
			mplew.writeInt(itemId);
			mplew.writeInt(quantity);
			mplew.writeInt(0);
			mplew.writeInt(0);
		}
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client that a monster was killed.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The objectID of the killed monster.
	 *  [USER=2000183830]para[/USER]m animation Show killed animation?
	 *  [USER=850422]return[/USER] The kill monster packet.
	 */
	public static MaplePacket killMonster(int oid, boolean animation) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.KILL_MONSTER.getValue());
		mplew.writeInt(oid);
		if (animation) {
                    mplew.write(1);
		} else {
                    mplew.write(0);
		}
		return mplew.getPacket();
	}

	/**
	 * Gets a packet telling the client to show mesos coming out of a map
	 * object.
	 * 
	 *  [USER=2000183830]para[/USER]m amount The amount of mesos.
	 *  [USER=2000183830]para[/USER]m itemoid The ObjectID of the dropped mesos.
	 *  [USER=2000183830]para[/USER]m dropperoid The OID of the dropper.
	 *  [USER=2000183830]para[/USER]m ownerid The ID of the drop owner.
	 *  [USER=2000183830]para[/USER]m dropfrom Where to drop from.
	 *  [USER=2000183830]para[/USER]m dropto Where the drop lands.
	 *  [USER=2000183830]para[/USER]m mod ?
	 *  [USER=850422]return[/USER] The drop mesos packet.
	 */
	public static MaplePacket dropMesoFromMapObject(int amount, int itemoid, int dropperoid, int ownerid, Point dropfrom, Point dropto, byte mod) {
            return dropItemFromMapObjectInternal(amount, itemoid, dropperoid, ownerid, dropfrom, dropto, mod, true);
	}

	/**
	 * Gets a packet telling the client to show an item coming out of a map
	 * object.
	 * 
	 *  [USER=2000183830]para[/USER]m itemid The ID of the dropped item.
	 *  [USER=2000183830]para[/USER]m itemoid The ObjectID of the dropped item.
	 *  [USER=2000183830]para[/USER]m dropperoid The OID of the dropper.
	 *  [USER=2000183830]para[/USER]m ownerid The ID of the drop owner.
	 *  [USER=2000183830]para[/USER]m dropfrom Where to drop from.
	 *  [USER=2000183830]para[/USER]m dropto Where the drop lands.
	 *  [USER=2000183830]para[/USER]m mod ?
	 *  [USER=850422]return[/USER] The drop mesos packet.
	 */
	public static MaplePacket dropItemFromMapObject(int itemid, int itemoid, int dropperoid, int ownerid, Point dropfrom, Point dropto, byte mod) {
		return dropItemFromMapObjectInternal(itemid, itemoid, dropperoid, ownerid, dropfrom, dropto, mod, false);
	}

	/**
	 * Internal function to get a packet to tell the client to drop an item onto
	 * the map.
	 * 
	 *  [USER=2000183830]para[/USER]m itemid The ID of the item to drop.
	 *  [USER=2000183830]para[/USER]m itemoid The ObjectID of the dropped item.
	 *  [USER=2000183830]para[/USER]m dropperoid The OID of the dropper.
	 *  [USER=2000183830]para[/USER]m ownerid The ID of the drop owner.
	 *  [USER=2000183830]para[/USER]m dropfrom Where to drop from.
	 *  [USER=2000183830]para[/USER]m dropto Where the drop lands.
	 *  [USER=2000183830]para[/USER]m mod ?
	 *  [USER=2000183830]para[/USER]m mesos Is the drop mesos?
	 *  [USER=850422]return[/USER] The item drop packet.
	 */
	public static MaplePacket dropItemFromMapObjectInternal(int itemid, int itemoid, int dropperoid, int ownerid, Point dropfrom, Point dropto, byte mod, boolean mesos) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DROP_ITEM_FROM_MAPOBJECT.getValue());
		mplew.write(mod); // type
		mplew.writeInt(itemoid);
		mplew.write(mesos ? 1 : 0); // 1 = mesos, 0 =item
		mplew.writeInt(itemid);
		mplew.writeInt(ownerid);
		mplew.write(0); // drop.Type
		mplew.writeShort(dropto.x);
		mplew.writeShort(dropto.y);
                mplew.writeInt(dropperoid); 
		if (mod != 2) {
                    mplew.writeShort(dropfrom.x);
                    mplew.writeShort(dropfrom.y);
                    mplew.writeShort(0);
		}
		if (!mesos) {
                    addExpirationTime(mplew, System.currentTimeMillis(), false);
		}
                mplew.write(0); // player dropped
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket moveSummon(int cid, int summonSkill, SeekableLittleEndianAccessor slea) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_SUMMON.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(summonSkill);
		mplew.write(slea.read((int)slea.available()));
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket moveMonster(int mobid, boolean useSkill, byte skill, Point startPos, SeekableLittleEndianAccessor slea) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MOVE_MONSTER.getValue());
		mplew.writeInt(mobid);
                mplew.write(useSkill ? 1 : 0);
		mplew.write(skill);
		mplew.writeInt(0);
		mplew.write(slea.read((int)slea.available()));
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket summonAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, List<Pair<Integer, List<Integer>>> damage) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SUMMON_ATTACK.getValue());
		addAttackSummon(mplew, cid, skill, stance, damage);
		return mplew.getPacket();
	}

	public static MaplePacket closeRangeAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, List<Pair<Integer, List<Integer>>> damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CLOSE_RANGE_ATTACK.getValue());
		if (skill == 4211006) {
                    addMesoExplosion(mplew, cid, skill, stance, direction, numAttackedAndDamage, 0, damage);
		} else {
                    addAttackBody(mplew, cid, skill, stance, direction, numAttackedAndDamage, 0, damage);
		}
		return mplew.getPacket();
	}

	public static MaplePacket rangedAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, int projectile, List<Pair<Integer, List<Integer>>> damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.RANGED_ATTACK.getValue()); // 0x54 in v40 tooo
		addAttackBody(mplew, cid, skill, stance, direction, numAttackedAndDamage, projectile, damage);
		return mplew.getPacket();
	}

	public static MaplePacket magicAttack(int cid, int skill, int stance, int direction, int numAttackedAndDamage, List<Pair<Integer, List<Integer>>> damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MAGIC_ATTACK.getValue());
		addAttackBody(mplew, cid, skill, stance, direction, numAttackedAndDamage, 0, damage);
		return mplew.getPacket();
	}

	private static void addAttackBody(MaplePacketLittleEndianWriter lew, int cid, int skill, int stance, int direction, int numAttackedAndDamage, int projectile, List<Pair<Integer, List<Integer>>> damage) {
            lew.writeInt(cid);
            lew.write(numAttackedAndDamage);
            if (skill > 0) {
                lew.write(0xFF); // skill level, too low and some skills don't work (?)
                lew.writeInt(skill);
            } else {
                lew.write(0);
            }
            lew.write(stance);
            lew.write(direction);
            lew.write(0); // mastery.. 
            lew.writeInt(projectile); // 00
            for (Pair<Integer, List<Integer>> oned : damage) {
                if (oned.getRight() != null) {
                    lew.writeInt(oned.getLeft().intValue());
                    lew.write(0x06);
                    for (Integer eachd : oned.getRight()) {
                        lew.writeInt(eachd.intValue());
                    }
                }
            }
            lew.writeLong(0);
	}
        
        private static void addAttackSummon(MaplePacketLittleEndianWriter lew, int cid, int skill, int stance, List<Pair<Integer, List<Integer>>> damage) {
            lew.writeInt(cid);
            lew.writeInt(skill); // 2311006 (Bishop - Summon Dragon) : SkillID == SummonID
            lew.write(stance); // ?
            lew.write(1); // numAttacked == 1
            for (Pair<Integer, List<Integer>> oned : damage) {
                if (oned.getRight() != null) {
                    lew.writeInt(oned.getLeft().intValue());
                    lew.write(0x06);
                    for (Integer eachd : oned.getRight()) {
                        lew.writeInt(eachd.intValue());
                    }
                }
            }
            lew.writeLong(0);
	}

	private static void addMesoExplosion(LittleEndianWriter lew, int cid, int skill, int stance, int direction, int numAttackedAndDamage, int projectile, List<Pair<Integer, List<Integer>>> damage) {
		lew.writeInt(cid);
		lew.write(numAttackedAndDamage);
		lew.write(0x1E);
		lew.writeInt(skill);
		lew.write(stance);
                lew.write(direction);
                lew.write(0); // mastery
		lew.writeInt(projectile); // 00
		for (Pair<Integer, List<Integer>> oned : damage) {
                    if (oned.getRight() != null) {
                        lew.writeInt(oned.getLeft().intValue());
                        lew.write(0xFF);
                        lew.write(oned.getRight().size());
                        for (Integer eachd : oned.getRight()) {
                            lew.writeInt(eachd.intValue());
                        }
                    }
		}
	}
        
        public static MaplePacket applyMonsterStatus(int oid, Map<MonsterStatus, Integer> stats, int skill, boolean monsterSkill, int delay) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.APPLY_MONSTER_STATUS.getValue());
		mplew.writeInt(oid);
		int mask = 0;
		for (MonsterStatus stat : stats.keySet()) {
                    mask |= stat.getValue();
		}
		mplew.writeInt(mask);
		for (Integer val : stats.values()) {
			mplew.writeShort(val);
			if (monsterSkill) {
                            mplew.writeShort(skill);
                            mplew.writeShort(1);
			} else {
                            mplew.writeInt(skill);
			}
			mplew.writeShort(100); // buffTime, this needs to be coded properly -- as a workaround, we'll use 100.
		}
                
		mplew.writeShort(delay); // delay in ms
		return mplew.getPacket();
	}

	public static MaplePacket cancelMonsterStatus(int oid, Map<MonsterStatus, Integer> stats) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CANCEL_MONSTER_STATUS.getValue());
		mplew.writeInt(oid);
		int mask = 0;
		for (MonsterStatus stat : stats.keySet()) {
                    mask |= stat.getValue();
		}
		mplew.writeInt(mask);
		mplew.write(1);
		return mplew.getPacket();
	}

        public static MaplePacket getNPCShop(int sid, List<MapleShopItem> items) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.OPEN_NPC_SHOP.getValue());
		mplew.writeInt(sid);
                mplew.writeShort(items.size()); // item count
		for (MapleShopItem item : items) {
                        mplew.writeInt(item.getItemId());
                        mplew.writeInt(item.getPrice());
                        if (item.getItemId() / 10000 == 207) {
                            mplew.writeLong(5);
                        }
                        mplew.writeShort(item.getBuyable());
		}
		return mplew.getPacket();
	}
        
        /**
	 * code (8 = sell, 0 = buy, 0x20 = due to an error the trade did not happen
	 * o.o)
	 * 
	 *  [USER=2000183830]para[/USER]m code
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket confirmShopTransaction(byte code) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CONFIRM_SHOP_TRANSACTION.getValue());
		mplew.write(code); // recharge == 8?
		return mplew.getPacket();
	}
        
        /*
	 * 19 reference 00 01 00 = new while adding 01 01 00 = add from drop 00 01 01 = update count 00 01 03 = clear slot
	 * 01 01 02 = move to empty slot 01 02 03 = move and merge 01 02 01 = move and merge with rest
	 */
	public static MaplePacket addInventorySlot(MapleInventoryType type, IItem item) {
		return addInventorySlot(type, item, false);
	}

	public static MaplePacket addInventorySlot(MapleInventoryType type, IItem item, boolean fromDrop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		if (fromDrop) {
                    mplew.write(1);
		} else {
                    mplew.write(1); // 0
		}
                mplew.write(1);
                mplew.write(0);
		mplew.write(type.getType()); // iv type
		addItemInfo(mplew, item, true);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket updateInventorySlot(MapleInventoryType type, IItem item) {
            return updateInventorySlot(type, item, false);
	}

	public static MaplePacket updateInventorySlot(MapleInventoryType type, IItem item, boolean fromDrop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		if (fromDrop) {
                    mplew.write(1);
		} else {
                    mplew.write(0);
		}
		mplew.write(HexTool.getByteArrayFromHexString("01 01")); // update mode
		mplew.write(type.getType()); // iv type
		mplew.write(item.getPosition()); // slot id
		mplew.write(0); // ?
		mplew.writeShort(item.getQuantity());
		return mplew.getPacket();
	}

	public static MaplePacket moveInventoryItem(MapleInventoryType type, byte src, byte dst) {
		return moveInventoryItem(type, src, dst, (byte) -1);
	}

	public static MaplePacket moveInventoryItem(MapleInventoryType type, byte src, byte dst, byte equipIndicator) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 01 02"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		mplew.writeShort(dst);
		if (equipIndicator != -1) {
                    mplew.write(equipIndicator);
		}
		return mplew.getPacket();
	}

	public static MaplePacket moveAndMergeInventoryItem(MapleInventoryType type, byte src, byte dst, short total) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 02 03"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		mplew.write(1); // merge mode?

		mplew.write(type.getType());
		mplew.writeShort(dst);
		mplew.writeShort(total);
		return mplew.getPacket();
	}

	public static MaplePacket moveAndMergeWithRestInventoryItem(MapleInventoryType type, byte src, byte dst, short srcQ, short dstQ) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 02 01"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		mplew.writeShort(srcQ);
		mplew.write(HexTool.getByteArrayFromHexString("01"));
		mplew.write(type.getType());
		mplew.writeShort(dst);
		mplew.writeShort(dstQ);
		return mplew.getPacket();
	}

	public static MaplePacket clearInventoryItem(MapleInventoryType type, byte slot, boolean fromDrop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(fromDrop ? 1 : 0);
		mplew.write(HexTool.getByteArrayFromHexString("01 03"));
		mplew.write(type.getType());
		mplew.writeShort(slot);
		return mplew.getPacket();
	}

	public static MaplePacket removePlayerFromMap(int cid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_PLAYER_FROM_MAP.getValue());
		mplew.writeInt(cid);
		return mplew.getPacket();
	}

        /**
         * animation: 0 - expire<br/> 1 - without animation<br/> 2 - pickup<br/>
         * 4 - explode<br/> cid is ignored for 0 and 1
         * 
         *  [USER=2000183830]para[/USER]m oid
         *  [USER=2000183830]para[/USER]m animation
         *  [USER=2000183830]para[/USER]m cid
         *  [USER=850422]return[/USER]
        */
        public static MaplePacket removeItemFromMap(int oid, int animation, int cid) {
            return removeItemFromMap(oid, animation, cid, false, 0);
        }
        
	/**
	 * animation: 0 - expire<br/> 1 - without animation<br/> 2 - pickup<br/>
	 * 4 - explode<br/> cid is ignored for 0 and 1
	 * 
	 *  [USER=2000183830]para[/USER]m oid
	 *  [USER=2000183830]para[/USER]m animation
	 *  [USER=2000183830]para[/USER]m cid
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket removeItemFromMap(int oid, int animation, int cid, boolean pet, int slot) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_ITEM_FROM_MAP.getValue());
		mplew.write(animation); // expire
		mplew.writeInt(oid);
		if (animation >= 2) {
                    mplew.writeInt(cid);
                    if (pet) {
                        mplew.write(slot);
                    }
		}
		return mplew.getPacket();
	}

	public static MaplePacket updateCharLook(MapleCharacter chr) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_CHAR_LOOK.getValue());
		mplew.writeInt(chr.getId());
                mplew.writeInt(0);
		mplew.write(1);
		
		addCharLook(mplew, chr, false);
                
                mplew.writeInt(0); // Unknown: 4bytes (int)
                
                mplew.write(0); // Unknown: 8bytes (int, int)
                mplew.write(0); // Unknown: 1bytes (byte)
                
                List<MapleRing> rings = getRing(chr);
		mplew.write(rings.size());  // Rings: 16 bytes (long, long)
                for (MapleRing ring : rings) { 
                    mplew.writeInt(ring.getRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getPartnerRingId());
                    mplew.writeInt(0);
                    mplew.writeInt(ring.getItemId());
                }
		return mplew.getPacket();
	}

	public static MaplePacket dropInventoryItem(MapleInventoryType type, short src) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 01 03"));
		mplew.write(type.getType());
		mplew.writeShort(src);
		if (src < 0) {
                    mplew.write(1);
		}
		return mplew.getPacket();
	}

	public static MaplePacket dropInventoryItemUpdate(MapleInventoryType type, IItem item) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("01 01 01"));
		mplew.write(type.getType());
		mplew.writeShort(item.getPosition());
		mplew.writeShort(item.getQuantity());
		return mplew.getPacket();
	}

	public static MaplePacket damagePlayer(int skill, int monsteridfrom, int cid, int damage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DAMAGE_PLAYER.getValue());
		mplew.writeInt(cid);
                mplew.write(0xFE);
		mplew.writeInt(damage);
		mplew.writeInt(damage);
                mplew.writeInt(damage);
		mplew.writeLong(0);
                mplew.writeLong(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	private static <E extends LongValueHolder> int getIntMask(List<Pair<E, Integer>> statups) {
		int mask = 0x0;
		for (Pair<E, Integer> statup : statups) {
                    mask += statup.getLeft().getValue();
		}
		return mask;
	}

	private static <E extends LongValueHolder> int getIntMaskFromList(List<E> statups) {
		int mask = 0;
		for (E statup : statups) {
                    mask |= statup.getValue();
		}
		return mask;
	}

	/**
	 * It is important that statups is in the correct order (see decleration
	 * order in MapleBuffStat) since this method doesn't do automagical
	 * reordering.
	 * 
	 *  [USER=2000183830]para[/USER]m buffid
	 *  [USER=2000183830]para[/USER]m bufflength
	 *  [USER=2000183830]para[/USER]m statups
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket giveBuff(int buffid, int bufflength, List<Pair<MapleBuffStat, Integer>> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.GIVE_BUFF.getValue());
		int mask = getIntMask(statups);
		mplew.writeInt(mask); // :|
		for (Pair<MapleBuffStat, Integer> statup : statups) {
                    mplew.writeShort(statup.getRight().shortValue());
                    mplew.writeInt(buffid);
                    mplew.writeShort(bufflength);
		}
                mplew.writeLong(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket giveForeignBuff(int cid, List<Pair<MapleBuffStat, Integer>> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.GIVE_FOREIGN_BUFF.getValue());
		mplew.writeInt(cid);
		int mask = getIntMask(statups);
		mplew.writeInt(mask);
		for (Pair<MapleBuffStat, Integer> statup : statups) {
                    mplew.writeShort(statup.getRight().byteValue());
		}
		mplew.writeShort(0); // same as give_buff
		return mplew.getPacket();
	}

	public static MaplePacket cancelForeignBuff(int cid, List<MapleBuffStat> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CANCEL_FOREIGN_BUFF.getValue());
		mplew.writeInt(cid);
		mplew.writeInt(getIntMaskFromList(statups));
		return mplew.getPacket();
	}

	public static MaplePacket cancelBuff(List<MapleBuffStat> statups) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.CANCEL_BUFF.getValue());
		mplew.writeInt(getIntMaskFromList(statups));
		mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalk(int npc, String talk, boolean prev, boolean next, boolean simple, boolean yesno) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
                mplew.write((simple ? 4 : yesno ? 1 : 0));
		mplew.writeMapleAsciiString(talk);
                if ((prev || next) && (!simple || !yesno)) { // dont send next,previous,or ok with yesno/simple else crash
                    mplew.write(prev ? 1 : 0);
                    mplew.write(next ? 1 : 0);
                }
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalkStyle(int npc, String talk, int styles[]) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
		mplew.write(5);
		mplew.writeMapleAsciiString(talk);
		mplew.write(styles.length);
		for (int i = 0; i < styles.length; i++) {
                    mplew.writeInt(styles[i]);
		}
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalkNum(int npc, String talk, int def, int min, int max) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
		mplew.write(3);
		mplew.writeMapleAsciiString(talk);
		mplew.writeInt(def);
		mplew.writeInt(min);
		mplew.writeInt(max);
		mplew.writeLong(0); // prob int but w/e
		return mplew.getPacket();
	}

	public static MaplePacket getNPCTalkText(int npc, String talk) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.NPC_TALK.getValue());
		mplew.write(4); // ?
		mplew.writeInt(npc);
		mplew.write(2);
		mplew.writeMapleAsciiString(talk);
                mplew.writeMapleAsciiString(""); // def
		mplew.writeShort(0); // min 
                mplew.writeShort(0); // max
		mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket showLevelup(int cid) {
		return showForeignEffect(cid, 0);
	}

	public static MaplePacket showJobChange(int cid) {
		return showForeignEffect(cid, 8);
	}

	public static MaplePacket showForeignEffect(int cid, int effect) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_FOREIGN_EFFECT.getValue());
		mplew.writeInt(cid); // ?
		mplew.write(effect);
                mplew.writeLong(0);
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket updateSkill(int skillid, int level, int masterlevel) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_SKILLS.getValue());
		mplew.write(1);
		mplew.writeShort(1);
		mplew.writeInt(skillid);
		mplew.writeInt(level);
		mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket updateQuestMobKills(MapleQuestStatus status) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_QUEST_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(status.getQuest().getId());
		mplew.write(1);
		String killStr = "";
		for (int kills : status.getMobKills().values()) {
			killStr += StringUtil.getLeftPaddedStr(String.valueOf(kills), '0', 3);
		}
		mplew.writeMapleAsciiString(killStr);
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	public static MaplePacket getShowQuestCompletion(int id) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_QUEST_COMPLETION.getValue());
		mplew.writeShort(id);
		return mplew.getPacket();
	}
        
        public static MaplePacket getWhisper(String sender, int channel, String text) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(0x12);
		mplew.writeMapleAsciiString(sender);
		mplew.write(channel - 1); // I guess this is the channel
		mplew.writeMapleAsciiString(text);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m target name of the target character
	 *  [USER=2000183830]para[/USER]m reply error code: 0x0 = cannot find char, 0x1 = success
	 *  [USER=850422]return[/USER] the MaplePacket
	 */
	public static MaplePacket getWhisperReply(String target, byte reply) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(0x0A); // whisper?
		mplew.writeMapleAsciiString(target);
		mplew.write(reply);
		return mplew.getPacket();
	}

	public static MaplePacket getFindReplyWithMap(String target, int mapid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(9);
		mplew.writeMapleAsciiString(target);
		mplew.write(1);
		mplew.writeInt(mapid);
		mplew.writeInt(0); // ?? official doesn't send zeros here but whatever
		return mplew.getPacket();
	}

	public static MaplePacket getFindReply(String target, int channel) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.WHISPER.getValue());
		mplew.write(9);
		mplew.writeMapleAsciiString(target);
		mplew.write(3);
		mplew.writeInt(channel - 1);
                mplew.writeInt(0);
		return mplew.getPacket();
	}

	public static MaplePacket getInventoryFull() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(1);
		mplew.write(0);
                mplew.write(0);//?
		return mplew.getPacket();
	}
        
        public static MaplePacket messengerInvite(String from, int messengerid) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(3);
            mplew.writeMapleAsciiString(from);
            mplew.write(0);
            mplew.writeInt(messengerid);
            mplew.write(0);
            return mplew.getPacket();
        }

        public static MaplePacket addMessengerPlayer(String from, MapleCharacter chr, int position, int channel) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(0);
            mplew.write(position);
            addCharLook(mplew, chr, true);
            mplew.writeMapleAsciiString(from);
            mplew.write(channel);
            mplew.write(0);
            return mplew.getPacket();
        }

        public static MaplePacket removeMessengerPlayer(int position) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(2);
            mplew.write(position);
            return mplew.getPacket();
        }

        public static MaplePacket updateMessengerPlayer(String from, MapleCharacter chr, int position, int channel) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(7);
            mplew.write(position);
            addCharLook(mplew, chr, true);
            mplew.writeMapleAsciiString(from);
            mplew.write(channel);
            mplew.write(0);
            return mplew.getPacket();
        }

        public static MaplePacket joinMessenger(int position) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(1);
            mplew.write(position);
            return mplew.getPacket();
        }

        public static MaplePacket messengerChat(String text) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(6);
            mplew.writeMapleAsciiString(text);
            return mplew.getPacket();
        }

        public static MaplePacket messengerNote(String text, int mode, int mode2) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MESSENGER.getValue());
            mplew.write(mode);
            mplew.writeMapleAsciiString(text);
            mplew.write(mode2);
            return mplew.getPacket();
        }
        
        // CASH SHOP PACKETS -- Credits to WvsBeta and OdinMS.
        public static MaplePacket sendWishList(int characterid, boolean update) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            if (update) {
                mplew.write(0x20);
            } else {
                mplew.write(0x1E);
            }
            Connection con = DatabaseConnection.getConnection();
            int i = 10;

            try {
                PreparedStatement ps = con.prepareStatement("SELECT sn FROM wishlist WHERE charid = ? LIMIT 10");
                ps.setInt(1, characterid);
                ResultSet rs = ps.executeQuery();
                while (rs.next()) {
                    mplew.writeInt(rs.getInt("sn"));
                    i--;
                }
                rs.close();
                ps.close();
            } catch (SQLException se) {
                log.info("Error getting wishlist data:", se);
            }
            while (i > 0) {
                mplew.writeInt(0);
                i--;
            }
            return mplew.getPacket();
        }

        public static MaplePacket warpCS(MapleClient c) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            MapleCharacter chr = c.getPlayer();
            mplew.write(0x27);
            
            mplew.writeShort(-1); //flags
            mplew.writeInt(chr.getId());
            mplew.writeAsciiString(chr.getName(), 13);
            mplew.write(chr.getGender());
            mplew.write(chr.getSkinColor().getId());
            mplew.writeInt(chr.getFace());
            mplew.writeInt(chr.getHair());

            mplew.writeLong(0); // pet serial number

            mplew.write(chr.getLevel());
            mplew.writeShort(chr.getJob().getId());
            mplew.writeShort(chr.getStr());
            mplew.writeShort(chr.getDex());
            mplew.writeShort(chr.getInt());
            mplew.writeShort(chr.getLuk());
            mplew.writeShort(chr.getHp());
            mplew.writeShort(chr.getMaxHp());
            mplew.writeShort(chr.getMp());
            mplew.writeShort(chr.getMaxMp());
            mplew.writeShort(chr.getRemainingAp());
            mplew.writeShort(chr.getRemainingSp());
            mplew.writeInt(chr.getExp());
            mplew.writeShort(chr.getFame());

            mplew.writeInt(chr.getMapId());
            mplew.write(chr.getInitialSpawnpoint());

            mplew.writeLong(0); // who knows
            mplew.writeInt(13);
            mplew.writeInt(12);

            mplew.write(chr.getBuddylist().getCapacity());
            mplew.writeInt(chr.getMeso());

            MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED);
            Collection<IItem> equippedList = iv.list();
            Item[] equipped = new Item[17];
            Item[] equippedCash = new Item[17];
            for (IItem item : equippedList) {
                byte pos = item.getPosition();
                if (pos < 0) {
                    pos = (byte)Math.abs(pos);
                    if (pos > 100) {
                        equippedCash[(byte)(pos - 100)] = (Item)item;
                    } else {
                        equipped[(byte)pos] = (Item)item;
                    }
                }
                if (pos < 0) {
                    if (pos < -100) {
                        pos += 100;
                        pos = (byte)Math.abs(pos);
                        equippedCash[(byte)(pos - 100)] = (Item)item;
                    } else {
                        pos = (byte)Math.abs(pos);
                        equipped[(byte)pos] = (Item)item;
                    }
                }
            }
            for (Item item : equipped) {
                if (item != null) {
                    addItemInfo(mplew, item, false);
                }
            }
            mplew.write(0);
            for (Item item : equippedCash) {
                if (item != null) {
                    addItemInfo(mplew, item, false);
                }
            }
            mplew.write(0);
            for (byte i = 1; i < 6; i++) {
                mplew.write(100); // TODO: custom max slots for GMS-like cash shop purposes
                iv = chr.getInventory(MapleInventoryType.getByType((byte)i));
                for (IItem item : iv.list()) {
                    if (item != null && item.getPosition() > 0) {
                        addItemInfo(mplew, item, false);
                    }
                }
                mplew.write(0);
            }

            Map<ISkill, MapleCharacter.SkillEntry> skills = chr.getSkills();
            mplew.writeShort(skills.size());
            for (Entry<ISkill, MapleCharacter.SkillEntry> skill : skills.entrySet()) {
                mplew.writeInt(skill.getKey().getId());
                mplew.writeInt((byte)skill.getValue().skillevel);
            }

            mplew.writeShort(0); // Quests
            mplew.writeShort(0); // Mini Games
            mplew.writeShort(0); // Rings
            
            for (int i = 0; i < 5; i++) { // Teleport rocks isn't short, it's 4 ints T_T
                mplew.writeInt(910000000 + i);
            }

            mplew.write(1); // 0 = beta or someshit lol
            mplew.writeMapleAsciiString(c.getAccountName()); 
            
            mplew.writeShort(0); // wishlist i think.. o.o
            
            //int[] itemz = new int[]{10000281, 10000282, 10000283, 10000284, 10000285};
            for (byte i = 1; i <= 8; i++) {
                for (byte j = 0; j <= 1; j++) {
                        mplew.writeInt(10000281); // best items, these are just first id's in Commodity
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000282);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000283);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000284);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                        
                        mplew.writeInt(10000285);
                        mplew.writeInt(i);
                        mplew.writeInt(j);
                }
            }
            
            mplew.writeShort(5); // Stock 
            mplew.writeInt(-1); // 1 = Sold Out, 2 = Not Sold      
            mplew.writeInt(20900028);
            mplew.writeInt(0); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900027);
            mplew.writeInt(2); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900026);
            mplew.writeInt(4); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900026);
            mplew.writeInt(5); // 1 = Sold Out, 2 = Not Sold
            mplew.writeInt(20900026);
                    
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            mplew.writeLong(0);
            
            return mplew.getPacket();
        }
        
        public static MaplePacket showBoughtCSItem(MapleCharacter chr, CashItemInfo item) {
            MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x22);
            
            mplew.writeLong(item.hashCode()); // Cash ID
            mplew.writeInt(chr.getId()); // Player ID
            mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01"));
            mplew.writeInt(item.getId());
            mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01"));
            mplew.writeShort(item.getCount()); // quantity is always 1?
            mplew.writeAsciiString(ii.getName(item.getId()), 13);
            mplew.writeLong(0); // Expiration.. items never expire so we shouldnt have a problem.
            mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01"));
            return mplew.getPacket();
        }
        
        public static MaplePacket sendItemInventory(CashItemInfo item, short slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x2F);
            
            mplew.writeShort(slot);
            mplew.write(2);//ServerConstants.getInventoryType(item.getId()).getType());
            Item i = new Item(2090000, (byte)slot, (short)1);
            addItemInfo(mplew, i, false, true);
            return mplew.getPacket();
        }
        
        public static MaplePacket showNXMapleTokens(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBA);
            mplew.writeInt(chr.getCSPoints(1)); // Paypal/PayByCash NX
            mplew.writeInt(chr.getCSPoints(2)); // Maple Points
            return mplew.getPacket();
        }
        
        // TODO, the players inventory of the items bought
        public static MaplePacket sendItems(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x1C);
            
            mplew.write(0);
            // for each byte 
            mplew.writeShort(10);

            return mplew.getPacket();
        }
        
        // TODO: Gifting
        public static MaplePacket showGifts(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0xBB);
            mplew.write(0x1E);
            mplew.writeAsciiString("fasfsa", 13);
            mplew.writeAsciiString("asfas", 73);
            return mplew.getPacket();
        }
        
        // END OF CASH SHOP PACKETS
        
        public static MaplePacket getStorage(int npcId, byte slots, Collection<IItem> items, int meso) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.OPEN_STORAGE.getValue());
		mplew.writeInt(npcId);
		mplew.write(slots);
		mplew.writeShort(0x7E);
		mplew.writeInt(meso);
                byte size1 = 0, size2 = 0, size3 = 0, size4 = 0, size5 = 0;
                for (IItem item : items) {
                    switch(item.getItemId() / 1000000) {
                        case 1: size1++; break;
                        case 2: size2++; break;
                        case 3: size3++; break;
                        case 4: size4++; break;
                        case 5: size5++; break;
                        default: System.out.println("Unknown type found!"); break;
                    }
                }
                mplew.write(size1);
		for (IItem item : items) { // Equip
                    if (item.getItemId() / 1000000 == 1) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
		mplew.write(size2); 
		for (IItem item : items) { // Use
                    if (item.getItemId() / 1000000 == 2) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size3); // Setup
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 3) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size4); // Etc
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 4) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size5); // Pets
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 5) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket getStorageFull() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1));
		mplew.write(0x0C);
		return mplew.getPacket();
	}

	public static MaplePacket mesoStorage(byte slots, int meso) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1)); // 0xA8
		mplew.write(0x0E);
		mplew.write(slots);
		mplew.writeShort(2);
		mplew.writeInt(meso);
		return mplew.getPacket();
	}

	public static MaplePacket storeStorage(byte slots, MapleInventoryType type, Collection<IItem> items) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1));
		mplew.write(0x09);
		mplew.write(slots);
		mplew.writeShort(type.getBitfieldEncoding());
		byte size1 = 0, size2 = 0, size3 = 0, size4 = 0, size5 = 0;
                for (IItem item : items) {
                    switch(item.getItemId() / 1000000) {
                        case 1: size1++; break;
                        case 2: size2++; break;
                        case 3: size3++; break;
                        case 4: size4++; break;
                        case 5: size5++; break;
                        default: System.out.println("Unknown type found!"); break;
                    }
                }
                mplew.write(size1);
		for (IItem item : items) { // Equip
                    if (item.getItemId() / 1000000 == 1) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
		mplew.write(size2); 
		for (IItem item : items) { // Use
                    if (item.getItemId() / 1000000 == 2) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size3); // Setup
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 3) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size4); // Etc
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 4) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size5); // Pets
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 5) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.writeLong(0);
		return mplew.getPacket();
	}

	public static MaplePacket takeOutStorage(byte slots, MapleInventoryType type, Collection<IItem> items) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write((SendPacketOpcode.OPEN_STORAGE.getValue() + 1));
		mplew.write(0x07);
		mplew.write(slots);
		mplew.writeShort(type.getBitfieldEncoding());
		byte size1 = 0, size2 = 0, size3 = 0, size4 = 0, size5 = 0;
                for (IItem item : items) {
                    switch(item.getItemId() / 1000000) {
                        case 1: size1++; break;
                        case 2: size2++; break;
                        case 3: size3++; break;
                        case 4: size4++; break;
                        case 5: size5++; break;
                        default: System.out.println("Unknown type found!"); break;
                    }
                }
                mplew.write(size1);
		for (IItem item : items) { // Equip
                    if (item.getItemId() / 1000000 == 1) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
		mplew.write(size2); 
		for (IItem item : items) { // Use
                    if (item.getItemId() / 1000000 == 2) {
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size3); // Setup
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 3) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size4); // Etc
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 4) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.write(size5); // Pets
		for (IItem item : items) {
                    if (item.getItemId() / 1000000 == 5) { 
                        addItemInfo(mplew, item, false, true);
                    }
		}
                mplew.writeLong(0);
		return mplew.getPacket();
	}
        
        public static MaplePacket spawnMist(int oid, int ownerCid, int skillId, Rectangle mistPosition) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_MIST.getValue());
		mplew.writeInt(oid); // maybe this should actually be the "mistid" - seems to always be 1 with only one mist in the map...
		mplew.write(0); // from mob? o.O
		mplew.writeInt(skillId);
		mplew.write(1); // skill level
		mplew.writeShort(7); // delay i guess
		mplew.writeInt(mistPosition.x); // left position
		mplew.writeInt(mistPosition.y); // bottom position
		mplew.writeInt(mistPosition.x + mistPosition.width); // left position
		mplew.writeInt(mistPosition.y + mistPosition.height); // upper position
		return mplew.getPacket();
	}

	public static MaplePacket removeMist(int oid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REMOVE_MIST.getValue());
		mplew.writeInt(oid);
		return mplew.getPacket();
	}
        
        // is there a way to spawn reactors non-animated?
	public static MaplePacket spawnReactor(MapleReactor reactor) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		Point pos = reactor.getPosition();
		mplew.write(SendPacketOpcode.REACTOR_SPAWN.getValue());
		mplew.writeShort(reactor.getObjectId());
                mplew.write(reactor.getState());
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		mplew.write(2); // Rx0?
                mplew.write(2); // Rx1?
		return mplew.getPacket();
	}
	
	public static MaplePacket triggerReactor(MapleReactor reactor, int stance) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		Point pos = reactor.getPosition();
		mplew.write(SendPacketOpcode.REACTOR_HIT.getValue());
		mplew.writeShort(reactor.getObjectId());
		mplew.write(reactor.getState());
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		mplew.writeShort(stance);
		mplew.write(0);
		//frame delay, set to 5 since there doesn't appear to be a fixed formula for it
		mplew.write(5);
		return mplew.getPacket();
	}
	
	public static MaplePacket destroyReactor(MapleReactor reactor) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.REACTOR_DESTROY.getValue());
		mplew.writeShort(reactor.getObjectId());
		return mplew.getPacket();
	}
        
        /**
	 * Gets a packet to spawn a portal.
	 * 
	 *  [USER=2000183830]para[/USER]m townId The ID of the town the portal goes to.
	 *  [USER=2000183830]para[/USER]m targetId The ID of the target.
	 *  [USER=2000183830]para[/USER]m pos Where to put the portal.
	 *  [USER=850422]return[/USER] The portal spawn packet.
	 */
	public static MaplePacket spawnPortal(int townId, int targetId, Point pos) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_PORTAL.getValue());
		mplew.writeInt(townId);
		mplew.writeInt(targetId);
		if (pos != null) {
                    mplew.writeShort(pos.x);
                    mplew.writeShort(pos.y);
		}
		return mplew.getPacket();
	}

	/**
	 * Gets a packet to spawn a door.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The door's object ID.
	 *  [USER=2000183830]para[/USER]m pos The position of the door.
	 *  [USER=2000183830]para[/USER]m town
	 *  [USER=850422]return[/USER] The remove door packet.
	 */
	public static MaplePacket spawnDoor(int oid, Point pos, boolean town) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SPAWN_DOOR.getValue());
		mplew.write(town ? 1 : 0);
		mplew.writeInt(oid);//?
		mplew.writeShort(pos.x);
		mplew.writeShort(pos.y);
		return mplew.getPacket();
	}

	/**
	 * Gets a packet to remove a door.
	 * 
	 *  [USER=2000183830]para[/USER]m oid The door's ID.
	 *  [USER=2000183830]para[/USER]m town
	 *  [USER=850422]return[/USER] The remove door packet.
	 */
	public static MaplePacket removeDoor(int oid, boolean town) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		if (town) {
			mplew.write(SendPacketOpcode.SPAWN_PORTAL.getValue());
			mplew.writeInt(999999999);
			mplew.writeInt(999999999);
		} else {
			mplew.write(SendPacketOpcode.REMOVE_DOOR.getValue());
			mplew.write(1); // or is it 0? lol
			mplew.writeInt(oid);
		}
		return mplew.getPacket();
	}
        
        /**
         * 
         *  [USER=2000183830]para[/USER]m oid object id of boss
         *  [USER=2000183830]para[/USER]m currHP current hp of boss
         *  [USER=2000183830]para[/USER]m maxHP max hp of boss
         *  [USER=2000183830]para[/USER]m tagColor the color of the hpbar
         *  [USER=2000183830]para[/USER]m tagBgColor the color of the background for the hpbar
         *  [USER=850422]return[/USER] 
         */
	public static MaplePacket showBossHP(int oid, int currHP, int maxHP, byte tagColor, byte tagBgColor) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.BOSS_ENV.getValue());
		mplew.write(5);
		mplew.writeInt(currHP);
		mplew.writeInt(maxHP);
		mplew.writeInt(tagColor);
		mplew.writeInt(tagBgColor);
		return mplew.getPacket();
	}
        
        public static MaplePacket updatePet(MaplePet pet, boolean alive) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
            mplew.write(0);
            mplew.write(2);
            mplew.write(3);
            mplew.write(5);
            mplew.write(pet.getPosition());
            mplew.writeShort(0);
            mplew.write(5);
            mplew.write(pet.getPosition());
            mplew.write(0);
            mplew.writeInt(pet.getItemId());
            mplew.write(1);
            mplew.writeLong(pet.getUniqueId());
            mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
            String petname = pet.getName();
            mplew.writeAsciiString(petname, 13);
            mplew.write(pet.getLevel());
            mplew.writeShort(pet.getCloseness());
            mplew.write(pet.getFullness());
            if (alive) {
                mplew.writeLong(getKoreanTimestamp((long) (System.currentTimeMillis() * 1.2)));
                mplew.writeInt(0);
            } else {
                mplew.writeLong(0);
            }
            return mplew.getPacket();
        }

        public static MaplePacket showPet(MapleCharacter chr, MaplePet pet, boolean remove) {
            return showPet(chr, pet, remove, false);
        }

        public static MaplePacket showPet(MapleCharacter chr, MaplePet pet, boolean remove, boolean hunger) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SPAWN_PET.getValue());
            mplew.writeInt(chr.getId());
            if (remove) {
                mplew.write(0);
                mplew.write(hunger ? 1 : 0);
            } else {
                mplew.write(1);
                mplew.writeInt(pet.getItemId());
                mplew.writeMapleAsciiString(pet.getName());
                mplew.writeLong(pet.getUniqueId());
                mplew.writeShort(pet.getPos().x);
                mplew.writeShort(pet.getPos().y - 12);
                mplew.write(pet.getStance());
                mplew.writeInt(pet.getFh());
            }
            return mplew.getPacket();
        }

        public static MaplePacket movePet(int cid, SeekableLittleEndianAccessor slea) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.MOVE_PET.getValue());
            mplew.writeInt(cid);
            mplew.write(slea.read((int)slea.available()));
            return mplew.getPacket();
        }

        public static MaplePacket petChat(int cid, int un, String text, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PET_CHAT.getValue());
            mplew.writeInt(cid);
            mplew.write(slot);
            mplew.write(un);
            mplew.writeMapleAsciiString(text);
            return mplew.getPacket();
        }

        public static MaplePacket commandResponse(int cid, byte command, boolean success, boolean food) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PET_COMMAND.getValue());
            mplew.writeInt(cid);
            mplew.write(command == 1 ? 1 : 0);
            mplew.write(command);
            mplew.write(success ? 1 : 0);
            return mplew.getPacket();
        }

        public static MaplePacket showOwnPetLevelUp(int index) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SHOW_ITEM_GAIN_INCHAT.getValue());
            mplew.write(4);
            mplew.write(0);
            mplew.write(index); // Pet Index? o.O
            return mplew.getPacket();
        }

        public static MaplePacket showPetLevelUp(MapleCharacter chr, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.SHOW_FOREIGN_EFFECT.getValue());
            mplew.write(4);
            mplew.write(0);
            mplew.write(slot);
            return mplew.getPacket();
        }

        public static MaplePacket changePetName(MapleCharacter chr, String newname, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PET_NAMECHANGE.getValue());
            mplew.writeInt(chr.getId());
            mplew.writeMapleAsciiString(newname);
            mplew.write(slot); // Pet index o.O
            return mplew.getPacket();
        }

        public static MaplePacket petStatUpdate(MapleCharacter chr) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.UPDATE_STATS.getValue());
            int mask = 0;
            mask |= MapleStat.PET.getValue();
            mplew.write(1);
            mplew.writeInt(mask);
            MaplePet pet = chr.getPet(0);
            if (pet != null) {
                mplew.writeInt(pet.getUniqueId());
                mplew.writeInt(0);
            } else {
                mplew.writeLong(0);
            }
            mplew.write(1);
            return mplew.getPacket();
        }
        
        /**
	 * state 0 = del ok state 12 = invalid bday
	 * 
	 *  [USER=2000183830]para[/USER]m cid
	 *  [USER=2000183830]para[/USER]m state
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket deleteCharResponse(int cid, int state) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.DELETE_CHAR_RESPONSE.getValue());
		mplew.writeInt(cid);
		mplew.write(state);
		return mplew.getPacket();
	}
        
        public static MaplePacket refreshVIPRockMapList(int[] maps, byte type) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(0x1C);
                mplew.write(type);
                for (int map : maps) {
                    mplew.writeInt(map);
                }
                return mplew.getPacket();
        }
        
        public static MaplePacket showNotes(ResultSet notes, int count) throws SQLException {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x1B); // OnMemoResult
            mplew.write(1);
            mplew.write(count);
            for (int i = 0; i < count; i++) {
                mplew.writeInt(notes.getInt("id"));
                mplew.writeMapleAsciiString(notes.getString("from"));
                mplew.writeMapleAsciiString(notes.getString("message"));
                mplew.writeLong(getKoreanTimestamp(notes.getLong("timestamp")));
                mplew.write(0);
                notes.next();
            }
            return mplew.getPacket();
        }
        
        /**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m shop
	 *  [USER=2000183830]para[/USER]m owner
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket getPlayerShop(MapleClient c, MaplePlayerShop shop, boolean owner) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("05 04 04"));
		mplew.write(owner ? 0 : 1);
		mplew.write(0);
		addCharLook(mplew, shop.getOwner(), true);
		mplew.writeMapleAsciiString(shop.getOwner().getName());
		MapleCharacter[] visitors = shop.getVisitors();
		for (int i = 0; i < visitors.length; i++) {
                    if (visitors[i] != null) {
                        mplew.write(i + 1);
                        addCharLook(mplew, visitors[i], true);
                        mplew.writeMapleAsciiString(visitors[i].getName());
                    }
		}
		mplew.write(0xFF);
		mplew.writeMapleAsciiString(shop.getDescription());
		List<MaplePlayerShopItem> items = shop.getItems();
		mplew.write(0x10);
		mplew.write(items.size());
		for (MaplePlayerShopItem item : items) {
                    mplew.writeShort(item.getBundles());
                    mplew.writeShort(item.getItem().getQuantity());
                    mplew.writeInt(item.getPrice());
                    addItemInfo(mplew, item.getItem(), true);
		}
		return mplew.getPacket();
	}

	public static MaplePacket getTradeStart(MapleClient c, MapleTrade trade, byte number) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("05 03 02"));
		mplew.write(number);
		if (number == 1) {
                    mplew.write(0);
                    addCharLook(mplew, trade.getPartner().getChr(), true);
                    mplew.writeMapleAsciiString(trade.getPartner().getChr().getName());
		}
		mplew.write(number);
		addCharLook(mplew, c.getPlayer(), true);
		mplew.writeMapleAsciiString(c.getPlayer().getName());
		mplew.write(0xFF);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeConfirmation() {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xF);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeCompletion(byte number, boolean success) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xA);
		mplew.write(number);
		mplew.write(success ? 5 : 6);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeCancel(byte number) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xA);
		mplew.write(number);
		mplew.write(2);
		return mplew.getPacket();
	}
        
        public static MaplePacket getTradePartnerAdd(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("04 01"));
		addCharLook(mplew, c, true);
		mplew.writeMapleAsciiString(c.getName());
		return mplew.getPacket();
	}

	public static MaplePacket getTradeInvite(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("02 03"));
		mplew.writeMapleAsciiString(c.getName());
		mplew.write(HexTool.getByteArrayFromHexString("B7 50 00 00")); // the room id, int
		return mplew.getPacket();
	}

	public static MaplePacket getTradeMesoSet(byte number, int meso) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xE);
		mplew.write(number);
		mplew.writeInt(meso);
		return mplew.getPacket();
	}

	public static MaplePacket getTradeItemAdd(byte number, IItem item) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0xD);
                mplew.write(number);
                mplew.write(item.getPosition());
                mplew.write(item.getType()); // 1 equip, 2 use(?), 5 pet
		addItemInfo(mplew, item, false, true);
		return mplew.getPacket();
	}
        
        public static MaplePacket sendInteractionBox(MapleCharacter c) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.UPDATE_CHAR_BOX.getValue()); // 0x40
                mplew.writeInt(c.getId());
                addAnnounceBox(mplew, c.getInteraction());
                return mplew.getPacket();
        }

        public static MaplePacket removeCharBox(MapleCharacter c) {
                MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
                mplew.write(SendPacketOpcode.UPDATE_CHAR_BOX.getValue());
                mplew.writeInt(c.getId());
                mplew.writeInt(0);
                return mplew.getPacket();
        }

	public static MaplePacket getPlayerShopChat(MapleCharacter c, String chat, boolean owner) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("06 08"));
		mplew.write(owner ? 0 : 1);
		mplew.writeMapleAsciiString(c.getName() + " : " + chat);
		return mplew.getPacket();
	}
        
        public static MaplePacket itemEffect(int characterid, int itemid) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_ITEM_EFFECT.getValue());
		mplew.writeInt(characterid);
		mplew.writeInt(itemid);
		return mplew.getPacket();
	}
        
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////
        ////////////////////////////////////////////////// ALL OUTDATED PACKETS //////////////////////////////////////////////////

	public static MaplePacket getPermBan(byte reason) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.LOGIN_STATUS.getValue());
		mplew.writeShort(0x02); // Account is banned
		mplew.write(0x0);
		mplew.write(reason);
		mplew.write(HexTool.getByteArrayFromHexString("01 01 01 01 00"));
		return mplew.getPacket();
	}

	public static MaplePacket getTempBan(long timestampTill, byte reason) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.LOGIN_STATUS.getValue());
		mplew.write(0x02);
		mplew.write(HexTool.getByteArrayFromHexString("00 00 00 00 00")); // Account is banned
		mplew.write(reason);
		mplew.writeLong(timestampTill); // Tempban date is handled as a 64-bit long, number of 100NS intervals since 1/1/1601. Lulz.
		return mplew.getPacket();
	}

	/**
	 * Adds a quest info entry for a character to an existing
	 * MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWrite instance to write the stats
	 *            to.
	 *  [USER=2000183830]para[/USER]m chr The character to add quest info about.
	 */
	private static void addQuestInfo(MaplePacketLittleEndianWriter mplew, MapleCharacter chr) {
		List<MapleQuestStatus> started = chr.getStartedQuests();
		mplew.writeShort(started.size());
		for (MapleQuestStatus q : started) {
			mplew.writeInt(q.getQuest().getId());
		}
		List<MapleQuestStatus> completed = chr.getCompletedQuests();
		mplew.writeShort(completed.size());
		for (MapleQuestStatus q : completed) {
			mplew.writeShort(q.getQuest().getId());
			mplew.writeInt(KoreanDateUtil.getQuestTimestamp(q.getCompletionTime()));
			mplew.writeInt(KoreanDateUtil.getQuestTimestamp(q.getCompletionTime()));
		}
	}

	/**
	 * Adds expiration time info to an existing MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to write to.
	 *  [USER=2000183830]para[/USER]m time The expiration time.
	 *  [USER=2000183830]para[/USER]m showexpirationtime Show the expiration time?
	 */
	private static void addExpirationTime(MaplePacketLittleEndianWriter mplew, long time, boolean showexpirationtime) {
		mplew.writeInt(KoreanDateUtil.getItemTimestamp(time));
		mplew.write(showexpirationtime ? 1 : 2);
	}
        
        public static MaplePacket scrolledItem(IItem scroll, IItem item, boolean destroyed) {
		// 18 00 01 02 03 02 08 00 03 01 F7 FF 01
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.MODIFY_INVENTORY_ITEM.getValue());
		mplew.write(1); // fromdrop always true
		if (destroyed) {
                    mplew.write(2);
		} else {
                    mplew.write(3); // is this 1? xD
		}
		if (scroll.getQuantity() > 0) {
                    mplew.write(1);
		} else {
                    mplew.write(3);
		}
		mplew.write(MapleInventoryType.USE.getType());
		mplew.writeShort(scroll.getPosition());
		if (scroll.getQuantity() > 0) {
                    mplew.writeShort(scroll.getQuantity());
		}
		mplew.write(3);
		if (!destroyed) {
                    mplew.write(MapleInventoryType.EQUIP.getType());
                    mplew.writeShort(item.getPosition());
                    mplew.write(0);
		}
		mplew.write(MapleInventoryType.EQUIP.getType());
		mplew.writeShort(item.getPosition());
		if (!destroyed) {
                    addItemInfo(mplew, item, true);
		}
		mplew.write(1);
		return mplew.getPacket();
	}

	public static MaplePacket getScrollEffect(ScrollResult scrollSuccess) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(6);
		switch (scrollSuccess) {
                    case SUCCESS:
                        mplew.write(2); // 1: Nothing | 2: Nothing? wtf..
                        break;
                    case FAIL:
                        mplew.write(0);
                        break;
                    case CURSE:
                        mplew.write(1);
                        break;
                    default:
                        throw new IllegalArgumentException("effects don't even work in v40 beta and we found a new one? wut");
		}
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket startQuest(MapleCharacter c, short quest) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(quest);
		mplew.writeShort(1);
		mplew.write(0);
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket forfeitQuest(MapleCharacter c, short quest) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_QUEST_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(quest);
		mplew.writeShort(0);
		mplew.write(0);
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket completeQuest(MapleCharacter c, short quest) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.UPDATE_QUEST_INFO.getValue());
		mplew.write(1);
		mplew.writeShort(quest);
		mplew.write(HexTool.getByteArrayFromHexString("02 A0 67 B9 DA 69 3A C8 01"));
		mplew.writeInt(0);
		mplew.writeInt(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m c
	 *  [USER=2000183830]para[/USER]m quest
	 *  [USER=2000183830]para[/USER]m npc
	 *  [USER=2000183830]para[/USER]m progress
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket updateQuestInfo(MapleCharacter c, short quest, int npc, byte progress) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(0x72); 
		mplew.write(progress);
		mplew.writeShort(quest);
		mplew.writeInt(npc);
		mplew.writeInt(0);
		return mplew.getPacket();
	}
        
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////
        ////////////////////////////// Working but needs fixing MiniGame packets //////////////////////////////

	/**
	 * Adds a announcement box to an existing MaplePacketLittleEndianWriter.
	 * 
	 *  [USER=2000183830]para[/USER]m mplew The MaplePacketLittleEndianWriter to add an announcement box
	 *            to.
	 *  [USER=2000183830]para[/USER]m shop The shop to announce.
	 */
	private static void addAnnounceBox(MaplePacketLittleEndianWriter mplew, IPlayerInteractionManager interaction) {
                mplew.write(interaction.getShopType());
                if (interaction.getShopType() == 4) {
                    mplew.writeInt(((MaplePlayerShop) interaction).getObjectId());
                } else {
                    mplew.writeInt(((MapleMiniGame) interaction).getObjectId());
                }
                mplew.writeMapleAsciiString(interaction.getDescription()); // desc
                mplew.write(0); // TODO: passwords
                mplew.write(interaction.getShopType() == 2 ? ((MapleMiniGame) interaction).getMatchesToWin() == 6 ? 2 : ((MapleMiniGame) interaction).getMatchesToWin() == 10 ? 1 : 0 : 0);
                mplew.write(1);
                mplew.write(interaction.getFreeSlot() > -1 ? 4 : 1);
                if (interaction.getShopType() == 4) {
                    mplew.write(0);
                } else {
                    mplew.write(((MapleMiniGame) interaction).getStarted() ? 1 : 0);
                }
        }
        
        private static void addShopBox(MaplePacketLittleEndianWriter mplew, MaplePlayerShop shop) {
		// 00: no game
		// 01: omok game
		// 02: card game
		// 04: shop
		mplew.write(4);
		mplew.writeInt(shop.getObjectId()); // gameid/shopid
		mplew.writeMapleAsciiString(shop.getDescription()); // desc
		// 00: public
		// 01: private
		mplew.write(0);
		// 00: red 4x3
		// 01: green 5x4
		// 02: blue 6x5
		// omok:
		// 00: normal
		mplew.write(0);
		// first slot: 1/2/3/4
		// second slot: 1/2/3/4
		mplew.write(1);
		mplew.write(4);
		// 0: open
		// 1: in progress
		mplew.write(0);
	}

	public static MaplePacket getPlayerShopNewVisitor(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(HexTool.getByteArrayFromHexString("04 02"));
		addCharLook(mplew, c, true);
		mplew.writeMapleAsciiString(c.getName());
		return mplew.getPacket();
	}

	public static MaplePacket getPlayerShopItemUpdate(MaplePlayerShop shop) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
		mplew.write(0x15);
		mplew.write(shop.getItems().size());
		for (MaplePlayerShopItem item : shop.getItems()) {
			mplew.writeShort(item.getBundles());
			mplew.writeShort(item.getItem().getQuantity());
			mplew.writeInt(item.getPrice());
                        mplew.write(item.getItem().getType());
                        addItemInfo(mplew, item.getItem(), false, true);
		}
		return mplew.getPacket();
	}

	public static MaplePacket updateCharBox(MapleCharacter c) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.UPDATE_CHAR_BOX.getValue());
		mplew.writeInt(c.getId());
		if (c.getPlayerShop() != null) {
                    addShopBox(mplew, c.getPlayerShop());
		} else {
                    mplew.writeInt(0);
		}
		return mplew.getPacket();
	}
        
        public static MaplePacket shopItemUpdate(IPlayerInteractionManager shop) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();

            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x17);
            if (shop.getShopType() == 1) {
                mplew.writeInt(0);
            }
            mplew.write(shop.getItems().size());
            for (net.sf.odinms.server.PlayerInteraction.MaplePlayerShopItem item : shop.getItems()) { // LULUL
                mplew.writeShort(item.getBundles());
                mplew.writeShort(item.getItem().getQuantity());
                mplew.writeInt(item.getPrice());
                addItemInfo(mplew, item.getItem(), true, true);
            }
            return mplew.getPacket();
        }
        
        public static MaplePacket addVisitor(MapleCharacter chr, boolean firstTime) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue()); 
            mplew.write(4);
            mplew.write(1);
            addCharLook(mplew, chr, true);
            mplew.writeMapleAsciiString(chr.getName());
            
            mplew.writeInt(1); // TODO: get stats
            mplew.writeInt(1);
            mplew.writeInt(1);
            mplew.writeInt(1);
            mplew.writeInt(2000);
            
            return mplew.getPacket();
        }
        
        public static MaplePacket getInteraction(MapleCharacter chr, boolean firstTime) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue()); // header.
            IPlayerInteractionManager ips = chr.getInteraction();
            int type = ips.getShopType();
            if (type == 1) {
                mplew.write(HexTool.getByteArrayFromHexString("05 01 04"));
            } else if (type == 2) {
                mplew.write(HexTool.getByteArrayFromHexString("05 02 02"));
            } else if (type == 4) {
                mplew.write(HexTool.getByteArrayFromHexString("05 04 04")); // might be wrong
            }
            mplew.write(ips.isOwner(chr) ? 0 : 1);
            mplew.write(0);
            if (type == 1 || type == 2) {
                addCharLook(mplew, ((MapleMiniGame) ips).getOwner(), true);
                mplew.writeMapleAsciiString(ips.getOwnerName());
            } else {
                addCharLook(mplew, ((net.sf.odinms.server.PlayerInteraction.MaplePlayerShop) ips).getMCOwner(), false);
                mplew.writeMapleAsciiString(ips.getOwnerName());
            }
            for (int i = 0; i < 3; i++) {
                if (ips.getVisitors()[i] != null) {
                    mplew.write(i + 1);
                    addCharLook(mplew, ips.getVisitors()[i], true);
                    mplew.writeMapleAsciiString(ips.getVisitors()[i].getName());
                }
            }
            mplew.write(0xFF);
            if (type == 1 || type == 2) {
                MapleMiniGame minigame = (MapleMiniGame) ips;
                mplew.write(0);
                if (type == 1 || type == 2) {
                    mplew.writeInt(1);
                } else {
                    mplew.writeInt(2);
                }
                mplew.writeInt(minigame.getOmokPoints("wins", true));
                mplew.writeInt(minigame.getOmokPoints("ties", true));
                mplew.writeInt(minigame.getOmokPoints("losses", true));
                mplew.writeInt(2000);
                if (ips.getVisitors()[0] != null) {
                    mplew.write(1);
                    if (type == 1 || type == 2) {
                        mplew.writeInt(1);
                    } else {
                        mplew.writeInt(2);
                    }
                    mplew.writeInt(minigame.getOmokPoints("wins", false));
                    mplew.writeInt(minigame.getOmokPoints("ties", false));
                    mplew.writeInt(minigame.getOmokPoints("losses", false));
                    mplew.writeInt(2000);
                }
                mplew.write(0xFF);
            }
            mplew.writeMapleAsciiString(ips.getDescription());
            if (type == 1 || type == 2) {
                mplew.write(ips.getItemType());
                mplew.write(0);
                mplew.write(0);
            }
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGame(MapleCharacter chr, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            MapleMiniGame game = (MapleMiniGame) chr.getInteraction();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(5); 
            mplew.write(chr.getInteraction().getShopType()); 
            mplew.write(4); // max slots is actually 4 in v40 but ya
            mplew.write(slot);
            if (slot == 0) {
                mplew.write(0);
                addCharLook(mplew, chr, true);
                mplew.writeMapleAsciiString(chr.getName());
            } else {
                MapleCharacter owner = game.getOwner();
                mplew.write(0);
                addCharLook(mplew, owner, true);
                mplew.writeMapleAsciiString(owner.getName());
                mplew.write(1);
                addCharLook(mplew, chr, true);
                mplew.writeMapleAsciiString(chr.getName());
            }
            mplew.write(0xFF);
            
            mplew.write(0); // Slot 0 -- Owner
            mplew.writeInt(game.getShopType()); // Game Type (1 = Omok)
            mplew.writeInt(game.getOmokPoints("wins", true)); // Wins
            mplew.writeInt(game.getOmokPoints("ties", true)); // Ties
            mplew.writeInt(game.getOmokPoints("losses", true)); // Losses
            mplew.writeInt(2000); // Score

            if (slot >= 1) {
                mplew.write(1); // Slot 1 -- Opponent
                mplew.writeInt(game.getShopType()); // Game Type (1 = Omok)
                mplew.writeInt(game.getOmokPoints("wins", false)); // Wins
                mplew.writeInt(game.getOmokPoints("ties", false)); // Ties
                mplew.writeInt(game.getOmokPoints("losses", false)); // Losses
                mplew.writeInt(2000); // Score
            }
            mplew.write(0xFF);
            
            mplew.writeMapleAsciiString(game.getDescription());
            mplew.write(0); // Piece type
            
            mplew.write(0);
            mplew.write(0);
            return mplew.getPacket();
        }
        
        public static MaplePacket shopChat(String message, int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("06 08"));
            mplew.write(slot);
            mplew.writeMapleAsciiString(message);
            return mplew.getPacket();
        }

        public static MaplePacket shopErrorMessage(int error, int type) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x0A);
            mplew.write(type);
            mplew.write(error);
            return mplew.getPacket();
        }
        
        public static MaplePacket shopVisitorLeave(int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x0A);
            if (slot > 0) { // owner != visitor
                mplew.write(slot);
            }
            return mplew.getPacket();
        }
        
        public static MaplePacket getMiniBoxFull() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(3);
            mplew.write(2);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameStart(int loser) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x23);
            mplew.write(loser == 0 ? 1 : 0);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameSkipTurn(int slot) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x39);
            mplew.write(slot);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameReady() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x20);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameUnReady() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x21);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameRequestTie() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x18);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameDenyTie() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x19);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameMoveOmok(int move1, int move2, int move3) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x26);
            mplew.writeInt(move1);
            mplew.writeInt(move2);
            mplew.write(move3);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameWin(MapleMiniGame game, int person) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("38 00"));
            mplew.write(person);
            mplew.writeInt(1); // start of owner; unknown
            mplew.writeInt(game.getOmokPoints("wins", true)); // wins
            mplew.writeInt(game.getOmokPoints("ties", true)); // ties
            mplew.writeInt(game.getOmokPoints("losses", true) + 1); // losses
            mplew.writeInt(2000); // points
            mplew.writeInt(1); // start of visitor; unknown
            mplew.writeInt(game.getOmokPoints("wins", false) + 1); // wins
            mplew.writeInt(game.getOmokPoints("ties", false)); // ties
            mplew.writeInt(game.getOmokPoints("losses", false)); // losses
            mplew.writeInt(2000); // points
            game.setOmokPoints(person + 1);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameTie(MapleMiniGame game) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("38 01"));
            mplew.writeInt(1); // unknown
            mplew.writeInt(game.getOmokPoints("wins", true)); // wins
            mplew.writeInt(game.getOmokPoints("ties", true) + 1); // ties
            mplew.writeInt(game.getOmokPoints("losses", true)); // losses
            mplew.writeInt(2000); // points
            mplew.writeInt(1); // start of visitor; unknown
            mplew.writeInt(game.getOmokPoints("wins", false)); // wins
            mplew.writeInt(game.getOmokPoints("ties", false) + 1); // ties
            mplew.writeInt(game.getOmokPoints("losses", false)); // losses
            mplew.writeInt(2000); // points
            game.setMatchCardPoints(3);
            return mplew.getPacket();
        }

        public static MaplePacket getMiniGameForfeit(MapleMiniGame game, int person) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(HexTool.getByteArrayFromHexString("38 02"));
            mplew.write(person);
            mplew.writeInt(1); // start of owner; unknown
            mplew.writeInt(game.getOmokPoints("wins", true)); // wins
            mplew.writeInt(game.getOmokPoints("ties", true)); // ties
            mplew.writeInt(game.getOmokPoints("losses", true) + 1); // losses
            mplew.writeInt(2000); // points
            mplew.writeInt(1); // start of visitor; unknown
            mplew.writeInt(game.getOmokPoints("wins", false) + 1); // wins
            mplew.writeInt(game.getOmokPoints("ties", false)); // ties
            mplew.writeInt(game.getOmokPoints("losses", false)); // losses
            mplew.writeInt(2000); // points
            game.setOmokPoints(person + 1);
            return mplew.getPacket();
        }

        public static MaplePacket getMatchCardStart(MapleMiniGame game) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x23);
            mplew.write(game.getLoser() == 1 ? 0 : 1);
            int times;
            if (game.getMatchesToWin() > 10) {
                times = 30;
            } else if (game.getMatchesToWin() > 6) {
                times = 20;
            } else {
                times = 12;
            }
            mplew.write(times);
            for (int i = 1; i <= times; i++) {
                mplew.writeInt(game.getCardId(i));
            }
            return mplew.getPacket();
        }

        public static MaplePacket getMatchCardSelect(int turn, int slot, int firstslot, int type) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.writeShort(SendPacketOpcode.PLAYER_INTERACTION.getValue());
            mplew.write(0x3E);
            mplew.write(turn);
            if (turn == 1) {
                mplew.write(slot);
            } else if (turn == 0) {
                mplew.write(slot);
                mplew.write(firstslot);
                mplew.write(type);
            }
            return mplew.getPacket();
        }
        
        // Maple Events
        
        public static MaplePacket leftKnockBack() {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9D); // LEFT_KNOCK_BACK
            return mplew.getPacket();
        }
        
        public static MaplePacket rollSnowball(int type, int rollDistance0, int rollDistance1, int startPoint0, int startPoint1) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9A); // ROLL_SNOWBALL
            mplew.write(type); // 0 = normal, 1 = rolls from start to end, 2 = btm invis, 3 = top invis
            if (type == 2) {
                MapleSnowball.isInvis0 = true;
            } else if (type == 3) {
                MapleSnowball.isInvis1 = true;
            }
            mplew.write(rollDistance0); // bottom snowball
            mplew.write(startPoint0); // stage
            mplew.write(0);
            mplew.write(rollDistance1); // top snowball
            mplew.write(startPoint1); // stage
            mplew.writeLong(0);
            return mplew.getPacket(); 
        }

        public static MaplePacket hitSnowBall(int team, int damage) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9B); // HIT_SNOWBALL
            mplew.write(team);// 0 is down, 1 is up
            mplew.writeInt(damage); // fixedDamage = 10
            return mplew.getPacket();
        }

        public static MaplePacket snowballMessage(int team, int message) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x9C); // SNOWBALL_MESSAGE
            mplew.write(team);// 0 is down, 1 is up
            mplew.writeInt(message);
            return mplew.getPacket();
        }
        
        public static MaplePacket showOXQuiz(int questionSet, int questionId, boolean askQuestion) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x34); // OX_QUIZ
            mplew.write(askQuestion ? 1 : 0);
            mplew.write(questionSet);
            mplew.writeInt(questionId);
            return mplew.getPacket();
        }
        
        public static MaplePacket getEventInstructions() { // same as v117.2
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x35); // SHOW_EVENT_INSTRUCTIONS
            mplew.write(0);
            return mplew.getPacket();
        }
        
        public static MaplePacket showForcedEquip(byte team) {
            MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
            mplew.write(0x2C); // FORCED_MAP_EQUIP
            if (team > -1) {
                mplew.writeShort(team); // 0x00 = red, 0x01 = blue
            }
            return mplew.getPacket();
        }
        
        public static MaplePacket coconutScore(int team1, int team2) {
           MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
           mplew.write(0x9D); // COCONUT_SCORE
           mplew.writeShort(team1);
           mplew.writeShort(team2);
           return mplew.getPacket();
       }

       public static MaplePacket hitCoconut(boolean spawn, int id, int type) {
           MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
           mplew.write(0x9C); // HIT_COCONUT
           if (spawn) {
                mplew.write(HexTool.getByteArrayFromHexString("00 80 00 00 00"));
            } else {
                mplew.writeInt(id);
                mplew.write(type); // What action to do for the coconut.
            }
            return mplew.getPacket();
        }
        
        // TODO: Fix inv full packet and remove mob hp packet
        
	public static MaplePacket getShowInventoryFull() { // TODO: fix if inventory is full, we haven't tested this yet.
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.write(SendPacketOpcode.SHOW_STATUS_INFO.getValue());
		mplew.write(0);
		mplew.write(0xFF);
		mplew.writeLong(0);
		return mplew.getPacket();
	}

	/**
	 * 
	 *  [USER=2000183830]para[/USER]m oid
	 *  [USER=2000183830]para[/USER]m remhp in %
	 *  [USER=850422]return[/USER]
	 */
	public static MaplePacket showMonsterHP(int oid, int remhppercentage) {
		MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
		mplew.writeShort(SendPacketOpcode.SHOW_MONSTER_HP.getValue()); // not in v40b. TODO: remove (;
		mplew.writeInt(oid);
		mplew.write(remhppercentage);
		return mplew.getPacket();
	}
}
 
Upvote 0
Back
Top