/*  EQEMu:  Everquest Server Emulator
	Copyright (C) 2001-2003  EQEMu Development Team (http://eqemulator.net)
	
	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; version 2 of the License.
	
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY except by those people which sell it, which
	are required to give you total support for your newly bought product;
	without even the implied warranty of MERCHANTABILITY or FITNESS FOR
	A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
	
	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
	
	client_process.cpp:
	Handles client login sequence and packets sent from client to zone
*/
#include "../common/debug.h"
#include <iostream>
#include <iomanip>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
#include <zlib.h>

#include "npc.h"
#ifdef WIN32
	#include <windows.h>
	#include <winsock.h>
	#define snprintf	_snprintf
	#define vsnprintf	_vsnprintf
	#define strncasecmp	_strnicmp
	#define strcasecmp  _stricmp
#else
	#include <pthread.h>
	#include <sys/socket.h>
	#include <netinet/in.h>
	#include <unistd.h>
#endif

#include "client.h"
#include "../common/database.h"
#include "../common/packet_functions.h"
#include "../common/packet_dump.h"
#include "worldserver.h"
#include "../common/packet_dump_file.h"
#include "PlayerCorpse.h"
#include "spdat.h"
#include "petitions.h"
#include "NpcAI.h"
#include "skills.h"
#include "forage.h"
#include "object.h"
#include "groups.h"
#include "zone.h"
#include "event_codes.h"
#include "faction.h"
#include "../common/crc32.h"
#include "doors.h"
#include "StringIDs.h"
using namespace std;

#ifdef GUILDWARS
#include "GuildWars.h"
extern GuildWars guildwars;
extern GuildLocationList location_list;
#endif

extern Database database;
extern Zone* zone;
extern volatile bool ZoneLoaded;
extern WorldServer worldserver;
extern GuildRanks_Struct guilds[512];
#ifndef NEW_LoadSPDat
	extern SPDat_Spell_Struct spells[SPDAT_RECORDS];
#endif
extern bool spells_loaded;
extern PetitionList petition_list;
extern EntityList entity_list;

int glob=0;

