/*  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
*/
#include "../common/debug.h"
#include <iostream>
using namespace std;
#include <iomanip>
using namespace std;
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <math.h>

// Disgrace: for windows compile
#ifdef WIN32
#include <windows.h>
#include <winsock.h>
#include <process.h>

#define snprintf	_snprintf
#define vsnprintf	_vsnprintf
#define strncasecmp	_strnicmp
#define strcasecmp  _stricmp
#else
#include <stdarg.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include "../common/unix.h"
#endif

extern volatile bool RunLoops;
extern bool spells_loaded;

#include "../common/version.h"
#include "masterentity.h"
#include "worldserver.h"
#include "net.h"
#include "skills.h"
#include "../common/database.h"
#include "spdat.h"
#include "../common/packet_dump.h"
#include "../common/packet_functions.h"
#include "petitions.h"
#include "../common/serverinfo.h"
#include "../common/ZoneNumbers.h"
#include "../common/moremath.h"
#include "../common/guilds.h"
#include "command.h"
#include "StringIDs.h"

extern Database database;
extern EntityList entity_list;
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 int32 numclients;
extern PetitionList petition_list;
bool commandlogged;
char entirecommand[255];

#ifdef GUILDWARS
#include "GuildWars.h"
extern GuildWars guildwars;
#endif
#define ITEM_MAX_STACK 20
Client::Client(EQNetworkConnection* ieqnc)
: Mob("No name",	// name
	"",	// lastname
	0,	// cur_hp
	0,	// max_hp
	0,	// gender
	0,	// race
	0,	// class
	0,	// bodytype
	0,	// deity
	0,	// level
	0,	// npctypeid
	0,	// skills
	0,	// size
	0.46,	// walkspeed
	0.7,	// runspeed
	0,	// heading
	0,	// x
	0,	// y
	0,	// z
	0,	// light
	0,	// equip
	0xFF,	// texture
	0xFF,	// helmtexture
	0,	// ac
	0,	// atk
	0,	// str
	0,	// sta
	0,	// dex
	0,	// agi
	0,	// int
	0,	// wis
	0,	// cha
	0xff,	// Luclin Hair Colour
	0xff,	// Luclin Beard
	0xff,	// Luclin Eye1
	0xff,	// Luclin Eye2
	0xff,	// Luclin Hair Style
	0xff,	// Luclin Face
	0xff,	// AA Title
	1, // fixedz
	0, // standart text1
	0, // standart text2
	0,	// see_invis
	0	// see_invis_undead
	)
{
	for(int cf=0;cf<21;cf++)
		ClientFilters[cf]=0;
	character_id = 0;
	feigned = false;
	this->berserk = false;
	this->dead = false;
	eqnc = ieqnc;
	ip = eqnc->GetrIP();
	port = ntohs(eqnc->GetrPort());
	client_state = CLIENT_CONNECTING;
	Trader=false;
	withcustomer=false;
	IsTracking=false;
	WID = 0;
	account_id = 0;
	admin = 0;
	lsaccountid = 0;
	guilddbid = 0;
	guildeqid = GUILD_NONE;
	guildrank = 0;
	memset(lskey, 0, sizeof(lskey));
	strcpy(account_name, "");
	tellsoff = false;
	gmhideme = false;
	AFK = false;
	LFG = false;
	cheater = false;
	cheatcount =0;
	cheat_x=0;
	cheat_y=0;
	playeraction = 0;
	target = 0;
	auto_attack = false;
	PendingGuildInvite = 0;
	LDTimer = new Timer(30000);
	LDTimer->Disable();
	zonesummon_x = -2;
	zonesummon_y = -2;
	zonesummon_z = -2;
	zonesummon_ignorerestrictions = 0;
	casting_spell_id = 0;
	npcflag = false;
	npclevel = 0;
	pQueuedSaveWorkID = 0;
	stamina_timer = new Timer(46000);
	position_timer = new Timer(250);
	position_timer->Disable();
	hpregen_timer = new Timer(1800);
	position_timer_counter = 0;
	camp_timer = new Timer(29000);
	ooc_timer = new Timer(1000);
	process_timer = new Timer(100);
	camp_timer->Disable();
	pLastUpdate = 0;
	pLastUpdateWZ = 0;
	// Kaiyodo - initialise haste variable
	HastePercentage = 0;
	m_tradeskill_object = NULL;
	IsSettingGuildDoor = false;
	delaytimer = false;
	pendingrezzexp = 0;
	numclients++;
	// emuerror;
	UpdateWindowTitle();
	hasmount = false;
	horseId = 0;
	tgb = false;
	AbilityTimer=false;
	memset(zonesummon_name, 0, sizeof(zonesummon_name));
	
	disc_timer = new Timer(60000);
	disc_timer->Disable();
	disc_elapse = new Timer(60000);
	disc_elapse->Disable();
	disc_inuse=0;
}

Client::~Client() {
	Mob* horse = entity_list.GetMob(this->CastToClient()->GetHorseId());
	if (horse)
		horse->Depop();
	if(Trader)
		database.DeleteTraderItem(this->CharacterID());
	Object* object=GetTradeskillObject();
	if(object)
		object->Close();

	if(AbilityTimer || GetLevel()>=51)
		database.UpdateAndDeleteAATimers(CharacterID());

	if(IsDueling() && GetDuelTarget() != 0) {
		Entity* entity = entity_list.GetID(GetDuelTarget());
		if(entity != NULL && entity->IsClient()) {
			entity->CastToClient()->SetDueling(false);
			entity->CastToClient()->SetDuelTarget(0);
		}
	}
	
	if (this->isgrouped && entity_list.GetGroupByClient(this) != NULL)
		entity_list.GetGroupByClient(this)->Remove(this->CastToMob());
	//	entity_list.GetGroupByClient(this)->DelMember(this->CastToMob(),true);
	
	eqnc->Free();
	UpdateWho(2);
	Save(1); // This fails when database destructor is called first on shutdown	
	safe_delete(position_timer);
	safe_delete(hpregen_timer);
	safe_delete(camp_timer);
	safe_delete(process_timer);
	safe_delete(disc_timer);
	safe_delete(disc_elapse);
	safe_delete(stamina_timer);
	safe_delete(LDTimer);
	safe_delete(ooc_timer);
	numclients--;
	UpdateWindowTitle();
#ifdef GUILDWARS
	guildwars.SetCurrentUsers(numclients);
#endif
	zone->RemoveAuth(GetName());
}

// Return max stat value for level
sint16 Client::GetMaxStat() {
	int level = GetLevel();
	
	if (level < 61) {
		return 255;
	}
	else if (level < 66) {
		return 255 + 5 * (level - 60);
	}
	else {
		return 280;
	}	
}

sint16 Client::GetMaxSTR() {
	return GetMaxStat();
}
sint16 Client::GetMaxSTA() {
	return GetMaxStat();
}
sint16 Client::GetMaxDEX() {
	return GetMaxStat();
}
sint16 Client::GetMaxAGI() {
	return GetMaxStat();
}
sint16 Client::GetMaxINT() {
	return GetMaxStat();
}
sint16 Client::GetMaxWIS() {
	return GetMaxStat();
}
sint16 Client::GetMaxCHA() {
	return GetMaxStat();
}

bool Client::GetIncreaseSpellDurationItem(int16 &spell_id, char *itemname)
{
	for (int i=0; i<22; i++) {
		const ItemInst* inst = m_inv[i];
		if (!inst || !inst->IsType(ItemTypeCommon))
			continue;
	    
		const Item_Struct* item = inst->GetItem();
		if (item->Common.FocusId && (item->Common.FocusId != 0xFFFF)) {
			if (IsIncreaseDurationSpell(item->Common.FocusId)) {
				spell_id = item->Common.FocusId;
				if (itemname)
					strcpy(itemname, item->Name);
				return true;
			}
		}
	}
	return false;
}

bool Client::GetReduceManaCostItem(int16 &spell_id, char *itemname)
{
	for (int i=0; i<22; i++) {
		const ItemInst* inst = m_inv[i];
		if (!inst || !inst->IsType(ItemTypeCommon))
			continue;
	    
		const Item_Struct* item = inst->GetItem();
		if (item->Common.FocusId && (item->Common.FocusId != 0xFFFF)) {
			if (IsReduceManaSpell(item->Common.FocusId)) {
				spell_id = item->Common.FocusId;
				if (itemname)
					strcpy(itemname, item->Name);
				return true;
			}
		}
	}
	return false;
}

bool Client::GetReduceCastTimeItem(int16 &spell_id, char *itemname)
{
	for (int i=0; i<22; i++) {
		const ItemInst* inst = m_inv[i];
		if (!inst || !inst->IsType(ItemTypeCommon))
			continue;
	    
		const Item_Struct* item = inst->GetItem();
		if (item->Common.FocusId && (item->Common.FocusId != 0xFFFF)) {
			if (IsReduceCastTimeSpell(item->Common.FocusId)) {
				spell_id = item->Common.FocusId;
				if (itemname)
					strcpy(itemname, item->Name);
				return true;
			}
		}
	}
	return false;
}

bool Client::GetExtendedRangeItem(int16 &spell_id, char *itemname)
{
	for (int i=0; i<22; i++) {
		const ItemInst* inst = m_inv[i];
		if (!inst || !inst->IsType(ItemTypeCommon))
			continue;
	    
		const Item_Struct* item = inst->GetItem();
		if (item->Common.FocusId && (item->Common.FocusId != 0xFFFF)) {
			if (IsExtRangeSpell(item->Common.FocusId)) {
				spell_id = item->Common.FocusId;
				if (itemname)
					strcpy(itemname, item->Name);
				return true;
			}
		}
	}
	return false;
}

bool Client::GetImprovedHealingItem(int16 &spell_id, char *itemname)
{
	for (int i=0; i<22; i++) {
		const ItemInst* inst = m_inv[i];
		if (!inst || !inst->IsType(ItemTypeCommon))
			continue;
	    
		const Item_Struct* item = inst->GetItem();
		if (item->Common.FocusId && (item->Common.FocusId != 0xFFFF)) {
			if (IsImprovedHealingSpell(item->Common.FocusId)) {
				spell_id = item->Common.FocusId;
				if (itemname)
					strcpy(itemname, item->Name);
				return true;
			}
		}
	}
	return false;
}

bool Client::GetImprovedDamageItem(int16 &spell_id, char *itemname)
{
	for (int i=0; i<22; i++) {
		const ItemInst* inst = m_inv[i];
		if (!inst || !inst->IsType(ItemTypeCommon))
			continue;
	    
		const Item_Struct* item = inst->GetItem();
		if (item->Common.FocusId && (item->Common.FocusId != 0xFFFF)) {
			if (IsImprovedDamageSpell(item->Common.FocusId)) {
				spell_id = item->Common.FocusId;
				if (itemname)
					strcpy(itemname, item->Name);
				return true;
			}
		}
	}
	return false;
}

sint32 Client::GenericFocus(int16 spell_id, int16 modspellid)
{
    int32 modifier = 100;
	
    for (int i=0; i<EFFECT_COUNT; i++) {
		switch( spells[modspellid].effectid[i] ) {
		case SE_LimitMaxLevel:
			if (spells[spell_id].classes[(GetClass()%16) - 1] > spells[modspellid].base[i])
				return 100;
			break;
		case SE_LimitMinLevel:
			if (spells[spell_id].classes[(GetClass()%16) - 1] < spells[modspellid].base[i])
				return 100;
			break;
		case SE_IncreaseRange:
			modifier += spells[modspellid].base[i];
			break;
		case SE_IncreaseSpellHaste:
			modifier -= spells[modspellid].base[i];
			break;
		case SE_IncreaseSpellDuration:
			modifier += spells[modspellid].base[i];
			break;
		case SE_LimitSpell:
			// negative sign means exclude
			// positive sign means include
			if (spells[modspellid].base[i] < 0) {
				if (spells[modspellid].base[i] * (-1) == spell_id)
					return 100;
			}
			else {
				if (spells[modspellid].base[i] != spell_id)
					return 100;
			}
			break;
		case SE_LimitEffect:
			switch( spells[modspellid].base[i] )
			{
			case -147:
				if (IsPercentalHeal(spell_id))
					return 100;
				break;
			case -101:
				if (IsCHDuration(spell_id))
					return 100;
				break;
			case -40:
				if (IsInvulnerability(spell_id))
					return 100;
				break;
			case -32:
				if (IsSummonItem(spell_id))
					return 100;
				break;
			case 0:
				if (!IsEffectHitpoints(spell_id))
					return 100;
				break;
			case 33:
				if (!IsSummonPet(spell_id))
					return 100;
				break;
			case 36:
				if (!IsPoisonCounter(spell_id))
					return 100;
				break;
			case 71:
				if (!IsSummonSkeleton(spell_id))
					return 100;
				break;
			default:
				LogFile->write(EQEMuLog::Normal, "unknown limit effect %d", spells[modspellid].base[i]);
			}
			break;
		case SE_LimitCastTime:
			if (spells[modspellid].base[i] < (sint16)spells[spell_id].cast_time)
				return 100;
			break;
		case SE_LimitSpellType:
			switch( spells[modspellid].base[i] )
			{
			case 0:
				if (!IsDetrimental(spell_id))
					return 100;
				break;
			case 1:
				if (!IsBeneficial(spell_id))
					return 100;
				break;
			default:
				LogFile->write(EQEMuLog::Normal, "unknown limit spelltype %d", spells[modspellid].base[i]);
			}
			break;
		case SE_LimitMinDur:
			if (spells[modspellid].base[i] > CalcBuffDuration(GetLevel(), spells[spell_id].buffdurationformula, spells[spell_id].buffduration))
				return 100;
			break;
		case SE_ImprovedDamage:
		case SE_ImprovedHeal:
			modifier += spells[modspellid].base[i];
			break;
		case SE_ReduceManaCost:
			modifier -= spells[modspellid].base[i];
			break;
		default:
			LogFile->write(EQEMuLog::Normal, "unknown effectid %d", spells[modspellid].effectid[i]);
			break;
        }
    }

    return modifier;
}

sint32 Client::GetActSpellRange(int16 spell_id, sint32 range)
{
	int16 modspellid = 0;

	int extrange = 100;
	if (GetExtendedRangeItem(modspellid, NULL)) {
		extrange = GenericFocus(spell_id, modspellid);
	}
	return (range * extrange) / 100;
}

sint32 Client::GetActSpellValue(int16 spell_id, sint32 value)
{
	int16 modspellid = 0;

	int modifier = 100;

    if (spells[spell_id].goodEffect == 0) {
		if (GetImprovedDamageItem(modspellid, NULL))
			modifier = GenericFocus(spell_id, modspellid);
	}
	else {
		if (GetImprovedHealingItem(modspellid, NULL))
			modifier = GenericFocus(spell_id, modspellid);
	}
	return (value * modifier) / 100;
}

sint32 Client::GetActSpellCost(int16 spell_id, sint32 cost)
{
	int16 modspellid = 0;

	int reduce = 100;
	if (GetReduceManaCostItem(modspellid, NULL)) {
		reduce = GenericFocus(spell_id, modspellid);
	}
	return (cost * reduce) / 100;
}

sint32 Client::GetActSpellDuration(int16 spell_id, sint32 duration)
{
	int16 modspellid = 0;

	int increase = 100;
	if (GetIncreaseSpellDurationItem(modspellid, NULL)) {
		increase = GenericFocus(spell_id, modspellid);
	}
	return (duration * increase) / 100;
}

sint32 Client::GetActSpellCasttime(int16 spell_id, sint32 casttime)
{
	int16 modspellid = 0;

	int reduce = 100;
	if (GetReduceCastTimeItem(modspellid, NULL)) {
		reduce = GenericFocus(spell_id, modspellid);
	}
	return (casttime * reduce) / 100;
}

bool Client::Save(int8 iCommitNow) {
	if (Connected()) {
#if 0
// Orig. Offset: 344 / 0x00000000
//       Length: 36 / 0x00000024
   unsigned char rawData[36] =
{
    0x0D, 0x30, 0xE1, 0x30, 0x1E, 0x10, 0x22, 0x10, 0x20, 0x10, 0x21, 0x10, 0x1C, 0x20, 0x1F, 0x10, 
    0x7C, 0x10, 0x68, 0x10, 0x51, 0x10, 0x78, 0x10, 0xBD, 0x10, 0xD2, 0x10, 0xCD, 0x10, 0xD1, 0x10, 
    0x01, 0x10, 0x6D, 0x10
} ;
	for (int tmp = 0;tmp <=35;tmp++){
		m_pp.unknown0256[89+tmp] = rawData[tmp];
	}
#endif
		m_pp.x = x_pos;
		m_pp.y = y_pos;
		m_pp.z = z_pos;
		m_pp.heading = heading;
		if (GetHP() <= 0) {
			if (GetMaxHP() > 30000)
				m_pp.cur_hp = 30000;

			else
				m_pp.cur_hp = GetMaxHP();
		}
		else if (GetHP() > 30000)
			m_pp.cur_hp = 30000;
		else
			m_pp.cur_hp = GetHP();
		m_pp.mana = cur_mana;
		
		for (int i=0; i<15; i++) {
			if (buffs[i].spellid <= SPDAT_RECORDS) {
				m_pp.buffs[i].spellid = buffs[i].spellid;
// solar: fix this if buffs struct is fixed
				m_pp.buffs[i].slotid = 2;
				m_pp.buffs[i].duration = buffs[i].ticsremaining;
				m_pp.buffs[i].level = buffs[i].casterlevel;
				m_pp.buffs[i].effect = 10;
				//buffs[i].effect;
			}
			else {
				m_pp.buffs[i].spellid = 0;
				m_pp.buffs[i].duration = 0;
				m_pp.buffs[i].level = 0;
				m_pp.buffs[i].effect = 0;
			}
		}
		if (pQueuedSaveWorkID) {
			dbasync->CancelWork(pQueuedSaveWorkID);
			pQueuedSaveWorkID = 0;
		}
		if (iCommitNow <= 1) {
			char* query = 0;
			uint32_breakdown workpt;
			workpt.b4() = DBA_b4_Entity;
			workpt.w2_3() = GetID();
			workpt.b1() = DBA_b1_Entity_Client_Save;
			DBAsyncWork* dbaw = new DBAsyncWork(MTdbafq, workpt, DBAsync::Write, 0xFFFFFFFF);
			dbaw->AddQuery(iCommitNow == 0 ? true : false, &query, database.SetPlayerProfile_MQ(&query, account_id, character_id, &m_pp, &m_inv), false);
			if (iCommitNow == 0){
				pQueuedSaveWorkID = dbasync->AddWork(&dbaw, 2500);
			}
			else {
				dbasync->AddWork(&dbaw, 0);
				SaveBackup();
			}
			safe_delete_array(query);
			return true;
		}
		else if (database.SetPlayerProfile(account_id, character_id, &m_pp, &m_inv)) {
			SaveBackup();
		}
		else {
			cerr << "Failed to update player profile" << endl;
			return false;
		}
	}
	
	return true;
}

