This commit is contained in:
Michael Cook (mackal) 2015-07-16 22:38:32 -04:00
parent 417b034273
commit a52ab7ae48
10 changed files with 169 additions and 203 deletions

View File

@ -1,7 +1,10 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 07/16/2013 ==
mackal: Rework spell aggro based on http://www.eqemulator.org/forums/showthread.php?t=39819
== 07/15/2015 == == 07/15/2015 ==
Hateborne: Added optional ability to enforce task level requirements in perl and lua via an added, optional parameter to $client->AssignTask and quest::assigntask. Hateborne: Added optional ability to enforce task level requirements in perl and lua via an added, optional parameter to $client->AssignTask and quest::assigntask.
Use cases: Use cases:
quest::assigntask(703); # this still assigns the task as normal, no functional change quest::assigntask(703); # this still assigns the task as normal, no functional change
quest::assigntask(703, 1); # this will assign the task, provided the character meets the db-stored level requirements quest::assigntask(703, 1); # this will assign the task, provided the character meets the db-stored level requirements

View File

@ -44,12 +44,8 @@ void EntityList::CheckClientAggro(Client *around)
if (mob->IsClient()) //also ensures that mob != around if (mob->IsClient()) //also ensures that mob != around
continue; continue;
if (mob->CheckWillAggro(around)) { if (mob->CheckWillAggro(around) && !mob->CheckAggro(around))
if (mob->IsEngaged()) mob->AddToHateList(around, 100);
mob->AddToHateList(around);
else
mob->AddToHateList(around, mob->GetLevel());
}
} }
} }
@ -343,7 +339,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
{ {
//FatherNiwtit: make sure we can see them. last since it is very expensive //FatherNiwtit: make sure we can see them. last since it is very expensive
if(CheckLosFN(mob)) { if(CheckLosFN(mob)) {
Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName()); Log.Out(Logs::Detail, Logs::Aggro, "Check aggro for %s target %s.", GetName(), mob->GetName());
return( mod_will_aggro(mob, this) ); return( mod_will_aggro(mob, this) );
} }
} }
@ -468,8 +464,8 @@ void EntityList::AIYellForHelp(Mob* sender, Mob* attacker) {
if(mob->CheckLosFN(sender)) { if(mob->CheckLosFN(sender)) {
#if (EQDEBUG>=5) #if (EQDEBUG>=5)
Log.Out(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f", Log.Out(Logs::General, Logs::None, "AIYellForHelp(\"%s\",\"%s\") %s attacking %s Dist %f Z %f",
sender->GetName(), attacker->GetName(), mob->GetName(), sender->GetName(), attacker->GetName(), mob->GetName(),
attacker->GetName(), DistanceSquared(mob->GetPosition(), attacker->GetName(), DistanceSquared(mob->GetPosition(),
sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ())); sender->GetPosition()), fabs(sender->GetZ()+mob->GetZ()));
#endif #endif
mob->AddToHateList(attacker, 1, 0, false); mob->AddToHateList(attacker, 1, 0, false);
@ -880,11 +876,11 @@ bool Mob::CombatRange(Mob* other)
float _DistNoRoot = DistanceSquared(m_Position, other->GetPosition()); float _DistNoRoot = DistanceSquared(m_Position, other->GetPosition());
if (GetSpecialAbility(NPC_CHASE_DISTANCE)){ if (GetSpecialAbility(NPC_CHASE_DISTANCE)){
bool DoLoSCheck = true; bool DoLoSCheck = true;
float max_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0)); float max_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 0));
float min_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1)); float min_dist = static_cast<float>(GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 1));
if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2)) if (GetSpecialAbilityParam(NPC_CHASE_DISTANCE, 2))
DoLoSCheck = false; //Ignore line of sight check DoLoSCheck = false; //Ignore line of sight check
@ -897,10 +893,10 @@ bool Mob::CombatRange(Mob* other)
min_dist = size_mod; //Default to melee range min_dist = size_mod; //Default to melee range
else else
min_dist = min_dist * min_dist; min_dist = min_dist * min_dist;
if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist)) if ((DoLoSCheck && CheckLastLosState()) && (_DistNoRoot >= min_dist && _DistNoRoot <= max_dist))
SetPseudoRoot(true); SetPseudoRoot(true);
else else
SetPseudoRoot(false); SetPseudoRoot(false);
} }
@ -919,7 +915,7 @@ bool Mob::CheckLosFN(Mob* other) {
Result = CheckLosFN(other->GetX(), other->GetY(), other->GetZ(), other->GetSize()); Result = CheckLosFN(other->GetX(), other->GetY(), other->GetZ(), other->GetSize());
SetLastLosState(Result); SetLastLosState(Result);
return Result; return Result;
} }
@ -954,11 +950,24 @@ bool Mob::CheckLosFN(float posX, float posY, float posZ, float mobSize) {
} }
//offensive spell aggro //offensive spell aggro
int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc) int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
{ {
int32 AggroAmount = 0; int32 AggroAmount = 0;
int32 nonModifiedAggro = 0; int32 nonModifiedAggro = 0;
uint16 slevel = GetLevel(); uint16 slevel = GetLevel();
bool add_default = false;
bool stun_proc = false;
bool dispel = false;
bool on_hatelist = target ? target->CheckAggro(this) : false;
int32 target_hp = target ? target->GetMaxHP() : 18000; // default to max
int32 default_aggro = 0;
if (target_hp >= 18000) // max
default_aggro = 1200;
else if (target_hp < 390) // min, 390 is the first number with int division that is 26
default_aggro = 25;
else
default_aggro = target_hp / 15;
for (int o = 0; o < EFFECT_COUNT; o++) { for (int o = 0; o < EFFECT_COUNT; o++) {
switch (spells[spell_id].effectid[o]) { switch (spells[spell_id].effectid[o]) {
@ -972,7 +981,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
case SE_MovementSpeed: { 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); 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) if (val < 0)
AggroAmount += (2 + ((slevel * slevel) / 8)); add_default = true;
break; break;
} }
case SE_AttackSpeed: case SE_AttackSpeed:
@ -980,60 +989,35 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
case SE_AttackSpeed3: { 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); 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) if (val < 100)
AggroAmount += (5 + ((slevel * slevel) / 5)); add_default = true;
break; break;
} }
case SE_Stun: { case SE_Stun:
int val = (5 + ((slevel * slevel) / 6)); add_default = true;
if (isproc && RuleI(Aggro,MaxStunProcAggro) > -1 && (val > RuleI(Aggro,MaxStunProcAggro))) stun_proc = isproc;
val = RuleI(Aggro,MaxStunProcAggro);
AggroAmount += val;
break; break;
} case SE_Blind:
case SE_Blind: { case SE_Mez:
AggroAmount += (5 + ((slevel * slevel) / 6)); case SE_Charm:
case SE_Fear:
add_default = true;
break; break;
} case SE_Root:
case SE_Mez: { AggroAmount += 10;
AggroAmount += (5 + ((slevel * slevel) / 5));
break; 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_ATK:
case SE_ACv2: case SE_ACv2:
case SE_ArmorClass: { 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); 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) if (val < 0)
AggroAmount -= val * 2; add_default = true;
break; break;
} }
case SE_ResistMagic: case SE_ResistMagic:
case SE_ResistFire: case SE_ResistFire:
case SE_ResistCold: case SE_ResistCold:
case SE_ResistPoison: case SE_ResistPoison:
case SE_ResistDisease: { 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_STR:
case SE_STA: case SE_STA:
case SE_DEX: case SE_DEX:
@ -1043,32 +1027,31 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
case SE_CHA: { 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); 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) if (val < 0)
AggroAmount -= val * 2; 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; break;
} }
case SE_AllStats: { 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); 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) if (val < 0)
AggroAmount -= val * 6; AggroAmount += 70;
break; break;
} }
case SE_BardAEDot: { case SE_BardAEDot:
AggroAmount += slevel * 2; AggroAmount += 10;
break; break;
} case SE_SpinTarget:
case SE_SpinTarget: {
AggroAmount += (5 + ((slevel * slevel) / 5));
break;
}
case SE_Amnesia: case SE_Amnesia:
case SE_Silence: { case SE_Silence:
AggroAmount += slevel * 2; case SE_Destroy:
add_default = true;
break; break;
} // unsure -- leave them this for now
case SE_Destroy: {
AggroAmount += slevel * 2;
break;
}
case SE_Harmony: case SE_Harmony:
case SE_CastingLevel: case SE_CastingLevel:
case SE_MeleeMitigation: case SE_MeleeMitigation:
@ -1091,6 +1074,7 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
AggroAmount += slevel * 2; AggroAmount += slevel * 2;
break; break;
} }
// unsure -- leave them this for now
case SE_CurrentMana: case SE_CurrentMana:
case SE_ManaRegen_v2: case SE_ManaRegen_v2:
case SE_ManaPool: case SE_ManaPool:
@ -1101,94 +1085,105 @@ int32 Mob::CheckAggroAmount(uint16 spell_id, bool isproc)
break; break;
} }
case SE_CancelMagic: case SE_CancelMagic:
case SE_DispelDetrimental: { case SE_DispelDetrimental:
AggroAmount += slevel; dispel = true;
break; break;
}
case SE_ReduceHate: case SE_ReduceHate:
case SE_InstantHate: { case SE_InstantHate:
nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id); nonModifiedAggro = CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], slevel, spell_id);
break; break;
}
} }
} }
if (IsAEDurationSpell(spell_id)) if (add_default) {
AggroAmount /= 2; if (stun_proc && RuleI(Aggro, MaxStunProcAggro) > -1 && (default_aggro > RuleI(Aggro, MaxStunProcAggro)))
AggroAmount += RuleI(Aggro, MaxStunProcAggro);
else if (IsBardSong(spell_id) && default_aggro > 40)
AggroAmount += 40; // bard songs seem to cap to 40 for most of their spells?
else
AggroAmount += default_aggro;
}
if (spells[spell_id].HateAdded > 0) 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; AggroAmount = spells[spell_id].HateAdded;
if (IsBardSong(spell_id))
AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100;
if (GetOwner() && IsPet()) if (GetOwner() && IsPet())
AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;
if (AggroAmount > 0) { if (AggroAmount > 0) {
int HateMod = RuleI(Aggro, SpellAggroMod); int HateMod = RuleI(Aggro, SpellAggroMod);
HateMod += GetFocusEffect(focusSpellHateMod, spell_id); HateMod += GetFocusEffect(focusSpellHateMod, spell_id);
AggroAmount = (AggroAmount * HateMod) / 100; 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
} }
// initial aggro gets a bonus 100
if (!dispel && spells[spell_id].HateAdded == 0 && !on_hatelist)
AggroAmount += 100;
return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro; return AggroAmount + spells[spell_id].bonushate + nonModifiedAggro;
} }
//healing and buffing aggro //healing and buffing aggro
int32 Mob::CheckHealAggroAmount(uint16 spell_id, uint32 heal_possible) int32 Mob::CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible)
{ {
int32 AggroAmount = 0; int32 AggroAmount = 0;
auto target_level = target ? target->GetLevel() : GetLevel();
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++) { for (int o = 0; o < EFFECT_COUNT; o++) {
switch (spells[spell_id].effectid[o]) { switch (spells[spell_id].effectid[o]) {
case SE_CurrentHP: { case SE_CurrentHP: {
AggroAmount += IsBuffSpell(spell_id) ? spells[spell_id].mana / 4 : spells[spell_id].mana; if (heal_possible == 0) {
AggroAmount += 1;
break; break;
} }
case SE_Rune: { // hate based on base healing power of the spell
AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[0], spells[spell_id].base[0], spells[spell_id].max[o], GetLevel(), spell_id) * 2; int val = CalcSpellEffectValue_formula(spells[spell_id].formula[o],
break; spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id);
} if (val > 0) {
case SE_HealOverTime: { if (heal_possible < val)
AggroAmount += CalcSpellEffectValue_formula(spells[spell_id].formula[o], spells[spell_id].base[o], spells[spell_id].max[o], GetLevel(), spell_id); val = heal_possible; // capped to amount healed
break; val = 2 * val / 3; // 3:2 ratio
}
default: { if (target_level > 50 && val > 1500)
break; 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[0],
spells[spell_id].base[0], 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 (IsBardSong(spell_id))
AggroAmount = AggroAmount * RuleI(Aggro, SongAggroMod) / 100;
if (GetOwner() && IsPet()) if (GetOwner() && IsPet())
AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100; AggroAmount = AggroAmount * RuleI(Aggro, PetSpellAggroMod) / 100;
if (!ignore_default_buff && IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id))
AggroAmount = 9;
if (AggroAmount > 0) { if (AggroAmount > 0) {
int HateMod = RuleI(Aggro, SpellAggroMod); int HateMod = RuleI(Aggro, SpellAggroMod);
HateMod += GetFocusEffect(focusSpellHateMod, spell_id); HateMod += GetFocusEffect(focusSpellHateMod, spell_id);
//Live AA - Spell casting subtlety //Live AA - Spell casting subtlety
HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod; HateMod += aabonuses.hatemod + spellbonuses.hatemod + itembonuses.hatemod;
AggroAmount = (AggroAmount * HateMod) / 100; 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) if (AggroAmount < 0)
@ -1249,7 +1244,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
return true; return true;
float resist_check = 0; float resist_check = 0;
if(IsCharmSpell(spell_id)) { if(IsCharmSpell(spell_id)) {
if (spells[spell_id].powerful_flag == -1) //If charm spell has this set(-1), it can not break till end of duration. if (spells[spell_id].powerful_flag == -1) //If charm spell has this set(-1), it can not break till end of duration.
@ -1265,7 +1260,7 @@ bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0, false, true); resist_check = ResistSpell(spells[spell_id].resisttype, spell_id, caster, false,0, false, true);
//2: The mob makes a resistance check against the charm //2: The mob makes a resistance check against the charm
if (resist_check == 100) if (resist_check == 100)
return true; return true;
else else

View File

@ -6220,14 +6220,14 @@ bool Bot::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
} }
int32 Bot::CheckAggroAmount(uint16 spellid) { int32 Bot::CheckAggroAmount(uint16 spellid) {
int32 AggroAmount = Mob::CheckAggroAmount(spellid); int32 AggroAmount = Mob::CheckAggroAmount(spellid, nullptr);
int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid);
AggroAmount = (AggroAmount * (100 + focusAggro) / 100); AggroAmount = (AggroAmount * (100 + focusAggro) / 100);
return AggroAmount; return AggroAmount;
} }
int32 Bot::CheckHealAggroAmount(uint16 spellid, uint32 heal_possible) { int32 Bot::CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible) {
int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, heal_possible); int32 AggroAmount = Mob::CheckHealAggroAmount(spellid, target, heal_possible);
int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid); int32 focusAggro = GetBotFocusEffect(BotfocusSpellHateMod, spellid);
AggroAmount = (AggroAmount * (100 + focusAggro) / 100); AggroAmount = (AggroAmount * (100 + focusAggro) / 100);
return AggroAmount; return AggroAmount;
@ -9081,7 +9081,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
return; return;
} }
} }
uint32 botId = GetBotIDByBotName(std::string(sep->arg[2])); uint32 botId = GetBotIDByBotName(std::string(sep->arg[2]));
if(GetBotOwnerCharacterID(botId, &TempErrorMessage) != c->CharacterID()) { if(GetBotOwnerCharacterID(botId, &TempErrorMessage) != c->CharacterID()) {
c->Message(0, "You can't spawn a bot that you don't own."); c->Message(0, "You can't spawn a bot that you don't own.");
@ -9106,7 +9106,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
c->Message(0, "You can't summon bots while you are engaged."); c->Message(0, "You can't summon bots while you are engaged.");
return; return;
} }
if(g && g->members[i] && g->members[i]->qglobal) if(g && g->members[i] && g->members[i]->qglobal)
return; return;
} }
@ -9264,12 +9264,12 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
c->Message(13, "Database Error: %s", TempErrorMessage.c_str()); c->Message(13, "Database Error: %s", TempErrorMessage.c_str());
return; return;
} }
if(item == nullptr) { if(item == nullptr) {
c->Message(15, "I need something for my %s (Item %i)", equipped[i], (i == 22 ? 9999 : i)); c->Message(15, "I need something for my %s (Item %i)", equipped[i], (i == 22 ? 9999 : i));
continue; continue;
} }
if((i == MainPrimary) && ((item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HPiercing))) { if((i == MainPrimary) && ((item->ItemType == ItemType2HSlash) || (item->ItemType == ItemType2HBlunt) || (item->ItemType == ItemType2HPiercing))) {
is2Hweapon = true; is2Hweapon = true;
} }
@ -9284,7 +9284,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
} }
else else
c->Message(15, "You must target a bot first."); c->Message(15, "You must target a bot first.");
return; return;
} }
@ -9301,7 +9301,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
c->Message(15, "A bot has 22 slots in its inventory, please choose a slot between 0 and 21 or 9999."); c->Message(15, "A bot has 22 slots in its inventory, please choose a slot between 0 and 21 or 9999.");
return; return;
} }
const char* equipped[EmuConstants::EQUIPMENT_SIZE + 1] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back", const char* equipped[EmuConstants::EQUIPMENT_SIZE + 1] = {"Charm", "Left Ear", "Head", "Face", "Right Ear", "Neck", "Shoulders", "Arms", "Back",
"Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand", "Left Wrist", "Right Wrist", "Range", "Hands", "Primary Hand", "Secondary Hand",
"Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo", "Powersource" }; "Left Finger", "Right Finger", "Chest", "Legs", "Feet", "Waist", "Ammo", "Powersource" };
@ -9336,7 +9336,7 @@ void Bot::ProcessBotCommands(Client *c, const Seperator *sep) {
Bot *gearbot = c->GetTarget()->CastToBot(); Bot *gearbot = c->GetTarget()->CastToBot();
if((slotId == MainRange)||(slotId == MainAmmo)||(slotId == MainPrimary)||(slotId == MainSecondary)) if((slotId == MainRange)||(slotId == MainAmmo)||(slotId == MainPrimary)||(slotId == MainSecondary))
gearbot->SetBotArcher(false); gearbot->SetBotArcher(false);
gearbot->RemoveBotItemBySlot(slotId, &TempErrorMessage); gearbot->RemoveBotItemBySlot(slotId, &TempErrorMessage);
if(!TempErrorMessage.empty()) { if(!TempErrorMessage.empty()) {

View File

@ -200,7 +200,7 @@ public:
virtual Mob* GetOwner(); virtual Mob* GetOwner();
virtual Mob* GetOwnerOrSelf(); virtual Mob* GetOwnerOrSelf();
inline virtual bool HasOwner() { return (GetBotOwner() ? true : false); } inline virtual bool HasOwner() { return (GetBotOwner() ? true : false); }
virtual int32 CheckHealAggroAmount(uint16 spellid, uint32 heal_possible = 0); virtual int32 CheckHealAggroAmount(uint16 spellid, Mob *target, uint32 heal_possible = 0);
virtual int32 CalcMaxMana(); virtual int32 CalcMaxMana();
virtual void SetAttackTimer(); virtual void SetAttackTimer();
uint32 GetClassHPFactor(); uint32 GetClassHPFactor();
@ -547,7 +547,7 @@ public:
void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; } void SetNumHealRotationMembers( uint8 numMembers ) { _numHealRotationMembers = numMembers; }
void SetBardUseOutOfCombatSongs(bool useOutOfCombatSongs) { _bardUseOutOfCombatSongs = useOutOfCombatSongs;} void SetBardUseOutOfCombatSongs(bool useOutOfCombatSongs) { _bardUseOutOfCombatSongs = useOutOfCombatSongs;}
void SetShowHelm(bool showhelm) { _showhelm = showhelm; } void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
std::string CreateSayLink(Client* botOwner, const char* message, const char* name); std::string CreateSayLink(Client* botOwner, const char* message, const char* name);
// Class Destructors // Class Destructors

View File

@ -3068,56 +3068,24 @@ bool EntityList::Fighting(Mob *targ)
return false; return false;
} }
void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 thedam) void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 hate)
{ {
NPC *cur = nullptr; if (hate == 0)
uint16 count = 0; return;
std::list<NPC *> npc_sub_list;
auto it = npc_list.begin();
while (it != npc_list.end()) {
cur = it->second;
if (!cur->CheckAggro(target)) { for (auto &e : npc_list) {
++it; auto &npc = e.second;
if (!npc->CheckAggro(target) || npc->IsFeared())
continue; continue;
}
if (!cur->IsMezzed() && !cur->IsStunned() && !cur->IsFeared()) {
npc_sub_list.push_back(cur);
++count;
}
++it;
}
if (zone->random.Roll(15)) // witness check -- place holder
// This is either a level check (con color check?) or a stat roll
continue;
if (thedam > 1) { if ((npc->IsMezzed() || npc->IsStunned()) && hate > 4) // patch notes say stunned/mezzed NPCs get a fraction of the hate
if (count > 0) npc->AddToHateList(caster, hate / 4); // made up number
thedam /= count; else
npc->AddToHateList(caster, hate);
if (thedam < 1)
thedam = 1;
}
cur = nullptr;
auto sit = npc_sub_list.begin();
while (sit != npc_sub_list.end()) {
cur = *sit;
if (cur->IsPet()) {
if (caster) {
if (cur->CheckAggro(caster)) {
cur->AddToHateList(caster, thedam);
}
}
} else {
if (caster) {
if (cur->CheckAggro(caster)) {
cur->AddToHateList(caster, thedam);
} else {
cur->AddToHateList(caster, thedam * 0.33);
}
}
}
++sit;
} }
} }