int Client::HandlePacket(const APPLAYER *app)
{
	bool ret = true;
	
	if (app->opcode == OP_AckPacket) {
    	return true;
	}
	
	#if EQDEBUG >= 9
		cout << "Received 0x" << hex << setw(4) << setfill('0') << app->opcode << ", size=" << dec << app->size << endl;
	#endif
	
	switch(client_state)
	{
		case CLIENT_CONNECTING:
		{
			this->IsOnBoat=false;
			
			if (app->opcode == OP_SetDataRate)
			{
				// Set client datarate
				if (app->size != sizeof(float)) {
					LogFile->write(EQEMuLog::Error,"Wrong size on OP_SetDatarate. Got: %i, Expected: %i", app->size, sizeof(float));
					break;
				}
				LogFile->write(EQEMuLog::Debug, "HandlePacket() OP_SetDataRate request : %f",  *(float*) app->pBuffer);
				float tmpDR = *(float*) app->pBuffer;
				if (tmpDR <= 0.0f) {
					LogFile->write(EQEMuLog::Error,"HandlePacket() OP_SetDataRate INVALID request : %f <= 0", tmpDR);
					LogFile->write(EQEMuLog::Normal,"WARNING: Setting datarate for client to 5.0 expect a client lock up =(");
					tmpDR = 5.0f;
				}
				if (tmpDR > 25.0f)
					tmpDR = 25.0f;
				eqnc->SetDataRate(tmpDR);
			}
			else if (app->opcode == OP_ZoneEntry) {
				// Quagmire - Antighost code
				// tmp var is so the search doesnt find this object
				char tmp[64] = {0};
				snprintf(tmp, 64, "%s", (char*)&app->pBuffer[4]);
				Client* client = entity_list.GetClientByName(tmp);
				if (!zone->GetAuth(ip, tmp, &WID, &account_id, &character_id, &admin, lskey, &tellsoff)) {
					LogFile->write(EQEMuLog::Error, "GetAuth() returned false kicking client");
					if (client != 0)
						client->Kick();
					ret = false; // TODO: Can we tell the client to get lost in a good way
					break;
				}
				
				strcpy(name, tmp);
				if (client != 0) {
					struct in_addr ghost_addr;
					ghost_addr.s_addr = eqnc->GetrIP();
					
					LogFile->write(EQEMuLog::Error,"Ghosting client: Account ID:%i Name:%s Character:%s IP:%s",
										client->AccountID(), client->AccountName(), client->GetName(), inet_ntoa(ghost_addr));
					client->Disconnect();
				}
				
				char* query = 0;
				uint32_breakdown workpt;
				workpt.b4() = DBA_b4_Entity;
				workpt.w2_3() = GetID();
				workpt.b1() = DBA_b1_Entity_Client_InfoForLogin;
				DBAsyncWork* dbaw = new DBAsyncWork(MTdbafq, workpt, DBAsync::Read);
				dbaw->AddQuery(1, &query, MakeAnyLenString(&query, "SELECT status,name,lsaccount_id,gmspeed FROM account WHERE id=%i", account_id));
				dbaw->AddQuery(2, &query, MakeAnyLenString(&query, "SELECT id,profile,zonename,x,y,z,alt_adv,guild,guildrank FROM character_ WHERE id=%i", character_id));
				dbaw->AddQuery(3, &query, MakeAnyLenString(&query, "SELECT faction_id,current_value FROM faction_values WHERE char_id = %i", character_id));
				if (!(pDBAsyncWorkID = dbasync->AddWork(&dbaw))) {
					safe_delete(dbaw);
					LogFile->write(EQEMuLog::Error,"dbasync->AddWork() returned false, client crash");
					ret = false;
					break;
				}
				break;
			}
			else if (app->opcode == OP_SetServerFilter) {
				SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer;
				ServerFilter(filter);
			}
			else if (app->opcode == OP_ReqClientSpawn) {
				
				//////////////////////////////////////////////////////
				// Spawn Appearance Packet
				APPLAYER* outapp = new APPLAYER(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
				SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
				sa->type = 0x10;			// Is 0x10 used to set the player id?
				sa->parameter = GetID();	// Four bytes for this parameter...
				QueuePacket(outapp);
				safe_delete(outapp);
				
				// Inform the world about the client
				outapp = new APPLAYER();
				
				CreateSpawnPacket(outapp);
				entity_list.QueueClients(this, outapp, true);
				safe_delete(outapp);
				
				outapp = new APPLAYER;
				
				// Send Zone Doors
				if (entity_list.MakeDoorSpawnPacket(outapp)) {
					outapp->Deflate();
					QueuePacket(outapp);
				}
				safe_delete(outapp);
				
				// Send Zone Objects
				entity_list.SendZoneObjects(this);
				
				// Send Zone Points
				// Hideously broken at the moment. Need to correct ZonePointEntry struct 1st - TC
				if (zone->numzonepoints > 0) {
					int32 zpsize = sizeof(ZonePoints) + ((zone->numzonepoints+1) * sizeof(ZonePoint_Entry));
					APPLAYER* outapp = new APPLAYER(OP_SendZonepoints,zpsize);
					ZonePoints* zp = (ZonePoints*)outapp->pBuffer;
					memset(zp, 0, zpsize);
					LinkedListIterator<ZonePoint*> iterator(zone->zone_point_list);
					iterator.Reset();
					zp->count = zone->numzonepoints;
					int32 count = 0;
					
					while(iterator.MoreElements())
					{
						ZonePoint* data = iterator.GetData();
						zp->zpe[count].iterator = data->number;
						zp->zpe[count].x = data->target_y;
						zp->zpe[count].y = data->target_x; //Backwards to convert to eqlives standard..
						zp->zpe[count].z = data->target_z;
						zp->zpe[count].heading=data->target_heading;
						zp->zpe[count].zoneid = database.GetZoneID((const char*)data->target_zone);
						iterator.Advance();
						count++;
					}
					outapp->Deflate();
					QueuePacket(outapp);
					safe_delete(outapp);
				}
				
				// Tell client they can continue we're done
				outapp = new APPLAYER(OP_SendExpZonein, 0);
				QueuePacket(outapp);
				safe_delete(outapp);

				if(strncasecmp(zone->GetShortName(),"bazaar",6)==0)
					SendBazaarWelcome();
			}
			else if (app->opcode == OP_SendExpZonein) {
				APPLAYER* outapp;
				
				// Send alt advance exp
				SendAAStats();
				if(this->GetLevel()>=51)
					database.GetAATimers(this->CharacterID());
				if(GuildEQID()!=0 && GuildEQID()!=0xFFFFFFFF)
					SendGuildMembers(GuildEQID());
				// Send exp packets
				outapp = new APPLAYER(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
				ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
				int32 tmpxp1 = GetEXPForLevel(GetLevel()+1);
				int32 tmpxp2 = GetEXPForLevel(GetLevel());
				// Quag: crash bug fix... Divide by zero when tmpxp1 and 2 equalled each other, most likely the error case from GetEXPForLevel() (invalid class, etc)
				if (tmpxp1 != tmpxp2 && tmpxp1 != 0xFFFFFFFF && tmpxp2 != 0xFFFFFFFF) {
					double tmpxp = (double) ( (double) m_pp.exp-tmpxp2 ) / ( (double) tmpxp1-tmpxp2 );
					eu->exp = (uint32)(330.0f * tmpxp);
					QueuePacket(outapp);
				}
				safe_delete(outapp);

				outapp = new APPLAYER(OP_SendExpZonein, 0);
				QueuePacket(outapp);
				safe_delete(outapp);

				outapp = new APPLAYER(0x010e, 1);
				QueuePacket(outapp);
				safe_delete(outapp);
			}
			else if(app->opcode==OP_ZoneComplete)
			{
				break;
			}
			else if (app->opcode == OP_ReqNewZone) {
				APPLAYER* outapp;
				
				/////////////////////////////////////
				// New Zone Packet
				outapp = new APPLAYER(OP_NewZone, sizeof(NewZone_Struct));
				NewZone_Struct* nz = (NewZone_Struct*)outapp->pBuffer;
				memcpy(outapp->pBuffer, &zone->newzone_data, sizeof(NewZone_Struct));
				strcpy(nz->char_name, m_pp.name);
				outapp->Deflate();
				QueuePacket(outapp);
				safe_delete(outapp);
			}
			else if (app->opcode == OP_WearChange) {
				// Last one recv'd for all clients, apparently
				if (app->pBuffer[9] == 6)
				{
					CompleteConnect();
				}
			}
			else if ((app->opcode == OP_ClientUpdate) || (app->opcode == OP_ClientReady)) {
				CompleteConnect();
			}
			else if (app->opcode == OP_SpawnAppearance) {
				// Not sure if we should handle this
			}
			else if (app->opcode == OP_ClientError) {
				// Client reporting error to server
				ClientError_Struct* error = (ClientError_Struct*)app->pBuffer;
				LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name);
				LogFile->write(EQEMuLog::Error, "Error message: %s", error->message);
				Message(13, error->message);
#if (EQDEBUG>=5)
				    DumpPacket(app);
#endif
			}
			else if (app->opcode == 0x02dc) {
					QueuePacket(app);
			}
			else if(app->opcode == OP_ApproveZone){
				ApproveZone_Struct* azone =(ApproveZone_Struct*)app->pBuffer;
				azone->approve=1;
				QueuePacket(app);
			}
			else {
				if(app->opcode==0x0150 || app->opcode==0x016c)//ignore these for now.
					break;
				LogFile->write(EQEMuLog::Error, "HandlePacket() Opcode error: Unexpected packet during CLIENT_CONNECTING: opcode: 0x%04x, size: %i", app->opcode, app->size);
#if EQDEBUG >= 9
					cout << "Unexpected packet during CLIENT_CONNECTING: OpCode: 0x" << hex << setw(4) << setfill('0') << app->opcode << dec << ", size: " << app->size << endl;
					DumpPacket(app);
#endif
			}
			break;
		}
		case CLIENT_CONNECTED:
			{
				adverrorinfo = app->opcode;
				#if EQDEBUG == 9
					LogFile->write(EQEMuLog::Debug,"HandlePacket() OPCODE debug enabled about to process");
					cerr << "OPCODE: " << hex << setw(4) << setfill('0') << app->opcode << dec << ", size: " << app->size << endl;
					DumpPacket(app);
				#endif
				
				switch(app->opcode)
				{
				case OP_ClientUpdate: {
					if (IsAIControlled())
						break;
					
					if (app->size != sizeof(PlayerPositionUpdateClient_Struct)) {
						LogFile->write(EQEMuLog::Error, "OP size error: OP_ClientUpdate expected:%i got:%i", sizeof(PlayerPositionUpdateClient_Struct), app->size);
						break;
					}
					PlayerPositionUpdateClient_Struct* ppu = (PlayerPositionUpdateClient_Struct*)app->pBuffer;
					
					// solar: a very low chance to improve at sense heading, since
					// the client doesn't send an opcode for the key anymore, ever
					// since they made it useless on live
					//
					// this checking of heading/x_pos is cheap, and the result is
					// subtle, but you'll notice your sense heading improve as you
					// travel around
					if(
						( (heading != ppu->heading) && !((int)heading % 3) ) ||	// turning
						( (x_pos != ppu->x_pos) && !((int)x_pos % 6) )					// moving
					)
					{
						CheckIncreaseSkill(SENSE_HEADING, -20);
					}

					// Update internal state
					delta_y			= ppu->delta_y;
					delta_x			= ppu->delta_x;
					delta_z			= ppu->delta_z;
					delta_heading	= ppu->delta_heading;
					heading			= ppu->heading;

					if(IsTracking && ((x_pos!=ppu->x_pos) || (y_pos!=ppu->y_pos))){
						if((rand()%100)<70)//should be good
							CheckIncreaseSkill(TRACKING,-10);
					}
					y_pos			= ppu->y_pos;
					x_pos			= ppu->x_pos;
					z_pos			= ppu->z_pos;
					animation		= ppu->animation;
#ifdef GUILDWARS
					if(animation > 65 && admin<80 && CheckCheat()){
						if(cheater || cheatcount>0){
							Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
							char descript[50]={0};
							sprintf(descript,"%s: %i","Player using a movement of",ppu->animation);
							database.logevents(this->AccountName(),this->AccountID(),admin,this->GetName(),"none","Movement speed cheat",descript,15);
							Damage(this,200,0,4,false);
							if(cheater==false){
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",this->GetName(),this->AccountName());
								cheater=true;
							}
							cheatcount=0;
						}
						else
							cheatcount++;
					}
					else
						cheatcount=0;
					cheat_x=x_pos;
					cheat_y=y_pos;
#endif
						//printf("animation: %i\n",ppu->animation);
					// Outgoing client packet
					SendPosUpdate();
					break;
				}
				case OP_AutoAttack: {
					if (app->size == 4)	{
						if (app->pBuffer[0] == 0) {
							auto_attack = false;
							if (IsAIControlled())
								break;
							attack_timer->Disable();
							attack_timer_dw->Disable();
						}
						else if (app->pBuffer[0] == 1) {
							auto_attack = true;
							if (IsAIControlled())
								break;
							SetAttackTimer();
						}
					}
					else {
						LogFile->write(EQEMuLog::Error, "OP size error: OP_AutoAttack expected:4 got:%i", app->size);
					}
					break;
				}
				case OP_AutoAttack2: { // Why 2?
					break;
				}
				case OP_TargetMouse:		// mouse targetting a person
				case OP_TargetCommand: {	// /target user
					if (app->size != sizeof(ClientTarget_Struct)) {
						LogFile->write(EQEMuLog::Error, "OP size error: OP_TargetMouse expected:%i got:%i", sizeof(ClientTarget_Struct), app->size);
						break;
					}
					
					// Locate and cache new target
					ClientTarget_Struct* ct=(ClientTarget_Struct*)app->pBuffer;
					pClientSideTarget = ct->new_target;
					if (!IsAIControlled())
						target = entity_list.GetMob(ct->new_target);
					
					// For /target, send reject or success packet
					if (app->opcode == OP_TargetCommand) {
						if (target && DistNoZ(*target) <= 50) {
							QueuePacket(app);
							APPLAYER hp_app;
							target->CreateHPPacket(&hp_app);
							QueuePacket(&hp_app);
						}
						else {
							APPLAYER* outapp = new APPLAYER(OP_TargetReject, sizeof(TargetReject_Struct));
							outapp->pBuffer[0] = 0x2f;
							outapp->pBuffer[1] = 0x01;
							outapp->pBuffer[4] = 0x0d;
							QueuePacket(outapp);
							safe_delete(outapp);
						}
					}
					break;
				}
				case OP_Jump: {
					// neotokyo: here we could reduce fatigue, if we knew how
					m_pp.fatigue += 10;
					if(m_pp.fatigue > 100)
						m_pp.fatigue = 100;
					break;
				}
				case OP_Consume: {
					if (app->size != sizeof(Consume_Struct))
					{
						LogFile->write(EQEMuLog::Error, "OP size error: OP_Consume expected:%i got:%i", sizeof(Consume_Struct), app->size);
						break;
					}
					Consume_Struct* pcs = (Consume_Struct*)app->pBuffer;
					if (pcs->type == 0x01) {
					  GetInv().DeleteItem(pcs->slot,1);
#if EQDEBUG >= 1
							LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)pcs->slot);
#endif
						// 6000 is the max. value
						m_pp.hunger_level += 1000;
					}
					else if (pcs->type == 0x02) {
					  GetInv().DeleteItem(pcs->slot,1);
#if EQDEBUG >= 1
							LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)pcs->slot);
#endif
						// 6000 is the max. value
						m_pp.thirst_level += 1000;
					}
					else {
						LogFile->write(EQEMuLog::Error, "OP_Consume: unknown type, type:%i", (int)pcs->type);
						break;
					}
					APPLAYER *outapp;
					outapp = new APPLAYER(OP_Stamina, sizeof(Stamina_Struct));
					Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer;
					sta->food = 6000;
					sta->water = 6000;

					//sta->fatigue = m_pp.fatigue;
					QueuePacket(outapp);
					safe_delete(outapp);
					break;
				}
				case OP_AdventureMerchantRequest: {
					// Packet contains entity id
					char msg[16000];
					memset(msg,0,16000);
					int8 count = 0;
					EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer;
					int32 merchantid = 0;
					//DumpPacket(app);

					Mob* tmp = entity_list.GetMob(eid->entity_id);
					if (tmp != 0)
						merchantid=tmp->CastToNPC()->MerchantType;
					else
						break;
  const Item_Struct *item = 0;
  for (int32 i=0;i<80; i++) {
    item=database.GetItem(database.GetMerchantData(merchantid,i+1));
	if(item)
	{
	sprintf(msg,"%s^%s,%i,%i,%i,0,1,32767,32767",msg,item->Name,item->ItemNumber,item->Common.ldonpointcost,item->Common.ldonpointtheme);
	//printf("%s\n",msg);
	count++;
	}
	if(!item)
		i=80;
  }
					//Count
					//^Item Name,Item ID,Cost in Points,Theme (0=none),0,1,32767,32767
					APPLAYER* outapp = new APPLAYER(OP_AdventureMerchantResponse,strlen(msg)+2);
					outapp->pBuffer[0] = count;
					strncpy((char*)&outapp->pBuffer[1],msg,strlen(msg));
					//DumpPacket(outapp);
					outapp->Deflate();
					QueuePacket(outapp);
					safe_delete(outapp);
					break;
					}
				case OP_AdventureMerchantPurchase: {
					Adventure_Purchase_Struct* aps = (Adventure_Purchase_Struct*)app->pBuffer;
/*
					Get item apc->itemid (can check NPC if thats necessary), ldon point theme check only if theme is not 0 (I am not sure what 1-5 are though for themes)
					if(ldon_available_points >= item ldonpointcost)
					{
					give item (67 00 00 00 for the packettype using opcode 0x02c5)
					ldon_available_points -= ldonpointcost;
					}
*/
					const Item_Struct* item = database.GetItem(aps->itemid);
					if (!item) {
						Message(13, "Error: The item you purchased does not exist!");
						break;
					}
					if(m_pp.ldon_available_points >= item->Common.ldonpointcost)
					{
					ItemInst* inst = ItemInst::Create(item);
					if (inst) {
						sint16 openslot = m_inv.FindFreeSlot(false,true);
						if(openslot == SLOT_INVALID)
							break;
						sint32 requiredpts = (sint32)item->Common.ldonpointcost*-1;
						if(!UpdateLDoNPoints(requiredpts,item->Common.ldonpointtheme))
							break;
						SendItemPacket(openslot, inst, ItemPacketTrade);
						PutItemInInventory(openslot,*inst);
						safe_delete(inst);
					}
					}
					//DumpPacket(app);
					break;
					}
				case OP_ConsiderCorpse: {
					if (app->size == sizeof(Consider_Struct))
					{
						Consider_Struct* conin = (Consider_Struct*)app->pBuffer;
						Corpse* tcorpse = entity_list.GetCorpseByID(conin->targetid);
						if (tcorpse && tcorpse->IsNPCCorpse()) {
							Message(10, "This corpse regards you sadly. Noone asked what i wanted my tombstone to say!");
							int32 min; int32 sec; int32 ttime;
							if ((ttime = tcorpse->GetDecayTime()) != 0) {
								sec = (ttime/1000)%60; // Total seconds
								min = (ttime/60000)%60; // Total seconds / 60 drop .00
								//Message(10,  "This corpse will decay in %i minutes, %i seconds.", min, sec);
								char val1[20]={0};
								char val2[20]={0};
								Message_StringID(10,CORPSE_DECAY1,ConvertArray(min,val1),ConvertArray(sec,val2));
							}
							else {
								SimpleMessage_StringID(10,CORPSE_DECAY_NOW);
								//Message(10,  "This corpse is waiting to expire.");
							}
						}
						else if (tcorpse && tcorpse->IsPlayerCorpse()) {
							Message(10, "This corpse glares at you threateningly! I told you that wasn't going to work");
							int32 min; int32 sec; int32 ttime;
							if ((ttime = tcorpse->GetDecayTime()) != 0) {
								sec = (ttime/1000)%60; // Total seconds
								min = (ttime/60000)%60; // Total seconds / 60 drop .00
								//Message(10,  "This corpse will decay in %i minutes, %i seconds.", min, sec);
								char val1[20]={0};
								char val2[20]={0};
								Message_StringID(10,CORPSE_DECAY1,ConvertArray(min,val1),ConvertArray(sec,val2));
							}
							else {
								//Message(10,  "This corpse is waiting to expire.");
								SimpleMessage_StringID(10,CORPSE_DECAY_NOW);
							}
						}
					}
					else {
						LogFile->write(EQEMuLog::Debug, "Size mismatch in Consider corpse expected %i got %i", sizeof(Consider_Struct), app->size);
					}
					break;
				}
				case OP_Consider: {
					Consider_Struct* conin = (Consider_Struct*)app->pBuffer;
					Mob* tmob = entity_list.GetMob(conin->targetid);
					if (tmob == 0)
						break;
					APPLAYER* outapp = new APPLAYER(OP_Consider, sizeof(Consider_Struct));
					Consider_Struct* con = (Consider_Struct*)outapp->pBuffer;
					con->playerid = GetID();
					con->targetid = conin->targetid;
					if(tmob->IsNPC())
						con->faction = GetFactionLevel(character_id,tmob->GetNPCTypeID(), race, class_, deity,(tmob->IsNPC()) ? tmob->CastToNPC()->GetPrimaryFaction():0, tmob); // rembrant, Dec. 20, 2001; TODO: Send the players proper deity
					else
						con->faction = 1;
					#ifdef GUILDWARS
					if(GuildDBID() != 0 && (tmob->IsNPC() && tmob->CastToNPC()->GetGuildID() != 0) || (tmob->IsClient() && tmob->CastToClient()->GuildDBID() != 0))
					{
					sint32 faction = guildwars.GetCurrentGuildFaction(this->CastToMob(),tmob);
					if(faction == 2000)
						con->faction = FACTION_ALLY;
					else if(faction < 2000 && faction >= 1600)
						con->faction = FACTION_WARMLY;
					else if(faction < 1600 && faction >= 1200)
						con->faction = FACTION_KINDLY;
					else if(faction < 1200 && faction >= 800)
						con->faction = FACTION_AMIABLE;
					else if(faction < 800 && faction >= 400)
						con->faction = FACTION_INDIFFERENT;
					else if(faction < 400 && faction >= -1)
						con->faction = FACTION_APPREHENSIVE;
					else if(faction < -1 && faction >= -400)
						con->faction = FACTION_DUBIOUS;
					else if(faction < -400 && faction >= -800)
						con->faction = FACTION_THREATENLY;
					else if(faction < -800 && faction >= -2000)
						con->faction = FACTION_SCOWLS;
					}
					#endif
					con->level = GetLevelCon(tmob->GetLevel());
					if(database.GetServerType() == 1) {
						if (!tmob->IsNPC() )
							con->pvpcon = tmob->CastToClient()->GetPVP();
					}

					// Mongrel: If we're feigned show NPC as indifferent 
					if (tmob->IsNPC()) 
					{ 
						if (feigned) 
							con->faction = FACTION_INDIFFERENT; 
					} 

					QueuePacket(outapp);
					safe_delete(outapp);
					break;
				}
				case OP_Begging:{
					Message(MT_Emote,"Not Implemented yet!");
					break;
				}
				case OP_Surname: {
					Surname_Struct* surname = (Surname_Struct*) app->pBuffer;
					char *c = surname->lastname;
					int found_bad_char = 0;

					// solar: fix name so first letter is capital, the rest is lowercase
					// and check for illegal chars too
					*c = toupper(*c);
					for(c = surname->lastname+1; *c; c++)
					{
						if(!isalpha(*c))
						{
							found_bad_char = 1;
							break;
						}
						*c = tolower(*c);
					}
					
					if (found_bad_char) {
						Message(13, "Surnames may only contain alphabet characters.");
						break;
					}
					else if (strlen(surname->lastname)>=20) {
						SimpleMessage_StringID(10,STRING_SURNAME_TOO_LONG);
						break;
					}
					ChangeLastName(surname->lastname);
					
					APPLAYER* outapp = new APPLAYER(OP_GMLastName, sizeof(GMLastName_Struct));
					GMLastName_Struct* lnc = (GMLastName_Struct*) outapp->pBuffer;
					strcpy(lnc->name, surname->name);
					strcpy(lnc->lastname, surname->lastname);
					strcpy(lnc->gmname, "SurnameOP");
					lnc->unknown[0] = 1;
					lnc->unknown[1] = 1;
					entity_list.QueueClients(this, outapp, false);
					safe_delete(outapp);
					
					outapp = app->Copy();
					surname = (Surname_Struct*) outapp->pBuffer;
					surname->unknown0064=1;
					outapp->Deflate();
					FastQueuePacket(&outapp);
					break;
				}
				case OP_YellForHelp: {
					entity_list.QueueCloseClients(this,app, true, 100.0);
					break;
				}
				case OP_SafePoint: {
					break;
				}
				case OP_Assist: {
					if (app->size != sizeof(EntityId_Struct)) {
						LogFile->write(EQEMuLog::Debug, "Size mismatch in OP_Assist expected %i got %i", sizeof(EntityId_Struct), app->size);
						break;
					}
					EntityId_Struct* eid = (EntityId_Struct*)app->pBuffer;
					Entity* entity = entity_list.GetID(eid->entity_id);
					
					APPLAYER* outapp = app->Copy();
					eid = (EntityId_Struct*)outapp->pBuffer;
			
					if (entity && entity->CastToMob()->GetTarget() != 0)
						eid->entity_id = entity->CastToMob()->GetTarget()->GetID();
					
					FastQueuePacket(&outapp);
					break;
				}
				case OP_GMTraining: {
					APPLAYER* outapp = app->Copy();
					GMTrainee_Struct* gmtrain = (GMTrainee_Struct*) outapp->pBuffer;
					Entity * pTrainer = entity_list.GetID(gmtrain->npcid);
					for (int cur_skill = 0; cur_skill <= HIGHEST_SKILL; cur_skill++){
						gmtrain->skills[cur_skill] = pTrainer->CastToMob()->MaxSkill(cur_skill);
					}
					FastQueuePacket(&outapp);
					if (pTrainer && pTrainer->IsNPC()) {
						//outapp = new APPLAYER(OP_CommonMessage,sizeof(CommonMessage_Struct)+16+(strlen(pTrainer->GetName() - (16-strlen(pTrainer->GetName())))+strlen(GetName())+7);
						outapp = new APPLAYER;
						outapp->opcode = OP_FormattedMessage;
						//outapp->size = 48;
						outapp->size = sizeof(CommonMessage_Struct)+strlen(pTrainer->GetName())+strlen(GetName())+5;
						outapp->pBuffer = new uchar[outapp->size];
						memset(outapp->pBuffer, 0, outapp->size);
						//cout<<"Size:"<<(int)outapp->size<<endl;
						CommonMessage_Struct *p = (CommonMessage_Struct*) outapp->pBuffer;
						p->unknown[0] = 0x0000;
						p->unknown[1] = 0x022a;
						p->unknown[2] = 0x000a;
						int length = 0;
						strcpy(p->rest, pTrainer->GetName());
						length+=(strlen(pTrainer->GetName())-1);
						p->rest[length-1] = 0x00;
						sprintf(&p->rest[length], "%d", 1204+rand()%3);
						length+=5;
						strcpy(&p->rest[length], GetName());
						QueuePacket(outapp);
						safe_delete(outapp);
					}
                    break;
				}
				case OP_GMEndTraining: {
					GMTrainEnd_Struct* p1 = (GMTrainEnd_Struct*) app->pBuffer;
					Entity * pTrainer = entity_list.GetID(p1->npcid);
					if (pTrainer && pTrainer->IsNPC()) {
						APPLAYER* outapp = new APPLAYER;
						outapp->pBuffer = new uchar[200];
						CommonMessage_Struct *p = (CommonMessage_Struct*) outapp->pBuffer;
						memset(outapp->pBuffer, 0x00, 200);
						p->unknown[0] = 0x0000;
						p->unknown[1] = 0x022a;
						p->unknown[2] = 0x000a;
						outapp->size = sizeof(CommonMessage_Struct);
						outapp->opcode = OP_FormattedMessage;
						int length = 0;
						length = strlen(pTrainer->GetName());
						strncpy(p->rest, pTrainer->GetName(),length-2);
						outapp->size += length;
						sprintf(&p->rest[length-1], "%d", 1208+rand()%3);
						outapp->size += 5;
						length += 5;
						strcpy(&p->rest[length], GetName());
						length = strlen(GetName()) + 1;
						DumpPacket(outapp);
						outapp->size += length;
						QueuePacket(outapp);
						outapp->opcode = OP_GMEndTrainingResponse;
						outapp->size = 0;
						QueuePacket(outapp);
						safe_delete(outapp);
					}
					break;
				}
				case OP_GMTrainSkill: {
					GMSkillChange_Struct* gmskill = (GMSkillChange_Struct*) app->pBuffer;
					if (gmskill->skillbank == 0x01) {
						// languages go here
						if (gmskill->skill_id > 25) {
							cout << "Wrong Training Skill (languages)" << endl;
							DumpPacket(app);
							break;
						}
						cout << "Training language: " << gmskill->skill_id << endl;
						IncreaseLanguageSkill(gmskill->skill_id);
						m_pp.points--;
					}
					else if (gmskill->skillbank == 0x00) {
						// normal skills go here
						if (gmskill->skill_id > 73) {
							cout << "Wrong Training Skill (abilities)" << endl;
							DumpPacket(app);
							break;
						}
						int8 skilllevel=GetSkill(gmskill->skill_id);
						if ( skilllevel == 255) {
							// Client never gets this skill check for gm status or fail
							break;
						}
						else if (skilllevel == 254) {
							// Client training new skill for the first time set the skill to level-1

							int16 t_level = database.GetTrainlevel(GetClass(), gmskill->skill_id);
							cout<<"t_level:"<<t_level<<endl;
							if (t_level == 66 || t_level == 0) {
								break;
							}
							m_pp.skills[gmskill->skill_id+1] = t_level;
						}
						else if (skilllevel <= 251) {
							// Client train a valid skill
							// FIXME If the client doesn't do the "You are more skilled than I" check we should do it here
							SetSkill(gmskill->skill_id,skilllevel+1);
						}
						else {
							// Log a warning someones been hacking
							LogFile->write(EQEMuLog::Error, "OP_GMTrainSkill: failed client: %s", GetName());
							break;
						}
						m_pp.points--;
					}
					break;
				}
				case OP_DuelResponse: {
					if(app->size != sizeof(DuelResponse_Struct))
						break;
					DuelResponse_Struct* ds = (DuelResponse_Struct*) app->pBuffer;
					Entity* entity = entity_list.GetID(ds->target_id);
					Entity* initiator = entity_list.GetID(ds->entity_id);
					if(!entity->IsClient() || !initiator->IsClient())
						break;
					
					entity->CastToClient()->SetDuelTarget(0);
					entity->CastToClient()->SetDueling(false);
					initiator->CastToClient()->SetDuelTarget(0);
					initiator->CastToClient()->SetDueling(false);
					if(GetID() == initiator->GetID())
						entity->CastToClient()->Message_StringID(10,DUEL_DECLINE,initiator->GetName());
					else
						initiator->CastToClient()->Message_StringID(10,DUEL_DECLINE,entity->GetName());
					break;
				}
				case OP_DuelResponse2: {
					if(app->size != sizeof(Duel_Struct))
						break;
					
					Duel_Struct* ds = (Duel_Struct*) app->pBuffer;
					Entity* entity = entity_list.GetID(ds->duel_target);
					Entity* initiator = entity_list.GetID(ds->duel_initiator);
					
					if (entity && initiator && entity == this && initiator->IsClient()) {
						APPLAYER* outapp = new APPLAYER(OP_RequestDuel, sizeof(Duel_Struct));
						Duel_Struct* ds2 = (Duel_Struct*) outapp->pBuffer;
						
						ds2->duel_initiator = entity->GetID();
						ds2->duel_target = entity->GetID();
						initiator->CastToClient()->QueuePacket(outapp);
						
						outapp->opcode = OP_DuelResponse2;
						ds2->duel_initiator = initiator->GetID();

						initiator->CastToClient()->QueuePacket(outapp);
						
						QueuePacket(outapp);
						SetDueling(true);
						initiator->CastToClient()->SetDueling(true);
						SetDuelTarget(ds->duel_initiator);
						safe_delete(outapp);

					}
					break;
				}
				case OP_RequestDuel: {
					if(app->size != sizeof(Duel_Struct))
						break;
					
					APPLAYER* outapp = app->Copy();
					Duel_Struct* ds = (Duel_Struct*) outapp->pBuffer;
					int32 duel = ds->duel_initiator;
					ds->duel_initiator = ds->duel_target;
					ds->duel_target = duel;
					Entity* entity = entity_list.GetID(ds->duel_target);
					if(GetID() != ds->duel_target && entity->IsClient() && (entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() != 0)) {
						Message_StringID(10,DUEL_CONSIDERING,entity->GetName());
						break;
					}
					if(IsDueling()) {
						Message_StringID(10,DUEL_INPROGRESS);
						break;
					}
					
					if(GetID() != ds->duel_target && entity->IsClient() && GetDuelTarget() == 0 && !IsDueling() && !entity->CastToClient()->IsDueling() && entity->CastToClient()->GetDuelTarget() == 0) {
						SetDuelTarget(ds->duel_target);
						entity->CastToClient()->SetDuelTarget(GetID());
						ds->duel_target = ds->duel_initiator;
						entity->CastToClient()->FastQueuePacket(&outapp);
						entity->CastToClient()->SetDueling(false);
						SetDueling(false);
					}
					else
						safe_delete(outapp);
					break;
				}
				case OP_SpawnAppearance: {
					if (app->size != sizeof(SpawnAppearance_Struct)) {
						cout << "Wrong size on OP_SpawnAppearance. Got: " << app->size << ", Expected: " << sizeof(SpawnAppearance_Struct) << endl;
						break;
					}
					
					SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)app->pBuffer;
					
					if (sa->type == APPEAR_VISIBLE) { 
						this->invisible = (sa->parameter == 1);
						entity_list.QueueClients(this, app, true);
						break;
					}
					else if (sa->type == APPEAR_ANIM) {
						if (IsAIControlled())
							break;
						if (sa->parameter == ANIM_STAND) {
							SetAppearance(0);
							playeraction = 0;
							SetFeigned(false);
							BindWound(this, false, true);
						}
						else if (sa->parameter == ANIM_SIT) {
							SetAppearance(1);
							playeraction = 1;
							InterruptSpell();
							SetFeigned(false);
							BindWound(this, false, true);
						}
						else if (sa->parameter == ANIM_CROUCH) {
							SetAppearance(2);
							playeraction = 2;
							InterruptSpell();
							SetFeigned(false);
							bardsong = 0;
							bardsong_timer->Disable();
						}
						else if (sa->parameter == ANIM_DEATH) { // feign death too
							SetAppearance(3);
							playeraction = 3;
							InterruptSpell();
							bardsong = 0;
							bardsong_timer->Disable();
						}
						else if (sa->parameter == ANIM_LOOT) {
							SetAppearance(4);
							playeraction = 4;
							SetFeigned(false);
						}
						// @merth: This is from old code
						// I have no clue what it's for
						/*
						else if (sa->parameter == 0x05) {
							// Illusion
							cout << "Illusion packet recv'd:" << endl;
							DumpPacket(app);
						}
						*/
						else {
							cerr << "Client " << name << " unknown apperance " << (int)sa->parameter << endl;
							break;
						}
						
						entity_list.QueueClients(this, app, true);
					}
					else if (sa->type == APPEAR_ANON) {
						// For Anon/Roleplay
						if (sa->parameter == 1) { // Anon
							m_pp.anon = 1;
						}
						else if ((sa->parameter == 2) || (sa->parameter == 3)) { // This is Roleplay, or anon+rp
							m_pp.anon = 2;
						}
						else if (sa->parameter == 0) { // This is Non-Anon
							m_pp.anon = 0;
						}
						else {
							cerr << "Client " << name << " unknown Anon/Roleplay Switch " << (int)sa->parameter << endl;
							break;
						}
						entity_list.QueueClients(this, app, true);
						UpdateWho();
					}
					else if ((sa->type == APPEAR_HP_TIC) && (dead == 0)) {
						// this is the client notifing the server of a hp regen tic
						sint32 hpregen = itembonuses->HPRegen;
						sint32 normalregen = LevelRegen();
						sint32 spellbonuses = CastToMob()->GetSpellHPRegen();
#if EQDEBUG>=11
						LogFile->write(EQEMuLog::Debug, " Regen tick: for %s - server hp: %d requested hp: %d normal regen: %i bonus regen: %i spell regen: %i", GetName(), GetHP(), sa->parameter,normalregen,hpregen,spellbonuses);
#endif						
						// client tends to send spurious hp regens where their hp doesn't
						// differ from what the server thinks it is.  seems to be done
						// to solicit an hp update so we'll give em one.  if you cut out
						// the hp update to  save bandwidth you're probably gonna put
						// the clients out of sync; further testing is needed  -solar
						if(GetHP() != sa->parameter)
						{
							if(hpregen_timer->Check())
								SetHP(GetHP()+hpregen+normalregen+spellbonuses);
							else
								LogFile->write(EQEMuLog::Debug, "Client %s sent a regen tick but the timer is not up.", GetName());
						}
						SendHPUpdate();
					}
					else if (sa->type == APPEAR_AFK) {
						this->AFK = (sa->parameter == 1);
						entity_list.QueueClients(this, app, true);
					}
					else if (sa->type == APPEAR_SNEAK) {
						this->sneaking = (sa->parameter == 1);
						entity_list.QueueClients(this, app, true);
					}
					else if (sa->type == APPEAR_HEIGHT)
					{
						entity_list.QueueClients(this, app, false);
					}
					else {
						cout << "Unknown SpawnAppearance type: 0x" << hex << setw(4) << setfill('0') << sa->type << dec
							<< " value: 0x" << hex << setw(8) << setfill('0') << sa->parameter << dec << endl;
					}
					break;
				}
				case OP_BazaarInspect:{
					if (app->size != sizeof(BazaarInspect_Struct)) {
						LogFile->write(EQEMuLog::Error, "Invalid size for BazaarInspect_Struct: Expected %i, Got %i",
							sizeof(BazaarInspect_Struct), app->size);
						break;
					}
					
					BazaarInspect_Struct* bis = (BazaarInspect_Struct*)app->pBuffer;
					const Item_Struct* item = database.GetItem(bis->item_id);
				
					if (!item) {
						Message(13, "Error: This item does not exist!");
						break;
					}
					
					ItemInst* inst = ItemInst::Create(item);
					if (inst) {
						SendItemPacket(0, inst, ItemPacketViewLink);
						safe_delete(inst);
					}
					
					break;
				}
				case OP_Death: {
					if(app->size != sizeof(Death_Struct))
						break;
					
					Death_Struct* ds = (Death_Struct*)app->pBuffer;
					
					if(GetHP() > 0)
						break;
					
					Mob* killer = entity_list.GetMob(ds->killer_id);
					Death(killer, ds->damage, ds->spell_id,ds->type);
					break;
				}
				case OP_MoveCoin: {
					MoveCoin_Struct* mc = (MoveCoin_Struct*)app->pBuffer;
					if (mc->from_slot == 0) {
						switch (mc->cointype1)
						{
						case 0:
							if ((m_pp.copper_cursor - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.copper_cursor, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.copper_cursor = 0;
							break;
						case 1:
							if ((m_pp.silver_cursor - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.silver_cursor, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.silver_cursor = 0;
							break;
						case 2:
							if ((m_pp.gold_cursor - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.gold_cursor, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.gold_cursor = 0;
							break;
						case 3:
							if ((m_pp.platinum_cursor - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.platinum_cursor, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.platinum_cursor = 0;
							break;
						}
					}
					if (mc->from_slot == 1) {
						switch (mc->cointype1)
						{
						case 0:
							if ((m_pp.copper - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.copper, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.copper = m_pp.copper - mc->amount;
							break;
						case 1:
							if ((m_pp.silver - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.silver, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.silver = m_pp.silver - mc->amount;
							break;
						case 2:
							if ((m_pp.gold - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.gold, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.gold = m_pp.gold - mc->amount;
							break;
						case 3:
							if ((m_pp.platinum - mc->amount) < 0) {
								cout << "Error in OP_MoveCoin: negative value)" << endl;
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.platinum, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.platinum = m_pp.platinum - mc->amount;
							break;
						}
					}
					if (mc->from_slot == 2) {						
						switch (mc->cointype1)
						{
						case 0:
							if ((m_pp.copper_bank - mc->amount) < 0){
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.copper_bank, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.copper_bank = m_pp.copper_bank - mc->amount;
							break;
						case 1:
							if ((m_pp.silver_bank - mc->amount) < 0){
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.silver_bank, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.silver_bank = m_pp.silver_bank - mc->amount;
							break;
						case 2:
							if ((m_pp.gold_bank - mc->amount) < 0){
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.gold_bank, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.gold_bank = m_pp.gold_bank - mc->amount;
							break;
						case 3:
							if ((m_pp.platinum_bank - mc->amount) < 0){
								Message(15,"Cheater log updated...yup your busted,its not nice to cheat.");
								char descript[100]={0};
								sprintf(descript,"%s %i%s %i","Player trying to hack coins. Real Total:",m_pp.platinum_bank, ", Given Total:",mc->amount);
								database.logevents(AccountName(),AccountID(),admin,GetName(),"none","Money update cheat",descript,15);
								worldserver.SendEmoteMessage(0,0,0,13,"<Cheater Locator> We have found a cheater.  %s (Acct: %s) was just caught hacking, please show them what we think of hackers...",GetName(),AccountName());
								Kick();
							}
							else
								m_pp.platinum_bank = m_pp.platinum_bank - mc->amount;
							break;
						}
					}
					if (mc->to_slot == 0) {
						if (mc->cointype2!=mc->cointype1){
							if (mc->cointype2<mc->cointype1)
							{
								int amount2=mc->cointype1-mc->cointype2;

								amount2=(int)pow(10.0f,amount2);
								mc->amount=mc->amount*amount2;
							}
							else if (mc->cointype2>mc->cointype1){
								int amount2=mc->cointype2-mc->cointype1;
								amount2=(int)pow(10.0f,amount2);
								mc->amount=(int)((mc->amount)/(amount2));
							}
						}
						switch (mc->cointype2)
						{
						case 0:
							m_pp.copper_cursor += mc->amount;
							break;
						case 1:
							m_pp.silver_cursor += mc->amount;
							break;
						case 2:
							m_pp.gold_cursor += mc->amount;
							break;
						case 3:
							m_pp.platinum_cursor += mc->amount;
							break;
						}
					}
					if (mc->to_slot == 1) {
						if (mc->cointype2!=mc->cointype1){
							if (mc->cointype2<mc->cointype1)
							{
								int amount2=mc->cointype1-mc->cointype2;

								amount2=(int)pow(10.0f,amount2);
								mc->amount=mc->amount*amount2;
							}
							else if (mc->cointype2>mc->cointype1){
								int amount2=mc->cointype2-mc->cointype1;
								amount2=(int)pow(10.0f,amount2);
								mc->amount=(int)((mc->amount)/(amount2));
							}
						}
						switch (mc->cointype2)
						{
						case 0:
							AddMoneyToPP(mc->amount,0,0,0,false);
							break;
						case 1:
							AddMoneyToPP(0,mc->amount,0,0,false);
							break;
						case 2:
							AddMoneyToPP(0,0,mc->amount,0,false);
							break;
						case 3:
							AddMoneyToPP(0,0,0,mc->amount,false);
							break;
						}
					}
					if (mc->to_slot==2) { // BANK Money
						if (mc->cointype2!=mc->cointype1){
							if (mc->cointype2<mc->cointype1) {
								int amount2=mc->cointype1-mc->cointype2;
								amount2=(int)pow(10.0f,amount2);
								mc->amount=mc->amount*amount2;
							}
							else if (mc->cointype2>mc->cointype1){
								int amount2=mc->cointype2-mc->cointype1;
								amount2=(int)pow(10.0f,amount2);

								mc->amount=(int)((mc->amount)/(amount2));
							}
						}
						switch (mc->cointype2)
						{
						case 0:
							m_pp.copper_bank=m_pp.copper_bank + mc->amount;
							break;
						case 1:
							m_pp.silver_bank=m_pp.silver_bank + mc->amount;
							break;
						case 2:
							m_pp.gold_bank=m_pp.gold_bank + mc->amount;
							break;
						case 3:
							m_pp.platinum_bank=m_pp.platinum_bank + mc->amount;
							break;
						}
					}
					if (mc->to_slot == 3) {
						Mob* trader = trade->With();
						if (trader) {
							switch (mc->cointype1) {
							case COINTYPE_CP:
								this->trade->cp += mc->amount;
								break;
							case COINTYPE_SP:
								this->trade->sp += mc->amount;
								break;
							case COINTYPE_GP:
								this->trade->gp += mc->amount;
								break;
							case COINTYPE_PP:
								this->trade->pp += mc->amount;
								break;
							}
							
							if (trader->IsClient()) {
								Client* recipient = trader->CastToClient();
								recipient->Message(15, "%s adds some coins to the trade.", this->GetName());
								recipient->Message(15, "The total trade is: %i PP, %i GP, %i SP, %i CP",
									trade->pp, trade->gp,
									trade->sp, trade->cp);
								APPLAYER* outapp = new APPLAYER(OP_TradeCoins,sizeof(TradeCoin_Struct));
								TradeCoin_Struct* tcs = (TradeCoin_Struct*)outapp->pBuffer;
								tcs->trader=trader->GetID();
								tcs->slot=mc->cointype1;
								tcs->unknown5=0x4fD2;
								tcs->unknown7=0;
								tcs->amount=mc->amount;
								recipient->QueuePacket(outapp);
								safe_delete(outapp);
							}
						}
					}
					//Save();
					break;
				}
				case OP_ItemLinkClick: {
					if(app->size != sizeof(ItemViewRequest_Struct)){
						LogFile->write(EQEMuLog::Error, "Wrong size on OP_ItemLinkClick.  Got: %i, Expected: %i", app->size, sizeof(ItemViewRequest_Struct));
						DumpPacket(app);
						break;
					}
					
					ItemViewRequest_Struct* ivrs = (ItemViewRequest_Struct*)app->pBuffer;
					
					const Item_Struct* item = database.GetItem(ivrs->item_id);
					if (!item) {
						Message(13, "Error: The item for the link you have clicked on does not exist!");
						break;
					}
					
					ItemInst* inst = ItemInst::Create(item);
					if (inst) {
						SendItemPacket(0, inst, ItemPacketViewLink);
						safe_delete(inst);
					}
					break;
				}
				case OP_MoveItem: {
					if(this->CharacterID()==0)
						break;
					if (app->size != sizeof(MoveItem_Struct)) {
						LogFile->write(EQEMuLog::Error, "Wrong size: OP_MoveItem, size=%i, expected %i", app->size, sizeof(MoveItem_Struct));
						break;
					}
					
					MoveItem_Struct* mi = (MoveItem_Struct*)app->pBuffer;
					SwapItem(mi);
					break;
				}
				case OP_Camp: {
					Save();
					if (GetGM() && admin >= 100) {
						Disconnect();
					}
					// TODO: Implement camp, LD and all that
					// camp_timer->Start(30000);
					break;
				}
				//solar: this isn't used anymore, the client doesn't send a packet
				case OP_SenseHeading: {
					if (rand()%100 <= 15 && (GetSkill(SENSE_HEADING) < 200) && (GetSkill(SENSE_HEADING) < this->GetLevel()*5+5)) {
						this->SetSkill(SENSE_HEADING, GetSkill(SENSE_HEADING) + 1);
					}
					break;
				}
				case OP_FeignDeath: {
					int16 primfeign = GetSkill(FEIGN_DEATH);
					int16 secfeign = GetSkill(FEIGN_DEATH);
					if (primfeign > 100) {
						primfeign = 100;
						secfeign = secfeign - 100;
						secfeign = secfeign / 2;
					}
					else
						secfeign = 0;
					
					int16 totalfeign = primfeign + secfeign;
					if (rand()%160 > totalfeign) {
						SetFeigned(false);
						// TODO: send to nearby clients as well
						Message_StringID(10,STRING_FEIGNFAILED,this->GetName(),0,0,0,0,0,0,0,0,200);
					}
					else {
						SetFeigned(true);
					}
					if (rand()%300 > GetSkill(FEIGN_DEATH) && rand()%4 == 1 && GetSkill(FEIGN_DEATH) < 200 && GetSkill(FEIGN_DEATH) < GetLevel()*5+5) {
						SetSkill(FEIGN_DEATH, GetSkill(FEIGN_DEATH) + 1);
					}
					break;
				}
				case OP_Sneak: {
					bool was = sneaking;
					if (sneaking){
						sneaking = false;
					}
					else {
						CheckIncreaseSkill(SNEAK,15);
					}
					float hidechance = (GetSkill(SNEAK)/300.0f) + .25;
					float random = (float)rand()/RAND_MAX;          
					if(!was && random < hidechance) {
						sneaking = true;
					}
					APPLAYER* outapp = new APPLAYER(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
					SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
					sa_out->spawn_id = GetID();
					sa_out->type = 0x0F;
					sa_out->parameter = sneaking;
					QueuePacket(outapp);
					safe_delete(outapp);
					if(GetClass() == ROGUE){
						if (sneaking){
							outapp = new APPLAYER(0x0202,12);
							uint8 rawData0[12] = { 0x5B, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
							memcpy(outapp->pBuffer,rawData0,12);
							QueuePacket(outapp);
							safe_delete(outapp)
						}
						else {
							outapp = new APPLAYER(0x0202,12);
							uint8 rawData0[12] = { 0x5C, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
							memcpy(outapp->pBuffer,rawData0,12);
							QueuePacket(outapp);
							safe_delete(outapp)
						}
					}
					break;
				}
				case OP_Hide: {
					float hidechance = (GetSkill(HIDE)/300.0f) + .25;
					float random = (float)rand()/RAND_MAX;		
					CheckIncreaseSkill(HIDE,15);					
					if (random < hidechance) {
						APPLAYER* outapp = new APPLAYER(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
						SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
						sa_out->spawn_id = GetID();
						sa_out->type = 0x03;
						this->invisible = true;
						sa_out->parameter = 1;
						entity_list.QueueClients(this, outapp, true);
						safe_delete(outapp);
						invisible = true;
					}
					if(GetClass() == ROGUE){
						if (invisible){
							APPLAYER* outapp = new APPLAYER(0x0202,12);
							uint8 rawData0[12] = { 0x5A, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
							memcpy(outapp->pBuffer,rawData0,12);
							QueuePacket(outapp);
							safe_delete(outapp)
						}
						else {
							APPLAYER* outapp = new APPLAYER(0x0202,12);
							uint8 rawData0[12] = { 0x59, 0x01, 0x00, 0x00, 0x0E, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
							memcpy(outapp->pBuffer,rawData0,12);
							QueuePacket(outapp);
							safe_delete(outapp)
						}
					}
					break;
				}
				case OP_ChannelMessage: {
					ChannelMessage_Struct* cm=(ChannelMessage_Struct*)app->pBuffer;
					
					if (app->size < sizeof(ChannelMessage_Struct)) {
						cout << "Wrong size " << app->size << ", should be " << sizeof(ChannelMessage_Struct) << "+ on 0x" << hex << setfill('0') << setw(4) << app->opcode << dec << endl;
						break;
					}
					if (IsAIControlled()) {
						Message(13, "You try to speak but cant move your mouth!");
						break;
					}
					
					ChannelMessageReceived(cm->chan_num, cm->language, &cm->message[0], cm->targetname);
					break;
				}
				case OP_WearChange: {
					if (app->size != sizeof(WearChange_Struct)) {
						cout << "Wrong size: OP_WearChange, size=" << app->size << ", expected " << sizeof(WearChange_Struct) << endl;
						//DumpPacket(app);
						break;
					}
					WearChange_Struct* wc=(WearChange_Struct*)app->pBuffer;
					if(wc->wear_slot_id>=0 && wc->wear_slot_id<7)
						m_pp.item_material[wc->wear_slot_id]=wc->material;
					entity_list.QueueClients(this, app, true);
					break;
				}
				case OP_ZoneChange: {
					if (app->size != sizeof(ZoneChange_Struct)) {
						cout << "Wrong size: OP_ZoneChange, size=" << app->size << ", expected " << sizeof(ZoneChange_Struct) << endl;
						break;
					}
					
#if EQDEBUG >= 5
						LogFile->write(EQEMuLog::Debug, "Zone request from %s", GetName());
						DumpPacket(app);
#endif
					ZoneChange_Struct* zc=(ZoneChange_Struct*)app->pBuffer;
					
					
					float tarx = -1, tary = -1, tarz = -1,tarheading=999;
					sint16 minstatus = 0;
					int8 minlevel = 0;
					sint8 myerror=ZONE_ERROR_NOTREADY;
					char target_zone[32] = {0};
					
					if (zc->zoneID == 0){
						if(strlen(zonesummon_name)==0)//Player Died
							strcpy(target_zone, zone->GetShortName());
						else
							strcpy(target_zone, zonesummon_name);
					}
					else if (database.GetZoneName(zc->zoneID))
						strcpy(target_zone, database.GetZoneName(zc->zoneID));
					else
						target_zone[0] = 0;
					
					// this both loads the safe points and does a sanity check on zone name
					if (!database.GetSafePoints(target_zone, &tarx, &tary, &tarz, &minstatus, &minlevel)) {
						target_zone[0] = 0;
					}
					int8 tmpzonesummon_ignorerestrictions = zonesummon_ignorerestrictions;
					if (zonesummon_ignorerestrictions) {
						minstatus = 0;
						minlevel = 0;
					}
					zonesummon_ignorerestrictions = 0;
					
					ZonePoint* zone_point = zone->GetClosestZonePoint(x_pos, y_pos, z_pos, zc->zoneID);
		
					tarx=zonesummon_x;
					tary=zonesummon_y;
					tarz=zonesummon_z;
					
					// -1, -1, -1 = code for zone safe point
					if ((x_pos == -1 && y_pos == -1 && (z_pos == -1 || z_pos == -10)) ||
						(zonesummon_x == -1 && zonesummon_y == -1 && (zonesummon_z == -1 || zonesummon_z == -10))) {
						cout << "Zoning to safe coords: " << target_zone << " (" << database.GetZoneID(target_zone) << ")" << endl;
						tarx=database.GetSafePoint(target_zone, "x");
						tary=database.GetSafePoint(target_zone, "y");
						tarz=database.GetSafePoint(target_zone, "z");
						zonesummon_x = -2;
						zonesummon_y = -2;
						zonesummon_z = -2;
					}
					// -3 -3 -3 = bind point
					else if (zonesummon_x == -3 && zonesummon_y == -3 && (zonesummon_z == -3 || zonesummon_z == -30) && database.GetZoneName(m_pp.bind_zone_id)) {
						strcpy(target_zone, database.GetZoneName(m_pp.bind_zone_id));
						tarx = m_pp.bind_x;
						tary = m_pp.bind_y;
						tarz = m_pp.bind_z;
						zonesummon_x = -2;
						zonesummon_y = -2;
						zonesummon_z = -2;
						minstatus = 0;
						minlevel = 0;
					}
					else if (zone_point != 0) {
						if(zone_point->target_x==999999)
							tarx=GetX();
						else
							tarx = zone_point->target_x;
						if(zone_point->target_y==999999)
							tary=GetY();
						else
							tary = zone_point->target_y;
						if(zone_point->target_z==999999)
							tarz=GetZ();
						else
							tarz = zone_point->target_z;
						tarheading = zone_point->target_heading;
					}
					// if not -2 -2 -2, zone to these coords. -2, -2, -2 = not a zonesummon zonerequest
					else if (!(zonesummon_x == -2 && zonesummon_y == -2 && (zonesummon_z == -2 || zonesummon_z == -20))) {
						tarx = zonesummon_x;
						tary = zonesummon_y;
						tarz = zonesummon_z;
						zonesummon_x = -2;
						zonesummon_y = -2;
						zonesummon_z = -2;
					}
					else {
						cout << "WARNING: No target coords for this zone in DB found" << endl;
						cout << "Zoning to safe coords: " << target_zone << " (" << database.GetZoneID(target_zone) << ")" << ", x=" << tarx << ", y=" << tary << ", z=" << tarz << endl;
						tarx=-1;
						tary=-1;
						tarz=-1;
						zonesummon_x = -2;
						zonesummon_y = -2;
						zonesummon_z = -2;
					}
					
					if (admin < minstatus || GetLevel() < minlevel)
						myerror = ZONE_ERROR_NOEXPERIENCE;
					
					APPLAYER* outapp = NULL;
					if (target_zone[0] != 0 && admin >= minstatus && GetLevel() >= minlevel) {
						LogFile->write(EQEMuLog::Status, "Zoning '%s' to: %s (%i) x=%f, y=%f, z=%f",
							m_pp.name, target_zone, database.GetZoneID(target_zone),
							tarx, tary, tarz);
						
						UpdateWho(1);
						
						x_pos = tarx; // Hmm, these coordinates will now be saved when ~client is called
						y_pos = tary;
						z_pos = tarz;
						if(tarheading!=999)
							m_pp.heading=tarheading;
						else
							m_pp.heading=heading;
						m_pp.zone_id = database.GetZoneID(target_zone);
						
						Save();
						
						if (m_pp.zone_id == zone->GetZoneID()) {
							// No need to ask worldserver if we're zoning to ourselves (most
							// likely to a bind point), also fixes a bug since the default response was failure
							APPLAYER* outapp = new APPLAYER(OP_ZoneChange,sizeof(ZoneChange_Struct));
							ZoneChange_Struct* zc2 = (ZoneChange_Struct*) outapp->pBuffer;
							strcpy(zc2->char_name, GetName());
							zc2->zoneID = m_pp.zone_id;
							zc2->success = 1;
							QueuePacket(outapp);
							safe_delete(outapp);
							zone->StartShutdownTimer(AUTHENTICATION_TIMEOUT * 1000);
						}
						else {
							ServerPacket* pack = new ServerPacket(ServerOP_ZoneToZoneRequest, sizeof(ZoneToZone_Struct));
							ZoneToZone_Struct* ztz = (ZoneToZone_Struct*) pack->pBuffer;
							ztz->response = 0;
							ztz->current_zone_id = zone->GetZoneID();
							ztz->requested_zone_id = database.GetZoneID(target_zone);
							ztz->admin = admin;
							ztz->ignorerestrictions = tmpzonesummon_ignorerestrictions;
							strcpy(ztz->name, GetName());
							worldserver.SendPacket(pack);
							safe_delete(pack);
						}
					}
					else {
						LogFile->write(EQEMuLog::Error, "Zone %i is not available", zc->zoneID);
						
						outapp = new APPLAYER(OP_ZoneChange, sizeof(ZoneChange_Struct));
						ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer;
						strcpy(zc2->char_name, zc->char_name);
						zc2->zoneID = zc->zoneID;
						zc2->success = myerror;
						memset(zc2->unknown0073, 0xff, sizeof(zc2->unknown0073));
						QueuePacket(outapp);
						safe_delete(outapp);
						
						int8 stuffdata[16] = {0xE6, 0x02, 0x10, 0x00, 0x00, 0x00, 0x68, 0x42, 0x00, 0x00, 0xDB, 0xC3, 0xFA, 0xFE, 0x00, 0xC2};
						outapp = new APPLAYER(0x2120, sizeof(stuffdata));
						memcpy(outapp->pBuffer, stuffdata, sizeof(stuffdata));
						QueuePacket(outapp);
						safe_delete(outapp);
					}
					//	entity_list.GetGroupByClient(this)->DelMember(this);
					break;
				}
				case OP_DeleteSpawn: {
					// The client will send this with his id when he zones, maybe when he disconnects too?
					RemoveData(); // Flushing the queue of packet data to allow for proper zoning -Kasai
					
					APPLAYER* outapp = new APPLAYER(OP_DeleteSpawn, sizeof(EntityId_Struct));
					EntityId_Struct* eid = (EntityId_Struct*)outapp->pBuffer;
					eid->entity_id = GetID();
					
					entity_list.QueueClients(this, outapp, false);
					safe_delete(outapp);
					
					Disconnect();
					break;
				}
				case OP_SaveOnZoneReq:
				case OP_Save: {
					// The payload is 192 bytes - Not sure what is contained in payload
					Save();
					break;
				}
				case OP_WhoAllRequest: {
					if (app->size != sizeof(Who_All_Struct)) {
						cout << "Wrong size on OP_WhoAll. Got: " << app->size << ", Expected: " << sizeof(Who_All_Struct) << endl;
						//DumpPacket(app);
						break;
					}
					Who_All_Struct* whoall = (Who_All_Struct*) app->pBuffer;
					WhoAll(whoall);
					break;
				}
				case OP_GMZoneRequest: {
					if (app->size != sizeof(GMZoneRequest_Struct)) {
						cout << "Wrong size on OP_GMZoneRequest. Got: " << app->size << ", Expected: " << sizeof(GMZoneRequest_Struct) << endl;
						break;
					}
					if (this->Admin() < 80) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/zone");
						break;
					}
					
					GMZoneRequest_Struct* gmzr = (GMZoneRequest_Struct*)app->pBuffer;
					float tarx = -1, tary = -1, tarz = -1;
					
					sint16 minstatus = 0;
					int8 minlevel = 0;
					char tarzone[32];
					if (gmzr->zone_id == 0)
						strcpy(tarzone, zonesummon_name);
					else if (database.GetZoneName(gmzr->zone_id))
						strcpy(tarzone, database.GetZoneName(gmzr->zone_id));
					else
						tarzone[0] = 0;
					
					// this both loads the safe points and does a sanity check on zone name
					if (!database.GetSafePoints(tarzone, &tarx, &tary, &tarz, &minstatus, &minlevel)) {
						tarzone[0] = 0;
					}
					
					APPLAYER* outapp = new APPLAYER(OP_GMZoneRequest, sizeof(GMZoneRequest_Struct));
					GMZoneRequest_Struct* gmzr2 = (GMZoneRequest_Struct*) outapp->pBuffer;
					strcpy(gmzr2->charname, this->GetName());
					gmzr2->zone_id = gmzr->zone_id;
					gmzr2->x = tarx;
					gmzr2->y = tary;
					gmzr2->z = tarz;
					// Next line stolen from ZoneChange as well... - This gives us a nicer message than the normal "zone is down" message...
					if (tarzone[0] != 0 && admin >= minstatus && GetLevel() >= minlevel)
						gmzr2->success = 1;
					else {
						cout << "GetZoneSafeCoords failed. zoneid = " << gmzr->zone_id << "; czone = " << zone->GetZoneID() << endl;
						gmzr2->success = 0;
					}
					
					QueuePacket(outapp);
					safe_delete(outapp);
					break;
				}
				case OP_GMZoneRequest2: {
					int32 zonereq = (int32)*app->pBuffer;
					if(zonereq == zone->GetZoneID())
						this->MovePC(zonereq, zone->safe_x(), zone->safe_y(), zone->safe_z(), 0, false);
					else
						this->MovePC(zonereq, -1, -1, -1, 0, false);
					break;
				}
				case OP_EndLootRequest: {
					if (app->size != sizeof(int32)) {
						cout << "Wrong size: OP_EndLootRequest, size=" << app->size << ", expected " << sizeof(int32) << endl;
						break;
					}
					
					Entity* entity = entity_list.GetID(*((int16*)app->pBuffer));
					if (entity == 0) {
						//DumpPacket(app);
						Message(13, "Error: OP_EndLootRequest: Corpse not found (ent = 0)");
						Corpse::SendLootReqErrorPacket(this);
						break;
					}
					else if (!entity->IsCorpse()) {
						Message(13, "Error: OP_EndLootRequest: Corpse not found (!entity->IsCorpse())");
						Corpse::SendLootReqErrorPacket(this);
						break;
					}
					else {
						entity->CastToCorpse()->EndLoot(this, app);
					}
					break;
				}
				case OP_LootRequest: {
					if (app->size != sizeof(int32)) {
						cout << "Wrong size: OP_LootRequest, size=" << app->size << ", expected " << sizeof(int32) << endl;
						break;
					}
					
					Entity* ent = entity_list.GetID(*((int32*)app->pBuffer));
					if (ent == 0) {
						Message(13, "Error: OP_LootRequest: Corpse not found (ent = 0)");
						Corpse::SendLootReqErrorPacket(this);
						break;
					}
					if (ent->IsCorpse()) {
						ent->CastToCorpse()->MakeLootRequestPackets(this, app);
						break;
					}
					else {
						cout << "npc == 0 LOOTING FOOKED3" << endl;
						Message(13, "Error: OP_LootRequest: Corpse not a corpse?");
						Corpse::SendLootReqErrorPacket(this);
					}
					break;
				}
				case OP_Dye:{
					if(app->size!=sizeof(DyeStruct))
						printf("Wrong size of DyeStruct, Got: %i, Expected: %i\n",app->size,sizeof(DyeStruct));
					else{
						DyeStruct* dye = (DyeStruct*)app->pBuffer;
						DieArmor(dye);
					}
					break;
				}
				case OP_LootItem: {
					if (app->size != sizeof(LootingItem_Struct)) {
						LogFile->write(EQEMuLog::Error, "Wrong size: OP_LootItem, size=%i, expected %i", app->size, sizeof(LootingItem_Struct));
						break;
					}
					/*
					** Disgrace:
					**	fixed the looting code so that it sends the correct opcodes
					**	and now correctly removes the looted item the player selected
					**	as well as gives the player the proper item.
					**	Also fixed a few UI lock ups that would occur.
					*/
					
					APPLAYER* outapp = 0;
					Entity* entity = entity_list.GetID(*((int16*)app->pBuffer));
					if (entity == 0) {
						Message(13, "Error: OP_LootItem: Corpse not found (ent = 0)");
						outapp = new APPLAYER(OP_LootComplete, 0);
						QueuePacket(outapp);
						safe_delete(outapp);
						break;
					}
					
					if (entity->IsCorpse()) {
						entity->CastToCorpse()->LootItem(this, app);
						break;
					}
					else {
						Message(13, "Error: Corpse not found! (!ent->IsCorpse())");
						Corpse::SendEndLootErrorPacket(this);
					}
					
					break;
				}
				case OP_GuildMOTD: {
					char tmp[600]={0};
					if (app->size !=sizeof(GuildMOTD_Struct)) {
						// client calls for a motd on login even if they arent in a guild
						printf("Error: app size of %i != size of GuildMOTD_Struct of %i\n",app->size,sizeof(GuildMOTD_Struct));
						break;
					}
					GuildMOTD_Struct* gmotd=(GuildMOTD_Struct*)app->pBuffer;
					if (guilds[guildeqid].rank[guildrank].motd && (strlen(gmotd->motd)>0)) {
						sprintf(tmp,"%s - %s",gmotd->name,gmotd->motd);
						if (!database.SetGuildMOTD(guilddbid, tmp)) {
							Message(0, "Motd update failed.");
						}
						worldserver.SendEmoteMessage(0, guilddbid, MT_Guild, "Guild MOTD: %s", tmp);
					}
					else {
						strcpy(tmp,database.GetGuildMOTD(guilddbid));
						if (strlen(tmp) != 0)
							Message_StringID(MT_Guild,GENERIC_STRING,tmp);
							//Message(MT_Guild, "Guild MOTD: %s", tmp);
						//Message(MT_Guild, "Guild MOTD Disabled for now");
					}
					
					break;
				}
				case OP_GuildPeace: {
#ifdef GUILDWARS
				if(GuildRank() != 0 || GuildDBID() == 0)
					Message(0,"You are not a guild leader or not in a guild.");
				else if(GetTarget() == 0 || !GetTarget()->IsClient() || GetTarget()->CastToClient()->GuildDBID() == 0 || GetTarget()->CastToClient()->GuildRank() != 0 || (GetTarget()->CastToClient()->GuildDBID() == GuildDBID()))
					Message(0,"You require a guild leader target.");
				else
				{
					if(GetTarget()->CastToClient()->guildtarget == CastToClient() && GetTarget()->CastToClient()->guildfaction == 0)
					{
					database.UpdateGuildFaction(GuildDBID(),GetTarget()->CastToClient()->GuildDBID(),GW_ALLY);
					database.UpdateGuildFaction(GetTarget()->CastToClient()->GuildDBID(),GuildDBID(),GW_ALLY);
					}
					else
					{
					guildfaction = 0;
					guildtarget = GetTarget()->CastToClient();
					GetTarget()->Message(0,"%s has asked for a guild alliance, target %s and type /guildpeace to accept.",GetName(),GetName());
					Message(0,"You offer a peace treaty to %s",GetTarget()->GetName());
					}
				}
#endif
					break;
				}
				case OP_GuildWar: {
#ifdef GUILDWARS
				if(GuildRank() != 0 || GuildDBID() == 0)
					Message(0,"You are not a guild leader or not in a guild.");
				else if(GetTarget() == 0 || !GetTarget()->IsClient() || GetTarget()->CastToClient()->GuildDBID() == 0 || (GetTarget()->CastToClient()->GuildDBID() == GuildDBID()))
					Message(0,"You require a target in another guild.");
				else
				{
					database.UpdateGuildFaction(GetTarget()->CastToClient()->GuildDBID(),GuildDBID(),GW_DEFAULTFACTION);
				}
#endif
					break;
				}
				case OP_GuildLeader: {
					if (app->size <= 1)
						break;
					app->pBuffer[app->size-1] = 0;
					GuildMakeLeader* gml=(GuildMakeLeader*)app->pBuffer;
					if (guilddbid == 0)
						Message(0, "Error: You arent in a guild!");
					else if (!(guilds[guildeqid].leader == account_id))
						Message(0, "Error: You arent the guild leader!");
					else if (!worldserver.Connected())
						Message(0, "Error: World server disconnected");
					else {
						ServerPacket* pack = new ServerPacket;
						pack->opcode = ServerOP_GuildLeader;
						pack->size = sizeof(ServerGuildCommand_Struct);
						pack->pBuffer = new uchar[pack->size];
						memset(pack->pBuffer, 0, pack->size);
						ServerGuildCommand_Struct* sgc = (ServerGuildCommand_Struct*) pack->pBuffer;
						sgc->guilddbid = guilddbid;
						sgc->guildeqid = guildeqid;
						sgc->fromrank = guildrank;
						sgc->fromaccountid = account_id;
						sgc->admin = admin;
						strcpy(sgc->from, name);
						strcpy(sgc->target, gml->target);
						worldserver.SendPacket(pack);
						safe_delete(pack);
					}
					break;
				}
				case OP_GuildDemote:{
					if(app->size==sizeof(GuildDemoteStruct)){
						GuildDemoteStruct* gds = (GuildDemoteStruct*) app->pBuffer;
						ServerPacket* pack = new ServerPacket(ServerOP_GuildDemote,sizeof(ServerGuildCommand_Struct));
						memset(pack->pBuffer, 0, pack->size);
						ServerGuildCommand_Struct* sgc = (ServerGuildCommand_Struct*) pack->pBuffer;
						sgc->guilddbid = guilddbid;
						sgc->guildeqid = guildeqid;
						sgc->fromrank = guildrank;
						sgc->newrank= GUILD_MAX_RANK;
						sgc->fromaccountid = account_id;
						sgc->admin = admin;
						strcpy(sgc->from, name);
						strcpy(sgc->target, gds->target);
						worldserver.SendPacket(pack);
						safe_delete(pack);
					}
					break;
				}
				case OP_GuildInvite: {
					if (app->size != sizeof(GuildCommand_Struct)) {
						cout << "Wrong size: OP_GuildInvite, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << endl;
						break;
					}
					if (guilddbid == 0)
						Message(0, "Error: You arent in a guild!");
					else if (!guilds[guildeqid].rank[guildrank].invite)
						Message(0, "You dont have permission to invite.");
					else if (!worldserver.Connected())
						Message(0, "Error: World server disconnected");
					#ifdef GUILDWARS
						//Set restrictions on invite locations
					#endif
					else {
						#ifdef GUILDWARS
							if (database.NumberInGuild(guilddbid)>MAXMEMBERS){
								Message(15,"Your Guild has reached its Guildwars size limit.  You cannot invite any more people.");
								break;
							}
						#endif
						
						GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer;
						ServerPacket* pack = new ServerPacket;
						pack->opcode = ServerOP_GuildInvite;
						pack->size = sizeof(ServerGuildCommand_Struct);
						pack->pBuffer = new uchar[pack->size];
						memset(pack->pBuffer, 0, pack->size);
						ServerGuildCommand_Struct* sgc = (ServerGuildCommand_Struct*) pack->pBuffer;
						sgc->guilddbid = guilddbid;
						sgc->guildeqid = guildeqid;
						if(gc->officer)
							sgc->fromrank = 1;
						else
							sgc->fromrank = guildrank;
						sgc->fromaccountid = account_id;
						sgc->admin = admin;
						strcpy(sgc->from, name);
						strcpy(sgc->target, gc->othername);
						worldserver.SendPacket(pack);
						safe_delete(pack);
					}
					break;
				}
				case OP_GuildRemove: {
					if (app->size != sizeof(GuildCommand_Struct)) {
						cout << "Wrong size: OP_GuildRemove, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << endl;
						break;
					}
					GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer;
					if (guilddbid == 0)
						Message(0, "Error: You arent in a guild!");
					else if (!(guilds[guildeqid].rank[guildrank].remove || strcasecmp(gc->othername, this->GetName()) == 0))
						Message(0, "You dont have permission to remove.");
					else if (!worldserver.Connected())
						Message(0, "Error: World server disconnected");
					#ifdef GUILDWARS
						//Restrictions on location to where you can remove a member
					#endif
					else {
						ServerPacket* pack = new ServerPacket(ServerOP_GuildRemove,sizeof(ServerGuildCommand_Struct));
						memset(pack->pBuffer, 0, pack->size);
						ServerGuildCommand_Struct* sgc = (ServerGuildCommand_Struct*) pack->pBuffer;
						sgc->guilddbid = guilddbid;
						sgc->guildeqid = guildeqid;
						sgc->fromrank = guildrank;
						sgc->fromaccountid = account_id;
						sgc->admin = admin;
						strcpy(sgc->from, name);
						strcpy(sgc->target, gc->othername);
						worldserver.SendPacket(pack);
						safe_delete(pack);
					}
					break;
				}
				case OP_GuildInviteAccept: {
					if (app->size != sizeof(GuildCommand_Struct)) {
						cout << "Wrong size: OP_GuildInviteAccept, size=" << app->size << ", expected " << sizeof(GuildCommand_Struct) << endl;
						break;
					}
					GuildCommand_Struct* gc = (GuildCommand_Struct*) app->pBuffer;
					if (gc->guildeqid == 0) {
						int32 tmpeq = database.GetGuildEQID(this->PendingGuildInvite);
						if (guilddbid != 0)
							Message(0, "Error: You're already in a guild!");
						else if (!worldserver.Connected())
							Message(0, "Error: World server disconnected");
						else if (this->PendingGuildInvite == 0)
							Message(0, "Error: No guild invite pending.");
						else {
							this->PendingGuildInvite = 0;
							if (this->SetGuild(guilds[tmpeq].databaseID, GUILD_MAX_RANK))
								worldserver.SendEmoteMessage(0, guilds[tmpeq].databaseID, MT_Guild, "%s has joined the guild. Rank: %s.", this->GetName(), guilds[tmpeq].rank[GUILD_MAX_RANK].rankname);
							else {
								worldserver.SendEmoteMessage(gc->othername, 0, 0, "Guild invite accepted, but failed.");
								Message(0, "Guild invite accepted, but failed.");
							}
						}
					}
					else if (gc->guildeqid == 4) {
						this->PendingGuildInvite = 0;
						worldserver.SendEmoteMessage(gc->othername, 0, 0, "%s's guild invite window timed out.", this->GetName());
					}
					else if (gc->guildeqid == 5) {
						this->PendingGuildInvite = 0;
						worldserver.SendEmoteMessage(gc->othername, 0, 0, "%s has declined to join the guild.", this->GetName());
					}
					else {
						this->PendingGuildInvite = 0;
						SetGuild(0, GUILD_MAX_RANK);
						worldserver.SendEmoteMessage(gc->othername, 0, 0, "Unknown response from %s to guild invite.", this->GetName());
					}
					break;
				}
                case OP_ManaChange: {
					if(app->size == 0) {
						// i think thats the sign to stop the songs
						if (bardsong != 0) {
							LogFile->write(EQEMuLog::Debug, "BARD: stop song requested bardsong:%i", bardsong);
							bardsong = 0;
							bardsong_timer->Disable();
							spellend_timer->Disable();
						} else {
							LogFile->write(EQEMuLog::Debug, "BARD: stop song requested bardsong:NULL");
							bardsong_timer->Disable();
						}
						break;
					}
					ManaChange_Struct* p = (ManaChange_Struct*)app->pBuffer;
					Mob::SetMana(p->new_mana);
					break;
				}
				case OP_MemorizeSpell: {
					OPMemorizeSpell(app);
					break;
				}
				case OP_SwapSpell: {
					if (app->size != sizeof(SwapSpell_Struct)) {
						cout << "Wrong size on OP_SwapSpell. Got: " << app->size << ", Expected: " << sizeof(SwapSpell_Struct) << endl;
						break;
					}
					const SwapSpell_Struct* swapspell = (const SwapSpell_Struct*) app->pBuffer;
					short swapspelltemp;
					
					swapspelltemp = m_pp.spell_book[swapspell->from_slot];
					m_pp.spell_book[swapspell->from_slot] = m_pp.spell_book[swapspell->to_slot];
					m_pp.spell_book[swapspell->to_slot] = swapspelltemp;
					
					QueuePacket(app);
					break;
				}
				case OP_CastSpell: {
					if (app->size != sizeof(CastSpell_Struct)) {
						cout << "Wrong size: OP_CastSpell, size=" << app->size << ", expected " << sizeof(CastSpell_Struct) << endl;
						break;
					}
					if (IsAIControlled()) {
						this->SimpleMessage_StringID(13,NOT_IN_CONTROL);
						//Message(13, "You cant cast right now, you arent in control of yourself!");
						break;
					}
					
					CastSpell_Struct* castspell = (CastSpell_Struct*)app->pBuffer;

#ifdef GUILDWARS
	if(IsClient() && castspell->spell_id == 770)
	{
#ifdef GWDEBUG
		printf("NPCPurchase: Spell: 770, IsClient()\n");
#endif
	InterruptSpell(castspell->spell_id);
	ItemInst* item= NULL;
	item = CastToClient()->m_inv.GetItem(13);
	if(item == 0)
	{
#ifdef GWDEBUG
		printf("NPCPurchase: Unable to get item, slotid 13\n");
#endif
	break; // Something is wrong
	}
	else if(item->GetCharges() == 0)
	{
#ifdef GWDEBUG
		printf("NPCPurchase: GetCharges() == 0\n");
#endif
	break; // No charges, I don't think so.
	}
	else
	{
	int32 itemid = item->GetItem()->ItemNumber;
	int32 npcid = 0;
	switch(itemid)
	{
	case 120: // LowLvlGuard
		{
		npcid = 180200;
		break;
		}
	case 121: // MedLvlGuard
		{
		npcid = 180201;
		break;
		}
	case 122: // MedHighLvlGuard
		{
		npcid = 180202;
		break;
		}
	case 123: // HighLvlGuard
		{
		npcid = 180203;
		break;
		}
	case 127: // Translocator
		{
		npcid = 180204;
		break;
		}
	case 124: // Cleric
		{
		npcid = 180205;
		break;
		}
	case 126: // Magician
		{
		npcid = 180206;
		break;
		}
	case 125: // Enchanter
		{
		npcid = 180207;
		break;
		}
	}
	if(npcid != 0 && guildwars.PurchaseNPC(CastToClient(),npcid))
	this->DeleteItemInInventory(13,1,true);
	}
	break;
	}
#endif

#ifdef _EQDEBUG
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[0], castspell->cs_unknown[0]);
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[1], castspell->cs_unknown[1]);
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[2], castspell->cs_unknown[2]);
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: %u %i", (uint8)castspell->cs_unknown[3], castspell->cs_unknown[3]);
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %u", &castspell->cs_unknown, *(uint32*) castspell->cs_unknown );
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: 32 %p %i", &castspell->cs_unknown, *(int32*) castspell->cs_unknown );
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %u %u", &castspell->cs_unknown, *(uint16*) castspell->cs_unknown, *(uint16*) castspell->cs_unknown+sizeof(uint16) );
						LogFile->write(EQEMuLog::Debug, "cs_unknown2: 16 %p %i %i", &castspell->cs_unknown, *(int16*) castspell->cs_unknown, *(int16*) castspell->cs_unknown+sizeof(int16) );
#endif

					if (bardsong != 0 && IsBardSong(castspell->spell_id)) {
						LogFile->write(EQEMuLog::Normal, "BARD: already casting disabling bardsong:%i", bardsong);
						//bardsong = 0;
						//bardsong_timer->Disable();
						SendSpellBarDisable(false);
						break;
					}
					if (castspell->slot == 10) { // this means right-click
						if (castspell->inventoryslot < 30) { // santity check
							const ItemInst* inst = m_inv[castspell->inventoryslot]; //@merth: slot values are sint16, need to check packet on this field
							
							//bool cancast = true;
							if (inst && inst->IsType(ItemTypeCommon)) {
								const Item_Struct* item = inst->GetItem();
								if ((item->Common.EffectType == 1) || (item->Common.EffectType == 3) || (item->Common.EffectType == 4) || (item->Common.EffectType == 5)) {
									if (item->Common.CastTime == 0)
										SpellFinished(item->Common.SpellId, castspell->target_id, castspell->slot, 0);
									else
										CastSpell(item->Common.SpellId, castspell->target_id, castspell->slot, item->Common.CastTime,-1,0,castspell->inventoryslot);
								}
								else {
									Message(0, "Error: unknown item->Common.EffectType (0x%02x)", item->Common.EffectType);
								}
								int8 charges=inst->GetItem()->Common.MaxCharges;
								if(charges<255){
										DeleteItemInInventory(castspell->inventoryslot,1,true);
								}
							}
							else
								Message(0, "Error: item not found for inventory slot #%i", castspell->inventoryslot);
						}
						else
							Message(0, "Error: castspell->inventoryslot >= 30 (0x%04x)", castspell->inventoryslot);
					}
					else{
						CastSpell(castspell->spell_id, castspell->target_id, castspell->slot);
						if(castspell->slot==9){ //ability, LoH, HT, etc
							int32 ability=0;
							if(GetClass()==PALADIN)
								ability=87;
							else if(GetClass()==SHADOWKNIGHT)
								ability=89;
							else{
								printf("Unknown ability being used by %s, spell being cast is: %i\n",GetName(),castspell->spell_id);
								break;
							}
							time_t timestamp=time(NULL);
							database.UpdateAATimers(CharacterID(),4320,0,ability);//72 minutes
							AbilityTimer=true;
						}
						else if(castspell->slot<=8)
							CheckIncreaseSkill(CHANNELING);
					}
					//	if (IsBardSong(castspell->spell_id) && spells[castspell->spell_id].targettype == ST_Self || spells[castspell->spell_id].targettype == ST_Group || spells[castspell->spell_id].targettype == ST_AECaster)
					//		castspell->target_id = GetID();
					//	
					//	SimpleCheckIncreaseSkill(spells[castspell->spell_id].skill);
					//}
					break;
				}
				case OP_ConsumeAmmo: {
					if (app->size != sizeof(CombatAbility_Struct)) {
						cout << "Wrong size on OP_ConsumeAmmo. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << endl;
						break;
					}
					
					// @merth: Need to figure out which slot to delete from .. or maybe m_id is the slot?
					CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer;
					DeleteItemInInventory(ca_atk->m_id, 1);
					break;
				}
				case OP_CombatAbility: {
					if (app->size != sizeof(CombatAbility_Struct)) {
						cout << "Wrong size on OP_CombatAbility. Got: " << app->size << ", Expected: " << sizeof(CombatAbility_Struct) << endl;
						break;
					}
					
					if (target) {
						if(!IsAttackAllowed(target))
							break;
						
						CombatAbility_Struct* ca_atk = (CombatAbility_Struct*) app->pBuffer;
						if ((ca_atk->m_atk == 100)&&(ca_atk->m_type==10)) {    // SLAM - Bash without a shield equipped
							DoAnim(7);
							sint32 dmg=(sint32) ((level/10)  * 3  * (GetSkill(BASH) + GetSTR() + level) / (700-GetSkill(BASH)));
							//this->Message(MT_Emote, "You Bash for a total of %d damage.",  dmg);
							target->Damage(this, dmg, 0xffff, BASH);
							
							CheckIncreaseSkill(BASH);
							
							if (GetClass()==WARRIOR&&(GetRace()==BARBARIAN||GetRace()==TROLL||GetRace()==OGRE)) { // large race warriors only *
								float wisebonus =  (m_pp.WIS > 200) ? 20 + ((m_pp.WIS - 200) * 0.05) : m_pp.WIS * 0.1;
								if (((55-(GetSkill(BASH)*0.240))+wisebonus > (float)rand()/RAND_MAX*100)&& (GetSkill(BASH)<(m_pp.level+1)*5))
										this->SetSkill(BASH,GetSkill(BASH)+1);
							}
							break;
						}
						
						if ((ca_atk->m_atk == 11)&&(ca_atk->m_type==51)) { //old was 51
							const ItemInst* RangeWeapon = m_inv[SLOT_RANGE];
							if (!RangeWeapon || !RangeWeapon->IsType(ItemTypeCommon)) {
								Message(0, "Error: RangeWeapon: GetItem(%i)==0, you have nothing to throw!", GetItemIDAt(SLOT_RANGE));
								break;
							}
							
							const Item_Struct* item = RangeWeapon->GetItem();
							uint8 WDmg = item->Common.Damage;
							// Throw stuff
							DoAnim(5);
							sint32 TotalDmg = 0;
							
							// borrowed this from attack.cpp
							// chance to hit
							
							float chancetohit = GetSkill(51) / 3.75;    // throwing
							
							if (GetLevel()-target->GetLevel() < 0) {
								chancetohit -= (float)((target->GetLevel()-GetLevel())*(target->GetLevel()-GetLevel()))/4;
							}
							
							int16 targetagi = target->GetAGI();
							int16 playerDex = (int16)GetDEX()/2;
							
							targetagi = (targetagi <= 200) ? targetagi:targetagi + ((targetagi-200)/5);
							chancetohit -= (float)targetagi*0.05;
							chancetohit += playerDex;
							chancetohit = (chancetohit > 0) ? chancetohit+30:30;
							chancetohit = chancetohit > 95 ? 95 : chancetohit; // cap to 95%
							
							uint8 levelBonus = (GetSTR()+GetLevel()+GetSkill(51)) / 100;
							uint8 MaxDmg = (WDmg)*levelBonus;
							if (MaxDmg == 0)
								MaxDmg = 1;
							TotalDmg = 1 + rand()%MaxDmg;
							
							// Hit?
							if (((float)rand()/RAND_MAX)*100 > chancetohit) {
									target->Damage(this, 0, 0xffff, THROWING);
							}
							else {
								//this->Message(MT_Emote, "You Hit for a total of %d damage.", TotalDmg);
								target->Damage(this, TotalDmg, 0xffff, THROWING);
							}
							// See if the player increases their skill - with cap
							float wisebonus =  (GetWIS() > 200) ? 20 + ((GetWIS() - 200) * 0.05) : GetWIS() * 0.1;
							
							if (((55-(GetSkill(51)*0.240))+wisebonus > (float)rand()/RAND_MAX*100)&& (GetSkill(51)<(GetLevel()+1)*5))
								this->SetSkill(51,GetSkill(51)+1);
							break;
						}
						
						if ((ca_atk->m_atk == 11)&&(ca_atk->m_type==7)) {
							DoAnim(9);
							
							const ItemInst* RangeWeapon = m_inv[SLOT_RANGE];
							const ItemInst* Ammo = m_inv[SLOT_AMMO];
							
							if (!RangeWeapon || !RangeWeapon->IsWeapon()) {
								Message(0, "Error: Rangeweapon: GetItem(%i)==0, you have nothing to throw!", GetItemIDAt(SLOT_RANGE));
								break;
							}
							if (!Ammo || !Ammo->IsWeapon()) {
								Message(0, "Error: Ammo: GetItem(%i)==0, you have nothing to throw!", GetItemIDAt(SLOT_RANGE));
								break;
							}				
		
							float chancetohit = 0;
							if(target){
								if(target->IsNPC())
									chancetohit = GetSkill(ARCHERY) / 3.75;
								else
									chancetohit = GetSkill(ARCHERY) / 4.75; //harder to hit players

								if (m_pp.level-target->GetLevel() < 0) {
									chancetohit -= (float)((target->GetLevel()-m_pp.level)*(target->GetLevel()-m_pp.level))/4;
								}
								
								int16 targetagi = target->GetAGI();
								int16 playerDex = (int16)(this->itembonuses->DEX + this->spellbonuses->DEX)/2;
								
								targetagi = (targetagi <= 200) ? targetagi:targetagi + ((targetagi-200)/5);
								chancetohit -= (float)targetagi*0.05;
								chancetohit += playerDex;
								chancetohit = (chancetohit > 0) ? chancetohit+30:30;
								chancetohit = chancetohit > 95 ? 95 : chancetohit; // cap to 95%
								
								// Hit?
								if (((float)rand()/RAND_MAX)*100 > chancetohit) {
									//this->Message(MT_Emote, "You missed your target");
									//this->Message_StringID(M,GENERIC_MISS,"You","your target.");
									target->Damage(this, 0, 0xffff, 0x07);
								}
								else {
									const Item_Struct* RangeItem = RangeWeapon->GetItem();
									const Item_Struct* AmmoItem = Ammo->GetItem();
									uint16 WDmg = RangeItem->Common.Damage;
									uint16 ADmg = AmmoItem->Common.Damage;
									
									uint16 levelBonus = (GetSTR()+GetLevel()+GetSkill(ARCHERY)) / 100;
									uint16 MaxDmg = (WDmg+ADmg)*levelBonus;
									
									sint32 TotalDmg = 0;
									sint32 critDmg = 0;
									
									if(GetClass()==RANGER) {
										critDmg = (sint32)(MaxDmg * 1.72);
									}
									
									if (MaxDmg == 0)
										MaxDmg = 1;
									TotalDmg = 1 + rand()%MaxDmg;
									if(target->IsClient()){ //Tone down pvp damage
										if(critDmg>0)
											critDmg-=critDmg/3;
										TotalDmg-=TotalDmg/3;
									}
									// no crits before level 12 cap is maxed
									if((GetClass()==RANGER)&&(GetSkill(ARCHERY)>65)&&(rand()%255<(GetSkill(ARCHERY)+playerDex)/2)&&(chancetohit > 85)) {
										if(target->IsNPC() && !target->IsMoving() && !target->IsRooted() && this->GetLevel()>50){
											if(this->GetGM())
												Message(0,"(GM ONLY) Doubling attack damage, npc isnt moving!");
											critDmg*=2;
										}
										char val1[20]={0};
										this->Message_StringID(MT_CritMelee,CRITICAL_HIT,GetName(),ConvertArray(critDmg,val1));
										//this->Message(MT_CritMelee, "You score a critical hit!(%d)", critDmg);
										target->Damage(this, critDmg, 0xffff, 0x07);
									}
									else {
										if(GetClass()==RANGER && !target->IsMoving() && !target->IsRooted() && this->GetLevel()>50){
											if(this->GetGM())
												Message(0,"(GM ONLY) Doubling attack damage, npc isnt moving!");
											TotalDmg*=2;
										}
										char hitname[64]={0};
										strncpy(hitname,target->GetName(),strlen(target->GetName())-2);
										char val1[20]={0};
										//Message_StringID(MT_Emote,HIT_NON_MELEE,"You",hitname,ConvertArray(TotalDmg,val1));
										//this->Message(MT_Emote, "You Hit for a total of %d non-melee damage.", TotalDmg);
										target->Damage(this, TotalDmg, 0xffff, 0x07);
									}
								}
							
								// See if the player increases their skill - with cap
								float wisebonus =  (GetWIS() > 200) ? 20 + ((GetWIS() - 200) * 0.05) : GetWIS() * 0.1;
								
								if (((55-(GetSkill(ARCHERY)*0.240))+wisebonus > (float)rand()/RAND_MAX*100)&& (GetSkill(ARCHERY)<(m_pp.level+1)*5) && GetSkill(ARCHERY) < 252)
									this->SetSkill(ARCHERY,GetSkill(ARCHERY)+1);
							}
							break;
						}
						float multiple=(GetLevel()/5);
						multiple++;
						switch(GetClass())
						{
						case WARRIOR:
							if (target!=this) {
									float dmg=((((GetSkill(KICK) + GetSTR() + GetLevel())/90)*multiple)+10) * (((float)rand()/RAND_MAX));
									if(target->IsClient())
										dmg*=.76;
									else{
										CheckIncreaseSkill(KICK);
										dmg*=1.2;//small increase for warriors
									}
									target->Damage(this, (int32)dmg, 0xffff, 0x1e);
									DoAnim(1);
							}
							break;
						case RANGER:
						case PALADIN:
						case SHADOWKNIGHT:
							if (target!=this) {
									float dmg=((((GetSkill(KICK) + GetSTR() + GetLevel())/100)*multiple)+5) * (((float)rand()/RAND_MAX));
									if(target->IsClient())
										dmg*=.67;
									else
										CheckIncreaseSkill(KICK);
									target->Damage(this, (int32)dmg, 0xffff, 0x1e);
									DoAnim(1);
							}
							break;
						case MONK:
							CheckIncreaseSkill(ca_atk->m_type);
							MonkSpecialAttack(target->CastToMob(), ca_atk->m_type);
							break;
						case ROGUE:
							if (ca_atk->m_atk == 100)
								{uint8 *aa_item = &(((uint8 *)&aa)[124]);// Chaotic backstab TODO make it do min damage
							if (target && BehindMob(target, GetX(), GetY())) // Player is behind target
							{
								// solar - chance to assassinate
								// TODO: it's set to 40% chance, should be a formula involving DEX
								float chance=0;
								if(
									level >= 60 && // player is 60 or higher
									target->GetLevel() <= 45 && // mob 45 or under
									!target->CastToNPC()->IsEngaged() && // not aggro
									target->GetHP()<=32000 &&
									(chance=rand()%100) < 40 // chance
									&& target->IsNPC()
									) {
									char temp[100];
									snprintf(temp, 100, "%s ASSASSINATES their victim!!", this->GetName());
									CheckIncreaseSkill(BACKSTAB);
									entity_list.MessageClose(this, 0, 200, 10, temp);
									RogueAssassinate(target);
								}
								else {
									RogueBackstab(target, m_inv.GetItem(SLOT_PRIMARY), GetSkill(BACKSTAB));
									if ((level > 54) && (target != 0)) {
										float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
										// Check for double attack with main hand assuming maxed DA Skill (MS)
										float random = (float)rand()/RAND_MAX;
										
										if(random < DoubleAttackProbability)		// Max 62.4 % chance of DA
											if(target && target->GetHP() > 0)
												RogueBackstab(target, m_inv.GetItem(SLOT_PRIMARY), GetSkill(BACKSTAB));
									}
									CheckIncreaseSkill(BACKSTAB);
								}
							}
							else if(*aa_item>0) {
								RogueBackstab(target, m_inv.GetItem(SLOT_PRIMARY), GetSkill(BACKSTAB));
								if ((level > 54) && (target != 0)) {
									float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
									CheckIncreaseSkill(BACKSTAB);
									// Check for double attack with main hand assuming maxed DA Skill (MS)
									float random = (float)rand()/RAND_MAX;
									if(random < DoubleAttackProbability)		// Max 62.4 % chance of DA
										if(target && target->GetHP() > 0)
											RogueBackstab(target, m_inv.GetItem(SLOT_PRIMARY), GetSkill(BACKSTAB));
								}
							}
							else {	// Player is in front of target
								Attack(target, 13);
								if ((level > 54) && (target != 0)) {
									float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
									
									// Check for double attack with main hand assuming maxed DA Skill (MS)
									float random = (float)rand()/RAND_MAX;
									if(random < DoubleAttackProbability)		// Max 62.4 % chance of DA
										if(target && target->GetHP() > 0)
											Attack(target, 13);
								}
							}
						}
						break;
						}
					}
					break;
				}
				case OP_Taunt: {
					if (this->GetTarget() == 0)
						break;
					if (!this->GetTarget()->IsNPC())
						break;
					sint32 newhate, tauntvalue;
					if (app->size != sizeof(ClientTarget_Struct)) {
						cout << "Wrong size on OP_Taunt. Got: " << app->size << ", Expected: "<< sizeof(ClientTarget_Struct) << endl;
						break;
					}
					
					// Check to see if we're already at the top of the target's hate list
					if ((target->CastToNPC()->GetHateTop() != this) && (target->GetLevel() < level))
					{

						// no idea how taunt success is actually calculated
						// TODO: chance for level 50+ mobs should be lower
						CheckIncreaseSkill(TAUNT);
						float tauntchance;
						int level_difference = level - target->GetLevel();
						if (level_difference <= 5) {
							tauntchance = 25.0;	// minimum
							tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0;	// skill modifier
							if (tauntchance > 65.0)
								tauntchance = 65.0;
						}
						else if (level_difference <= 10) {
							tauntchance = 30.0;	// minimum
							tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0;	// skill modifier
							if (tauntchance > 85.0)
								tauntchance = 85.0;
						}
						else if (level_difference <= 15) {
							tauntchance = 40.0;	// minimum
							tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0;	// skill modifier
							if (tauntchance > 90.0)
								tauntchance = 90.0;
						}
						else {
							tauntchance = 50.0;	// minimum
							tauntchance += tauntchance * (float)GetSkill(TAUNT) / 200.0;	// skill modifier
							if (tauntchance > 95.0)
								tauntchance = 95.0;
						}
						if (tauntchance > ((float)rand()/(float)RAND_MAX)*100.0) {
							// this is the max additional hate added per succesfull taunt
							tauntvalue = (sint32) ((float)level * 10.0 * (float)rand()/(float)RAND_MAX + 1);
							// new hate: find diff of player's hate and whoever's at top of list, add that plus tauntvalue to players hate
							newhate = target->CastToNPC()->GetNPCHate(target->CastToNPC()->GetHateTop()) - target->CastToNPC()->GetNPCHate(this) + tauntvalue;
							// add the hate
							target->CastToNPC()->AddToHateList(this, newhate);
						}
					}
					break;
				}
				case OP_InstillDoubt: {
					//Fear Spell not yet implemented
					Instill_Doubt_Struct* iatk = (Instill_Doubt_Struct*) app->pBuffer;
					if (iatk->i_atk == 0x2E) {
						SimpleMessage_StringID(4,NOT_SCARING);
						//Message(4, "You\'re not scaring anyone.");
					}
					break;
				}
				case OP_RezzAnswer: {
					OPRezzAnswer(app);
					break;
				}
				case OP_GMSummon: { // and /corpse
					if (app->size != sizeof(GMSummon_Struct)) {
						cout << "Wrong size on OP_GMSummon. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << endl;
						break;
					}
					
					GMSummon_Struct* gms = (GMSummon_Struct*) app->pBuffer;
					Mob* st = entity_list.GetMob(gms->charname);
					if (admin >= 80) {
						int8 tmp = gms->charname[strlen(gms->charname)-1];
						if (st != 0) {
							this->Message(0, "Local: Summoning %s to %i, %i, %i", gms->charname, gms->x, gms->y, gms->z);
							if (st->IsClient() && (st->CastToClient()->GetAnon() != 1 || this->Admin() >= st->CastToClient()->Admin()))
								st->CastToClient()->MovePC((char*) 0, gms->x, gms->y, gms->z, 2, true);
							else
								st->GMMove(this->GetX(), this->GetY(), this->GetZ(),this->GetHeading());
						}
						else if (!worldserver.Connected())
							Message(0, "Error: World server disconnected");
						else if (tmp < '0' || tmp > '9') { // dont send to world if it's not a player's name
							ServerPacket* pack = new ServerPacket;
							pack->opcode = ServerOP_ZonePlayer;
							pack->size = sizeof(ServerZonePlayer_Struct);
							pack->pBuffer = new uchar[pack->size];
							memset(pack->pBuffer, 0, pack->size);
							ServerZonePlayer_Struct* szp = (ServerZonePlayer_Struct*) pack->pBuffer;
							strcpy(szp->adminname, this->GetName());
							szp->adminrank = this->Admin();
							strcpy(szp->name, gms->charname);
							strcpy(szp->zone, zone->GetShortName());
							szp->x_pos = gms->x;
							szp->y_pos = gms->y;
							szp->z_pos = gms->z;
							szp->ignorerestrictions = 2;
							worldserver.SendPacket(pack);
							safe_delete(pack);
						}
						else {
							Message(13, "Error: '%s' not found.", gms->charname);
						}
					}
					else if (st && st->IsCorpse()) {
						st->CastToCorpse()->Summon(this, false);
					}
					else if (st) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/summon");
					}
					break;
				}
				case OP_TradeRequest: {
					// Client requesting a trade session from an npc/client
					// Trade session not started until OP_TradeRequestAck is sent
					TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer;
					trade->Start(msg->to_mob_id);
					
					// Pass trade request on to recipient
					Mob* with = trade->With();
					if (with && with->IsClient()) {
						with->CastToClient()->QueuePacket(app);
					}
					else if (with) {
						//npcs always accept
						APPLAYER* outapp = new APPLAYER(OP_TradeRequestAck, sizeof(TradeRequest_Struct));
						TradeRequest_Struct* acc = (TradeRequest_Struct*) outapp->pBuffer;
						acc->from_mob_id = msg->to_mob_id;
						acc->to_mob_id = msg->from_mob_id;
						FastQueuePacket(&outapp);
						safe_delete(outapp);
					}
					break;
				}
				case OP_TradeRequestAck: {
					// Trade request recipient is acknowledging they are able to trade
					// After this, the trade session has officially started
					TradeRequest_Struct* msg = (TradeRequest_Struct*) app->pBuffer;
					
					// Send ack on to trade initiator if client
					Mob* with = trade->With();
					if (with->IsClient()) {
						with->CastToClient()->QueuePacket(app);
					}
					break;
				}
				case OP_CancelTrade: {
					Mob* with = trade->With();
					if (with && with->IsClient()) {
						CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer;
						
						// Forward cancel packet to other client
						msg->fromid = with->GetID();
						//msg->action = 1;
	
						with->CastToClient()->QueuePacket(app);
						
						// Put trade items/cash back into inventory
						this->FinishTrade(this);
					}
					else if(with){
						CancelTrade_Struct* msg = (CancelTrade_Struct*) app->pBuffer;
						msg->fromid = with->GetID();
						QueuePacket(app);
						FinishTrade(this);
					}
					break;
				}
				case OP_TradeAcceptClick: {
					Mob* with = trade->With();
					trade->state = TradeAccepted;
					if (with && with->IsClient()) {
						// Have both accepted?
						Client* other = with->CastToClient();
						other->QueuePacket(app);
						
						if (other->trade->state == trade->state) {
							other->trade->state = TradeCompleting;
							trade->state = TradeCompleting;
							
							// Audit trade to database for both trade streams
							other->trade->LogTrade();
							trade->LogTrade();
							
							// Perform actual trade
							this->FinishTrade(other);
							other->FinishTrade(this);
							
							// All done
							APPLAYER* outapp = new APPLAYER(OP_FinishTrade, 0);
							other->QueuePacket(outapp);
							this->FastQueuePacket(&outapp);
						}
					}
					else if(with){
						APPLAYER* outapp = new APPLAYER(OP_FinishTrade,0);
						QueuePacket(outapp);
						safe_delete(outapp);
						FinishTrade(with->CastToNPC());						
					}
					
					break;
				}
				case OP_BoardBoat: {
					char *boatname;
					this->IsOnBoat=true;
					boatname = new char[app->size-4];
					memset(boatname, 0, app->size-4);
					memcpy(boatname, app->pBuffer, app->size-4);
					printf("%s has gotten on the boat %s\n",GetName(),boatname);
					Mob* boat = entity_list.GetMob(boatname);
					if (boat){
						boat->CastToNPC()->passengers=true;
					}
					safe_delete(boatname);
					break;
				}
				case OP_LeaveBoat: {
					this->IsOnBoat=false;
					break;
				}
				case OP_RandomReq: {
					const RandomReq_Struct* rndq = (const RandomReq_Struct*) app->pBuffer;
					uint32 randLow=rndq->low > rndq->high?rndq->high:rndq->low;
					uint32 randHigh=rndq->low > rndq->high?rndq->low:rndq->high;
					uint32 randResult;

					if(randLow==0 && randHigh==0)
					{	// defaults
						randLow=0;
						randHigh=100;
					}
					randResult=MakeRandomInt(randLow, randHigh);

					APPLAYER* outapp = new APPLAYER(OP_RandomReply, sizeof(RandomReply_Struct));
					RandomReply_Struct* rr = (RandomReply_Struct*)outapp->pBuffer;
					rr->low=randLow;
					rr->high=randHigh;
					rr->result=randResult;
					strcpy(rr->name, GetName());
					entity_list.QueueClients(this, outapp, false);

					break;
				}
				case OP_Buff: {
					if (app->size != sizeof(SpellBuffFade_Struct))
					{
						LogFile->write(EQEMuLog::Error, "Size mismatch in OP_Buff. expected %i got %i", sizeof(SpellBuffFade_Struct), app->size);
						DumpPacket(app);
						break;
					}
					
					SpellBuffFade_Struct* sbf = (SpellBuffFade_Struct*) app->pBuffer;
					if(sbf->spellid == 0xFFFF)
						QueuePacket(app);
					else
						BuffFade(sbf->spellid);

					break;
				}
				// @merth: GMHideMe not yet implemented
				/*
				case OP_GMHideMe: {
					if(this->Admin() < 80) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/hideme");
						break;
					}
					APPLAYER* outapp = new APPLAYER(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
					SpawnAppearance_Struct* sa_out = (SpawnAppearance_Struct*)outapp->pBuffer;
					sa_out->spawn_id = GetID();
					sa_out->type = 0x03;
					if (this->invisible == false) {
						this->invisible = true;
						sa_out->parameter = 1;
					}
					else {
						this->invisible = false;
						sa_out->parameter = 0;
					}
					
					entity_list.QueueClients(this, outapp, true);
					safe_delete(outapp);
					break;
				}
				*/
				case OP_GMNameChange: {
					const GMName_Struct* gmn = (const GMName_Struct *)app->pBuffer;
					if(this->Admin() < 100){
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/name");
						break;
					}
					Client* client = entity_list.GetClientByName(gmn->oldname);
					LogFile->write(EQEMuLog::Status, "GM(%s) changeing players name. Old:%s New:%s", GetName(), gmn->oldname, gmn->newname);
					bool usedname = database.CheckUsedName((const char*) gmn->newname);
					if(client==0) {
						Message(13, "%s not found for name change. Operation failed!", gmn->oldname);
						break;
					}
					if((strlen(gmn->newname) > 63) || (strlen(gmn->newname) == 0)) {
						Message(13, "Invalid number of characters in new name (%s).", gmn->newname);
						break;
					}
					if (!usedname) {
						Message(13, "%s is already in use.  Operation failed!", gmn->newname);
						break;

					}
					database.UpdateName(gmn->oldname, gmn->newname);
					strcpy(client->name, gmn->newname);
					client->Save();
					
					if(gmn->badname==1) {
						database.AddToNameFilter(gmn->oldname);
					}
					APPLAYER* outapp = app->Copy();
					GMName_Struct* gmn2 = (GMName_Struct*) outapp->pBuffer;
					gmn2->unknown[0] = 1;
					gmn2->unknown[1] = 1;
					gmn2->unknown[2] = 1;
					entity_list.QueueClients(this, outapp, false);
					safe_delete(outapp);
					UpdateWho();
					break;
				}
				case OP_GMKill: {
					if(this->Admin() < 100) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/kill");
						break;
					}
					GMKill_Struct* gmk = (GMKill_Struct *)app->pBuffer;
					Mob* obj = entity_list.GetMob(gmk->name);
					Client* client = entity_list.GetClientByName(gmk->name);
					if(obj!=0) {
						if(client!=0) {
							entity_list.QueueClients(this,app);
						}
						else {
							obj->Kill();
						}
					}
					else {
						if (!worldserver.Connected())
							Message(0, "Error: World server disconnected");
						else {
							ServerPacket* pack = new ServerPacket;
							pack->opcode = ServerOP_KillPlayer;
							pack->size = sizeof(ServerKillPlayer_Struct);
							pack->pBuffer = new uchar[pack->size];
							ServerKillPlayer_Struct* skp = (ServerKillPlayer_Struct*) pack->pBuffer;
							strcpy(skp->gmname, gmk->gmname);
							strcpy(skp->target, gmk->name);
							skp->admin = this->Admin();
							worldserver.SendPacket(pack);
							safe_delete(pack);
						}
					}
					break;
				}
				case OP_GMLastName: {
					if (app->size != sizeof(GMLastName_Struct)) {
						cout << "Wrong size on OP_GMLastName. Got: " << app->size << ", Expected: " << sizeof(GMLastName_Struct) << endl;
						break;
					}
					GMLastName_Struct* gmln = (GMLastName_Struct*) app->pBuffer;
					if (strlen(gmln->lastname) >= 64) {
						Message(13, "/LastName: New last name too long. (max=63)");
					}
					else {
						Client* client = entity_list.GetClientByName(gmln->name);
						if (client == 0) {
							Message(13, "/LastName: %s not found", gmln->name);
						}
						else {
							if (this->Admin() < 80) {
								Message(13, "Your account has been reported for hacking.");
								database.SetHackerFlag(client->account_name, client->name, "/lastname");
								break;
							}
							else

								client->ChangeLastName(gmln->lastname);
						}
						gmln->unknown[0] = 1;
						gmln->unknown[1] = 1;
						gmln->unknown[2] = 1;
						gmln->unknown[3] = 1;
						entity_list.QueueClients(this, app, false);
					}
					break;
				}
				case OP_GMToggle: {
					if (app->size != 68) {
						cout << "Wrong size on OP_GMToggle. Got: " << app->size << ", Expected: " << 36 << endl;
						break;
					}
					if (this->Admin() < 80) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/toggle");
						break;
					}
					if (app->pBuffer[64] == 0) {
						this->SimpleMessage_StringID(0,TOGGLE_OFF);
						//Message(0, "Turning tells OFF");
						tellsoff = true;
					}
					else if (app->pBuffer[64] == 1) {
						//Message(0, "Turning tells ON");
						this->SimpleMessage_StringID(0,TOGGLE_ON);
						tellsoff = false;
					}
					else {
						Message(0, "Unkown value in /toggle packet");
					}
					UpdateWho();
					break;
				}
				case OP_LFGCommand: {
					if (app->size != sizeof(LFG_Struct)) {
						cout << "Wrong size on OP_LFGCommand. Got: " << app->size << ", Expected: " << sizeof(LFG_Struct) << endl;
						DumpPacket(app);
						break;
					}
					
					// Process incoming packet
					LFG_Struct* lfg = (LFG_Struct*) app->pBuffer;
					if (lfg->value == 1) {
						LFG = true;
					}
					else if (lfg->value == 0) {
						LFG = false;
					}
					else
						Message(0, "Error: unknown LFG value");
					UpdateWho();
					
					// Issue outgoing packet to notify other clients
					APPLAYER* outapp = new APPLAYER(OP_LFGAppearance, sizeof(LFG_Appearance_Struct));
					LFG_Appearance_Struct* lfga = (LFG_Appearance_Struct*)outapp->pBuffer;
					lfga->spawn_id = this->GetID();
					lfga->lfg = (uint8)LFG;
					
					entity_list.QueueClients(this, outapp, true);
					break;
				}
				case OP_GMGoto: {
					if (app->size != sizeof(GMSummon_Struct)) {
						cout << "Wrong size on OP_GMGoto. Got: " << app->size << ", Expected: " << sizeof(GMSummon_Struct) << endl;
						break;
					}
					if (this->Admin() < 80) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/goto");
						break;
					}
					GMSummon_Struct* gmg = (GMSummon_Struct*) app->pBuffer;
					Mob* gt = entity_list.GetMob(gmg->charname);
					if (gt != 0) {
						this->MovePC((char*) 0, gt->GetX(), gt->GetY(), gt->GetZ());
					}
					else if (!worldserver.Connected())
						Message(0, "Error: World server disconnected.");
					else {
						ServerPacket* pack = new ServerPacket;
						pack->opcode = ServerOP_GMGoto;
						pack->size = sizeof(ServerGMGoto_Struct);
						pack->pBuffer = new uchar[pack->size];
						memset(pack->pBuffer, 0, pack->size);
						ServerGMGoto_Struct* wsgmg = (ServerGMGoto_Struct*) pack->pBuffer;
						strcpy(wsgmg->myname, this->GetName());
						strcpy(wsgmg->gotoname, gmg->charname);
						wsgmg->admin = admin;
						worldserver.SendPacket(pack);
						safe_delete(pack);
					}
					break;
				}
				case OP_TraderShop:{
					TraderClick_Struct* tcs= (TraderClick_Struct*)app->pBuffer;
					if(app->size==sizeof(TraderClick_Struct)){
						APPLAYER* outapp = new APPLAYER(OP_TraderShop, sizeof(TraderClick_Struct));
						TraderClick_Struct* outtcs=(TraderClick_Struct*)outapp->pBuffer;
						Client* tmp = entity_list.GetClientByID(tcs->traderid);
						if (tmp)
							outtcs->approval=tmp->WithCustomer();
						else
							break;
						outtcs->traderid=tcs->traderid;
						QueuePacket(outapp);
						if(outtcs->approval)
							this->BulkSendTraderInventory(tmp->CharacterID());
						safe_delete(outapp);
					}
					break;
				}
				case OP_ShopRequest: {
					// this works
					Merchant_Click_Struct* mc=(Merchant_Click_Struct*)app->pBuffer;
					if (app->size == sizeof(Merchant_Click_Struct)) {
						// Send back opcode OP_ShopRequest - tells client to open merchant window.
						APPLAYER* outapp = new APPLAYER(OP_ShopRequest, sizeof(Merchant_Click_Struct));
						Merchant_Click_Struct* mco=(Merchant_Click_Struct*)outapp->pBuffer;
						
						mco->npcid = mc->npcid;
						mco->playerid = 0;
						mco->unknown[0] = 1; // Merchant command 0x01 = open
						mco->unknown[1] = 0x00;
						mco->unknown[2] = 0x00;
						mco->unknown[3] = 0x00;
						mco->unknown[4] = 0xE0;
						mco->unknown[5] = 0xCB; // 32
						mco->unknown[6] = 0x90; // 139
						mco->unknown[7] = 0x3F; // 63
						
						QueuePacket(outapp);
						safe_delete(outapp);
						
						int merchantid=0;
						Mob* tmp = entity_list.GetMob(mc->npcid);
						if (tmp != 0)
							merchantid=tmp->CastToNPC()->MerchantType;
						if(merchantid == 0)
							break;
						if(tmp->IsEngaged()){
							this->SimpleMessage_StringID(0,MERCHANT_BUSY,0);
							break;
						}
						BulkSendMerchantInventory(merchantid,mc->npcid);
					}
					break;
				}
				case OP_Bazaar: {
 					if (app->size==sizeof(BazaarSearch_Struct)) {
						BazaarSearch_Struct* bss= (BazaarSearch_Struct*)app->pBuffer;
						this->SendBazaarResults(bss->traderid,bss->class_,bss->race,bss->stat,bss->slot,bss->type,bss->name,bss->minprice,bss->maxprice);
					}
					else if (app->size==sizeof(BazaarWelcome_Struct)) {
						BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)app->pBuffer;
						if (bws->beginning.action==9)
							SendBazaarWelcome();
					}
					else
						LogFile->write(EQEMuLog::Error, "Malformed BazaarSearch_Struct packet received, ignoring...\n");
					break;
				}
				case OP_ShopPlayerBuy: {
					Merchant_Sell_Struct* mp=(Merchant_Sell_Struct*)app->pBuffer;
#if EQDEBUG >= 5
						LogFile->write(EQEMuLog::Debug, "%s, purchase item..", GetName());
						DumpPacket(app);
#endif
					
					int merchantid;
					
					Mob* tmp = entity_list.GetMob(mp->npcid);
					if (tmp != 0)
						merchantid=tmp->CastToNPC()->MerchantType;
					else
						break;
					uint32 item_id = database.GetMerchantData(merchantid, mp->itemslot-272);
					const Item_Struct* item = NULL;
					if (item_id == 0) { // Inventory item?
						char mki[3] = "";
						if (database.GetVariable("MerchantsKeepItems", mki, 3) && mki[0] == '1'  && tmp->CastToNPC()->CountLoot() != 0 ) {
							int vlc = tmp->CastToNPC()->CountLoot();
							int i_slot=database.GetMerchantSlot(merchantid,item_id);
							int i_quan = tmp->CastToNPC()->GetItem(i_slot)->charges;
							int i_num = tmp->CastToNPC()->GetItem(i_slot)->item_id;
#if EQDEBUG>=5
								LogFile->write(EQEMuLog::Debug,"MerchantsKeepItems: vlc:%i i_slot:%i i_quan:%i i_num:%i",vlc, i_slot, i_quan, i_num);
#endif							
							item = database.GetItem( i_num );
							if (i_quan < mp->quantity) {
								mp->quantity = (1 * tmp->CastToNPC()->GetItem(i_slot)->charges);
							}
							tmp->CastToNPC()->RemoveItem(i_num, mp->quantity,i_slot);
						} else {
							// MerchantsKeepItems off, not setup, or Vendor has no non-db items
							APPLAYER* outapp = new APPLAYER(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct));
							Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer;
							mpo->quantity = mp->quantity;
							mpo->npcid = mp->npcid;
							mpo->itemslot=0;
							mpo->price=0;
							QueuePacket(outapp);
							safe_delete(outapp);
							Message(0, "%s tells you, Sorry I seem to have misplaced that item try back later.", tmp->GetName());
							break;
						}
					} else {
						item = database.GetItem(item_id);
					}
					if (!item)
						break;
					
					APPLAYER* outapp = new APPLAYER(OP_ShopPlayerBuy, sizeof(Merchant_Sell_Struct));
					Merchant_Sell_Struct* mpo=(Merchant_Sell_Struct*)outapp->pBuffer;
					mpo->quantity = mp->quantity;

					mpo->npcid = mp->npcid;
					mpo->itemslot=mp->itemslot;
					
					mpo->price = (int)((item->Cost*mp->quantity)*1.27);
 					string packet;
					sint16 freeslotid=0;
					
					ItemInst* inst = ItemInst::Create(item, mp->quantity);
					if (inst) {
						freeslotid = m_inv.FindFreeSlot(false, true);
						PutItemInInventory(freeslotid, *inst);
						
						if(freeslotid!=SLOT_INVALID) 
							TakeMoneyFromPP(mpo->price);

						SendItemPacket(freeslotid, inst, ItemPacketTrade);
						safe_delete(inst);
					}
					else {
						LogFile->write(EQEMuLog::Error, "OP_ShopPlayerBuy: item->Type Unknown! Type: %i", item->Type);
					}
					
					if (zone->merchantvar!=0){
						if (zone->merchantvar==7){
								LogMerchant(this,tmp,mpo,item,true);
						}
						else if ((admin>=10) && (admin<20)){
							if ((zone->merchantvar<8) && (zone->merchantvar>5))
								LogMerchant(this,tmp,mpo,item,true);
						}
						else if (admin<=20){
							if ((zone->merchantvar<8) && (zone->merchantvar>4))
								LogMerchant(this,tmp,mpo,item,true);
						}
						else if (admin<=80){
							if ((zone->merchantvar<8) && (zone->merchantvar>3))
								LogMerchant(this,tmp,mpo,item,true);
						}
						else if (admin<=100){
							if ((zone->merchantvar<9) && (zone->merchantvar>2))
								LogMerchant(this,tmp,mpo,item,true);
						}
						else if (admin<=150){
							if (((zone->merchantvar<8) && (zone->merchantvar>1)) || (zone->merchantvar==9))
								LogMerchant(this,tmp,mpo,item,true);
						}
						else if (admin<=255){
							if ((zone->merchantvar<8) && (zone->merchantvar>0))
								LogMerchant(this,tmp,mpo,item,true);	
						}
					}
					
					QueuePacket(outapp);
					safe_delete(outapp);
					break;
				}
				case OP_ShopPlayerSell: {
					Merchant_Purchase_Struct* mp=(Merchant_Purchase_Struct*)app->pBuffer;
					Mob* vendor = entity_list.GetMob(mp->npcid);
					Item_Struct* item2 = NULL;
					int32 price=0;
					const Item_Struct* item = database.GetItem(GetItemIDAt(mp->itemslot));
					if (item){
						price=((item->Cost*mp->quantity)*.884);
						AddMoneyToPP(price,false);
						if (zone->merchantvar!=0){
							if (zone->merchantvar==7) {
								LogMerchant(this,vendor,mp,item,false);
							}
							else if ((admin>=10) && (admin<20)) {
								if ((zone->merchantvar<8) && (zone->merchantvar>5))
									LogMerchant(this,vendor,mp,item,false);
							}
							else if (admin<=20) {
								if ((zone->merchantvar<8) && (zone->merchantvar>4))
									LogMerchant(this,vendor,mp,item,false);
							}
							else if (admin<=80) {
								if ((zone->merchantvar<8) && (zone->merchantvar>3))
									LogMerchant(this,vendor,mp,item,false);
							}
							else if (admin<=100) {
								if ((zone->merchantvar<9) && (zone->merchantvar>2))
									LogMerchant(this,vendor,mp,item,false);
							}
							else if (admin<=150) {
								if (((zone->merchantvar<8) && (zone->merchantvar>1)) || (zone->merchantvar==9))
									LogMerchant(this,vendor,mp,item,false);
							}
							else if (admin<=255) {
								if ((zone->merchantvar<8) && (zone->merchantvar>0))
									LogMerchant(this,vendor,mp,item,false);	
							}
						}
					}
					else
						Message(0, "Error #1, item == 0");
					
					char mki[3] = "";
					if (database.GetVariable("MerchantsKeepItems", mki, 3)) {
#if EQDEBUG >= 11
							cout<<"MerchantKeepItems: OP_ShopPlayerSell mki="<<mki<<endl;
#endif
						if ( mki[0] == '1' ) {
							Mob* vendor = entity_list.GetMob(mp->npcid);
							if (!vendor)
								break; // DT who ever just killed the merchant and the player and 12 random people
							int vlc = vendor->CastToNPC()->CountLoot();
							int vdbc = database.GetMerchantListNumb(vendor->CastToNPC()->MerchantType);
							int32 cur_inr = 0;
							bool have_item = false;
							cur_inr = GetItemIDAt(mp->itemslot);
							int cur_i; // Give WIN32 a nice big hug
							// Step thru Merchants inventory/loot
							for (cur_i = 0; cur_i < vlc; cur_i++) {
								if (vendor->CastToNPC()->GetItem(cur_i)->item_id == cur_inr) {
									// If we find the item in the loottable
									have_item = true; // Trip have_item flag
									vendor->CastToNPC()->GetItem(cur_i)->charges += mp->quantity; // And increase the number merchant has available
									break; // Exit for loop
								}
							}
							
							// Now step thru the Merchants db items only if we don't already any of the item in the loottable
							if (!have_item) {
								for (cur_i = 0; cur_i < vdbc; cur_i++) {
									const Item_Struct* tmpItem = database.GetItem(database.GetMerchantData(vendor->CastToNPC()->MerchantType,cur_i+1));
									if ( tmpItem->ItemNumber == cur_inr) {
										have_item = true; // Found the item in the Merchantlist trip the flag
										break;
									}
								}
							}
							
							if (!have_item) {
								// Add Item to Merchants inventory
								vendor->CastToNPC()->AddItem( cur_inr, 1*mp->quantity, vlc);
							}
							
							// Then remove the item from the player
							this->DeleteItemInInventory(mp->itemslot,1*mp->quantity,false);
						} else {
							this->DeleteItemInInventory(mp->itemslot,1*mp->quantity,false);
						}
					} else {
						// Update zones copy of player inventory
						cout<<"Deleting item..   MerchantsKeepItems not configured"<<endl;
						this->DeleteItemInInventory(mp->itemslot);
					}
					
					APPLAYER* outapp = new APPLAYER(OP_ShopPlayerSell, sizeof(Merchant_Purchase_Struct));
					Merchant_Purchase_Struct* mco=(Merchant_Purchase_Struct*)outapp->pBuffer;
					mco->npcid = vendor->GetID();
					mco->itemslot=mp->itemslot;
					mco->quantity=mp->quantity;
					mco->price=price;
					QueuePacket(outapp);
					safe_delete(outapp);
					//BulkSendMerchantInventory(vendor->CastToNPC()->MerchantType,vendor->GetID());
					// Just send it back to accept the deal
					Save();
					
					break;
				}
				case OP_ShopEnd: {
					APPLAYER* outapp = new APPLAYER(OP_ShopEndConfirm, 2);
					outapp->pBuffer[0] = 0x0a;
					outapp->pBuffer[1] = 0x66;
					QueuePacket(outapp);
					safe_delete(outapp);
					Save();
					break;
				}
				case OP_CloseContainer: { // Player is closing tradeskill container
					if (app->size != sizeof(CloseContainer_Struct)) {
						LogFile->write(EQEMuLog::Error, "Invalid size on CloseContainer_Struct: Expected %i, Got %i",
							sizeof(CloseContainer_Struct), app->size);
						break;
					}
					
					CloseContainer_Struct* oos = (CloseContainer_Struct*)app->pBuffer;
					Entity* entity = entity_list.GetID(oos->drop_id);
					if (entity && entity->IsObject()) {
						Object* object = entity->CastToObject();
						object->Close();
					}
					break;
				}
				case OP_ClickObject: { // Player clicked on zone object (forge, item on ground, etc)
					if (app->size != sizeof(ClickObject_Struct)) {
						LogFile->write(EQEMuLog::Error, "Invalid size on ClickObject_Struct: Expected %i, Got %i",
							sizeof(ClickObject_Struct), app->size);
						break;
					}
					
					ClickObject_Struct* click_object = (ClickObject_Struct*)app->pBuffer;
					Entity* entity = entity_list.GetID(click_object->drop_id);
					if (entity && entity->IsObject()) {
						Object* object = entity->CastToObject();
						object->HandleClick(this, click_object);
					}
					break;
				}
				case OP_TradeSkillCombine: {
					if (app->size != sizeof(NewCombine_Struct)) {
						LogFile->write(EQEMuLog::Error, "Invalid size for NewCombine_Struct: Expected: %i, Got: %i",
							sizeof(NewCombine_Struct), app->size);
						break;
					}
					//else if (m_tradeskill_object == NULL) {
					//	Message(13, "Error: Server is not aware of the tradeskill container you are attempting to use");
					//	break;
					//}
					
					// Delegate to tradeskill object to perform combine
					NewCombine_Struct* in_combine = (NewCombine_Struct*)app->pBuffer;
					m_tradeskill_object->HandleCombine(this, in_combine);
					break;
				}
				case OP_ClickDoor: {
                                        ClickDoor_Struct* cd = (ClickDoor_Struct*)app->pBuffer;
                                        Doors* currentdoor = entity_list.FindDoor(cd->doorid);
                                        if(!currentdoor)
                                        {
                                        	Message(0,"Unable to find door, please notify a GM (DoorID: %i).",cd->doorid);
                                                break;
                                        }

					#ifdef GUILDWARS
                                        if (IsSettingGuildDoor){
                                                        if (database.SetGuildDoor(cd->doorid,SetGuildDoorID,zone->GetShortName())){
                                                                cout << SetGuildDoorID << endl;
                                                                if (SetGuildDoorID){
                                                                        Message(0,"This is now a guilddoor of '%s'",guilds[SetGuildDoorID].name);
                                                                        currentdoor->SetGuildID(SetGuildDoorID);
                                                                }
                                                                else{
                                                                                        Message(0,"Guildoor deleted");
                                                                                        currentdoor->SetGuildID(0);
                                                                }
                                                        }
                                                        else
                                                                Message(0,"Failed to edit guilddoor!");
                                                        IsSettingGuildDoor = false;
                                                        break;
                                                }
                                                if (cd->doorid >= 128){
                                                        if(!database.CheckGuildDoor(cd->doorid,GuildEQID(),zone->GetShortName())){
                                                                // heh hope grammer on this is right lol
                                                                this->Message(0,"A magic barrier protects this hall against intruders!");

                                                                break;
                                                        }
                                                        else
                                                                this->Message(0,"The magic barrier disappears and you open the door!");
                                        }
					#endif
                        		currentdoor->HandleClick(this);
					break;
				}
				case OP_CreateObject: { // User dropping item from cursor to ground
					DropItem(SLOT_CURSOR);
					break;
				}
				case OP_FaceChange: { // When client changes their face & stuff
					// Notify other clients in zone
					entity_list.QueueClients(this, app, false);
					
					FaceChange_Struct* fc = (FaceChange_Struct*)app->pBuffer;
					m_pp.haircolor	= fc->haircolor;
					m_pp.beardcolor	= fc->beardcolor;
					m_pp.eyecolor1	= fc->eyecolor1;
					m_pp.eyecolor2	= fc->eyecolor2;
					m_pp.hairstyle	= fc->hairstyle;
					m_pp.face		= fc->face;
					//m_pp.beard	= fc->?;
					//m_pp.?		= fc->woad;
					
					Save();
					SimpleMessage_StringID(13,FACE_ACCEPTED);
					//Message(13, "Facial features updated.");
					break;
				}
				case OP_GroupInvite:
				case OP_GroupInvite2: {
					if(this->GetTarget() != 0 && this->GetTarget()->IsClient()) {
						this->GetTarget()->CastToClient()->QueuePacket(app);
						break;
					}
					/*if(this->GetTarget() != 0 && this->GetTarget()->IsNPC() && this->GetTarget()->CastToNPC()->IsInteractive()) {
						if(!this->GetTarget()->CastToNPC()->IsGrouped()) {
							APPLAYER* outapp = new APPLAYER(OP_GroupUpdate,sizeof(GroupUpdate_Struct));
							GroupUpdate_Struct* gu = (GroupUpdate_Struct*) outapp->pBuffer;
							gu->action = 9;
							strcpy(gu->membername,GetName());
							strcpy(gu->yourname,GetTarget()->CastToNPC()->GetName());
							FastQueuePacket(&outapp);
							if (!isgrouped){
								Group* ng = new Group(this);
								entity_list.AddGroup(ng);
							}
							entity_list.GetGroupByClient(this->CastToClient())->AddMember(GetTarget());
							this->GetTarget()->CastToNPC()->TakenAction(22,this->CastToMob());
						}
						else {
							    LogFile->write(EQEMuLog::Debug, "IPC: %s already grouped.", this->GetTarget()->GetName());
						}
					}*/
					break;
				}
				case OP_GroupAcknowledge:
					break;
				case OP_CancelInvite: {
					GroupGeneric_Struct* gf = (GroupGeneric_Struct*) app->pBuffer;
					Mob* inviter = entity_list.GetClientByName(gf->name1);
					if(inviter != 0 && inviter->IsClient())
						inviter->CastToClient()->QueuePacket(app);
					break;
				}
				/* //wrong opcode for AutoFollow?
				case OP_AutoFollow:{//TODO
					break;
				}
				*/ 
				case OP_GroupFollow:
				case OP_GroupFollow2: {
					GroupGeneric_Struct* gf = (GroupGeneric_Struct*) app->pBuffer;
					Mob* inviter = entity_list.GetClientByName(gf->name1);
					if(inviter != 0 && inviter->IsClient()) {
						isgrouped = true;
						strcpy(gf->name1,inviter->GetName());
						strcpy(gf->name2,this->GetName());
						inviter->CastToClient()->QueuePacket(app);//notify inviter the client accepted
						Group* group=entity_list.GetGroupByClient(inviter->CastToClient());
						if(group){
							group->AddMember(this);
							group->SendUpdate(7,this);
							group->SendHPPackets(this);
						}
						else{
							//Invite the inviter into the group first.....dont ask
							APPLAYER* outapp=new APPLAYER(OP_GroupUpdate,sizeof(GroupJoin_Struct));
							GroupJoin_Struct* outgj=(GroupJoin_Struct*)outapp->pBuffer;
							strcpy(outgj->membername, inviter->GetName());
							strcpy(outgj->yourname, inviter->GetName());
							outgj->action = 9;
							inviter->CastToClient()->QueuePacket(outapp);
							safe_delete(outapp);

							//Make new group
							Group* ng = new Group(inviter);
							entity_list.AddGroup(ng);
							ng->AddMember(this);
							ng->SendUpdate(7,this);
							ng->SendHPPackets(this);
							cout << "New group created" << endl;
						}
					}
					break;
				}
				case OP_GroupDisband: {
					printf("Member Disband Request\n");
					GroupGeneric_Struct* gd = (GroupGeneric_Struct*) app->pBuffer;
					Group* group = entity_list.GetGroupByClient(this);
					if((group && group->IsLeader(this) && target == 0) || (group && group->GroupCount()<3)) {
						if (group)
							group->DisbandGroup();
					}
					else if (this->isgrouped && group != 0)
						group->DelMember(entity_list.GetMob(gd->name2),false);
					break;
				}
				case OP_GroupDelete: {
					printf("Group Delete Request\n");
					Group* group = entity_list.GetGroupByClient(this);
					if (this->isgrouped && group != 0)
						group->DisbandGroup();
					break;
				}
				case OP_GMEmoteZone: {
					if(this->Admin() < 80) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/emote");
						break;
					}
					GMEmoteZone_Struct* gmez = (GMEmoteZone_Struct*)app->pBuffer;
					char* newmessage=0;
					if(strstr(gmez->text,"^")==0)
						entity_list.Message(0, 15, gmez->text);
					else{
						for(newmessage = strtok((char*)gmez->text,"^");newmessage!=NULL;newmessage=strtok(NULL, "^"))
							entity_list.Message(0, 15, newmessage);
					}
					break;
				}
				case OP_InspectRequest: {
					Inspect_Struct* ins = (Inspect_Struct*) app->pBuffer;
					Mob* tmp = entity_list.GetMob(ins->TargetID);
					if(tmp != 0 && tmp->IsClient())
						tmp->CastToClient()->QueuePacket(app); // Send request to target
				/*	else {
						if(tmp != 0 && tmp->IsNPC() && tmp->CastToNPC()->IsInteractive()) {
							APPLAYER* outapp = app->Copy();
							ins = (Inspect_Struct*) outapp->pBuffer;
							int16 id1,id2;
							id1 = ins->PlayerID;
							id2 = ins->TargetID;
							ins->TargetID = id1;
							ins->PlayerID = id2;
							outapp->opcode = OP_InspectAnswer;
							FastQueuePacket(&outapp);
							tmp->CastToNPC()->TakenAction(20,this);
						}
					}*/
					break;
				}
				case OP_InspectAnswer: {
					Inspect_Struct* ins = (Inspect_Struct*) app->pBuffer;
					Mob* tmp = entity_list.GetMob(ins->TargetID);
					if(tmp != 0 && tmp->IsClient())
						tmp->CastToClient()->QueuePacket(app); // Send answer to requester
					break;
				}
				case OP_Medding: {
					if (app->pBuffer[0])
						medding = true;
					else
						medding = false;
					break;
				}
				case OP_DeleteSpell: {
					if(app->size != sizeof(DeleteSpell_Struct))
						break;
					APPLAYER* outapp = app->Copy();
					DeleteSpell_Struct* dss = (DeleteSpell_Struct*) outapp->pBuffer;
					if(m_pp.spell_book[dss->spell_slot] != 0xFFFFFFFF) {
						m_pp.spell_book[dss->spell_slot] = 0xFFFFFFFF;
						dss->success = 1;
					}
					else
						dss->success = 0;
					
					FastQueuePacket(&outapp);
					break;
				}
				case OP_PetitionBug:{
					if(app->size!=sizeof(PetitionBug_Struct))
						printf("Wrong size of BugStruct! Expected: %i, Got: %i\n",sizeof(PetitionBug_Struct),app->size);
					else{
						PetitionBug_Struct* bug=(PetitionBug_Struct*)app->pBuffer;
						database.UpdateBug(bug);
					}
					break;
				}
				case OP_Bug:{
					if(app->size!=sizeof(BugStruct))
						printf("Wrong size of BugStruct!\n");
					else{
						BugStruct* bug=(BugStruct*)app->pBuffer;
						database.UpdateBug(bug);
					}
					break;
				}
				case OP_Petition: {
					if (app->size <= 1)
						break;
					if (!worldserver.Connected())
						Message(0, "Error: World server disconnected");
					/*else if(petition_list.FindPetitionByAccountName(this->AccountName()))
						{
						Message(0,"You already have a petition in queue, you cannot petition again until this one has been responded to or you have deleted the petition.");
						break;
						}*/
					else
					{
						if(petition_list.FindPetitionByAccountName(AccountName()))
						{
						Message(0,"You already have a petition in the queue, you must wait for it to be answered or use /deletepetition to delete it.");
						break;
						}
						Petition* pet = new Petition;
						pet->SetAName(this->AccountName());
						pet->SetClass(this->GetClass());
						pet->SetLevel(this->GetLevel());
						pet->SetCName(this->GetName());
						pet->SetRace(this->GetRace());
						pet->SetLastGM("");
						pet->SetCName(this->GetName());
						pet->SetPetitionText((char*) app->pBuffer);
						pet->SetZone(zone->GetZoneID());
						pet->SetUrgency(0);
						petition_list.AddPetition(pet);						
						database.InsertPetitionToDB(pet);						petition_list.UpdateGMQueue();
						petition_list.UpdateZoneListQueue();
						worldserver.SendEmoteMessage(0, 0, 80, 15, "%s has made a petition. #%i", GetName(), pet->GetID());
					}
					break;
				}
				case OP_PetitionCheckIn: {
					Petition_Struct* inpet = (Petition_Struct*) app->pBuffer;

					Petition* pet = petition_list.GetPetitionByID(inpet->petnumber);
					//if (inpet->urgency != pet->GetUrgency())
						pet->SetUrgency(inpet->urgency);
					pet->SetLastGM(this->GetName());
					pet->SetGMText(inpet->gmtext);

					pet->SetCheckedOut(false);
					petition_list.UpdatePetition(pet);
					petition_list.UpdateGMQueue();
					petition_list.UpdateZoneListQueue();
					break;
				}
				case OP_PetitionResolve:
				case OP_PetitionDelete: {
					APPLAYER* outapp = new APPLAYER(OP_PetitionUpdate,sizeof(PetitionUpdate_Struct));
					PetitionUpdate_Struct* pet = (PetitionUpdate_Struct*) outapp->pBuffer;
					pet->petnumber = *((int*) app->pBuffer);
					pet->color = 0x00;
					pet->status = 0xFFFFFFFF;
					pet->senttime = 0;
					strcpy(pet->accountid, "");
					strcpy(pet->gmsenttoo, "");
					pet->quetotal = petition_list.GetMaxPetitionID();
					strcpy(pet->charname, "");
					FastQueuePacket(&outapp);
					
					if (petition_list.DeletePetition(pet->petnumber) == -1)
						cout << "Something is borked with: " << pet->petnumber << endl;
					petition_list.ClearPetitions();
					petition_list.UpdateGMQueue();
					petition_list.ReadDatabase();
					petition_list.UpdateZoneListQueue();
					break;
				}
				case OP_PetCommands: {
					PetCommand_Struct* pet = (PetCommand_Struct*) app->pBuffer;
					Mob* mypet = this->GetPet();
					if(!mypet) break;
					char mypetname[64]={0};
					strncpy(mypetname,mypet->GetName(),strlen(mypet->GetName())-2);
					if(mypet == 0)
                    {
                        // try to get a familiar if we dont have a real pet
                        mypet = this->GetFamiliar();
                        if (mypet == NULL)
							break;
                    }
					
                    if(GetClass() == ENCHANTER && mypet->GetPetType() != 0xFF)
						break;
					
                    // just let the command "/pet get lost" work for familiars
                    if(mypet == GetFamiliar() && pet->command != PET_GETLOST)
						break;
					char val1[20]={0};
					switch(pet->command)
					{
					case PET_ATTACK: {
                        if (!target)
                            break;
						char mypetname[64]={0};
						strncpy(mypetname,mypet->GetName(),strlen(mypet->GetName())-2);
						char pettarget[64]={0};
						
                        if (target->IsMezzed()) {
							Message_StringID(10,CANNOT_WAKE,mypetname,"target");
                            //Message(10,"%s tells you, 'Cannot wake up target, Master.'", mypet->GetName());
                            break;
                        }
						if (mypet->GetHateTop()==0 && target != this && DistNoZ(*target) <= 100) {
							zone->AddAggroMob();
							mypet->AddToHateList(target, 1);
							strncpy(pettarget,target->GetName(),strlen(target->GetName())-2);
							Message_StringID(10,PET_ATTACKING,mypetname,pettarget);
							//Message(10,"%s tells you, 'Attacking %s, Master.'", mypet->GetName(), this->target->GetName());
						}
						break;
					}
					case PET_BACKOFF: {
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_CALMING,val1),0,0,0,0,0,0,0,200);
						//entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'Sorry, Master..calming down.'", mypet->GetName());
						mypet->WhipeHateList();
						break;
					}
					case PET_HEALTHREPORT: {
						char charhp[10]={0};		
						Message_StringID(10,PET_REPORT_HP,ConvertArrayF(mypet->GetHPRatio(),val1));
						//Message(10,"%s tells you, 'I have %d percent of my hit points left.'",mypet->GetName(),(int8)mypet->GetHPRatio());
						break;
					}
					case PET_GETLOST: {
                        if (mypet->GetPetType() == 0xFF || !mypet->IsNPC()) {
                            // eqlive ignores this command
                            // we could just remove the charm
                            // and continue
							mypet->BuffFadeByEffect(SE_Charm);
                            break;
                        }
                        if (mypet == GetFamiliar()) {
                            SetFamiliarID(0);
                        }
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_GETLOST_STRING,val1),0,0,0,0,0,0,0,200);
						//entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'As you wish, oh great one.'",mypet->GetName());
						mypet->CastToNPC()->Depop();
						break;
					}
					case PET_LEADER: {
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_LEADERIS,val1),this->GetName(),0,0,0,0,0,0,200);
						//entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'My leader is %s'",mypet->GetName(),this->GetName());
                        break;
					}
					case PET_GUARDHERE: {
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_GUARDINGLIFE,val1),0,0,0,0,0,0,0,200);
                        //entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'Guarding with my life..oh splendid one.'",mypet->GetName());
                        mypet->SetPetOrder(SPO_Guard);
                        mypet->SaveGuardSpot();
						break;
					}
					case PET_FOLLOWME: {
                        mypet->SetPetOrder(SPO_Follow);
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_FOLLOWING,val1),0,0,0,0,0,0,0,200);
                       // entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'Following Master.'",mypet->GetName());
						mypet->SendAppearancePacket(0x0e,0x64);

						break;
					}
					case PET_TAUNT: {
						break;
					}
					case PET_GUARDME: {
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_GUARDME_STRING,val1),0,0,0,0,0,0,0,200);
                        //entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'Guarding you, Master.'",mypet->GetName());
                        mypet->SetPetOrder(SPO_Follow);
						mypet->SendAppearancePacket(0x0e,0x64);
						break;
					}
					case PET_SITDOWN: {
                        mypet->SetPetOrder(SPO_Sit);
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_SIT_STRING,val1),0,0,0,0,0,0,0,200);
						//entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'Changing position, Master.'",mypet->GetName());
						mypet->InterruptSpell(); //Baron-Sprite: No cast 4 u. // neotokyo: i guess the pet should start casting
						mypet->SendAppearancePacket(0x0e,0x6e);
						break;
					}
					case PET_STANDUP: {
                        mypet->SetPetOrder(SPO_Follow);
						//entity_list.MessageClose(mypet, false, 200, 10, "%s says, 'Changing position, Master.'",mypet->GetName());
						Message_StringID(10,GENERIC_SAY,mypetname,ConvertArray(PET_SIT_STRING,val1),0,0,0,0,0,0,0,200);
						mypet->SendAppearancePacket(0x0e,0x64);
						break;
					}
					default: {
						printf("Client attempted to use a unknown pet command:\n");
						break;
					}
					}
					break;
				}
				case OP_PetitionUnCheckout:{
					if (app->size != sizeof(int32)) {
						cout << "Wrong size: OP_PetitionUnCheckout, size=" << app->size << ", expected " << sizeof(int32) << endl;
						break;
					}
					if (!worldserver.Connected())
						Message(0, "Error: World server disconnected");
					else {
						int32 getpetnum = *((int32*) app->pBuffer);
						Petition* getpet = petition_list.GetPetitionByID(getpetnum);
						if (getpet != 0) {
							getpet->SetCheckedOut(false);
							petition_list.UpdatePetition(getpet);
							petition_list.UpdateGMQueue();
							petition_list.UpdateZoneListQueue();
						}
					}
					break;
				}
				case OP_PetitionQue: {
#ifdef _EQDEBUG
						printf("%s looking at petitions..\n",this->GetName());
#endif
					break;
				}
				case OP_PDeletePetition:{
					if(petition_list.DeletePetitionByCharName((char*)app->pBuffer))
						SimpleMessage_StringID(0,PETITION_DELETED);	
					else
						SimpleMessage_StringID(0,PETITION_NO_DELETE);	
					break;
				}
				case OP_PetitionCheckout: {
					if (app->size != sizeof(int32)) {
						cout << "Wrong size: OP_PetitionCheckout, size=" << app->size << ", expected " << sizeof(int32) << endl;
						break;
					}
					if (!worldserver.Connected())
						Message(0, "Error: World server disconnected");
					else {
						int32 getpetnum = *((int32*) app->pBuffer);
						Petition* getpet = petition_list.GetPetitionByID(getpetnum);
						if (getpet != 0) {
							getpet->AddCheckout();
							getpet->SetCheckedOut(true);
							getpet->SendPetitionToPlayer(this->CastToClient());
							petition_list.UpdatePetition(getpet);
							petition_list.UpdateGMQueue();
							petition_list.UpdateZoneListQueue();
						}
					}
					break;
				}
				case OP_PetitionRefresh: {
					// This is When Client Asks for Petition Again and Again...
					// break is here because it floods the zones and causes lag if it
					// Were to actually do something:P  We update on our own schedule now.
					break;
				}
				case OP_ReadBook: {
					BookRequest_Struct* book = (BookRequest_Struct*) app->pBuffer;
					ReadBook(book->txtfile);
					break;
				}
				case OP_Emote: {
					// Calculate new packet dimensions
					Emote_Struct* in	= (Emote_Struct*)app->pBuffer;
					const char* name	= GetName();
					uint32 len_name		= strlen(name);
					uint32 len_msg		= strlen(in->message);
					uint32 len_packet	= sizeof(in->unknown01) + len_name
										+ strlen(in->message) + 1;
					
					// Construct outgoing packet
					APPLAYER* outapp = new APPLAYER(OP_Emote, len_packet);
					Emote_Struct* out = (Emote_Struct*)outapp->pBuffer;
					out->unknown01 = in->unknown01;
					memcpy(out->message, name, len_name);
					memcpy(&out->message[len_name], in->message, len_msg);
					
					//cout << "######### Outgoing emote packet" << endl;
					//DumpPacket(outapp);
					
					/*
					if (target && target->IsClient()) {
						entity_list.QueueCloseClients(this, outapp, false, 100, target);
						
						cptr = outapp->pBuffer + 2;
						
                        // not sure if live does this or not.  thought it was a nice feature, but would take a lot to
						// clean up grammatical and other errors.  Maybe with a regex parser...
						replacestr((char *)cptr, target->GetName(), "you");
						replacestr((char *)cptr, " he", " you");
						replacestr((char *)cptr, " she", " you");
						replacestr((char *)cptr, " him", " you");
						replacestr((char *)cptr, " her", " you");
						target->CastToClient()->QueuePacket(outapp);
						
					}
					else
					*/
					entity_list.QueueCloseClients(this, outapp, true, 100,0,true,FILTER_SOCIALS);
					
					safe_delete(outapp);
					break;
				}
				case OP_EmoteAnim: {
					// Someone animation an emote (i.e., waving arm to say hello)
					entity_list.QueueCloseClients(this, app, true);
					break;
				}
				case OP_SetServerFilter: {
					SetServerFilter_Struct* filter=(SetServerFilter_Struct*)app->pBuffer;
					ServerFilter(filter);
					break;
				}
				case OP_GMDelCorpse: {
					if(this->Admin() < 100) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/delcorpse");
						break;
					}
					GMDelCorpse_Struct* dc = (GMDelCorpse_Struct *)app->pBuffer;
					Mob* corpse = entity_list.GetMob(dc->corpsename);
					if(corpse==0) {
						break;
					}
					if(corpse->IsCorpse() != true) {
						break;
					}
					corpse->CastToCorpse()->Delete();
					cout << name << " deleted corpse " << dc->corpsename << endl;
					Message(13, "Corpse %s deleted.", dc->corpsename);
					break;
				}
				case OP_GMKick: {
					if(this->Admin() < 150) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/kick");
						break;
					}
					GMKick_Struct* gmk = (GMKick_Struct *)app->pBuffer;

					Client* client = entity_list.GetClientByName(gmk->name);
					if(client==0) {
						if (!worldserver.Connected())
							Message(0, "Error: World server disconnected");
						else {
							ServerPacket* pack = new ServerPacket;
							pack->opcode = ServerOP_KickPlayer;
							pack->size = sizeof(ServerKickPlayer_Struct);
							pack->pBuffer = new uchar[pack->size];
							ServerKickPlayer_Struct* skp = (ServerKickPlayer_Struct*) pack->pBuffer;
							strcpy(skp->adminname, gmk->gmname);
							strcpy(skp->name, gmk->name);
							skp->adminrank = this->Admin();
							worldserver.SendPacket(pack);
							safe_delete(pack);
						}
					}
					else {
						entity_list.QueueClients(this,app);
						//client->Kick();
					}
					break;
				}
				case OP_GMServers: {
					if (!worldserver.Connected())
						Message(0, "Error: World server disconnected");
					else {
						ServerPacket* pack = new ServerPacket;
						pack->size = strlen(this->GetName())+2;
						pack->pBuffer = new uchar[pack->size];
						memset(pack->pBuffer, 0, pack->size);
						pack->opcode = ServerOP_ZoneStatus;
						memset(pack->pBuffer, (int8) admin, 1);
						strcpy((char *) &pack->pBuffer[1], this->GetName());
						worldserver.SendPacket(pack);
						safe_delete(pack);
					}
					break;
				}
				case OP_Illusion: {
					Illusion_Struct* bnpc = (Illusion_Struct*)app->pBuffer;
					// @merth: these need to be implemented
					/*
					texture		= bnpc->texture;
					helmtexture	= bnpc->helmtexture;
					luclinface	= bnpc->luclinface;
					*/
					race		= bnpc->race;
					size		= 0;
					
					entity_list.QueueClients(this,app);
					break;
				}
				case OP_GMBecomeNPC: {
					if(this->Admin() < 80) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/becomenpc");
						break;
					}
					entity_list.QueueClients(this, app, false);
					BecomeNPC_Struct* bnpc = (BecomeNPC_Struct*)app->pBuffer;
					
					Mob* cli = (Mob*) entity_list.GetMob(bnpc->id);
					if(cli==0)
						break;
					
					cli->SendAppearancePacket(30, 1, true);
					cli->CastToClient()->SetBecomeNPC(true);
					cli->CastToClient()->SetBecomeNPCLevel(bnpc->maxlevel);
					cli->CastToClient()->SimpleMessage_StringID(0,TOGGLE_OFF);
					cli->CastToClient()->tellsoff = true;
					//TODO: Make this toggle a BecomeNPC flag so that it gets updated when people zone in as well; Make combat work with this.
					break;
				}
				case OP_SafeFallSuccess: {
					break;
				}
				// Changes made based on Bobs work on foraging.  Now can set items in the forage database table to 
				// forage for.
				case OP_Forage:	{
					// @merth: This needs to be redone with new item classes
					
					uint32 food_id = ForageItem(m_pp.zone_id, GetSkill(FORAGE));
					const Item_Struct* food_item = database.GetItem(food_id);
					
					if (food_item && food_item->Name!=0) {
						int32 stringid=0;
						switch(food_id){
							case 13044:
								stringid=FORAGE_WATER;
								break;
							case 13106:
								stringid=FORAGE_GRUBS;
								break;
							case 13045:
							case 13046:
							case 13047:
							case 13048:
							case 13419:
								stringid=FORAGE_FOOD;
								break;
							default:
								stringid=FORAGE_NOEAT;
						}
						this->SimpleMessage_StringID(MT_Skills,stringid);
						const ItemInst* inst = ItemInst::Create(food_item, 1);
						this->PutItemInInventory(SLOT_CURSOR,*inst);
						this->SendItemPacket(SLOT_CURSOR,inst,ItemPacketSummonItem);
					}
					else 
							this->SimpleMessage_StringID(MT_Skills,FORAGE_FAILED);
					
					//See if the player increases their skill
					float wisebonus =  (m_pp.WIS > 200) ? 20 + ((m_pp.WIS - 200) * 0.05) : m_pp.WIS * 0.1;
					if ((55-(GetSkill(FORAGE)*0.236))+wisebonus > (float)rand()/RAND_MAX*100)
						this->SetSkill(FORAGE,GetSkill(FORAGE)+1);
					
					break;
				}
				case OP_Mend: {
					int mendhp = GetMaxHP() / 4;
					int noadvance = (rand()%200);
					int currenthp = GetHP();
					if (rand()%100 <= GetSkill(MEND)) {
						SetHP(GetHP() + mendhp);
						SendHPUpdate();
						SimpleMessage_StringID(4,MEND_SUCCESS);
						//Message(4, "You mend your wounds and heal some damage");
					}
					else if (noadvance > 175 && currenthp > mendhp) {
						SetHP(GetHP() - mendhp);
						SendHPUpdate();
						//Message(4, "You fail to mend your wounds and damage yourself!");
						SimpleMessage_StringID(4,MEND_WORSEN);
					}
					else if (noadvance > 175 && currenthp <= mendhp) {
						SetHP(1);
						SendHPUpdate();
						//Message(4, "You fail to mend your wounds and damage yourself!");
						SimpleMessage_StringID(4,MEND_WORSEN);
					}
					else	{
						//Message(4, "You fail to mend your wounds");
						SimpleMessage_StringID(4,MEND_FAIL);
					}
					
					if ((GetSkill(MEND) < noadvance) && (rand()%100 < 35) && (GetSkill(MEND) < 101))
						this->SetSkill(MEND,GetSkill(MEND)+1);
					break;
				}
				case OP_ConfirmDelete:
				case OP_Drink: {
					break;
				}
				case OP_EnvDamage: {
					EnvDamage2_Struct* ed = (EnvDamage2_Struct*)app->pBuffer;
					if(admin>=100 && GetGM()){
						Message(13, "Your GM status protects you from %i points of type %i environmental damage.", ed->damage, ed->dmgtype);
						SetHP(GetHP()-1);//needed or else the client wont acknowledge
					}
					else
						SetHP(GetHP()-ed->damage);
					APPLAYER outapp;
					CreateHPPacket(&outapp);
					entity_list.QueueClientsByTarget(this, &outapp,false);
					break;
				}
				case OP_Damage: {
					// Broadcast to other clients
					entity_list.QueueClients(this, app, false);
					break;
				}
				case OP_Report: {
					break;
				}
				case OP_AAAction: {
					if(strncmp((char *)app->pBuffer,"on ",3) == 0) {
						// turn on percentage
						m_pp.perAA = atoi((char *)&app->pBuffer[3]);
						// send an update
						SendAAStats();
						SendAATable();
					} else if(strcmp((char *)app->pBuffer,"off") == 0) {
						// set percentage to 0%
						m_pp.perAA = 0;
						// send an update
						SendAAStats();
						SendAATable();
						//	cout << "UpdateAA packet: OFF." << endl;
					} else if(strncmp((char *)app->pBuffer,"buy ",4) == 0) {
						char item_name[128];
						int buy_item = atoi((char *)&app->pBuffer[4]);
						
						int item_cost = 1;
						int max_level = 1;
						int bought = 0;
						if(database.GetAASkillVars(buy_item,item_name,&item_cost,&max_level)) {
							uint8 *aa_item = &(((uint8 *)&aa)[buy_item]);
							int32 cur_level = (*aa_item)+1;
							int32 cost = buy_item <= 17 ? item_cost : item_cost * cur_level;
							//Disabled for now due to problems... - NOT
							if(m_pp.aapoints >= cost && cur_level <= (int32)max_level) {
								*aa_item = cur_level;
								m_pp.aapoints -= cost;
								database.SetPlayerAlternateAdv(account_id, m_pp.name, &aa);
								Message(15,"Skill \'%s\' (%d) purchased for the cost of %d ability point(s)",item_name,cur_level,cost);
								bought = 1;
							}
							if(bought){
								//aa_array[122]
								int cur = 0;
								for (cur = 0; cur <= 121; cur++){
									if (m_pp.aa_array[cur].AA == 0 || m_pp.aa_array[cur].AA == buy_item ){
										m_pp.aa_array[cur].AA = buy_item;
										m_pp.aa_array[cur].value = cur_level*16;
										break;
									}
								}
								int counter = 0;
								int counter2 = 0;
								for (cur = 1;cur<=17;cur++){
									if(GetAA(cur)!=0)
										counter += GetAA(cur);
								}
								if (counter <= 6)
									counter2 = 1;
								counter = 0;
								for (cur = 18;cur<=34;cur++){
									if(GetAA(cur)!=0)
										counter += GetAA(cur);
								}
								if (counter <= 12)
									counter2 = 2;
								counter = 0;
								for (cur = 35;cur<=128;cur++){
									if(GetAA(cur)!=0)
										counter += GetAA(cur);
								}
								if (counter <= 24)
									counter2 = 3;
								ChangeAATitle((int8)counter2);
							}
							SendAATable();
							SendAAStats();
							cout << "UpdateAA packet: BUY. Item: " << buy_item << " Cost: " << item_cost;
							cout << (bought ? " (bought)" : " (not enough points or max level)") << endl;
						}
					}
					else if(strncmp((char *)app->pBuffer,"activate ",9) == 0) {
						LogFile->write(EQEMuLog::Debug, "%s::ActivateAA(%i,%i)", GetName(), atoi((char *)app->pBuffer+9), atoi((char *)app->pBuffer+13));
						int activate = atoi((char *)&app->pBuffer[9]);
						ActivateAA(activate);
					}
					else {
						cout << "Unknown command in UpdateAA opcode: 0x" << hex << setfill('0') << setw(4) << app->opcode << dec;
						//cout << " size:" << app->size << endl;
						//							DumpPacket(app->pBuffer, app->size);
					}
					break;
				}
				case OP_TraderBuy:{
					if(app->size==sizeof(TraderBuy_Struct)){
						TraderBuy_Struct* tbs = (TraderBuy_Struct*)app->pBuffer;
						if(Client* trader=entity_list.GetClientByID(tbs->traderid)){
							BuyTraderItem(tbs,trader,app);
						}
					}
					break;
				}
				case OP_Trader:{
					if(app->size==sizeof(Trader_ShowItems_Struct)){ //Show Items
						Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)app->pBuffer;
						if(sis->code==2){//end trader
							this->Trader_EndTrader();
						}
						else if(sis->code==4){ //end transaction
							Client* tmp=entity_list.GetClientByID(sis->traderid);
							if(tmp)
								tmp->withcustomer=false;
						}
						else if(sis->code==11){
							this->Trader_ShowItems();
						}
					}
					else if(app->size==sizeof(ClickTrader_Struct)){
						ClickTrader_Struct* ints = (ClickTrader_Struct*)app->pBuffer;
						if(ints->code==1){
							GetItems_Struct* gis=GetTraderItems();
							for(int i=0;i<80;i++){
								if(gis->items[i]>0 && gis->items[i]<database.GetMaxItem() && database.GetItem(gis->items[i])!=0)
									database.SaveTraderItem(this->CharacterID(),gis->items[i],ints->itemcost[i],i);
								else
									i=80; //sony doesnt memset so assume done on first bad item
							}
							safe_delete(gis);
							this->Trader_StartTrader();
						}
						else
							LogFile->write(EQEMuLog::Error, "Unknown TraderStruct code of: %i\n", ints->code);
					}
					else{
						LogFile->write(EQEMuLog::Error, "Unknown size for OP_Trader: %i\n", app->size);
						DumpPacket(app);
						break;
					}
					
					break;
				}
				case OP_GMFind: {
					if (this->Admin() < 80) {
						Message(13, "Your account has been reported for hacking.");
						database.SetHackerFlag(this->account_name, this->name, "/find");
						break;
					}
					//Break down incoming
					GMSummon_Struct* request=(GMSummon_Struct*)app->pBuffer;
					//Create a new outgoing
					APPLAYER *outapp = new APPLAYER(OP_GMFind, sizeof(GMSummon_Struct));
					GMSummon_Struct* foundplayer=(GMSummon_Struct*)outapp->pBuffer;
					//Copy the constants
					strcpy(foundplayer->charname,request->charname);
					strcpy(foundplayer->gmname, request->gmname);
					//Check if the NPC exits intrazone...
					Mob* gt = entity_list.GetMob(request->charname);
					if (gt != 0) {
						foundplayer->success=1;
						foundplayer->x=(sint32)gt->GetX();
						foundplayer->y=(sint32)gt->GetY();

						foundplayer->z=(sint32)gt->GetZ();
						foundplayer->zoneID=zone->GetZoneID();
					}
					//Send the packet...
					FastQueuePacket(&outapp);
					break;
				}