void Client::SaveBackup() {
	if (!RunLoops)
		return;
	char* query = 0;
	DBAsyncWork* dbaw = new DBAsyncWork(&DBAsyncCB_CharacterBackup, this->CharacterID(), DBAsync::Read);
	dbaw->AddQuery(0, &query, MakeAnyLenString(&query, "Select id, UNIX_TIMESTAMP()-UNIX_TIMESTAMP(ts) as age from character_backup where charid=%u and backupreason=0 order by ts asc", this->CharacterID()), true);
	dbasync->AddWork(&dbaw, 0);
}

CLIENTPACKET::CLIENTPACKET()
{
    app = NULL;
    ack_req = false;
}

CLIENTPACKET::~CLIENTPACKET()
{
    safe_delete(app);
}

bool Client::AddPacket(const APPLAYER *pApp, bool bAckreq) {
	if (!pApp)
		return false;
    CLIENTPACKET *c = new CLIENTPACKET;

    c->ack_req = bAckreq;
    c->app = pApp->Copy();
    clientpackets.Append(c);
    return true;
}

bool Client::AddPacket(APPLAYER** pApp, bool bAckreq) {
	if (!pApp || !(*pApp))
		return false;
    CLIENTPACKET *c = new CLIENTPACKET;
	
    c->ack_req = bAckreq;
    c->app = *pApp;
	*pApp = 0;
	
    clientpackets.Append(c);
    return true;
}


sint32 Client::LevelRegen()
{
	sint32 hp = 0;
	if (GetLevel() <= 19) {
		if(IsSitting())
			hp+=2;
		else
			hp+=1;
	}
	else if(GetLevel() <= 49) {
		if(IsSitting())
			hp+=3;
		else
			hp+=1;
	}
	else if(GetLevel() == 50) {
		if(IsSitting())
			hp+=4;

		else
			hp+=1;
	}
	else if(GetLevel() >= 51) {
		if(IsSitting())
			hp+=5;
		else
			hp+=2;
	}
	if(GetRace() == IKSAR || GetRace() == TROLL) {
		if (GetLevel() <= 19) { // 1 4
			if(IsSitting())
				hp+=2;
		}
		else if(GetLevel() <= 49) { // 2 6
			if(IsSitting())
				hp+=3;
			else
				hp+=1;
		}
		else if(GetLevel() >= 50) { // 4 12
			if(IsSitting())
				hp+=7;
			else
				hp+=2;
		}
	}
	if (GetAA(225) >= 1){
		hp += GetAA(225);
	}
	if (GetAA(29) >= 1){
		hp += GetAA(29);
	}
	
	return hp;
}

bool Client::SendAllPackets() {
	LinkedListIterator<CLIENTPACKET*> iterator(clientpackets);
	
	CLIENTPACKET* cp = 0;
	iterator.Reset();
	while(iterator.MoreElements()) {
		cp = iterator.GetData();
		if(eqnc)
			eqnc->FastQueuePacket(&cp->app, iterator.GetData()->ack_req);
		iterator.RemoveCurrent();
		LogFile->write(EQEMuLog::Normal, "Transmitting a packet");
	}
	return true;
}

void Client::QueuePacket(const APPLAYER* app, bool ack_req, CLIENT_CONN_STATUS required_state,int8 filter) {
	if(filter!=0){
		if(GetFilter(filter)==0)
			return; //Client has this filter on, no need to send packet
	}
	if (app != 0) {
		if (app->size >= 31500) {
			cout << "WARNING: abnormal packet size. n='" << this->GetName() << "', o=0x" << hex << app->opcode << dec << ", s=" << app->size << endl;
		}
	}
	
	//#ifdef EQDEBUG >= 9
		// This just here while figuring out new opcodes/packets
		#ifdef MERTHALICIOUS
			//@merth: this just here temporarily for my debugging
			cout << "Sending: 0x" << hex << setw(4) << setfill('0') << app->opcode << dec << ", size=" << app->size << endl;
		#endif
	//#endif
	
	// if the program doesnt care about the status or if the status isnt what we requested
    if (required_state != CLIENT_CONNECTINGALL && client_state != required_state)
    {
        // todo: save packets for later use
        AddPacket(app, ack_req);
        LogFile->write(EQEMuLog::Normal, "Adding Packet to list (%d) (%d)", app->opcode, (int)required_state);
    }
    else
	    if(eqnc)
            eqnc->QueuePacket(app, ack_req);
}

void Client::FastQueuePacket(APPLAYER** app, bool ack_req, CLIENT_CONN_STATUS required_state) {
	if (app != 0 && (*app) != 0) {
		if ((*app)->size >= 31500) {
			cout << "WARNING: abnormal packet size. n='" << this->GetName() << "', o=0x" << hex << (*app)->opcode << dec << ", s=" << (*app)->size << endl;
		}
	}
	
	//cout << "Sending: 0x" << hex << setw(4) << setfill('0') << (*app)->opcode << dec << ", size=" << (*app)->size << endl;
	
	// if the program doesnt care about the status or if the status isnt what we requested
    if (required_state != CLIENT_CONNECTINGALL && client_state != required_state) {
        // todo: save packets for later use
        AddPacket(app, ack_req);
        LogFile->write(EQEMuLog::Normal, "Adding Packet to list (%d) (%d)", (*app)->opcode, (int)required_state);
    }
    else {
	    if(eqnc)
            eqnc->FastQueuePacket(app, ack_req);
		else if (app && (*app))
			delete *app;
		*app = 0;
	}
}

void Client::ChannelMessageReceived(int8 chan_num, int8 language, const char* message, const char* targetname) {
	#if EQDEBUG >= 11
		LogFile->write(EQEMuLog::Debug,"Client::ChannelMessageReceived() Channel:%i message:'%s'", chan_num, message);
	#endif
	
	if (targetname == NULL) {
		targetname = (target==NULL) ? NULL : target->GetName();
	}
	
	switch(chan_num)
	{
	case 0: { // GuildChat
		if (guilddbid == 0 || guildeqid >= 512)
			Message(0, "Error: You arent in a guild.");
		else if (!guilds[guildeqid].rank[guildrank].speakgu)
			Message(0, "Error: You dont have permission to speak to the guild.");
		else if (!worldserver.SendChannelMessage(this, targetname, chan_num, guilddbid, language, message))
			Message(0, "Error: World server disconnected");
		break;
	}
	case 2: { // GroupChat
		Group* group = entity_list.GetGroupByMob(this);
		if (this->isgrouped && group != 0) {
			group->GroupMessage(this,(const char*) message);
		}
		break;
	}
	case 3: // Shout
	case 4: { // Auction
		entity_list.ChannelMessage(this, chan_num, language, message);
		break;
	}
	case 5: { // OOC
		if(!ooc_timer->Check())
		{
			if(strlen(targetname)==0)
			ChannelMessageReceived(5, language, message,"discard"); //Fast typer or spammer??
			else
			return;
		}
		if(worldserver.oocmuted && admin < 100)
		{
			Message(0,"OOC has been muted.  Try again later.");
			return;
		}
		if(revoked)
		{
			Message(0, "You have been revoked.  You may not talk on OOC.");
			return;
		}
		else if (!worldserver.SendChannelMessage(this, 0, 5, 0, language, message))
			Message(0, "Error: World server disconnected");
		break;
	}
	case 6: // Broadcast
	case 11: { // GMSay
		if (!(admin >= 80))
			Message(0, "Error: Only GMs can use this channel");
		else if (!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, message))
			Message(0, "Error: World server disconnected");
		break;
	}
	case 7: { // Tell
		if(!worldserver.SendChannelMessage(this, targetname, chan_num, 0, language, message))
			Message(0, "Error: World server disconnected");
		break;
	}
	case 8: { // /say
		if(message[0] == COMMAND_CHAR) 
			command_dispatch(this, message);
		else
		{
//			if ((target != 0) && (DistNoRootNoZ(target) <= 200)) {
//				parse->Event(EVENT_SAY, target->GetNPCTypeID(), message, target, this->CastToMob());
//			}
			entity_list.ChannelMessage(this, chan_num, language, message);
			if (target != 0 && target->IsNPC() && !target->CastToNPC()->IsEngaged()) {
				if (DistNoRootNoZ(*target) <= 200) {
					parse->Event(EVENT_SAY, target->GetNPCTypeID(), message, target, this->CastToMob());
				#ifdef IPC
                    if(target->CastToNPC()->IsInteractive()) {
						target->CastToNPC()->InteractiveChat(chan_num,language,message,targetname,this);
					}
				#endif
					//parse->Event(EVENT_SAY, target->GetNPCTypeID(), message, target, this->CastToMob());
				}
			}
		}
		break;
	}
	default: {
		Message(0, "Channel (%i) not implemented",(int16)chan_num);
	}
	}
}

void Client::ChannelMessageSend(const char* from, const char* to, int8 chan_num, int8 language, const char* message, ...) {
	if ((chan_num==11 && !(this->GetGM())) || (chan_num==10 && this->Admin()<80)) // dont need to send /pr & /petition to everybody
		return;
	va_list argptr;
	char buffer[4096];

	va_start(argptr, message);
	vsnprintf(buffer, 4096, message, argptr);
	va_end(argptr);

	APPLAYER app(OP_ChannelMessage, sizeof(ChannelMessage_Struct)+strlen(buffer)+1);
	ChannelMessage_Struct* cm = (ChannelMessage_Struct*)app.pBuffer;

	if (from == 0)
		strcpy(cm->sender, "ZServer");
	else if (from[0] == 0)
		strcpy(cm->sender, "ZServer");
	else
		strcpy(cm->sender, from);
	if (to != 0)
		strcpy((char *) cm->targetname, to);
	else if (chan_num == 7)
		strcpy(cm->targetname, m_pp.name);
	else
		cm->targetname[0] = 0;
	if (language < MAX_PP_LANGUAGE) {
		cm->skill_in_language = m_pp.languages[language];
		cm->language = language;
	}
	else {
		cm->skill_in_language = 100;
		cm->language = 0;
	}

	cm->chan_num = chan_num;
	strcpy(&cm->message[0], buffer);
	app.Deflate();
	QueuePacket(&app);
}

void Client::Message(uint32 type, const char* message, ...) {
	va_list argptr;
	char buffer[4096];
	
	va_start(argptr, message);
	vsnprintf(buffer, sizeof(buffer), message, argptr);
	va_end(argptr);
	
	// @merth: haven't figured out the packet entirely.. sending 4096k packet
	// for now to ensure client clears buffer properly
	uint32 len_packet = sizeof(buffer);
	uint32 len_buf = strlen(buffer)+1;
	if (len_buf > len_packet) {
		LogFile->write(EQEMuLog::Debug, "Client::Message() - OP_SpecialMesg needs work (%s)", buffer);
		len_packet = sizeof(SpecialMesg_Struct)+strlen(buffer)+1;
	}
	
	//uint32 len_packet = sizeof(SpecialMesg_Struct)+strlen(buffer)+1;
	APPLAYER* app = new APPLAYER(OP_SpecialMesg, len_packet);
	SpecialMesg_Struct* sm=(SpecialMesg_Struct*)app->pBuffer;
	sm->header[0] = 0x04; // Header used for #emote style messages..
	sm->header[1] = 0x04; // Play around with these to see other types
	sm->header[2] = 0x00;
	sm->msg_type = type;
	sm->target_spawn_id = this->GetID();
	memcpy(sm->message, buffer, strlen(buffer));
	app->Deflate();
	
	QueuePacket(app);
	safe_delete(app);
}

void Client::SendHPUpdate()	 {
	APPLAYER hp_app;
 	CreateHPPacket(&hp_app);
 	entity_list.QueueClientsByTarget(this, &hp_app,false);
}

void Client::SetMaxHP() {
	if(dead)
		return;
	SetHP(CalcMaxHP());
	SendHPUpdate();
	Save();
}

bool Client::UpdateLDoNPoints(sint32 points, int32 theme)
{
	if(points < 0)
	{
		if(m_pp.ldon_available_points < points*-1)
			return false;
	}
	switch(theme)
	{
	case 1:
		{
			if(points < 0)
			{
				if(m_pp.ldon_guk_points < points*-1)
					return false;
			}
			m_pp.ldon_guk_points += points;
			break;
		}
	case 2:
		{
			if(points < 0)
			{
				if(m_pp.ldon_mirugal_points < points*-1)
					return false;
			}
			m_pp.ldon_mirugal_points += points;
			break;
		}
	case 3:
		{
			if(points < 0)
			{
				if(m_pp.ldon_mistmoore_points < points*-1)
					return false;
			}
			m_pp.ldon_mistmoore_points += points;
			break;
		}
	case 4:
		{
			if(points < 0)
			{
				if(m_pp.ldon_rujarkian_points < points*-1)
					return false;
			}
			m_pp.ldon_rujarkian_points += points;
			break;
		}
	case 5:
		{
			if(points < 0)
			{
				if(m_pp.ldon_takish_points < points*-1)
					return false;
			}
			m_pp.ldon_takish_points += points;
			break;
		}
	}
	m_pp.ldon_available_points += points;
#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;
	if(GuildDBID() != 0 || GetLevel() >= 40)
	m_pp.pvp = true;
	if(points != 0)
	guildwars.UpdateGuildWarsPoints(CharacterID(),points);
#endif
	APPLAYER* outapp = new APPLAYER(OP_AdventurePointsUpdate, sizeof(AdventurePoints_Update_Struct));
	AdventurePoints_Update_Struct* apus = (AdventurePoints_Update_Struct*)outapp->pBuffer;
	apus->ldon_available_points = m_pp.ldon_available_points;
	apus->ldon_guk_points = m_pp.ldon_guk_points;
	apus->ldon_mirugal_points = m_pp.ldon_mirugal_points;
	apus->ldon_mistmoore_points = m_pp.ldon_mistmoore_points;
	apus->ldon_rujarkian_points = m_pp.ldon_rujarkian_points;
	apus->ldon_takish_points = m_pp.ldon_takish_points;
	QueuePacket(outapp);
	safe_delete(outapp);
	return true;
}

void Client::AddEXP(int32 add_exp) {
	int32 add_aaxp = (int32)((float)add_exp * ((float)(m_pp.perAA) / 100.0f));

	// Old function
	//int32 exp = GetEXP() + (add_exp - add_aaxp);

	// TC - Uses modifier now from variables table.
	int32 exp = GetEXP() + (int32)((zone->GetEXPMod()) * (add_exp - add_aaxp));

	//int32 aaexp = GetAAXP() + add_aaxp;
	// TC - New function

	int32 aaexp = (int32)((zone->GetAAXPMod()) * add_aaxp);
	if(GetAAXP()<0xFFFFFFFF)
		aaexp+=GetAAXP();
	SetEXP(exp, aaexp, false);
}

