Пример #1
0
Файл: aa.cpp Проект: af4t/Server
int Client::GroupLeadershipAAHealthRegeneration()
{
	if (IsRaidGrouped()) {
		int bonus = 0;
		Raid *raid = GetRaid();
		if (!raid)
			return 0;
		uint32 group_id = raid->GetGroup(this);
		if (group_id < 12 && raid->GroupCount(group_id) >= 3) {
			switch (raid->GetLeadershipAA(groupAAHealthRegeneration, group_id)) {
			case 1:
				bonus = 4;
				break;
			case 2:
				bonus = 6;
				break;
			case 3:
				bonus = 8;
				break;
			}
		}
		if (raid->RaidCount() >= 18) {
			switch (raid->GetLeadershipAA(raidAAHealthRegeneration)) {
			case 1:
				bonus += 4;
				break;
			case 2:
				bonus += 6;
				break;
			case 3:
				bonus += 8;
				break;
			}
		}
		return bonus;
	}

	Group *g = GetGroup();

	if(!g || (g->GroupCount() < 3))
		return 0;

	switch(g->GetLeadershipAA(groupAAHealthRegeneration))
	{
		case 0:
			return 0;
		case 1:
			return 4;
		case 2:
			return 6;
		case 3:
			return 8;
	}

	return 0;
}
Пример #2
0
Файл: aa.cpp Проект: af4t/Server
int Client::GroupLeadershipAAManaEnhancement()
{
	if (IsRaidGrouped()) {
		int bonus = 0;
		Raid *raid = GetRaid();
		if (!raid)
			return 0;
		uint32 group_id = raid->GetGroup(this);
		if (group_id < 12 && raid->GroupCount(group_id) >= 3) {
			switch (raid->GetLeadershipAA(groupAAManaEnhancement, group_id)) {
			case 1:
				bonus = 30;
				break;
			case 2:
				bonus = 60;
				break;
			case 3:
				bonus = 100;
				break;
			}
		}
		if (raid->RaidCount() >= 18) {
			switch (raid->GetLeadershipAA(raidAAManaEnhancement)) {
			case 1:
				bonus += 30;
				break;
			case 2:
				bonus += 60;
				break;
			case 3:
				bonus += 100;
				break;
			}
		}
		return bonus;
	}

	Group *g = GetGroup();

	if(!g || (g->GroupCount() < 3))
		return 0;

	switch(g->GetLeadershipAA(groupAAManaEnhancement))
	{
		case 0:
			return 0;
		case 1:
			return 30;
		case 2:
			return 60;
		case 3:
			return 100;
	}

	return 0;
}
Пример #3
0
bool Aura::ShouldISpawnFor(Client *c)
{
	if (spawn_type == AuraSpawns::Noone)
		return false;

	if (spawn_type == AuraSpawns::Everyone)
		return true;

	// hey, it's our owner!
	if (c->GetID() == m_owner)
		return true;

	// so this one is a bit trickier
	auto owner = GetOwner();
	if (owner == nullptr)
		return false; // hmm

	owner = owner->GetOwnerOrSelf(); // pet auras we need the pet's owner
	if (owner == nullptr) // shouldn't really be needed
		return false;

	// gotta check again for pet aura case -.-
	if (owner == c)
		return true;

	if (owner->IsRaidGrouped() && owner->IsClient()) {
		auto raid = owner->GetRaid();
		if (raid == nullptr)
			return false; // hmm
		auto group_id = raid->GetGroup(owner->CastToClient());
		if (group_id == 0xFFFFFFFF) // owner handled above, and they're in a raid and groupless
			return false;

		auto idx = raid->GetPlayerIndex(c);
		if (idx == 0xFFFFFFFF) // they're not in our raid!
			return false;

		if (raid->members[idx].GroupNumber != group_id) // in our raid, but not our group
			return false;

		return true; // we got here so we know that 1 they're in our raid and 2 they're in our group!
	} else if (owner->IsGrouped()) {
		auto group = owner->GetGroup();
		if (group == nullptr)
			return false; // hmm

		// easy, in our group
		return group->IsGroupMember(c);
	}

	// our owner is not raided or grouped, and they're handled above so we don't spawn!
	return false;
}
Пример #4
0
void Client::SetLevel(uint8 set_level, bool command)
{
        if (GetEXPForLevel(set_level) == 0xFFFFFFFF) {
                LogFile->write(EQEMuLog::Error,"Client::SetLevel() GetEXPForLevel(%i) = 0xFFFFFFFF", set_level);
                return;
        }

        EQApplicationPacket* outapp = new EQApplicationPacket(OP_LevelUpdate, sizeof(LevelUpdate_Struct));
        LevelUpdate_Struct* lu = (LevelUpdate_Struct*)outapp->pBuffer;
        lu->level = set_level;
        if(m_pp.level2 != 0)
                lu->level_old = m_pp.level2;
        else
                lu->level_old = level;

        level = set_level;

        if(IsRaidGrouped())
        {
                Raid *r = this->GetRaid();
                if(r){
                        r->UpdateLevel(GetName(), set_level);
                }
        }
        if(set_level > m_pp.level2)
        {
                if(m_pp.level2 == 0)
                        m_pp.points += 5;
                else
                        m_pp.points += (5 * (set_level - m_pp.level2));

                m_pp.level2 = set_level;
        }
        if(set_level > m_pp.level) {
            parse->EventPlayer(EVENT_LEVEL_UP, this, "", 0);
        }

        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 {
                float tmpxp = (float) ( (float) m_pp.exp - GetEXPForLevel( GetLevel() )) /
                                                ( (float) GetEXPForLevel(GetLevel()+1) - GetEXPForLevel(GetLevel()));
                lu->exp =  (uint32)(330.0f * tmpxp);
    }
        QueuePacket(outapp);
        safe_delete(outapp);
        this->SendAppearancePacket(AT_WhoLevel, set_level); // who level change

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

        CalcBonuses();
        if(!RuleB(Character, HealOnLevel))
        {
                int mhp = CalcMaxHP();
                if(GetHP() > mhp)
                        SetHP(mhp);
        }
        else
        {
                SetHP(CalcMaxHP());             // Why not, lets give them a free heal
        }
		
		DoTributeUpdate();
        SendHPUpdate();
        SetMana(CalcMaxMana());
        UpdateWho();
		if(GetMerc())
			UpdateMercLevel();
        Save();
}
Пример #5
0
void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
	_log(CLIENT__EXP, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false");
	//max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51);	//GetEXPForLevel() doesn't depend on class/race, just level, so it shouldn't change between Clients
	max_AAXP = RuleI(AA, ExpPerPoint);	//this may be redundant since we're doing this in Client::FinishConnState2()
	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+m_pp.expAA)) {
		if (isrezzexp)
			this->Message_StringID(MT_Experience, REZ_REGAIN);
		else{
			if(this->IsGrouped())
				this->Message_StringID(MT_Experience, GAIN_GROUPXP);
			else if(IsRaidGrouped())
				Message_StringID(MT_Experience, GAIN_RAIDEXP);
			else
				this->Message_StringID(MT_Experience, GAIN_XP);
		}
	}
	else if((set_exp + set_aaxp) < (m_pp.exp+m_pp.expAA)){ //only loss message if you lose exp, no message if you gained/lost nothing.
		Message(15, "You have lost experience.");
	}
	
	//check_level represents the level we should be when we have
	//this ammount of exp (once these loops complete)
	uint16 check_level = GetLevel()+1;
	//see if we gained any levels
	while (set_exp >= GetEXPForLevel(check_level)) {
		check_level++;
		if (check_level > 127) {	//hard level cap
			check_level = 127;
			break;
		}
		if(GetMercID())
			UpdateMercLevel();
	}
	//see if we lost any levels
	while (set_exp < GetEXPForLevel(check_level-1)) {
		check_level--;
		if (check_level < 2) {	//hard level minimum
			check_level = 2;
			break;
		}
		if(GetMercID())
			UpdateMercLevel();
	}
	check_level--;

	
	//see if we gained any AAs
	if (set_aaxp >= max_AAXP) {
		/*
			Note: AA exp is stored differently than normal exp.
			Exp points are only stored in m_pp.expAA until you 
			gain a full AA point, once you gain it, a point is 
			added to m_pp.aapoints and the ammount needed to gain
			that point is subtracted from m_pp.expAA
			
			then, once they spend an AA point, it is subtracted from
			m_pp.aapoints. In theory it then goes into m_pp.aapoints_spent,
			but im not sure if we have that in the right spot.
		*/
		//record how many points we have
		uint32 last_unspentAA = m_pp.aapoints;
		
		//figure out how many AA points we get from the exp were setting
		m_pp.aapoints = set_aaxp / max_AAXP;
		_log(CLIENT__EXP, "Calculating additional AA Points from AAXP for %s: %u / %u = %.1f points", this->GetCleanName(), set_aaxp, max_AAXP, (float)set_aaxp / (float)max_AAXP);
		
		//get remainder exp points, set in PP below
		set_aaxp = set_aaxp - (max_AAXP * m_pp.aapoints);
		
		//add in how many points we had
		m_pp.aapoints += last_unspentAA;
		//set_aaxp = m_pp.expAA % max_AAXP;
		
		//figure out how many points were actually gained
		/*uint32 gained = m_pp.aapoints - last_unspentAA;*/	//unused
		
		//Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
		char val1[20]={0};
		Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)");	//You have gained an ability point!  You now have %1 ability point%2.
		//Message(15, "You now have %d skill points available to spend.", m_pp.aapoints);
	}

	uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1;

	if(maxlevel <= 1)
		maxlevel = RuleI(Character, MaxLevel) + 1;
	
	if(check_level > maxlevel) {
		check_level = maxlevel;

		//DCBOOKMARK
		if(RuleB(Character, KeepLevelOverMax))
		{
			set_exp = GetEXPForLevel(GetLevel()+1);
		}
		else
		{
			set_exp = GetEXPForLevel(maxlevel);
		}
	}
	
	if(RuleB(Character, PerCharacterQglobalMaxLevel)){
		uint32 MaxLevel = GetCharMaxLevelFromQGlobal();
		if(MaxLevel){
			if(GetLevel() >= MaxLevel){
				uint32 expneeded = GetEXPForLevel(MaxLevel);
				if(set_exp > expneeded)
				{
					set_exp =  expneeded;
				}
			}
		}
	}

	if ((GetLevel() != check_level) && !(check_level >= maxlevel)) {
		char val1[20]={0};
		if (GetLevel() == check_level-1){
			Message_StringID(MT_Experience, GAIN_LEVEL,ConvertArray(check_level,val1));
			SendLevelAppearance();
			//Message(15, "You have gained a level! Welcome to level %i!", check_level);
		}
		if (GetLevel() == check_level){
			Message_StringID(MT_Experience, LOSE_LEVEL,ConvertArray(check_level,val1));
			//Message(15, "You lost a level! You are now level %i!", check_level);
		}
		else
			Message(15, "Welcome to level %i!", check_level);
		SetLevel(check_level);
	}
	
	//If were at max level then stop gaining experience if we make it to the cap
	if(GetLevel() == maxlevel - 1){
		uint32 expneeded = GetEXPForLevel(maxlevel);
		if(set_exp > expneeded)
		{
			set_exp =  expneeded;
		}
	}	
	
	//set the client's EXP and AAEXP
	m_pp.exp = set_exp;
	m_pp.expAA = set_aaxp;
	
	if (GetLevel() < 51) {
		m_epp.perAA = 0;	// turn off aa exp if they drop below 51
	} else
		SendAAStats();	//otherwise, send them an AA update

	//send the expdata in any case so the xp bar isnt stuck after leveling
	uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1);
	uint32 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) {
		EQApplicationPacket* outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
		ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
		float tmpxp = (float) ( (float) set_exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 );
		eu->exp = (uint32)(330.0f * tmpxp);
		FastQueuePacket(&outapp);
	}
	
	if (admin>=100 && GetGM()) {
		char val1[20]={0};
		char val2[20]={0};
		char val3[20]={0};
		Message_StringID(MT_Experience, GM_GAINXP,ConvertArray(set_aaxp,val1),ConvertArray(set_exp,val2),ConvertArray(GetEXPForLevel(GetLevel()+1),val3));	//[GM] You have gained %1 AXP and %2 EXP (%3).
		//Message(15, "[GM] You now have %d / %d EXP and %d / %d AA exp.", set_exp, GetEXPForLevel(GetLevel()+1), set_aaxp, max_AAXP);
	}
}
Пример #6
0
void Client::SetLevel(uint8 set_level, bool command)
{
	if (GetEXPForLevel(set_level) == 0xFFFFFFFF) {
		Log.Out(Logs::General, Logs::Error, "Client::SetLevel() GetEXPForLevel(%i) = 0xFFFFFFFF", set_level);
		return;
	}

	EQApplicationPacket* outapp = new EQApplicationPacket(OP_LevelUpdate, sizeof(LevelUpdate_Struct));
	LevelUpdate_Struct* lu = (LevelUpdate_Struct*)outapp->pBuffer;
	lu->level = set_level;
	if(m_pp.level2 != 0)
		lu->level_old = m_pp.level2;
	else
		lu->level_old = level;

	level = set_level;

	if(IsRaidGrouped()) {
		Raid *r = this->GetRaid();
		if(r){
			r->UpdateLevel(GetName(), set_level);
		}
	}
	if(set_level > m_pp.level2) {
		if(m_pp.level2 == 0)
			m_pp.points += 5;
		else
			m_pp.points += (5 * (set_level - m_pp.level2));

		m_pp.level2 = set_level;
	}
	if(set_level > m_pp.level) {
		parse->EventPlayer(EVENT_LEVEL_UP, this, "", 0);
		/* QS: PlayerLogLevels */
		if (RuleB(QueryServ, PlayerLogLevels)){
			std::string event_desc = StringFormat("Leveled UP :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID());
			QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc); 
		}
	}
	else if (set_level < m_pp.level){
		/* QS: PlayerLogLevels */
		if (RuleB(QueryServ, PlayerLogLevels)){
			std::string event_desc = StringFormat("Leveled DOWN :: to Level:%i from Level:%i in zoneid:%i instid:%i", set_level, m_pp.level, this->GetZoneID(), this->GetInstanceID());
			QServ->PlayerLogEvent(Player_Log_Levels, this->CharacterID(), event_desc);
		}
	}

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

	Log.Out(Logs::General, Logs::Normal, "Setting Level for %s to %i", GetName(), set_level);

	CalcBonuses();

	if(!RuleB(Character, HealOnLevel)) {
		int mhp = CalcMaxHP();
		if(GetHP() > mhp)
			SetHP(mhp);
	}
	else {
		SetHP(CalcMaxHP()); // Why not, lets give them a free heal
	}
	if(!RuleB(Character, ManaOnLevel))
	{
		int mp = CalcMaxMana();
		if(GetMana() > mp)
			SetMana(mp);
	}
	else
	{
		SetMana(CalcMaxMana()); // Why not, lets give them a free heal
	}

	SendHPUpdate();
	SendManaUpdate();
	UpdateWho();
	Save();
}
Пример #7
0
void Aura::ProcessOnGroupMembersPets(Mob *owner)
{
	auto &mob_list = entity_list.GetMobList(); // read only reference so we can do it all inline
	std::set<int> delayed_remove;
	bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter
	// This type can either live on the pet (level 55/70 MAG aura) or on the pet owner (level 85 MAG aura)
	auto group_member = owner->GetOwnerOrSelf();

	if (group_member->IsRaidGrouped() && group_member->IsClient()) { // currently raids are just client, but safety check
		auto raid = group_member->GetRaid();
		if (raid == nullptr) { // well shit
			owner->RemoveAura(GetID(), false, true);
			return;
		}
		auto group_id = raid->GetGroup(group_member->CastToClient());

		// some lambdas so the for loop is less horrible ...
		auto verify_raid_client_pet = [&raid, &group_id, &group_member, this](Mob *m) {
			auto idx = raid->GetPlayerIndex(m->GetOwner()->CastToClient());
			if (m->GetOwner()->GetID() == group_member->GetID()) {
				return DistanceSquared(GetPosition(), m->GetPosition()) <= distance;
			} else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) {
				return false;
			} else if (DistanceSquared(GetPosition(), m->GetPosition()) > distance) {
				return false;
			}
			return true;
		};

		auto verify_raid_client_swarm = [&raid, &group_id, &group_member, this](NPC *n) {
			auto owner = entity_list.GetMob(n->GetSwarmOwner());
			if (owner == nullptr)
				return false;
			auto idx = raid->GetPlayerIndex(owner->CastToClient());
			if (owner->GetID() == group_member->GetID()) {
				return DistanceSquared(GetPosition(), n->GetPosition()) <= distance;
			} else if (idx == 0xFFFFFFFF || raid->members[idx].GroupNumber != group_id || raid->members[idx].GroupNumber == 0xFFFFFFFF) {
				return false;
			} else if (DistanceSquared(GetPosition(), n->GetPosition()) > distance) {
				return false;
			}
			return true;
		};

		for (auto &e : mob_list) {
			auto mob = e.second;
			// step 1: check if we're already managing this NPC's buff
			auto it = casted_on.find(mob->GetID());
			if (it != casted_on.end()) {
				// verify still good!
				if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) {
					if (!verify_raid_client_pet(mob))
						delayed_remove.insert(mob->GetID());
				} else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
					auto npc = mob->CastToNPC();
					if (!verify_raid_client_swarm(npc))
						delayed_remove.insert(mob->GetID());
				}
			} else { // we're not on it!
				if (mob->IsClient()) {
					continue; // never hit client
				} else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) {
					casted_on.insert(mob->GetID());
					if (is_buff)
						SpellFinished(spell_id, mob);
				} else if (mob->IsNPC() && mob->IsPetOwnerClient()) {
					auto npc = mob->CastToNPC();
					if (verify_raid_client_swarm(npc)) {
						casted_on.insert(mob->GetID());
						if (is_buff)
							SpellFinished(spell_id, mob);
					}
				}
			}
		}
	} else if (group_member->IsGrouped()) {
		auto group = group_member->GetGroup();
		if (group == nullptr) { // uh oh
			owner->RemoveAura(GetID(), false, true);
			return;
		}

		// lambdas to make for loop less ugly
		auto verify_group_pet = [&group, this](Mob *m) {
			auto owner = m->GetOwner();
			if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), m->GetPosition()) <= distance)
				return true;
			return false;
		};

		auto verify_group_swarm = [&group, this](NPC *n) {
			auto owner = entity_list.GetMob(n->GetSwarmOwner());
			if (owner != nullptr && group->IsGroupMember(owner) && DistanceSquared(GetPosition(), n->GetPosition()) <= distance)
				return true;
			return false;
		};

		for (auto &e : mob_list) {
			auto mob = e.second;
			auto it = casted_on.find(mob->GetID());

			if (it != casted_on.end()) { // make sure we're still valid
				if (mob->IsPet()) {
					if (!verify_group_pet(mob))
						delayed_remove.insert(mob->GetID());
				} else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo()) {
					if (!verify_group_swarm(mob->CastToNPC()))
						delayed_remove.insert(mob->GetID());
				}
			} else { // not on, check if we should be!
				if (mob->IsClient()) {
					continue;
				} else if (mob->IsPet() && verify_group_pet(mob)) {
					casted_on.insert(mob->GetID());
					if (is_buff)
						SpellFinished(spell_id, mob);
				} else if (mob->IsNPC() && mob->CastToNPC()->GetSwarmInfo() && verify_group_swarm(mob->CastToNPC())) {
					casted_on.insert(mob->GetID());
					if (is_buff)
						SpellFinished(spell_id, mob);
				}
			}
		}
	} else {
		auto verify_solo = [&group_member, this](Mob *m) {
			if (m->IsPet() && m->GetOwnerID() == group_member->GetID())
				return true;
			else if (m->IsNPC() && m->CastToNPC()->GetSwarmOwner() == group_member->GetID())
				return true;
			else
				return false;
		};
		for (auto &e : mob_list) {
			auto mob = e.second;
			auto it = casted_on.find(mob->GetID());
			bool good = verify_solo(mob);

			if (it != casted_on.end()) { // make sure still valid
				if (!good || DistanceSquared(GetPosition(), mob->GetPosition()) > distance) {
					delayed_remove.insert(mob->GetID());
				}
			} else if (good && DistanceSquared(GetPosition(), mob->GetPosition()) <= distance) {
				casted_on.insert(mob->GetID());
				if (is_buff)
					SpellFinished(spell_id, mob);
			}
		}
	}

	for (auto &e : delayed_remove) {
		auto mob = entity_list.GetMob(e);
		if (mob != nullptr && is_buff) // some auras cast instant spells so no need to remove
			mob->BuffFadeBySpellIDAndCaster(spell_id, GetID());
		casted_on.erase(e);
	}

	// so if we have a cast timer and our set isn't empty and timer is disabled we need to enable it
	if (cast_timer.GetDuration() > 0 && !cast_timer.Enabled() && !casted_on.empty())
		cast_timer.Start();

	if (!cast_timer.Enabled() || !cast_timer.Check())
		return;

	// some auras have to recast (DRU for example, non-buff too)
	for (auto &e : casted_on) {
		auto mob = entity_list.GetMob(e);
		if (mob != nullptr)
			SpellFinished(spell_id, mob);
	}
}
Пример #8
0
void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
	Log.Out(Logs::Detail, Logs::None, "Attempting to Set Exp for %s (XP: %u, AAXP: %u, Rez: %s)", this->GetCleanName(), set_exp, set_aaxp, isrezzexp ? "true" : "false");
	//max_AAXP = GetEXPForLevel(52) - GetEXPForLevel(51);	//GetEXPForLevel() doesn't depend on class/race, just level, so it shouldn't change between Clients
	max_AAXP = RuleI(AA, ExpPerPoint);	//this may be redundant since we're doing this in Client::FinishConnState2()
	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+m_pp.expAA)) {
		if (isrezzexp)
			this->Message_StringID(MT_Experience, REZ_REGAIN);
		else{
			if(this->IsGrouped())
				this->Message_StringID(MT_Experience, GAIN_GROUPXP);
			else if(IsRaidGrouped())
				Message_StringID(MT_Experience, GAIN_RAIDEXP);
			else
				this->Message_StringID(MT_Experience, GAIN_XP);
		}
	}
	else if((set_exp + set_aaxp) < (m_pp.exp+m_pp.expAA)){ //only loss message if you lose exp, no message if you gained/lost nothing.
		Message(15, "You have lost experience.");
	}

	//check_level represents the level we should be when we have
	//this ammount of exp (once these loops complete)
	uint16 check_level = GetLevel()+1;
	//see if we gained any levels
	bool level_increase = true;
	int8 level_count = 0;

	while (set_exp >= GetEXPForLevel(check_level)) {
		check_level++;
		if (check_level > 127) {	//hard level cap
			check_level = 127;
			break;
		}
		level_count++;

		if(GetMercID())
			UpdateMercLevel();
	}
	//see if we lost any levels
	while (set_exp < GetEXPForLevel(check_level-1)) {
		check_level--;
		if (check_level < 2) {	//hard level minimum
			check_level = 2;
			break;
		}
		level_increase = false;
		if(GetMercID())
			UpdateMercLevel();
	}
	check_level--;


	//see if we gained any AAs
	if (set_aaxp >= max_AAXP) {
		/*
			Note: AA exp is stored differently than normal exp.
			Exp points are only stored in m_pp.expAA until you
			gain a full AA point, once you gain it, a point is
			added to m_pp.aapoints and the ammount needed to gain
			that point is subtracted from m_pp.expAA

			then, once they spend an AA point, it is subtracted from
			m_pp.aapoints. In theory it then goes into m_pp.aapoints_spent,
			but im not sure if we have that in the right spot.
		*/
		//record how many points we have
		uint32 last_unspentAA = m_pp.aapoints;

		//figure out how many AA points we get from the exp were setting
		m_pp.aapoints = set_aaxp / max_AAXP;
		Log.Out(Logs::Detail, Logs::None, "Calculating additional AA Points from AAXP for %s: %u / %u = %.1f points", this->GetCleanName(), set_aaxp, max_AAXP, (float)set_aaxp / (float)max_AAXP);

		//get remainder exp points, set in PP below
		set_aaxp = set_aaxp - (max_AAXP * m_pp.aapoints);

		//add in how many points we had
		m_pp.aapoints += last_unspentAA;
		//set_aaxp = m_pp.expAA % max_AAXP;

		//figure out how many points were actually gained
		/*uint32 gained = m_pp.aapoints - last_unspentAA;*/	//unused

		//Message(15, "You have gained %d skill points!!", m_pp.aapoints - last_unspentAA);
		char val1[20]={0};
		Message_StringID(MT_Experience, GAIN_ABILITY_POINT,ConvertArray(m_pp.aapoints, val1),m_pp.aapoints == 1 ? "" : "(s)");	//You have gained an ability point! You now have %1 ability point%2.
		
		/* QS: PlayerLogAARate */
		if (RuleB(QueryServ, PlayerLogAARate)){
			int add_points = (m_pp.aapoints - last_unspentAA);
			std::string query = StringFormat("INSERT INTO `qs_player_aa_rate_hourly` (char_id, aa_count, hour_time) VALUES (%i, %i, UNIX_TIMESTAMP() - MOD(UNIX_TIMESTAMP(), 3600)) ON DUPLICATE KEY UPDATE `aa_count` = `aa_count` + %i", this->CharacterID(), add_points, add_points);
			QServ->SendQuery(query.c_str());
		}

		//Message(15, "You now have %d skill points available to spend.", m_pp.aapoints);
	}

	uint8 maxlevel = RuleI(Character, MaxExpLevel) + 1;

	if(maxlevel <= 1)
		maxlevel = RuleI(Character, MaxLevel) + 1;

	if(check_level > maxlevel) {
		check_level = maxlevel;

		if(RuleB(Character, KeepLevelOverMax)) {
			set_exp = GetEXPForLevel(GetLevel()+1);
		}
		else {
			set_exp = GetEXPForLevel(maxlevel);
		}
	}

	if(RuleB(Character, PerCharacterQglobalMaxLevel)){
		uint32 MaxLevel = GetCharMaxLevelFromQGlobal();
		if(MaxLevel){
			if(GetLevel() >= MaxLevel){
				uint32 expneeded = GetEXPForLevel(MaxLevel);
				if(set_exp > expneeded) {
					set_exp = expneeded;
				}
			}
		}
	}

	if ((GetLevel() != check_level) && !(check_level >= maxlevel)) {
		char val1[20]={0};
		if (level_increase)
		{
			if (level_count == 1)
				Message_StringID(MT_Experience, GAIN_LEVEL, ConvertArray(check_level, val1));
			else
				Message(15, "Welcome to level %i!", check_level);

			if (check_level == RuleI(Character, DeathItemLossLevel))
				Message_StringID(15, CORPSE_ITEM_LOST);

			if (check_level == RuleI(Character, DeathExpLossLevel))
				Message_StringID(15, CORPSE_EXP_LOST);
		}
		else
			Message_StringID(MT_Experience, LOSE_LEVEL, ConvertArray(check_level, val1));

#ifdef BOTS
		uint8 myoldlevel = GetLevel();
#endif

		SetLevel(check_level);

#ifdef BOTS
		if(RuleB(Bots, BotLevelsWithOwner))
			// hack way of doing this..but, least invasive... (same criteria as gain level for sendlvlapp)
			Bot::LevelBotWithClient(this, GetLevel(), (myoldlevel==check_level-1));
#endif
	}

	//If were at max level then stop gaining experience if we make it to the cap
	if(GetLevel() == maxlevel - 1){
		uint32 expneeded = GetEXPForLevel(maxlevel);
		if(set_exp > expneeded) {
			set_exp = expneeded;
		}
	}

	//set the client's EXP and AAEXP
	m_pp.exp = set_exp;
	m_pp.expAA = set_aaxp;

	if (GetLevel() < 51) {
		m_epp.perAA = 0;	// turn off aa exp if they drop below 51
	} else
		SendAAStats();	//otherwise, send them an AA update

	//send the expdata in any case so the xp bar isnt stuck after leveling
	uint32 tmpxp1 = GetEXPForLevel(GetLevel()+1);
	uint32 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) {
		EQApplicationPacket* outapp = new EQApplicationPacket(OP_ExpUpdate, sizeof(ExpUpdate_Struct));
		ExpUpdate_Struct* eu = (ExpUpdate_Struct*)outapp->pBuffer;
		float tmpxp = (float) ( (float) set_exp-tmpxp2 ) / ( (float) tmpxp1-tmpxp2 );
		eu->exp = (uint32)(330.0f * tmpxp);
		FastQueuePacket(&outapp);
	}

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