/*				case OP_PickPocket: { // Pickpocket
					if (app->size != sizeof(PickPocket_Struct)){
						LogFile->write(EQEMuLog::Error, "Size mismatch for Pick Pocket packet");
						DumpPacket(app);
					}
					PickPocket_Struct* pick_in = (PickPocket_Struct*) app->pBuffer;
					LogFile->write(EQEMuLog::Debug,
						"PickPocket to:%i from:%i myskill:%i type:%i",
						pick_in->to, pick_in->from ,pick_in->myskill, pick_in->type);
						
					APPLAYER* outapp = new APPLAYER(OP_PickPocket, sizeof(sPickPocket_Struct));
					sPickPocket_Struct* pick_out = (sPickPocket_Struct*) outapp->pBuffer;
					Mob* victim = entity_list.GetMob(pick_in->to);
					if (!victim)
						break;
					if (pick_in->myskill == 0) {
						LogFile->write(EQEMuLog::Debug,
							"Client pick pocket response");
						DumpPacket(app);
						safe_delete(outapp);
						break;
					}
					uint8 success = 0;
					if (RandomTimer(1,900) >= (GetSkill(PICK_POCKETS)+GetDEX())){
						success = RandomTimer(1,5);
						if (GetSkill(PICK_POCKETS) >=200)
							success += 1;
					}
					if ( (victim->IsNPC() && victim->CastToNPC()->IsInteractive()) || (victim->IsClient()) ) {
						if (EQDEBUG>=5) LogFile->write(EQEMuLog::Debug, "PickPocket picking ipc/client");
						pick_out->to   = pick_in->from;
						pick_out->from = pick_in->to;
						if (victim->IsClient() && !success){
								victim->CastToClient()->QueuePacket(app);
						}
						else if (victim->IsNPC()
						&& victim->CastToNPC()->IsInteractive()
						&& GetPVP() 
						&& (GetPVP() == victim->CastToClient()->GetPVP())
						&& !success

						) {
						int16 hate = RandomTimer(1,503);
						int16 tmpskill = (GetSkill(PICK_POCKETS)+GetDEX());
						if( hate >= tmpskill ){
							victim->AddToHateList(this, 1, 0);
							entity_list.MessageClose(victim, true, 200, 0, "%s says, your not as quick as you thought you were thief!", victim->GetName());
						}
					}
					if(!success){
						// Failed
						QueuePacket(outapp);
					}
					else if (success) {
						pick_out->to   = pick_in->from;
						pick_out->from = pick_in->to;

						pick_out->type = success;
						pick_out->coin = (RandomTimer(1,RandomTimer(2,10)));
						switch (success){
							case 1:
								AddMoneyToPP(0,0,0,pick_out->coin, false);
								break;
							case 2:
								AddMoneyToPP(0,0,pick_out->coin,0, false);
								break;
							case 3:
								AddMoneyToPP(0,pick_out->coin,0,0, false);
								break;
							case 4:
								AddMoneyToPP(pick_out->coin,0,0,0, false);
								break;
							case 5:
							case 6: {
								// Item
								// @merth: This needs to be redone with new item classes
								
								APPLAYER* outapp2 = new APPLAYER(OP_PickPocket, sizeof(sItem_PickPocket_Struct));
								sItem_PickPocket_Struct* pick_out2 = (sItem_PickPocket_Struct*) outapp2->pBuffer;
								pick_out2->to   = pick_in->from;
								pick_out2->from = pick_in->to;
								pick_out2->type = success;
								// FIXME: This should search the npc inventory for an item of fail the Pickpocket attempt
								const Item_Struct* temp_item = 0;
								while (!temp_item) {
									temp_item = database.GetItem(RandomTimer(1001,32768));
								}
								//Inventory[30] = temp_item->ItemNumber;
								PutItemInInventory(30, temp_item);
								memcpy(&pick_out2->item, temp_item, sizeof(Item_Struct));
								pick_out2->item.CurrentEquipSlot = 30;
								QueuePacket(outapp2);
								safe_delete(outapp2);
								
								break;
							}
							default:
								LogFile->write(EQEMuLog::Error,"Unknown success in OP_PickPocket %i", __LINE__); break;
						}
						if (success!=5)
							QueuePacket(outapp);
					}
					else {
						LogFile->write(EQEMuLog::Error, "Unknown error in OP_PickPocket");
					}
				}
				else {
					if (EQDEBUG>=5)
						LogFile->write(EQEMuLog::Debug, "PickPocket picking npc");
					if (success) {
						pick_out->to   = pick_in->from;
						pick_out->from = pick_in->to;
						pick_out->type = success;
						pick_out->coin = (RandomTimer(1,RandomTimer(2,10)));
						switch (success){
							case 1:
								AddMoneyToPP(0,0,0,pick_out->coin, false);
								break;
							case 2:
								AddMoneyToPP(0,0,pick_out->coin,0, false);
								break;
							case 3:
								AddMoneyToPP(0,pick_out->coin,0,0, false);
								break;
							case 4:
								AddMoneyToPP(pick_out->coin,0,0,0, false);
								break;
							case 5:
							case 6: {
								// Item
								// @merth: This needs to be redone with new item classes
								
								APPLAYER* outapp2 = new APPLAYER(OP_PickPocket, sizeof(sItem_PickPocket_Struct));
								sItem_PickPocket_Struct* pick_out2 = (sItem_PickPocket_Struct*) outapp2->pBuffer;
								pick_out2->to   = pick_in->from;
								pick_out2->from = pick_in->to;
								pick_out2->type = success;
								// FIXME: This should search the npc inventory for an item of fail the Pickpocket attempt
								const Item_Struct* temp_item = 0;
								while (!temp_item) {
									temp_item = database.GetItem(RandomTimer(1001,32768));
								}
								memcpy(&pick_out2->item, temp_item, sizeof(Item_Struct));
								pick_out2->item.CurrentEquipSlot = 30;
								QueuePacket(outapp2);
								safe_delete(outapp2);
								break;
								
							}
							default:
								LogFile->write(EQEMuLog::Error,"Unknown success in OP_PickPocket %i", __LINE__); break;
						}
						if (success!=5)
							QueuePacket(outapp);
					}
					else {
						int16 hate = RandomTimer(1,503);
						int16 tmpskill = (GetSkill(PICK_POCKETS)+GetDEX());
						if( hate >= tmpskill ){
							victim->AddToHateList(this, 1, 0);
								entity_list.MessageClose(victim, true, 200, 0, "%s says, your not as quick as you thought you were thief!", victim->GetName());
						}
						QueuePacket(outapp);
					}
				}
					safe_delete(outapp);
					break;
				}*/
				case OP_Bind_Wound:{
					if (app->size != sizeof(BindWound_Struct)){
						LogFile->write(EQEMuLog::Error, "Size mismatch for Bind wound packet");
						DumpPacket(app);
					}
					BindWound_Struct* bind_in = (BindWound_Struct*) app->pBuffer;
					Mob* bindmob = entity_list.GetMob(bind_in->to);
					if (!bindmob){
					    LogFile->write(EQEMuLog::Error, "Bindwound on non-exsistant mob from %s", this->GetName());
					}
					LogFile->write(EQEMuLog::Debug, "BindWound in: to:\'%s\' from=\'%s\'", bindmob->GetName(), GetName());
					BindWound(bindmob, true);
					break;
				}
				case OP_Disciplines:{
					ClientDiscipline_Struct* Disc_in = (ClientDiscipline_Struct*) app->pBuffer;
					Discipline(Disc_in, target);
					break;
				}
				case OP_TrackTarget:{
					IsTracking=(IsTracking==false);
					break;
				}
				case OP_Track:{
					IsTracking=false;
					if(((GetClass()==RANGER) || (GetClass()==DRUID)) && (GetSkill(TRACKING)==0))
						SetSkill(TRACKING,1);
					if(GetClass() != RANGER && GetClass() != DRUID)
						break;

					entity_list.MakeTrackPacket(this);
					break;
				}
				case OP_TrackUnknown:{
					// size 0 send right after OP_Track
					break;
				}
				/*case OP_TrackTarget: {
					// Looks like an entityid should probably do something with it.
					break;
				}*/
				/*case 0x0193: {
					// Not sure what this opcode does.  It started being sent when OP_ClientUpdate was
					// changed to pump OP_ClientUpdate back out instead of OP_MobUpdate
					// 2 bytes: 00 00
				}*/
				case 0x01e7: {
					// Dunno what this opcode does but client needs an answer
					if(app->size==8){
						app->pBuffer[4]=0;
						app->pBuffer[5]=0;
						app->pBuffer[6]=0;
						app->pBuffer[7]=0;
						QueuePacket(app);
					}
					break;
				}
				case OP_ClientError: {
					ClientError_Struct* error = (ClientError_Struct*)app->pBuffer;
					LogFile->write(EQEMuLog::Error, "Client error: %s", error->character_name);
					LogFile->write(EQEMuLog::Error, "Error message:%s", error->message);
					//if (EQDEBUG>=5)
					//	DumpPacket(app);
					break;
				}
				case OP_DumpName:
				case OP_CrashDump:
			#ifndef _EQDEBUG
					//remove the following when they are found here to reduce spam
				case 0x01ca:
				case 0x0228:
				case 0x00a9:
				case 0x001a:
			#endif
					break;
				default: {
					cout << "Unknown opcode: 0x" << hex << setfill('0') << setw(4) << app->opcode << dec
						<< " size:" << app->size << " Client:" << GetName() << endl;
					DumpPacket(app->pBuffer, app->size);
					break;
				}
			}
			break;
		}
		case CLIENT_KICKED:
		case DISCONNECTED:
		case CLIENT_LINKDEAD:
			break;
		default: {
			cerr << "Unknown client_state:" << (int16) client_state << endl;
			break;
		}
	}
	
	return ret;
}