void Client::SetEXP(int32 set_exp, int32 set_aaxp, bool isrezzexp) {
	max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51);
	if (max_AAXP == 0 || GetEXPForLevel(GetLevel()) == 0xFFFFFFFF) {
		Message(13, "Error in Client::SetEXP. EXP not set.");
		return; // Must be invalid class/race
	}
	if ((set_exp + set_aaxp) > m_pp.exp) {
		if (isrezzexp)
			this->SimpleMessage_StringID(15,REZ_REGAIN,0);
		else{
			if(this->IsGrouped())
				this->SimpleMessage_StringID(15,GAIN_GROUPXP,0);
			else
				this->SimpleMessage_StringID(15,GAIN_XP,0);
		}
	}
	else
		Message(15, "You have lost experience.");
	
	int16 check_level = GetLevel()+1;
	while (set_exp >= GetEXPForLevel(check_level)) {
		check_level++;
		if (check_level > 100) { // Quagmire - this was happening because GetEXPForLevel returned 0 on unknown race/class combo, Changed it to return 0xFFFFFFFF on error
			check_level = GetLevel()+1;
			break;
		}
	}
	while (set_exp < GetEXPForLevel(check_level-1)) {
		check_level--;
		if (check_level < 2) {
			check_level = 2;
			break;
		}
	}
	
	if (set_aaxp >= max_AAXP) {
		int last_unspentAA = m_pp.aapoints;
		m_pp.aapoints = set_aaxp / max_AAXP;
		set_aaxp = set_aaxp - (max_AAXP * m_pp.aapoints);
		if(set_aaxp <=0) {
			set_aaxp = 0;
		}
		m_pp.expAA = set_aaxp;
		m_pp.aapoints += last_unspentAA;
		set_aaxp = m_pp.expAA % max_AAXP;
		
		//Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
		char val1[20]={0};
		Message_StringID(15,GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints,val1),"(s)");
		//Message(15, "You now have %d skill points available to spend.", m_pp.aapoints);
	}
	
	m_pp.expAA = set_aaxp;
	
	int8 maxlevel = 66;
	#ifdef GUILDWARS
		if(GuildDBID() == 0)
			maxlevel = NOGUILDCAPLEVEL;
		else
			maxlevel = GAINLEVEL;
	#endif
	if ((GetLevel() != check_level-1) && !(check_level-1 >= maxlevel)) {
		char val1[20]={0};
		if (GetLevel() == check_level-2){
			Message_StringID(15,GAIN_LEVEL,ConvertArray(check_level-1,val1));
			//Message(15, "You have gained a level! Welcome to level %i!", check_level-1);
		}
		if (GetLevel() == check_level){
			Message_StringID(15,LOSE_LEVEL,ConvertArray(check_level-1,val1));
			//Message(15, "You lost a level! You are now level %i!", check_level-1);
		}
		else
			Message(15, "Welcome to level %i!", check_level-1);
		m_pp.exp = set_exp;
		SetLevel(check_level-1);
	}

	//send the expdata in any case so the xp bar isnt stuck after leveling
	APPLAYER* 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) set_exp-tmpxp2 ) / ( (double) tmpxp1-tmpxp2 );
		eu->exp = (uint32)(330.0f * tmpxp);
		QueuePacket(outapp);
	}
	safe_delete(outapp);
	m_pp.exp = set_exp;

	SendAAStats();
	if (admin>=100 && GetGM()) {
		char val1[20]={0};
		char val2[20]={0};
		char val3[20]={0};
		Message_StringID(15,GM_GAINXP,ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3));
		//Message(15, "[GM] You now have %d / %d EXP and %d / %d AA exp.", set_exp, GetEXPForLevel(GetLevel()+1), set_aaxp, max_AAXP);

	}
}

void Client::MovePC(int32 zoneID, float x, float y, float z, int8 ignorerestrictions, bool summoned)
{
	MovePC(database.GetZoneName(zoneID), x, y, z, ignorerestrictions, summoned);
}

void Client::MovePC(const char* zonename, float x, float y, float z, int8 ignorerestrictions, bool summoned)
{
	//if (this->isgrouped && entity_list.GetGroupByClient(this) != 0)
	//	entity_list.GetGroupByClient(this)->DelMember(this->CastToMob());
	if (IsAIControlled() && zonename == 0) {
		GMMove(x, y, z);
		return;
	}

	zonesummon_ignorerestrictions = ignorerestrictions;
	APPLAYER* outapp = new APPLAYER;

	if (summoned == true) {
		outapp->size = sizeof(GMSummon_Struct);
		outapp->pBuffer = new uchar[outapp->size];
		memset(outapp->pBuffer, 0, outapp->size);
		GMSummon_Struct* gms = (GMSummon_Struct*) outapp->pBuffer;

		strcpy(gms->charname, this->GetName());
		strcpy(gms->gmname, this->GetName());

		outapp->opcode = OP_GMSummon;
		gms->x = (sint32) x;
		gms->y = (sint32) y;
		gms->z = (sint32) z;

		if (zonename == 0) {
			gms->zoneID = zone->GetZoneID();
		}
		else {
			gms->zoneID = database.GetZoneID(zonename);
			strcpy(zonesummon_name, zonename);
			zonesummon_x = x;
			zonesummon_y = y;
			zonesummon_z = z;
		}
	}
	else {
		outapp->size = sizeof(GMGoto_Struct);
		outapp->pBuffer = new uchar[outapp->size];
		memset(outapp->pBuffer, 0, outapp->size);
		GMGoto_Struct* gmg = (GMGoto_Struct*) outapp->pBuffer;

		strcpy(gmg->charname, this->GetName());
		strcpy(gmg->gmname, this->GetName());

		outapp->opcode = OP_GMGoto;
		gmg->x = (sint32) x;
		gmg->y = (sint32) y;
		gmg->z = (sint32) z;

		if (zonename == 0) {
            gmg->zoneID = zone->GetZoneID();
        }
        else {
            gmg->zoneID = database.GetZoneID(zonename);
            if (gmg->zoneID == 0)
			{
				Message(0, "Invalid zone name");
				safe_delete(outapp);
				return;
			}
			strcpy(zonesummon_name, zonename);
            zonesummon_x = x;
            zonesummon_y = y;
            zonesummon_z = z;
        }
	}

	QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::SetLevel(int8 set_level, bool command)
{
	#ifdef GUILDWARS
		if(set_level > SETLEVEL) {
			Message(0,"You cannot exceed level %i on a GuildWars Server.",SETLEVEL);
			return;
		}
	#endif

	if (GetEXPForLevel(set_level) == 0xFFFFFFFF) {
		LogFile->write(EQEMuLog::Error,"Client::SetLevel() GetEXPForLevel(%i) = 0xFFFFFFFF", set_level);
		return;
	}

	APPLAYER* outapp = new APPLAYER(OP_LevelUpdate, sizeof(LevelUpdate_Struct));
	LevelUpdate_Struct* lu = (LevelUpdate_Struct*)outapp->pBuffer;
	lu->level = set_level;
	lu->level_old = level;
	level = set_level;

	if(set_level > m_pp.level) // Yes I am aware that you could delevel yourself and relevel this is just to test!
		m_pp.points += 5;

	m_pp.level = set_level;
	if (command){
		m_pp.exp = GetEXPForLevel(set_level);
		Message(15, "Welcome to level %i!", set_level);
		lu->exp = 0;
	}
	else {
		double tmpxp = (double) ( (double) m_pp.exp - GetEXPForLevel( GetLevel() )) /
						( (double) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel()));
		lu->exp =  (int32)(330.0f * tmpxp);
    }
	QueuePacket(outapp);
	safe_delete(outapp);
	this->SendAppearancePacket(0x01, set_level); // who level change

    LogFile->write(EQEMuLog::Normal,"Setting Level for %s to %i", GetName(), set_level);

	SetHP(CalcMaxHP());		// Why not, lets give them a free heal
	SendHPUpdate();
	SetMana(CalcMaxMana());
	UpdateWho();
	Save();
}

//                            hum     bar     eru     elf     hie     def     hef     dwa     tro     ogr     hal    gno     iks,    vah     frog
float  race_modifiers[15] = { 100.0f, 105.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 100.0f, 120.0f, 115.0f, 95.0f, 100.0f, 120.0f, 100.0f, 100.0f}; // Quagmire - Guessed on iks and vah
//                            war   cle    pal    ran    shd    dru    mnk    brd    rog    shm    nec    wiz    mag    enc    bst
float class_modifiers[15] = { 9.0f, 10.0f, 14.0f, 14.0f, 14.0f, 10.0f, 12.0f, 14.0f, 9.05f, 10.0f, 11.0f, 11.0f, 11.0f, 11.0f, 10.0f};

// Note: The client calculates exp separately, we cant change this function
// Add: You can set the values you want now, client will be always sync :) - Merkur
uint32 Client::GetEXPForLevel(int16 check_level)
{
	int16 tmprace = GetBaseRace();
	if (tmprace == IKSAR) // Quagmire, set these up so they read from array right
		tmprace = 12;
	else if (tmprace == VAHSHIR)
		tmprace = 13;
	else if ((tmprace == FROGLOK) || (tmprace == FROGLOK2))
		tmprace = 14;
	else
		tmprace--;

	if (tmprace >= sizeof(race_modifiers) || GetClass() < 1 || GetClass() > 15)
		return 0xFFFFFFFF;

	int16 check_levelm1 = check_level-1;
	if (check_level < 31)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]);
	else if (check_level < 36)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.1);
	else if (check_level < 41)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.2);
	else if (check_level < 46)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.3);
	else if (check_level < 52)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.4);
	else if (check_level < 53)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.5);
	else if (check_level < 54)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.6);
	else if (check_level < 55)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.7);
	else if (check_level < 56)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*1.9);
	else if (check_level < 57)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*2.1);
	else if (check_level < 58)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*2.3);
	else if (check_level < 59)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*2.5);
	else if (check_level < 60)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*2.7);
	else if (check_level < 61)
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*3.0);
	else
		return (uint32)((check_levelm1)*(check_levelm1)*(check_levelm1)*class_modifiers[GetClass()-1]*race_modifiers[tmprace]*3.1);
}

sint32 Client::CalcMaxHP() {
	max_hp = (CalcBaseHP() + itembonuses->HP + spellbonuses->HP);
	if (GetAA(28) != 0) {
		if (GetAA(28) == 1)
			max_hp += (uint32)((float)max_hp/100.0f) * ((GetAA(120)!=0) ? 4.0f:2.0f);
		if (GetAA(28) == 2)
			max_hp += (uint32)((float)max_hp/100.0f) * ((GetAA(120)!=0) ? 7.0f:5.0f);
		if (GetAA(28) == 3)
			max_hp += (uint32)((float)max_hp/100.0f) * ((GetAA(120)!=0) ? 12.0f:10.0f);
	}
/*
	if (GetAA(120) != 0) {
		LogFile->write(EQEMuLog::Debug, "Physical Enhancement: MaxHP=%i", max_hp);
		max_hp += (uint32)((float)max_hp/100.0f) * 2.3f;
		LogFile->write(EQEMuLog::Debug, "Physical Enhancement: MaxHP=%i", max_hp);
	}
*/
	if (cur_hp > max_hp)
		cur_hp = max_hp;
	return max_hp;
}

// Note: The client calculates max hp separatly, we cant change this function
sint32 Client::CalcBaseHP()
{
	int8 multiplier=GetClassLevelFactor();

	if (multiplier == 0) {
		cerr << "Multiplier == 0 in Client::CalcBaseHP, Using Generic..." << endl;
		multiplier=12;
	}

	#if EQDEBUG >= 11
		LogFile->write(EQEMuLog::Debug,"Client::CalcBaseHP() multiplier:%i level:%i sta:%i", multiplier, GetLevel(), GetSTA());
	#endif
int16 sta = GetSTA();
if(sta > 305)
sta = 305;
	base_hp = 5+multiplier*GetLevel()+multiplier*GetLevel()*sta/300;
	return base_hp;
}

void Client::SetSkill(int16 skillid, int8 value) {
	if (skillid > HIGHEST_SKILL)
		return;
	m_pp.skills[skillid + 1] = value; // We need to be able to #setskill 254 and 255 to reset skills

	if(value <= 252) {
		APPLAYER* outapp = new APPLAYER(OP_SkillUpdate, sizeof(SkillUpdate_Struct));
		SkillUpdate_Struct* skill = (SkillUpdate_Struct*)outapp->pBuffer;
		skill->skillId=skillid;
		skill->value=value;
		QueuePacket(outapp);
		safe_delete(outapp);
	}
}

void Client::AddSkill(int16 skillid, int8 value) {
	if (skillid > HIGHEST_SKILL)
		return;
	value = GetSkill(skillid) + value;
	if (value > 252)
		value = 252;
	SetSkill(skillid, value);
}

// This should return the combined AC of all the items the player is wearing.
sint16 Client::GetRawItemAC() {
	sint16 Total = 0;
	
	for (sint16 slot_id=0; slot_id<21; slot_id++) {
		const ItemInst* inst = m_inv[slot_id];
		if (inst && inst->IsType(ItemTypeCommon)) {
			Total += inst->GetItem()->Common.AC;
		}
	}
	
	return Total;
}

sint16 Client::acmod() {
	int agility = GetAGI();
	int level = GetLevel();
	if (agility >=1 && agility <=74){
		if (agility == 1)
			return -24;
		else if (agility >=2 && agility <=3)
			return -23;
		else if (agility == 4)
			return -22;
		else if (agility >=5 && agility <=6)
			return -21;
		else if (agility >=7 && agility <=8)
			return -20;
		else if (agility == 9)
			return -19;
		else if (agility >=10 && agility <=11)
			return -18;
		else if (agility == 12)
			return -17;
		else if (agility >=13 && agility <=14)
			return -16;
		else if (agility >=15 && agility <=16)
			return -15;
		else if (agility == 17)
			return -14;
		else if (agility >=18 && agility <=19)
			return -13;
		else if (agility == 20)
			return -12;
		else if (agility >=21 && agility <=22)
			return -11;
		else if (agility >=23 && agility <=24)
			return -10;
		else if (agility == 25)
			return -9;
		else if (agility >=26 && agility <=27)
			return -8;
		else if (agility == 28)
			return -7;
		else if (agility >=29 && agility <=30)
			return -6;
		else if (agility >=31 && agility <=32)
			return -5;
		else if (agility == 33)
			return -4;
		else if (agility >=34 && agility <=35)
			return -3;
		else if (agility == 36)
			return -2;
		else if (agility >=37 && agility <=38)
			return -1;
		else if (agility >=39 && agility <=65)
			return 0;
		else if (agility >=66 && agility <=70)
			return 1;
		else if (agility >=71 && agility <=74)
			return 5;
	}
	else {
		if (agility == 75){
			if (level >= 1 && level <= 6)
				return 9;
			else if (level >= 7 && level <= 19)
				return 23;
			else if (level >= 20 && level <= 39)
				return 33;
			else if (level >= 40)
				return 39;
		}
		else if (agility >= 76 && agility <= 79){
			if (level >= 1 && level <= 6)
				return 10;
			else if (level >= 7 && level <= 19)
				return 23;
			else if (level >= 20 && level <= 39)
				return 33;
			else if (level >= 40)
				return 40;
		}
		else if (agility == 80){
			if (level >= 1 && level <= 6)
				return 11;
			else if (level >= 7 && level <= 19)
				return 24;
			else if (level >= 20 && level <= 39)
				return 34;
			else if (level >= 40)
				return 41;
		}
		else if (agility >= 81 && agility <= 85){
			if (level >= 1 && level <= 6)
				return 12;
			else if (level >= 7 && level <= 19)
				return 25;
			else if (level >= 20 && level <= 39)
				return 35;
			else if (level >= 40)
				return 42;
		}
		else if (agility >= 86 && agility <= 90){
			if (level >= 1 && level <= 6)
				return 12;
			else if (level >= 7 && level <= 19)
				return 26;
			else if (level >= 20 && level <= 39)
				return 36;
			else if (level >= 40)
				return 42;
		}
		else if (agility >= 91 && agility <= 95){
			if (level >= 1 && level <= 6)
				return 13;
			else if (level >= 7 && level <= 19)
				return 26;
			else if (level >= 20 && level <= 39)
				return 36;
			else if (level >= 40)
				return 43;
		}
		else if (agility >= 96 && agility <= 99){
			if (level >= 1 && level <= 6)
				return 14;
			else if (level >= 7 && level <= 19)
				return 27;
			else if (level >= 20 && level <= 39)
				return 37;
			else if (level >= 40)
				return 44;
		}
		else if (agility == 100 && level >= 7){
			if (level >= 7 && level <= 19)
				return 28;
			else if (level >= 20 && level <= 39)
				return 38;
			else if (level >= 40)
				return 45;
		}
		else if (level >= 1 && level <= 6) {
			return 15;
		}
		else if (agility >= 101 && agility <= 105){
			if (level >= 7 && level <= 19)
				return 29;
			else if (level >= 20 && level <= 39)
				return 39;// not verified
			else if (level >= 40)
				return 45;
		}
		else if (agility >= 106 && agility <= 110){
			if (level >= 7 && level <= 19)
				return 29;
			else if (level >= 20 && level <= 39)
				return 39;// not verified
			else if (level >= 40)
				return 46;
		}
		else if (agility >= 111 && agility <= 115){
			if (level >= 7 && level <= 19)
				return 30;
			else if (level >= 20 && level <= 39)
				return 40;// not verified
			else if (level >= 40)
				return 47;
		}
		else if (agility >= 116 && agility <= 119){
			if (level >= 7 && level <= 19)
				return 31;
			else if (level >= 20 && level <= 39)
				return 41;
			else if (level >= 40)
				return 47;
		}
		else if (agility == 120 && level >= 20){
			if (level >= 20 && level <= 39)
				return 42;
			else if (level >= 40)
				return 48;
		}
		else if (level >= 7 && level <= 19) {
				return 32;
		}
		else if (agility >= 121 && agility <= 125){
			if (level >= 20 && level <= 39)
				return 42;
			else if (level >= 40)
				return 49;
		}
		else if (agility >= 126 && agility <= 135){
			if (level >= 20 && level <= 39)
				return 42;
			else if (level >= 40)
				return 50;
		}
		else if (agility >= 136){
			if (level >= 20 && level <= 39)
				return 42;
			else if (level >= 40)
				return 51;
		}
	}
	LogFile->write(EQEMuLog::Error, "Error in Client::acmod()");
	return 0;
};