View File

@ -41,7 +41,7 @@ class EntityList;
class Group; class Group;
class Merc; class Merc;
class Mob; class Mob;
class NPC; class NPC;
class Object; class Object;
class Petition; class Petition;
class Raid; class Raid;
@ -333,7 +333,7 @@ public:
void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos); void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos);
Trap* FindNearbyTrap(Mob* searcher, float max_dist); Trap* FindNearbyTrap(Mob* searcher, float max_dist);
void AddHealAggro(Mob* target, Mob* caster, uint16 thedam); void AddHealAggro(Mob* target, Mob* caster, uint16 hate);
Mob* FindDefenseNPC(uint32 npcid); Mob* FindDefenseNPC(uint32 npcid);
void OpenDoorsNear(NPC* opener); void OpenDoorsNear(NPC* opener);
void UpdateWho(bool iSendFullUpdate = false); void UpdateWho(bool iSendFullUpdate = false);

View File

@ -1172,22 +1172,22 @@ bool Lua_Mob::Charmed() {
int Lua_Mob::CheckAggroAmount(int spell_id) { int Lua_Mob::CheckAggroAmount(int spell_id) {
Lua_Safe_Call_Int(); Lua_Safe_Call_Int();
return self->CheckAggroAmount(spell_id); return self->CheckAggroAmount(spell_id, nullptr);
} }
int Lua_Mob::CheckAggroAmount(int spell_id, bool is_proc) { int Lua_Mob::CheckAggroAmount(int spell_id, bool is_proc) {
Lua_Safe_Call_Int(); Lua_Safe_Call_Int();
return self->CheckAggroAmount(spell_id, is_proc); return self->CheckAggroAmount(spell_id, nullptr, is_proc);
} }
int Lua_Mob::CheckHealAggroAmount(int spell_id) { int Lua_Mob::CheckHealAggroAmount(int spell_id) {
Lua_Safe_Call_Int(); Lua_Safe_Call_Int();
return self->CheckHealAggroAmount(spell_id); return self->CheckHealAggroAmount(spell_id, nullptr);
} }
int Lua_Mob::CheckHealAggroAmount(int spell_id, uint32 heal_possible) { int Lua_Mob::CheckHealAggroAmount(int spell_id, uint32 heal_possible) {
Lua_Safe_Call_Int(); Lua_Safe_Call_Int();
return self->CheckHealAggroAmount(spell_id, heal_possible); return self->CheckHealAggroAmount(spell_id, nullptr, heal_possible);
} }
int Lua_Mob::GetAA(int id) { int Lua_Mob::GetAA(int id) {

View File

@ -879,8 +879,8 @@ public:
bool Charmed() const { return charmed; } bool Charmed() const { return charmed; }
static uint32 GetLevelHP(uint8 tlevel); static uint32 GetLevelHP(uint8 tlevel);
uint32 GetZoneID() const; //for perl uint32 GetZoneID() const; //for perl
virtual int32 CheckAggroAmount(uint16 spell_id, bool isproc = false); virtual int32 CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc = false);
virtual int32 CheckHealAggroAmount(uint16 spell_id, uint32 heal_possible = 0); virtual int32 CheckHealAggroAmount(uint16 spell_id, Mob *target, uint32 heal_possible = 0);
uint32 GetInstrumentMod(uint16 spell_id) const; uint32 GetInstrumentMod(uint16 spell_id) const;
int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0); int CalcSpellEffectValue(uint16 spell_id, int effect_id, int caster_level = 1, uint32 instrument_mod = 10, Mob *caster = nullptr, int ticsremaining = 0);

View File

@ -6316,7 +6316,7 @@ XS(XS_Mob_CheckAggroAmount)
if(THIS == nullptr) if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->CheckAggroAmount(spellid); RETVAL = THIS->CheckAggroAmount(spellid, nullptr);
XSprePUSH; PUSHu((UV)RETVAL); XSprePUSH; PUSHu((UV)RETVAL);
} }
XSRETURN(1); XSRETURN(1);
@ -6349,7 +6349,7 @@ XS(XS_Mob_CheckHealAggroAmount)
possible = (uint32)SvUV(ST(2)); possible = (uint32)SvUV(ST(2));
} }
RETVAL = THIS->CheckHealAggroAmount(spellid, possible); RETVAL = THIS->CheckHealAggroAmount(spellid, nullptr, possible);
XSprePUSH; PUSHu((UV)RETVAL); XSprePUSH; PUSHu((UV)RETVAL);
} }
XSRETURN(1); XSRETURN(1);
@ -7405,7 +7405,7 @@ XS(XS_Mob_GetGlobal)
RETVAL = ret_val.c_str(); RETVAL = ret_val.c_str();
sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG; sv_setpv(TARG, RETVAL); XSprePUSH; PUSHTARG;
} }
XSRETURN(1); XSRETURN(1);
} }
XS(XS_Mob_SetGlobal); XS(XS_Mob_SetGlobal);