void Client::DBAWComplete(int8 workpt_b1, DBAsyncWork* dbaw) {
	Entity::DBAWComplete(workpt_b1, dbaw);
	switch (workpt_b1) {
		case DBA_b1_Entity_Client_InfoForLogin: {
			if (!FinishConnState2(dbaw))
				client_state = CLIENT_ERROR;
			break;
		}
		case DBA_b1_Entity_Client_Save: {
			char errbuf[MYSQL_ERRMSG_SIZE];
			int32 affected_rows = 0;
			DBAsyncQuery* dbaq = dbaw->PopAnswer();
			if (dbaq->GetAnswer(errbuf, 0, &affected_rows) && affected_rows == 1) {
				if (dbaq->QPT())
					SaveBackup();
			}
			else {
				cout << "Async client save failed. '" << errbuf << "'" << endl;
				Message(13, "Error: Asyncronous save of your character failed.");
				if (Admin() >= 200)
					Message(13, "errbuf: %s", errbuf);
			}
			pQueuedSaveWorkID = 0;
			break;
		}
		default: {
			cout << "Error: Client::DBAWComplete(): Unknown workpt_b1" << endl;
			break;
		}
	}
}

bool Client::FinishConnState2(DBAsyncWork* dbaw) {
	int8 gmspeed = 0;
	uint32 pplen = 0, aalen = 0;
	memset(&m_pp, 0, sizeof(PlayerProfile_Struct));
	DBAsyncQuery* dbaq = 0;
	APPLAYER* outapp = 0;
	MYSQL_RES* result = 0;
	bool loaditems;
	char errbuf[MYSQL_ERRMSG_SIZE];
	
	for (int8 i=1; i<=3; i++) {
		dbaq = dbaw->PopAnswer();
		if (!dbaq) {
			cout << "Error in FinishConnState2(): dbaq==0" << endl;
			return false;
		}
		if (!dbaq->GetAnswer(errbuf, &result)) {
			cout << "Error in FinishConnState2(): !dbaq[" << dbaq->QPT() << "]->GetAnswer(): " << errbuf << endl;
			return false;
		}
		if (dbaq->QPT() == 1) {
			database.GetAccountInfoForLogin_result(result, 0, account_name, &lsaccountid, &gmspeed);
		}
		else if (dbaq->QPT() == 2) {
			loaditems = database.GetCharacterInfoForLogin_result(result, 0, 0, &m_pp, &m_inv, &pplen, &aa, &aalen, &guilddbid, &guildrank);
		}
		else if (dbaq->QPT() == 3) {
			database.LoadFactionValues_result(result, &factionvalue_list);
		}
		else {
			cout << "Error in FinishConnState2(): dbaq->PQT() unknown" << endl;
			return false;
		}
	}
	
	char temp1[64];
	if (database.GetVariable("Max_AAXP", temp1, sizeof(temp1)-1)) {
		max_AAXP = atoi(temp1);
	}
	
	//int32 aalen = database.GetPlayerAlternateAdv(account_id, name, &aa);
	//if (aalen == 0) {
	//	cout << "Client dropped: !GetPlayerAlternateAdv, name=" << name << endl;
	//	return false;
	//}
	
	
	
	////////////////////////////////////////////////////////////	// Player Profile Packet
	// Try to find the EQ ID for the guild, if doesnt exist, guild has been deleted.

	// Clear memory, but leave it in the DB (no reason not to, guild might be restored?)
	strcpy(name, m_pp.name);
	strcpy(lastname, m_pp.last_name);
	if((m_pp.x == -1 && m_pp.y == -1 && m_pp.z == -1)||(m_pp.x == -2 && m_pp.y == -2 && m_pp.z == -2)) {
		m_pp.x = zone->safe_x();
		m_pp.y = zone->safe_y();
		m_pp.z = zone->safe_z();
	}

	x_pos		= m_pp.x;
	y_pos		= m_pp.y;
	z_pos		= m_pp.z/10;
	heading		= m_pp.heading/16;
	race		= m_pp.race;
	base_race	= m_pp.race;
	class_		= m_pp.class_;
	gender		= m_pp.gender;
	base_gender	= m_pp.gender;
	level		= m_pp.level;
	deity		= m_pp.deity;//FYI: DEITY_AGNOSTIC = 396; still valid?
	haircolor	= m_pp.haircolor;
	beardcolor	= m_pp.beardcolor;
	eyecolor1	= m_pp.eyecolor1;
	eyecolor2	= m_pp.eyecolor2;
	hairstyle	= m_pp.hairstyle;
	luclinface	= m_pp.face;
	m_pp.hunger_level = 6000;
	m_pp.thirst_level = 6000;
	//aa_title	= m_pp.aa_title;
	//m_pp.timeplayed=64;
	//m_pp.birthday=1057434792;
	//m_pp.lastlogin=1057464792;
	if (admin < 80)
		m_pp.gm = 0;
	
	guildeqid = database.GetGuildEQID(guilddbid);
	if (guildeqid == GUILD_NONE) {
		guilddbid = 0;
		guildrank = GUILD_MAX_RANK;
		m_pp.guildid = 0xFFFFFFFF;
	}
	else
		m_pp.guildid = guildeqid;
	
	switch (race)
	{
		case OGRE:
			size = 9;break;
		case TROLL:
			size = 8;break;
		case VAHSHIR:

		case BARBARIAN:
			size = 7;break;
		case HUMAN:
		case HIGH_ELF:
		case ERUDITE:
		case IKSAR:
			size = 6;break;
		case HALF_ELF:
			size = 5.5;break;
		case WOOD_ELF:
		case DARK_ELF:
			size = 5;break;
		case DWARF:
			size = 4;break;
		case HALFLING:
			size = 3.5;break;
		case GNOME:
			size = 3;break;
		default:
			size = 0;break;
	}

#ifdef GUILDWARS
	m_pp.ldon_guk_points = 0;
	m_pp.ldon_mirugal_points = 0;
	m_pp.ldon_mistmoore_points = 0;
	m_pp.ldon_rujarkian_points = 0;
	m_pp.ldon_takish_points = 0;
	m_pp.ldon_available_points = 0;
	if(m_pp.pvp)
		m_pp.pvp = false;
	profit = 0;
	if(GuildDBID() != 0 && GuildRank() == 0)
	{
	GuildLocation* gl = 0;
	gl = location_list.FindClosestLocationByClient(this);
	if(gl != 0 && gl->GetLocationType() == CITY && gl->GetGuildOwner() == GuildDBID())
	{
	if(gl->GetProfit() > 0)
	{
	m_pp.platinum_bank += gl->GetProfit()/1000;
	profit = gl->GetProfit()/1000;
	gl->SetProfit(0);
	database.SetLocationProfit(gl->GetLocationID(),0);
	}
	}
	}
	if(GuildDBID() != 0)
	{
	this->castpercentbonus = location_list.GetCasterAttackBonus(GuildDBID());
	this->meleepercentbonus = location_list.GetMeleeAttackBonus(GuildDBID());
	}
					sint32 availpts = database.GetAvailablePoints(CharacterID(),0);
#ifdef GWDEBUG
					printf("Available points: %i for %s\n",availpts,GetName());
#endif
					if(availpts == -9999999)
					{
#ifdef GWDEBUG
					printf("Setting up points table for %s\n",GetName());
#endif
						database.SetupPointsTable(CharacterID(),0);
						availpts = 0;
					}
					if(availpts < 0)
					availpts = 0;

					m_pp.ldon_available_points = (int32)availpts;

					if(Admin() == 0)
					{
					if(guildwars.GetMaxUsers() == 0 && GetLevel() >= guildwars.GetMinimumLevel())
					{
						guildwars.SetMaxUsers(1);
						guildwars.SetCurrentUsers(1);
					}
					else if(guildwars.GetMaxUsers() != 0)
					{
						guildwars.SetCurrentUsers(guildwars.GetCurrentUsers()+1);
						if(guildwars.GetCurrentUsers() > guildwars.GetMaxUsers())
							guildwars.SetMaxUsers(guildwars.GetCurrentUsers());
					}
					}
#endif

	if (spells_loaded) {
		for (int i=0; i<15; i++) {
			for(int z=0;z<15;z++) {
			// check for duplicates
				if(buffs[z].spellid == m_pp.buffs[i].spellid) {
					buffs[z].spellid = -1;
				}
			}
			
			if (m_pp.buffs[i].spellid <= SPDAT_RECORDS && m_pp.buffs[i].spellid != 0 && m_pp.buffs[i].duration > 0) {
				buffs[i].spellid			= m_pp.buffs[i].spellid;
				buffs[i].ticsremaining		= m_pp.buffs[i].duration;
				buffs[i].casterlevel		= m_pp.buffs[i].level;
				buffs[i].effect = spells[buffs[i].spellid].effectid[0];
				if(m_pp.buffs[i].level == 0 || m_pp.buffs[i].level > 100)
				{
					m_pp.buffs[i].level = 1;
					buffs[i].casterlevel = 1;
				}
				buffs[i].casterid			= 0;
				buffs[i].durationformula	= spells[buffs[i].spellid].buffdurationformula;
			}
			else {
				m_pp.buffs[i].slotid			= 0;
				buffs[i].spellid			= -1;
				m_pp.buffs[i].level			= 0;
				m_pp.buffs[i].spellid			= -1;
				m_pp.buffs[i].duration			= 0;
				m_pp.buffs[i].effect		= 0;

			}
		}
		for (int j1=0; j1<15; j1++) {
			if (buffs[j1].spellid <= SPDAT_RECORDS) {
				for (int x1=0; x1 < 12; x1++) {
					switch (spells[buffs[j1].spellid].effectid[x1]) {
						case SE_SummonHorse: {
							hasmount = false;
							break;
						}
						case SE_Illusion: {
							buffs[j1].spellid = 0xFFFF;
							x1 = 12;
							break;
						}
						case SE_Charm: {
							buffs[j1].spellid = 0xFFFF;
							x1 = 12;
							break;
						}
					}
				}
			}
		}
	}
	
	/*
	if(this->isgrouped) {
		Group* group;
		group = entity_list.GetGroupByClient(this);
		for(int z=0; z<5; z++) {
			memset(m_pp.GMembers[z],0,sizeof(group->members[z]->GetName()));
		}
	}
	else {
		for(int z=0; z<5; z++) {
			memset(m_pp.GMembers[z],0,sizeof(m_pp.GMembers[0]));
		}
	}
	*/
	
	CalcBonuses();
	CalcMaxHP();
	CalcMaxMana();
	if (m_pp.cur_hp <= 0)
		m_pp.cur_hp = GetMaxHP();
	
	SetHP(m_pp.cur_hp);
	Mob::SetMana(m_pp.mana);
	
	m_pp.zone_change_count++;
	/*
	Unknowns

	int bp = 0;


	unsigned char rawData1[4] = { 0x6A, 0x01, 0x00, 0x00 };
	for (bp = 0; bp <= 3; bp++ ){
		m_pp.unknown0652[bp] = rawData1[bp];
	}
	m_pp.unknown0672 = 5;
	unsigned char rawData2[4] = { 0x64, 0x00, 0x00, 0x00};
	for (bp = 0; bp <= 3; bp++ ){
		m_pp.unknown2909[bp] = rawData2[bp];
	}
	uint32 rawData3[52] = {
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0x00000000, 0x00000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000, 
		0xFF000000, 0xFF000000, 0xFF000000, 0xFF000000 
	};
	for (bp = 0; bp <= 52; bp++ ){
		m_pp.unknown_skills[bp] = rawData3[bp];
	}
	unsigned char rawData4[88] =	{
		0x00, 0x00, 0xE0, 0x40, 0x00, 0x00, 0x40, 0x40, 0x00, 0x00, 0x20, 0x40, 0x00, 0x00, 0xB0, 0x40, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x20, 0x27, 0x00, 0x00, 0x28, 0x27, 0x00, 0x00, 
	};
	for (bp = 0; bp <= 87; bp++ ){
		m_pp.unknown3472[bp] = rawData4[bp];
	}
	unsigned char rawData5[28] =	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	};
	for (bp = 0; bp <= 27; bp++ ){
		m_pp.unknown3564[bp] = rawData5[bp];
	}
	unsigned char rawData6[52] =	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x91, 0x9E, 0x30, 0xCF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 
	};
	for (bp = 0; bp <= 51; bp++ ){
		m_pp.unknown3596[bp] = rawData6[bp];
	}
	unsigned char rawData7[20] =	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 
	};
	for (bp = 0; bp <= 19; bp++ ){
		m_pp.unknown3656[bp] = rawData7[bp];
	}
	unsigned char rawData8[384] =	{
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
	};
	for (bp = 0; bp <= 383; bp++ ){
		m_pp.unknown3920[bp] = rawData8[bp];
	}
*/
	int32 groupid=database.GetGroupID(this->GetName());
	Group* group=0;
	if(groupid>0){
		group=entity_list.GetGroupByID(groupid);
		if(group)
			group->UpdatePlayer(this->CastToMob());
		else
			database.SetGroupID(this->GetName(),0);
	}
	if(groupid<=0 || !group){
		int xy=0;
		for(xy=0;xy<6;xy++)
			memset(m_pp.groupMembers[xy],0,64);
	}

	if(m_pp.z<zone->newzone_data.underworld){
		m_pp.x = zone->newzone_data.safe_x;
		m_pp.y = zone->newzone_data.safe_y;
		m_pp.z = zone->newzone_data.safe_z;
	}
	if(m_pp.class_==SHADOWKNIGHT || m_pp.class_==PALADIN){
		int32 abilitynum=0;
		if(m_pp.class_==SHADOWKNIGHT)
			abilitynum=89;
		else
			abilitynum=87;
		int32 remaining=database.GetTimerRemaining(CharacterID(),abilitynum);
		if(remaining>0 && remaining<15300){
			m_pp.ability_down=1;
			m_pp.ability_up=0;
			m_pp.ability_number=abilitynum;
			int8 minutes=0;
			if(remaining>60){
				minutes=(remaining/60);
				remaining=remaining-(minutes*60);
			}
			m_pp.ability_time_minutes=minutes;
			m_pp.ability_time_seconds=remaining;
			AbilityTimer=true;
		}
		else{
			m_pp.ability_down=0;
			m_pp.ability_up=1;
			m_pp.ability_number=0;
			m_pp.ability_time_minutes=0;
			m_pp.ability_time_seconds=0;
		}
	}
	char val[3] = {0};
	if (database.GetVariable("Expansions", val, 3))
		m_pp.expansion = atoi(val);
	else
		m_pp.expansion = 0x3F;
	CRC32::SetEQChecksum((unsigned char*)&m_pp, sizeof(PlayerProfile_Struct)-4);
	outapp = new APPLAYER(OP_PlayerProfile,sizeof(PlayerProfile_Struct));
	memcpy(outapp->pBuffer,&m_pp,outapp->size);
	outapp->Deflate();
	QueuePacket(outapp);
	safe_delete(outapp);
	
	
	
	
	////////////////////////////////////////////////////////////
	// Server Zone Entry Packet
	outapp = new APPLAYER(OP_ZoneEntry, sizeof(ServerZoneEntry_Struct));
	ServerZoneEntry_Struct* sze = (ServerZoneEntry_Struct*)outapp->pBuffer;
	
	strcpy(sze->name, m_pp.name);
	strcpy(sze->last_name, m_pp.last_name);
	sze->gm				= m_pp.gm;
	sze->race			= m_pp.race;
	sze->class_			= m_pp.class_;
	sze->level			= m_pp.level;
	sze->size			= size;
	sze->deity			= m_pp.deity;
	sze->zone_id		= zone->GetZoneID();
	sze->x				= m_pp.x;
	sze->y				= m_pp.y;
	sze->z				= m_pp.z;
	sze->heading		= m_pp.heading;
	if(sze->heading>0)
		sze->heading/=4;
	//sze->delta_x		= delta_x;
	//sze->delta_z		= delta_z;
	//sze->delta_heading	= delta_heading;
	sze->gender			= m_pp.gender;
	sze->anon			= m_pp.anon;
	sze->pvp			= m_pp.pvp;
	sze->lfg			= LFG;
	sze->beardcolor		= m_pp.beardcolor;
	sze->beard			= m_pp.beard;
	sze->eyecolor1		= m_pp.eyecolor1;
	sze->eyecolor2		= m_pp.eyecolor2;
	sze->face			= m_pp.face;
	sze->haircolor		= m_pp.haircolor;
	sze->hairstyle		= m_pp.hairstyle;
	sze->linkdead		= 0;
	sze->guild_id		= guildeqid;
	sze->walk_mode		= 0;
	sze->size			= 0; // Changing size works, but then movement stops! (wth?)
	sze->runspeed		= (gmspeed == 0) ? runspeed : 3.125f;
	sze->walkspeed		= 0.46000001f;
	
	// @bp Movement settings dunno whats what but i can walk run and jump
	sze->unknown0340[0] = 3.0f;
	sze->unknown0340[1] = 2.5f;
	sze->unknown0352[0]	= 5.5f;
	sze->unknown0352[1]	= 3.125f;
	
	for (int cur_slot = 0; cur_slot <= 8 ; cur_slot++ ){
		sze->colors[cur_slot].rgb.blue = m_pp.item_tint[cur_slot].rgb.blue;
		sze->colors[cur_slot].rgb.red = m_pp.item_tint[cur_slot].rgb.red;
		sze->colors[cur_slot].rgb.green = m_pp.item_tint[cur_slot].rgb.green;
		sze->colors[cur_slot].rgb.unused=0xFF;
		sze->equip[cur_slot] = m_pp.item_material[cur_slot];
	}
	
	//for (int crap = 1; crap <= 12; crap++){
	//	sze->unknown0328[crap-1] = crap;
	//}
	
	// Send OP_ZoneEntry
	uint32 crc = CRC32::GenerateNoFlip((int8*)sze+4, sizeof(ServerZoneEntry_Struct)-4);
	memcpy(sze, &crc, 4);
	outapp->Deflate();
	QueuePacket(outapp);
	safe_delete(outapp);
	
	
	
	
	////////////////////////////////////////////////////////////
	// Zone Spawns Packet
	entity_list.SendZoneSpawnsBulk(this);
	entity_list.SendZoneCorpsesBulk(this);
	entity_list.SendTraders(this);
	
	
	
	////////////////////////////////////////////////////////////
	// Time of Day packet
	outapp = new APPLAYER(OP_TimeOfDay, sizeof(TimeOfDay_Struct));
	TimeOfDay_Struct* tod = (TimeOfDay_Struct*)outapp->pBuffer;
	zone->zone_time.getEQTimeOfDay(time(0), tod);
	QueuePacket(outapp);
	safe_delete(outapp);
	
	
	
	
	////////////////////////////////////////////////////////////
	// Character Inventory Packet
	if (loaditems) //dont load if a length error occurs
		BulkSendInventoryItems();
	
	
	//////////////////////////////////////
	// Weather Packet
	outapp = new APPLAYER(OP_Weather, 8);
	if (zone->zone_weather == 1)
		outapp->pBuffer[6] = 0x31; // Rain
	if (zone->zone_weather == 2)
	{
		outapp->pBuffer[0] = 0x01;
		outapp->pBuffer[4] = 0x02;
	}
	QueuePacket(outapp);
	safe_delete(outapp);
	SetAttackTimer();
	return true;
}