// This is a testing formula for AC, the value this returns should be the same value as the one the client shows...
// ac1 and ac2 are probably the damage migitation and damage avoidance numbers, not sure which is which.
// I forgot to include the iksar defense bonus and i cant find my notes now...
// AC from spells are not included (cant even cast spells yet..)
int16 Client::GetCombinedAC_TEST() {
#if 0
	int ac1;

	ac1 = GetRawItemAC();
	if (m_pp.class_ != WIZARD && m_pp.class_ != MAGICIAN && m_pp.class_ != NECROMANCER && m_pp.class_ != ENCHANTER) {
		ac1 = ac1*4/3;
	}
	ac1 += GetSkill(DEFENSE)/3;
	if (GetAGI() > 70) {
		ac1 += GetAGI()/20;
	}

	int ac2;

	ac2 = GetRawItemAC();
	if (m_pp.class_ != WIZARD && m_pp.class_ != MAGICIAN && m_pp.class_ != NECROMANCER && m_pp.class_ != ENCHANTER) {
		ac2 = ac2*4/3;
	}
	ac2 += GetSkill(DEFENSE)*400/255;

	int combined_ac = (ac1+ac2)*1000/847;
	return combined_ac;
	float combined_ac = ((float)ac1+(float)ac2)*1000.0f/847.0f;
	return (int16) combined_ac;//*10.0f)-10;
#else
	// new formula
	int avoidance = 0;
	avoidance = (acmod() + ((GetSkill(DEFENSE)*16)/9));
	if (avoidance < 0)
		avoidance = 0;
	int mitigation = 0;
	if (m_pp.class_ != WIZARD && m_pp.class_ != MAGICIAN && m_pp.class_ != NECROMANCER && m_pp.class_ != ENCHANTER) {
		mitigation = (spellbonuses->AC/4) + (GetSkill(DEFENSE)/3) + ((itembonuses->AC*4)/3);
	}
	else {
		mitigation = (spellbonuses->AC/3) + (GetSkill(DEFENSE)/2) + (itembonuses->AC+1);
	}
	int displayed = 0;
	displayed = ((avoidance+mitigation)*1000)/847;
	
	return displayed;
#endif
}


void Client::UpdateWho(int8 remove) {
	if (account_id == 0)
		return;
	if (!worldserver.Connected())
		return;
	ServerPacket* pack = new ServerPacket(ServerOP_ClientList, sizeof(ServerClientList_Struct));
	ServerClientList_Struct* scl = (ServerClientList_Struct*) pack->pBuffer;
	scl->remove = remove;
	scl->wid = this->GetWID();
	scl->IP = this->GetIP();
	scl->charid = this->CharacterID();
	strcpy(scl->name, this->GetName());

	scl->gm = GetGM();
	scl->Admin = this->Admin();
	scl->AccountID = this->AccountID();
	strcpy(scl->AccountName, this->AccountName());
	scl->LSAccountID = this->LSAccountID();
	strn0cpy(scl->lskey, lskey, sizeof(scl->lskey));
	scl->zone = zone->GetZoneID();
	scl->race = this->GetRace();
	scl->class_ = GetClass();
	scl->level = GetLevel();
	if (m_pp.anon == 0)
		scl->anon = 0;
	else if (m_pp.anon == 1)
		scl->anon = 1;
	else if (m_pp.anon >= 2)
		scl->anon = 2;

	scl->tellsoff = tellsoff;
	scl->guilddbid = guilddbid;
	scl->guildeqid = guildeqid;
	scl->LFG = LFG;

	worldserver.SendPacket(pack);
	safe_delete(pack);
}

void Client::WhoAll(Who_All_Struct* whom) {
	if (!worldserver.Connected())
		Message(0, "Error: World server disconnected");
	else {
		ServerPacket* pack = new ServerPacket(ServerOP_Who, sizeof(ServerWhoAll_Struct));
		ServerWhoAll_Struct* whoall = (ServerWhoAll_Struct*) pack->pBuffer;
		whoall->admin = (int8) admin;
		whoall->fromid=this->GetID();
		strcpy(whoall->from, this->GetName());
		strcpy(whoall->whom, whom->whom);
		whoall->lvllow = whom->lvllow;
		whoall->lvlhigh = whom->lvlhigh;
		whoall->gmlookup = whom->gmlookup;
		whoall->wclass = whom->wclass;
		whoall->wrace = whom->wrace;
		worldserver.SendPacket(pack);
		safe_delete(pack);
	}
}
void Client::SendGuildMembers(int32 guildid){
	return; // Dont remove this, this is disabled for now because it wasnt being deleted properly - LE
	GuildMember_Struct* gms=database.GetGuildMembers(guildid);
	if(!gms || gms->count==0){
		printf("Error in SendGuildMembers, no members!\n");
		if(gms)
			safe_delete(gms);
		return;
	}
	int16 namelen=strlen(GetName());
	APPLAYER* outapp = new APPLAYER(OP_GuildMemberList,gms->length+(22*gms->count)+namelen+4);
	memset(outapp->pBuffer,0,outapp->size);
	uchar* buffer=(uchar*)outapp->pBuffer;
	memcpy(buffer,GetName(), namelen);
	buffer+=namelen+1;
	int32 count=htonl(gms->count);
	memcpy(buffer,&count, sizeof(int32));
	buffer+=sizeof(int32);
	Client* member=0;
	for(int32 i=0;i<gms->count;i++){	
		memcpy(buffer,&gms->member[i].name, strlen(gms->member[i].name));
		buffer+=(strlen(gms->member[i].name)+1);
		memcpy(buffer,&gms->member[i].level, sizeof(int32));
		buffer+=sizeof(int32);
		memcpy(buffer,&gms->member[i].class_, sizeof(int32));
		buffer+=sizeof(int32);
		if(gms->member[i].rank==0)//convert from our guildranks to eqlive's
			gms->member[i].rank=htonl(2);
		else if(gms->member[i].rank==1)
			gms->member[i].rank=htonl(1);
		else if(gms->member[i].rank>1)
			gms->member[i].rank=0;
		memcpy(buffer,&gms->member[i].rank, sizeof(int32));
		buffer+=sizeof(int32);
		memcpy(buffer,&gms->member[i].timelaston, sizeof(int32));
		buffer+=sizeof(int32);
		if(strlen(gms->member[i].publicnote)>1){
			memcpy(buffer,&gms->member[i].publicnote, strlen(gms->member[i].publicnote));
			buffer+=strlen(gms->member[i].publicnote);
		}
		buffer+=sizeof(int32);
		member=entity_list.GetClientByName(gms->member[i].name);
		if(member)//only add zone info if player is online :)
			memcpy(buffer,&gms->member[i].zoneid, sizeof(int8));	
		buffer+=sizeof(int8);
	}
	//DumpPacket(outapp);
	outapp->Deflate();
	QueuePacket(outapp);
	safe_delete(outapp);
}
bool Client::SetGuild(int32 in_guilddbid, int8 in_rank) {
	if (in_guilddbid == 0) {
		// update DB
		if (!database.SetGuild(character_id, 0, GUILD_MAX_RANK))
			return false;
		// clear guildtag
		guilddbid = in_guilddbid;
		guildeqid = GUILD_NONE;
		guildrank = GUILD_MAX_RANK;
		SendAppearancePacket(22, GUILD_NONE);
		SendAppearancePacket(23, 3);
		UpdateWho();
		return true;
	}
	else {
		int32 tmp = database.GetGuildEQID(in_guilddbid);
		if (tmp != GUILD_NONE) {
			if (!database.SetGuild(character_id, in_guilddbid, in_rank))
				return false;
			guildeqid = tmp;
			guildrank = in_rank;
			if (guilddbid != in_guilddbid) {
				guilddbid = in_guilddbid;
				SendAppearancePacket(22, guildeqid);
			}

			if (guilds[tmp].rank[in_rank].warpeace || guilds[tmp].leader == account_id)
				SendAppearancePacket(23, 2);
			else if (guilds[tmp].rank[in_rank].invite || guilds[tmp].rank[in_rank].remove || guilds[tmp].rank[in_rank].motd)
				SendAppearancePacket(23, 1);
			else
				SendAppearancePacket(23, 0);
			UpdateWho();
			return true;
		}
	}
	UpdateWho();
	return false;
}

sint32 Client::CalcMaxMana()
{
	switch(GetCasterClass())
	{
		case 'I': {
			max_mana = (int32)(((GetINT()/5)+2) * GetLevel()) +spellbonuses->Mana + itembonuses->Mana;
			break;
				  }
		case 'W': {
			max_mana = (((GetWIS()/5)+2) * GetLevel()) + spellbonuses->Mana + itembonuses->Mana;
			break;
				  }
		case 'N': {
			max_mana = 0;
			break;
		}
		default: {
			cerr << "Invalid Class in CalcMaxMana" << endl;
			max_mana = 0;
			break;
		}
	}
	if (cur_mana > max_mana) {
		cur_mana = max_mana;
		//SendManaUpdatePacket();
	}
#if EQDEBUG >= 11
	LogFile->write(EQEMuLog::Debug, "Client::CalcMaxMana() called for %s - returning %d", GetName(), max_mana);
#endif
	return max_mana;
}

void Client::UpdateAdmin(bool iFromDB) {
	sint16 tmp = admin;
	if (iFromDB)
		admin = database.CheckStatus(account_id);
	if (tmp == admin && iFromDB)
		return;
	UpdateWho();

	if (admin >= 50) {
		if(m_pp.gm == 1) {
			SendAppearancePacket(20, 1, false);
			petition_list.UpdateGMQueue();
		}
	}
	else {
		m_pp.gm = 0;
		SendAppearancePacket(20, 0);
	}
}

// @merth: this needs to be touched up
uint32 Client::NukeItem(uint32 itemnum) {
	if (itemnum == 0)
		return 0;
	int i;
	uint32 x = 0;
	/*
	for (i=0; i<=29; i++) { // Equipped and personal inventory
		if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
			DeleteItemInInventory(i, 0, true);
			x++;
		}
	}
	for (i=251; i<=339; i++) { // Main inventory's and cursor's containers
		if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
			DeleteItemInInventory(i, 0, true);
			x++;
		}
	}
	for (i=2000; i<=2015; i++) { // Bank slots
		if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
			DeleteItemInInventory(i, 0, true);
			x++;
		}
	}
	for (i=2030; i<=2109; i++) { // Bank's containers
		if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
			DeleteItemInInventory(i, 0, true);
			x++;
		}
	}
	for (i=2500; i<=2501; i++) { // Shared bank
		if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
			DeleteItemInInventory(i, 0, true);
			x++;
		}
	}
	for (i=2531; i<=2550; i++) { // Shared bank's containers
		if (GetItemIDAt(i) == itemnum || (itemnum == 0xFFFE && GetItemIDAt(i) != INVALID_ID)) {
			DeleteItemInInventory(i, 0, true);
			x++;
		}
	}
	*/
	return x;
}


bool Client::CheckLoreConflict(const Item_Struct* item) {
	if (!item)
		return false;
	if (!(item->attribs & ItemAttribLore))
		return false;
	
	return (m_inv.HasItem(item->ItemNumber) != SLOT_INVALID);
}
void Client::SetStats(int8 type,sint16 increase_val){
	if(type>STAT_DISEASE){
		printf("Error in Client::SetStats, received invalid type of: %i\n",type); 
		return;
	}
	APPLAYER* outapp = new APPLAYER(OP_IncreaseStats,sizeof(IncreaseStat_Struct));
	IncreaseStat_Struct* iss=(IncreaseStat_Struct*)outapp->pBuffer;
	switch(type){
		case STAT_STR:
			if(increase_val>0)
				iss->str=increase_val;
			if((m_pp.STR+increase_val*2)<0)
				m_pp.STR=0;
			else if((m_pp.STR+increase_val*2)>255)
				m_pp.STR=255;
			else
				m_pp.STR+=increase_val*2;
			break;
		case STAT_STA:
			if(increase_val>0)
				iss->sta=increase_val;
			if((m_pp.STA+increase_val*2)<0)
				m_pp.STA=0;
			else if((m_pp.STA+increase_val*2)>255)
				m_pp.STA=255;
			else
				m_pp.STA+=increase_val*2;
			break;
		case STAT_AGI:
			if(increase_val>0)
				iss->agi=increase_val;
			if((m_pp.AGI+increase_val*2)<0)
				m_pp.AGI=0;
			else if((m_pp.AGI+increase_val*2)>255)
				m_pp.AGI=255;
			else
				m_pp.AGI+=increase_val*2;
			break;
		case STAT_DEX:
			if(increase_val>0)
				iss->dex=increase_val;
			if((m_pp.DEX+increase_val*2)<0)
				m_pp.DEX=0;
			else if((m_pp.DEX+increase_val*2)>255)
				m_pp.DEX=255;
			else
				m_pp.DEX+=increase_val*2;
			break;
		case STAT_INT:
			if(increase_val>0)
				iss->int_=increase_val;
			if((m_pp.INT+increase_val*2)<0)
				m_pp.INT=0;
			else if((m_pp.INT+increase_val*2)>255)
				m_pp.INT=255;
			else
				m_pp.INT+=increase_val*2;
			break;
		case STAT_WIS:
			if(increase_val>0)
				iss->wis=increase_val;
			if((m_pp.WIS+increase_val*2)<0)
				m_pp.WIS=0;
			else if((m_pp.WIS+increase_val*2)>255)
				m_pp.WIS=255;
			else
				m_pp.WIS+=increase_val*2;
			break;
		case STAT_CHA:
			if(increase_val>0)
				iss->cha=increase_val;
			if((m_pp.CHA+increase_val*2)<0)
				m_pp.CHA=0;
			else if((m_pp.CHA+increase_val*2)>255)
				m_pp.CHA=255;
			else
				m_pp.CHA+=increase_val*2;
			break;
	}
	QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::SummonItem(uint32 item_id, sint8 charges) {
	// For now, we're not allowing summon when an item is already on cursor
	int16 slot=SLOT_CURSOR;
 	if (m_inv[SLOT_CURSOR]) {
		for(int i=0;i<10;i++){
			if(!m_inv[8000+i]){
				slot=(8000+i);
				break;
			}
		}
 		//Message(13, "Error: Item already on cursor! (%s)", m_inv[SLOT_CURSOR]->GetItem()->Name);
 		//return;
 	}
	const Item_Struct* item = database.GetItem(item_id);
	
	if (item == NULL) {
		Message(0, "No such item: %i", item_id);
		return;
	}
	
	// Checking to see if the Item is lore or not.
	bool foundlore = CheckLoreConflict(item);
	
	// Checking to see if it is a GM only Item or not.
	bool foundgm = (item->gm && (this->Admin() < 100));
	
	if (!foundlore && !foundgm) { // Okay, It isn't LORE, or if it is, it is not in player's inventory.
		ItemInst* inst = ItemInst::Create(item, charges);
		if (inst) {
			// Custom logic for SummonItem
			if ((inst->GetCharges()==0))// && inst->IsStackable())
				inst->SetCharges(1);
			//inst->SetCharges(
			PutItemInInventory(slot, *inst);
			// Send item packet to user
			SendItemPacket(SLOT_CURSOR, inst, ItemPacketSummonItem);
			safe_delete(inst);
		}
	}
	else { // Item was already in inventory & is a LORE item or was a GM only item.  Give them a message about it.
		if (foundlore){
			SimpleMessage_StringID(0,PICK_LORE);
			//Message(0, "You already have a %s (%i) in your inventory!", item->Name, item_id);
		}
		else if (foundgm)
			Message(0, "You are not a GM to summon this item");
	}
}

// Drop item from inventory to ground (generally only dropped from SLOT_CURSOR)
void Client::DropItem(sint16 slot_id)
{
	// Take control of item in client inventory
	ItemInst* inst = m_inv.PopItem(slot_id);
	
	if (!inst) {
		// Item doesn't exist in inventory!
		Message(13, "Error: Item not found in slot %i", slot_id);
		return;
	}
	
	// Save client inventory change to database
	database.SaveInventory(CharacterID(), NULL, slot_id);
	
	// Package as zone object
	Object* object = new Object(this, inst);
	entity_list.AddObject(object, true);
	object->Save();
	
	safe_delete(inst);
}

const sint32& Client::SetMana(sint32 amount) {
	Mob::SetMana(amount);
	SendManaUpdatePacket();
	return cur_mana;
}

void Client::SendManaUpdatePacket() {
	if (!Connected() || IsCasting())
		return;
	APPLAYER* outapp = new APPLAYER(OP_ManaChange, sizeof(ManaChange_Struct));
	ManaChange_Struct* manachange = (ManaChange_Struct*)outapp->pBuffer;
//	manachange->new_mana = cur_mana*1.1+2;
	manachange->new_mana = cur_mana;
	manachange->stamina = 6000;
	manachange->spell_id = casting_spell_id;
//	Message(0, "Queueing a manachange with %d new mana (%d max)", manachange->new_mana, GetMaxMana());
	QueuePacket(outapp);
	safe_delete(outapp);
}

void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
{
	Mob::FillSpawnStruct(ns, ForWho);
	
	// Populate client-specific spawn information
	ns->spawn.afk		= AFK;
	ns->spawn.lfg		= LFG; // @bp: afk and lfg are cleared on zoneing on live
	ns->spawn.anon		= m_pp.anon;
	ns->spawn.gm		= GetGM() ? 1 : 0;
	ns->spawn.guild_id	= GuildEQID();
	ns->spawn.linkdead	= IsLD() ? 1 : 0;
	ns->spawn.aa_title	= aa_title;
	ns->spawn.pvp		= GetPVP() ? 1 : 0;
	
	if (IsBecomeNPC() == true)
		ns->spawn.npc = true;
	else if (ForWho == this)
		ns->spawn.npc = 10;
	else
		ns->spawn.npc = 0;
	
	if (guildeqid == GUILD_NONE) {
		ns->spawn.guild_rank = -1;
	}
	else {
		if (guilds[guildeqid].rank[guildrank].warpeace || guilds[guildeqid].leader == account_id)
			ns->spawn.guild_rank = 2;
		else if (guilds[guildeqid].rank[guildrank].invite || guilds[guildeqid].rank[guildrank].remove || guilds[guildeqid].rank[guildrank].motd)
			ns->spawn.guild_rank = 1;
		else
			ns->spawn.guild_rank = 0;
	}
	
	// @merth: pp also hold this info; should we pull from there or inventory?
	// (update: i think pp should do it, as this holds LoY dye - plus, this is ugly code with Inventory!)
	const Item_Struct* item = NULL;
	const ItemInst* inst = NULL;
	if ((inst = m_inv[SLOT_HANDS]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_HANDS]	= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_HANDS]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_HEAD]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_HEAD]	= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_HEAD]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_ARMS]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_ARMS]	= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_ARMS]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_BRACER01]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_BRACER]= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_BRACER]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_BRACER02]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_BRACER]= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_BRACER]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_CHEST]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_CHEST]	= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_CHEST]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_LEGS]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_LEGS]	= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_LEGS]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_FEET]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		ns->spawn.equipment[MATERIAL_FEET]	= item->Common.Material;
		ns->spawn.dye_rgb[MATERIAL_FEET]	= item->Common.Color;
	}
	if ((inst = m_inv[SLOT_PRIMARY]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		if (strlen(item->IDFile) > 2)
			ns->spawn.equipment[MATERIAL_PRIMARY] = atoi(&item->IDFile[2]);
	}
	if ((inst = m_inv[SLOT_SECONDARY]) && inst->IsType(ItemTypeCommon)) {
		item = inst->GetItem();
		if (strlen(item->IDFile) > 2)
			ns->spawn.equipment[MATERIAL_SECONDARY] = atoi(&item->IDFile[2]);
	}
	
	// @merth: these two may be related to ns->spawn.equip_chest2
	/*
	ns->spawn.npc_armor_graphic = texture;
	ns->spawn.npc_helm_graphic = helmtexture;
	*/
}

