Implement blocked_buffs and blocked_pet_buffs

This commit is contained in:
nytmyr
2024-12-15 23:09:32 -06:00
parent d144d58abc
commit e220163153
15 changed files with 1323 additions and 11 deletions
@@ -1178,6 +1178,25 @@ WHERE NOT EXISTS
(SELECT *
FROM spells_new
WHERE bot_spells_entries.spell_id = spells_new.id);
)"
},
ManifestEntry{
.version = 9052,
.description = "2024_12_15_bot_blocked_buffs.sql",
.check = "SHOW TABLES LIKE 'bot_blocked_buffs'",
.condition = "empty",
.match = "",
.sql = R"(
CREATE TABLE `bot_blocked_buffs` (
`bot_id` INT(11) UNSIGNED NOT NULL,
`spell_id` INT(11) UNSIGNED NOT NULL,
`blocked` TINYINT(4) UNSIGNED NULL DEFAULT '0',
`blocked_pet` TINYINT(4) UNSIGNED NULL DEFAULT '0',
PRIMARY KEY (`bot_id`, `spell_id`) USING BTREE
)
COLLATE='latin1_swedish_ci'
ENGINE=InnoDB
;
)"
}
// -- template; copy/paste this when you need to create a new entry
@@ -0,0 +1,416 @@
/**
* DO NOT MODIFY THIS FILE
*
* This repository was automatically generated and is NOT to be modified directly.
* Any repository modifications are meant to be made to the repository extending the base.
* Any modifications to base repositories are to be made by the generator only
*
* @generator ./utils/scripts/generators/repository-generator.pl
* @docs https://docs.eqemu.io/developer/repositories
*/
#ifndef EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H
#define EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H
#include "../../database.h"
#include "../../strings.h"
#include <ctime>
class BaseBotBlockedBuffsRepository {
public:
struct BotBlockedBuffs {
uint32_t bot_id;
uint32_t spell_id;
uint8_t blocked;
uint8_t blocked_pet;
};
static std::string PrimaryKey()
{
return std::string("bot_id");
}
static std::vector<std::string> Columns()
{
return {
"bot_id",
"spell_id",
"blocked",
"blocked_pet",
};
}
static std::vector<std::string> SelectColumns()
{
return {
"bot_id",
"spell_id",
"blocked",
"blocked_pet",
};
}
static std::string ColumnsRaw()
{
return std::string(Strings::Implode(", ", Columns()));
}
static std::string SelectColumnsRaw()
{
return std::string(Strings::Implode(", ", SelectColumns()));
}
static std::string TableName()
{
return std::string("bot_blocked_buffs");
}
static std::string BaseSelect()
{
return fmt::format(
"SELECT {} FROM {}",
SelectColumnsRaw(),
TableName()
);
}
static std::string BaseInsert()
{
return fmt::format(
"INSERT INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static BotBlockedBuffs NewEntity()
{
BotBlockedBuffs e{};
e.bot_id = 0;
e.spell_id = 0;
e.blocked = 0;
e.blocked_pet = 0;
return e;
}
static BotBlockedBuffs GetBotBlockedBuffs(
const std::vector<BotBlockedBuffs> &bot_blocked_buffss,
int bot_blocked_buffs_id
)
{
for (auto &bot_blocked_buffs : bot_blocked_buffss) {
if (bot_blocked_buffs.bot_id == bot_blocked_buffs_id) {
return bot_blocked_buffs;
}
}
return NewEntity();
}
static BotBlockedBuffs FindOne(
Database& db,
int bot_blocked_buffs_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {} = {} LIMIT 1",
BaseSelect(),
PrimaryKey(),
bot_blocked_buffs_id
)
);
auto row = results.begin();
if (results.RowCount() == 1) {
BotBlockedBuffs e{};
e.bot_id = row[0] ? static_cast<uint32_t>(atoi(row[0])) : 0;
e.spell_id = row[1] ? static_cast<uint32_t>(atoi(row[1])) : 0;
e.blocked = row[2] ? static_cast<uint8_t>(atoi(row[2])) : 0;
e.blocked_pet = row[3] ? static_cast<uint8_t>(atoi(row[3])) : 0;
return e;
}
return NewEntity();
}
static int DeleteOne(
Database& db,
int bot_blocked_buffs_id
)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {} = {}",
TableName(),
PrimaryKey(),
bot_blocked_buffs_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int UpdateOne(
Database& db,
const BotBlockedBuffs &e
)
{
std::vector<std::string> v;
auto columns = Columns();
v.push_back(columns[0] + " = " + std::to_string(e.bot_id));
v.push_back(columns[1] + " = " + std::to_string(e.spell_id));
v.push_back(columns[2] + " = " + std::to_string(e.blocked));
v.push_back(columns[3] + " = " + std::to_string(e.blocked_pet));
auto results = db.QueryDatabase(
fmt::format(
"UPDATE {} SET {} WHERE {} = {}",
TableName(),
Strings::Implode(", ", v),
PrimaryKey(),
e.bot_id
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static BotBlockedBuffs InsertOne(
Database& db,
BotBlockedBuffs e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.blocked));
v.push_back(std::to_string(e.blocked_pet));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseInsert(),
Strings::Implode(",", v)
)
);
if (results.Success()) {
e.bot_id = results.LastInsertedID();
return e;
}
e = NewEntity();
return e;
}
static int InsertMany(
Database& db,
const std::vector<BotBlockedBuffs> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.blocked));
v.push_back(std::to_string(e.blocked_pet));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseInsert(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static std::vector<BotBlockedBuffs> All(Database& db)
{
std::vector<BotBlockedBuffs> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{}",
BaseSelect()
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BotBlockedBuffs e{};
e.bot_id = row[0] ? static_cast<uint32_t>(atoi(row[0])) : 0;
e.spell_id = row[1] ? static_cast<uint32_t>(atoi(row[1])) : 0;
e.blocked = row[2] ? static_cast<uint8_t>(atoi(row[2])) : 0;
e.blocked_pet = row[3] ? static_cast<uint8_t>(atoi(row[3])) : 0;
all_entries.push_back(e);
}
return all_entries;
}
static std::vector<BotBlockedBuffs> GetWhere(Database& db, const std::string &where_filter)
{
std::vector<BotBlockedBuffs> all_entries;
auto results = db.QueryDatabase(
fmt::format(
"{} WHERE {}",
BaseSelect(),
where_filter
)
);
all_entries.reserve(results.RowCount());
for (auto row = results.begin(); row != results.end(); ++row) {
BotBlockedBuffs e{};
e.bot_id = row[0] ? static_cast<uint32_t>(atoi(row[0])) : 0;
e.spell_id = row[1] ? static_cast<uint32_t>(atoi(row[1])) : 0;
e.blocked = row[2] ? static_cast<uint8_t>(atoi(row[2])) : 0;
e.blocked_pet = row[3] ? static_cast<uint8_t>(atoi(row[3])) : 0;
all_entries.push_back(e);
}
return all_entries;
}
static int DeleteWhere(Database& db, const std::string &where_filter)
{
auto results = db.QueryDatabase(
fmt::format(
"DELETE FROM {} WHERE {}",
TableName(),
where_filter
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int Truncate(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"TRUNCATE TABLE {}",
TableName()
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int64 GetMaxId(Database& db)
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COALESCE(MAX({}), 0) FROM {}",
PrimaryKey(),
TableName()
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static int64 Count(Database& db, const std::string &where_filter = "")
{
auto results = db.QueryDatabase(
fmt::format(
"SELECT COUNT(*) FROM {} {}",
TableName(),
(where_filter.empty() ? "" : "WHERE " + where_filter)
)
);
return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0);
}
static std::string BaseReplace()
{
return fmt::format(
"REPLACE INTO {} ({}) ",
TableName(),
ColumnsRaw()
);
}
static int ReplaceOne(
Database& db,
const BotBlockedBuffs &e
)
{
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.blocked));
v.push_back(std::to_string(e.blocked_pet));
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES ({})",
BaseReplace(),
Strings::Implode(",", v)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
static int ReplaceMany(
Database& db,
const std::vector<BotBlockedBuffs> &entries
)
{
std::vector<std::string> insert_chunks;
for (auto &e: entries) {
std::vector<std::string> v;
v.push_back(std::to_string(e.bot_id));
v.push_back(std::to_string(e.spell_id));
v.push_back(std::to_string(e.blocked));
v.push_back(std::to_string(e.blocked_pet));
insert_chunks.push_back("(" + Strings::Implode(",", v) + ")");
}
std::vector<std::string> v;
auto results = db.QueryDatabase(
fmt::format(
"{} VALUES {}",
BaseReplace(),
Strings::Implode(",", insert_chunks)
)
);
return (results.Success() ? results.RowsAffected() : 0);
}
};
#endif //EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H
@@ -0,0 +1,50 @@
#ifndef EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H
#define EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H
#include "../database.h"
#include "../strings.h"
#include "base/base_bot_blocked_buffs_repository.h"
class BotBlockedBuffsRepository: public BaseBotBlockedBuffsRepository {
public:
/**
* This file was auto generated and can be modified and extended upon
*
* Base repository methods are automatically
* generated in the "base" version of this repository. The base repository
* is immutable and to be left untouched, while methods in this class
* are used as extension methods for more specific persistence-layer
* accessors or mutators.
*
* Base Methods (Subject to be expanded upon in time)
*
* Note: Not all tables are designed appropriately to fit functionality with all base methods
*
* InsertOne
* UpdateOne
* DeleteOne
* FindOne
* GetWhere(std::string where_filter)
* DeleteWhere(std::string where_filter)
* InsertMany
* All
*
* Example custom methods in a repository
*
* BotBlockedBuffsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
* BotBlockedBuffsRepository::GetWhereNeverExpires()
* BotBlockedBuffsRepository::GetWhereXAndY()
* BotBlockedBuffsRepository::DeleteWhereXAndY()
*
* Most of the above could be covered by base methods, but if you as a developer
* find yourself re-using logic for other parts of the code, its best to just make a
* method that can be re-used easily elsewhere especially if it can use a base repository
* method and encapsulate filters there
*/
// Custom extended repository methods here
};
#endif //EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H
+1
View File
@@ -891,6 +891,7 @@ RULE_BOOL(Bots, AllowCommandedLull, true, "If enabled bots can be commanded to l
RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.")
RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.")
RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.")
RULE_BOOL(Bots, AllowBotBlockedBuffs, true, "If enabled, you can create blocked buffs for each bot and for their pets.")
RULE_CATEGORY_END()
RULE_CATEGORY(Chat)
+1 -1
View File
@@ -43,7 +43,7 @@
*/
#define CURRENT_BINARY_DATABASE_VERSION 9286
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9051 //TODO bot rewrite
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9052 //TODO bot rewrite
#endif
+181 -8
View File
@@ -108,6 +108,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
GenerateAppearance();
GenerateBaseStats();
bot_timers.clear();
bot_blocked_buffs.clear();
// Calculate HitPoints Last As It Uses Base Stats
current_hp = GenerateBaseHitPoints();
@@ -247,13 +248,16 @@ Bot::Bot(
GenerateBaseStats();
bot_timers.clear();
database.botdb.LoadTimers(this);
LoadDefaultBotSettings();
database.botdb.LoadBotSettings(this);
if (RuleB(Bots, AllowBotBlockedBuffs)) {
bot_blocked_buffs.clear();
database.botdb.LoadBotBlockedBuffs(this);
}
LoadAAs();
if (database.botdb.LoadBuffs(this)) {
@@ -1374,6 +1378,10 @@ bool Bot::Save()
database.botdb.SaveStance(this);
database.botdb.SaveBotSettings(this);
if (RuleB(Bots, AllowBotBlockedBuffs)) {
database.botdb.SaveBotBlockedBuffs(this);
}
if (!SavePet())
bot_owner->Message(Chat::White, "Failed to save pet for '%s'", GetCleanName());
@@ -1439,6 +1447,10 @@ bool Bot::DeleteBot()
return false;
}
if (!database.botdb.DeleteBotBlockedBuffs(GetBotID())) {
return false;
}
if (!database.botdb.DeleteBot(GetBotID())) {
return false;
}
@@ -9573,11 +9585,22 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec
return false;
}
if (IsBeneficialSpell(spell_id) && tar->IsBlockedBuff(spell_id)) {
LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme
if (RuleB(Spells, EnableBlockedBuffs) && IsBeneficialSpell(spell_id) && tar->IsClient() && tar->IsBlockedBuff(spell_id)) {
LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme
return false;
}
if (RuleB(Bots, AllowBotBlockedBuffs) && IsBeneficialSpell(spell_id)) {
if (tar->IsBot() && tar->IsBlockedBuff(spell_id)) {
LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme
return false;
}
else if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsBot() && tar->GetOwner()->IsBlockedPetBuff(spell_id)) {
LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme
return false;
}
}
LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme
if (!CanCastSpellType(spellType, spell_id, tar)) {
return false;
@@ -11887,7 +11910,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell
return true;
}
break;
break;
case CommandedSubTypes::InvisAnimals:
if (IsEffectInSpell(spell_id, SE_InvisVsAnimals) || IsEffectInSpell(spell_id, SE_ImprovedInvisAnimals)) {
return true;
@@ -11898,7 +11921,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell
if (
(IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) < 100) ||
(IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) < 100)
) {
) {
return true;
}
@@ -11907,7 +11930,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell
if (
(IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) > 100) ||
(IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) > 100)
) {
) {
return true;
}
@@ -11945,7 +11968,7 @@ uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) {
if (!points || !rank) {
LogTestDebug("{}: {} says, 'No {} found'", __LINE__, GetCleanName(), (!points ? "points" : "rank")); //deleteme
return spell_id;
}
}
spell_id = rank->spell;
@@ -11953,3 +11976,153 @@ uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) {
return spell_id;
}
void Bot::SetBotBlockedBuff(uint16 spell_id, bool block) {
if (!IsValidSpell(spell_id)) {
OwnerMessage("Failed to set blocked buff.");
return;
}
if (!bot_blocked_buffs.empty()) {
bool found = false;
for (int i = 0; i < bot_blocked_buffs.size(); i++) {
if (bot_blocked_buffs[i].spell_id != spell_id) {
continue;
}
bot_blocked_buffs[i].blocked = block;
found = true;
}
if (!found) {
BotBlockedBuffs_Struct t;
t.spell_id = spell_id;
t.blocked = block;
bot_blocked_buffs.push_back(t);
}
}
else {
BotBlockedBuffs_Struct t;
t.spell_id = spell_id;
t.blocked = block;
bot_blocked_buffs.push_back(t);
}
CleanBotBlockedBuffs();
}
bool Bot::IsBlockedBuff(int32 spell_id)
{
bool result = false;
if (!IsValidSpell(spell_id)) {
OwnerMessage("Failed to get blocked buff.");
return result;
}
CleanBotBlockedBuffs();
if (!bot_blocked_buffs.empty()) {
for (int i = 0; i < bot_blocked_buffs.size(); i++) {
if (bot_blocked_buffs[i].spell_id != spell_id) {
continue;
}
return bot_blocked_buffs[i].blocked;
}
}
return result;
}
void Bot::SetBotBlockedPetBuff(uint16 spell_id, bool block) {
if (!IsValidSpell(spell_id)) {
OwnerMessage("Failed to set blocked pet buff.");
return;
}
if (!bot_blocked_buffs.empty()) {
bool found = false;
for (int i = 0; i < bot_blocked_buffs.size(); i++) {
if (bot_blocked_buffs[i].spell_id != spell_id) {
continue;
}
bot_blocked_buffs[i].blocked_pet = block;
found = true;
}
if (!found) {
BotBlockedBuffs_Struct t;
t.spell_id = spell_id;
t.blocked_pet = block;
bot_blocked_buffs.push_back(t);
}
}
else {
BotBlockedBuffs_Struct t;
t.spell_id = spell_id;
t.blocked_pet = block;
bot_blocked_buffs.push_back(t);
}
CleanBotBlockedBuffs();
}
bool Bot::IsBlockedPetBuff(int32 spell_id)
{
bool result = false;
if (!IsValidSpell(spell_id)) {
OwnerMessage("Failed to get blocked pet buff.");
return result;
}
CleanBotBlockedBuffs();
if (!bot_blocked_buffs.empty()) {
for (int i = 0; i < bot_blocked_buffs.size(); i++) {
if (bot_blocked_buffs[i].spell_id != spell_id) {
continue;
}
return bot_blocked_buffs[i].blocked_pet;
}
}
return result;
}
void Bot::CleanBotBlockedBuffs()
{
if (!bot_blocked_buffs.empty()) {
int current = 0;
int end = bot_blocked_buffs.size();
while (current < end) {
if (!IsValidSpell(bot_blocked_buffs[current].spell_id) || (bot_blocked_buffs[current].blocked == 0 && bot_blocked_buffs[current].blocked_pet == 0)) {
bot_blocked_buffs.erase(bot_blocked_buffs.begin() + current);
}
else {
current++;
}
end = bot_blocked_buffs.size();
}
}
}
+9
View File
@@ -478,6 +478,12 @@ public:
void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false);
BotSpell GetSpellByHealType(uint16 spellType, Mob* tar);
uint16 GetSpellByAA(int id, AA::Rank* &rank);
void CleanBotBlockedBuffs();
void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); }
bool IsBlockedBuff(int32 spell_id) override; //TODO bot rewrite - fix these to call from mob.h
bool IsBlockedPetBuff(int32 spell_id) override; //TODO bot rewrite - fix these to call from mob.h
void SetBotBlockedBuff(uint16 spell_id, bool block);
void SetBotBlockedPetBuff(uint16 spell_id, bool block);
std::string GetBotSpellCategoryName(uint8 setting_type);
std::string GetBotSettingCategoryName(uint8 setting_type);
@@ -882,6 +888,8 @@ public:
bool DeleteBot();
std::vector<BotTimer_Struct> GetBotTimers() { return bot_timers; }
void SetBotTimers(std::vector<BotTimer_Struct> timers) { bot_timers = timers; }
std::vector<BotBlockedBuffs_Struct> GetBotBlockedBuffs() { return bot_blocked_buffs; }
void SetBotBlockedBuffs(std::vector<BotBlockedBuffs_Struct> blockedBuff) { bot_blocked_buffs = blockedBuff; }
uint32 GetLastZoneID() const { return _lastZoneId; }
int32 GetBaseAC() const { return _baseAC; }
int32 GetBaseATK() const { return _baseATK; }
@@ -1010,6 +1018,7 @@ protected:
std::vector<BotSpells_Struct> AIBot_spells;
std::vector<BotSpells_Struct> AIBot_spells_enforced;
std::vector<BotTimer_Struct> bot_timers;
std::vector<BotBlockedBuffs_Struct> bot_blocked_buffs;
private:
// Class Members
+3
View File
@@ -1251,6 +1251,8 @@ int bot_command_init(void)
bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) ||
bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) ||
bot_command_add("behindmob", "Toggles whether or not your bot tries to stay behind a mob", AccountStatus::Player, bot_command_behind_mob) ||
bot_command_add("blockedbuffs", "Set, view and clear blocked buffs for the selected bot(s)", AccountStatus::Player, bot_command_blocked_buffs) ||
bot_command_add("blockedpetbuffs", "Set, view and clear blocked pet buffs for the selected bot(s)", AccountStatus::Player, bot_command_blocked_pet_buffs) ||
bot_command_add("bot", "Lists the available bot management [subcommands]", AccountStatus::Player, bot_command_bot) ||
bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", AccountStatus::Player, bot_command_appearance) ||
bot_command_add("botbeardcolor", "Changes the beard color of a bot", AccountStatus::Player, bot_command_beard_color) ||
@@ -2216,6 +2218,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) {
#include "bot_commands/apply_potion.cpp"
#include "bot_commands/attack.cpp"
#include "bot_commands/behind_mob.cpp"
#include "bot_commands/blocked_buffs.cpp"
#include "bot_commands/bot.cpp"
#include "bot_commands/bot_settings.cpp"
#include "bot_commands/cast.cpp"
+2
View File
@@ -1669,6 +1669,8 @@ void bot_command_apply_poison(Client *c, const Seperator *sep);
void bot_command_apply_potion(Client* c, const Seperator* sep);
void bot_command_attack(Client *c, const Seperator *sep);
void bot_command_behind_mob(Client* c, const Seperator* sep);
void bot_command_blocked_buffs(Client* c, const Seperator* sep);
void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep);
void bot_command_bot(Client *c, const Seperator *sep);
void bot_command_bot_settings(Client* c, const Seperator* sep);
void bot_command_cast(Client* c, const Seperator* sep);
+518
View File
@@ -0,0 +1,518 @@
#include "../bot_command.h"
void bot_command_blocked_buffs(Client* c, const Seperator* sep)
{
if (!RuleB(Bots, AllowBotBlockedBuffs)) {
c->Message(Chat::Yellow, "This command is disabled.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
std::vector<std::string> description =
{
"Allows you to set, view and wipe blocked buffs for the selected bots"
};
std::vector<std::string> notes =
{
"- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list."
};
std::vector<std::string> example_format =
{
fmt::format(
"{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]"
, sep->arg[0]
)
};
std::vector<std::string> examples_one =
{
"To add Courage (Spell ID #202) to the targeted bot's blocked list:",
fmt::format(
"{} add 202",
sep->arg[0],
c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
)
};
std::vector<std::string> examples_two =
{
"To view the targeted bot's blocked buff list:",
fmt::format(
"{} list",
sep->arg[0]
)
};
std::vector<std::string> examples_three =
{
"To wipe all Warriors bots' blocked buff list:",
fmt::format(
"{} wipe byclass {}",
sep->arg[0],
Class::Warrior
)
};
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());
c->Message(
Chat::Yellow,
fmt::format(
"You can also control bot buffs ({}).",
Saylink::Silent("^blockedbuffs help", "^blockedbuffs")
).c_str()
);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool add = false;
bool remove = false;
bool list = false;
bool wipe = false;
uint16 spell_id;
//AA help
if (!arg1.compare("add")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
add = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("remove")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
remove = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("list")) {
list = true;
}
else if (!arg1.compare("wipe")) {
wipe = true;
}
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;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionableArg = sep->arg[ab_arg];
if (actionableArg.empty()) {
actionableArg = "target";
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionableArg, 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;
}
sbl.remove(nullptr);
bool isSuccess = false;
uint16 successCount = 0;
Bot* firstFound = nullptr;
for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid(c)) {
continue;
}
if (!firstFound) {
firstFound = bot_iter;
}
if (add) {
bot_iter->SetBotBlockedBuff(spell_id, true);
}
else if (remove) {
bot_iter->SetBotBlockedBuff(spell_id, false);
}
else if (list) {
std::vector<BotBlockedBuffs_Struct> blockedBuffs = bot_iter->GetBotBlockedBuffs();
bool found = false;
if (!blockedBuffs.empty()) {
for (auto& blocked_buff : blockedBuffs) {
if (blocked_buff.blocked == 1 && IsValidSpell(blocked_buff.spell_id)) {
found = true;
c->Message(
Chat::Yellow,
fmt::format(
"{} says, '{} [#{}] is currently blocked.'",
bot_iter->GetCleanName(),
spells[blocked_buff.spell_id].name,
blocked_buff.spell_id
).c_str()
);
}
}
}
if (!found) {
c->Message(
Chat::Yellow,
fmt::format(
"{} says, 'I am not currently blocking any buffs.'",
bot_iter->GetCleanName()
).c_str()
);
}
}
else if (wipe) {
bot_iter->ClearBotBlockedBuffs();
c->Message(
Chat::Yellow,
fmt::format(
"{} says, I have wiped my blocked buffs list.'",
bot_iter->GetCleanName()
).c_str()
);
}
isSuccess = true;
++successCount;
}
if (!isSuccess) {
c->Message(Chat::Yellow, "No bots were selected.");
}
else {
if (add || remove) {
c->Message(
Chat::Yellow,
fmt::format(
"{} {} {} blocking {} [#{}]",
((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())),
((successCount == 1 && firstFound) ? "is" : "of your bots"),
(add ? "now" : "no longer"),
spells[spell_id].name,
spell_id
).c_str()
);
}
}
}
void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep)
{
if (!RuleB(Bots, AllowBotBlockedBuffs)) {
c->Message(Chat::Yellow, "This command is disabled.");
return;
}
if (helper_is_help_or_usage(sep->arg[1])) {
std::vector<std::string> description =
{
"Allows you to set, view and wipe blocked pet buffs for the selected bots"
};
std::vector<std::string> notes =
{
"- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list.",
"- This controls whether or not any pet the selected bot(s) own will prevent certain buffs from being cast."
};
std::vector<std::string> example_format =
{
fmt::format(
"{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]"
, sep->arg[0]
)
};
std::vector<std::string> examples_one =
{
"To add Courage (Spell ID #202) to the targeted bot's blocked list:",
fmt::format(
"{} add 202",
sep->arg[0],
c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke)
)
};
std::vector<std::string> examples_two =
{
"To view the targeted bot's blocked buff list:",
fmt::format(
"{} list",
sep->arg[0]
)
};
std::vector<std::string> examples_three =
{
"To wipe all Warriors bots' blocked buff list:",
fmt::format(
"{} wipe byclass {}",
sep->arg[0],
Class::Warrior
)
};
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());
c->Message(
Chat::Yellow,
fmt::format(
"You can also control pet buffs ({}).",
Saylink::Silent("^blockedpetbuffs help", "^blockedpetbuffs")
).c_str()
);
if (RuleB(Bots, SendClassRaceOnHelp)) {
c->Message(
Chat::Yellow,
fmt::format(
"Use {} for information about race/class IDs.",
Saylink::Silent("^classracelist")
).c_str()
);
}
return;
}
std::string arg1 = sep->arg[1];
std::string arg2 = sep->arg[2];
int ab_arg = 2;
bool add = false;
bool remove = false;
bool list = false;
bool wipe = false;
uint16 spell_id;
//AA help
if (!arg1.compare("add")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
add = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("remove")) {
if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) {
c->Message(Chat::Yellow, "You must enter a valid spell ID.");
return;
}
remove = true;
spell_id = atoi(sep->arg[2]);
++ab_arg;
}
else if (!arg1.compare("list")) {
list = true;
}
else if (!arg1.compare("wipe")) {
wipe = true;
}
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;
}
const int ab_mask = ActionableBots::ABM_Type1;
std::string actionableArg = sep->arg[ab_arg];
if (actionableArg.empty()) {
actionableArg = "target";
}
std::string class_race_arg = sep->arg[ab_arg];
bool class_race_check = false;
if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) {
class_race_check = true;
}
std::list<Bot*> sbl;
if (ActionableBots::PopulateSBL(c, actionableArg, 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;
}
sbl.remove(nullptr);
bool isSuccess = false;
uint16 successCount = 0;
Bot* firstFound = nullptr;
for (auto bot_iter : sbl) {
if (!bot_iter->IsInGroupOrRaid(c)) {
continue;
}
if (!firstFound) {
firstFound = bot_iter;
}
if (add) {
bot_iter->SetBotBlockedPetBuff(spell_id, true);
}
else if (remove) {
bot_iter->SetBotBlockedPetBuff(spell_id, false);
}
else if (list) {
std::vector<BotBlockedBuffs_Struct> blockedBuffs = bot_iter->GetBotBlockedBuffs();
bool found = false;
if (!blockedBuffs.empty()) {
for (auto& blocked_buff : blockedBuffs) {
if (blocked_buff.blocked_pet == 1 && IsValidSpell(blocked_buff.spell_id)) {
found = true;
c->Message(
Chat::Yellow,
fmt::format(
"{} says, '{} [#{}] is currently blocked for my pet.'",
bot_iter->GetCleanName(),
spells[blocked_buff.spell_id].name,
blocked_buff.spell_id
).c_str()
);
}
}
}
if (!found) {
c->Message(
Chat::Yellow,
fmt::format(
"{} says, 'I am not currently blocking any pet buffs.'",
bot_iter->GetCleanName()
).c_str()
);
}
}
else if (wipe) {
bot_iter->ClearBotBlockedBuffs();
c->Message(
Chat::Yellow,
fmt::format(
"{} says, I have wiped my blocked buffs list.'",
bot_iter->GetCleanName()
).c_str()
);
}
isSuccess = true;
++successCount;
}
if (!isSuccess) {
c->Message(Chat::Yellow, "No bots were selected.");
}
else {
if (add || remove) {
c->Message(
Chat::Yellow,
fmt::format(
"{} {} {} blocking {} [#{}] on their pet.",
((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())),
((successCount == 1 && firstFound) ? "is" : "of your bots"),
(add ? "now" : "no longer"),
spells[spell_id].name,
spell_id
).c_str()
);
}
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ 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.",
"- 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",
fmt::format(
"- You can use {} aa # to cast any clickable AA or specifically {} harmtouch / {} layonhands"
+108
View File
@@ -22,6 +22,7 @@
#include "../common/strings.h"
#include "../common/eqemu_logsys.h"
#include "../common/repositories/bot_blocked_buffs_repository.h"
#include "../common/repositories/bot_buffs_repository.h"
#include "../common/repositories/bot_create_combinations_repository.h"
#include "../common/repositories/bot_data_repository.h"
@@ -2403,3 +2404,110 @@ bool BotDatabase::DeleteBotSettings(const uint32 bot_id)
return true;
}
bool BotDatabase::LoadBotBlockedBuffs(Bot* b)
{
if (!b) {
return false;
}
const auto& l = BotBlockedBuffsRepository::GetWhere(
database,
fmt::format(
"`bot_id` = {}",
b->GetBotID()
)
);
std::vector<BotBlockedBuffs_Struct> v;
BotBlockedBuffs_Struct t{ };
for (const auto& e : l) {
t.spell_id = e.spell_id;
t.blocked = e.blocked;
t.blocked_pet = e.blocked_pet;
v.push_back(t);
}
if (!v.empty()) {
b->SetBotBlockedBuffs(v);
}
return true;
}
bool BotDatabase::SaveBotBlockedBuffs(Bot* b)
{
if (!b) {
return false;
}
if (!DeleteBotBlockedBuffs(b->GetBotID())) {
return false;
}
std::vector<BotBlockedBuffs_Struct> v = b->GetBotBlockedBuffs();
if (v.empty()) {
return true;
}
std::vector<BotBlockedBuffsRepository::BotBlockedBuffs> l;
if (!v.empty()) {
for (auto& blocked_buff : v) {
if (blocked_buff.blocked == 0 && blocked_buff.blocked_pet == 0) {
continue;
}
auto e = BotBlockedBuffsRepository::BotBlockedBuffs{
.bot_id = b->GetBotID(),
.spell_id = blocked_buff.spell_id,
.blocked = blocked_buff.blocked,
.blocked_pet = blocked_buff.blocked_pet
};
l.push_back(e);
}
if (l.empty()) {
return true;
}
BotBlockedBuffsRepository::DeleteWhere(
database,
fmt::format(
"`bot_id` = {}",
b->GetBotID()
)
);
const int inserted = BotBlockedBuffsRepository::InsertMany(database, l);
if (!inserted) {
DeleteBotBlockedBuffs(b->GetBotID());
return false;
}
}
return true;
}
bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id)
{
if (!bot_id) {
return false;
}
BotBlockedBuffsRepository::DeleteWhere(
database,
fmt::format(
"`bot_id` = {}",
bot_id
)
);
return true;
}
+3
View File
@@ -74,6 +74,9 @@ public:
bool SaveTimers(Bot* b);
bool DeleteTimers(const uint32 bot_id);
bool LoadBotBlockedBuffs(Bot* b);
bool SaveBotBlockedBuffs(Bot* b);
bool DeleteBotBlockedBuffs(const uint32 bot_id);
/* Bot inventory functions */
bool QueryInventoryCount(const uint32 bot_id, uint32& item_count);
+7
View File
@@ -97,4 +97,11 @@ struct BotSpellTypeOrder {
uint16 priority;
};
struct BotBlockedBuffs_Struct {
uint32_t bot_id;
uint32_t spell_id;
uint8_t blocked;
uint8_t blocked_pet;
};
#endif // BOT_STRUCTS
+4 -1
View File
@@ -4080,7 +4080,10 @@ bool Mob::SpellOnTarget(
}
// now check if the spell is allowed to land
if (RuleB(Spells, EnableBlockedBuffs)) {
if (
(!spelltar->IsBot() && RuleB(Spells, EnableBlockedBuffs)) ||
(spelltar->IsBot() && RuleB(Bots, AllowBotBlockedBuffs))
) {
// We return true here since the caster's client should act like normal
if (spelltar->IsBlockedBuff(spell_id)) {
LogSpells(