// Finish client connecting state
void Client::CompleteConnect()
{

	APPLAYER* outapp;

				outapp = new APPLAYER(OP_ZoneInSendName, sizeof(ZoneInSendName_Struct));
				ZoneInSendName_Struct* zonesendname=(ZoneInSendName_Struct*)outapp->pBuffer;
				strcpy(zonesendname->name,m_pp.name);
				strcpy(zonesendname->name2,m_pp.name);
				zonesendname->unknown0=0x0A;
				outapp->Deflate();
				QueuePacket(outapp);
				safe_delete(outapp);
				outapp = new APPLAYER(OP_ZoneInSendName2, sizeof(ZoneInSendName_Struct2));
				ZoneInSendName_Struct2* zonesendname2=(ZoneInSendName_Struct2*)outapp->pBuffer;
				strcpy(zonesendname2->name,m_pp.name);
				outapp->Deflate();
				QueuePacket(outapp);
				safe_delete(outapp);
	
	///////////////////////////////////////////////////////
	// Stamina packet
	outapp = new APPLAYER(OP_Stamina, sizeof(Stamina_Struct));
	Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer;
	sta->food = 6000;
	sta->water = 6000;
	QueuePacket(outapp);
	safe_delete(outapp);
	SendAATable();
	
	// Guild ID is in sze now, rank still using this tho
	if (guilddbid != 0) {
		outapp = new APPLAYER(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct));
		SpawnAppearance_Struct* sa = (SpawnAppearance_Struct*)outapp->pBuffer;
		sa->spawn_id = GetID();
		sa->type = 23;
		if (guilds[guildeqid].rank[guildrank].warpeace || guilds[guildeqid].leader == account_id)
			sa->parameter = 2;
		else if (guilds[guildeqid].rank[guildrank].invite || guilds[guildeqid].rank[guildrank].remove || guilds[guildeqid].rank[guildrank].motd)
			sa->parameter = 1;
		else
			sa->parameter = 0;
		QueuePacket(outapp);
		safe_delete(outapp);
	}
	
	/*for (int i=0; i<BUFF_COUNT; i++) {
		if (buffs[i].spellid != 0xFFFF) {
			SpellEffect(NULL, buffs[i].spellid, buffs[i].casterlevel,i,buffs[i].ticsremaining);
		}
	}*/
	
	// Sets GM Flag if needed & Sends Petition Queue
	UpdateAdmin(false);
	
	position_timer->Start();
	hpregen_timer->Start();
	SetDuelTarget(0);
	SetDueling(false);
		
	UpdateWho();
	database.UpdateTimersClientConnected(CharacterID());
	client_state = CLIENT_CONNECTED;
	SendManaUpdatePacket();
	for(int x=0;x<8;x++)
		SendWearChange(x);
	if(!m_inv[SLOT_CURSOR]){
		for(int ndx=0;ndx<10;ndx++){
			if(m_inv[8000+ndx]){
				if(!m_inv[SLOT_CURSOR]){//no item has been put on the cursor from the que
					m_inv.SwapItem(8000+ndx,SLOT_CURSOR);//put the next item in line onto the cursor
					const ItemInst* inst = m_inv[SLOT_CURSOR];
					if (inst)
						SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
				}
				else{//item is on cursor now
					m_inv.SwapItem(8000+ndx,8000+ndx-1);//move items ahead in the que
					const ItemInst* inst = m_inv[8000+ndx-1];
					if (inst)
						SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
				}
				DeleteItemInInventory(8000+ndx);//delete the source item
			}
		}
	}
	else{
		for(int ndx2=0;ndx2<10;ndx2++){
			const ItemInst* inst = m_inv[8000+ndx2];
			if (inst)
				SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
		}
	}