// Returns a slot's item ID (returns INVALID_ID if not found)
uint32 Client::GetItemIDAt(sint16 slot_id) {
	const ItemInst* inst = m_inv[slot_id];
	if (inst)
		return inst->GetItem()->ItemNumber;
	
	// None found
	return INVALID_ID;
}

// Remove item from inventory
void Client::DeleteItemInInventory(sint16 slot_id, sint8 quantity, bool client_update) {
	#if (EQDEBUG >= 5)
		LogFile->write(EQEMuLog::Debug, "DeleteItemInInventory(%i, %i, %s)", slot_id, quantity, (client_update) ? "true":"false");
	#endif
	
	// Nuke from inventory
	m_inv.DeleteItem(slot_id, quantity);
	
	// Save change to database
	const ItemInst* inst = m_inv[slot_id];
	if(inst)//quantity 0 is delete
		database.SaveInventory(character_id, inst, slot_id);
	else
		database.SaveInventory(character_id, 0, slot_id);
	if (client_update && inst && inst->GetCharges()) {
		APPLAYER* outapp = new APPLAYER(OP_TraderDelItem, sizeof(TraderDelItem_Struct));
		TraderDelItem_Struct* delitem	= (TraderDelItem_Struct*)outapp->pBuffer;
		delitem->slotid			= slot_id;
		delitem->unknown			= 0xFFFFFFFF;
		//if(inst->GetCharges()<=quantity && (inst->GetItem()->Common.SpellId>=0xFFFF ||inst->GetItem()->Common.SpellId<=0))
		//	delitem->quantity=0xFFFFFFFF; //fully delete item
		//else
		delitem->quantity	= quantity; // @merth: check that this packet is constructed correctly..
		QueuePacket(outapp);
		safe_delete(outapp);
	}
	else if (client_update) {
		APPLAYER* outapp = new APPLAYER(OP_TraderDelItem, sizeof(TraderDelItem_Struct));
		TraderDelItem_Struct* delitem	= (TraderDelItem_Struct*)outapp->pBuffer;
		delitem->slotid			= slot_id;
		delitem->unknown			= 0xFFFFFFFF;
		delitem->quantity	= 0xFFFFFFFF;
		QueuePacket(outapp);
		safe_delete(outapp);
	}
	
}

// Puts an item into the person's inventory
// Any items already there will be removed from user's inventory
// (Also saves changes back to the database: this may be optimized in the future)
// client_update: Sends packet to client
bool Client::PutItemInInventory(sint16 slot_id, const ItemInst& inst, bool client_update)
{
	m_inv.PutItem(slot_id, inst);
	
	if (client_update && inst) {
		SendItemPacket(slot_id, &inst, ItemPacketSummonItem);
	}
	
	return database.SaveInventory(this->CharacterID(), &inst, slot_id);
}

// Locate an available space in inventory to place an item
// and then put the item there
// The change will be saved to the database
void Client::AutoPutItemInInventory(const ItemInst& inst, bool try_worn, bool try_cursor, ServerLootItem_Struct** bag_item_data)
{
	sint8 charges = inst.GetCharges();
	
	// Looting a container
	if (inst.IsType(ItemTypeContainer)) {
		sint16 slot_id = m_inv.FindFreeSlot(true, try_cursor);
		if (slot_id != SLOT_INVALID){
			m_inv.PutItem(slot_id, inst);
			database.SaveInventory(this->CharacterID(), &inst, slot_id);
			return;
		}
		// @merth: need to set bag_item_data as well (bag_item_data)
	}
	else { // All other item types
		
		// #1: Try to auto equip
		if (try_worn && inst.IsEquipable(GetRace(), GetClass())) {
			for (sint16 i=0; i<22; i++) {
				if (!m_inv[i]) {
					if ((i==SLOT_SECONDARY) && (inst.IsWeapon() && !CanThisClassDuelWield()) )
						continue;
					
					if (inst.IsEquipable(i)) { // Equippable at this slot?
						m_inv.PutItem(i, inst);
						SendLootItemInPacket(&inst, i);
						database.SaveInventory(this->CharacterID(), &inst, i);
						return;
					}
				}
			}
		}
		
		// #2: Stackable item?
		if (inst.IsStackable()) {
			// Pass 1: (Inventory) Attempt to fill stacks that aren't yet full
			sint16 i=0;
			for (i=22; i<=29; i++) {
				ItemInst* tmp_inst = m_inv.GetItem(i);
				
				if (tmp_inst && (tmp_inst->GetItem()==inst.GetItem()) && (tmp_inst->GetCharges() < ITEM_MAX_STACK)) {
					uint8 usedcharges = tmp_inst->GetCharges();
					tmp_inst->SetCharges(inst.GetCharges() + usedcharges);
					SendLootItemInPacket(tmp_inst, i);
					database.SaveInventory(this->CharacterID(), tmp_inst, i);
					return;
				}
			}
			
			// Pass 2: (Inventory Bags) Attempt to fill stacks that aren't yet full
			for (i=22; i<=29; i++) {
				for (uint8 j=0; j<10; j++) {
					ItemInst* tmp_inst = m_inv.GetItem(i, j);			
					if (tmp_inst && (tmp_inst->GetItem()==inst.GetItem()) && (tmp_inst->GetCharges() < ITEM_MAX_STACK)) {
						uint8 usedcharges = tmp_inst->GetCharges();
						tmp_inst->SetCharges(inst.GetCharges() + usedcharges);
						int16 slotid=Inventory::CalcSlotId(i, j);
						SendLootItemInPacket(tmp_inst,slotid);
						database.SaveInventory(this->CharacterID(), tmp_inst, slotid);
						return;
					}
				}
			}
		}
		
		// #3: Try inventory slots
		int16 slot_id = m_inv.FindFreeSlot(false, try_cursor);
		m_inv.PutItem(slot_id, inst);
		ItemInst* tmp = m_inv.GetItem(slot_id);
		if (tmp)
			tmp->SetCharges(charges);
		SendLootItemInPacket(tmp, slot_id);
		database.SaveInventory(this->CharacterID(), &inst, slot_id);
	}
}

void Client::SendItemLink(const ItemInst* inst, bool send_to_all)
{
	if (!inst)
		return;
	
	const Item_Struct* item = inst->GetItem();
	const char* name2 = &item->Name[0];
	APPLAYER* outapp = new APPLAYER(OP_ItemLinkText,strlen(name2)+68);
	char buffer2[135] = {0};
	char itemlink[135] = {0};
	sprintf(itemlink,"%c%07u%s%s%c",0x12,item->ItemNumber,"-00001-00001-00001-00001-0000000000000",name2,0x12);
	sprintf(buffer2,"%c%c%c%c%c%c%c%c%c%c%c%c%s",0x00,0x00,0x00,0x00,0xD3,0x01,0x00,0x00,0x1E,0x01,0x00,0x00,itemlink);
	memcpy(outapp->pBuffer,buffer2,outapp->size);
	QueuePacket(outapp);
	safe_delete(outapp);
	if (send_to_all==false)
		return;
	const char* charname = this->GetName();
	outapp = new APPLAYER(OP_ItemLinkText,strlen(itemlink)+14+strlen(charname));
	char buffer3[150] = {0};
	sprintf(buffer3,"%c%c%c%c%c%c%c%c%c%c%c%c%6s%c%s",0x00,0x00,0x00,0x00,0xD2,0x01,0x00,0x00,0x00,0x00,0x00,0x00,charname,0x00,itemlink);
	memcpy(outapp->pBuffer,buffer3,outapp->size);
	outapp->Deflate();
	entity_list.QueueCloseClients(this->CastToMob(),outapp,true,200,0,false);
	safe_delete(outapp);
}

void Client::SendLootItemInPacket(const ItemInst* inst, sint16 slot_id)
{
	SendItemPacket(slot_id,inst, ItemPacketTrade);
}

bool Client::GMHideMe(Client* client) {
	if (gmhideme) {
		if (client == 0)
			return true;
		else if (admin > client->Admin())
			return true;
		else
			return false;
	}
	else
		return false;
}

void Client::Duck() {
	SetAppearance(2, false);
}

void Client::Stand() {
	SetAppearance(0, false);
}

void Client::ChangeLastName(const char* in_lastname) {
	memset(m_pp.last_name, 0, sizeof(m_pp.last_name));
	if (strlen(in_lastname) >= sizeof(m_pp.last_name))
		strncpy(m_pp.last_name, in_lastname, sizeof(m_pp.last_name) - 1);
	else
		strcpy(m_pp.last_name, in_lastname);
	APPLAYER* outapp = new APPLAYER(OP_GMLastName, sizeof(GMLastName_Struct));
	GMLastName_Struct* gmn = (GMLastName_Struct*)outapp->pBuffer;
	strcpy(gmn->name, name);
	strcpy(gmn->gmname, name);
	strcpy(gmn->lastname, in_lastname);
	gmn->unknown[0]=1;
	gmn->unknown[1]=1;
	gmn->unknown[2]=1;
	gmn->unknown[3]=1;
	entity_list.QueueClients(this, outapp, false);
	// Send name update packet here... once know what it is
	safe_delete(outapp);
}

void Client::ChangeFirstName(const char* oldname,const char* in_firstname,const char* gmname) {
	APPLAYER* outapp = new APPLAYER(OP_GMNameChange, sizeof(GMName_Struct));
	GMName_Struct* gmn=(GMName_Struct*)outapp->pBuffer;
	strncpy(gmn->gmname,gmname,64);
	strncpy(gmn->oldname,oldname,64);
	strncpy(gmn->newname,in_firstname,64);
	
	bool usedname = database.CheckUsedName((const char*) in_firstname);
	if (!usedname) {
		return;
	}
	
	database.UpdateName(oldname, in_firstname);
	Save();
	
	memset(m_pp.name, 0, sizeof(m_pp.name));
	snprintf(m_pp.name, sizeof(m_pp.name), "%s", in_firstname);
	strcpy(name, m_pp.name);
	
	gmn->unknown[0] = 1;
	gmn->unknown[1] = 1;
	gmn->unknown[2] = 1;
	entity_list.QueueClients(this, outapp, false);
	
	safe_delete(outapp);
	
	UpdateWho();
}

void Client::SetGM(bool toggle) {
	if(toggle) {
		m_pp.gm = 1;
		SendAppearancePacket(20, 1);
		Save();
		Message(13, "You are now a GM.");
	}
	else {
		m_pp.gm=0;
		SendAppearancePacket(20, 0);
		Save();
		Message(13, "You are no longer a GM.");
	}
	UpdateWho();
}

void Client::ReadBook(char txtfile[20]) {
	string booktxt2=database.GetBook(txtfile);
	int length=strlen(booktxt2.c_str())+3;
	char booktxt[5000]={0};//booktxt2.c_str();
	strcpy(booktxt,booktxt2.c_str());
	if (booktxt != 0) {
		//char *buffer=(char*)malloc(length);
		//char *bufptr=buffer;
		uchar *buffer=new uchar[length];
		uchar *bufptr=buffer;
		LogFile->write(EQEMuLog::Normal,"Client::ReadBook() textfile:%s Text:%s", txtfile, booktxt);
		APPLAYER* outapp = new APPLAYER(OP_ReadBook,length);
		int16 unknown0=0x00FF;
		outapp->pBuffer=new uchar[(outapp->size)];
		memset(buffer,0,length);
		memcpy(bufptr,&unknown0, sizeof(int16));
		bufptr+=sizeof(int16);
		memcpy(bufptr,&booktxt,strlen(booktxt));
		bufptr+=strlen(booktxt);
		memcpy(outapp->pBuffer, buffer, outapp->size);
		QueuePacket(outapp);
		safe_delete(outapp);
		//free(buffer);
		safe_delete_array(outapp);
	}
}

void Client::SendClientMoneyUpdate(int8 type,int32 amount){
	APPLAYER* outapp = new APPLAYER(OP_TradeMoneyUpdate,sizeof(TradeMoneyUpdate_Struct));
	TradeMoneyUpdate_Struct* mus= (TradeMoneyUpdate_Struct*)outapp->pBuffer;
	mus->amount=amount;
	mus->trader=0;
	mus->type=type;
	QueuePacket(outapp);
	safe_delete(outapp);
}

bool Client::TakeMoneyFromPP(uint32 copper){
	sint32 copperpp,silver,gold,platinum;
	copperpp = m_pp.copper;
	silver = m_pp.silver*10;
	gold = m_pp.gold*100;
	platinum = m_pp.platinum*1000;
	
	sint32 clienttotal = m_pp.copper+m_pp.silver*10+m_pp.gold*100+m_pp.platinum*1000;
	clienttotal -= copper;
	if(clienttotal < 0)
	{
		return false; // Not enough money!
	}
	else
	{
		copperpp -= copper;
		if(copperpp <= 0)
		{
			copper = abs(copperpp);
			m_pp.copper = 0;
		}
		else
		{
			m_pp.copper = copperpp;
			Save();
			return true;
		}
		silver -= copper;
		if(silver <= 0)
		{
			copper = abs(silver);
			m_pp.silver = 0;
		}
		else
		{
			m_pp.silver = silver/10;
			m_pp.copper += (silver-(m_pp.silver*10));
			Save();
			return true;
		}
		
		gold -=copper;
		
		if(gold <= 0)
		{
			copper = abs(gold);
			m_pp.gold = 0;
		}
		else
		{
			m_pp.gold = gold/100;
			int32 silvertest = (gold-(m_pp.gold*100))/10;
			m_pp.silver += silvertest;
			int32 coppertest = (gold-(m_pp.gold*100+silvertest*10));
			m_pp.copper += coppertest;
			Save();
			return true;
		}
		
		platinum -= copper;
		
		//Impossible for plat to be negative, already checked above
		
		m_pp.platinum = platinum/1000;
		int32 goldtest = (platinum-(m_pp.platinum*1000))/100;
		m_pp.gold += goldtest;
		int32 silvertest = (platinum-(m_pp.platinum*1000+goldtest*100))/10;
		m_pp.silver += silvertest;
		int32 coppertest = (platinum-(m_pp.platinum*1000+goldtest*100+silvertest*10));
		m_pp.copper = coppertest;
		Save();
		return true;
	}
}

void Client::AddMoneyToPP(uint32 copper,bool updateclient){
	uint32 tmp;
	uint32 tmp2;
	tmp = copper;
	
	// Add Amount of Platinum
	tmp2 = tmp/1000;
	m_pp.platinum = m_pp.platinum + tmp2;
	tmp-=tmp2*1000;
	
	if (updateclient)
		SendClientMoneyUpdate(3,tmp2);
	
	// Add Amount of Gold
	tmp2 = tmp/100;
	m_pp.gold = m_pp.gold + tmp2;
	tmp-=tmp2*100;
	if (updateclient)
		SendClientMoneyUpdate(2,tmp2);
	
	// Add Amount of Silver
	tmp2 = tmp/10;
	tmp-=tmp2*10;
	m_pp.silver = m_pp.silver + tmp2;
	if (updateclient)
		SendClientMoneyUpdate(1,tmp2);
	
	// Add Copper
	//tmp	= tmp - (tmp2* 10);
	if (updateclient)
		SendClientMoneyUpdate(0,tmp);
	m_pp.copper = m_pp.copper + tmp;
	Save();
	LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have:  plat:%i gold:%i silver:%i copper:%i", GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper);
}

void Client::AddMoneyToPP(uint32 copper, uint32 silver, uint32 gold, uint32 platinum, bool updateclient){
	if ((updateclient) && platinum!=0)
		SendClientMoneyUpdate(3, platinum);
	if ((updateclient) && gold!=0)
		SendClientMoneyUpdate(2, gold);
	if ((updateclient) && silver!=0)
		SendClientMoneyUpdate(1, silver);
	if ((updateclient) && copper!=0)
		SendClientMoneyUpdate(0, copper);
	
	m_pp.platinum += platinum;
	m_pp.gold += gold;
	m_pp.silver += silver;
	m_pp.copper += + copper;
	
	Save();
	
#if (EQDEBUG>=5)
		LogFile->write(EQEMuLog::Debug, "Client::AddMoneyToPP() %s should have:  plat:%i gold:%i silver:%i copper:%i",
			GetName(), m_pp.platinum, m_pp.gold, m_pp.silver, m_pp.copper);
#endif
}

