More fixes

TGB, ^cast, group/ae checks, in group/raid checks, inviting others bots to group, group disband fix, prevent rogue bs spam, ^follow fixes and cleanup, follow owner only by default when joining raid/group, group buff fixes for bots, range fixes for group buffs
This commit is contained in:
nytmyr
2024-10-31 07:32:16 -05:00
parent 6574f780db
commit 9b87aaf39b
17 changed files with 1053 additions and 670 deletions
+3 -1
View File
@@ -836,7 +836,7 @@ RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will b
RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid")
RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption")
RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption")
RULE_INT(Bots, StackSizeMin, 100, "100 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).")
RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.")
RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.")
RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.")
@@ -871,6 +871,8 @@ RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Def
RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass the anti-spam system") RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass the anti-spam system")
RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. Default 120.") RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. Default 120.")
RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.")
RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.")
RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.")
RULE_CATEGORY_END() RULE_CATEGORY_END()
RULE_CATEGORY(Chat) RULE_CATEGORY(Chat)
+73 -3
View File
@@ -2782,15 +2782,16 @@ int8 SpellEffectsCount(uint16 spell_id) {
if (!IsValidSpell(spell_id)) { if (!IsValidSpell(spell_id)) {
return false; return false;
} }
int8 i = 0;
int8 x = 0;
for (int i = 0; i < EFFECT_COUNT; i++) { for (int i = 0; i < EFFECT_COUNT; i++) {
if (!IsBlankSpellEffect(spell_id, i)) { if (!IsBlankSpellEffect(spell_id, i)) {
++i; ++x;
} }
} }
return i; return x;
} }
bool IsLichSpell(uint16 spell_id) bool IsLichSpell(uint16 spell_id)
@@ -3175,3 +3176,72 @@ bool RequiresStackCheck(uint16 spellType) {
return true; return true;
} }
bool IsResistanceOnlySpell(uint16 spell_id) {
if (!IsValidSpell(spell_id)) {
return false;
}
const auto& spell = spells[spell_id];
for (int i = 0; i < EFFECT_COUNT; i++) {
if (IsBlankSpellEffect(spell_id, i)) {
continue;
}
if (
spell.effect_id[i] == SE_ResistFire ||
spell.effect_id[i] == SE_ResistCold ||
spell.effect_id[i] == SE_ResistPoison ||
spell.effect_id[i] == SE_ResistDisease ||
spell.effect_id[i] == SE_ResistMagic ||
spell.effect_id[i] == SE_ResistCorruption ||
spell.effect_id[i] == SE_ResistAll
) {
continue;
}
return false;
}
return true;
}
bool IsDamageShieldOnlySpell(uint16 spell_id) {
if (SpellEffectsCount(spell_id) == 1 && IsEffectInSpell(spell_id, SE_DamageShield)) {
return true;
}
return false;
}
bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id) {
if (!IsValidSpell(spell_id)) {
return false;
}
const auto& spell = spells[spell_id];
for (int i = 0; i < EFFECT_COUNT; i++) {
if (IsBlankSpellEffect(spell_id, i)) {
continue;
}
if (
spell.effect_id[i] == SE_DamageShield ||
spell.effect_id[i] == SE_ResistFire ||
spell.effect_id[i] == SE_ResistCold ||
spell.effect_id[i] == SE_ResistPoison ||
spell.effect_id[i] == SE_ResistDisease ||
spell.effect_id[i] == SE_ResistMagic ||
spell.effect_id[i] == SE_ResistCorruption ||
spell.effect_id[i] == SE_ResistAll
) {
continue;
}
return false;
}
return true;
}
+3
View File
@@ -1722,5 +1722,8 @@ bool IsLichSpell(uint16 spell_id);
bool IsInstantHealSpell(uint32 spell_id); bool IsInstantHealSpell(uint32 spell_id);
bool IsResurrectSpell(uint16 spell_id); bool IsResurrectSpell(uint16 spell_id);
bool RequiresStackCheck(uint16 spellType); bool RequiresStackCheck(uint16 spellType);
bool IsResistanceOnlySpell(uint16 spell_id);
bool IsDamageShieldOnlySpell(uint16 spell_id);
bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id);
#endif #endif
+420 -481
View File
File diff suppressed because it is too large Load Diff
+5 -3
View File
@@ -278,6 +278,7 @@ public:
void SetHoldFlag(bool flag = true) { m_hold_flag = flag; } void SetHoldFlag(bool flag = true) { m_hold_flag = flag; }
bool GetAttackFlag() const { return m_attack_flag; } bool GetAttackFlag() const { return m_attack_flag; }
void SetAttackFlag(bool flag = true) { m_attack_flag = flag; } void SetAttackFlag(bool flag = true) { m_attack_flag = flag; }
bool GetCombatRoundForAlerts() const { return m_combat_round_alert_flag; }
bool GetAttackingFlag() const { return m_attacking_flag; } bool GetAttackingFlag() const { return m_attacking_flag; }
bool GetPullFlag() const { return m_pull_flag; } bool GetPullFlag() const { return m_pull_flag; }
void SetPullFlag(bool flag = true) { m_pull_flag = flag; } void SetPullFlag(bool flag = true) { m_pull_flag = flag; }
@@ -403,7 +404,7 @@ public:
void SetGuardMode(); void SetGuardMode();
void SetHoldMode(); void SetHoldMode();
bool IsValidSpellRange(uint16 spell_id, Mob const* tar); bool IsValidSpellRange(uint16 spell_id, Mob* tar);
// Bot AI Methods // Bot AI Methods
void AI_Bot_Init(); void AI_Bot_Init();
@@ -519,7 +520,6 @@ public:
void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; }
bool HasLoS() const { return _hasLoS; } bool HasLoS() const { return _hasLoS; }
bool IsInGroupOrRaid(bool announce = false);
void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false); void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false);
std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priorityType); std::list<BotSpellTypeOrder> GetSpellTypesPrioritized(uint8 priorityType);
@@ -981,7 +981,7 @@ public:
bool CheckDoubleRangedAttack(); bool CheckDoubleRangedAttack();
// Public "Refactor" Methods // Public "Refactor" Methods
static bool CheckSpawnConditions(Client* c); static bool CheckCampSpawnConditions(Client* c);
inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); } inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); }
@@ -1047,6 +1047,7 @@ private:
bool m_guard_flag; bool m_guard_flag;
bool m_hold_flag; bool m_hold_flag;
bool m_attack_flag; bool m_attack_flag;
bool m_combat_round_alert_flag;
bool m_attacking_flag; bool m_attacking_flag;
bool m_pull_flag; bool m_pull_flag;
bool m_pulling_flag; bool m_pulling_flag;
@@ -1104,6 +1105,7 @@ private:
int32 GenerateBaseManaPoints(); int32 GenerateBaseManaPoints();
void GenerateSpecialAttacks(); void GenerateSpecialAttacks();
void SetBotID(uint32 botID); void SetBotID(uint32 botID);
void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag; }
void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; } void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; }
void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; } void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; }
void SetReturningFlag(bool flag = true) { m_returning_flag = flag; } void SetReturningFlag(bool flag = true) { m_returning_flag = flag; }
+20 -3
View File
@@ -30,12 +30,19 @@ void bot_command_bot(Client *c, const Seperator *sep)
void bot_command_camp(Client *c, const Seperator *sep) void bot_command_camp(Client *c, const Seperator *sep)
{ {
if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) {
return; return;
}
if (helper_is_help_or_usage(sep->arg[1])) { if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]);
return; return;
} }
if (!Bot::CheckCampSpawnConditions(c)) {
return;
}
const int ab_mask = ActionableBots::ABM_Type1; const int ab_mask = ActionableBots::ABM_Type1;
std::string class_race_arg = sep->arg[1]; std::string class_race_arg = sep->arg[1];
@@ -49,8 +56,14 @@ void bot_command_camp(Client *c, const Seperator *sep)
return; return;
} }
for (auto bot_iter : sbl) uint16 campCount;
for (auto bot_iter : sbl) {
bot_iter->Camp(); bot_iter->Camp();
++campCount;
}
c->Message(Chat::White, "%i of your bots have been camped.", campCount);
} }
void bot_command_clone(Client *c, const Seperator *sep) void bot_command_clone(Client *c, const Seperator *sep)
@@ -428,6 +441,10 @@ void bot_command_delete(Client *c, const Seperator *sep)
return; return;
} }
if (!Bot::CheckCampSpawnConditions(c)) {
return;
}
auto my_bot = ActionableBots::AsTarget_ByBot(c); auto my_bot = ActionableBots::AsTarget_ByBot(c);
if (!my_bot) { if (!my_bot) {
c->Message(Chat::White, "You must <target> a bot that you own to use this command"); c->Message(Chat::White, "You must <target> a bot that you own to use this command");
@@ -829,7 +846,7 @@ void bot_command_spawn(Client *c, const Seperator *sep)
return; return;
} }
if (!Bot::CheckSpawnConditions(c)) { if (!Bot::CheckCampSpawnConditions(c)) {
return; return;
} }
+9 -1
View File
@@ -223,7 +223,7 @@ void bot_command_cast(Client* c, const Seperator* sep)
Bot* firstFound = nullptr; Bot* firstFound = nullptr;
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid()) { if (!bot_iter->IsInGroupOrRaid(c)) {
continue; continue;
} }
@@ -248,6 +248,14 @@ void bot_command_cast(Client* c, const Seperator* sep)
continue; continue;
} }
if (
BOT_SPELL_TYPES_BENEFICIAL(spellType) &&
!RuleB(Bots, CrossRaidBuffingAndHealing) &&
!bot_iter->IsInGroupOrRaid(newTar, true)
) {
continue;
}
if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) {
bot_iter->BotGroupSay( bot_iter->BotGroupSay(
bot_iter, bot_iter,
+182 -70
View File
@@ -2,47 +2,112 @@
void bot_command_follow(Client* c, const Seperator* sep) void bot_command_follow(Client* c, const Seperator* sep)
{ {
if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) {
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(Chat::White, "usage: (<friendly_target>) %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | mmr | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]);
c->Message(Chat::White, "usage: %s chain", sep->arg[0]);
return; return;
} }
if (helper_is_help_or_usage(sep->arg[1])) {
std::vector<std::string> description =
{
"Sets bots of your choosing to follow your target, view their current following state or reset their following state."
};
std::vector<std::string> notes =
{
"- You can only follow players, bots or mercenaries belonging to your group or raid."
};
std::vector<std::string> example_format =
{
fmt::format(
"{} [optional] [actionable]"
, sep->arg[0]
)
};
std::vector<std::string> examples_one =
{
"To set all Clerics to follow your target:",
fmt::format(
"{} byclass {}",
sep->arg[0],
Class::Cleric
)
};
std::vector<std::string> examples_two =
{
"To check the current state of all bots:",
fmt::format(
"{} current spawned",
sep->arg[0]
)
};
std::vector<std::string> examples_three =
{
"To reset all bots:",
fmt::format(
"{} reset spawned",
sep->arg[0]
)
};
std::vector<std::string> actionables =
{
"target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned"
};
std::vector<std::string> options = { };
std::vector<std::string> options_one = { };
std::vector<std::string> options_two = { };
std::vector<std::string> options_three = { };
std::string popup_text = c->SendCommandHelpWindow(
c,
description,
notes,
example_format,
examples_one, examples_two, examples_three,
actionables,
options,
options_one, options_two, options_three
);
popup_text = DialogueWindow::Table(popup_text);
c->SendPopupToClient(sep->arg[0], popup_text.c_str());
return;
}
const int ab_mask = ActionableBots::ABM_Type2; const int ab_mask = ActionableBots::ABM_Type2;
bool chain = false;
bool reset = false; bool reset = false;
bool currentCheck = false;
int ab_arg = 1; int ab_arg = 1;
int name_arg = 2;
Mob* target_mob = nullptr; Mob* target_mob = nullptr;
std::string optional_arg = sep->arg[1]; std::string optional_arg = sep->arg[1];
if (!optional_arg.compare("chain")) {
auto chain_count = helper_bot_follow_option_chain(c); if (!optional_arg.compare("reset")) {
c->Message(Chat::White, "%i of your bots %s now chain following you", chain_count, (chain_count == 1 ? "is" : "are")); target_mob = c;
return;
}
else if (!optional_arg.compare("reset")) {
reset = true; reset = true;
++ab_arg; ++ab_arg;
++name_arg ; }
else if (!optional_arg.compare("current")) {
currentCheck = true;
++ab_arg;
} }
else { else {
//target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single);
target_mob = c->GetTarget(); target_mob = c->GetTarget();
if (!target_mob) {
c->Message(Chat::White, "You must <target> a friendly player or bot within your group or raid to use this command"); if (!target_mob || !target_mob->IsOfClientBotMerc() || !c->IsInGroupOrRaid(target_mob)) {
c->Message(Chat::Yellow, "You must <target> a friendly player, bot or merc within your group or raid to use this command");
return; return;
} }
else if (!target_mob->IsBot() && !target_mob->IsClient()) {
c->Message(Chat::White, "You must <target> a friendly player or bot within your group or raid to use this command"); if (!optional_arg.compare("chain")) {
return; chain = true;
} ++ab_arg;
else if ((target_mob->GetGroup() && target_mob->GetGroup() != c->GetGroup()) || (target_mob->GetRaid() && target_mob->GetRaid() != c->GetRaid())) {
c->Message(Chat::White, "You must <target> a friendly player or bot within your group or raid to use this command");
return;
} }
} }
@@ -53,12 +118,66 @@ void bot_command_follow(Client* c, const Seperator* sep)
} }
std::list<Bot*> sbl; std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) { //if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg] : nullptr, class_race_check ? atoi(sep->arg[ab_arg]) : 0) == ActionableBots::ABT_None) {
if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) {
return; return;
} }
sbl.remove(nullptr); sbl.remove(nullptr);
auto botCount = sbl.size();
Mob* follow_mob = nullptr;
std::list<Bot*> chainList;
std::list<Bot*>::const_iterator it = chainList.begin();
uint16 count = 0;
for (auto bot_iter : sbl) { for (auto bot_iter : sbl) {
if (currentCheck) {
follow_mob = entity_list.GetMob(bot_iter->GetFollowID());
c->Message(
Chat::Green,
fmt::format(
"{} says, 'I am currently following {}.'",
bot_iter->GetCleanName(),
follow_mob ? follow_mob->GetCleanName() : "no one"
).c_str()
);
if (!follow_mob && RuleB(Bots, DoResponseAnimations)) {
bot_iter->DoAnim(28);
}
continue;
}
if (bot_iter == target_mob) {
if (botCount == 1) {
c->Message(
Chat::Yellow,
fmt::format(
"{} says, 'I cannot follow myself, you want me to run circles?",
bot_iter->GetCleanName()
).c_str()
);
if (RuleB(Bots, DoResponseAnimations)) {
bot_iter->DoAnim(60);
}
return;
}
bot_iter->WipeHateList();
--botCount;
continue;
}
if (!bot_iter->IsInGroupOrRaid(target_mob)) {
--botCount;
continue;
}
bot_iter->WipeHateList(); bot_iter->WipeHateList();
if (!bot_iter->GetGroup() && !bot_iter->GetRaid()) { if (!bot_iter->GetGroup() && !bot_iter->GetRaid()) {
@@ -71,57 +190,52 @@ void bot_command_follow(Client* c, const Seperator* sep)
bot_iter->SetManualFollow(false); bot_iter->SetManualFollow(false);
} }
else { else {
if (target_mob->IsGrouped() || target_mob->IsRaidGrouped()) { if (chain) {
bot_iter->SetFollowID(target_mob->GetID()); Mob* nextTar = target_mob;
bot_iter->SetManualFollow(true);
} if (count > 0) {
else if (bot_iter == target_mob) { nextTar = *it;
bot_iter->SetFollowID(c->GetID());
bot_iter->SetManualFollow(true); if (!nextTar) {
nextTar = target_mob;
}
}
LogTestDebug("{} is now following {}.", bot_iter->GetCleanName(), nextTar->GetCleanName()); //deleteme
chainList.push_back(bot_iter);
++it;
++count;
bot_iter->SetFollowID(nextTar->GetID());
} }
else { else {
bot_iter->SetFollowID(0); bot_iter->SetFollowID(target_mob->GetID());
bot_iter->SetManualFollow(false);
} }
bot_iter->SetManualFollow(true);
} }
} }
//auto my_group = bot_iter->GetGroup();
//if (my_group) { if (!bot_iter->GetPet()) {
// if (reset) {
// if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter)
// bot_iter->SetFollowID(c->GetID());
// else
// bot_iter->SetFollowID(my_group->GetLeader()->GetID());
//
// bot_iter->SetManualFollow(false);
// }
// else {
// if (bot_iter == target_mob)
// bot_iter->SetFollowID(c->GetID());
// else
// bot_iter->SetFollowID(target_mob->GetID());
//
// bot_iter->SetManualFollow(true);
// }
//}
//else {
// bot_iter->SetFollowID(0);
// bot_iter->SetManualFollow(false);
//}
if (!bot_iter->GetPet())
continue; continue;
}
bot_iter->GetPet()->WipeHateList(); bot_iter->GetPet()->WipeHateList();
bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); bot_iter->GetPet()->SetFollowID(bot_iter->GetID());
} }
Mob* follow_mob = nullptr; if (currentCheck || !botCount) {
if (sbl.size() == 1) { return;
}
follow_mob = target_mob;
if (botCount == 1) {
follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); follow_mob = entity_list.GetMob(sbl.front()->GetFollowID());
c->Message( c->Message(
Chat::White, Chat::Green,
fmt::format( fmt::format(
"Following {}.", "{} says, 'Following {}.'",
sbl.front()->GetCleanName(),
follow_mob ? follow_mob->GetCleanName() : "you" follow_mob ? follow_mob->GetCleanName() : "you"
).c_str() ).c_str()
); );
@@ -129,22 +243,20 @@ void bot_command_follow(Client* c, const Seperator* sep)
else { else {
if (reset) { if (reset) {
c->Message( c->Message(
Chat::White, Chat::Green,
fmt::format( fmt::format(
"{} of your bots are following you.", "{} of your bots are following you.",
sbl.size() botCount
).c_str() ).c_str()
); );
} }
else { else {
if (!sbl.empty()) {
follow_mob = entity_list.GetMob(sbl.front()->GetFollowID());
}
c->Message( c->Message(
Chat::White, Chat::Green,
fmt::format( fmt::format(
"{} of your bots are following {}.", "{} of your bots are {} {}.",
sbl.size(), botCount,
chain ? "chain following" : "following",
follow_mob ? follow_mob->GetCleanName() : "you" follow_mob ? follow_mob->GetCleanName() : "you"
).c_str() ).c_str()
); );
+6 -2
View File
@@ -179,7 +179,9 @@ void bot_command_item_use(Client* c, const Seperator* sep)
).c_str() ).c_str()
); );
bot_iter->DoAnim(29); if (RuleB(Bots, DoResponseAnimations)) {
bot_iter->DoAnim(29);
}
} }
else if (!equipped_item) { else if (!equipped_item) {
c->Message( c->Message(
@@ -204,7 +206,9 @@ void bot_command_item_use(Client* c, const Seperator* sep)
).c_str() ).c_str()
); );
bot_iter->DoAnim(29); if (RuleB(Bots, DoResponseAnimations)) {
bot_iter->DoAnim(29);
}
} }
} }
} }
+1 -1
View File
@@ -20,7 +20,7 @@ void bot_command_mesmerize(Client *c, const Seperator *sep)
continue; continue;
} }
if (!bot_iter->IsInGroupOrRaid()) { if (!bot_iter->IsInGroupOrRaid(c)) {
continue; continue;
} }
+1 -11
View File
@@ -176,13 +176,7 @@ void Bot::ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite) {
// If the Bot Owner is in our raid we need to be able to invite their Bots // If the Bot Owner is in our raid we need to be able to invite their Bots
} }
else if (invitee->IsBot() && (invitee->CastToBot()->GetBotOwnerCharacterID() != invitor->CharacterID())) { else if (invitee->IsBot() && (invitee->CastToBot()->GetBotOwnerCharacterID() != invitor->CharacterID())) {
invitor->Message( invitor->Message(Chat::Red, "%s's owner needs to be in your raid to be able to invite them.", invitee->GetCleanName());
Chat::Red,
fmt::format(
"{} is not your Bot. You can only invite your own Bots, or Bots that belong to a Raid member.",
invitee->GetCleanName()
).c_str()
);
return; return;
} }
@@ -257,10 +251,6 @@ void Bot::CreateBotRaid(Mob* invitee, Client* invitor, bool group_invite, Raid*
} else { } else {
raid->AddBot(b); raid->AddBot(b);
} }
if (new_raid) {
invitee->SetFollowID(invitor->GetID());
}
} }
} }
+61 -12
View File
@@ -994,11 +994,28 @@ std::list<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa
continue; continue;
} }
if (
!RuleB(Bots, EnableBotTGB) &&
IsGroupSpell(botSpellList[i].spellid) &&
!IsTGBCompatibleSpell(botSpellList[i].spellid) &&
!botCaster->IsInGroupOrRaid(tar, true)
) {
continue;
}
if ( if (
( (
!botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez)) !botCaster->IsCommandedSpell() ||
(
botCaster->IsCommandedSpell() &&
(spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez)
)
)
&&
(
!IsPBAESpell(botSpellList[i].spellid) &&
!botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))
) )
&& (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsGroupBotSpellType(spellType))) // TODO bot rewrite - needed for ae spells?
) { ) {
continue; continue;
} }
@@ -1232,7 +1249,15 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spell
if (botCaster) { if (botCaster) {
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP);
const std::vector<Mob*> v = botCaster->GatherSpellTargets(); std::vector<Mob*> v;
if (RuleB(Bots, CrossRaidBuffingAndHealing)) {
v = botCaster->GatherSpellTargets(true);
}
else {
v = botCaster->GatherGroupSpellTargets();
}
int targetCount = 0; int targetCount = 0;
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
@@ -1273,7 +1298,15 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint
if (botCaster) { if (botCaster) {
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime);
const std::vector<Mob*> v = botCaster->GatherSpellTargets(); std::vector<Mob*> v;
if (RuleB(Bots, CrossRaidBuffingAndHealing)) {
v = botCaster->GatherSpellTargets(true);
}
else {
v = botCaster->GatherGroupSpellTargets();
}
int targetCount = 0; int targetCount = 0;
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
@@ -1314,7 +1347,15 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint
if (botCaster) { if (botCaster) {
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal);
const std::vector<Mob*> v = botCaster->GatherSpellTargets(); std::vector<Mob*> v;
if (RuleB(Bots, CrossRaidBuffingAndHealing)) {
v = botCaster->GatherSpellTargets(true);
}
else {
v = botCaster->GatherGroupSpellTargets();
}
int targetCount = 0; int targetCount = 0;
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
@@ -1629,7 +1670,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType
continue; continue;
} }
if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) {
continue; continue;
} }
@@ -1679,7 +1720,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType
continue; continue;
} }
if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) {
continue; continue;
} }
@@ -2583,19 +2624,27 @@ bool Bot::HasBotSpellEntry(uint16 spellid) {
return false; return false;
} }
bool Bot::IsValidSpellRange(uint16 spell_id, Mob const* tar) { bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) {
if (!IsValidSpell(spell_id)) { if (!IsValidSpell(spell_id)) {
return false; return false;
} }
if (tar) { if (tar) {
int spellrange = (GetActSpellRange(spell_id, spells[spell_id].range) * GetActSpellRange(spell_id, spells[spell_id].range)); float range = spells[spell_id].range;
if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) {
if (tar != this) {
range += GetRangeDistTargetSizeMod(tar);
}
range = GetActSpellRange(spell_id, range);
if (range >= Distance(m_Position, tar->GetPosition())) {
return true; return true;
} }
spellrange = (GetActSpellRange(spell_id, spells[spell_id].aoe_range) * GetActSpellRange(spell_id, spells[spell_id].aoe_range)); range = GetActSpellRange(spell_id, spells[spell_id].aoe_range);
if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) {
if (range >= Distance(m_Position, tar->GetPosition())) {
return true; return true;
} }
} }
+126 -12
View File
@@ -1017,6 +1017,27 @@ void Group::GetBotList(std::list<Bot*>& bot_list, bool clear_list)
} }
} }
void Group::GetRawBotList(std::list<uint32>& bot_list, bool clear_list)
{
if (clear_list) {
bot_list.clear();
}
const auto& l = GroupIdRepository::GetWhere(
database,
fmt::format(
"`group_id` = {}",
GetID()
)
);
for (const auto& e : l) {
if (e.bot_id) {
bot_list.push_back(e.bot_id);
}
}
}
bool Group::Process() { bool Group::Process() {
if(disbandcheck && !GroupCount()) if(disbandcheck && !GroupCount())
return false; return false;
@@ -1234,30 +1255,49 @@ void Group::GroupMessageString(Mob* sender, uint32 type, uint32 string_id, const
void Client::LeaveGroup() { void Client::LeaveGroup() {
Group *g = GetGroup(); Group *g = GetGroup();
if(g) if (g) {
{
int32 MemberCount = g->GroupCount(); int32 MemberCount = g->GroupCount();
// Account for both client and merc leaving the group // Account for both client and merc leaving the group
if (GetMerc() && g == GetMerc()->GetGroup()) if (GetMerc() && g == GetMerc()->GetGroup()) {
{
MemberCount -= 1; MemberCount -= 1;
} }
if(MemberCount < 3) if (RuleB(Bots, Enabled)) {
{ std::list<uint32> sbl;
g->GetRawBotList(sbl);
for (auto botID : sbl) {
auto b = entity_list.GetBotByBotID(botID);
if (b) {
if (b->GetBotOwnerCharacterID() == CharacterID()) {
MemberCount -= 1;
}
}
else {
if (database.botdb.GetOwnerID(botID) == CharacterID()) {
MemberCount -= 1;
}
}
}
}
if (MemberCount < 3) {
g->DisbandGroup(); g->DisbandGroup();
} }
else else {
{
g->DelMember(this); g->DelMember(this);
if (GetMerc() != nullptr && g == GetMerc()->GetGroup() )
{ if (GetMerc() != nullptr && g == GetMerc()->GetGroup()) {
GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup());
} }
if (RuleB(Bots, Enabled)) {
g->RemoveClientsBots(this);
}
} }
} }
else else {
{
//force things a little //force things a little
Group::RemoveFromGroup(this); Group::RemoveFromGroup(this);
@@ -2576,3 +2616,77 @@ void Group::AddToGroup(AddToGroupRequest r)
} }
); );
} }
void Group::RemoveClientsBots(Client* c) {
std::list<uint32> sbl;
GetRawBotList(sbl);
for (auto botID : sbl) {
auto b = entity_list.GetBotByBotID(botID);
if (b) {
if (b->GetBotOwnerCharacterID() == c->CharacterID()) {
b->RemoveBotFromGroup(b, this);
}
}
else {
if (database.botdb.GetOwnerID(botID) == c->CharacterID()) {
auto botName = database.botdb.GetBotNameByID(botID);
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (membername[i] == botName) {
members[i] = nullptr;
membername[i][0] = '\0';
memset(membername[i], 0, 64);
MemberRoles[i] = 0;
break;
}
}
auto pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct));
ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer;
gl->gid = GetID();
gl->zoneid = zone->GetZoneID();
gl->instance_id = zone->GetInstanceID();
strcpy(gl->member_name, botName.c_str());
worldserver.SendPacket(pack);
safe_delete(pack);
auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct));
GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer;
gu->action = groupActLeave;
strcpy(gu->membername, botName.c_str());
strcpy(gu->yourname, botName.c_str());
gu->leader_aas = LeaderAbilities;
for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (members[i] == nullptr) {
//if (DEBUG>=5) LogFile->write(EQEMuLog::Debug, "Group::DelMember() null member at slot %i", i);
continue;
}
if (membername[i] != botName.c_str()) {
strcpy(gu->yourname, members[i]->GetCleanName());
if (members[i]->IsClient()) {
members[i]->CastToClient()->QueuePacket(outapp);
}
}
}
safe_delete(outapp);
DelMemberOOZ(botName.c_str());
GroupIdRepository::DeleteWhere(
database,
fmt::format(
"`bot_id` = {}",
botID
)
);
}
}
}
}
+2
View File
@@ -69,6 +69,7 @@ public:
void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true); void GetMemberList(std::list<Mob*>& member_list, bool clear_list = true);
void GetClientList(std::list<Client*>& client_list, bool clear_list = true); void GetClientList(std::list<Client*>& client_list, bool clear_list = true);
void GetBotList(std::list<Bot*>& bot_list, bool clear_list = true); void GetBotList(std::list<Bot*>& bot_list, bool clear_list = true);
void GetRawBotList(std::list<uint32>& bot_list, bool clear_list = true);
bool IsGroupMember(Mob* c); bool IsGroupMember(Mob* c);
bool IsGroupMember(const char* name); bool IsGroupMember(const char* name);
bool Process(); bool Process();
@@ -153,6 +154,7 @@ public:
void AddToGroup(AddToGroupRequest r); void AddToGroup(AddToGroupRequest r);
void AddToGroup(Mob* m); void AddToGroup(Mob* m);
static void RemoveFromGroup(Mob* m); static void RemoveFromGroup(Mob* m);
void RemoveClientsBots(Client* c);
void SetGroupMentor(int percent, char *name); void SetGroupMentor(int percent, char *name);
void ClearGroupMentor(); void ClearGroupMentor();
+97 -61
View File
@@ -6112,18 +6112,17 @@ int32 Mob::GetVulnerability(Mob *caster, uint32 spell_id, uint32 ticsremaining,
bool Mob::IsTargetedFocusEffect(int focus_type) { bool Mob::IsTargetedFocusEffect(int focus_type) {
switch (focus_type) { switch (focus_type) {
case focusSpellVulnerability: case focusSpellVulnerability:
case focusFcSpellDamagePctIncomingPC: case focusFcSpellDamagePctIncomingPC:
case focusFcDamageAmtIncoming: case focusFcDamageAmtIncoming:
case focusFcSpellDamageAmtIncomingPC: case focusFcSpellDamageAmtIncomingPC:
case focusFcCastSpellOnLand: case focusFcCastSpellOnLand:
case focusFcHealAmtIncoming: case focusFcHealAmtIncoming:
case focusFcHealPctCritIncoming: case focusFcHealPctCritIncoming:
case focusFcHealPctIncoming: case focusFcHealPctIncoming:
return true; return true;
default: default:
return false; return false;
} }
} }
@@ -7254,56 +7253,56 @@ void Mob::SlowMitigation(Mob* caster)
EQ::skills::SkillType Mob::GetSkillByItemType(int ItemType) EQ::skills::SkillType Mob::GetSkillByItemType(int ItemType)
{ {
switch (ItemType) { switch (ItemType) {
case EQ::item::ItemType1HSlash: case EQ::item::ItemType1HSlash:
return EQ::skills::Skill1HSlashing; return EQ::skills::Skill1HSlashing;
case EQ::item::ItemType2HSlash: case EQ::item::ItemType2HSlash:
return EQ::skills::Skill2HSlashing; return EQ::skills::Skill2HSlashing;
case EQ::item::ItemType1HPiercing: case EQ::item::ItemType1HPiercing:
return EQ::skills::Skill1HPiercing;
case EQ::item::ItemType1HBlunt:
return EQ::skills::Skill1HBlunt;
case EQ::item::ItemType2HBlunt:
return EQ::skills::Skill2HBlunt;
case EQ::item::ItemType2HPiercing:
if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2)
return EQ::skills::Skill1HPiercing; return EQ::skills::Skill1HPiercing;
else case EQ::item::ItemType1HBlunt:
return EQ::skills::Skill2HPiercing; return EQ::skills::Skill1HBlunt;
case EQ::item::ItemTypeBow: case EQ::item::ItemType2HBlunt:
return EQ::skills::SkillArchery; return EQ::skills::Skill2HBlunt;
case EQ::item::ItemTypeLargeThrowing: case EQ::item::ItemType2HPiercing:
case EQ::item::ItemTypeSmallThrowing: if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2)
return EQ::skills::SkillThrowing; return EQ::skills::Skill1HPiercing;
case EQ::item::ItemTypeMartial: else
return EQ::skills::SkillHandtoHand; return EQ::skills::Skill2HPiercing;
default: case EQ::item::ItemTypeBow:
return EQ::skills::SkillHandtoHand; return EQ::skills::SkillArchery;
case EQ::item::ItemTypeLargeThrowing:
case EQ::item::ItemTypeSmallThrowing:
return EQ::skills::SkillThrowing;
case EQ::item::ItemTypeMartial:
return EQ::skills::SkillHandtoHand;
default:
return EQ::skills::SkillHandtoHand;
} }
} }
uint8 Mob::GetItemTypeBySkill(EQ::skills::SkillType skill) uint8 Mob::GetItemTypeBySkill(EQ::skills::SkillType skill)
{ {
switch (skill) { switch (skill) {
case EQ::skills::SkillThrowing: case EQ::skills::SkillThrowing:
return EQ::item::ItemTypeSmallThrowing; return EQ::item::ItemTypeSmallThrowing;
case EQ::skills::SkillArchery: case EQ::skills::SkillArchery:
return EQ::item::ItemTypeArrow; return EQ::item::ItemTypeArrow;
case EQ::skills::Skill1HSlashing: case EQ::skills::Skill1HSlashing:
return EQ::item::ItemType1HSlash; return EQ::item::ItemType1HSlash;
case EQ::skills::Skill2HSlashing: case EQ::skills::Skill2HSlashing:
return EQ::item::ItemType2HSlash; return EQ::item::ItemType2HSlash;
case EQ::skills::Skill1HPiercing: case EQ::skills::Skill1HPiercing:
return EQ::item::ItemType1HPiercing; return EQ::item::ItemType1HPiercing;
case EQ::skills::Skill2HPiercing: // watch for undesired client behavior case EQ::skills::Skill2HPiercing: // watch for undesired client behavior
return EQ::item::ItemType2HPiercing; return EQ::item::ItemType2HPiercing;
case EQ::skills::Skill1HBlunt: case EQ::skills::Skill1HBlunt:
return EQ::item::ItemType1HBlunt; return EQ::item::ItemType1HBlunt;
case EQ::skills::Skill2HBlunt: case EQ::skills::Skill2HBlunt:
return EQ::item::ItemType2HBlunt; return EQ::item::ItemType2HBlunt;
case EQ::skills::SkillHandtoHand: case EQ::skills::SkillHandtoHand:
return EQ::item::ItemTypeMartial; return EQ::item::ItemTypeMartial;
default: default:
return EQ::item::ItemTypeMartial; return EQ::item::ItemTypeMartial;
} }
} }
@@ -7311,7 +7310,6 @@ uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) {
uint16 weapon_speed = 0; uint16 weapon_speed = 0;
switch (hand) { switch (hand) {
case 13: case 13:
weapon_speed = attack_timer.GetDuration(); weapon_speed = attack_timer.GetDuration();
break; break;
@@ -9416,14 +9414,14 @@ void Mob::SetBaseSetting(uint16 baseSetting, int settingValue) {
} }
bool Mob::TargetValidation(Mob* other) { bool Mob::TargetValidation(Mob* other) {
if (!other || GetAppearance() == eaDead) { if (!other || GetAppearance() == eaDead) {
return false; return false;
} }
return true; return true;
} }
std::unordered_map<uint16, Mob *> &Mob::GetCloseMobList(float distance) std::unordered_map<uint16, Mob*>& Mob::GetCloseMobList(float distance)
{ {
return entity_list.GetCloseMobList(this, distance); return entity_list.GetCloseMobList(this, distance);
} }
@@ -9445,3 +9443,41 @@ void Mob::ClearDataBucketCache()
DataBucket::DeleteFromCache(id, t); DataBucket::DeleteFromCache(id, t);
} }
} }
bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) {
if (!other || !IsOfClientBotMerc() || !other->IsOfClientBotMerc()) {
return false;
}
auto* r = GetRaid();
auto* rO = other->GetRaid();
if (r) {
if (!rO || r != rO) {
return false;
}
auto rG = r->GetGroup(GetCleanName());
auto rOG = rO->GetGroup(other->GetCleanName());
if (rG == RAID_GROUPLESS || rOG == RAID_GROUPLESS || (sameRaidGroup && rG != rOG)) {
return false;
}
return true;
}
else {
auto* g = GetGroup();
auto* gO = other->GetGroup();
if (g) {
if (!gO || g != gO) {
return false;
}
return true;
}
}
return false;
}
+1
View File
@@ -774,6 +774,7 @@ public:
virtual bool HasGroup() = 0; virtual bool HasGroup() = 0;
virtual Raid* GetRaid() = 0; virtual Raid* GetRaid() = 0;
virtual Group* GetGroup() = 0; virtual Group* GetGroup() = 0;
bool IsInGroupOrRaid(Mob* other, bool sameRaidGroup = false);
//Faction //Faction
virtual inline int32 GetPrimaryFaction() const { return 0; } virtual inline int32 GetPrimaryFaction() const { return 0; }
+41 -7
View File
@@ -2182,14 +2182,42 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
case ST_Group: case ST_Group:
case ST_GroupNoPets: case ST_GroupNoPets:
{ {
if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB))) { if (
if( (!target) || IsClient() && CastToClient()->TGB() &&
(target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) || IsTGBCompatibleSpell(spell_id) &&
(target->IsCorpse()) ) (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB))
) {
if (
!target ||
target->IsCorpse() ||
(
target->IsNPC() &&
!(target->GetOwner() && target->GetOwner()->IsClient())
)
) {
spell_target = this; spell_target = this;
else }
else {
spell_target = target; spell_target = target;
} else { }
}
else if (
IsBot() && RuleB(Bots, EnableBotTGB) &&
IsTGBCompatibleSpell(spell_id) &&
(slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB))
) {
if (
!spell_target ||
spell_target->IsCorpse() ||
(
spell_target->IsNPC() &&
!(spell_target->GetOwner() && spell_target->GetOwner()->IsOfClientBot())
)
) {
spell_target = this;
}
}
else {
spell_target = this; spell_target = this;
} }
@@ -2516,8 +2544,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
//range check our target, if we have one and it is not us //range check our target, if we have one and it is not us
float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target); float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target);
if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) if (
(
(IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB)
) &&
IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id))
) {
range = spells[spell_id].aoe_range; range = spells[spell_id].aoe_range;
}
range = GetActSpellRange(spell_id, range); range = GetActSpellRange(spell_id, range);
if(IsClient() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){ if(IsClient() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){