#ifdef GUILDWARS
	guildwars.EnteringMessages(this);
#endif
}


bool Client::Process() {
	adverrorinfo = 1;
	bool ret = true;
	//bool throughpacket = true;
	if (Connected())
	{
        // try to send all packets that weren't send before
        SendAllPackets();
		
		if(dead)
			SetHP(-100);
		

		if (IsStunned() && stunned_timer->Check()) {
			this->stunned = false;
			this->stunned_timer->Disable();
		}
		if(LDTimer->Check()){
			return false; //delete client
		}
		if(this->client_state == CLIENT_LINKDEAD)
			this->CastToMob()->AI_Process();
		/*if(opcodetimer->Check()){
			time_t rawtime;
			struct tm* gmt_t;
			time(&rawtime);
			gmt_t = gmtime(&rawtime);
			cout << "Opcode: " << opcode2 << " " << (gmt_t->tm_year + 1900) << "/" << setw(2) << setfill('0') << (gmt_t->tm_mon + 1) << "/" << setw(2) << setfill('0') << gmt_t->tm_mday << " " << setw(2) << setfill('0') << gmt_t->tm_hour << ":" << setw(2) << setfill('0') << gmt_t->tm_min << ":" << setw(2) << setfill('0') << gmt_t->tm_sec << " GMT\n";
            
            Client* client = entity_list.GetClientByName("Lethal");
            if(client){
                    //client->Message(0,"Trying Opcode: %i",opcode2);
					char blah2[20];
					sprintf(blah2,"%i",opcode2);
					char* blah=blah2;
					APPLAYER* app = new APPLAYER(opcode2);
					app->size = 4+strlen(blah)+1;
					app->pBuffer = new uchar[app->size];
					SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer;
					sm->msg_type = 0;
					client->Message(0,"%i (dec) sent now!",opcode2);
					strcpy(sm->message, blah);
					//if(opcode2<1000){
						app->Deflate();
						app->opcode |= FLAG_COMPRESSED;
					//}
					QueuePacket(app);
					delete app;
                    opcode2++;
            }
            else{
                    opcodetimer->Disable();
                    cout << "Stopped at: " << opcode2 << endl;
            }
            if(opcode2>=0xFFFF)
                    opcodetimer->Disable();
        }*/
		if (bardsong_timer->Check() && bardsong != 0)
		{
			if (target == 0)
				SpellFinished(bardsong, this->GetID());
			else
				SpellFinished(bardsong, target->GetID());
		}
		if (bindwound_timer->Check() && bindwound_target != 0) {
		    BindWound(bindwound_target, false);
		}
		if (auto_attack && !IsAIControlled() && !(spellend_timer->Enabled() && (spells[casting_spell_id].classes[7] < 1 && spells[casting_spell_id].classes[7] > 65)) && target != 0 && attack_timer->Check() && !IsStunned() && !IsMezzed() && dead == 0) {
			if (!CombatRange(target)) {
				//Message(0,"Target's Name: %s",target->GetName());
				//Message(0,"Target's X: %f, Your X: %f",target->CastToMob()->GetX(),GetX());
				//Message(0,"Target's Y: %f, Your Y: %f",target->CastToMob()->GetY(),GetY());
				//Message(0,"Target's Z: %f, Your Z: %f",target->CastToMob()->GetZ(),GetZ());
				SimpleMessage_StringID(13,TARGET_TOO_FAR);
				//Message(13,"Your target is too far away, get closer!");
			}
			else if (target == this) {
				SimpleMessage_StringID(13,TRY_ATTACKING_SOMEONE);
				//Message(13,"Try attacking someone else then yourself!");
			}
			/*
			else if (CantSee(target)) {
				Message(13,"You can't see your target from here.");
			}*/
			else if (!IsNPC() && appearance == 3) {
			}
			else if (target->GetHP() > -10) { // -10 so we can watch people bleed in PvP
				Attack(target, 13); 	// Kaiyodo - added attacking hand to arguments
				// Kaiyodo - support for double attack. Chance based on formula from Monkly business
				if( GetSkill(DOUBLE_ATTACK) ) {
					float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
					// Check for double attack with main hand assuming maxed DA Skill (MS)
					float random = (float)rand()/RAND_MAX;
					if (random > 0.9)
						CheckIncreaseSkill(DOUBLE_ATTACK);
					if(random < DoubleAttackProbability)		// Max 62.4 % chance of DA
						if(target && target->GetHP() > -10){
							Attack(target, 13);
							CheckIncreaseSkill(DOUBLE_ATTACK);
						}
				}
			}
		}
		if (GetClass() == WARRIOR && dead == 0 && !this->berserk && this->GetHPRatio() < 30) {
			char temp[100];
			snprintf(temp, 100, "%s goes into a berserker frenzy!", this->GetName());
			this->berserk = true;
			entity_list.MessageClose(this, 0, 200, 10, temp);
		}
				if (GetClass() == WARRIOR && this->berserk && this->GetHPRatio() > 30) {

			char temp[100];
			snprintf(temp, 100, "%s is no longer berserk.", this->GetName());
			this->berserk = false;
			entity_list.MessageClose(this, 0, 200, 10, temp);
		}
		// Kaiyodo - Check offhand attack timer
		if(auto_attack && !IsAIControlled() && CanThisClassDuelWield() && target != 0 && attack_timer_dw->Check()&& !IsStunned() && !IsMezzed() && dead == 0) {
			if(!CombatRange(target)) {
				//Message(13,"Your target is too far away, get closer! (dual)");
				SimpleMessage_StringID(13,TARGET_TOO_FAR);
			}
			// Range check
			else if(target == this) {
				//Message(13,"Try attacking someone else then yourself! (dual)");
				SimpleMessage_StringID(13,TARGET_TOO_FAR);
			}
			// Don't attack yourself
			else if (!IsNPC() && appearance == 3) {// Mezzed? Stunned?
        	}
			else if(target->GetHP() > -10) {
				float DualWieldProbability = (GetSkill(DUEL_WIELD) + GetLevel()) / 400.0f; // 78.0 max
				
				float random = (float)rand()/RAND_MAX;
				if (random > 0.9)
					CheckIncreaseSkill(DUEL_WIELD);
				if (random < DualWieldProbability) { // Max 78% of DW
					Attack(target, 14);	// Single attack with offhand
					CheckIncreaseSkill(DUEL_WIELD);
					float DoubleAttackProbability = (GetSkill(DOUBLE_ATTACK) + GetLevel()) / 500.0f; // 62.4 max
					
					// Check for double attack with off hand assuming maxed DA Skill
					random = (float)rand()/RAND_MAX;
					if(random <= DoubleAttackProbability)	// Max 62.4% chance of DW/DA
						if(target && target->GetHP() > -10)
							Attack(target, 14);
				}
			}
		}
		if (disc_timer->Check()) {
			disc_timer->Disable();
			//Message(0, "Your disciplines are available for use!");
			SimpleMessage_StringID(0,DESCIPLINE_RDY);
		}
		else if (disc_elapse->Check()) {
			disc_elapse->Disable();
			disc_inuse = 0;
			//Message(0, "You lose your concentration!");
			SimpleMessage_StringID(0,DESCIPLINE_CONLOST);
		}
		
		adverrorinfo = 2;
		if (position_timer->Check()) {
			if (IsAIControlled())
				SendPosUpdate(2);
			
			// Send a position packet every 8 seconds - if not done, other clients
			// see this char disappear after 10-12 seconds of inactivity
			if (position_timer_counter >= 36) { // Approx. 4 ticks per second
				entity_list.SendPositionUpdates(this, pLastUpdateWZ, 500, target, true);
				pLastUpdate = Timer::GetCurrentTime();
				pLastUpdateWZ = pLastUpdate;
				position_timer_counter = 0;
			}
			else {
				pLastUpdate = Timer::GetCurrentTime();
				position_timer_counter++;
			}
		}
		
		adverrorinfo = 3;
		SpellProcess();
		adverrorinfo = 4;
		if (tic_timer->Check() && dead == 0) {
			CalcMaxHP();
			CalcMaxMana();
			TicProcess();
			if(stamina_timer->Check()){
				APPLAYER* outapp = new APPLAYER(OP_Stamina, sizeof(Stamina_Struct));
				Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer;
				//m_pp.hunger_level--;
				//m_pp.thirst_level--;
				/*// add the same fatigue per tick as subtracted per jump (like 800?)
				if(m_pp.fatigue <= 9)
					m_pp.fatigue = 0;
				else
					m_pp.fatigue -= 10;*/
				//sta->food = m_pp.hunger_level;
				//sta->water = m_pp.thirst_level;
				sta->food = 6000;
				sta->water = 6000; //STA isn't working might as well max it
				//sta->fatigue = m_pp.fatigue;
				QueuePacket(outapp);
				safe_delete(outapp);
			}
			if (GetMana() < max_mana) {
				int32 level=GetLevel();
				if (IsSitting()) {
					medding = true;
					int32 newmana=0;
					int32 oldmana=0;
					if(GetSkill(MEDITATE)>0){
						oldmana=GetMana();
						newmana=oldmana+(((GetSkill(MEDITATE)/10)+(level-(level/4)))/4)+4;
						newmana+=(spellbonuses->ManaRegen+itembonuses->ManaRegen);
						SetMana(newmana);
						CheckIncreaseSkill(MEDITATE);
					}
					else
						SetMana(GetMana()+2+spellbonuses->ManaRegen+itembonuses->ManaRegen+(level/5));
				}
				else {
					medding = false;
					SetMana(GetMana()+2+spellbonuses->ManaRegen+itembonuses->ManaRegen+(level/5));
				}
			}
				APPLAYER hpupdate;
				CreateHPPacket(&hpupdate);
				entity_list.QueueClientsByTarget(this, &hpupdate,false);
					Group* group=entity_list.GetGroupByClient(this);
					if(group)
						group->SendHPPackets(this);
		}
	}
    
	
	
	if (client_state == CLIENT_KICKED) {
		eqnc->Close();
		cout << "Client disconnected (cs=k): " << GetName() << endl;
		return false;
	}
	
	if (client_state == DISCONNECTED) {
		eqnc->Close();
		cout << "Client disconnected (cs=d): " << GetName() << endl;
		return false;
	}
	
	if (client_state == CLIENT_ERROR) {
		eqnc->Close();
		cout << "Client disconnected (cs=e): " << GetName() << endl;
		return false;
	}
	
	if (client_state != CLIENT_LINKDEAD && !eqnc->CheckActive()) {
		cout << "Client linkdead: " << name << endl;
		eqnc->Close();

		if (GetGM()) {
			return false;
		}
		else {
			LDTimer->Start();
			client_state = CLIENT_LINKDEAD;
			AI_Start(CLIENT_LD_TIMEOUT);
			SendAppearancePacket(AT_LD, 1);
		}
	}
	/************ Get all packets from packet manager out queue and process them ************/
	adverrorinfo = 5;
	if((int32)eqnc==0xFEEEFEEE){
		eqnc->Close();
		safe_delete(eqnc);
		return false;
	}
	APPLAYER *app = 0;
	if((int32)eqnc!=0xfeeefeee){
		if(eqnc->GetState()>=EQNC_Closing && eqnc->CheckActive()){
			eqnc->Close();
			return false;
		}
		while(ret && (app = eqnc->PopPacket())) {
			ret = HandlePacket(app);
			safe_delete(app);
		}
	}
	AI_Process();

	return ret;
}