bool Client::CheckIncreaseSkill(int16 skillid,sint16 chancemodi) {
	if (IsAIControlled()) // no skillups while chamred =p
		return false;
	if (skillid > HIGHEST_SKILL)
		return false;
	// Make sure we're not already at skill cap
	if (GetSkill(skillid) < MaxSkill(skillid))
	{
		// the higher your current skill level, the harder it is
		sint16 Chance = 10 + chancemodi + ((252 - GetSkill(skillid)) / 20);
		if (Chance < 1)
			Chance = 1; // Make it always possible
		if(((float)rand()/RAND_MAX)*100 < Chance)
		{
			SetSkill(skillid, GetSkill(skillid) + 1);
			return true;
		}
	}
	return false;
}

// Made this function to check for skill increases in skills where a linear algorithm for
// skill progression is acceptable.  For the time being using (10+level*5)
// solar: use CheckIncreaseSkill instead
/*
bool Client::SimpleCheckIncreaseSkill(int16 skillid,sint16 chancemodi){
	if (IsAIControlled()) // no skillups while chamred =p
		return false;
	if (skillid > HIGHEST_SKILL)
		return false;
	// Make sure we're not already at skill cap
	if (GetSkill(skillid) < (10+level*5) ){
		// the higher your current skill level, the harder it is
		sint16 Chance = 10 + chancemodi + ((252 - GetSkill(skillid)) / 20);
		if (Chance < 0)
			Chance = 0; // Make it always possible
		if (MakeRandomInt(0,100) < Chance){
			SetSkill(skillid,GetSkill(skillid)+1);
			return true;
		}
	}
	return false;
}
*/

#include "maxskill.h"
/*
int8 Mob::MaxSkill(int16 skillid, int16 class_, int16 level) {
	switch (skillid) {
		case OFFENSE:
		case DEFENSE: {
			int16 tmp = level * 5;
			if (tmp > 200)
				tmp = 200;
			return tmp;
		}
		default: {
			return level*4;
		}
	}
}
*/

// Place items into inventory of client specified
void Client::FinishTrade(NPC* with){
	int32 items[4]={0};
	int8 charges[4]={0};
	for (sint16 i=3000; i<=3003; i++){
		const ItemInst* inst = m_inv[i];
		if (inst) {
			items[i-3000]=inst->GetItem()->ItemNumber;
			charges[i-3000]=inst->GetCharges();
			DeleteItemInInventory(i);
		}
	}
	char temp1[100];
	memset(temp1,0x0,100);
	char temp2[100];
	memset(temp2,0x0,100);
	for ( int z=0; z < 4; z++ ) {
		sprintf(temp1,"item%d.%d", z+1,with->GetNPCTypeID());
		sprintf(temp2,"%d",items[z]);
		parse->AddVar(temp1,temp2);
		memset(temp1,0x0,100);
		memset(temp2,0x0,100);
	}
	sprintf(temp1,"copper.%d",with->GetNPCTypeID());
	sprintf(temp2,"%i",trade->cp);
	parse->AddVar(temp1,temp2);
	memset(temp1,0x0,100);
	memset(temp2,0x0,100);
	sprintf(temp1,"silver.%d",with->GetNPCTypeID());
	sprintf(temp2,"%i",trade->sp);
	parse->AddVar(temp1,temp2);
	memset(temp1,0x0,100);
	memset(temp2,0x0,100);
	sprintf(temp1,"gold.%d",with->GetNPCTypeID());
	sprintf(temp2,"%i",trade->gp);
	parse->AddVar(temp1,temp2);
	memset(temp1,0x0,100);
	memset(temp2,0x0,100);
	sprintf(temp1,"platinum.%d",with->GetNPCTypeID());
	sprintf(temp2,"%i",trade->pp);
	parse->AddVar(temp1,temp2);
	memset(temp1,0x0,100);
	memset(temp2,0x0,100);
	parse->Event(EVENT_ITEM, with->GetNPCTypeID(), 0, with, this->CastToMob());
	LinkedListIterator<ServerLootItem_Struct*> iterator(*with->CastToNPC()->itemlist);
	iterator.Reset();
	int xy = 0;
	while(iterator.MoreElements()) {
		xy++;
		iterator.Advance();
	}
	
	for(int y=0;y<4;y++){
		if (xy <20){
			xy++;
			NPC* npc=with->CastToNPC();
			const Item_Struct* item2 = database.GetItem(items[y]);
			if (item2) { //no "no drop" items for j00!
				ServerLootItem_Struct* item = new ServerLootItem_Struct;
				item->item_id = item2->ItemNumber;
				item->charges = charges[y];
				char newid[20];
				memset(newid, 0, sizeof(newid));
				for(int i=0;i<7;i++){
					if (!isalpha(item2->IDFile[i])){
						strncpy(newid, &item2->IDFile[i],5);
						i=8;
					}
				}
				APPLAYER* outapp = new APPLAYER(OP_WearChange, sizeof(WearChange_Struct));
	 			WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer;
	 			wc->spawn_id = npc->GetID();
				wc->material=0;
				if (((item2->EquipSlots==24576) || (item2->EquipSlots==8192)) && (npc->d_meele_texture1==0)) {
					wc->wear_slot_id=7;
					if (item2->Common.SpellId!=0)
						npc->CastToMob()->AddProcToWeapon(item2->Common.SpellId,true);
					npc->equipment[7]=item2->ItemNumber;
					npc->d_meele_texture1=atoi(newid);
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}
				else if (((item2->EquipSlots==24576) || (item2->EquipSlots==16384)) && (npc->d_meele_texture2 ==0) && ((npc->GetLevel()>=13) || (item2->Common.Damage==0))) 
				{
					if (item2->Common.SpellId!=0)
						npc->CastToMob()->AddProcToWeapon(item2->Common.SpellId,true);
					npc->d_meele_texture2=atoi(newid);
					npc->equipment[8]=item2->ItemNumber;
					wc->wear_slot_id=8;
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}
				else if ((item2->EquipSlots==4) && (npc->equipment[0]==0)){

					npc->equipment[0]=atoi(newid);
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					wc->wear_slot_id=0;
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}
				else if ((item2->EquipSlots==131072) && (npc->equipment[1]==0)){
					npc->equipment[1]=atoi(newid);
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					wc->wear_slot_id=1;
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}
				else if ((item2->EquipSlots==128) && (npc->equipment[2]==0)){
					npc->equipment[2]=atoi(newid);
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					wc->wear_slot_id=2;
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;

					npc->INT+=item2->Common.INT;
				}
				else if ((item2->EquipSlots==1536) && (npc->equipment[3]==0)){
					npc->equipment[3]=atoi(newid);
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					wc->wear_slot_id=3;
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}

				else if ((item2->EquipSlots==4096) && (npc->equipment[4]==0)){
					npc->equipment[4]=atoi(newid);
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					wc->wear_slot_id=4;
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}
				else if ((item2->EquipSlots==262144) && (npc->equipment[5]==0)){
					npc->equipment[5]=atoi(newid);
					if (item2->Common.Material >0)

						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					wc->wear_slot_id=5;
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}
				else if ((item2->EquipSlots==524288) && (npc->equipment[6]==0)){
					npc->equipment[6]=atoi(newid);
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					wc->wear_slot_id=6;
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;


					npc->INT+=item2->Common.INT;
				}
				if (((npc->GetRace()==127) && (npc->CastToMob()->GetOwnerID()!=0)) && (item2->EquipSlots==24576) || (item2->EquipSlots==8192) || (item2->EquipSlots==16384)){
					npc->d_meele_texture2=atoi(newid);
					wc->wear_slot_id=8;
					if (item2->Common.Material >0)
						wc->material=item2->Common.Material;
					else
						wc->material=atoi(newid);
					npc->AC+=item2->Common.AC;
					npc->STR+=item2->Common.STR;
					npc->INT+=item2->Common.INT;
				}
				item->equipSlot = item2->EquipSlots;
				if(item2->NoDrop != 0 || this->GetGM())
					(*npc->itemlist).Append(item);
	 			entity_list.QueueClients(this, outapp);
	 			safe_delete(outapp);
			}
		}
	}

}
void Client::FinishTrade(Client* other)
{
	sint16 slot_id;
	if (!other)
		return;
	// Move each trade slot into free inventory slot
	for (sint16 i=3000; i<=3007; i++){
		const ItemInst* inst = m_inv[i];
		
		if (inst) {
			slot_id = other->GetInv().FindFreeSlot(inst->IsType(ItemTypeContainer), true);
			
			if (other->PutItemInInventory(slot_id, *inst, true))
				this->DeleteItemInInventory(i);
		}
	}
	
	// Money @merth: look into how NPC's receive cash
	this->AddMoneyToPP(other->trade->cp, other->trade->sp, other->trade->gp, other->trade->pp, true);
	
	// Clear trade inventory
	trade->Reset();
}

void Client::SetPVP(bool toggle) {
	if(toggle) {
		m_pp.pvp = 1;
		Save();
		this->SimpleMessage_StringID(13,PVP_ON,0);
	}
	else {
		m_pp.pvp = 0;
		Save();
		Message(13, "You no longer follow the ways of discord.");
	}
	SendAppearancePacket(4, GetPVP());
}

bool Database::CheckGuildDoor(int8 doorid,int16 guildid,const char* zone) {
	MYSQL_ROW row;
	char errbuf[MYSQL_ERRMSG_SIZE];
	char *query = 0;
    MYSQL_RES *result;
	if (!RunQuery(query, MakeAnyLenString(&query, "SELECT guild FROM doors where doorid=%i AND zone='%s'",doorid-128, zone), errbuf, &result)) {
		cerr << "Error in CheckUsedName query '" << query << "' " << errbuf << endl;
		if (query != 0)
			safe_delete_array(query);
		return false;
	}
	else { 
		if (mysql_num_rows(result) == 1) {
			row = mysql_fetch_row(result);
			if (atoi(row[0]) == guildid)
			{
				mysql_free_result(result);
				return true;
			}
			else
			{
				mysql_free_result(result);
				return false;
			}
			
			// code below will never be reached
			mysql_free_result(result);
			return false;
		}
	}
	return false;
}

bool Database::SetGuildDoor(int8 doorid,int16 guildid, const char* zone) {
	char errbuf[MYSQL_ERRMSG_SIZE];
	char *query = 0;
	int32	affected_rows = 0;
	if (doorid > 127)
		doorid = doorid - 128;
	if (!RunQuery(query, MakeAnyLenString(&query, "UPDATE doors SET guild = %i WHERE (doorid=%i) AND (zone='%s')",guildid,doorid, zone), errbuf, 0,&affected_rows)) {
		cerr << "Error in SetGuildDoor query '" << query << "' " << errbuf << endl;
		return false;
	}
	
	safe_delete_array(query);
	
	if (affected_rows == 0)
	{
		return false;
	}
	
	return true;
}

void Client::WorldKick() {
	APPLAYER* outapp = new APPLAYER(OP_GMKick, sizeof(GMKick_Struct));
	GMKick_Struct* gmk = (GMKick_Struct *)outapp->pBuffer;
	strcpy(gmk->name,GetName());
	QueuePacket(outapp);
	safe_delete(outapp);
	Kick();
}

void Client::GMKill() {
	APPLAYER* outapp = new APPLAYER(OP_GMKill, sizeof(GMKill_Struct));
	GMKill_Struct* gmk = (GMKill_Struct *)outapp->pBuffer;
	strcpy(gmk->name,GetName());
	QueuePacket(outapp);
	safe_delete(outapp);
}

bool Client::CheckAccess(sint16 iDBLevel, sint16 iDefaultLevel) {
	if ((admin >= iDBLevel) || (iDBLevel == 255 && admin >= iDefaultLevel))
		return true;
	else
		return false;
}

void Client::SendAAStats() {
	APPLAYER* outapp = new APPLAYER(OP_SendAAStats, sizeof(AltAdvStats_Struct));
	AltAdvStats_Struct *aps = (AltAdvStats_Struct *)outapp->pBuffer;
	aps->experience = m_pp.expAA;
	aps->experience = (int32)(((float)330.0f * (float)m_pp.expAA) / (float)max_AAXP);
	aps->unspent = m_pp.aapoints;
	aps->percentage = m_pp.perAA;
	QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::MemorizeSpell(int32 slot,int32 spellid,int32 scribing){
	APPLAYER* outapp = new APPLAYER(OP_MemorizeSpell,sizeof(MemorizeSpell_Struct));
	MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer;
	mss->scribing=scribing;
	mss->slot=slot;
	mss->spell_id=spellid;
	QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::SendAATimer(UseAA_Struct *uaa){
	APPLAYER* outapp = new APPLAYER(OP_AAAction,sizeof(UseAA_Struct));
	UseAA_Struct* uaaout = (UseAA_Struct*)outapp->pBuffer;
	uaaout->ability=uaa->ability;
	uaaout->begin=uaa->begin;
	uaaout->end=uaa->end;
	QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::ActivateAA(int activate){
	int32 timermod=0;
	switch(activate){
		case 50:
			MemorizeSpell(8,2750,3);
			CastToMob()->CastSpell(2750,this->GetID());
			timermod=7200;
			break;
		case 51:
			MemorizeSpell(8,2751,3);
			CastToMob()->CastSpell(2751,target->GetID());
			timermod=112;
			break;
		case 80:
			MemorizeSpell(8,2765,3);
			CastToMob()->CastSpell(2765,this->GetID());
			timermod=7;
			break;
	}
	time_t timestamp=time(NULL);
	
	APPLAYER* outapp = new APPLAYER(OP_AAAction,sizeof(UseAA_Struct));
	UseAA_Struct* uaa = (UseAA_Struct*)outapp->pBuffer;
	uaa->ability=activate;
	uaa->begin=timestamp;
	uaa->end=timestamp;
	database.UpdateAATimers(this->CharacterID(),timestamp+timermod,timestamp,activate);
	QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::SendAATable() {
    APPLAYER* outapp = new APPLAYER(OP_RespondAA, sizeof(PlayerAA_Struct));
    memcpy(outapp->pBuffer,&aa,outapp->size);
	outapp->Deflate();
    QueuePacket(outapp);
    safe_delete(outapp);
}

bool Client::LootToStack(int32 itemid) {  //Loots stackable items to existing stacks - Wiz
	// @merth: Need to do loot code with new inventory struct
	/*
	const Item_Struct* item;
	int i;
	for (i=22; i<=29; i++) {
		item = GetItemAt(i);
		if (item) {
			if (m_pp.invitemproperties[i].charges < 20 && item->ItemNumber == itemid)
			{
				m_pp.invitemproperties[i].charges += 1;
				APPLAYER* outapp = new APPLAYER(OP_PlaceItem, sizeof(Item_Struct));
				memcpy(outapp->pBuffer, item, outapp->size);
				Item_Struct* outitem = (Item_Struct*) outapp->pBuffer;
				outitem->equipSlot = i;
				outitem->common.charges = m_pp.invitemproperties[i].charges;
				QueuePacket(outapp);
				safe_delete(outapp);
				return true;
			}
		}
	}
	for (i=0; i<=pp_containerinv_size; i++) {
		if (m_pp.containerinv[i] != 0xFFFF) {
			item = database.GetItem(m_pp.containerinv[i]);
			if (m_pp.bagitemproperties[i].charges < 20 && item->ItemNumber == itemid)
			{
				m_pp.bagitemproperties[i].charges += 1;

				APPLAYER* outapp = new APPLAYER(OP_PlaceItem, sizeof(Item_Struct));
				memcpy(outapp->pBuffer, item, outapp->size);
				Item_Struct* outitem = (Item_Struct*) outapp->pBuffer;
				outitem->equipSlot = 250+i;
				outitem->common.charges = m_pp.bagitemproperties[i].charges;
				QueuePacket(outapp);
				safe_delete(outapp);
				return true;
			}
		}
	}
	*/
	return false;
}

void Client::SetFeigned(bool in_feigned) {
	if (in_feigned)
	{
		SetPet(0);
		entity_list.ClearFeignAggro(this);
	}
	feigned=in_feigned;
 }

sint16	Client::GetMR()
{
    return 20 + itembonuses->MR + spellbonuses->MR + aa.general_skills.named.innate_magic_protection * 5;
}

sint16	Client::GetFR()
{
    return 20 + itembonuses->FR + spellbonuses->FR + aa.general_skills.named.innate_fire_protection * 5;
}

sint16	Client::GetDR()
{
    return 20 + itembonuses->DR + spellbonuses->DR + aa.general_skills.named.innate_disease_protection * 5;
}

sint16	Client::GetPR()
{
    return 20 + itembonuses->PR + spellbonuses->PR + aa.general_skills.named.innate_poison_protection * 5;
}

sint16	Client::GetCR()
{
    return 20 + itembonuses->CR + spellbonuses->CR + aa.general_skills.named.innate_cold_protection * 5;
}

void Client::LogMerchant(Client* player, Mob* merchant, Merchant_Sell_Struct* mp, const Item_Struct* item, bool buying)
{
	Merchant_Purchase_Struct* mps = new Merchant_Purchase_Struct;
	mps->itemslot=mp->itemslot;
	mps->npcid=mp->npcid;
	mps->price=mp->price;
	mps->quantity=mp->quantity;
	LogMerchant(player,merchant,mps,item,buying);
	safe_delete(mps);
}
void Client::LogMerchant(Client* player, Mob* merchant, Merchant_Purchase_Struct* mp, const Item_Struct* item, bool buying)
{
	char* logtext;
	char itemid[100];
	char itemcost[100];
	char itemname[100];
	char itemquantity[100];
	if (buying==true) {
		memset(itemid,0,sizeof(itemid));
		memset(itemcost,0,sizeof(itemid));
		memset(itemname,0,sizeof(itemid));
		memset(itemquantity,0,sizeof(itemid));
		itoa(mp->quantity,itemquantity,10);
		itoa(item->ItemNumber,itemid,10);
		sprintf(itemname,"%s",item->Name);
//		itoa(mp->price,itemcost,10);
		logtext=itemname;
		strcat(logtext,"(");
		strcat(logtext,itemid);
		strcat(logtext,"), Quantity: ");
		strcat(logtext,itemquantity);
		strcat(logtext,", Cost: ");
		strcat(logtext,itemcost);
		database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Buying from Merchant",logtext,2);
	}
	else {
		memset(itemid,0,sizeof(itemid));
		memset(itemcost,0,sizeof(itemid));
		memset(itemname,0,sizeof(itemid));
		memset(itemquantity,0,sizeof(itemid));
		itoa(mp->quantity,itemquantity,10);
		itoa(item->ItemNumber,itemid,10);
		sprintf(itemname,"%s",item->Name);
		// @merth: struct change broke this
		/*
		itoa((int)(item->cost*((mp->quantity == 0) ? 1:mp->quantity))-(item->cost * ((mp->quantity == 0) ? 1:mp->quantity) *0.08),itemcost,10);
		*/
		logtext=itemname;
		strcat(logtext,"(");
		strcat(logtext,itemid);
		strcat(logtext,"), Quantity: ");
		strcat(logtext,itemquantity);
		strcat(logtext,", Sell Total: ");
		strcat(logtext,itemcost);
		database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),merchant->GetName(),"Selling to Merchant",logtext,3);
	}
}

void Client::LogLoot(Client* player, Corpse* corpse, const Item_Struct* item){
	char* logtext;
	char itemid[100];
	char itemname[100];
	char coinloot[100];
	if (item!=0){
		memset(itemid,0,sizeof(itemid));
		memset(itemname,0,sizeof(itemid));
		itoa(item->ItemNumber,itemid,10);
		sprintf(itemname,"%s",item->Name);
		logtext=itemname;
		
		strcat(logtext,"(");
		strcat(logtext,itemid);
		strcat(logtext,") Looted");
		database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),corpse->orgname,"Looting Item",logtext,4);
	}
	else{
		if ((corpse->GetPlatinum() + corpse->GetGold() + corpse->GetSilver() + corpse->GetCopper())>0) {
			memset(coinloot,0,sizeof(coinloot));
			sprintf(coinloot,"%i PP %i GP %i SP %i CP",corpse->GetPlatinum(),corpse->GetGold(),corpse->GetSilver(),corpse->GetCopper());
			logtext=coinloot;
			strcat(logtext," Looted");
			if (corpse->GetPlatinum()>10000)
				database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),corpse->orgname,"Excessive Loot!",logtext,9);
			else
				database.logevents(player->AccountName(),player->AccountID(),player->admin,player->GetName(),corpse->orgname,"Looting Money",logtext,5);
		}
	}
}

