Exemple #1
0
bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
	AA::Ability *ability = rank->base_ability;

	if(!ability)
		return false;

	if(!(ability->classes & (1 << GetClass()))) {
		return false;
	}

	// Passive and Active Shroud AAs
	// For now we skip them
	if(ability->category == 3 || ability->category == 4) {
		return false;
	}

	//the one titanium hack i will allow
	//just to make sure we dont crash the client with newer aas
	//we'll exclude any expendable ones
	if(IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::maskTitaniumAndEarlier) {
		if(ability->charges > 0) {
			return false;
		}
	}

	if(IsClient()) {
		if(rank->expansion && !(CastToClient()->GetPP().expansions & (1 << (rank->expansion - 1)))) {
			return false;
		}
	} else {
		if(rank->expansion && !(RuleI(World, ExpansionSettings) & (1 << (rank->expansion - 1)))) {
			return false;
		}
	}

	auto race = GetPlayerRaceValue(GetBaseRace());
	race = race > 16 ? 1 : race;
	if(!(ability->races & (1 << (race - 1)))) {
		return false;
	}

	auto deity = GetDeityBit();
	if(!(ability->deities & deity)) {
		return false;
	}

	if(IsClient() && CastToClient()->Admin() < ability->status) {
		return false;
	}

	if(GetBaseRace() == 522) {
		//drakkin_heritage
		if(!(ability->drakkin_heritage & (1 << GetDrakkinHeritage()))) {
			return false;
		}
	}

	return true;
}
Exemple #2
0
int32 Client::LevelRegen()
{
	bool sitting = IsSitting();
	bool feigned = GetFeigned();
	int level = GetLevel();
	bool bonus = GetRaceBitmask(GetBaseRace()) & RuleI(Character, BaseHPRegenBonusRaces);
	uint8 multiplier1 = bonus ? 2 : 1;
	int32 hp = 0;

	//these calculations should match up with the info from Monkly Business, which was last updated ~05/2008: http://www.monkly-business.net/index.php?pageid=abilities
	if (level < 51) {
		if (sitting) {
			if (level < 20)
				hp += 2 * multiplier1;
			else if (level < 50)
				hp += 3 * multiplier1;
			else	//level == 50
				hp += 4 * multiplier1;
		}
		else	//feigned or standing
			hp += 1 * multiplier1;
	}
	//there may be an easier way to calculate this next part, but I don't know what it is
	else {	//level >= 51
		int32 tmp = 0;
		float multiplier2 = 1;
		if (level < 56) {
			tmp = 2;
			if (bonus)
				multiplier2 = 3;
		}
		else if (level < 60) {
			tmp = 3;
			if (bonus)
				multiplier2 = 3.34;
		}
		else if (level < 61) {
			tmp = 4;
			if (bonus)
				multiplier2 = 3;
		}
		else if (level < 63) {
			tmp = 5;
			if (bonus)
				multiplier2 = 2.8;
		}
		else if (level < 65) {
			tmp = 6;
			if (bonus)
				multiplier2 = 2.67;
		}
		else {	//level >= 65
			tmp = 7;
			if (bonus)
				multiplier2 = 2.58;
		}

		hp += int32(float(tmp) * multiplier2);

		if (sitting)
			hp += 3 * multiplier1;
		else if (feigned)
			hp += 1 * multiplier1;
	}

	return hp;
}
Exemple #3
0
int32	Client::CalcFR()
{
	//racial bases
	switch(GetBaseRace()) {
		case HUMAN:
			FR = 25;
			break;
		case BARBARIAN:
			FR = 25;
			break;
		case ERUDITE:
			FR = 25;
			break;
		case WOOD_ELF:
			FR = 25;
			break;
		case HIGH_ELF:
			FR = 25;
			break;
		case DARK_ELF:
			FR = 25;
			break;
		case HALF_ELF:
			FR = 25;
			break;
		case DWARF:
			FR = 25;
			break;
		case TROLL:
			FR = 5;
			break;
		case OGRE:
			FR = 25;
			break;
		case HALFLING:
			FR = 25;
			break;
		case GNOME:
			FR = 25;
			break;
		case IKSAR:
			FR = 30;
			break;
		case VAHSHIR:
			FR = 25;
			break;
		case FROGLOK:
			FR = 25;
			break;
		case DRAKKIN:
			FR = 25;
			break;
		default:
			FR = 20;
	}

	int c = GetClass();
	if(c == RANGER) {
		FR += 4;

		int l = GetLevel();
		if(l > 49)
			FR += l - 49;
	}

	FR += itembonuses.FR + spellbonuses.FR + aabonuses.FR;

	if(FR < 1)
		FR = 1;

	if(FR > GetMaxFR())
		FR = GetMaxFR();

	return(FR);
}
Exemple #4
0
//The AA multipliers are set to be 5, but were 2 on WR
//The resistant discipline which I think should be here is implemented
//in Mob::ResistSpell
int32	Client::CalcMR()
{
	//racial bases
	switch(GetBaseRace()) {
		case HUMAN:
			MR = 25;
			break;
		case BARBARIAN:
			MR = 25;
			break;
		case ERUDITE:
			MR = 30;
			break;
		case WOOD_ELF:
			MR = 25;
			break;
		case HIGH_ELF:
			MR = 25;
			break;
		case DARK_ELF:
			MR = 25;
			break;
		case HALF_ELF:
			MR = 25;
			break;
		case DWARF:
			MR = 30;
			break;
		case TROLL:
			MR = 25;
			break;
		case OGRE:
			MR = 25;
			break;
		case HALFLING:
			MR = 25;
			break;
		case GNOME:
			MR = 25;
			break;
		case IKSAR:
			MR = 25;
			break;
		case VAHSHIR:
			MR = 25;
			break;
		case FROGLOK:
			MR = 30;
			break;
		case DRAKKIN:
			MR = 35;
			break;
		default:
			MR = 20;
	}

	MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR;

	if(GetClass() == WARRIOR)
		MR += GetLevel() / 2;

	if(MR < 1)
		MR = 1;

	if(MR > GetMaxMR())
		MR = GetMaxMR();

	return(MR);
}
Exemple #5
0
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {

	uint32 add_exp = in_add_exp;

	if(!resexp && (XPRate != 0))
		add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));
	
	if (m_epp.perAA<0 || m_epp.perAA>100)
		m_epp.perAA=0;	// stop exploit with sanity check
	
	uint32 add_aaxp;
	if(resexp) {
		add_aaxp = 0;
	} else {

		//figure out how much of this goes to AAs
		add_aaxp = add_exp * m_epp.perAA / 100;
		//take that ammount away from regular exp
		add_exp -= add_aaxp;
	
		float totalmod = 1.0;
		float zemmod = 1.0;
		//get modifiers
		if(RuleR(Character, ExpMultiplier) >= 0){
			totalmod *= RuleR(Character, ExpMultiplier);
		}

		if(zone->newzone_data.zone_exp_multiplier >= 0){
			zemmod *= zone->newzone_data.zone_exp_multiplier;
		}

		if(RuleB(Character,UseRaceClassExpBonuses))
		{
			if(GetBaseRace() == HALFLING){
				totalmod *= 1.05;
			}

			if(GetClass() == ROGUE || GetClass() == WARRIOR){
				totalmod *= 1.05;
			}
		}

		if(zone->IsHotzone())
		{
			totalmod += RuleR(Zone, HotZoneBonus);
		}

		add_exp = uint32(float(add_exp) * totalmod * zemmod);
	
		if(RuleB(Character,UseXPConScaling))
		{
			if (conlevel != 0xFF && !resexp) {
				switch (conlevel)
				{
					case CON_GREEN:
						add_exp = 0;
						add_aaxp = 0;
						return;
					case CON_LIGHTBLUE:
							add_exp = add_exp * RuleI(Character, LightBlueModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, LightBlueModifier)/100;
						break;
					case CON_BLUE:
							add_exp = add_exp * RuleI(Character, BlueModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, BlueModifier)/100;
						break;
					case CON_WHITE:
							add_exp = add_exp * RuleI(Character, WhiteModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, WhiteModifier)/100;
						break;
					case CON_YELLOW:
							add_exp = add_exp * RuleI(Character, YellowModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, YellowModifier)/100;
						break;
					case CON_RED:
							add_exp = add_exp * RuleI(Character, RedModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, RedModifier)/100;
						break;
				}
			}
		}

		if(IsLeadershipEXPOn() && ((conlevel == CON_BLUE) || (conlevel == CON_WHITE) || (conlevel == CON_YELLOW) || (conlevel == CON_RED))) {
			add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);

			if(GetGroup())
			{
				if((m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel()))
				   && (RuleI(Character, KillsPerGroupLeadershipAA) > 0))
				{
					AddLeadershipEXP(GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA), 0);
					Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
				}
				else
					Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
			}
			else
			{
				if((m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel()))
				   && (RuleI(Character, KillsPerRaidLeadershipAA) > 0))
				{
					AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
					Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
				}
				else
					Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
			}

		}

	}	//end !resexp

	float aatotalmod = 1.0;
	if(zone->newzone_data.zone_exp_multiplier >= 0){
		aatotalmod *= zone->newzone_data.zone_exp_multiplier;
	}

	

	if(RuleB(Character,UseRaceClassExpBonuses))
	{
		if(GetBaseRace() == HALFLING){
			aatotalmod *= 1.05;
		}

		if(GetClass() == ROGUE || GetClass() == WARRIOR){
			aatotalmod *= 1.05;
		}
	}

	if(RuleB(Zone, LevelBasedEXPMods)){
		if(zone->level_exp_mod[GetLevel()].ExpMod){
			add_exp *= zone->level_exp_mod[GetLevel()].ExpMod;
			add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod;
		}
	}

	uint32 exp = GetEXP() + add_exp;

	uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
	uint32 had_aaexp = GetAAXP();
	aaexp += had_aaexp;
	if(aaexp < had_aaexp)
		aaexp = had_aaexp;	//watch for wrap
	
	SetEXP(exp, aaexp, resexp);
}
Exemple #6
0
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {

	this->EVENT_ITEM_ScriptStopReturn();

	uint32 add_exp = in_add_exp;

	if(!resexp && (XPRate != 0))
		add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));

	if (m_epp.perAA<0 || m_epp.perAA>100)
		m_epp.perAA=0;	// stop exploit with sanity check

	uint32 add_aaxp;
	if(resexp) {
		add_aaxp = 0;
	} else {

		//figure out how much of this goes to AAs
		add_aaxp = add_exp * m_epp.perAA / 100;
		//take that ammount away from regular exp
		add_exp -= add_aaxp;

		float totalmod = 1.0;
		float zemmod = 1.0;
		//get modifiers
		if(RuleR(Character, ExpMultiplier) >= 0){
			totalmod *= RuleR(Character, ExpMultiplier);
		}

		if(zone->newzone_data.zone_exp_multiplier >= 0){
			zemmod *= zone->newzone_data.zone_exp_multiplier;
		}

		if(RuleB(Character,UseRaceClassExpBonuses))
		{
			if(GetBaseRace() == HALFLING){
				totalmod *= 1.05;
			}

			if(GetClass() == ROGUE || GetClass() == WARRIOR){
				totalmod *= 1.05;
			}
		}

		if(zone->IsHotzone())
		{
			totalmod += RuleR(Zone, HotZoneBonus);
		}

		add_exp = uint32(float(add_exp) * totalmod * zemmod);

		if(RuleB(Character,UseXPConScaling))
		{
			if (conlevel != 0xFF && !resexp) {
				switch (conlevel)
				{
					case CON_GREEN:
						add_exp = 0;
						add_aaxp = 0;
						return;
					case CON_LIGHTBLUE:
							add_exp = add_exp * RuleI(Character, LightBlueModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, LightBlueModifier)/100;
						break;
					case CON_BLUE:
							add_exp = add_exp * RuleI(Character, BlueModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, BlueModifier)/100;
						break;
					case CON_WHITE:
							add_exp = add_exp * RuleI(Character, WhiteModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, WhiteModifier)/100;
						break;
					case CON_YELLOW:
							add_exp = add_exp * RuleI(Character, YellowModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, YellowModifier)/100;
						break;
					case CON_RED:
							add_exp = add_exp * RuleI(Character, RedModifier)/100;
							add_aaxp = add_aaxp * RuleI(Character, RedModifier)/100;
						break;
				}
			}
		}

		if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) {
			add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);

			if (GetGroup()) {
				if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
						&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
					uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
					Client *mentoree = GetGroup()->GetMentoree();
					if (GetGroup()->GetMentorPercent() && mentoree &&
							mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
						uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f);
						exp -= mentor_exp;
						mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down
						mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
					}
					if (exp > 0) { // possible if you mentor 100% to the other client
						AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it
						Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
					}
				} else {
					Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
				}
			} else {
				Raid *raid = GetRaid();
				// Raid leaders CAN NOT gain group AA XP, other group leaders can though!
				if (raid->IsLeader(this)) {
					if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())
							&& RuleI(Character, KillsPerRaidLeadershipAA) > 0) {
						AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
						Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
					} else {
						Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
					}
				} else {
					if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
							&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
						uint32 group_id = raid->GetGroup(this);
						uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
						Client *mentoree = raid->GetMentoree(group_id);
						if (raid->GetMentorPercent(group_id) && mentoree &&
								mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
							uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f);
							exp -= mentor_exp;
							mentoree->AddLeadershipEXP(mentor_exp, 0);
							mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
						}
						if (exp > 0) {
							AddLeadershipEXP(exp, 0);
							Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
						}
					} else {
						Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
					}
				}
			}

		}

	}	//end !resexp

	float aatotalmod = 1.0;
	if(zone->newzone_data.zone_exp_multiplier >= 0){
		aatotalmod *= zone->newzone_data.zone_exp_multiplier;
	}



	if(RuleB(Character,UseRaceClassExpBonuses))
	{
		if(GetBaseRace() == HALFLING){
			aatotalmod *= 1.05;
		}

		if(GetClass() == ROGUE || GetClass() == WARRIOR){
			aatotalmod *= 1.05;
		}
	}

	if(RuleB(Zone, LevelBasedEXPMods)){
		if(zone->level_exp_mod[GetLevel()].ExpMod){
			add_exp *= zone->level_exp_mod[GetLevel()].ExpMod;
			add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod;
		}
	}

	uint32 exp = GetEXP() + add_exp;

	uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
	uint32 had_aaexp = GetAAXP();
	aaexp += had_aaexp;
	if(aaexp < had_aaexp)
		aaexp = had_aaexp;	//watch for wrap

	SetEXP(exp, aaexp, resexp);
}
Exemple #7
0
void Client::HandleAAAction(aaID activate) {
	if (activate < 0 || activate >= aaHighestID)
		return;

	uint8 activate_val = GetAA(activate);

	if (activate_val == 0)
		return;

	if (activate_val > MAX_AA_ACTION_RANKS)
		activate_val = MAX_AA_ACTION_RANKS;
	activate_val--;		//to get array index.

	//get our current node, now that the indices are well bounded
	const AA_DBAction *caa = &AA_Actions[activate][activate_val];

	uint16 timer_id = 0;
	uint16 timer_duration = caa->duration;
	aaTargetType target = aaTargetUser;

	uint16 spell_id = SPELL_UNKNOWN;	//gets cast at the end if not still unknown

	switch (caa->action) {
	case aaActionAETaunt:
		entity_list.AETaunt(this);
		break;

	case aaActionMassBuff:
		EnableAAEffect(aaEffectMassGroupBuff, 3600);
		Message_StringID(MT_Disciplines, MGB_STRING);	//The next group buff you cast will hit all targets in range.
		break;

	case aaActionFlamingArrows:
		//toggle it
		if (CheckAAEffect(aaEffectFlamingArrows))
			EnableAAEffect(aaEffectFlamingArrows);
		else
			DisableAAEffect(aaEffectFlamingArrows);
		break;

	case aaActionFrostArrows:
		if (CheckAAEffect(aaEffectFrostArrows))
			EnableAAEffect(aaEffectFrostArrows);
		else
			DisableAAEffect(aaEffectFrostArrows);
		break;

	case aaActionRampage:
		EnableAAEffect(aaEffectRampage, 10);
		break;

	case aaActionSharedHealth:
		if (CheckAAEffect(aaEffectSharedHealth))
			EnableAAEffect(aaEffectSharedHealth);
		else
			DisableAAEffect(aaEffectSharedHealth);
		break;

	case aaActionCelestialRegen: {
		//special because spell_id depends on a different AA
		switch (GetAA(aaCelestialRenewal)) {
		case 1:
			spell_id = 3250;
			break;
		case 2:
			spell_id = 3251;
			break;
		default:
			spell_id = 2740;
			break;
		}
		target = aaTargetCurrent;
		break;
	}

	case aaActionDireCharm: {
		//special because spell_id depends on class
		switch (GetClass())
		{
		case DRUID:
			spell_id = 2760;	//2644?
			break;
		case NECROMANCER:
			spell_id = 2759;	//2643?
			break;
		case ENCHANTER:
			spell_id = 2761;	//2642?
			break;
		}
		target = aaTargetCurrent;
		break;
	}

	case aaActionImprovedFamiliar: {
		//Spell IDs might be wrong...
		if (GetAA(aaAllegiantFamiliar))
			spell_id = 3264;	//1994?
		else
			spell_id = 2758;	//2155?
		break;
	}

	case aaActionActOfValor:
		if (GetTarget() != nullptr) {
			int curhp = GetTarget()->GetHP();
			target = aaTargetCurrent;
			GetTarget()->HealDamage(curhp, this);
			Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
		}
		break;

	case aaActionSuspendedMinion:
		if (GetPet()) {
			target = aaTargetPet;
			switch (GetAA(aaSuspendedMinion)) {
			case 1:
				spell_id = 3248;
				break;
			case 2:
				spell_id = 3249;
				break;
			}
			//do we really need to cast a spell?

			Message(0, "You call your pet to your side.");
			GetPet()->WipeHateList();
			GetPet()->GMMove(GetX(), GetY(), GetZ());
			if (activate_val > 1)
				entity_list.ClearFeignAggro(GetPet());
		}
		else {
			Message(0, "You have no pet to call.");
		}
		break;

	case aaActionProjectIllusion:
		EnableAAEffect(aaEffectProjectIllusion, 3600);
		Message(10, "The power of your next illusion spell will flow to your grouped target in your place.");
		break;


	case aaActionEscape:
		Escape();
		break;

		// Don't think this code is used any longer for Bestial Alignment as the aa.has a spell_id and no nonspell_action.
	case aaActionBeastialAlignment:
		switch (GetBaseRace()) {
		case BARBARIAN:
			spell_id = AA_Choose3(activate_val, 4521, 4522, 4523);
			break;
		case TROLL:
			spell_id = AA_Choose3(activate_val, 4524, 4525, 4526);
			break;
		case OGRE:
			spell_id = AA_Choose3(activate_val, 4527, 4527, 4529);
			break;
		case IKSAR:
			spell_id = AA_Choose3(activate_val, 4530, 4531, 4532);
			break;
		case VAHSHIR:
			spell_id = AA_Choose3(activate_val, 4533, 4534, 4535);
			break;
		}

	case aaActionLeechTouch:
		target = aaTargetCurrent;
		spell_id = SPELL_HARM_TOUCH2;
		EnableAAEffect(aaEffectLeechTouch, 1000);
		break;

	case aaActionFadingMemories:
		// Do nothing since spell effect works correctly, but mana isn't used.
		break;

		default:
			Log.Out(Logs::General, Logs::Error, "Unknown AA nonspell action type %d", caa->action);
			return;
	}


	uint16 target_id = 0;
	//figure out our target
	switch (target) {
	case aaTargetUser:
	case aaTargetGroup:
		target_id = GetID();
		break;
	case aaTargetCurrent:
	case aaTargetCurrentGroup:
		if (GetTarget() == nullptr) {
			Message_StringID(MT_DefaultText, AA_NO_TARGET);	//You must first select a target for this ability!
			p_timers.Clear(&database, timer_id + pTimerAAEffectStart);
			return;
		}
		target_id = GetTarget()->GetID();
		break;
	case aaTargetPet:
		if (GetPet() == nullptr) {
			Message(0, "A pet is required for this skill.");
			return;
		}
		target_id = GetPetID();
		break;
	}

	//cast the spell, if we have one
	if (IsValidSpell(spell_id)) {
		int aatid = GetAATimerID(activate);
		if (!CastSpell(spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, pTimerAAStart + aatid, CalcAAReuseTimer(caa), 1)) {
			SendAATimer(activate, 0, 0xFFFFFF);
			Message_StringID(CC_Yellow, ABILITY_FAILED);
			p_timers.Clear(&database, pTimerAAStart + aatid);
			return;
		}
	}

	//handle the duration timer if we have one.
	if (timer_id > 0 && timer_duration > 0) {
		p_timers.Start(pTimerAAEffectStart + timer_id, timer_duration);
	}
}
Exemple #8
0
int32	Client::CalcCR()
{
	//racial bases
	switch(GetBaseRace()) {
		case HUMAN:
			CR = 25;
			break;
		case BARBARIAN:
			CR = 35;
			break;
		case ERUDITE:
			CR = 25;
			break;
		case WOOD_ELF:
			CR = 25;
			break;
		case HIGH_ELF:
			CR = 25;
			break;
		case DARK_ELF:
			CR = 25;
			break;
		case HALF_ELF:
			CR = 25;
			break;
		case DWARF:
			CR = 25;
			break;
		case TROLL:
			CR = 25;
			break;
		case OGRE:
			CR = 25;
			break;
		case HALFLING:
			CR = 25;
			break;
		case GNOME:
			CR = 25;
			break;
		case IKSAR:
			CR = 15;
			break;
		case VAHSHIR:
			CR = 25;
			break;
		case FROGLOK:
			CR = 25;
			break;
		case DRAKKIN:
			CR = 25;
			break;
		default:
			CR = 25;
	}

	int c = GetClass();
	if(c == RANGER) {
		CR += 4;

		int l = GetLevel();
		if(l > 49)
			CR += l - 49;
	}

	CR += itembonuses.CR + spellbonuses.CR + aabonuses.CR;

	if(CR < 1)
		CR = 1;

	if(CR > GetMaxCR())
		CR = GetMaxCR();

	return(CR);
}
Exemple #9
0
int32	Client::CalcCR()
{
	//racial bases
	switch (GetBaseRace()) {
		case HUMAN:
			CR = 25;
			break;
		case BARBARIAN:
			CR = 35;
			break;
		case ERUDITE:
			CR = 25;
			break;
		case WOOD_ELF:
			CR = 25;
			break;
		case HIGH_ELF:
			CR = 25;
			break;
		case DARK_ELF:
			CR = 25;
			break;
		case HALF_ELF:
			CR = 25;
			break;
		case DWARF:
			CR = 25;
			break;
		case TROLL:
			CR = 25;
			break;
		case OGRE:
			CR = 25;
			break;
		case HALFLING:
			CR = 25;
			break;
		case GNOME:
			CR = 25;
			break;
		case IKSAR:
			CR = 15;
			break;
		case VAHSHIR:
			CR = 25;
			break;
		case FROGLOK:
			CR = 25;
			break;
		case DRAKKIN:
		{
			CR = 25;
			if (GetDrakkinHeritage() == 4)
				CR += 10;
			else if (GetDrakkinHeritage() == 5)
				CR += 2;
			break;
		}
		default:
			CR = 25;
	}
	int c = GetClass();
	if (c == RANGER || c == BEASTLORD) {
		CR += 4;
		int l = GetLevel();
		if (l > 49) {
			CR += l - 49;
		}
	}
	CR += itembonuses.CR + spellbonuses.CR + aabonuses.CR;
	if (CR < 1) {
		CR = 1;
	}
	if (CR > GetMaxCR()) {
		CR = GetMaxCR();
	}
	return (CR);
}
Exemple #10
0
//o--------------------------------------------------------------
//| BuffFadeBySlot; Yeahlight, Nov 16, 2008
//o--------------------------------------------------------------
//| Adapted from EQEMU 7.0: Removes the buff in the supplied
//| buff slot 'slot'
//o--------------------------------------------------------------
void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
{
	bool debugFlag = true;

	if(slot < 0 || slot > BUFF_COUNT)
		return;

	if(!buffs[slot].spell || !buffs[slot].spell->IsValidSpell())
		return;

	if(IsClient())
		CastToClient()->MakeBuffFadePacket(buffs[slot].spell, slot);


	if(debugFlag && this->IsClient() && this->CastToClient()->GetDebugMe())
		this->Message(LIGHTEN_BLUE, "Debug: Fading buff %d from slot %d", buffs[slot].spell->GetSpellID(), slot);

	for(int i = 0; i < EFFECT_COUNT; i++)
	{
		if(buffs[slot].spell->IsBlankSpellEffect(i))
			continue;
		switch(buffs[slot].spell->GetSpellEffectID(i))
		{
			case SE_WeaponProc:
			{
				SetBonusProcSpell(0);
				break;
			}
			case SE_Illusion: case SE_IllusionCopy:
			{
				SendIllusionPacket(GetBaseRace(), GetBaseGender(), GetTexture(), GetHelmTexture());
				SendAppearancePacket(this->GetID(), SAT_Size, GetDefaultSize(), true);
				break;
			}
			case SE_Levitate:
			{
				SendAppearancePacket(this->GetID(), SAT_Levitate, 0, true);
				break;
			}
			case SE_Invisibility:
			{
				SetInvisible(false);
				break;
			}
			case SE_InvisVsUndead:
			{
				SetInvisibleUndead(false);
				break;
			}
			case SE_InvisVsAnimals:
			{
				SetInvisibleAnimal(false);
				break;
			}
			case SE_Silence:
			{
				break;
			}
			case SE_DivineAura:
			{
				SetInvulnerable(false);
				break;
			}
			case SE_Rune:
			{
				break;
			}
			case SE_AbsorbMagicAtt:
			{
				break;
			}
			case SE_Mez:
			{
				//Yeahlight: Unfreeze the PC's UI
				if(IsClient())
					SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true);
				this->mesmerized = false;
				break;
			}
			case SE_Charm:
			{
				Mob* charmer = entity_list.GetMob(this->GetOwnerID());
				if(charmer && charmer->IsClient())
				{
					char npcNameSuffix[] = "_CHARM00";
					char npcNamePrefix[100] = "";
					strcpy(npcNamePrefix, charmer->GetName());
					strcat(npcNamePrefix, npcNameSuffix);
					Mob* charmPH = entity_list.GetMob(npcNamePrefix);
					if(charmPH && charmPH->IsNPC())
					{
						charmPH->Depop();
					}
					//Yeahlight: Check for _CHARM01 NPC, too
					npcNamePrefix[strlen(npcNamePrefix) - 1] = '1';
					charmPH = entity_list.GetMob(npcNamePrefix);
					if(charmPH && charmPH->IsNPC())
					{
						charmPH->Depop();
					}
					//Yeahlight: Generate hate for towards the charmer if the charmer is not FD'ed
					if(this->IsNPC() && charmer->IsClient() && !charmer->CastToClient()->GetFeigned())
					{
						CastToNPC()->AddToHateList(charmer, 0, GetSpellHate(SE_Charm, this->GetLevel(), false));
					}
				}
				//Yeahlight: Unfreeze the PC's UI
				if(IsClient())
				{
					SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true);
					CastToClient()->SetCharmed(false);
					CastToClient()->charmPositionUpdate_timer->Disable();
				}
				//Yeahlight: Deflag the NPC as charmed
				else if(IsNPC())
				{
					CastToNPC()->SetCharmed(false);
				}
				this->SetOwnerID(0, false);
				break;
			}
			case SE_Root:
			{
				break;
			}
			case SE_Fear:
			{
				//Yeahlight: Unfreeze the PC's UI
				if(IsClient())
				{
					CastToClient()->SetFeared(false);
					SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true);
				}
				else if(IsNPC())
				{
					CastToNPC()->SetFeared(false);
					CastToNPC()->SetOnFearPath(false);
				}
				break;
			}
			case SE_SpinTarget:
			{
				//Yeahlight: Unfreeze the PC's UI
				if(IsClient())
					SendAppearancePacket(this->GetID(), SAT_Position_Update, SAPP_Sitting_To_Standing, true);
				break;
			}
			case SE_EyeOfZomm:
			{
				//Yeahlight: Clear the eye of zomm pointer and depop the eye
				if(IsClient() && CastToClient()->myEyeOfZomm)
				{
					CastToClient()->myEyeOfZomm->Depop();
					CastToClient()->myEyeOfZomm = NULL;
				}
				break;
			}
			case SE_DeathSave:
			{
				//Yeahlight: Clear the death save chance
				if(IsClient())
					CastToClient()->SetDeathSave(0);
				break;
			}
			case SE_VoiceGraft:
			{
				//Yeahlight: Drop the voice grafting flag from the client
				if(GetOwner() && GetOwner()->IsClient())
				{
					GetOwner()->CastToClient()->SetVoiceGrafting(false);
				}
				break;
			}
			case SE_SeeInvis:
			{
				//Yeahlight: Mob may no longer see through invis
				SetCanSeeThroughInvis(false);
				break;
			}
		}
	}

	buffs[slot].spell = NULL;

	if(iRecalcBonuses)
		CalcBonuses(true, true);
}
Exemple #11
0
int32	Client::CalcPR()
{
	//racial bases
	switch (GetBaseRace()) {
		case HUMAN:
			PR = 15;
			break;
		case BARBARIAN:
			PR = 15;
			break;
		case ERUDITE:
			PR = 15;
			break;
		case WOOD_ELF:
			PR = 15;
			break;
		case HIGH_ELF:
			PR = 15;
			break;
		case DARK_ELF:
			PR = 15;
			break;
		case HALF_ELF:
			PR = 15;
			break;
		case DWARF:
			PR = 20;
			break;
		case TROLL:
			PR = 15;
			break;
		case OGRE:
			PR = 15;
			break;
		case HALFLING:
			PR = 20;
			break;
		case GNOME:
			PR = 15;
			break;
		case IKSAR:
			PR = 15;
			break;
		case VAHSHIR:
			PR = 15;
			break;
		case FROGLOK:
			PR = 30;
			break;
		case DRAKKIN:
		{
			PR = 15;
			if (GetDrakkinHeritage() == 3)
				PR += 10;
			else if (GetDrakkinHeritage() == 5)
				PR += 2;
			break;
		}
		default:
			PR = 15;
	}
	int c = GetClass();
	// this monk bonus is part of the base
	if (c == MONK) {
		int l = GetLevel();
		if (l > 50)
			PR += l - 50;
	}
	if (c == ROGUE) {
		PR += 8;
		int l = GetLevel();
		if (l > 49) {
			PR += l - 49;
		}
	}
	else if (c == SHADOWKNIGHT) {
		PR += 4;
		int l = GetLevel();
		if (l > 49) {
			PR += l - 49;
		}
	}
	PR += itembonuses.PR + spellbonuses.PR + aabonuses.PR;
	if (PR < 1) {
		PR = 1;
	}
	if (PR > GetMaxPR()) {
		PR = GetMaxPR();
	}
	return (PR);
}
Exemple #12
0
int32	Client::CalcDR()
{
	//racial bases
	switch (GetBaseRace()) {
		case HUMAN:
			DR = 15;
			break;
		case BARBARIAN:
			DR = 15;
			break;
		case ERUDITE:
			DR = 10;
			break;
		case WOOD_ELF:
			DR = 15;
			break;
		case HIGH_ELF:
			DR = 15;
			break;
		case DARK_ELF:
			DR = 15;
			break;
		case HALF_ELF:
			DR = 15;
			break;
		case DWARF:
			DR = 15;
			break;
		case TROLL:
			DR = 15;
			break;
		case OGRE:
			DR = 15;
			break;
		case HALFLING:
			DR = 20;
			break;
		case GNOME:
			DR = 15;
			break;
		case IKSAR:
			DR = 15;
			break;
		case VAHSHIR:
			DR = 15;
			break;
		case FROGLOK:
			DR = 15;
			break;
		case DRAKKIN:
		{
			DR = 15;
			if (GetDrakkinHeritage() == 1)
				DR += 10;
			else if (GetDrakkinHeritage() == 5)
				DR += 2;
			break;
		}
		default:
			DR = 15;
	}
	int c = GetClass();
	// the monk one is part of base resist
	if (c == MONK) {
		int l = GetLevel();
		if (l > 50)
			DR += l - 50;
	}
	if (c == PALADIN) {
		DR += 8;
		int l = GetLevel();
		if (l > 49) {
			DR += l - 49;
		}
	}
	else if (c == SHADOWKNIGHT || c == BEASTLORD) {
		DR += 4;
		int l = GetLevel();
		if (l > 49) {
			DR += l - 49;
		}
	}
	DR += itembonuses.DR + spellbonuses.DR + aabonuses.DR;
	if (DR < 1) {
		DR = 1;
	}
	if (DR > GetMaxDR()) {
		DR = GetMaxDR();
	}
	return (DR);
}
Exemple #13
0
//The AA multipliers are set to be 5, but were 2 on WR
//The resistant discipline which I think should be here is implemented
//in Mob::ResistSpell
int32	Client::CalcMR()
{
	//racial bases
	switch (GetBaseRace()) {
		case HUMAN:
			MR = 25;
			break;
		case BARBARIAN:
			MR = 25;
			break;
		case ERUDITE:
			MR = 30;
			break;
		case WOOD_ELF:
			MR = 25;
			break;
		case HIGH_ELF:
			MR = 25;
			break;
		case DARK_ELF:
			MR = 25;
			break;
		case HALF_ELF:
			MR = 25;
			break;
		case DWARF:
			MR = 30;
			break;
		case TROLL:
			MR = 25;
			break;
		case OGRE:
			MR = 25;
			break;
		case HALFLING:
			MR = 25;
			break;
		case GNOME:
			MR = 25;
			break;
		case IKSAR:
			MR = 25;
			break;
		case VAHSHIR:
			MR = 25;
			break;
		case FROGLOK:
			MR = 30;
			break;
		case DRAKKIN:
		{
			MR = 25;
			if (GetDrakkinHeritage() == 2)
				MR += 10;
			else if (GetDrakkinHeritage() == 5)
				MR += 2;
			break;
		}
		default:
			MR = 20;
	}
	MR += itembonuses.MR + spellbonuses.MR + aabonuses.MR;
	if (GetClass() == WARRIOR || GetClass() == BERSERKER) {
		MR += GetLevel() / 2;
	}
	if (MR < 1) {
		MR = 1;
	}
	if (MR > GetMaxMR()) {
		MR = GetMaxMR();
	}
	return (MR);
}
Exemple #14
0
int32	Client::CalcDR()
{
	//racial bases
	switch(GetBaseRace()) {
		case HUMAN:
			DR = 15;
			break;
		case BARBARIAN:
			DR = 15;
			break;
		case ERUDITE:
			DR = 10;
			break;
		case WOOD_ELF:
			DR = 15;
			break;
		case HIGH_ELF:
			DR = 15;
			break;
		case DARK_ELF:
			DR = 15;
			break;
		case HALF_ELF:
			DR = 15;
			break;
		case DWARF:
			DR = 15;
			break;
		case TROLL:
			DR = 15;
			break;
		case OGRE:
			DR = 15;
			break;
		case HALFLING:
			DR = 20;
			break;
		case GNOME:
			DR = 15;
			break;
		case IKSAR:
			DR = 15;
			break;
		case VAHSHIR:
			DR = 15;
			break;
		case FROGLOK:
			DR = 15;
			break;
		case DRAKKIN:
			DR = 15;
			break;
		default:
			DR = 15;
	}

	int c = GetClass();
	if(c == PALADIN) {
		DR += 8;

		int l = GetLevel();
		if(l > 49)
			DR += l - 49;

	} else if(c == SHADOWKNIGHT) {
		DR += 4;

		int l = GetLevel();
		if(l > 49)
			DR += l - 49;
	}

	DR += itembonuses.DR + spellbonuses.DR + aabonuses.DR;

	if(DR < 1)
		DR = 1;

	if(DR > GetMaxDR())
		DR = GetMaxDR();

	return(DR);
}
Exemple #15
0
uint32 Client::GetEXPForLevel(uint16 check_level, bool aa)
{

	// Warning: Changing anything in this method WILL cause levels to change in-game the first time a player
	// gains or loses XP. 

	if(aa)
	{
		if(m_epp.perAA > 99)
			return (RuleI(AA, ExpPerPoint));
		else
			check_level = 52;
	}

	check_level -= 1;
	float base = (check_level)*(check_level)*(check_level);

	// Classes: In the XP formula AK used, they WERE calculated in. This was due to Sony not being able to change their XP
	// formula drastically (see above comment.) Instead, they gave the penalized classes a bonus on gain. We've decided to go
	// the easy route, and simply not use a class mod at all.

	float playermod = 10;
	uint8 race = GetBaseRace();
	if(race == HALFLING)
		playermod *= 95.0;
	else if(race == DARK_ELF || race == DWARF || race == ERUDITE || race == GNOME || 
		race == HALF_ELF || race == HIGH_ELF || race == HUMAN || race == WOOD_ELF ||
		race == VAHSHIR)
		playermod *= 100.0;
	else if(race == BARBARIAN)
		playermod *= 105.0;
	else if(race == OGRE)
		playermod *= 115.0;
	else if(race == IKSAR || race == TROLL)
		playermod *= 120.0;

	float mod;
	if (check_level <= 29)
		mod = 1.0;
	else if (check_level <= 34)
		mod = 1.1;
	else if (check_level <= 39)
		mod = 1.2;
	else if (check_level <= 44)
		mod = 1.3;
	else if (check_level <= 50)
		mod = 1.4;
	else if (check_level == 51)
		mod = 1.5;
	else if (check_level == 52)
		mod = 1.6;
	else if (check_level == 53)
		mod = 1.7;
	else if (check_level == 54)
		mod = 1.9;
	else if (check_level == 55)
		mod = 2.1;
	else if (check_level == 56)
		mod = 2.3;
	else if (check_level == 57)
		mod = 2.5;
	else if (check_level == 58)
		mod = 2.7;
	else if (check_level == 59)
		mod = 3.0;
	else if (check_level == 60)
		mod = 3.1;
	else if (check_level == 61)
		mod = 3.2;
	else if (check_level == 62)
		mod = 3.3;
	else if (check_level == 63)
		mod = 3.4;
	else if (check_level == 64)
		mod = 3.5;
	else
		mod = 3.6;

	uint32 finalxp = uint32(base * playermod * mod);
	if(aa)
	{
		uint32 aaxp;
		aaxp = finalxp - GetEXPForLevel(51);
		return aaxp;
	}

	finalxp = mod_client_xp_for_level(finalxp, check_level);
	return finalxp;
}
Exemple #16
0
int32	Client::CalcPR()
{
	//racial bases
	switch(GetBaseRace()) {
		case HUMAN:
			PR = 15;
			break;
		case BARBARIAN:
			PR = 15;
			break;
		case ERUDITE:
			PR = 15;
			break;
		case WOOD_ELF:
			PR = 15;
			break;
		case HIGH_ELF:
			PR = 15;
			break;
		case DARK_ELF:
			PR = 15;
			break;
		case HALF_ELF:
			PR = 15;
			break;
		case DWARF:
			PR = 20;
			break;
		case TROLL:
			PR = 15;
			break;
		case OGRE:
			PR = 15;
			break;
		case HALFLING:
			PR = 20;
			break;
		case GNOME:
			PR = 15;
			break;
		case IKSAR:
			PR = 15;
			break;
		case VAHSHIR:
			PR = 15;
			break;
		case FROGLOK:
			PR = 30;
			break;
		case DRAKKIN:
			PR = 15;
			break;
		default:
			PR = 15;
	}

	int c = GetClass();
	if(c == ROGUE) {
		PR += 8;

		int l = GetLevel();
		if(l > 49)
			PR += l - 49;

	} else if(c == SHADOWKNIGHT) {
		PR += 4;

		int l = GetLevel();
		if(l > 49)
			PR += l - 49;
	}

	PR += itembonuses.PR + spellbonuses.PR + aabonuses.PR;

	if(PR < 1)
		PR = 1;

	if(PR > GetMaxPR())
		PR = GetMaxPR();

	return(PR);
}
Exemple #17
0
// Split from the basic MakePet to allow backward compatiblity with existing code while also
// making it possible for petpower to be retained without the focus item having to
// stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus
// of a client is searched for and used instead.
void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
		const char *petname, float in_size) {
	// Sanity and early out checking first.
	if(HasPet() || pettype == nullptr)
		return;

	int16 act_power = 0; // The actual pet power we'll use.
	if (petpower == -1) {
		if (this->IsClient()) {
			act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);//Client only
			act_power = CastToClient()->mod_pet_power(act_power, spell_id);
		}
#ifdef BOTS
		else if (this->IsBot())
			act_power = CastToBot()->GetBotFocusEffect(Bot::BotfocusPetPower, spell_id);
#endif
	}
	else if (petpower > 0)
		act_power = petpower;

	// optional rule: classic style variance in pets. Achieve this by
	// adding a random 0-4 to pet power, since it only comes in increments
	// of five from focus effects.

	//lookup our pets table record for this type
	PetRecord record;
	if(!database.GetPoweredPetEntry(pettype, act_power, &record)) {
		Message(13, "Unable to find data for pet %s", pettype);
		Log.Out(Logs::General, Logs::Error, "Unable to find data for pet %s, check pets table.", pettype);
		return;
	}

	//find the NPC data for the specified NPC type
	const NPCType *base = database.LoadNPCTypesData(record.npc_type);
	if(base == nullptr) {
		Message(13, "Unable to load NPC data for pet %s", pettype);
		Log.Out(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type);
		return;
	}

	//we copy the npc_type data because we need to edit it a bit
	auto npc_type = new NPCType;
	memcpy(npc_type, base, sizeof(NPCType));

	// If pet power is set to -1 in the DB, use stat scaling
	if ((this->IsClient() 
#ifdef BOTS
		|| this->IsBot()
#endif
		) && record.petpower == -1)
	{
		float scale_power = (float)act_power / 100.0f;
		if(scale_power > 0)
		{
			npc_type->max_hp *= (1 + scale_power);
			npc_type->cur_hp = npc_type->max_hp;
			npc_type->AC *= (1 + scale_power);
			npc_type->level += 1 + ((int)act_power / 25) > npc_type->level + RuleR(Pets, PetPowerLevelCap) ? RuleR(Pets, PetPowerLevelCap) : 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power
			npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2)));
			npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2)));
			npc_type->size = npc_type->size * (1 + (scale_power / 2)) > npc_type->size * 3 ? npc_type->size * 3 : npc_type-> size * (1 + (scale_power / 2));
		}
		record.petpower = act_power;
	}

	//Live AA - Elemental Durability
	int16 MaxHP = aabonuses.PetMaxHP + itembonuses.PetMaxHP + spellbonuses.PetMaxHP;

	if (MaxHP){
		npc_type->max_hp += (npc_type->max_hp*MaxHP)/100;
		npc_type->cur_hp = npc_type->max_hp;
	}

	//TODO: think about regen (engaged vs. not engaged)

	// Pet naming:
	// 0 - `s pet
	// 1 - `s familiar
	// 2 - `s Warder
	// 3 - Random name if client, `s pet for others
	// 4 - Keep DB name


	if (petname != nullptr) {
		// Name was provided, use it.
		strn0cpy(npc_type->name, petname, 64);
	} else if (record.petnaming == 0) {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	} else if (record.petnaming == 1) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[19] = '\0';
		strcat(npc_type->name, "`s_familiar");
	} else if (record.petnaming == 2) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[21] = 0;
		strcat(npc_type->name, "`s_Warder");
	} else if (record.petnaming == 4) {
		// Keep the DB name
	} else if (record.petnaming == 3 && IsClient()) {
		strcpy(npc_type->name, GetRandPetName());
	} else {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	}

	//handle beastlord pet appearance
	if(record.petnaming == 2)
	{
		switch(GetBaseRace())
		{
		case VAHSHIR:
			npc_type->race = TIGER;
			npc_type->size *= 0.8f;
			break;
		case TROLL:
			npc_type->race = ALLIGATOR;
			npc_type->size *= 2.5f;
			break;
		case OGRE:
			npc_type->race = BEAR;
			npc_type->texture = 3;
			npc_type->gender = 2;
			break;
		case BARBARIAN:
			npc_type->race = WOLF;
			npc_type->texture = 2;
			break;
		case IKSAR:
			npc_type->race = WOLF;
			npc_type->texture = 0;
			npc_type->gender = 1;
			npc_type->size *= 2.0f;
			npc_type->luclinface = 0;
			break;
		default:
			npc_type->race = WOLF;
			npc_type->texture = 0;
		}
	}

	// handle monster summoning pet appearance
	if(record.monsterflag) {

		uint32 monsterid = 0;

		// get a random npc id from the spawngroups assigned to this zone
		auto query = StringFormat("SELECT npcID "
									"FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) "
									"INNER JOIN npc_types ON npc_types.id = spawnentry.npcID "
									"WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) "
									"AND npc_types.race NOT IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 44, "
									"55, 67, 71, 72, 73, 77, 78, 81, 90, 92, 93, 94, 106, 112, 114, 127, 128, "
									"130, 139, 141, 183, 236, 237, 238, 239, 254, 266, 329, 330, 378, 379, "
									"380, 381, 382, 383, 404, 522) "
									"ORDER BY RAND() LIMIT 1", zone->GetShortName());
		auto results = database.QueryDatabase(query);
		if (!results.Success()) {
			safe_delete(npc_type);
			return;
		}

		if (results.RowCount() != 0) {
			auto row = results.begin();
			monsterid = atoi(row[0]);
		}

		// since we don't have any monsters, just make it look like an earth pet for now
		if (monsterid == 0)
			monsterid = 567;

		// give the summoned pet the attributes of the monster we found
		const NPCType* monster = database.LoadNPCTypesData(monsterid);
		if(monster) {
			npc_type->race = monster->race;
			npc_type->size = monster->size;
			npc_type->texture = monster->texture;
			npc_type->gender = monster->gender;
			npc_type->luclinface = monster->luclinface;
			npc_type->helmtexture = monster->helmtexture;
			npc_type->herosforgemodel = monster->herosforgemodel;
		} else
			Log.Out(Logs::General, Logs::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid);

	}

	//this takes ownership of the npc_type data
	auto npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower);

	// Now that we have an actual object to interact with, load
	// the base items for the pet. These are always loaded
	// so that a rank 1 suspend minion does not kill things
	// like the special back items some focused pets may receive.
	uint32 petinv[EQEmu::legacy::EQUIPMENT_SIZE];
	memset(petinv, 0, sizeof(petinv));
	const EQEmu::ItemData *item = 0;

	if (database.GetBasePetItems(record.equipmentset, petinv)) {
		for (int i = 0; i < EQEmu::legacy::EQUIPMENT_SIZE; i++)
			if (petinv[i]) {
				item = database.GetItem(petinv[i]);
				npc->AddLootDrop(item, &npc->itemlist, 0, 1, 127, true, true);
			}
	}

	npc->UpdateEquipmentLight();

	// finally, override size if one was provided
	if (in_size > 0.0f)
		npc->size = in_size;

	entity_list.AddNPC(npc, true, true);
	SetPetID(npc->GetID());
	// We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet


	if (record.petcontrol == petTargetLock)
	{
		Mob* target = GetTarget();

		if (target){
			npc->AddToHateList(target, 1);
			npc->SetPetTargetLockID(target->GetID());
			npc->SetSpecialAbility(IMMUNE_AGGRO, 1);
		}
		else
			npc->Kill(); //On live casts spell 892 Unsummon (Kayen - Too limiting to use that for emu since pet can have more than 20k HP)
	}
}
Exemple #18
0
// Split from the basic MakePet to allow backward compatiblity with existing code while also 
// making it possible for petpower to be retained without the focus item having to
// stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus
// of a client is searched for and used instead.
void Mob::MakePoweredPet(int16 spell_id, const char* pettype, sint16 petpower, const char *petname) {
	// Sanity and early out checking first.
	if(HasPet() || pettype == NULL)
		return;

	sint16 act_power = 0; // lets see if this works to randomize pet levels // The actual pet power we'll use.
	if (petpower == 0) {  // was == -1
		if (this->IsClient())
			act_power = CastToClient()->GetFocusEffect(focusPetPower, spell_id);
	}
	if (petpower == -1)  {
		act_power = petpower + MakeRandomInt(0,5);
	}


	// optional rule: classic style variance in pets. Achieve this by
	// adding a random 0-4 to pet power, since it only comes in increments
	// of five from focus effects.

	//lookup our pets table record for this type
	PetRecord record;
	if(!database.GetPoweredPetEntry(pettype, act_power, &record)) {
		Message(13, "Unable to find data for pet %s", pettype);
		LogFile->write(EQEMuLog::Error, "Unable to find data for pet %s, check pets table.", pettype);
		return;
	}

	//find the NPC data for the specified NPC type
	const NPCType *base = database.GetNPCType(record.npc_type);
	if(base == NULL) {
		Message(13, "Unable to load NPC data for pet %s", pettype);
		LogFile->write(EQEMuLog::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type);
		return;
	}

	//we copy the npc_type data because we need to edit it a bit
	NPCType *npc_type = new NPCType;
	memcpy(npc_type, base, sizeof(NPCType));

	// If pet power is set to -1 in the DB, use stat scaling
	if (this->IsClient() && petpower == -1) 
	{
		float scale_power = (float)act_power / 100.0f;
		if(scale_power > 0)
		{
			npc_type->max_hp *= (1 + scale_power);
			npc_type->cur_hp = npc_type->max_hp;
			npc_type->AC *= (1 + scale_power);
			npc_type->level += (float)scale_power * 50; //(int)act_power; // gains an additional level for every 25 pet power
			npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2)));
			npc_type->max_dmg = (npc_type->max_dmg * (1 + (scale_power / 2)));
			npc_type->size *= (1 + (scale_power / 2));
		}
		petpower = act_power;
	}
	
	switch (GetAA(aaElementalDurability))
	{
	case 1:
		npc_type->max_hp *= 1.02;
		npc_type->cur_hp = npc_type->max_hp;
		break;
	case 2:
		npc_type->max_hp *= 1.05;
		npc_type->cur_hp = npc_type->max_hp;
		break;
	case 3:
		npc_type->max_hp *= 1.10;
		npc_type->cur_hp = npc_type->max_hp;
		break;
	}

	//TODO: think about regen (engaged vs. not engaged)
	
	// Pet naming:
	// 0 - `s pet
	// 1 - `s familiar
	// 2 - `s Warder
	// 3 - Random name if client, `s pet for others
	// 4 - Keep DB name


	if (petname != NULL) {
		// Name was provided, use it.
		strn0cpy(npc_type->name, petname, 64);
	} else if (record.petnaming == 0) {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	} else if (record.petnaming == 1) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[19] = '\0';
		strcat(npc_type->name, "`s_familiar");
	} else if (record.petnaming == 2) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[21] = 0;
		strcat(npc_type->name, "`s_Warder");
	} else if (record.petnaming == 4) {
		// Keep the DB name
	} else if (record.petnaming == 3 && IsClient()) {
		strcpy(npc_type->name, GetRandPetName());
	} else {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	}
	
	//handle beastlord pet appearance
	if(record.petnaming == 2) 
	{
		switch(GetBaseRace()) 
		{
		case VAHSHIR: 
			npc_type->race = TIGER; 
			npc_type->size *= 0.8f; 
			break;
		case TROLL: 
			npc_type->race = ALLIGATOR; 
			npc_type->size *= 2.5f; 
			break;
		case OGRE: 
			npc_type->race = BEAR; 
			npc_type->texture = 3; 
			npc_type->gender = 2; 
			break;
		case BARBARIAN: 
			npc_type->race = WOLF; 
			npc_type->texture = 2;
			break;
		case IKSAR: 
			npc_type->race = WOLF; 
			npc_type->texture = 0; 
			npc_type->gender = 1; 
			npc_type->size *= 2.0f;
			npc_type->luclinface = 0;
			break;
		default: 
			npc_type->race = WOLF; 
			npc_type->texture = 0;
		}
	}

	// handle monster summoning pet appearance
	if(record.monsterflag) {
		char errbuf[MYSQL_ERRMSG_SIZE];
		char* query = 0;
		MYSQL_RES *result = NULL;
		MYSQL_ROW row = NULL;
		uint32 monsterid;
		
		// get a random npc id from the spawngroups assigned to this zone
		if (database.RunQuery(query,	MakeAnyLenString(&query,
			"SELECT npcID FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) "
			"INNER JOIN npc_types ON npc_types.id = spawnentry.npcID "
			"WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) "
			"AND npc_types.race NOT IN (0,1,2,3,4,5,6,7,8,9,10,11,12,44,55,67,71,72,73,77,78,81,90,92,93,94,106,112,114,127,128,130,139,141,183,236,237,238,239,254,266,330,378,379,380,381,382,383,404,522) "
			"ORDER BY RAND() LIMIT 1",	zone->GetShortName()), errbuf, &result))
		{
			row = mysql_fetch_row(result);
			if (row) 
				monsterid = atoi(row[0]);
			else 
				monsterid = 567;	// since we don't have any monsters, just make it look like an earth pet for now
		}
		else {	// if the database query failed
			LogFile->write(EQEMuLog::Error, "Error querying database for monster summoning pet in zone %s (%s)", zone->GetShortName(), errbuf);
			monsterid = 567;
		}
		
		// give the summoned pet the attributes of the monster we found
		const NPCType* monster = database.GetNPCType(monsterid);
		if(monster) {
			npc_type->race = monster->race;
			npc_type->size = monster->size;
			npc_type->texture = monster->texture;
			npc_type->gender = monster->gender;
		}
		else {
			LogFile->write(EQEMuLog::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid);
		}

		safe_delete_array(query);
	}

	//this takes ownership of the npc_type data
	Pet *npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower);

	// Now that we have an actual object to interact with, load
	// the base items for the pet. These are always loaded
	// so that a rank 1 suspend minion does not kill things
	// like the special back items some focused pets may receive.
	int32 petinv[MAX_WORN_INVENTORY];
	memset(petinv, 0, sizeof(petinv));
	const Item_Struct *item = 0;

	if (database.GetBasePetItems(record.equipmentset, petinv)) {
		for (int i=0; i<MAX_WORN_INVENTORY; i++)
			if (petinv[i]) {
				item = database.GetItem(petinv[i]);
				npc->AddLootDrop(item, &npc->itemlist, 0, true, true);
			}
	}


	entity_list.AddNPC(npc, true, true);
	SetPetID(npc->GetID());
	// We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet
}
Exemple #19
0
// Split from the basic MakePet to allow backward compatiblity with existing code while also
// making it possible for petpower to be retained without the focus item having to
// stay equipped when the character zones. petpower of -1 means that the currently equipped petfocus
// of a client is searched for and used instead.
void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
		const char *petname, float in_size) {
	// Sanity and early out checking first.
	bool scale_pet = false;
	if(HasPet() || pettype == nullptr)
		return;

	//lookup our pets table record for this type
	PetRecord record;
	if(!database.GetPoweredPetEntry(pettype, petpower, &record)) {
		Message(CC_Red, "Unable to find data for pet %s", pettype);
		Log.Out(Logs::General, Logs::Error, "Unable to find data for pet %s, check pets table.", pettype);
		return;
	}

	//find the NPC data for the specified NPC type
	const NPCType *base = database.GetNPCType(record.npc_type);
	if(base == nullptr) {
		Message(CC_Red, "Unable to load NPC data for pet %s", pettype);
		Log.Out(Logs::General, Logs::Error, "Unable to load NPC data for pet %s (NPC ID %d), check pets and npc_types tables.", pettype, record.npc_type);
		return;
	}

	int act_power = 0; // The actual pet power we'll use.
	if (petpower == -1) {
		if (this->IsClient()) {
			//Message(CC_Red, "We are a client time to check for focus items");
			uint16 focusItemId;
			FocusPetItem petItem;
			// Loop over all the focus items and figure out which on is the best to use
			// It will step down from PoP - Classic looking for the best focus item to use based on pet level
			int16 slot = 0;
			for(int i=0; i < FocusPetItemSize; i++) {
				petItem = Pet::focusItems[i];
				// Look in out inventory
				int16 slot_id = this->CastToClient()->GetInv().HasItem(petItem.item_id, 1, invWhereWorn);
				if(slot_id != INVALID_INDEX) {
					//skip this focus item if its effect is out of rage for the pet we are casting
					if(base->level >= petItem.min_level && base->level <= petItem.max_level) {
						if(EQDEBUG>8) Message(CC_Red, "Found Focus Item: %d in Inventory: %d", petItem.item_id, slot_id);
						if(EQDEBUG>8) Message(CC_Red, "Npc spell levels: %d (%d - %d)", base->level, petItem.min_level, petItem.max_level);
						slot = slot_id;
						focusItemId = petItem.item_id;
						break;
					} else {
						if(EQDEBUG>8) Message(CC_Red, "Moving on Pet base level is out of range: %d (%d - %d)", base->level, petItem.min_level, petItem.max_level);
					}
				}
			}
			// we have a focus item
			if(focusItemId)
			{
				FocusPetType focusType;
				// Symbol or Gloves can be used by all NEC, MAG, BST
				if(petItem.pet_type == FocusPetType::ALL)
				{
					//Message(CC_Red, "Type is ALL");
					focusType = FocusPetType::ALL;
				} else {
					// make sure we can use the focus item as the class .. client should never let us fail this but for sanity!
					if (GetClass() == MAGICIAN) {
						if(EQDEBUG>8) Message(CC_Red, "Looking up mage");
						if(EQDEBUG>8) Message(CC_Red, "Looking up if spell: %d is allowed ot be focused", spell_id);
						focusType = Pet::GetPetItemPetTypeFromSpellId(spell_id);
						if(EQDEBUG>8) Message(CC_Red, "FocusType fround %i", focusType);
					} else if (GetClass() == NECROMANCER) {
						if(EQDEBUG>8) Message(CC_Red, "We are a necro");
						focusType = FocusPetType::NECRO;
					}
				}
				// Sets the power to be what the focus item has as a mod
				if(EQDEBUG>8) {
					Message(CC_Red, "Pet Item Type ALL is  %i", FocusPetType::ALL);
					Message(CC_Red, "Pet Item Type FIRE is  %i", FocusPetType::FIRE);
					Message(CC_Red, "Pet Item Type WATER is  %i", FocusPetType::WATER);
					Message(CC_Red, "Pet Item Type AIR is  %i", FocusPetType::AIR);
					Message(CC_Red, "Pet Item Type EARTH is  %i", FocusPetType::EARTH);
					Message(CC_Red, "Pet Item Type NECRO is  %i", FocusPetType::NECRO);
				}
				if(EQDEBUG>8) Message(CC_Red, "Pet Item Type  %i", petItem.pet_type);
				if (focusType == petItem.pet_type) {
					if(EQDEBUG>8) Message(CC_Red, "Setting power to: %d", petItem.power);
					act_power = petItem.power;
					scale_pet = true;
				}
				
				if(act_power > 0)
				{
					if(EQDEBUG>8) Message(CC_Yellow, "Debug: You have cast a powered pet. Unadjusted pet power is: %i. HasItem returned: %i.", act_power, slot);

					if(focusType == FocusPetType::NECRO ||focusType == FocusPetType::EARTH || focusType == FocusPetType::AIR
					|| focusType == FocusPetType::FIRE || focusType == FocusPetType::WATER)
					{
						if(act_power > 10)
							act_power = 10;
					}
					else
					{
						if(act_power > 25)
							act_power = 25;
					}
				}
			}
		}
	}
	else if (petpower > 0) {
		act_power = petpower;
		scale_pet = true;
	}

	if(EQDEBUG>8) Message(CC_Red, "Power is: %d", act_power);
	// optional rule: classic style variance in pets. Achieve this by
	// adding a random 0-4 to pet power, since it only comes in increments
	// of five from focus effects.

	//we copy the npc_type data because we need to edit it a bit
	NPCType *npc_type = new NPCType;
	memcpy(npc_type, base, sizeof(NPCType));

	// If pet power is set to -1 in the DB, use stat scaling
	if (this->IsClient() && record.petpower == -1)
	{
		if(scale_pet) {
			float scale_power = (float)act_power / 100.0f;
			if(scale_power > 0)
			{
				npc_type->max_hp  = (int16) (npc_type->max_hp * (1 + scale_power));
				npc_type->cur_hp  = npc_type->max_hp;
				npc_type->AC	  = (int16) (npc_type->AC * (1 + scale_power));
				npc_type->level  += (int16) 1 + ((int16)act_power / 25); // gains an additional level for every 25 pet power
				npc_type->min_dmg = (int16) (npc_type->min_dmg * (1 + (scale_power / 2)));
				npc_type->max_dmg = (int16) (npc_type->max_dmg * (1 + (scale_power / 2)));
				npc_type->size	  = (npc_type->size * (1 + scale_power));
			}
			record.petpower = act_power;
		}
	}

	//Live AA - Elemental Durability
	int16 MaxHP = aabonuses.PetMaxHP + itembonuses.PetMaxHP + spellbonuses.PetMaxHP;

	if (MaxHP){
		npc_type->max_hp += (npc_type->max_hp*MaxHP)/100;
		npc_type->cur_hp = npc_type->max_hp;
	}

	//TODO: think about regen (engaged vs. not engaged)

	// Pet naming:
	// 0 - `s pet
	// 1 - `s familiar
	// 2 - `s Warder
	// 3 - Random name if client, `s pet for others
	// 4 - Keep DB name


	if (petname != nullptr) {
		// Name was provided, use it.
		strn0cpy(npc_type->name, petname, 64);
	} else if (record.petnaming == 0) {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	} else if (record.petnaming == 1) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[19] = '\0';
		strcat(npc_type->name, "`s_familiar");
	} else if (record.petnaming == 2) {
		strcpy(npc_type->name, this->GetName());
		npc_type->name[21] = 0;
		strcat(npc_type->name, "`s_warder");
	} else if (record.petnaming == 4) {
		// Keep the DB name
	} else if (record.petnaming == 3 && IsClient()) {
		strcpy(npc_type->name, GetRandPetName());
	} else {
		strcpy(npc_type->name, this->GetCleanName());
		npc_type->name[25] = '\0';
		strcat(npc_type->name, "`s_pet");
	}

	//handle beastlord pet appearance
	if(record.petnaming == 2)
	{
		switch(GetBaseRace())
		{
		case VAHSHIR:
			npc_type->race = TIGER;
			npc_type->size *= 0.8f;
			break;
		case TROLL:
			npc_type->race = ALLIGATOR;
			npc_type->size *= 2.5f;
			break;
		case OGRE:
			npc_type->race = BEAR;
			npc_type->texture = 3;
			npc_type->gender = 2;
			break;
		case BARBARIAN:
			npc_type->race = WOLF;
			npc_type->texture = 2;
			break;
		case IKSAR:
			npc_type->race = WOLF;
			npc_type->texture = 0;
			npc_type->gender = 1;
			npc_type->luclinface = 0;
			break;
		default:
			npc_type->race = WOLF;
			npc_type->texture = 0;
		}
	}

	// handle monster summoning pet appearance
	if(record.monsterflag) {

		uint32 monsterid = 0;

		// get a random npc id from the spawngroups assigned to this zone
		auto query = StringFormat("SELECT npcID "
									"FROM (spawnentry INNER JOIN spawn2 ON spawn2.spawngroupID = spawnentry.spawngroupID) "
									"INNER JOIN npc_types ON npc_types.id = spawnentry.npcID "
									"WHERE spawn2.zone = '%s' AND npc_types.bodytype NOT IN (11, 33, 66, 67) "
									"AND npc_types.race NOT IN (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 44, "
									"55, 67, 71, 72, 73, 77, 78, 81, 90, 92, 93, 94, 106, 112, 114, 127, 128, "
									"130, 139, 141, 183, 236, 237, 238, 239, 254, 266, 329, 330, 378, 379, "
									"380, 381, 382, 383, 404, 522) "
									"ORDER BY RAND() LIMIT 1", zone->GetShortName());
		auto results = database.QueryDatabase(query);
		if (!results.Success()) {
			// if the database query failed
		}

		if (results.RowCount() != 0) {
			auto row = results.begin();
			monsterid = atoi(row[0]);
		}

		// since we don't have any monsters, just make it look like an earth pet for now
		if (monsterid == 0)
			monsterid = 567;

		// give the summoned pet the attributes of the monster we found
		const NPCType* monster = database.GetNPCType(monsterid);
		if(monster) {
			npc_type->race = monster->race;
			npc_type->size = monster->size;
			npc_type->texture = monster->texture;
			npc_type->gender = monster->gender;
			npc_type->luclinface = monster->luclinface;
			npc_type->helmtexture = monster->helmtexture;
		} else
			Log.Out(Logs::General, Logs::Error, "Error loading NPC data for monster summoning pet (NPC ID %d)", monsterid);

	}

	//this takes ownership of the npc_type data
	Pet *npc = new Pet(npc_type, this, (PetType)record.petcontrol, spell_id, record.petpower);

	// Now that we have an actual object to interact with, load
	// the base items for the pet. These are always loaded
	// so that a rank 1 suspend minion does not kill things
	// like the special back items some focused pets may receive.
	uint32 petinv[EmuConstants::EQUIPMENT_SIZE];
	memset(petinv, 0, sizeof(petinv));
	const Item_Struct *item = 0;

	if (database.GetBasePetItems(record.equipmentset, petinv)) {
		for (int i = 0; i<EmuConstants::EQUIPMENT_SIZE; i++)
			if (petinv[i]) {
				item = database.GetItem(petinv[i]);
				npc->AddLootDrop(item, &npc->itemlist, 0, 1, 127, true, true);
			}
	}

	npc->UpdateEquipmentLight();

	// finally, override size if one was provided
	if (in_size > 0.0f)
		npc->size = in_size;

	entity_list.AddNPC(npc, true, true);
	SetPetID(npc->GetID());
	// We need to handle PetType 5 (petHatelist), add the current target to the hatelist of the pet
}