// Sends the client complete inventory used in character login
void Client::BulkSendInventoryItems()
{
	// Search all inventory buckets for items
	
	// Worn items and Inventory items
	sint16 slot_id = 0;
	for (slot_id=0; slot_id<=30; slot_id++) {
		const ItemInst* inst = m_inv[slot_id];
		if (inst)
			SendItemPacket(slot_id, inst, ItemPacketCharInventory);
	}
	// Bank items
	for (slot_id=2000; slot_id<=2015; slot_id++) {
		const ItemInst* inst = m_inv[slot_id];
		if (inst)
			SendItemPacket(slot_id, inst, ItemPacketCharInventory);
	}
	
	// Shared Bank items
	for (slot_id=2500; slot_id<=2501; slot_id++) {
		const ItemInst* inst = m_inv[slot_id];
		if (inst)
			SendItemPacket(slot_id, inst, ItemPacketCharInventory);
	}
	
	// LINKDEAD TRADE ITEMS
	// If player went LD during a trade, they have items in the trade inventory
	// slots.  These items are now being put into their inventory (then queue up on cursor)
	for (sint16 trade_slot_id=3000; trade_slot_id<=3007; trade_slot_id++) {
		const ItemInst* inst = m_inv[slot_id];
		if (inst) {
			sint16 free_slot_id = m_inv.FindFreeSlot(inst->IsType(ItemTypeContainer), true);
			DeleteItemInInventory(trade_slot_id, 0, false);
			PutItemInInventory(free_slot_id, *inst, true);
		}
	}
}

