Example #1
0
//healing and buffing aggro
int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible)
{
	int32 AggroAmount = 0;
	auto target_level = target ? target->GetLevel() : 1;
	bool ignore_default_buff = false; // rune/hot don't use the default 9, HP buffs that heal (virtue) do use the default

	for (int o = 0; o < EFFECT_COUNT; o++) {
		switch (spells[spell_id].effectid[o]) {
		case SE_CurrentHP: {
			if (heal_possible == 0) {
				AggroAmount += 1;
				break;
			}
			// hate based on base healing power of the spell
			int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o],
							 spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id);
			if (val > 0) {
				if (heal_possible < val)
					val = heal_possible; // capped to amount healed
				val = 2 * val / 3; // 3:2 ratio

				if (target_level > 50 && val > 1500)
					val = 1500; // target 51+ seems ~1500
				else if (target_level <= 50 && val > 800)
					val = 800; // per live patch notes, capped to 800
			}
			AggroAmount += std::max(val, 1);
			break;
		}
		case SE_Rune:
			AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o],
							 spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id) * 2;
			ignore_default_buff = true;
			break;
		case SE_HealOverTime:
			AggroAmount += 10;
			ignore_default_buff = true;
			break;
		default:
			break;
		}
	}
	if (GetOwner() && IsPet())
		AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;

	if (!ignore_default_buff && IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id))
		AggroAmount = IsBardSong(spell_id) ? 2 : 9;

	if (AggroAmount > 0) {
		int HateMod = RuleI(Aggro, SpellAggroMod);
		HateMod += GetFocusEffect(focusSpellHateMod, spell_id);

		//Live AA - Spell casting subtlety
		HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod;

		AggroAmount = (AggroAmount * HateMod) / 100;
	}

	return std::max(0, AggroAmount);
}
Example #2
0
//healing and buffing aggro
int32 Mob::CheckHealAggroAmount(uint16 spellid, uint32 heal_possible) {
	uint16 spell_id = spellid;
	int32 AggroAmount = 0;

	for (int o = 0; o < EFFECT_COUNT; o++) {
		switch(spells[spell_id].effectid[o]) {
			case SE_CurrentHP: {
				AggroAmount += spells[spell_id].mana;
				break;
			}
			case SE_Rune: {
				AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[0], spells[spell_id].base[0], spells[spell_id].max[o], this->GetLevel(), spellid) * 2;
				break;
			}
			case SE_HealOverTime:{
				AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], this->GetLevel(), spell_id);
				break;
			}
			default:{
				break;
			}
		}
	}
	if (IsBardSong(spell_id))
		AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100;
	if (GetOwner() && IsPet())
		AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;

	if(AggroAmount > 0)
	{
		int HateMod = RuleI(Aggro, SpellAggroMod);

		if(IsClient())
		{
			HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id);
		}

		//Live AA - Spell casting subtlety
		HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod;

		AggroAmount = (AggroAmount * HateMod) / 100;

		//made up number probably scales a bit differently on live but it seems like it will be close enough
		//every time you cast on live you get a certain amount of "this is a spell" aggro
		//confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected
		//by hate modifiers either.
		//AggroAmount += (slevel*slevel/72); // Moved Below


	}


	if(AggroAmount < 0)
		return 0;
	else
		return AggroAmount;
}
Example #3
0
// checks if this spell can be targeted
bool IsTGBCompatibleSpell(uint16 spell_id)
{
	if (IsValidSpell(spell_id) &&
			(!IsDetrimentalSpell(spell_id) && spells[spell_id].buffduration != 0 &&
			 !IsBardSong(spell_id) && !IsEffectInSpell(spell_id, SE_Illusion)))
		return true;

	return false;
}
Example #4
0
int32 Client::GetActSpellDuration(uint16 spell_id, int32 duration)
{
	if (spells[spell_id].not_extendable)
		return duration;

	int increase = 100;
	increase += GetFocusEffect(focusSpellDuration, spell_id);
	int tic_inc = 0;
	tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id);

	// Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1
	// However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync
	if (!(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
			IsFearSpell(spell_id) ||
			IsCharmSpell(spell_id) ||
			IsMezSpell(spell_id) ||
			IsBlindSpell(spell_id))
		tic_inc += 1;

	return (((duration * increase) / 100) + tic_inc);
}
Example #5
0
//offensive spell aggro
int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
{
	if (NoDetrimentalSpellAggro(spell_id))
		return 0;

	int32 AggroAmount = 0;
	int32 nonModifiedAggro = 0;
	uint16 slevel = GetLevel();
	bool dispel = false;
	bool on_hatelist = target ? target->CheckAggro(this) : false;
	int proc_cap = RuleI(Aggro, MaxScalingProcAggro);
	int hate_cap = isproc && proc_cap != -1 ? proc_cap : 1200;

	int32 target_hp = target ? target->GetMaxHP() : 18000; // default to max
	int32 default_aggro = 25;
	if (target_hp >= 18000) // max
		default_aggro = hate_cap;
	else if (target_hp >= 390) // min, 390 is the first number with int division that is 26
		default_aggro = target_hp / 15;

	for (int o = 0; o < EFFECT_COUNT; o++) {
		switch (spells[spell_id].effectid[o]) {
			case SE_CurrentHPOnce:
			case SE_CurrentHP: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if(val < 0)
					AggroAmount -= val;
				break;
			}
			case SE_MovementSpeed: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount += default_aggro;
				break;
			}
			case SE_AttackSpeed:
			case SE_AttackSpeed2:
			case SE_AttackSpeed3: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 100)
					AggroAmount += default_aggro;
				break;
			}
			case SE_Stun:
			case SE_Blind:
			case SE_Mez:
			case SE_Charm:
			case SE_Fear:
				AggroAmount += default_aggro;
				break;
			case SE_Root:
				AggroAmount += 10;
				break;
			case SE_ACv2:
			case SE_ArmorClass: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount += default_aggro;
				break;
			}
			case SE_ATK:
			case SE_ResistMagic:
			case SE_ResistFire:
			case SE_ResistCold:
			case SE_ResistPoison:
			case SE_ResistDisease:
			case SE_STR:
			case SE_STA:
			case SE_DEX:
			case SE_AGI:
			case SE_INT:
			case SE_WIS:
			case SE_CHA: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount += 10;
				break;
			}
			case SE_ResistAll: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount += 50;
				break;
			}
			case SE_AllStats: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount += 70;
				break;
			}
			case SE_BardAEDot:
				AggroAmount += 10;
				break;
			case SE_SpinTarget:
			case SE_Amnesia:
			case SE_Silence:
			case SE_Destroy:
				AggroAmount += default_aggro;
				break;
			// unsure -- leave them this for now
			case SE_Harmony:
			case SE_CastingLevel:
			case SE_MeleeMitigation:
			case SE_CriticalHitChance:
			case SE_AvoidMeleeChance:
			case SE_RiposteChance:
			case SE_DodgeChance:
			case SE_ParryChance:
			case SE_DualWieldChance:
			case SE_DoubleAttackChance:
			case SE_MeleeSkillCheck:
			case SE_HitChance:
			case SE_DamageModifier:
			case SE_MinDamageModifier:
			case SE_IncreaseBlockChance:
			case SE_Accuracy:
			case SE_DamageShield:
			case SE_SpellDamageShield:
			case SE_ReverseDS: {
				AggroAmount += slevel * 2;
				break;
			}
			// unsure -- leave them this for now
			case SE_CurrentMana:
			case SE_ManaRegen_v2:
			case SE_ManaPool:
			case SE_CurrentEndurance: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount -= val * 2;
				break;
			}
			case SE_CancelMagic:
			case SE_DispelDetrimental:
				dispel = true;
				break;
			case SE_ReduceHate:
			case SE_InstantHate:
				nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				break;
		}
	}

	if (IsBardSong(spell_id) && AggroAmount > 40)
		AggroAmount = 40; // bard songs seem to cap to 40 for most of their spells?

	if (dispel && target && target->GetHateAmount(this) < 100)
		AggroAmount += 50;

	if (spells[spell_id].HateAdded > 0) // overrides the hate (ex. tash)
		AggroAmount = spells[spell_id].HateAdded;

	if (GetOwner() && IsPet())
		AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;

	// hate focus ignored on first action for some reason
	if (!on_hatelist && AggroAmount > 0) {
		int HateMod = RuleI(Aggro, SpellAggroMod);
		HateMod += GetFocusEffect(focusSpellHateMod, spell_id);

		AggroAmount = (AggroAmount * HateMod) / 100;
	}

	// initial aggro gets a bonus 100 besides for dispel or hate override
	// We add this 100 in AddToHateList so we need to account for the oddities here
	if (dispel && spells[spell_id].HateAdded > 0 && !on_hatelist)
		AggroAmount -= 100;

	return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro;
}
Example #6
0
//offensive spell aggro
int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
{
	int32 AggroAmount = 0;
	int32 nonModifiedAggro = 0;
	uint16 slevel = GetLevel();

	for (int o = 0; o < EFFECT_COUNT; o++) {
		switch (spells[spell_id].effectid[o]) {
			case SE_CurrentHPOnce:
			case SE_CurrentHP: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if(val < 0)
					AggroAmount -= val;
				break;
			}
			case SE_MovementSpeed: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount += (2 + ((slevel * slevel) / 8));
				break;
			}
			case SE_AttackSpeed:
			case SE_AttackSpeed2:
			case SE_AttackSpeed3: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 100)
					AggroAmount += (5 + ((slevel * slevel) / 5));
				break;
			}
			case SE_Stun: {
				int val = (5 + ((slevel * slevel) / 6));
				if (isproc && RuleI(Aggro,MaxStunProcAggro) > -1 && (val > RuleI(Aggro,MaxStunProcAggro)))
					val = RuleI(Aggro,MaxStunProcAggro);
				AggroAmount += val;
				break;
			}
			case SE_Blind: {
				AggroAmount += (5 + ((slevel * slevel) / 6));
				break;
			}
			case SE_Mez: {
				AggroAmount += (5 + ((slevel * slevel) / 5));
				break;
			}
			case SE_Charm: {
				AggroAmount += (5 + ((slevel * slevel) / 5));
				break;
			}
			case SE_Root: {
				AggroAmount += (2 + ((slevel * slevel) / 8));
				break;
			}
			case SE_Fear: {
				AggroAmount += (5 + ((slevel * slevel) / 6));
				break;
			}
			case SE_ATK:
			case SE_ACv2:
			case SE_ArmorClass: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount -= val * 2;
				break;
			}
			case SE_ResistMagic:
			case SE_ResistFire:
			case SE_ResistCold:
			case SE_ResistPoison:
			case SE_ResistDisease: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount -= val * 3;
				break;
			}
			case SE_ResistAll: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount -= val * 6;
				break;
			}
			case SE_STR:
			case SE_STA:
			case SE_DEX:
			case SE_AGI:
			case SE_INT:
			case SE_WIS:
			case SE_CHA: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount -= val * 2;
				break;
			}
			case SE_AllStats: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount -= val * 6;
				break;
			}
			case SE_BardAEDot: {
				AggroAmount += slevel * 2;
				break;
			}
			case SE_SpinTarget: {
				AggroAmount += (5 + ((slevel * slevel) / 5));
				break;
			}
			case SE_Amnesia:
			case SE_Silence: {
				AggroAmount += slevel * 2;
				break;
			}
			case SE_Destroy: {
				AggroAmount += slevel * 2;
				break;
			}
			case SE_Harmony:
			case SE_CastingLevel:
			case SE_MeleeMitigation:
			case SE_CriticalHitChance:
			case SE_AvoidMeleeChance:
			case SE_RiposteChance:
			case SE_DodgeChance:
			case SE_ParryChance:
			case SE_DualWieldChance:
			case SE_DoubleAttackChance:
			case SE_MeleeSkillCheck:
			case SE_HitChance:
			case SE_IncreaseArchery:
			case SE_DamageModifier:
			case SE_MinDamageModifier:
			case SE_IncreaseBlockChance:
			case SE_Accuracy:
			case SE_DamageShield:
			case SE_SpellDamageShield:
			case SE_ReverseDS: {
				AggroAmount += slevel * 2;
				break;
			}
			case SE_CurrentMana:
			case SE_ManaRegen_v2:
			case SE_ManaPool:
			case SE_CurrentEndurance: {
				int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				if (val < 0)
					AggroAmount -= val * 2;
				break;
			}
			case SE_CancelMagic:
			case SE_DispelDetrimental: {
				AggroAmount += slevel;
				break;
			}
			case SE_ReduceHate:
			case SE_InstantHate: {
				nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
				break;
			}
		}
	}

	if (IsAEDurationSpell(spell_id))
		AggroAmount /= 2;

	if (spells[spell_id].HateAdded > 0)
		AggroAmount = spells[spell_id].HateAdded;

	if (IsBardSong(spell_id))
		AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100;
	if (GetOwner() && IsPet())
		AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;

	if (AggroAmount > 0) {

		int HateMod = RuleI(Aggro, SpellAggroMod);

		if (IsClient())
			HateMod += CastToClient()->GetFocusEffect(focusSpellHateMod, spell_id);

		AggroAmount = (AggroAmount * HateMod) / 100;

		//made up number probably scales a bit differently on live but it seems like it will be close enough
		//every time you cast on live you get a certain amount of "this is a spell" aggro
		//confirmed by EQ devs to be 100 exactly at level 85. From their wording it doesn't seem like it's affected
		//by hate modifiers either.
		//AggroAmount += (slevel*slevel/72);
		// Saved so I can reimplement it;
		// this should only be on the spell to aggro the npc not every spell

	}

	return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro;
}
Example #7
0
File: aa.cpp Project: af4t/Server
void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
	AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
	if(!rank) {
		return;
	}

	AA::Ability *ability = rank->base_ability;
	if(!ability) {
		return;
	}

	if(!IsValidSpell(rank->spell)) {
		return;
	}

	if(!CanUseAlternateAdvancementRank(rank)) {
		return;
	}

	//make sure it is not a passive
	if(!rank->effects.empty()) {
		return;
	}

	uint32 charges = 0;
	// We don't have the AA
	if (!GetAA(rank_id, &charges))
		return;

	//if expendable make sure we have charges
	if(ability->charges > 0 && charges < 1)
		return;

	//check cooldown
	if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart, false)) {
		uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart);
		uint32 aaremain_hr = aaremain / (60 * 60);
		uint32 aaremain_min = (aaremain / 60) % 60;
		uint32 aaremain_sec = aaremain % 60;

		if(aaremain_hr >= 1) {
			Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds",
			aaremain_hr, aaremain_min, aaremain_sec);
		}
		else {
			Message(13, "You can use this ability again in %u minute(s) %u seconds",
			aaremain_min, aaremain_sec);
		}

		return;
	}

	//calculate cooldown
	int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
	if(cooldown < 0) {
		cooldown = 0;
	}

	if (!IsCastWhileInvis(rank->spell))
		CommonBreakInvisible();

	if (spells[rank->spell].sneak && (!hidden || (hidden && (Timer::GetCurrentTime() - tmHidden) < 4000))) {
		Message_StringID(MT_SpellFailure, SNEAK_RESTRICT);
		return;
	}
	//
	// Modern clients don't require pet targeted for AA casts that are ST_Pet
	if (spells[rank->spell].targettype == ST_Pet || spells[rank->spell].targettype == ST_SummonedPet)
		target_id = GetPetID();

	// extra handling for cast_not_standing spells
	if (!spells[rank->spell].cast_not_standing) {
		if (GetAppearance() == eaSitting) // we need to stand!
			SetAppearance(eaStanding, false);

		if (GetAppearance() != eaStanding) {
			Message_StringID(MT_SpellFailure, STAND_TO_CAST);
			return;
		}
	}

	// Bards can cast instant cast AAs while they are casting another song
	if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
		if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQEmu::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
			return;
		}
		ExpendAlternateAdvancementCharge(ability->id);
	} else {
		if(!CastSpell(rank->spell, target_id, EQEmu::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
			return;
		}
	}

	CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown);
	SendAlternateAdvancementTimer(rank->spell_type, 0, 0);
}
Example #8
0
void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
	AA::Rank *rank = zone->GetAlternateAdvancementRank(rank_id);
	if(!rank) {
		return;
	}

	AA::Ability *ability = rank->base_ability;
	if(!ability) {
		return;
	}

	if(!IsValidSpell(rank->spell)) {
		return;
	}

	if(!CanUseAlternateAdvancementRank(rank)) {
		return;
	}

	//make sure it is not a passive
	if(!rank->effects.empty()) {
		return;
	}

	uint32 charges = 0;
	// We don't have the AA
	if (!GetAA(rank_id, &charges))
		return;

	//if expendable make sure we have charges
	if(ability->charges > 0 && charges < 1)
		return;

	//check cooldown
	if(!p_timers.Expired(&database, rank->spell_type + pTimerAAStart)) {
		uint32 aaremain = p_timers.GetRemainingTime(rank->spell_type + pTimerAAStart);
		uint32 aaremain_hr = aaremain / (60 * 60);
		uint32 aaremain_min = (aaremain / 60) % 60;
		uint32 aaremain_sec = aaremain % 60;

		if(aaremain_hr >= 1) {
			Message(13, "You can use this ability again in %u hour(s) %u minute(s) %u seconds",
			aaremain_hr, aaremain_min, aaremain_sec);
		}
		else {
			Message(13, "You can use this ability again in %u minute(s) %u seconds",
			aaremain_min, aaremain_sec);
		}

		return;
	}

	//calculate cooldown
	int cooldown = rank->recast_time - GetAlternateAdvancementCooldownReduction(rank);
	if(cooldown < 0) {
		cooldown = 0;
	}

	if (!IsCastWhileInvis(rank->spell))
		CommonBreakInvisible();
	// Bards can cast instant cast AAs while they are casting another song
	if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
		if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), ALTERNATE_ABILITY_SPELL_SLOT, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
			return;
		}
		ExpendAlternateAdvancementCharge(ability->id);
	} else {
		if(!CastSpell(rank->spell, target_id, ALTERNATE_ABILITY_SPELL_SLOT, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
			return;
		}
	}

	CastToClient()->GetPTimers().Start(rank->spell_type + pTimerAAStart, cooldown);
	SendAlternateAdvancementTimer(rank->spell_type, 0, 0);
}
Example #9
0
uint32 Mob::GetInstrumentMod(uint16 spell_id) const
{
	if (GetClass() != BARD || spells[spell_id].IsDisciplineBuff) // Puretone is Singing but doesn't get any mod
		return 10;

	uint32 effectmod = 10;
	int effectmodcap = 0;
	bool nocap = false;
	if (RuleB(Character, UseSpellFileSongCap)) {
		effectmodcap = spells[spell_id].songcap / 10;
		// this looks a bit weird, but easiest way I could think to keep both systems working
		if (effectmodcap == 0)
			nocap = true;
		else
			effectmodcap += 10;
	} else {
		effectmodcap = RuleI(Character, BaseInstrumentSoftCap);
	}
	// this should never use spell modifiers...
	// if a spell grants better modifers, they are copied into the item mods
	// because the spells are supposed to act just like having the intrument.
	// item mods are in 10ths of percent increases
	// clickies (Symphony of Battle) that have a song skill don't get AA bonus for some reason
	// but clickies that are songs (selo's on Composers Greaves) do get AA mod as well
	switch (spells[spell_id].skill) {
	case SkillPercussionInstruments:
		if (itembonuses.percussionMod == 0 && spellbonuses.percussionMod == 0)
			effectmod = 10;
		else if (GetSkill(SkillPercussionInstruments) == 0)
			effectmod = 10;
		else if (itembonuses.percussionMod > spellbonuses.percussionMod)
			effectmod = itembonuses.percussionMod;
		else
			effectmod = spellbonuses.percussionMod;
		if (IsBardSong(spell_id))
			effectmod += aabonuses.percussionMod;
		break;
	case SkillStringedInstruments:
		if (itembonuses.stringedMod == 0 && spellbonuses.stringedMod == 0)
			effectmod = 10;
		else if (GetSkill(SkillStringedInstruments) == 0)
			effectmod = 10;
		else if (itembonuses.stringedMod > spellbonuses.stringedMod)
			effectmod = itembonuses.stringedMod;
		else
			effectmod = spellbonuses.stringedMod;
		if (IsBardSong(spell_id))
			effectmod += aabonuses.stringedMod;
		break;
	case SkillWindInstruments:
		if (itembonuses.windMod == 0 && spellbonuses.windMod == 0)
			effectmod = 10;
		else if (GetSkill(SkillWindInstruments) == 0)
			effectmod = 10;
		else if (itembonuses.windMod > spellbonuses.windMod)
			effectmod = itembonuses.windMod;
		else
			effectmod = spellbonuses.windMod;
		if (IsBardSong(spell_id))
			effectmod += aabonuses.windMod;
		break;
	case SkillBrassInstruments:
		if (itembonuses.brassMod == 0 && spellbonuses.brassMod == 0)
			effectmod = 10;
		else if (GetSkill(SkillBrassInstruments) == 0)
			effectmod = 10;
		else if (itembonuses.brassMod > spellbonuses.brassMod)
			effectmod = itembonuses.brassMod;
		else
			effectmod = spellbonuses.brassMod;
		if (IsBardSong(spell_id))
			effectmod += aabonuses.brassMod;
		break;
	case SkillSinging:
		if (itembonuses.singingMod == 0 && spellbonuses.singingMod == 0)
			effectmod = 10;
		else if (itembonuses.singingMod > spellbonuses.singingMod)
			effectmod = itembonuses.singingMod;
		else
			effectmod = spellbonuses.singingMod;
		if (IsBardSong(spell_id))
			effectmod += aabonuses.singingMod + spellbonuses.Amplification;
		break;
	default:
		effectmod = 10;
		return effectmod;
	}
	if (!RuleB(Character, UseSpellFileSongCap))
		effectmodcap += aabonuses.songModCap + spellbonuses.songModCap + itembonuses.songModCap;
	if (effectmod < 10)
		effectmod = 10;
	if (!nocap && effectmod > effectmodcap) // if the cap is calculated to be 0 using new rules, no cap.
		effectmod = effectmodcap;
	Log.Out(Logs::Detail, Logs::Spells, "%s::GetInstrumentMod() spell=%d mod=%d modcap=%d\n", GetName(), spell_id,
		effectmod, effectmodcap);
	return effectmod;
}
Example #10
0
void Client::ActivateAA(aaID activate){
	if (activate < 0 || activate >= aaHighestID)
		return;
	if (IsStunned() || IsFeared() || IsMezzed() || IsSilenced() || IsPet() || IsSitting() || GetFeigned())
		return;

	int AATimerID = GetAATimerID(activate);

	SendAA_Struct* aa2 = nullptr;
	aaID aaid = activate;
	uint8 activate_val = GetAA(activate);
	//this wasn't taking into acct multi tiered act talents before...
	if (activate_val == 0){
		aa2 = zone->FindAA(activate);
		if (!aa2){
			int i;
			int a;
			for (i = 1; i<MAX_AA_ACTION_RANKS; i++){
				a = activate - i;
				if (a <= 0)
					break;

				aa2 = zone->FindAA(a);
				if (aa2 != nullptr)
					break;
			}
		}
		if (aa2){
			aaid = (aaID)aa2->id;
			activate_val = GetAA(aa2->id);
		}
	}

	if (activate_val == 0){
		return;
	}

	if (aa2)
	{
		if (aa2->account_time_required)
		{
			if ((Timer::GetTimeSeconds() + account_creation) < aa2->account_time_required)
			{
				return;
			}
		}
	}

	if (!p_timers.Expired(&database, AATimerID + pTimerAAStart))
	{
		uint32 aaremain = p_timers.GetRemainingTime(AATimerID + pTimerAAStart);
		uint32 aaremain_hr = aaremain / (60 * 60);
		uint32 aaremain_min = (aaremain / 60) % 60;
		uint32 aaremain_sec = aaremain % 60;

		if (aa2) {
			if (aaremain_hr >= 1)	//1 hour or more
				Message(CC_Red, "You can use the ability %s again in %u hour(s) %u minute(s) %u seconds",
				aa2->name, aaremain_hr, aaremain_min, aaremain_sec);
			else	//less than an hour
				Message(CC_Red, "You can use the ability %s again in %u minute(s) %u seconds",
				aa2->name, aaremain_min, aaremain_sec);
		}
		else {
			if (aaremain_hr >= 1)	//1 hour or more
				Message(CC_Red, "You can use this ability again in %u hour(s) %u minute(s) %u seconds",
				aaremain_hr, aaremain_min, aaremain_sec);
			else	//less than an hour
				Message(CC_Red, "You can use this ability again in %u minute(s) %u seconds",
				aaremain_min, aaremain_sec);
		}
		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[aaid][activate_val];

	if ((aaid == aaImprovedHarmTouch || aaid == aaLeechTouch) && !p_timers.Expired(&database, pTimerHarmTouch)){
		Message(CC_Red, "Ability recovery time not yet met.");
		return;
	}

	//everything should be configured out now

	uint16 target_id = 0;

	//figure out our target
	switch (caa->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, AATimerID + pTimerAAStart);
			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;
	}

	//handle non-spell action
	if (caa->action != aaActionNone) {
		if (caa->mana_cost > 0) {
			if (GetMana() < caa->mana_cost) {
				Message_StringID(CC_Red, INSUFFICIENT_MANA);
				return;
			}
			SetMana(GetMana() - caa->mana_cost);
		}
		if (caa->reuse_time > 0)
		{
			uint32 timer_base = CalcAAReuseTimer(caa);
			if (activate == aaImprovedHarmTouch || activate == aaLeechTouch)
			{
				p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime);
			}
			p_timers.Start(AATimerID + pTimerAAStart, timer_base);
			SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr)));
		}
		HandleAAAction(aaid);
	}

	//cast the spell, if we have one
	if (caa->spell_id > 0 && caa->spell_id < SPDAT_RECORDS) {

		if (caa->reuse_time > 0)
		{
			uint32 timer_base = CalcAAReuseTimer(caa);
			SendAATimer(activate, static_cast<uint32>(time(nullptr)), static_cast<uint32>(time(nullptr)));
			p_timers.Start(AATimerID + pTimerAAStart, timer_base);
			if (activate == aaImprovedHarmTouch || activate == aaLeechTouch)
			{
				p_timers.Start(pTimerHarmTouch, HarmTouchReuseTime);
			}
			// Bards can cast instant cast AAs while they are casting another song
			if (spells[caa->spell_id].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
				if (!SpellFinished(caa->spell_id, entity_list.GetMob(target_id), 10, -1, -1, spells[caa->spell_id].ResistDiff, false)) {
					//Reset on failed cast
					SendAATimer(activate, 0, 0xFFFFFF);
					Message_StringID(CC_Yellow, ABILITY_FAILED);
					p_timers.Clear(&database, AATimerID + pTimerAAStart);
					return;
				}
			}
			else {
				if (!CastSpell(caa->spell_id, target_id, USE_ITEM_SPELL_SLOT, -1, -1, 0, -1, AATimerID + pTimerAAStart, timer_base, 1)) {
					//Reset on failed cast
					SendAATimer(activate, 0, 0xFFFFFF);
					Message_StringID(CC_Yellow, ABILITY_FAILED);
					p_timers.Clear(&database, AATimerID + pTimerAAStart);
					return;
				}
			}
		}
		else
		{
			if (!CastSpell(caa->spell_id, target_id))
				return;
		}
	}
}