bool Client::BindWound(Mob* bindmob, bool start, bool fail){
	APPLAYER* outapp = 0;
	if(!fail) {
		outapp = new APPLAYER(OP_Bind_Wound, sizeof(BindWound_Struct));
		BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer;
		// Start bind
		if(!bindwound_timer->Enabled()) {
			// start complete timer
			bindwound_timer->Start(10000);
			bindwound_target = bindmob;

			// Send client unlock
			bind_out->type = 3;
			QueuePacket(outapp);
			bind_out->type = 0;
			// Client Unlocked
			if(!bindmob) {
				// send "bindmob dead" to client
				bind_out->type = 4;
				QueuePacket(outapp);
				bind_out->type = 0;
				bindwound_timer->Disable();
				bindwound_target = 0;
			}
			else {
					// send bindmob "stand still"
					if(!bindmob->IsAIControlled() && bindmob != this ) {
						bind_out->type = 2; // ?
						//bind_out->type = 3; // ?
						bind_out->to = GetID(); // ?
						bindmob->CastToClient()->QueuePacket(outapp);
						bind_out->type = 0;
						bind_out->to = 0;
					}
					else if (bindmob->IsAIControlled() && bindmob != this ){
						; // Tell IPC to stand still?
					}
					else {
						; // Binding self
					}
			}
		}
		else if (bindwound_timer->Enabled()){
		// finish bind
			// disable complete timer
			bindwound_timer->Disable();
			bindwound_target = 0;
			if(!bindmob){
					// send "bindmob gone" to client
					bind_out->type = 5; // not in zone
					QueuePacket(outapp);
					bind_out->type = 0;
			}

			else {
				if (bindmob->Dist(*this) <= 20) {
					// send bindmob bind done 
					if(!bindmob->IsAIControlled() && bindmob != this ) {
		            
					}
					else if(bindmob->IsAIControlled() && bindmob != this ) {
					// Tell IPC to resume??
					}
					else {
					// Binding self
					}
					// Send client bind done
					DeleteItemInInventory(m_inv.HasItem(13009, 1), 1, true);
					bind_out->type = 1; // Done
					QueuePacket(outapp);
					bind_out->type = 0;
					CheckIncreaseSkill(BIND_WOUND);
					
					float max_percent = 0.5f;
					uint8 *aa_item = &(((uint8 *)&aa)[14]);
					if (*aa_item){
						max_percent += 0.1f * (float) *aa_item;
					}
					
					// send bindmob new hp's
					if (bindmob->GetHP() < bindmob->GetMaxHP() && bindmob->GetHP() <= (bindmob->GetMaxHP()*max_percent)-1){
						// 0.120 per skill point, 0.60 per skill level, minimum 3 max 30
						int bindhps = 3;

						if (GetSkill(BIND_WOUND) >= 10) {
							bindhps += (int) GetSkill(BIND_WOUND)*0.120;
						}
						if (bindhps > 30){
							bindhps = 30;
						}
						bindmob->SetHP( bindmob->GetHP() + bindhps);
						if(!IsFullHP || cur_hp<max_hp){
							APPLAYER* hpapp = new APPLAYER(OP_HPUpdate, sizeof(SpawnHPUpdate_Struct));
							bindmob->CreateHPPacket(hpapp);
							entity_list.QueueClientsByTarget(this, hpapp,false);
								//TODO: Queue Group Clients
							safe_delete(hpapp);
						}
					}
					else {
						// Too many hp message goes here.
					}
				}
				else {
					// Send client bind failed
					bind_out->type = 6; // They moved
					QueuePacket(outapp);
					bind_out->type = 0;
				}
			}
		}
	}
	else if (fail && bindwound_timer->Enabled()) {
		// You moved
		outapp = new APPLAYER(OP_Bind_Wound, sizeof(BindWound_Struct));
		BindWound_Struct* bind_out = (BindWound_Struct*) outapp->pBuffer;
		bindwound_timer->Disable();
		bindwound_target = 0;
		bind_out->type = 7;
		QueuePacket(outapp);
		bind_out->type = 3;
		QueuePacket(outapp);
	}
	safe_delete(outapp);
	return true;
}

void Client::SetMaterial(sint16 in_slot, uint32 item_id){
	const Item_Struct* item = database.GetItem(item_id);
	if (item && (item->Type==ItemTypeCommon)) {
		if (in_slot==SLOT_HEAD)
			m_pp.item_material[MATERIAL_HEAD]		= item->Common.Material;
		else if (in_slot==SLOT_CHEST)
			m_pp.item_material[MATERIAL_CHEST]		= item->Common.Material;
		else if (in_slot==SLOT_ARMS)
			m_pp.item_material[MATERIAL_ARMS]		= item->Common.Material;
		else if (in_slot==SLOT_BRACER01)
			m_pp.item_material[MATERIAL_BRACER]		= item->Common.Material;
		else if (in_slot==SLOT_BRACER02)
			m_pp.item_material[MATERIAL_BRACER]		= item->Common.Material;
		else if (in_slot==SLOT_HANDS)
			m_pp.item_material[MATERIAL_HANDS]		= item->Common.Material;
		else if (in_slot==SLOT_LEGS)
			m_pp.item_material[MATERIAL_LEGS]		= item->Common.Material;
		else if (in_slot==SLOT_FEET)
			m_pp.item_material[MATERIAL_FEET]		= item->Common.Material;
		else if (in_slot==SLOT_PRIMARY)
			m_pp.item_material[MATERIAL_PRIMARY]	= atoi(item->IDFile+2);
		else if (in_slot==SLOT_SECONDARY)
			m_pp.item_material[MATERIAL_SECONDARY]	= atoi(item->IDFile+2);
	}
}

void Client::SetTint(sint16 in_slot, uint32 color) {
	Color_Struct new_color;
	new_color.color = color;
	SetTint(in_slot, new_color);
}
void Client::SimpleMessage_StringID(uint32 type,int32 string_id,int32 distance){
	APPLAYER* outapp = new APPLAYER(OP_SimpleMessage,12);
	SimpleMessage_Struct* sms = (SimpleMessage_Struct*)outapp->pBuffer;
	sms->color=type;
	sms->string_id=string_id;
	sms->unknown8=0;
	if(distance>0)
		entity_list.QueueCloseClients(this,outapp,false,distance);
	else
		this->QueuePacket(outapp);
	safe_delete(outapp);
}
void Client::ServerFilter(SetServerFilter_Struct* filter){
	ClientFilters[FILTER_DAMAGESHIELD]=filter->damageshield;
	ClientFilters[FILTER_NPCSPELLS]=filter->npcspells;
	if(filter->pcspells==0)
		ClientFilters[FILTER_PCSPELLS]=1; //all pc spells on
	else if(filter->pcspells==1)
		ClientFilters[FILTER_PCSPELLS]=0; //pc spells off
	else
		ClientFilters[FILTER_PCSPELLS]=99;//group pc spells on
	if(filter->bardsongs==0 || filter->bardsongs==1)
		ClientFilters[FILTER_BARDSONGS]=1;
	else if(filter->bardsongs==2)//group
		ClientFilters[FILTER_BARDSONGS]=99;
	else	
		ClientFilters[FILTER_BARDSONGS]=0;//turn off all pc bard songs
	ClientFilters[FILTER_GUILDSAY]=filter->guildsay;
	ClientFilters[FILTER_SOCIALS]=filter->socials;
	ClientFilters[FILTER_GROUP]=filter->group;
	ClientFilters[FILTER_SHOUT]=filter->shout;
	ClientFilters[FILTER_AUCTION]=filter->auction;
	ClientFilters[FILTER_OOC]=filter->ooc;
	ClientFilters[FILTER_MYMISSES]=filter->mymisses;
	ClientFilters[FILTER_OTHERMISSES]=filter->othermisses;
	ClientFilters[FILTER_OTHERHITS]=filter->otherhits;
	ClientFilters[FILTER_ATKMISSESME]=filter->atkmissesme;
	if(filter->critspells==0)
		ClientFilters[FILTER_CRITSPELLS]=1;//all
	else if(filter->critspells==1)
		ClientFilters[FILTER_CRITSPELLS]=98; //me only
	else
		ClientFilters[FILTER_CRITSPELLS]=0;//off
	if(filter->critmelee==0)
		ClientFilters[FILTER_CRITMELEE]=1;//all
	else if(filter->critmelee==1)
		ClientFilters[FILTER_CRITMELEE]=98;//me only
	else
		ClientFilters[FILTER_CRITMELEE]=0;//off
	if(filter->spelldamage==0)
		ClientFilters[FILTER_SPELLDAMAGE]=1;//all
	else if(filter->spelldamage==1)
		ClientFilters[FILTER_SPELLDAMAGE]=98;//me only
	else
		ClientFilters[FILTER_SPELLDAMAGE]=0;//off
	ClientFilters[FILTER_DOTDAMAGE]=filter->dotdamage;
	ClientFilters[FILTER_MYPETHITS]=filter->mypethits;
	ClientFilters[FILTER_MYPETMISSES]=filter->mypetmisses;
}
void Client::Message_StringID(uint32 type,int32 string_id,const char* message,const char* message2,const char* message3,const char* message4,const char* message5,const char* message6,const char* message7,const char* message8,const char* message9,int32 distance){
	const char* messagearray[9]={0};
	if(type==MT_Emote)
		type=4;
	messagearray[0]=message;
	messagearray[1]=message2;
	messagearray[2]=message3;
	messagearray[3]=message4;
	messagearray[4]=message5;
	messagearray[5]=message6;
	messagearray[6]=message7;
	messagearray[7]=message8;
	messagearray[8]=message9;
	int length=0;
	int i=0;
	for(i=0;i<9;i++){
		if(messagearray[i])
			length+=strlen(messagearray[i])+1;
	}
	APPLAYER* outapp = new APPLAYER(OP_FormattedMessage,length+13);
	uchar *bufptr=outapp->pBuffer;
	memset(bufptr,0,outapp->size);
	bufptr+=sizeof(int32);
	memcpy(bufptr,&string_id, sizeof(int32));
	bufptr+=sizeof(int32);
	memcpy(bufptr,&type, sizeof(int32));
	bufptr+=sizeof(int32);
	for(i=0;i<9;i++){
		if(messagearray[i]){
			strcpy((char*)bufptr,messagearray[i]);
			bufptr+=strlen(messagearray[i])+1;
		}
	}
	if(distance>0)
		entity_list.QueueCloseClients(this,outapp,false,distance);
	else
		this->QueuePacket(outapp);
	safe_delete(outapp);
	//free(buffer);
}
// @merth: Still need to reconcile bracer01 versus bracer02
void Client::SetTint(sint16 in_slot, Color_Struct& color) {
	switch(in_slot){
		case SLOT_HEAD:
			m_pp.item_tint[MATERIAL_HEAD].color=color.color;
			break;
		case SLOT_ARMS:
			m_pp.item_tint[MATERIAL_ARMS].color=color.color;
			break;
		case SLOT_BRACER01:
			m_pp.item_tint[MATERIAL_BRACER].color=color.color;
			break;
		case SLOT_BRACER02:
			m_pp.item_tint[MATERIAL_BRACER].color=color.color;
			break;
		case SLOT_HANDS:
			m_pp.item_tint[MATERIAL_HANDS].color=color.color;
			break;
		case SLOT_PRIMARY:
			m_pp.item_tint[MATERIAL_PRIMARY].color=color.color;
			break;
		case SLOT_SECONDARY:
			m_pp.item_tint[MATERIAL_SECONDARY].color=color.color;
			break;
		case SLOT_CHEST:
			m_pp.item_tint[MATERIAL_CHEST].color=color.color;
			break;
		case SLOT_LEGS:
			m_pp.item_tint[MATERIAL_LEGS].color=color.color;
			break;
		case SLOT_FEET:
			m_pp.item_tint[MATERIAL_FEET].color=color.color;
	}
}

