Implement AAs and harmtouch/layonhands to ^cast --- fix IsValidSpellRange

This commit is contained in:
nytmyr
2024-12-08 22:36:56 -06:00
parent a8f048c4e4
commit e9534e3f75
4 changed files with 242 additions and 176 deletions
+2 -2
View File
@@ -9739,7 +9739,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) {
case BotSpellTypes::Invisibility:
case BotSpellTypes::MovementSpeed:
case BotSpellTypes::SendHome:
if (
if ( // TODO bot rewrite - fix this, missing other target types (43 for example)
!(
spells[spell_id].target_type == ST_Target ||
spells[spell_id].target_type == ST_Pet ||
@@ -9748,7 +9748,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) {
spells[spell_id].target_type == ST_GroupTeleport
)
) {
LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme
LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme
return false;
}
+2
View File
@@ -402,6 +402,7 @@ public:
// AI Methods
bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX);
bool AttemptAICastSpell(uint16 spellType);
bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank);
bool AI_EngagedCastCheck() override;
bool AI_PursueCastCheck() override;
bool AI_IdleCastCheck() override;
@@ -478,6 +479,7 @@ public:
void LoadDefaultBotSettings();
void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false);
BotSpell GetSpellByHealType(uint16 spellType, Mob* tar);
uint16 GetSpellByAA(int id, AA::Rank* &rank);
std::string GetBotSpellCategoryName(uint8 setting_type);
std::string GetBotSettingCategoryName(uint8 setting_type);
+237 -173
View File
@@ -11,7 +11,13 @@ void bot_command_cast(Client* c, const Seperator* sep)
std::vector<std::string> notes =
{
"- This will interrupt any spell currently being cast by bots told to use the command.",
"- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells"
"- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells",
fmt::format(
"- You can use {} aa # to cast any clickable AA or specifically {} harmtouch / {} layonhands"
, sep->arg[0]
, sep->arg[0]
, sep->arg[0]
)
};
std::vector<std::string> example_format =
@@ -29,12 +35,12 @@ void bot_command_cast(Client* c, const Seperator* sep)
{
"To tell everyone to Nuke the target:",
fmt::format(
"{} {} spawned",
"{} {}",
sep->arg[0],
c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
),
fmt::format(
"{} {} spawned",
"{} {}",
sep->arg[0],
BotSpellTypes::Nuke
)
@@ -57,16 +63,14 @@ void bot_command_cast(Client* c, const Seperator* sep)
};
std::vector<std::string> examples_three =
{
"To tell Clrbot to resurrect the targeted corpse:",
"To tell Skbot to Harm Touch the target:",
fmt::format(
"{} {} byname Clrbot",
sep->arg[0],
c->GetSpellTypeShortNameByID(BotSpellTypes::Resurrect)
"{} aa 6000 byname Skbot",
sep->arg[0]
),
fmt::format(
"{} {} byname Clrbot",
sep->arg[0],
BotSpellTypes::Resurrect
"{} harmtouch byname Skbot",
sep->arg[0]
)
};
@@ -120,6 +124,11 @@ void bot_command_cast(Client* c, const Seperator* sep)
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
//AA help
if (!arg1.compare("aa") && !arg2.compare("help")) {
c->Message(Chat::Yellow, "Enter the ID of an AA to attempt to cast.", sep->arg[0]);
}
//Commanded type help prompts
if (!arg2.compare("help")) {
c->Message(Chat::Yellow, "You can also use [single], [group], [ae]. Ex: ^cast movementspeed group.", sep->arg[0]);
@@ -174,144 +183,168 @@ void bot_command_cast(Client* c, const Seperator* sep)
}
int ab_arg = 2;
uint16 spellType = 0;
// String/Int type checks
if (sep->IsNumber(1)) {
spellType = atoi(sep->arg[1]);
if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
else {
if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spellType = c->GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
switch (spellType) { //Allowed command checks
case BotSpellTypes::Charm:
if (!RuleB(Bots, AllowCommandedCharm)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::AEMez:
case BotSpellTypes::Mez:
if (!RuleB(Bots, AllowCommandedMez)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::Resurrect:
if (!RuleB(Bots, AllowCommandedResurrect)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::SummonCorpse:
if (!RuleB(Bots, AllowCommandedSummonCorpse)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
default:
break;
}
std::string argString = sep->arg[ab_arg];
uint16 spellType = UINT16_MAX;
uint16 subType = UINT16_MAX;
uint16 subTargetType = UINT16_MAX;
if (!argString.compare("shrink")) {
subType = CommandedSubTypes::Shrink;
++ab_arg;
}
else if (!argString.compare("grow")) {
subType = CommandedSubTypes::Grow;
++ab_arg;
}
else if (!argString.compare("see")) {
subType = CommandedSubTypes::SeeInvis;
++ab_arg;
}
else if (!argString.compare("invis")) {
subType = CommandedSubTypes::Invis;
++ab_arg;
}
else if (!argString.compare("undead")) {
subType = CommandedSubTypes::InvisUndead;
++ab_arg;
}
else if (!argString.compare("animals")) {
subType = CommandedSubTypes::InvisAnimals;
++ab_arg;
}
else if (!argString.compare("selo")) {
subType = CommandedSubTypes::Selo;
++ab_arg;
bool aaType = false;
int aaID = 0;
if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) {
if (!arg1.compare("harmtouch")) {
aaID = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id;
}
else if (!arg1.compare("layonhands")) {
aaID = zone->GetAlternateAdvancementAbilityByRank(aaLayonHands)->id;
}
else if (!sep->IsNumber(2) || !zone->GetAlternateAdvancementAbility(Strings::ToInt(arg2))) {
c->Message(Chat::Yellow, "You must enter an AA ID.");
return;
}
else {
++ab_arg;
aaID = Strings::ToInt(arg2);
}
aaType = true;
}
argString = sep->arg[ab_arg];
if (!aaType) {
// String/Int type checks
if (sep->IsNumber(1)) {
spellType = atoi(sep->arg[1]);
if (!argString.compare("single")) {
subTargetType = CommandedSubTypes::SingleTarget;
++ab_arg;
}
else if (!argString.compare("group")) {
subTargetType = CommandedSubTypes::GroupTarget;
++ab_arg;
}
else if (!argString.compare("ae")) {
subTargetType = CommandedSubTypes::AETarget;
++ab_arg;
}
if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) {
c->Message(
Chat::Yellow,
fmt::format(
"You must choose a valid spell type. Use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
if (
spellType == BotSpellTypes::PetBuffs ||
spellType == BotSpellTypes::PetCompleteHeals ||
spellType == BotSpellTypes::PetFastHeals ||
spellType == BotSpellTypes::PetHoTHeals ||
spellType == BotSpellTypes::PetRegularHeals ||
spellType == BotSpellTypes::PetVeryFastHeals
) {
c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type.");
return;
return;
}
}
else {
if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) {
spellType = c->GetSpellTypeIDByShortName(arg1);
}
else {
c->Message(
Chat::Yellow,
fmt::format(
"Incorrect argument, use {} for information regarding this command.",
Saylink::Silent(
fmt::format("{} help", sep->arg[0])
)
).c_str()
);
return;
}
}
switch (spellType) { //Allowed command checks
case BotSpellTypes::Charm:
if (!RuleB(Bots, AllowCommandedCharm)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::AEMez:
case BotSpellTypes::Mez:
if (!RuleB(Bots, AllowCommandedMez)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::Resurrect:
if (!RuleB(Bots, AllowCommandedResurrect)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
case BotSpellTypes::SummonCorpse:
if (!RuleB(Bots, AllowCommandedSummonCorpse)) {
c->Message(Chat::Yellow, "This commanded type is currently disabled.");
return;
}
break;
default:
break;
}
std::string argString = sep->arg[ab_arg];
if (!argString.compare("shrink")) {
subType = CommandedSubTypes::Shrink;
++ab_arg;
}
else if (!argString.compare("grow")) {
subType = CommandedSubTypes::Grow;
++ab_arg;
}
else if (!argString.compare("see")) {
subType = CommandedSubTypes::SeeInvis;
++ab_arg;
}
else if (!argString.compare("invis")) {
subType = CommandedSubTypes::Invis;
++ab_arg;
}
else if (!argString.compare("undead")) {
subType = CommandedSubTypes::InvisUndead;
++ab_arg;
}
else if (!argString.compare("animals")) {
subType = CommandedSubTypes::InvisAnimals;
++ab_arg;
}
else if (!argString.compare("selo")) {
subType = CommandedSubTypes::Selo;
++ab_arg;
}
argString = sep->arg[ab_arg];
if (!argString.compare("single")) {
subTargetType = CommandedSubTypes::SingleTarget;
++ab_arg;
}
else if (!argString.compare("group")) {
subTargetType = CommandedSubTypes::GroupTarget;
++ab_arg;
}
else if (!argString.compare("ae")) {
subTargetType = CommandedSubTypes::AETarget;
++ab_arg;
}
if (
spellType == BotSpellTypes::PetBuffs ||
spellType == BotSpellTypes::PetCompleteHeals ||
spellType == BotSpellTypes::PetFastHeals ||
spellType == BotSpellTypes::PetHoTHeals ||
spellType == BotSpellTypes::PetRegularHeals ||
spellType == BotSpellTypes::PetVeryFastHeals
) {
c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type.");
return;
}
}
Mob* tar = c->GetTarget();
//LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme
if (!tar) {
if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) {
if (!aaType && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) {
c->Message(Chat::Yellow, "You need a target for that.");
return;
}
@@ -356,10 +389,11 @@ void bot_command_cast(Client* c, const Seperator* sep)
if (IsBotSpellTypeBeneficial(spellType)) {
if (
(!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) ||
((tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())))
(tar->IsNPC() && !tar->GetOwner()) ||
(tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) ||
(tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar))
) {
c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName());
c->Message(Chat::Yellow, "[%s] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName());
return;
}
@@ -408,49 +442,79 @@ void bot_command_cast(Client* c, const Seperator* sep)
}
Mob* newTar = tar;
//LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme
if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) {
newTar = bot_iter;
if (!aaType) {
//LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme
if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) {
newTar = bot_iter;
}
if (!newTar) {
continue;
}
if (
IsBotSpellTypeBeneficial(spellType) &&
!RuleB(Bots, CrossRaidBuffingAndHealing) &&
!bot_iter->IsInGroupOrRaid(newTar, true)
) {
continue;
}
if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) {
bot_iter->BotGroupSay(
bot_iter,
fmt::format(
"I cannot attack [{}].",
newTar->GetCleanName()
).c_str()
);
continue;
}
}
if (!newTar) {
continue;
}
if (aaType) {
if (!bot_iter->GetAA(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)) {
continue;
}
if (
IsBotSpellTypeBeneficial(spellType) &&
!RuleB(Bots, CrossRaidBuffingAndHealing) &&
!bot_iter->IsInGroupOrRaid(newTar, true)
) {
continue;
}
LogTestDebug("{}: {} says, 'aaID is {}'", __LINE__, bot_iter->GetCleanName(), aaID); //deleteme
AA::Rank* tempRank = nullptr;
AA::Rank*& rank = tempRank;
uint16 spell_id = bot_iter->GetSpellByAA(aaID, rank);
if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) {
bot_iter->BotGroupSay(
bot_iter,
fmt::format(
"I cannot attack [{}].",
newTar->GetCleanName()
).c_str()
);
if (!IsValidSpell(spell_id)) {
continue;
}
continue;
}
LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme
bot_iter->SetCommandedSpell(true);
if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) {
if (!firstFound) {
firstFound = bot_iter;
if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) {
continue;
}
isSuccess = true;
++successCount;
}
else {
LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme
bot_iter->SetCommandedSpell(true);
if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) {
if (!firstFound) {
firstFound = bot_iter;
}
bot_iter->SetCommandedSpell(false);
isSuccess = true;
++successCount;
}
else {
bot_iter->GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", bot_iter->GetCleanName());
continue;
}
bot_iter->SetCommandedSpell(false);
}
continue;
}
@@ -460,7 +524,7 @@ void bot_command_cast(Client* c, const Seperator* sep)
Chat::Yellow,
fmt::format(
"No bots are capable of casting [{}] on {}.",
c->GetSpellTypeNameByID(spellType),
(!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)),
tar ? tar->GetCleanName() : "your target"
).c_str()
);
@@ -471,7 +535,7 @@ void bot_command_cast(Client* c, const Seperator* sep)
"{} {} [{}]{}",
((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())),
((successCount == 1 && firstFound) ? "casted" : "of your bots casted"),
c->GetSpellTypeNameByID(spellType),
(!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)),
tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "."
).c_str()
);
+1 -1
View File
@@ -2566,7 +2566,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in
}
range = GetActSpellRange(spell_id, range);
if(IsClient() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){
if(IsOfClientBot() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){
range = 100;
}