// Send an item packet (including all subitems of the item)
void Client::SendItemPacket(sint16 slot_id, const ItemInst* inst, ItemPacketType packet_type)
{
	if (!inst)
		return;
	
	// Serialize item into |-delimited string
	string packet = inst->Serialize(slot_id);
	
	uint16 opcode = 0;
	APPLAYER* outapp = NULL;
	ItemPacket_Struct* itempacket = NULL;
	
	// Construct packet
	opcode = (packet_type==ItemPacketViewLink) ? OP_ItemLinkResponse : OP_ItemPacket;
	outapp = new APPLAYER(opcode, packet.length()+5);
	itempacket = (ItemPacket_Struct*)outapp->pBuffer;
	memcpy(itempacket->SerializedItem, packet.c_str(), packet.length());
	itempacket->PacketType = packet_type;
	
#if EQDEBUG >= 5
		DumpPacket(outapp);
#endif
	outapp->Deflate();
	QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::RemoveData() {
	eqnc->RemoveData();
}

void Client::BulkSendMerchantInventory(int merchant_id, int16 npcid) {
	const Item_Struct* handyitem = NULL;
	int32 numItems=database.GetMerchantListNumb(merchant_id);
  int32 numItemSlots=80;  //The max number of items passed in the transaction.   // We don't have 81 slots we have 80 it's misleading (BigPull)
  int32 cpisize = sizeof(MerchantItem_Struct) + (numItemSlots * sizeof(MerchantItemD_Struct));
  MerchantItem_Struct* cpi = (MerchantItem_Struct*) new uchar[cpisize];
  memset(cpi, 0, cpisize);
  const Item_Struct *item;
  for ( int32 i=0;i<numItems && i < numItemSlots; i++) {
    //if (cpi->count >= numItemSlots + 1) { // Check if we're over the limit of items to send before we copy any data
    //  cout << "ERROR IN DATABASE: merchant("<<merchant_id<<") has more than 80 items players list will be truncated" << endl;
    //  cpi->count = 80;
    //  break; // Exit the loop and send what we have
    //}
	  int8 handychance=0;
	if(numItems>1)
		handychance=rand()%numItems;
    item=database.GetItem(database.GetMerchantData(merchant_id,i+1));
    if (item) {
		//if(!cpi->count)
		if(handychance==0)
			handyitem=item;
		else
			handychance--;
		int charges=1;
		if(item->Type==ItemTypeCommon)
			charges=item->Common.MaxCharges;
		ItemInst* inst = ItemInst::Create(item,charges);
		if (inst) {
			inst->SetPrice(item->Cost*1.27);
			inst->SetUnknown5(i+273);
			SendItemPacket(i, inst, ItemPacketMerchant);
			safe_delete(inst);
		}
    }
  }
  // And now proccess the merchants loot table as this is where player sold items end up
  /*char mki[3] = "";
  if (database.GetVariable("MerchantsKeepItems", mki, 2)) {
    if ( mki[0] == '1' ) {
      Entity* vendor = entity_list.GetMob(npcid);
      if (vendor->CastToNPC()->CountLoot() > 0) {
        int db_count = 80;
        for ( int i = 0; i < numItems && i < vendor->CastToNPC()->CountLoot(); i++ ) {
          ServerLootItem_Struct* t_item = vendor->CastToNPC()->GetItem(i);
          if (! t_item ) {
            // Empty inventory slot
            continue;
          } else {
            item = database.GetItem(t_item->item_id);
            if (item) {
              memcpy(&cpi->packets[i].item, item, sizeof(Item_Struct));
              cpi->packets[i].item.CurrentEquipSlot = db_count;
              db_count++;
            } else {
              // Shouldn't happen but if it does we'll fail nice and silent like
              continue;
            }
          }
        }
      }
    }// MerchantsKeepItems off
  } // MerchantsKeepItems configured
	
	APPLAYER* outapp = new APPLAYER(OP_ShopItem);
	outapp->size = numItems*sizeof(MerchantItemD_Struct)+4;
	outapp->pBuffer=new uchar[outapp->size];
	outapp->pBuffer=(uchar*) cpi->packets;
	QueuePacket(outapp);
    safe_delete(outapp);
	
 //  	if(numItems > 1)
	//{
	//	int randitem=rand()%numItems;
	//	if(randitem)	// not sure why, but using item 0 causes the name to be corrupt?
	//		memcpy(&handyitem,&cpi->packets[i].item,sizeof(Item_Struct));
	//	//handyitem=&cpi->packets[randitem].item;
	//}
	
	//DumpPacket(outapp);
	//printf("Merchant has %i items available.\n",cpi->count);
	*/
	Mob* merch = entity_list.GetMob(npcid);
	if(merch != NULL)
		merch->CastToNPC()->FaceTarget(this->CastToMob());
	/*if(merch != NULL && handyitem)
	{
		uchar unknown[]={0x00,0x00,0x00,0x00,0x2a,0x02,0x00,0x00,0x0A,0x00,0x00,0x00};
		char number[5]="1148";
        int packet_len;
        int pos;
        if (handyitem)
        {
		    printf("handyitem is %s\n", handyitem->Name);
        }
		else
			return;

        packet_len = (sizeof(unknown) + strlen(number) + 1);
		
        if (handyitem->Name != NULL)
        {
            packet_len += (strlen(handyitem->Name) + 1);
        }
        else
        {
            packet_len += 1;
        }
        if (this->GetName() != NULL)
        {
            packet_len += (strlen(this->GetName()) + 1);
        }
        else
        {
            packet_len += 1;
        }
		
        if (merch->GetName() != NULL)
        {
            packet_len += (strlen(merch->GetName()) + 1);
        }
        else
        {
            packet_len += 1;
        }
		APPLAYER* outapp = new APPLAYER(0x01dd, packet_len);
		
		memset(outapp->pBuffer, 0, packet_len);
        pos=0;
		
        memcpy(outapp->pBuffer + pos, unknown, 6);
		pos+=(sizeof(unknown));
		

        if (merch->GetName() != NULL)

        {
            memcpy(outapp->pBuffer + pos, merch->GetName(), strlen(merch->GetName())-2);
		    pos+=strlen(merch->GetName())-1;
        }
        else
        {
            pos+=1; //leave string as null
        }
		
		memcpy(outapp->pBuffer + pos, number, 4);
		pos+=5;
		
        if (this->GetName() != NULL)
        {
		    memcpy(outapp->pBuffer + pos, this->GetName(), strlen(this->GetName()));
		    pos+=strlen(this->GetName())+1;
        }

        else
        {
            pos+=1; //leave string as null
        }
		
        if (handyitem->Name != NULL)
        {
		    memcpy(outapp->pBuffer + pos, handyitem->Name, strlen(handyitem->Name));
		    pos+=strlen(handyitem->Name)+1;
        }
        else
        {
            pos+=1; //leave string as null
        }
		
		entity_list.QueueCloseClients(merch,outapp);*/
        //delete cpi;
		safe_delete_array(cpi);
		//safe_delete(outapp);
	//}
}

void Client::BulkSendTraderInventory(int32 char_id) {
  const Item_Struct *item;
  Trader_Struct* outints2 = database.LoadTraderItem(char_id);
  for (int8 i=0;i<80;i++) {
		if(outints2->itemid[i]==0)
			continue;
		else
			item=database.GetItem(outints2->itemid[i]);
		
		if (item && (item->NoDrop!=0)) {
			ItemInst* inst = ItemInst::Create(item);
			if (inst) {
				inst->SetPrice(outints2->itemcost[i]);
				inst->SetUnknown5(outints2->itemid[i]);
				SendItemPacket(30, inst, ItemPacketMerchant);
				safe_delete(inst);
			}
		}
	}
}

void Client::Trader_ShowItems(){
	APPLAYER* outapp= new APPLAYER(OP_Trader,sizeof(Trader_Struct));
	outapp->pBuffer= new uchar[sizeof(Trader_Struct)];
	memset(outapp->pBuffer,0,sizeof(Trader_Struct));
	Trader_Struct* outints = (Trader_Struct*)outapp->pBuffer;
	Trader_Struct* outints2 = database.LoadTraderItem(this->CharacterID());
	for(int i=0;i<160;i=i+2){
		if(i==0){
			outints->itemcost[i]=outints2->itemcost[i];
			outints->itemid[i+1]=outints2->itemid[i];
		}
		else{
			outints->itemcost[(i/2)]=outints2->itemcost[(i/2)];
			outints->itemid[i+1]=outints2->itemid[(i/2)];
		}
	}
	outints->code=11;
	QueuePacket(outapp);
	safe_delete(outapp);
	safe_delete(outints2);
}

void Client::SendTraderPacket(Client* trader){
	APPLAYER* outapp= new APPLAYER(OP_BecomeTrader,sizeof(BecomeTrader_Struct));
	BecomeTrader_Struct* bts = (BecomeTrader_Struct*)outapp->pBuffer;
	bts->code=1;
	bts->id=trader->GetID();
	outapp->Deflate();
	this->QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::Trader_StartTrader(){
	Trader=true;
	APPLAYER* outapp2= new APPLAYER(OP_Trader,sizeof(Trader_ShowItems_Struct));
	Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)outapp2->pBuffer;
	sis->code=1;
	sis->traderid=this->GetID();
	QueuePacket(outapp2);
	safe_delete(outapp2);
	APPLAYER* outapp= new APPLAYER(OP_BecomeTrader,sizeof(BecomeTrader_Struct));
	BecomeTrader_Struct* bts = (BecomeTrader_Struct*)outapp->pBuffer;
	bts->code=1;
	bts->id=this->GetID();
	entity_list.QueueCloseClients(this,outapp,false,15000);
	safe_delete(outapp);
}
void Client::Trader_EndTrader(){
	database.DeleteTraderItem(this->CharacterID());
	APPLAYER* outapp= new APPLAYER(OP_BecomeTrader,sizeof(BecomeTrader_Struct));
	BecomeTrader_Struct* bts = (BecomeTrader_Struct*)outapp->pBuffer;
	bts->code=0;
	bts->id=this->GetID();
	entity_list.QueueCloseClients(this,outapp,false,5000);
	safe_delete(outapp);
	APPLAYER* outapp2= new APPLAYER(OP_Trader,sizeof(Trader_ShowItems_Struct));
	Trader_ShowItems_Struct* sis = (Trader_ShowItems_Struct*)outapp2->pBuffer;
	sis->code=2;
	sis->traderid=0;
	QueuePacket(outapp2);
	safe_delete(outapp2);
	this->withcustomer=false;
	this->Trader=false;
}
void Client::SendTraderItem(int32 item_id,int16 quantity){
	string packet;
	sint16 freeslotid=0;
	const Item_Struct* item = database.GetItem(item_id);
	if(!item){
		printf("Bogus item deleted in Client::SendTraderItem!\n");
		return;
	}
	
	ItemInst* inst = ItemInst::Create(item, quantity);
	if (inst) {
		freeslotid = m_inv.FindFreeSlot(false, true);
		PutItemInInventory(freeslotid, *inst);
		SendItemPacket(freeslotid, inst, ItemPacketTrade);
		safe_delete(inst);
	}
}

int8 Client::WithCustomer(){
	if(this->withcustomer)
		return 0;
	else{
		this->withcustomer=true;
		return 1;
	}
}

void Client::OPRezzAnswer(const APPLAYER* app) {
	if (!pendingrezzexp)
		return;
	const Resurrect_Struct* ra = (const Resurrect_Struct*) app->pBuffer;
	if (ra->action == 1){
		cout << "Player " << this->name << " got a " << (int16)spells[ra->spellid].base[0] << "% Rezz" << endl;
		this->BuffFade(0xFFFe);
		SetMana(0);
		SetHP(GetMaxHP()/5);
		APPLAYER* outapp = app->Copy();
		outapp->opcode = OP_RezzComplete;
		worldserver.RezzPlayer(outapp,0,OP_RezzComplete);
		cout << "pe: " << pendingrezzexp << endl;
		SetEXP(((int)(GetEXP()+((float)((pendingrezzexp/100)*spells[ra->spellid].base[0])))),GetAAXP(),true);
		pendingrezzexp = 0;
		if (strcmp(ra->zone,zone->GetShortName()) != 0){
			SetZoneSummonCoords(ra->x,ra->y,ra->z);
		}
		this->FastQueuePacket(&outapp);
	}
}
int16 Client::FindTraderItemCharges(int32 item_id){
	const ItemInst* item= NULL;
	int16 slotid=0;
	for(int i=0;i<8;i++){
		item=this->GetInv().GetItem(22+i);
		if(item && item->GetItem()->ItemNumber==17899){ //Traders Satchel
			for(int x=0;x<10;x++){
				slotid=(((22+i+3)*10)+x+1);
				item=this->GetInv().GetItem(slotid);
				if(item && item->GetItem()->ItemNumber==item_id)
					return item->GetCharges();
			}
		}
	}
	return 9999;
}
GetItems_Struct* Client::GetTraderItems(){
	const ItemInst* item= NULL;
	int16 charges=0;
	int16 slotid=0;
	GetItems_Struct* gis= new GetItems_Struct;
	memset(gis,0,sizeof(GetItems_Struct));
	int8 ndx=0;
	for(int i=0;i<8;i++){
		item=this->GetInv().GetItem(22+i);
		if(item && item->GetItem()->ItemNumber==17899){ //Traders Satchel
			for(int x=0;x<10;x++){
				slotid=(((22+i+3)*10)+x+1);
				item=this->GetInv().GetItem(slotid);
				if(item){
					gis->items[ndx]=item->GetItem()->ItemNumber;
					ndx++;
				}
			}
		}
	}
	return gis;
}
int16 Client::FindTraderItem(int32 item_id,int16 quantity){
	const ItemInst* item= NULL;
	int16 charges=0;
	int16 slotid=0;
	for(int i=0;i<8;i++){
		item=this->GetInv().GetItem(22+i);
		if(item && item->GetItem()->ItemNumber==17899){ //Traders Satchel
			for(int x=0;x<10;x++){
				slotid=(((22+i+3)*10)+x+1);
				item=this->GetInv().GetItem(slotid);
				if(item && item->GetItem()->ItemNumber==item_id && (item->GetCharges()>=quantity || (item->GetCharges()==0 && quantity==1))){
					return slotid;
				}
			}
		}
	}
	printf("Could NOT find a match for Item: %i with a quantity of: %i on Trader: %s\n",item_id,quantity,this->GetName());
	return 0;
}
void Client::NukeTraderItem(int16 slot,int16 charges,int16 quantity,Client* customer,int16 traderslot){
	APPLAYER* outapp = new APPLAYER(OP_TraderDelItem,sizeof(TraderDelItem_Struct));
	TraderDelItem_Struct* tdis = (TraderDelItem_Struct*)outapp->pBuffer;
	tdis->quantity=0xFFFFFFFF;
	tdis->unknown=0xFFFFFFFF;
	tdis->slotid=slot;
	if(quantity<=20 && charges>quantity){
		for(int y=0;y<quantity;y++)
			this->QueuePacket(outapp);
	}
	else{
		APPLAYER* outapp2 = new APPLAYER(OP_PlaceItem,sizeof(MoveItem_Struct));
		MoveItem_Struct* mis=(MoveItem_Struct*)outapp2->pBuffer;
		mis->from_slot=slot;
		mis->to_slot=0xFFFFFFFF;
		mis->number_in_stack=0;
		this->QueuePacket(outapp2);
		safe_delete(outapp2);
		customer->TraderUpdate(traderslot,this->GetID());
	}
	safe_delete(outapp);
}
void Client::TraderUpdate(int16 slot_id,int32 trader_id){
	APPLAYER* outapp = new APPLAYER(OP_TraderItemUpdate,sizeof(TraderItemUpdate_Struct));
	TraderItemUpdate_Struct* tus=(TraderItemUpdate_Struct*)outapp->pBuffer;
	tus->charges=0xFFFF;
	tus->fromslot=slot_id;
	tus->toslot=0xFF;
	tus->traderid=trader_id;
	tus->unknown0=0;
	QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::FindAndNukeTraderItem(int32 item_id,int16 quantity,Client* customer,int16 traderslot){
	const ItemInst* item= NULL;
	int16 charges=0;
	int16 slotid=FindTraderItem(item_id,quantity);
	if(slotid>0){
		item=this->GetInv().GetItem(slotid);
		if(item)
			charges=this->GetInv().GetItem(slotid)->GetCharges();
		if(item && item->GetItem()->ItemNumber==item_id && (charges>=quantity || (charges==0 && quantity==1))){
			this->DeleteItemInInventory(slotid,quantity);
			Trader_Struct* getslot = database.LoadTraderItem(this->CharacterID());
			int8 count=0;
			bool testslot=true;
			for(int y=0;y<80;y++){
				if(testslot && getslot->itemid[y]==item_id){
					database.DeleteTraderItem(this->CharacterID(),y);
					testslot=false;
				}
				else if(getslot->itemid[y]>0)
					count++;
			}
			if(count==0)
				Trader_EndTrader();
			NukeTraderItem(slotid,charges,quantity,customer,traderslot);
			return;
		}
	}
	printf("Could NOT find a match for Item: %i with a quantity of: %i on Trader: %s\n",item_id,quantity,this->GetName());
}
void Client::ReturnTraderReq(const APPLAYER* app,int16 traderitemcharges){
	TraderBuy_Struct* tbs=(TraderBuy_Struct*)app->pBuffer;
	APPLAYER* outapp = new APPLAYER(OP_TraderBuy,sizeof(TraderBuy_Struct));
	TraderBuy_Struct* outtbs  = (TraderBuy_Struct*)outapp->pBuffer;
	memcpy(outtbs,tbs,app->size);
	outtbs->price=(tbs->price*traderitemcharges);
	outtbs->quantity=traderitemcharges;
	outtbs->traderid=this->GetID();
	this->QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::BuyTraderItem(TraderBuy_Struct* tbs,Client* trader,const APPLAYER* app){
	APPLAYER* outapp = new APPLAYER(OP_Trader,sizeof(TraderBuy_Struct));
	TraderBuy_Struct* outtbs  = (TraderBuy_Struct*)outapp->pBuffer;
	outtbs->itemid=tbs->itemid;
	outtbs->price=tbs->price;
	int16 traderitemcharges=trader->FindTraderItemCharges(tbs->itemid);
	if(traderitemcharges<=0)
		traderitemcharges=1;
	else if(traderitemcharges==9999){
		Message(15,"Item not found!");
		return;
	}
	const Item_Struct* item2=database.GetItem(tbs->itemid);
	if(!item2)
		return;
	if(traderitemcharges<tbs->quantity)
		outtbs->quantity=traderitemcharges;
	else
		outtbs->quantity=tbs->quantity;
	ReturnTraderReq(app,outtbs->quantity);
	outtbs->traderid=this->GetID();
	outtbs->slot_num=tbs->slot_num;
	outtbs->unknown0=0x0A;
	strncpy(outtbs->itemname-4,item2->Name,64);
	int traderslot=0;
	SendTraderItem(outtbs->itemid,outtbs->quantity);

	APPLAYER* outapp2 = new APPLAYER(OP_MoneyUpdate,sizeof(MoneyUpdate_Struct));
	MoneyUpdate_Struct* mus= (MoneyUpdate_Struct*)outapp2->pBuffer;
	int32 itemcost=tbs->price;
	this->TakeMoneyFromPP(tbs->price);
	mus->platinum=(int)itemcost/1000;
	itemcost-=(mus->platinum*1000);
	mus->gold=(int)itemcost/100;
	itemcost-=(mus->gold*100);
	mus->silver=(int)itemcost/10;
	itemcost-=(mus->silver*10);
	mus->copper=itemcost;
	//trader->AddMoneyToPP(tbs->price,true);
	trader->AddMoneyToPP(mus->copper,mus->silver,mus->gold,mus->platinum,false);
	mus->platinum=trader->GetPP().platinum;
	mus->gold=trader->GetPP().gold;
	mus->silver=trader->GetPP().silver;
	mus->copper=trader->GetPP().copper;
	traderslot=trader->FindTraderItem(tbs->itemid,outtbs->quantity);
	trader->QueuePacket(outapp2);
	trader->FindAndNukeTraderItem(tbs->itemid,outtbs->quantity,this,tbs->slot_num);
	trader->QueuePacket(outapp);
	safe_delete(outapp);
	//safe_delete(outapp2);
}
void Client::SendBazaarWelcome(){
	char errbuf[MYSQL_ERRMSG_SIZE];
    char* query = 0;
	MYSQL_RES *result;
	MYSQL_ROW row;
	if (database.RunQuery(query,MakeAnyLenString(&query, "select count(distinct char_id),count(char_id) from trader"),errbuf,&result)){
		if(mysql_num_rows(result)==1){
			row = mysql_fetch_row(result);
			APPLAYER* outapp = new APPLAYER(OP_Bazaar,sizeof(BazaarWelcome_Struct));
			memset(outapp->pBuffer,0,outapp->size);
			BazaarWelcome_Struct* bws = (BazaarWelcome_Struct*)outapp->pBuffer;
			bws->beginning.action=9;
			bws->items=atoi(row[1]);
			bws->traders=atoi(row[0]);
			this->QueuePacket(outapp);
			safe_delete(outapp);
		}
		mysql_free_result(result);
	}
	
	safe_delete_array(query);
}
void Client::SendBazaarResults(int32 trader_id,int32 class_,int32 race,int32 stat,int32 slot,int32 type,char name[64],int32 minprice,int32 maxprice){
	char errbuf[MYSQL_ERRMSG_SIZE];
    char* query = 0;
	string search,values;
	MYSQL_RES *result;
	MYSQL_ROW row;
	char tmp[100]={0};
	values.append("count(item_id),trader.*,items.name");
	search.append("where trader.item_id=items.id");
	if(trader_id>0){
		Client* trader=entity_list.GetClientByID(trader_id);
		if(trader){
			sprintf(tmp," and trader.char_id=%i",trader->CharacterID());
			search.append(tmp);
		}
			
	}
	string searchresults;
	if(minprice!=0){
		sprintf(tmp," and trader.item_cost>=%i",minprice);
		search.append(tmp);
	}
	if(maxprice!=0){
		sprintf(tmp," and trader.item_cost<=%i",maxprice);
		search.append(tmp);
	}
	if(strlen(name)>0){
		sprintf(tmp," and items.name like '%%%s%%'",name);
		search.append(tmp);
	}
	if(class_!=0xFFFFFFFF){
			sprintf(tmp," and mid(reverse(bin(items.classes)),%i,1)=1",class_);
			search.append(tmp);
	}
	if(race!=0xFFFFFFFF){
			sprintf(tmp," and mid(reverse(bin(items.races)),%i,1)=1",race);
			search.append(tmp);
	}
	if(slot!=0xFFFFFFFF){
			sprintf(tmp," and mid(reverse(bin(items.slots)),%i,1)=1",slot+1);
			search.append(tmp);
	}
	if(type!=0xFFFFFFFF){
		switch(type){
			case 31:
				sprintf(tmp," and items.itemclass=2");
				search.append(tmp);
				break;
			case 46:
				sprintf(tmp," and items.spellid>0 and items.spellid<65000");
				search.append(tmp);
				break;
			case 47:
				sprintf(tmp," and items.spellid=998");
				search.append(tmp);
				break;
			case 48:
				sprintf(tmp," and items.spellid>=1298 and items.spellid<=1307");
				search.append(tmp);
				break;
			case 49:
				sprintf(tmp," and items.focusid>0");
				search.append(tmp);
				break;
			default:
				sprintf(tmp," and items.itemtype=%i",type);
				search.append(tmp);
		}
	}
	if(stat!=0xFFFFFFFF){
		if(stat==14){
			search.append(" and items.ac>0");
			values.append(",items.ac");
		}
		else if(stat==2){
			search.append(" and items.aagi>0");
			values.append(",items.aagi");
		}
		else if(stat==6){
			search.append(" and items.acha>0");
			values.append(",items.acha");
		}
		else if(stat==3){
			search.append(" and items.adex>0");
			values.append(",items.adex");
		}
		else if(stat==4){
			search.append(" and items.aint>0");
			values.append(",items.aint");
		}
		else if(stat==1){
			search.append(" and items.asta>0");
			values.append(",items.asta");
		}
		else if(stat==0){
			search.append(" and items.astr>0");
			values.append(",items.astr");
		}
		else if(stat==5){
			search.append(" and items.awis>0");
			values.append(",items.awis");
		}
		else if(stat==8){
			search.append(" and items.cr>0");
			values.append(",items.cr");
		}
		else if(stat==11){
			search.append(" and items.dr>0");
			values.append(",items.dr");
		}
		else if(stat==9){
			search.append(" and items.fr>0");
			values.append(",items.fr");
		}
		else if(stat==7){
			values.append(",items.mr");
			search.append(" and items.mr>0");
		}
		else if(stat==10){
			search.append(" and items.pr>0");
			values.append(",items.pr");
		}
		else if(stat==13){
			search.append(" and items.hp>0");
			values.append(",items.hp");
		}
		else if(stat==12){
			search.append(" and items.mana>0");
			values.append(",items.mana");
		}
	}
	if (database.RunQuery(query,MakeAnyLenString(&query, "select %s from trader,items %s group by items.id limit 50",values.c_str(),search.c_str()),errbuf,&result)){
		safe_delete_array(query);
		int i=0;
		int size=0;
		int32 id=0;
		BazaarSearchResults_Struct* brs= new BazaarSearchResults_Struct;
		if(mysql_num_rows(result)==0){
			APPLAYER* outapp2 = new APPLAYER(OP_Bazaar,sizeof(BazaarReturnDone_Struct));
			BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer;
			brds->traderid=id;
			brds->type=0x0C;
			brds->unknown8=0xFFFFFFFF;
			brds->unknown12=0xFFFFFFFF;
			brds->unknown16=0xFFFFFFFF;
			this->QueuePacket(outapp2);
			safe_delete(outapp2);
			mysql_free_result(result);
			return;
		}
		size=mysql_num_rows(result)*sizeof(BazaarSearchResults_Struct);
		//char *buffer=(char*)malloc(size);
		//char *bufptr=buffer;
		uchar *buffer=new uchar[size];
		uchar *bufptr=buffer;
		memset(buffer,0,size);
		int action=7;
		int32 cost=0;
		int32 item_id=0;
		char name[64]={0};
		int count=0;
		int32 statvalue=0;
		while ((row = mysql_fetch_row(result))) {
			memcpy(bufptr,&action, sizeof(int32));
			bufptr+=sizeof(int32);
			count=atoi(row[0]);
			memcpy(bufptr,&count, sizeof(int32));
			bufptr+=sizeof(int32);
			item_id=atoi(row[2]);
			memcpy(bufptr,&item_id, sizeof(int32));
			bufptr+=sizeof(int32);
			Client* trader2=entity_list.GetClientByCharID(atoi(row[1]));
			if(trader2){
				id=trader2->GetID();
				memcpy(bufptr,&id, sizeof(int32));
				bufptr+=sizeof(int32);
			}
			else{
				printf("Unable to find trader: %i\n",atoi(row[1]));
				memcpy(bufptr,&id, sizeof(int32));
				bufptr+=sizeof(int32);
			}
			cost=atoi(row[3]);
			memcpy(bufptr,&cost, sizeof(int32));
			bufptr+=sizeof(int32);
			statvalue=atoi(row[6]);
			memcpy(bufptr,&statvalue, sizeof(int32));
			bufptr+=sizeof(int32);
			sprintf(name,"%s(%i)",row[5],count);
			memcpy(bufptr,&name, strlen(name));
			bufptr+=64;
		}
		mysql_free_result(result);
		APPLAYER* outapp = new APPLAYER(OP_Bazaar,size);
		outapp->pBuffer=new uchar[outapp->size];
		memcpy(outapp->pBuffer,buffer,size);
		this->QueuePacket(outapp);
		safe_delete(outapp);
		//free(buffer);
		safe_delete_array(buffer);
		APPLAYER* outapp2 = new APPLAYER(OP_Bazaar,sizeof(BazaarReturnDone_Struct));
		BazaarReturnDone_Struct* brds = (BazaarReturnDone_Struct*)outapp2->pBuffer;
		brds->traderid=id;
		brds->type=0x0C;
		brds->unknown8=0xFFFFFFFF;
		brds->unknown12=0xFFFFFFFF;
		brds->unknown16=0xFFFFFFFF;
		this->QueuePacket(outapp2);
		safe_delete(outapp2);
		
	}
	else{
		printf("Failed to retrieve Bazaar Search!! %s\n",query);
		safe_delete_array(query);
		return;
	}
}
void Client::OPMemorizeSpell(const APPLAYER* app) {
	if(app->size != sizeof(MemorizeSpell_Struct))
		return;
	
	const MemorizeSpell_Struct* memspell = (const MemorizeSpell_Struct*) app->pBuffer;
	
	if (memspell->spell_id > SPDAT_RECORDS) {
		Message(13, "Unexpected error: spell id out of range");
		return;
	}
	if(GetClass()>0 && GetClass()<16 && (GetLevel() >= spells[memspell->spell_id].classes[GetClass()-1]))
			QueuePacket(app);
		else{
			char val1[20]={0};
			Message_StringID(13,SPELL_LEVEL_TO_LOW,ConvertArray(spells[memspell->spell_id].classes[GetClass()-1],val1),spells[memspell->spell_id].name);
			//Message(13, "Unexpected error: Class cant use this spell at your level!");
			return;
		}

	if (memspell->scribing == 0) {
		m_pp.spell_book[memspell->slot] = memspell->spell_id;
		
		// Destroy scroll on cursor
		APPLAYER* outapp = new APPLAYER(OP_MoveItem, sizeof(MoveItem_Struct));
		MoveItem_Struct* spellmoveitem = (MoveItem_Struct*) outapp->pBuffer;
		spellmoveitem->from_slot = SLOT_CURSOR;
		spellmoveitem->to_slot = SLOT_INVALID;
		spellmoveitem->number_in_stack = 0;
		QueuePacket(outapp);
		safe_delete(outapp);
		DeleteItemInInventory(SLOT_CURSOR);
	}
	else if (memspell->scribing == 1) {
		APPLAYER* outapp = new APPLAYER(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct));
		MemorizeSpell_Struct* mem = (MemorizeSpell_Struct*)outapp->pBuffer;
		mem->spell_id = memspell->spell_id;
		mem->slot = memspell->slot;
		mem->scribing = memspell->scribing;
		QueuePacket(outapp);
		safe_delete(outapp);
		m_pp.mem_spells[memspell->slot] = memspell->spell_id;
	}
	else if (memspell->scribing == 2) {
		m_pp.mem_spells[memspell->slot] = 0xffff;
	}
	Save();
}