// Moves items around both internally and in the database
// In the future, this can be optimized by pushing all changes through one database REPLACE call
bool Client::SwapItem(MoveItem_Struct* move_in) {
	if (move_in->from_slot == move_in->to_slot)
		return true; // Item summon, no further proccessing needed
	
	if (move_in->to_slot == SLOT_INVALID) {
		DeleteItemInInventory(move_in->from_slot);
		if(move_in->from_slot==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
						DeleteItemInInventory(8000+ndx);//delete the source item
					}
					else{//item is on cursor now
						DeleteItemInInventory(8000+ndx-1);//delete from destination
						m_inv.SwapItem(8000+ndx,8000+ndx-1);//move items ahead in the que
					}
				}
			}
		}
		database.SaveInventory(character_id, m_inv[move_in->to_slot], move_in->to_slot);
		database.SaveInventory(character_id, m_inv[move_in->from_slot], move_in->from_slot);
		return true; // Item deletetion
	}
	if(auto_attack && (move_in->from_slot == SLOT_PRIMARY || move_in->from_slot == SLOT_SECONDARY))
		SetAttackTimer();
	else if(auto_attack && (move_in->to_slot == SLOT_PRIMARY || move_in->to_slot == SLOT_SECONDARY))
		SetAttackTimer();
	// Step 1: Variables
	sint16 src_slot_id = (sint16)move_in->from_slot;
	sint16 dst_slot_id = (sint16)move_in->to_slot;
	
	//Setup world containers
	uint32 srcitemid = 0;
	uint32 dstitemid = 0;
	ItemInst* src_inst = m_inv.GetItem(src_slot_id);
	ItemInst* dst_inst = m_inv.GetItem(dst_slot_id);
	if (src_inst){
		srcitemid = src_inst->GetItem()->ItemNumber;
		SetTint(dst_slot_id,src_inst->GetColor());
	}
	if (dst_inst)
		dstitemid = dst_inst->GetItem()->ItemNumber;
	if (Trader && srcitemid>0){
		ItemInst* srcbag;
		uint32 srcbagid =0;
		if (src_slot_id>=250 && src_slot_id<330){
			srcbag=m_inv.GetItem(((int)(src_slot_id/10))-3);
			if(srcbag)
				srcbagid=srcbag->GetItem()->ItemNumber;
		}
		
		if (srcitemid==17899 || srcbagid==17899){
			this->Trader_EndTrader();
			this->Message(15,"You cannot move items while trading!");
		}
	}
	
	// Step 2: Validate item in from_slot
	// After this, we can assume src_inst is a valid ptr
	if (!src_inst && (src_slot_id<4000 || src_slot_id>4009) ) {
		Message(13, "Error: Server found no item in slot %i, Deleting Item!", src_slot_id);
		this->DeleteItemInInventory(dst_slot_id,0,true);
		return false;
	}
	
	// Step 3: Check for interaction with World Container (tradeskills)
	if (src_slot_id>=4000 && src_slot_id<=4009 && m_tradeskill_object!=NULL) {
		// Picking up item from world container
		ItemInst* inst = m_tradeskill_object->PopItem(Inventory::CalcBagIdx(src_slot_id));
		if (inst) {
			PutItemInInventory(dst_slot_id, *inst, false);
			safe_delete(inst);
		}
		
		return true;
	}
	else if (dst_slot_id>=4000 && dst_slot_id<=4009 && m_tradeskill_object!=NULL) {
		// Putting item into world container, which may swap (or pile onto) with existing item
		uint8 world_idx = Inventory::CalcBagIdx(dst_slot_id);
		ItemInst* world_inst = m_tradeskill_object->PopItem(world_idx);
		
		// Case 1: No item in container, unidirectional "Put"
		if (world_inst == NULL) {
			m_tradeskill_object->PutItem(world_idx, src_inst);
			m_inv.DeleteItem(src_slot_id);
		}
		else {
			const Item_Struct* world_item = world_inst->GetItem();
			const Item_Struct* src_item = src_inst->GetItem();
			if (world_item && (int32)world_item!=0xFEEEFEEE && src_item) {
				// Case 2: Same item on cursor, stacks, transfer of charges needed
				if ((world_item->ItemNumber == src_item->ItemNumber) && src_inst->IsStackable()) {
					sint8 world_charges = world_inst->GetCharges();
					sint8 src_charges = src_inst->GetCharges();
					
					// Fill up destination stack as much as possible
					world_charges += src_charges;
					if (world_charges > ITEM_MAX_STACK) {
						src_charges = world_charges - ITEM_MAX_STACK;
						world_charges = ITEM_MAX_STACK;
					}
					else {
						src_charges = 0;
					}
					
					world_inst->SetCharges(world_charges);
					m_tradeskill_object->Save();
					
					if (src_charges == 0) {
						m_inv.DeleteItem(src_slot_id); // DB remove will occur below
					}
					else {
						src_inst->SetCharges(src_charges);
					}
				}
				else {
					// Case 3: Swap the item on user with item in world container
					// World containers don't follow normal rules for swapping
					ItemInst* inv_inst = m_inv.PopItem(src_slot_id);
					m_tradeskill_object->PutItem(world_idx, inv_inst);
					m_inv.PutItem(src_slot_id, *world_inst);
					safe_delete(inv_inst);
				}
			}
		}
		
		safe_delete(world_inst);
		database.SaveInventory(character_id, m_inv[src_slot_id], src_slot_id);
		return true;
	}
	
	// Step 4: Check for entity trade
	Mob* with = trade->With();
	if (with && dst_slot_id>=3000 && dst_slot_id<=3007) {

#if EQDEBUG>=5
			LogFile->write(EQEMuLog::Debug, "Trade: %s adding item(s) to trade session with %s", GetName(), with->GetName());
#endif		
		// Fill Trade list with items from cursor
		if (!m_inv[SLOT_CURSOR]) {
			Message(13, "Error: Cursor item not located on server!");
			return false;
		}
		
		// Add cursor item to trade bucket
		// Also sends trade information to other client of trade session
		trade->AddEntity(src_slot_id, dst_slot_id);
		return true;
	}
	
	// Step 5: Swap (or stack) items
	if (move_in->number_in_stack > 0) {
		// Determine if charged items can stack
		if ((dst_inst) && (src_inst->GetItem()==dst_inst->GetItem()) && (dst_inst->GetCharges() < 20)) {
			// Charges can be emptied into dst
			uint8 usedcharges = 20 - dst_inst->GetCharges();
			if (usedcharges > move_in->number_in_stack)
				usedcharges = move_in->number_in_stack;
			
			dst_inst->SetCharges(dst_inst->GetCharges() + usedcharges);
			src_inst->SetCharges(src_inst->GetCharges() - usedcharges);
			
			// Depleted all charges?
			if (src_inst->GetCharges() < 1)
				m_inv.DeleteItem(src_slot_id);
		}
		else {
			// Nothing in destination slot: split stack into two
			if (move_in->number_in_stack >= src_inst->GetCharges()) {
				// Move entire stack
				m_inv.SwapItem(src_slot_id, dst_slot_id);
			}
			else {
				// Split into two
				src_inst->SetCharges(src_inst->GetCharges() - move_in->number_in_stack);
				ItemInst* inst = ItemInst::Create(src_inst->GetItem(), move_in->number_in_stack);
				m_inv.PutItem(dst_slot_id, *inst);
				safe_delete(inst);
			}
		}
	}
	else {
		// Not dealing with charges - just do direct swap
		if(src_inst && dst_slot_id<22 && dst_slot_id>0)
			SetMaterial(dst_slot_id,src_inst->GetItem()->ItemNumber);
		m_inv.SwapItem(src_slot_id, dst_slot_id);
	}
	if(move_in->from_slot ==SLOT_CURSOR){
		if(!m_inv[SLOT_CURSOR]){//item on cursor is deleted, see if there is something in the cursor que
			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
					else//item is on cursor now
						m_inv.SwapItem(8000+ndx,8000+ndx-1);//move items ahead in the que
					DeleteItemInInventory(8000+ndx);//delete the source item
				}
			}
		}
	}
	// Step 7: Save change to the database
	database.SaveInventory(character_id, m_inv[src_slot_id], src_slot_id);
	database.SaveInventory(character_id, m_inv[dst_slot_id], dst_slot_id);
	
	// Step 8: Re-calc stats
	CalcBonuses();
	return true;
}
const char* Client::ConvertArray(int32 input,char* returnchar){
	sprintf(returnchar,"%i",input);
	return returnchar;
}
const char* Client::ConvertArrayF(float input,char* returnchar){
	sprintf(returnchar,"%f",input);
	return returnchar;
}
void Client::Discipline(ClientDiscipline_Struct* disc_in, Mob* tar) {
	if (disc_timer->Enabled()) {
		char val1[20]={0};
		char val2[20]={0};
		Message_StringID(0,DESCIPLINE_CANUSEIN,ConvertArray((disc_timer->GetRemainingTime()/1000)/60,val1),ConvertArray(disc_timer->GetRemainingTime()/1000%60,val2));
		//Message(0,"You can use a new discipline in %i minutes %i seconds.", (disc_timer->GetRemainingTime()/1000)/60,	disc_timer->GetRemainingTime()/1000%60);
		return;
	}
    switch(disc_in->disc_id){
	// Shared?
	case 30: { // Resistant
		// 1 minute duration
		// 1 hour reuse
		// +3 to +10 to resists 
		if (GetLevel()<=29)
			return;
		disc_timer->Start(1000*60*60);
		disc_elapse->Start(1000*60);
		entity_list.MessageClose(this, false, 100, 0, "%s has become more resistant!", GetName());
	    break;
	}
	case 31: { // Fearless
		// 11 second duration
		// 1 hour reuse
		// 100% fear immunity
		if (GetLevel()<=39)
			return;
		disc_timer->Start(1000*60*60);
		disc_elapse->Start(1000*11);
		Message_StringID(0,DESCIPLINE_FEARLESS,GetName(),0,0,0,0,0,0,0,0,100);
		//entity_list.MessageClose(this, false, 100, 0, "%s becomes fearless!", GetName());
	    break;
	}
	case 6: { // Counterattack/Whirlwind/Furious
		// warrior level 56
		// rogue/monk level 53
		// 9 second duration
		// 1 hour reuse
		if (      (GetClass() == WARRIOR && GetLevel() <= 56)
			||(GetLevel() <= 53)
			) return;
		disc_timer->Start(1000*60*60);
		disc_elapse->Start(1000*9);
		entity_list.MessageClose(this, false, 100, 0, "%s\'s face becomes twisted with fury!", GetName());
	    break;
	}
	case 14: { // Duelist/Innerflame/Fellstrike
		// monk level 56
		// rogue level 59
		// warrior level 58
		// 12 second duration
		// 30 minute reuse
		// min 4*base hand/weapon damage
		if (      (GetClass() == MONK && GetLevel() <= 55)
			||(GetClass() == WARRIOR && GetLevel() <= 58)
			||(GetClass() == ROGUE && GetLevel() <= 59)
			) return;
		disc_timer->Start(1000*60*30);
		disc_elapse->Start(1000*12);
		entity_list.MessageClose(this, false, 100, 0, "%s\'s muscles bulge with force of will!", GetName());
	    break;
	}
	case 15: { // Blindingspeed/Hundredfist
		// rogue level 58
		// monk level 57
		// 15 second duration
		// 30 minute reuse
		if (      (GetClass() == MONK && GetLevel() <= 58)
			||(GetClass() == ROGUE && GetLevel() <= 57)
			) return;
		//disc_timer->Start(1000*60*30);
		//disc_elapse->Start(1000*15);
		Message(0, "This discipline not implemented..");
	    break;
	}
	case 16: { // Deadeye/Charge
		// warrior level 53
		// rogue level 54
		// 14 second duration
		// 30 minute reuse
		if (      (GetClass() == WARRIOR && GetLevel() <= 53)
			||(GetClass() == ROGUE && GetLevel() <= 54)
			) return;
		disc_timer->Start(1000*60*30);
		disc_elapse->Start(1000*14);
		entity_list.MessageClose(this, false, 100, 0, "%s feels unstopable!", GetName());
	    break;
	}
	// Warrior
	case 4: { // Evasive
		// level 52
		// 3 minute duration
		// 15 minute reuse
		// +35% avoidance
		// -15% out
	    break;
	}
	case 17: { // Mightystrike
		// level 54
		// 10 second duration
		// 1 hour reuse
		// Auto crit
	    break;
	}
	case 3: { // Defensive
		// level 55
		// 3 minute duration
		// 15 minute reuse
		// +35% mitigation
		// -15% out
	    break;
	}
	case 2: { // Precise
		// level 57
		// 3 minute duration
		// 30 minute reuse
		// -15% avoidance
		// +35% out
	    break;
	}
	case 1: { // Aggressive
		// level 60
		// 3 minute duration
		// 27 minute reuse
		// -15% mitigation
		// +35% out
	    break;
	}
	// Monk
	case 11: { // Stonestance
	    break;
	}
	case 12: { // Thunderkick
	    break;
	}
	case 13: { // Voidance
	    break;
	}
	case 20: { // Silentfist
		// level 59
		// 9 minute reuse
		// Dragon punch damage bonus
		// Chance to stun
	    break;
	}
	case 5: { // Ashenhand
		// level 60
		// 72 minute reuse
		// Eagle Strike damage bonus
		// Chance to slay
	    break;
	}
	// Rogue
	case 19: { // Nimble
		// level 55
		// 12 second duration
		// 30 minute reuse
		// Auto dodge
	    break;
	}
	case 21: { // Kinesthetics
		// level 57
		// 18 second duration
		// 30 minute reuse
		// Auto dualwield
		// Auto double attack
	    break;
	}
	// Paladin
	case 22: { // Holyforge
		// level 55
		// 2 minute duration
		// 72 minute reuse
		// Crit/Crip undead
		// +15% to crit chance
	    break;
	}
	case 23: { // Sanctification
		// level 60
		// 10 second duration
		// 72 minute reuse
		// Spell immunity
	    break;
	}
	// Ranger
	case 24: { // Trueshot
		// level 55
		// 2 minute duration
		// 72 minute reuse
		// Max to two times max bow damage
		// +15% to hit
	    break;
	}
	case 25: { // Weaponshield
		// level 60
		// 15 second duration
		// 72 minute reuse
		// auto parry
	    break;
	}
	// Bard
	case 28: { // Deftdance
		// level 55
		// 10 second duration
		// 72 minute reuse
		// auto dodge
		// auto dualwield
	    break;
	}
	case 29: { // Puretone
		// level 60
		// 2 minute duration
		// 72 minute reuse
		// Auto instrument
	    break;
	}
	// Shadow knight
	case 26: { // Unholy
		// level 55
		// 72 minute reuse
		// +25% to harmtouch
		// -300 to resist
	    break;
	}
	case 27: { // Leech curse
		// level 60
		// 15 second duration
		// 72 minute reuse
		// Heal self for each point of melee damage done
	    break;
	}
	// Default
	case 0:{ // Timer request
		break;
	}
	default: 
	    LogFile->write(EQEMuLog::Error, "Unknown Discipline requested by client: %s class: %i Disciline:%i", GetName(), class_,disc_in->disc_id);
	    return;
    }
	disc_inuse = disc_in->disc_id;
}

uint16 Client::GetAA(uint8 aa_id){
	uint8 *aa_ = &(((uint8 *)&aa)[aa_id]);
	if (*aa_ >= 0){
		return (uint16)*aa_;

	}
	return 0;
}
int16 Client::FindItem(int32 item_id){
	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==item_id)
			return (22+i);
		else if(item && item->GetItem()->Type==1){
			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 slotid;
				}
			}
		}
	}
	return 0;
}
int16 Client::FindItem(int32 item_id,int8 max_charges){
	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==item_id) && (item->GetCharges()<max_charges))
			return (22+i);
		else if(item && item->GetItem()->Type==1){
			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()<max_charges)){
					return slotid;
				}
			}
		}
	}
	return 0;
}
int8 Client::SlotConvert(int8 slot,bool bracer){
	int8 slot2=0;
	if(bracer)
		return SLOT_BRACER02;
	switch(slot){
		case MATERIAL_HEAD:
			slot2=SLOT_HEAD;
			break;
		case MATERIAL_CHEST:
			slot2=SLOT_CHEST;
			break;
		case MATERIAL_ARMS:
			slot2=SLOT_ARMS;
			break;
		case MATERIAL_BRACER:
			slot2=SLOT_BRACER01;
			break;
		case MATERIAL_HANDS:
			slot2=SLOT_HANDS;
			break;
		case MATERIAL_LEGS:
			slot2=SLOT_LEGS;
			break;
		case MATERIAL_FEET:
			slot2=SLOT_FEET;
			break;
		}
	return slot2;
}
void Client::SendWearChange(int8 slot,bool bracer){
	APPLAYER* outapp = new APPLAYER(OP_WearChange, sizeof(WearChange_Struct));
	WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer;
	wc->spawn_id = GetID();
	wc->wear_slot_id = slot;
	wc->material = m_pp.item_material[slot];
	if(m_pp.item_tint[slot].rgb.blue!=0 && m_pp.item_tint[slot].rgb.red!=0 && m_pp.item_tint[slot].rgb.green){
		wc->blue=m_pp.item_tint[slot].rgb.blue;
		wc->red=m_pp.item_tint[slot].rgb.red;
		wc->green=m_pp.item_tint[slot].rgb.green;
	}
	else{//Player doesnt have a color set, use original item tint
		int8 slot2=0;
		if(slot==MATERIAL_BRACER && !bracer){
			SendWearChange(MATERIAL_BRACER,true);
			slot2=SlotConvert(slot,true);
		}
		else
			slot2=SlotConvert(slot);
		const ItemInst* inst = this->m_inv.GetItem(slot2);
		int32 color=0;
		if(inst && ((color=inst->GetColor())>0)){
			wc->red = (int8)(color/65536);
			color-=(65536*wc->red);
			if(color>0){
				wc->green = (int8)(color/256);
				color-=(256*wc->green);
			}
			else
				wc->green = 0;
			if(color>0)
				wc->blue = (int8)color;
			else
				wc->blue = 0;
		}
	}
	entity_list.QueueClients(this, outapp);
	safe_delete(outapp);
}
void Client::DieArmor(DyeStruct* dye){
	int16 slot=0;
	for(int i=0;i<7;i++){
		if(m_pp.item_tint[i].rgb.blue!=dye->dye[i].blue ||
			m_pp.item_tint[i].rgb.red!=dye->dye[i].red || 
			m_pp.item_tint[i].rgb.green!=dye->dye[i].green){
			slot=FindItem(32557);
			if(slot>0){
				DeleteItemInInventory(slot,1,true);
				int8 slot2=SlotConvert(i);
				ItemInst* inst = this->m_inv.GetItem(slot2);
				if(inst){
					inst->SetColor((dye->dye[i].red*65536)+(dye->dye[i].green*256)+(dye->dye[i].blue));
					database.SaveInventory(CharacterID(),inst,slot2);
				}
				m_pp.item_tint[i].rgb.blue=dye->dye[i].blue;
				m_pp.item_tint[i].rgb.red=dye->dye[i].red;
				m_pp.item_tint[i].rgb.green=dye->dye[i].green;
			}
			else{
				Message(13,"Could not locate A Vial of Prismatic Dye.");
				return;
			}
		}
	}
	APPLAYER* outapp=new APPLAYER(OP_Dye,0);
	QueuePacket(outapp);
	safe_delete(outapp);
	Save();
}
bool Client::SetAA(uint8 aa_id, uint8 new_value){
	for (int cur = 0 ; cur <= 121; cur++){
		if (m_pp.aa_array[cur].AA == 0 && m_pp.aa_array[cur].value == 0){
			m_pp.aa_array[cur].AA = aa_id;
			m_pp.aa_array[cur].value = new_value;
			return true;
		}
		else if (m_pp.aa_array[cur].AA == aa_id) {
			m_pp.aa_array[cur].value = new_value;
			return true;
		}
	}
	return false;
}
bool Client::CheckCheat(){
	float dx=cheat_x-x_pos;
	float dy=cheat_y-y_pos;
	float result=sqrt((dx*dx)+(dy*dy));
	return result>70;
}