View File

@ -3704,7 +3704,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
} }
if (spelltar->IsAIControlled()) { if (spelltar->IsAIControlled()) {
int32 aggro = CheckAggroAmount(spell_id); int32 aggro = CheckAggroAmount(spell_id, spelltar);
if (aggro > 0) { if (aggro > 0) {
if (!IsHarmonySpell(spell_id)) if (!IsHarmonySpell(spell_id))
spelltar->AddToHateList(this, aggro); spelltar->AddToHateList(this, aggro);
@ -3733,20 +3733,20 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
spelltar->DamageShield(this, true); spelltar->DamageShield(this, true);
if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) { if (spelltar->IsAIControlled() && IsDetrimentalSpell(spell_id) && !IsHarmonySpell(spell_id)) {
int32 aggro_amount = CheckAggroAmount(spell_id, isproc); int32 aggro_amount = CheckAggroAmount(spell_id, spelltar, isproc);
Log.Out(Logs::Detail, Logs::Spells, "Spell %d cast on %s generated %d hate", spell_id, spelltar->GetName(), aggro_amount); Log.Out(Logs::Detail, Logs::Spells, "Spell %d cast on %s generated %d hate", spell_id,
if(aggro_amount > 0) spelltar->GetName(), aggro_amount);
spelltar->AddToHateList(this, aggro_amount); else{ if (aggro_amount > 0) {
spelltar->AddToHateList(this, aggro_amount);
} else {
int32 newhate = spelltar->GetHateAmount(this) + aggro_amount; int32 newhate = spelltar->GetHateAmount(this) + aggro_amount;
if (newhate < 1) { spelltar->SetHateAmountOnEnt(this, std::max(newhate, 1));
spelltar->SetHateAmountOnEnt(this,1);
} else {
spelltar->SetHateAmountOnEnt(this,newhate);
}
} }
} else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id)) {
entity_list.AddHealAggro(
spelltar, this,
CheckHealAggroAmount(spell_id, spelltar, (spelltar->GetMaxHP() - spelltar->GetHP())));
} }
else if (IsBeneficialSpell(spell_id) && !IsSummonPCSpell(spell_id))
entity_list.AddHealAggro(spelltar, this, CheckHealAggroAmount(spell_id, (spelltar->GetMaxHP() - spelltar->GetHP())));
// make sure spelltar is high enough level for the buff // make sure spelltar is high enough level for the buff
if(RuleB(Spells, BuffLevelRestrictions) && !spelltar->CheckSpellLevelRestriction(spell_id)) if(RuleB(Spells, BuffLevelRestrictions) && !spelltar->CheckSpellLevelRestriction(spell_id))
@ -4059,7 +4059,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(GetSpecialAbility(UNMEZABLE)) { if(GetSpecialAbility(UNMEZABLE)) {
Log.Out(Logs::Detail, Logs::Spells, "We are immune to Mez spells."); Log.Out(Logs::Detail, Logs::Spells, "We are immune to Mez spells.");
caster->Message_StringID(MT_Shout, CANNOT_MEZ); caster->Message_StringID(MT_Shout, CANNOT_MEZ);
int32 aggro = caster->CheckAggroAmount(spell_id); int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) { if(aggro > 0) {
AddToHateList(caster, aggro); AddToHateList(caster, aggro);
} else { } else {
@ -4086,7 +4086,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{ {
Log.Out(Logs::Detail, Logs::Spells, "We are immune to Slow spells."); Log.Out(Logs::Detail, Logs::Spells, "We are immune to Slow spells.");
caster->Message_StringID(MT_Shout, IMMUNE_ATKSPEED); caster->Message_StringID(MT_Shout, IMMUNE_ATKSPEED);
int32 aggro = caster->CheckAggroAmount(spell_id); int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) { if(aggro > 0) {
AddToHateList(caster, aggro); AddToHateList(caster, aggro);
} else { } else {
@ -4102,7 +4102,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(GetSpecialAbility(UNFEARABLE)) { if(GetSpecialAbility(UNFEARABLE)) {
Log.Out(Logs::Detail, Logs::Spells, "We are immune to Fear spells."); Log.Out(Logs::Detail, Logs::Spells, "We are immune to Fear spells.");
caster->Message_StringID(MT_Shout, IMMUNE_FEAR); caster->Message_StringID(MT_Shout, IMMUNE_FEAR);
int32 aggro = caster->CheckAggroAmount(spell_id); int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) { if(aggro > 0) {
AddToHateList(caster, aggro); AddToHateList(caster, aggro);
} else { } else {
@ -4119,7 +4119,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{ {
Log.Out(Logs::Detail, Logs::Spells, "Level is %d, cannot be feared by this spell.", GetLevel()); Log.Out(Logs::Detail, Logs::Spells, "Level is %d, cannot be feared by this spell.", GetLevel());
caster->Message_StringID(MT_Shout, FEAR_TOO_HIGH); caster->Message_StringID(MT_Shout, FEAR_TOO_HIGH);
int32 aggro = caster->CheckAggroAmount(spell_id); int32 aggro = caster->CheckAggroAmount(spell_id, this);
if (aggro > 0) { if (aggro > 0) {
AddToHateList(caster, aggro); AddToHateList(caster, aggro);
} else { } else {
@ -4142,7 +4142,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
{ {
Log.Out(Logs::Detail, Logs::Spells, "We are immune to Charm spells."); Log.Out(Logs::Detail, Logs::Spells, "We are immune to Charm spells.");
caster->Message_StringID(MT_Shout, CANNOT_CHARM); caster->Message_StringID(MT_Shout, CANNOT_CHARM);
int32 aggro = caster->CheckAggroAmount(spell_id); int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) { if(aggro > 0) {
AddToHateList(caster, aggro); AddToHateList(caster, aggro);
} else { } else {
@ -4182,7 +4182,7 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
if(GetSpecialAbility(UNSNAREABLE)) { if(GetSpecialAbility(UNSNAREABLE)) {
Log.Out(Logs::Detail, Logs::Spells, "We are immune to Snare spells."); Log.Out(Logs::Detail, Logs::Spells, "We are immune to Snare spells.");
caster->Message_StringID(MT_Shout, IMMUNE_MOVEMENT); caster->Message_StringID(MT_Shout, IMMUNE_MOVEMENT);
int32 aggro = caster->CheckAggroAmount(spell_id); int32 aggro = caster->CheckAggroAmount(spell_id, this);
if(aggro > 0) { if(aggro > 0) {
AddToHateList(caster, aggro); AddToHateList(caster, aggro);
} else { } else {