mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +00:00
[Bots] Add Bot-specific Spell Settings. (#2553)
* [Bots] Add Bot-specific Spell Settings. # Notes - Allows players to set `priority`, `min_level`, `max_level`, `min_hp`, `max_hp`, and `is_enabled` settings per spell based on targeted bot. - Lets players disable spells they don't want their bots casting or change the criteria they cast them at if they want. * Update botspellsai.cpp * Update 2022_11_19_bot_spell_settings.sql * Typo. * Update botspellsai.cpp * Cleanup and add Reload Methods to Perl/Lua.
This commit is contained in:
parent
f6c5560e9c
commit
2d364e2fd1
@ -403,6 +403,7 @@ namespace DatabaseSchema {
|
||||
"bot_pet_inventories",
|
||||
"bot_pets",
|
||||
"bot_spell_casting_chances",
|
||||
"bot_spell_settings",
|
||||
"bot_spells_entries",
|
||||
"bot_stances",
|
||||
"bot_timers",
|
||||
|
||||
403
common/repositories/base/base_bot_spell_settings_repository.h
Normal file
403
common/repositories/base/base_bot_spell_settings_repository.h
Normal file
@ -0,0 +1,403 @@
|
||||
/**
|
||||
* 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://eqemu.gitbook.io/server/in-development/developer-area/repositories
|
||||
*/
|
||||
|
||||
#ifndef EQEMU_BASE_BOT_SPELL_SETTINGS_REPOSITORY_H
|
||||
#define EQEMU_BASE_BOT_SPELL_SETTINGS_REPOSITORY_H
|
||||
|
||||
#include "../../database.h"
|
||||
#include "../../strings.h"
|
||||
#include <ctime>
|
||||
|
||||
class BaseBotSpellSettingsRepository {
|
||||
public:
|
||||
struct BotSpellSettings {
|
||||
uint32_t id;
|
||||
int32_t bot_id;
|
||||
int16_t spell_id;
|
||||
int16_t priority;
|
||||
uint16_t min_level;
|
||||
uint16_t max_level;
|
||||
int16_t min_hp;
|
||||
int16_t max_hp;
|
||||
uint8_t is_enabled;
|
||||
};
|
||||
|
||||
static std::string PrimaryKey()
|
||||
{
|
||||
return std::string("id");
|
||||
}
|
||||
|
||||
static std::vector<std::string> Columns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"bot_id",
|
||||
"spell_id",
|
||||
"priority",
|
||||
"min_level",
|
||||
"max_level",
|
||||
"min_hp",
|
||||
"max_hp",
|
||||
"is_enabled",
|
||||
};
|
||||
}
|
||||
|
||||
static std::vector<std::string> SelectColumns()
|
||||
{
|
||||
return {
|
||||
"id",
|
||||
"bot_id",
|
||||
"spell_id",
|
||||
"priority",
|
||||
"min_level",
|
||||
"max_level",
|
||||
"min_hp",
|
||||
"max_hp",
|
||||
"is_enabled",
|
||||
};
|
||||
}
|
||||
|
||||
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_spell_settings");
|
||||
}
|
||||
|
||||
static std::string BaseSelect()
|
||||
{
|
||||
return fmt::format(
|
||||
"SELECT {} FROM {}",
|
||||
SelectColumnsRaw(),
|
||||
TableName()
|
||||
);
|
||||
}
|
||||
|
||||
static std::string BaseInsert()
|
||||
{
|
||||
return fmt::format(
|
||||
"INSERT INTO {} ({}) ",
|
||||
TableName(),
|
||||
ColumnsRaw()
|
||||
);
|
||||
}
|
||||
|
||||
static BotSpellSettings NewEntity()
|
||||
{
|
||||
BotSpellSettings e{};
|
||||
|
||||
e.id = 0;
|
||||
e.bot_id = 0;
|
||||
e.spell_id = 0;
|
||||
e.priority = 0;
|
||||
e.min_level = 0;
|
||||
e.max_level = 255;
|
||||
e.min_hp = 0;
|
||||
e.max_hp = 0;
|
||||
e.is_enabled = 1;
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static BotSpellSettings GetBotSpellSettings(
|
||||
const std::vector<BotSpellSettings> &bot_spell_settingss,
|
||||
int bot_spell_settings_id
|
||||
)
|
||||
{
|
||||
for (auto &bot_spell_settings : bot_spell_settingss) {
|
||||
if (bot_spell_settings.id == bot_spell_settings_id) {
|
||||
return bot_spell_settings;
|
||||
}
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static BotSpellSettings FindOne(
|
||||
Database& db,
|
||||
int bot_spell_settings_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} WHERE id = {} LIMIT 1",
|
||||
BaseSelect(),
|
||||
bot_spell_settings_id
|
||||
)
|
||||
);
|
||||
|
||||
auto row = results.begin();
|
||||
if (results.RowCount() == 1) {
|
||||
BotSpellSettings e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.bot_id = static_cast<int32_t>(atoi(row[1]));
|
||||
e.spell_id = static_cast<int16_t>(atoi(row[2]));
|
||||
e.priority = static_cast<int16_t>(atoi(row[3]));
|
||||
e.min_level = static_cast<uint16_t>(strtoul(row[4], nullptr, 10));
|
||||
e.max_level = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
|
||||
e.min_hp = static_cast<int16_t>(atoi(row[6]));
|
||||
e.max_hp = static_cast<int16_t>(atoi(row[7]));
|
||||
e.is_enabled = static_cast<uint8_t>(strtoul(row[8], nullptr, 10));
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
return NewEntity();
|
||||
}
|
||||
|
||||
static int DeleteOne(
|
||||
Database& db,
|
||||
int bot_spell_settings_id
|
||||
)
|
||||
{
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"DELETE FROM {} WHERE {} = {}",
|
||||
TableName(),
|
||||
PrimaryKey(),
|
||||
bot_spell_settings_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static int UpdateOne(
|
||||
Database& db,
|
||||
const BotSpellSettings &e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[0] + " = " + std::to_string(e.id));
|
||||
v.push_back(columns[1] + " = " + std::to_string(e.bot_id));
|
||||
v.push_back(columns[2] + " = " + std::to_string(e.spell_id));
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.priority));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.min_level));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.max_level));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.min_hp));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.max_hp));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.is_enabled));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE {} = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
PrimaryKey(),
|
||||
e.id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? results.RowsAffected() : 0);
|
||||
}
|
||||
|
||||
static BotSpellSettings InsertOne(
|
||||
Database& db,
|
||||
BotSpellSettings e
|
||||
)
|
||||
{
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
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.priority));
|
||||
v.push_back(std::to_string(e.min_level));
|
||||
v.push_back(std::to_string(e.max_level));
|
||||
v.push_back(std::to_string(e.min_hp));
|
||||
v.push_back(std::to_string(e.max_hp));
|
||||
v.push_back(std::to_string(e.is_enabled));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{} VALUES ({})",
|
||||
BaseInsert(),
|
||||
Strings::Implode(",", v)
|
||||
)
|
||||
);
|
||||
|
||||
if (results.Success()) {
|
||||
e.id = results.LastInsertedID();
|
||||
return e;
|
||||
}
|
||||
|
||||
e = NewEntity();
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
static int InsertMany(
|
||||
Database& db,
|
||||
const std::vector<BotSpellSettings> &entries
|
||||
)
|
||||
{
|
||||
std::vector<std::string> insert_chunks;
|
||||
|
||||
for (auto &e: entries) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
v.push_back(std::to_string(e.id));
|
||||
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.priority));
|
||||
v.push_back(std::to_string(e.min_level));
|
||||
v.push_back(std::to_string(e.max_level));
|
||||
v.push_back(std::to_string(e.min_hp));
|
||||
v.push_back(std::to_string(e.max_hp));
|
||||
v.push_back(std::to_string(e.is_enabled));
|
||||
|
||||
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<BotSpellSettings> All(Database& db)
|
||||
{
|
||||
std::vector<BotSpellSettings> all_entries;
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"{}",
|
||||
BaseSelect()
|
||||
)
|
||||
);
|
||||
|
||||
all_entries.reserve(results.RowCount());
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
BotSpellSettings e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.bot_id = static_cast<int32_t>(atoi(row[1]));
|
||||
e.spell_id = static_cast<int16_t>(atoi(row[2]));
|
||||
e.priority = static_cast<int16_t>(atoi(row[3]));
|
||||
e.min_level = static_cast<uint16_t>(strtoul(row[4], nullptr, 10));
|
||||
e.max_level = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
|
||||
e.min_hp = static_cast<int16_t>(atoi(row[6]));
|
||||
e.max_hp = static_cast<int16_t>(atoi(row[7]));
|
||||
e.is_enabled = static_cast<uint8_t>(strtoul(row[8], nullptr, 10));
|
||||
|
||||
all_entries.push_back(e);
|
||||
}
|
||||
|
||||
return all_entries;
|
||||
}
|
||||
|
||||
static std::vector<BotSpellSettings> GetWhere(Database& db, const std::string &where_filter)
|
||||
{
|
||||
std::vector<BotSpellSettings> 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) {
|
||||
BotSpellSettings e{};
|
||||
|
||||
e.id = static_cast<uint32_t>(strtoul(row[0], nullptr, 10));
|
||||
e.bot_id = static_cast<int32_t>(atoi(row[1]));
|
||||
e.spell_id = static_cast<int16_t>(atoi(row[2]));
|
||||
e.priority = static_cast<int16_t>(atoi(row[3]));
|
||||
e.min_level = static_cast<uint16_t>(strtoul(row[4], nullptr, 10));
|
||||
e.max_level = static_cast<uint16_t>(strtoul(row[5], nullptr, 10));
|
||||
e.min_hp = static_cast<int16_t>(atoi(row[6]));
|
||||
e.max_hp = static_cast<int16_t>(atoi(row[7]));
|
||||
e.is_enabled = static_cast<uint8_t>(strtoul(row[8], nullptr, 10));
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif //EQEMU_BASE_BOT_SPELL_SETTINGS_REPOSITORY_H
|
||||
77
common/repositories/bot_spell_settings_repository.h
Normal file
77
common/repositories/bot_spell_settings_repository.h
Normal file
@ -0,0 +1,77 @@
|
||||
#ifndef EQEMU_BOT_SPELL_SETTINGS_REPOSITORY_H
|
||||
#define EQEMU_BOT_SPELL_SETTINGS_REPOSITORY_H
|
||||
|
||||
#include "../database.h"
|
||||
#include "../strings.h"
|
||||
#include "base/base_bot_spell_settings_repository.h"
|
||||
|
||||
class BotSpellSettingsRepository: public BaseBotSpellSettingsRepository {
|
||||
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
|
||||
*
|
||||
* BotSpellSettingsRepository::GetByZoneAndVersion(int zone_id, int zone_version)
|
||||
* BotSpellSettingsRepository::GetWhereNeverExpires()
|
||||
* BotSpellSettingsRepository::GetWhereXAndY()
|
||||
* BotSpellSettingsRepository::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
|
||||
|
||||
static bool UpdateSpellSetting(
|
||||
Database& db,
|
||||
const BotSpellSettings &e
|
||||
) {
|
||||
std::vector<std::string> v;
|
||||
|
||||
auto columns = Columns();
|
||||
|
||||
v.push_back(columns[3] + " = " + std::to_string(e.priority));
|
||||
v.push_back(columns[4] + " = " + std::to_string(e.min_level));
|
||||
v.push_back(columns[5] + " = " + std::to_string(e.max_level));
|
||||
v.push_back(columns[6] + " = " + std::to_string(e.min_hp));
|
||||
v.push_back(columns[7] + " = " + std::to_string(e.max_hp));
|
||||
v.push_back(columns[8] + " = " + std::to_string(e.is_enabled));
|
||||
|
||||
auto results = db.QueryDatabase(
|
||||
fmt::format(
|
||||
"UPDATE {} SET {} WHERE `bot_id` = {} AND `spell_id` = {}",
|
||||
TableName(),
|
||||
Strings::Implode(", ", v),
|
||||
e.bot_id,
|
||||
e.spell_id
|
||||
)
|
||||
);
|
||||
|
||||
return (results.Success() ? true : false);
|
||||
}
|
||||
};
|
||||
|
||||
#endif //EQEMU_BOT_SPELL_SETTINGS_REPOSITORY_H
|
||||
@ -37,7 +37,7 @@
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9212
|
||||
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9032
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9033
|
||||
#else
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||
#endif
|
||||
|
||||
@ -31,6 +31,7 @@
|
||||
9030|2022_10_27_bot_data_buckets.sql|SHOW COLUMNS FROM `bot_spells_entries` LIKE 'bucket_name'|empty|
|
||||
9031|2022_11_13_bot_spells_entries.sql|SELECT * FROM db_version WHERE bots_version >= 9031|empty|
|
||||
9032|2022_11_07_bot_expansion_bitmask.sql|SHOW COLUMNS FROM `bot_data` LIKE 'expansion_bitmask'|empty|
|
||||
9033|2022_11_19_bot_spell_settings.sql|SHOW TABLES LIKE 'bot_spell_settings'|empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
@ -0,0 +1,12 @@
|
||||
CREATE TABLE `bot_spell_settings` (
|
||||
`id` int(11) unsigned NOT NULL AUTO_INCREMENT,
|
||||
`bot_id` int(11) NOT NULL DEFAULT 0,
|
||||
`spell_id` smallint(5) NOT NULL DEFAULT 0,
|
||||
`priority` smallint(5) NOT NULL DEFAULT 0,
|
||||
`min_level` smallint(5) unsigned NOT NULL DEFAULT 0,
|
||||
`max_level` smallint(5) unsigned NOT NULL DEFAULT 255,
|
||||
`min_hp` smallint(5) NOT NULL DEFAULT 0,
|
||||
`max_hp` smallint(5) NOT NULL DEFAULT 0,
|
||||
`is_enabled` tinyint(1) unsigned NOT NULL DEFAULT 1,
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
|
||||
316
zone/bot.cpp
316
zone/bot.cpp
@ -25,6 +25,7 @@
|
||||
#include "lua_parser.h"
|
||||
#include "../common/strings.h"
|
||||
#include "../common/say_link.h"
|
||||
#include "../common/repositories/bot_spell_settings_repository.h"
|
||||
|
||||
extern volatile bool is_zone_loaded;
|
||||
|
||||
@ -407,6 +408,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
|
||||
GetBotOwnerDataBuckets();
|
||||
GetBotDataBuckets();
|
||||
LoadBotSpellSettings();
|
||||
AI_AddBotSpells(GetBotSpellID());
|
||||
|
||||
CalcBotStats(false);
|
||||
@ -10538,18 +10540,24 @@ bool Bot::GetBotOwnerDataBuckets()
|
||||
return false;
|
||||
}
|
||||
|
||||
auto query = fmt::format(
|
||||
const auto query = fmt::format(
|
||||
"SELECT `key`, `value` FROM data_buckets WHERE `key` LIKE '{}-%'",
|
||||
Strings::Escape(bot_owner->GetBucketKey())
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bot_owner_data_buckets.clear();
|
||||
|
||||
if (!results.RowCount()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto row : results) {
|
||||
bot_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
|
||||
bot_owner_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -10557,16 +10565,22 @@ bool Bot::GetBotOwnerDataBuckets()
|
||||
|
||||
bool Bot::GetBotDataBuckets()
|
||||
{
|
||||
auto query = fmt::format(
|
||||
const auto query = fmt::format(
|
||||
"SELECT `key`, `value` FROM data_buckets WHERE `key` LIKE '{}-%'",
|
||||
Strings::Escape(GetBucketKey())
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
auto results = database.QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bot_data_buckets.clear();
|
||||
|
||||
if (!results.RowCount()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto row : results) {
|
||||
bot_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
|
||||
}
|
||||
@ -10591,7 +10605,7 @@ bool Bot::CheckDataBucket(std::string bucket_name, std::string bucket_value, uin
|
||||
bucket_name
|
||||
);
|
||||
|
||||
player_value = bot_data_buckets[full_name];
|
||||
player_value = bot_owner_data_buckets[full_name];
|
||||
if (player_value.empty()) {
|
||||
return false;
|
||||
}
|
||||
@ -10635,6 +10649,292 @@ void Bot::SetExpansionBitmask(int expansion_bitmask, bool save)
|
||||
LoadAAs();
|
||||
}
|
||||
|
||||
bool Bot::AddBotSpellSetting(uint16 spell_id, BotSpellSetting* bs)
|
||||
{
|
||||
if (!IsValidSpell(spell_id) || !bs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto obs = GetBotSpellSetting(spell_id);
|
||||
if (obs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto s = BotSpellSettingsRepository::NewEntity();
|
||||
|
||||
s.spell_id = spell_id;
|
||||
s.bot_id = GetBotID();
|
||||
|
||||
s.priority = bs->priority;
|
||||
s.min_level = bs->min_level;
|
||||
s.max_level = bs->max_level;
|
||||
s.min_hp = bs->min_hp;
|
||||
s.max_hp = bs->max_hp;
|
||||
s.is_enabled = bs->is_enabled;
|
||||
|
||||
const auto& nbs = BotSpellSettingsRepository::InsertOne(content_db, s);
|
||||
if (!nbs.id) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadBotSpellSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Bot::DeleteBotSpellSetting(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto bs = GetBotSpellSetting(spell_id);
|
||||
if (!bs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
BotSpellSettingsRepository::DeleteWhere(
|
||||
content_db,
|
||||
fmt::format(
|
||||
"bot_id = {} AND spell_id = {}",
|
||||
GetBotID(),
|
||||
spell_id
|
||||
)
|
||||
);
|
||||
LoadBotSpellSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
BotSpellSetting* Bot::GetBotSpellSetting(uint16 spell_id)
|
||||
{
|
||||
if (!IsValidSpell(spell_id) || !bot_spell_settings.count(spell_id)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto b = bot_spell_settings.find(spell_id);
|
||||
if (b != bot_spell_settings.end()) {
|
||||
return &b->second;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Bot::ListBotSpells()
|
||||
{
|
||||
auto bot_owner = GetBotOwner();
|
||||
if (!bot_owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (AIBot_spells.empty()) {
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} has no AI Spells.",
|
||||
GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto spell_count = 0;
|
||||
auto spell_number = 1;
|
||||
|
||||
for (const auto& s : AIBot_spells) {
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell {} | Spell: {} ({})",
|
||||
spell_number,
|
||||
spells[s.spellid].name,
|
||||
s.spellid
|
||||
).c_str()
|
||||
);
|
||||
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell {} | Priority: {} Health: {}",
|
||||
spell_number,
|
||||
s.priority,
|
||||
GetHPString(s.min_hp, s.max_hp)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
spell_count++;
|
||||
spell_number++;
|
||||
}
|
||||
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} has {} AI Spell{}.",
|
||||
GetCleanName(),
|
||||
spell_count,
|
||||
spell_count != 1 ? "s" :""
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void Bot::ListBotSpellSettings()
|
||||
{
|
||||
auto bot_owner = GetBotOwner();
|
||||
if (!bot_owner) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bot_spell_settings.size()) {
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} does not have any spell settings.",
|
||||
GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto setting_count = 0;
|
||||
auto setting_number = 1;
|
||||
|
||||
for (const auto& bs : bot_spell_settings) {
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Setting {} | Spell: {} ({}) Enabled: {}",
|
||||
setting_number,
|
||||
spells[bs.first].name,
|
||||
bs.first,
|
||||
bs.second.is_enabled ? "Yes" : "No"
|
||||
).c_str()
|
||||
);
|
||||
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Setting {} | Priority: {} Levels: {} Health: {}",
|
||||
setting_number,
|
||||
bs.second.priority,
|
||||
GetLevelString(bs.second.min_level, bs.second.max_level),
|
||||
GetHPString(bs.second.min_hp, bs.second.max_hp)
|
||||
).c_str()
|
||||
);
|
||||
|
||||
setting_count++;
|
||||
setting_number++;
|
||||
}
|
||||
|
||||
bot_owner->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} has {} spell setting{}.",
|
||||
GetCleanName(),
|
||||
setting_count,
|
||||
setting_count != 1 ? "s" : ""
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void Bot::LoadBotSpellSettings()
|
||||
{
|
||||
bot_spell_settings.clear();
|
||||
|
||||
auto s = BotSpellSettingsRepository::GetWhere(content_db, fmt::format("bot_id = {}", GetBotID()));
|
||||
if (s.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& e : s) {
|
||||
BotSpellSetting b;
|
||||
|
||||
b.priority = e.priority;
|
||||
b.min_level = e.min_level;
|
||||
b.max_level = e.max_level;
|
||||
b.min_hp = e.min_hp;
|
||||
b.max_hp = e.max_hp;
|
||||
b.is_enabled = e.is_enabled;
|
||||
|
||||
bot_spell_settings[e.spell_id] = b;
|
||||
}
|
||||
}
|
||||
|
||||
bool Bot::UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs)
|
||||
{
|
||||
if (!IsValidSpell(spell_id) || !bs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto s = BotSpellSettingsRepository::NewEntity();
|
||||
|
||||
s.spell_id = spell_id;
|
||||
s.bot_id = GetBotID();
|
||||
s.priority = bs->priority;
|
||||
s.min_level = bs->min_level;
|
||||
s.max_level = bs->max_level;
|
||||
s.min_hp = bs->min_hp;
|
||||
s.max_hp = bs->max_hp;
|
||||
s.is_enabled = bs->is_enabled;
|
||||
|
||||
auto obs = GetBotSpellSetting(spell_id);
|
||||
if (!obs) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BotSpellSettingsRepository::UpdateSpellSetting(content_db, s)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadBotSpellSettings();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string Bot::GetLevelString(uint8 min_level, uint8 max_level)
|
||||
{
|
||||
std::string level_string = "Any";
|
||||
if (min_level && max_level) {
|
||||
level_string = fmt::format(
|
||||
"{} to {}",
|
||||
min_level,
|
||||
max_level
|
||||
);
|
||||
} else if (min_level && !max_level) {
|
||||
level_string = fmt::format(
|
||||
"{}+",
|
||||
min_level
|
||||
);
|
||||
} else if (!min_level && max_level) {
|
||||
level_string = fmt::format(
|
||||
"1 to {}",
|
||||
max_level
|
||||
);
|
||||
}
|
||||
|
||||
return level_string;
|
||||
}
|
||||
|
||||
std::string Bot::GetHPString(int8 min_hp, int8 max_hp)
|
||||
{
|
||||
std::string hp_string = "Any";
|
||||
if (min_hp && max_hp) {
|
||||
hp_string = fmt::format(
|
||||
"{}%% to {}%%",
|
||||
min_hp,
|
||||
max_hp
|
||||
);
|
||||
} else if (min_hp && !max_hp) {
|
||||
hp_string = fmt::format(
|
||||
"{}%% to 100%%",
|
||||
min_hp
|
||||
);
|
||||
} else if (!min_hp && max_hp) {
|
||||
hp_string = fmt::format(
|
||||
"1%% to {}%%",
|
||||
max_hp
|
||||
);
|
||||
}
|
||||
|
||||
return hp_string;
|
||||
}
|
||||
|
||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||
|
||||
#endif
|
||||
|
||||
18
zone/bot.h
18
zone/bot.h
@ -307,7 +307,7 @@ public:
|
||||
void DoEnduranceRegen(); //This Regenerates endurance
|
||||
void DoEnduranceUpkeep(); //does the endurance upkeep
|
||||
|
||||
bool AI_AddBotSpells(uint32 iDBSpellsID);
|
||||
bool AI_AddBotSpells(uint32 bot_spell_id);
|
||||
void AddSpellToBotList(
|
||||
int16 iPriority,
|
||||
uint16 iSpellID,
|
||||
@ -470,6 +470,7 @@ public:
|
||||
//static void UpdateRaidCastingRoles(const Raid* raid, bool disband = false);
|
||||
|
||||
bool IsBotCaster() { return IsCasterClass(GetClass()); }
|
||||
bool IsBotHybrid() { return IsHybridClass(GetClass()); }
|
||||
bool IsBotINTCaster() { return IsINTCasterClass(GetClass()); }
|
||||
bool IsBotWISCaster() { return IsWISCasterClass(GetClass()); }
|
||||
bool IsBotSpellFighter() { return IsSpellFighterClass(GetClass()); }
|
||||
@ -596,6 +597,18 @@ public:
|
||||
int GetExpansionBitmask();
|
||||
void SetExpansionBitmask(int expansion_bitmask, bool save = true);
|
||||
|
||||
void ListBotSpells();
|
||||
|
||||
std::string GetLevelString(uint8 min_level, uint8 max_level);
|
||||
std::string GetHPString(int8 min_hp, int8 max_hp);
|
||||
|
||||
bool AddBotSpellSetting(uint16 spell_id, BotSpellSetting* bs);
|
||||
bool DeleteBotSpellSetting(uint16 spell_id);
|
||||
BotSpellSetting* GetBotSpellSetting(uint16 spell_id);
|
||||
void ListBotSpellSettings();
|
||||
void LoadBotSpellSettings();
|
||||
bool UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs);
|
||||
|
||||
static void SpawnBotGroupByName(Client* c, std::string botgroup_name, uint32 leader_id);
|
||||
|
||||
std::string CreateSayLink(Client* botOwner, const char* message, const char* name);
|
||||
@ -762,6 +775,9 @@ private:
|
||||
|
||||
BotCastingRoles m_CastingRoles;
|
||||
std::map<std::string,std::string> bot_data_buckets;
|
||||
std::map<std::string,std::string> bot_owner_data_buckets;
|
||||
|
||||
std::map<uint16, BotSpellSetting> bot_spell_settings;
|
||||
|
||||
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
|
||||
|
||||
|
||||
@ -1416,6 +1416,12 @@ int bot_command_init(void)
|
||||
bot_command_add("rune", "Orders a bot to cast a rune of protection", AccountStatus::Player, bot_command_rune) ||
|
||||
bot_command_add("sendhome", "Orders a bot to open a magical doorway home", AccountStatus::Player, bot_command_send_home) ||
|
||||
bot_command_add("size", "Orders a bot to change a player's size", AccountStatus::Player, bot_command_size) ||
|
||||
bot_command_add("spelllist", "Lists a Caster of Hybrid bot's spells", AccountStatus::Player, bot_command_spell_list) ||
|
||||
bot_command_add("spellsettingsadd", "Add a bot spell setting for a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_add) ||
|
||||
bot_command_add("spellsettingsdelete", "Delete a bot spell setting from a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_delete) ||
|
||||
bot_command_add("spellsettingslist", "Lists a Caster or Hybrid bot's spell settings", AccountStatus::Player, bot_command_spell_settings_list) ||
|
||||
bot_command_add("spellsettingstoggle", "Toggle a bot spell for a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_toggle) ||
|
||||
bot_command_add("spellsettingsupdate", "Update a bot spell setting for a Caster or Hybrid bot", AccountStatus::Player, bot_command_spell_settings_update) ||
|
||||
bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) ||
|
||||
bot_command_add("suspend", "Suspends a bot's AI processing until released", AccountStatus::Player, bot_command_suspend) ||
|
||||
bot_command_add("taunt", "Toggles taunt use by a bot", AccountStatus::Player, bot_command_taunt) ||
|
||||
@ -10086,7 +10092,7 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b
|
||||
if (local_entry->single != single_flag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
msg = fmt::format(
|
||||
"{}circle {}{}",
|
||||
std::to_string(BOT_COMMAND_CHAR),
|
||||
@ -10122,7 +10128,7 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b
|
||||
if (local_entry->single != single_flag) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
msg = fmt::format(
|
||||
"{}portal {}{}",
|
||||
std::to_string(BOT_COMMAND_CHAR),
|
||||
@ -10226,4 +10232,514 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp
|
||||
return false;
|
||||
}
|
||||
|
||||
void bot_command_spell_list(Client* c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_list", sep->arg[0], "spelllist")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_bot = ActionableBots::AsTarget_ByBot(c);
|
||||
if (!my_bot) {
|
||||
c->Message(Chat::White, "You must target a bot that you own to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my_bot->IsBotCaster() && !my_bot->IsBotHybrid()) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
my_bot->ListBotSpells();
|
||||
}
|
||||
|
||||
void bot_command_spell_settings_add(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_settings_add", sep->arg[0], "spellsettingsadd")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_bot = ActionableBots::AsTarget_ByBot(c);
|
||||
if (!my_bot) {
|
||||
c->Message(Chat::White, "You must target a bot that you own to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my_bot->IsBotCaster() && !my_bot->IsBotHybrid()) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto arguments = sep->argnum;
|
||||
if (
|
||||
arguments < 6 ||
|
||||
!sep->IsNumber(1) ||
|
||||
!sep->IsNumber(2) ||
|
||||
!sep->IsNumber(3) ||
|
||||
!sep->IsNumber(4) ||
|
||||
!sep->IsNumber(5) ||
|
||||
!sep->IsNumber(6)
|
||||
) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto spell_id = static_cast<uint16>(std::stoul(sep->arg[1]));
|
||||
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell ID {} is invalid or could not be found.",
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (my_bot->GetBotSpellSetting(spell_id)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"{} already has a spell setting for {} ({}), trying using {} instead.",
|
||||
my_bot->GetCleanName(),
|
||||
spells[spell_id].name,
|
||||
spell_id,
|
||||
Saylink::Silent("^spellsettingsupdate")
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto priority = static_cast<int16>(std::stoi(sep->arg[2]));
|
||||
auto min_level = static_cast<uint8>(std::stoul(sep->arg[3]));
|
||||
auto max_level = static_cast<uint8>(std::stoul(sep->arg[4]));
|
||||
auto min_hp = static_cast<int8>(EQ::Clamp(std::stoi(sep->arg[5]), -1, 99));
|
||||
auto max_hp = static_cast<int8>(EQ::Clamp(std::stoi(sep->arg[6]), -1, 100));
|
||||
|
||||
BotSpellSetting bs;
|
||||
|
||||
bs.priority = priority;
|
||||
bs.min_level = min_level;
|
||||
bs.max_level = max_level;
|
||||
bs.min_hp = min_hp;
|
||||
bs.max_hp = max_hp;
|
||||
|
||||
if (!my_bot->UpdateBotSpellSetting(spell_id, &bs)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Failed to add spell setting for {}.",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Successfully added spell setting for {}.",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell Setting Added | Spell: {} ({}) ",
|
||||
spells[spell_id].name,
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell Setting Added | Priority: {} Levels: {} Health: {}",
|
||||
priority,
|
||||
my_bot->GetLevelString(min_level, max_level),
|
||||
my_bot->GetHPString(min_hp, max_hp)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void bot_command_spell_settings_delete(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_settings_delete", sep->arg[0], "spellsettingsdelete")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_bot = ActionableBots::AsTarget_ByBot(c);
|
||||
if (!my_bot) {
|
||||
c->Message(Chat::White, "You must target a bot that you own to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my_bot->IsBotCaster() && !my_bot->IsBotHybrid()) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto arguments = sep->argnum;
|
||||
if (
|
||||
arguments < 1 ||
|
||||
!sep->IsNumber(1)
|
||||
) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto spell_id = static_cast<uint16>(std::stoul(sep->arg[1]));
|
||||
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell ID {} is invalid or could not be found.",
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my_bot->DeleteBotSpellSetting(spell_id)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Failed to delete spell setting for {}.",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Successfully deleted spell setting for {}.",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell Setting Deleted | Spell: {} ({})",
|
||||
spells[spell_id].name,
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void bot_command_spell_settings_list(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_settings_list", sep->arg[0], "spellsettingslist")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {}",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_bot = ActionableBots::AsTarget_ByBot(c);
|
||||
if (!my_bot) {
|
||||
c->Message(Chat::White, "You must target a bot that you own to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my_bot->IsBotCaster() && !my_bot->IsBotHybrid()) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
my_bot->ListBotSpellSettings();
|
||||
}
|
||||
|
||||
void bot_command_spell_settings_toggle(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_settings_toggle", sep->arg[0], "spellsettingstoggle")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID] [Toggle]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_bot = ActionableBots::AsTarget_ByBot(c);
|
||||
if (!my_bot) {
|
||||
c->Message(Chat::White, "You must target a bot that you own to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my_bot->IsBotCaster() && !my_bot->IsBotHybrid()) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto arguments = sep->argnum;
|
||||
if (
|
||||
arguments < 2 ||
|
||||
!sep->IsNumber(1)
|
||||
) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID] [Toggle]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto spell_id = static_cast<uint16>(std::stoul(sep->arg[1]));
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell ID {} is invalid or could not be found.",
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
bool toggle = (
|
||||
sep->IsNumber(2) ?
|
||||
(std::stoi(sep->arg[2]) ? true : false) :
|
||||
atobool(sep->arg[2])
|
||||
);
|
||||
|
||||
auto obs = my_bot->GetBotSpellSetting(spell_id);
|
||||
if (!obs) {
|
||||
return;
|
||||
}
|
||||
|
||||
BotSpellSetting bs;
|
||||
|
||||
bs.priority = obs->priority;
|
||||
bs.min_level = obs->min_level;
|
||||
bs.max_level = obs->max_level;
|
||||
bs.min_hp = obs->min_hp;
|
||||
bs.max_hp = obs->max_hp;
|
||||
bs.is_enabled = toggle;
|
||||
|
||||
if (!my_bot->UpdateBotSpellSetting(spell_id, &bs)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Failed to {}able spell for {}.",
|
||||
toggle ? "en" : "dis",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Successfully {}abled spell for {}.",
|
||||
toggle ? "en" : "dis",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell {}abled | Spell: {} ({})",
|
||||
toggle ? "En" : "Dis",
|
||||
spells[spell_id].name,
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
void bot_command_spell_settings_update(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (helper_command_alias_fail(c, "bot_command_spell_settings_update", sep->arg[0], "spellsettingsupdate")) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto my_bot = ActionableBots::AsTarget_ByBot(c);
|
||||
if (!my_bot) {
|
||||
c->Message(Chat::White, "You must target a bot that you own to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!my_bot->IsBotCaster() && !my_bot->IsBotHybrid()) {
|
||||
c->Message(Chat::White, "You must target a Caster or Hybrid bot to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto arguments = sep->argnum;
|
||||
if (
|
||||
arguments < 6 ||
|
||||
!sep->IsNumber(1) ||
|
||||
!sep->IsNumber(2) ||
|
||||
!sep->IsNumber(3) ||
|
||||
!sep->IsNumber(4) ||
|
||||
!sep->IsNumber(5) ||
|
||||
!sep->IsNumber(6)
|
||||
) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Usage: {} [Spell ID] [Priority] [Min Level] [Max Level] [Min HP] [Max HP]",
|
||||
sep->arg[0]
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto spell_id = static_cast<uint16>(std::stoul(sep->arg[1]));
|
||||
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell ID {} is invalid or could not be found.",
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
auto priority = static_cast<int16>(std::stoi(sep->arg[2]));
|
||||
auto min_level = static_cast<uint8>(std::stoul(sep->arg[3]));
|
||||
auto max_level = static_cast<uint8>(std::stoul(sep->arg[4]));
|
||||
auto min_hp = static_cast<int8>(EQ::Clamp(std::stoi(sep->arg[5]), -1, 99));
|
||||
auto max_hp = static_cast<int8>(EQ::Clamp(std::stoi(sep->arg[6]), -1, 100));
|
||||
|
||||
BotSpellSetting bs;
|
||||
|
||||
bs.priority = priority;
|
||||
bs.min_level = min_level;
|
||||
bs.max_level = max_level;
|
||||
bs.min_hp = min_hp;
|
||||
bs.max_hp = max_hp;
|
||||
|
||||
if (!my_bot->UpdateBotSpellSetting(spell_id, &bs)) {
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Failed to update spell setting for {}.",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
my_bot->AI_AddBotSpells(my_bot->GetBotSpellID());
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Successfully updated spell setting for {}.",
|
||||
my_bot->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell Setting Updated | Spell: {} ({})",
|
||||
spells[spell_id].name,
|
||||
spell_id
|
||||
).c_str()
|
||||
);
|
||||
|
||||
c->Message(
|
||||
Chat::White,
|
||||
fmt::format(
|
||||
"Spell Setting Updated | Priority: {} Levels: {} Health: {}",
|
||||
priority,
|
||||
my_bot->GetLevelString(min_level, max_level),
|
||||
my_bot->GetHPString(min_hp, max_hp)
|
||||
).c_str()
|
||||
);
|
||||
}
|
||||
|
||||
#endif // BOTS
|
||||
|
||||
@ -526,7 +526,6 @@ typedef std::map<BCEnum::SpType, std::map<uint8, std::string>> bcst_required_bot
|
||||
typedef std::map<uint8, uint8> bcst_levels;
|
||||
typedef std::map<BCEnum::SpType, bcst_levels> bcst_levels_map;
|
||||
|
||||
|
||||
#define BOT_COMMAND_CHAR '^'
|
||||
|
||||
typedef void (*BotCmdFuncPtr)(Client *,const Seperator *);
|
||||
@ -540,8 +539,7 @@ typedef struct {
|
||||
extern int (*bot_command_dispatch)(Client *,char const*);
|
||||
extern int bot_command_count; // number of bot commands loaded
|
||||
|
||||
|
||||
// the bot command system:
|
||||
// Bot Command System:
|
||||
int bot_command_init(void);
|
||||
void bot_command_deinit(void);
|
||||
int bot_command_add(std::string bot_command_name, const char *desc, int access, BotCmdFuncPtr function);
|
||||
@ -549,8 +547,7 @@ int bot_command_not_avail(Client *c, const char *message);
|
||||
int bot_command_real_dispatch(Client *c, char const *message);
|
||||
void bot_command_log_command(Client *c, const char *message);
|
||||
|
||||
|
||||
// bot commands
|
||||
// Bot Commands
|
||||
void bot_command_actionable(Client *c, const Seperator *sep);
|
||||
void bot_command_aggressive(Client *c, const Seperator *sep);
|
||||
void bot_command_apply_poison(Client *c, const Seperator *sep);
|
||||
@ -589,6 +586,12 @@ void bot_command_resurrect(Client *c, const Seperator *sep);
|
||||
void bot_command_rune(Client *c, const Seperator *sep);
|
||||
void bot_command_send_home(Client *c, const Seperator *sep);
|
||||
void bot_command_size(Client *c, const Seperator *sep);
|
||||
void bot_command_spell_list(Client* c, const Seperator *sep);
|
||||
void bot_command_spell_settings_add(Client* c, const Seperator *sep);
|
||||
void bot_command_spell_settings_delete(Client* c, const Seperator *sep);
|
||||
void bot_command_spell_settings_list(Client* c, const Seperator *sep);
|
||||
void bot_command_spell_settings_toggle(Client* c, const Seperator *sep);
|
||||
void bot_command_spell_settings_update(Client* c, const Seperator *sep);
|
||||
void bot_command_summon_corpse(Client *c, const Seperator *sep);
|
||||
void bot_command_suspend(Client *c, const Seperator *sep);
|
||||
void bot_command_taunt(Client *c, const Seperator *sep);
|
||||
@ -596,8 +599,7 @@ void bot_command_track(Client *c, const Seperator *sep);
|
||||
void bot_command_view_combos(Client *c, const Seperator *sep);
|
||||
void bot_command_water_breathing(Client *c, const Seperator *sep);
|
||||
|
||||
|
||||
// bot subcommands
|
||||
// Bot Subcommands
|
||||
void bot_subcommand_bot_appearance(Client *c, const Seperator *sep);
|
||||
void bot_subcommand_bot_beard_color(Client *c, const Seperator *sep);
|
||||
void bot_subcommand_bot_beard_style(Client *c, const Seperator *sep);
|
||||
|
||||
@ -79,6 +79,15 @@ struct BotAA {
|
||||
uint8 total_levels;
|
||||
};
|
||||
|
||||
struct BotSpellSetting {
|
||||
int16 priority;
|
||||
uint8 min_level;
|
||||
uint8 max_level;
|
||||
int8 min_hp;
|
||||
int8 max_hp;
|
||||
bool is_enabled;
|
||||
};
|
||||
|
||||
#endif // BOTS
|
||||
|
||||
#endif // BOT_STRUCTS
|
||||
|
||||
@ -21,6 +21,8 @@
|
||||
#include "bot.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/strings.h"
|
||||
#include "../common/repositories/bot_spells_entries_repository.h"
|
||||
#include "../common/repositories/npc_spells_repository.h"
|
||||
|
||||
#if EQDEBUG >= 12
|
||||
#define BotAI_DEBUG_Spells 25
|
||||
@ -2877,16 +2879,16 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
return database.botdb.GetSpellCastingChance(spell_type_index, class_index, stance_index, type_index);
|
||||
}
|
||||
|
||||
bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
||||
bool Bot::AI_AddBotSpells(uint32 bot_spell_id) {
|
||||
// ok, this function should load the list, and the parent list then shove them into the struct and sort
|
||||
npc_spells_id = iDBSpellsID;
|
||||
npc_spells_id = bot_spell_id;
|
||||
AIBot_spells.clear();
|
||||
if (!iDBSpellsID) {
|
||||
if (!bot_spell_id) {
|
||||
AIautocastspell_timer->Disable();
|
||||
return false;
|
||||
}
|
||||
|
||||
auto* spell_list = content_db.GetBotSpells(iDBSpellsID);
|
||||
auto* spell_list = content_db.GetBotSpells(bot_spell_id);
|
||||
if (!spell_list) {
|
||||
AIautocastspell_timer->Disable();
|
||||
return false;
|
||||
@ -2897,7 +2899,7 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
||||
auto debug_msg = fmt::format(
|
||||
"Loading NPCSpells onto {}: dbspellsid={}, level={}",
|
||||
GetName(),
|
||||
iDBSpellsID,
|
||||
bot_spell_id,
|
||||
GetLevel()
|
||||
);
|
||||
|
||||
@ -2993,7 +2995,50 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp, e.bucket_name, e.bucket_value, e.bucket_comparison);
|
||||
|
||||
const auto& bs = GetBotSpellSetting(e.spellid);
|
||||
if (bs) {
|
||||
if (!bs->is_enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bs->min_level > 0 && GetLevel() < bs->min_level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bs->max_level > 0 && GetLevel() > bs->max_level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddSpellToBotList(
|
||||
bs->priority,
|
||||
e.spellid,
|
||||
e.type,
|
||||
e.manacost,
|
||||
e.recast_delay,
|
||||
e.resist_adjust,
|
||||
bs->min_hp,
|
||||
bs->max_hp,
|
||||
e.bucket_name,
|
||||
e.bucket_value,
|
||||
e.bucket_comparison
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
AddSpellToBotList(
|
||||
e.priority,
|
||||
e.spellid,
|
||||
e.type,
|
||||
e.manacost,
|
||||
e.recast_delay,
|
||||
e.resist_adjust,
|
||||
e.min_hp,
|
||||
e.max_hp,
|
||||
e.bucket_name,
|
||||
e.bucket_value,
|
||||
e.bucket_comparison
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3049,7 +3094,50 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp, e.bucket_name, e.bucket_value, e.bucket_comparison);
|
||||
|
||||
const auto& bs = GetBotSpellSetting(e.spellid);
|
||||
if (bs) {
|
||||
if (!bs->is_enabled) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bs->min_level > 0 && GetLevel() < bs->min_level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (bs->max_level > 0 && GetLevel() > bs->max_level) {
|
||||
continue;
|
||||
}
|
||||
|
||||
AddSpellToBotList(
|
||||
bs->priority,
|
||||
e.spellid,
|
||||
e.type,
|
||||
e.manacost,
|
||||
e.recast_delay,
|
||||
e.resist_adjust,
|
||||
bs->min_hp,
|
||||
bs->max_hp,
|
||||
e.bucket_name,
|
||||
e.bucket_value,
|
||||
e.bucket_comparison
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
AddSpellToBotList(
|
||||
e.priority,
|
||||
e.spellid,
|
||||
e.type,
|
||||
e.manacost,
|
||||
e.recast_delay,
|
||||
e.resist_adjust,
|
||||
e.min_hp,
|
||||
e.max_hp,
|
||||
e.bucket_name,
|
||||
e.bucket_value,
|
||||
e.bucket_comparison
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3108,108 +3196,91 @@ bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID) {
|
||||
return it != spell_list->entries.end();
|
||||
}
|
||||
|
||||
DBbotspells_Struct *ZoneDatabase::GetBotSpells(uint32 iDBSpellsID)
|
||||
DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id)
|
||||
{
|
||||
if (!iDBSpellsID) {
|
||||
if (!bot_spell_id) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto it = Bot_Spells_Cache.find(iDBSpellsID);
|
||||
|
||||
if (it != Bot_Spells_Cache.end()) { // it's in the cache, easy =)
|
||||
return &it->second;
|
||||
auto c = bot_spells_cache.find(bot_spell_id);
|
||||
if (c != bot_spells_cache.end()) { // it's in the cache, easy =)
|
||||
return &c->second;
|
||||
}
|
||||
|
||||
if (!Bot_Spells_LoadTried.count(iDBSpellsID)) { // no reason to ask the DB again if we have failed once already
|
||||
Bot_Spells_LoadTried.insert(iDBSpellsID);
|
||||
if (!bot_spells_loadtried.count(bot_spell_id)) { // no reason to ask the DB again if we have failed once already
|
||||
bot_spells_loadtried.insert(bot_spell_id);
|
||||
|
||||
auto query = fmt::format(
|
||||
"SELECT id, parent_list, attack_proc, proc_chance, "
|
||||
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
||||
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
||||
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
||||
"pursue_no_sp_recast_min, pursue_no_sp_recast_max, "
|
||||
"pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, "
|
||||
"idle_b_chance FROM npc_spells WHERE id = {}",
|
||||
iDBSpellsID
|
||||
);
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
auto n = NpcSpellsRepository::FindOne(content_db, bot_spell_id);
|
||||
if (!n.id) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
DBbotspells_Struct spell_set;
|
||||
|
||||
spell_set.parent_list = std::stoul(row[1]);
|
||||
spell_set.attack_proc = static_cast<uint16>(std::stoul(row[2]));
|
||||
spell_set.proc_chance = static_cast<uint8>(std::stoul(row[3]));
|
||||
spell_set.range_proc = static_cast<uint16>(std::stoul(row[4]));
|
||||
spell_set.rproc_chance = static_cast<int16>(std::stoi(row[5]));
|
||||
spell_set.defensive_proc = static_cast<uint16>(std::stoul(row[6]));
|
||||
spell_set.dproc_chance = static_cast<int16>(std::stoi(row[7]));
|
||||
spell_set.fail_recast = std::stoul(row[8]);
|
||||
spell_set.engaged_no_sp_recast_min = std::stoul(row[9]);
|
||||
spell_set.engaged_no_sp_recast_max = std::stoul(row[10]);
|
||||
spell_set.engaged_beneficial_self_chance = static_cast<uint8>(std::stoul(row[11]));
|
||||
spell_set.engaged_beneficial_other_chance = static_cast<uint8>(std::stoul(row[12]));
|
||||
spell_set.engaged_detrimental_chance = static_cast<uint8>(std::stoul(row[13]));
|
||||
spell_set.pursue_no_sp_recast_min = std::stoul(row[14]);
|
||||
spell_set.pursue_no_sp_recast_max = std::stoul(row[15]);
|
||||
spell_set.pursue_detrimental_chance = static_cast<uint8>(std::stoul(row[16]));
|
||||
spell_set.idle_no_sp_recast_min = std::stoul(row[17]);
|
||||
spell_set.idle_no_sp_recast_max = std::stoul(row[18]);
|
||||
spell_set.idle_beneficial_chance = static_cast<uint8>(std::stoul(row[19]));
|
||||
spell_set.parent_list = n.parent_list;
|
||||
spell_set.attack_proc = n.attack_proc;
|
||||
spell_set.proc_chance = n.proc_chance;
|
||||
spell_set.range_proc = n.range_proc;
|
||||
spell_set.rproc_chance = n.rproc_chance;
|
||||
spell_set.defensive_proc = n.defensive_proc;
|
||||
spell_set.dproc_chance = n.dproc_chance;
|
||||
spell_set.fail_recast = n.fail_recast;
|
||||
spell_set.engaged_no_sp_recast_min = n.engaged_no_sp_recast_min;
|
||||
spell_set.engaged_no_sp_recast_max = n.engaged_no_sp_recast_max;
|
||||
spell_set.engaged_beneficial_self_chance = n.engaged_b_self_chance;
|
||||
spell_set.engaged_beneficial_other_chance = n.engaged_b_other_chance;
|
||||
spell_set.engaged_detrimental_chance = n.engaged_d_chance;
|
||||
spell_set.pursue_no_sp_recast_min = n.pursue_no_sp_recast_min;
|
||||
spell_set.pursue_no_sp_recast_max = n.pursue_no_sp_recast_max;
|
||||
spell_set.pursue_detrimental_chance = n.pursue_d_chance;
|
||||
spell_set.idle_no_sp_recast_min = n.idle_no_sp_recast_min;
|
||||
spell_set.idle_no_sp_recast_max = n.idle_no_sp_recast_max;
|
||||
spell_set.idle_beneficial_chance = n.idle_b_chance;
|
||||
|
||||
// pulling fixed values from an auto-increment field is dangerous...
|
||||
query = fmt::format(
|
||||
"SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, min_hp, max_hp, resist_adjust, "
|
||||
"bucket_name, bucket_value, bucket_comparison "
|
||||
"FROM bot_spells_entries "
|
||||
"WHERE npc_spells_id = {} ORDER BY minlevel",
|
||||
iDBSpellsID
|
||||
auto bse = BotSpellsEntriesRepository::GetWhere(
|
||||
content_db,
|
||||
fmt::format(
|
||||
"npc_spells_id = {}",
|
||||
bot_spell_id
|
||||
)
|
||||
);
|
||||
results = QueryDatabase(query);
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
if (bse.empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto row : results) {
|
||||
for (const auto& e : bse) {
|
||||
DBbotspells_entries_Struct entry;
|
||||
auto spell_id = std::stoi(row[0]);
|
||||
entry.spellid = spell_id;
|
||||
entry.type = std::stoul(row[1]);
|
||||
entry.minlevel = static_cast<uint8>(std::stoul(row[2]));
|
||||
entry.maxlevel = static_cast<uint8>(std::stoul(row[3]));
|
||||
entry.manacost = static_cast<int16>(std::stoi(row[4]));
|
||||
entry.recast_delay = std::stoi(row[5]);
|
||||
entry.priority = static_cast<int16>(std::stoi(row[6]));
|
||||
entry.min_hp = static_cast<int8>(std::stoi(row[7]));
|
||||
entry.max_hp = static_cast<int8>(std::stoi(row[8]));
|
||||
entry.resist_adjust = static_cast<int16>(std::stoi(row[9]));
|
||||
entry.bucket_name = row[10];
|
||||
entry.bucket_value = row[11];
|
||||
entry.bucket_comparison = static_cast<uint8>(std::stoul(row[12]));
|
||||
entry.spellid = e.spellid;
|
||||
entry.type = e.type;
|
||||
entry.minlevel = e.minlevel;
|
||||
entry.maxlevel = e.maxlevel;
|
||||
entry.manacost = e.manacost;
|
||||
entry.recast_delay = e.recast_delay;
|
||||
entry.priority = e.priority;
|
||||
entry.min_hp = e.min_hp;
|
||||
entry.max_hp = e.max_hp;
|
||||
entry.resist_adjust = e.resist_adjust;
|
||||
entry.bucket_name = e.bucket_name;
|
||||
entry.bucket_value = e.bucket_value;
|
||||
entry.bucket_comparison = e.bucket_comparison;
|
||||
|
||||
// some spell types don't make much since to be priority 0, so fix that
|
||||
if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0) {
|
||||
entry.priority = 1;
|
||||
}
|
||||
|
||||
if (row[9]) {
|
||||
entry.resist_adjust = static_cast<int16>(std::stoi(row[9]));
|
||||
} else if (IsValidSpell(spell_id)) {
|
||||
entry.resist_adjust = spells[spell_id].resist_difficulty;
|
||||
if (e.resist_adjust) {
|
||||
entry.resist_adjust = e.resist_adjust;
|
||||
} else if (IsValidSpell(e.spellid)) {
|
||||
entry.resist_adjust = spells[e.spellid].resist_difficulty;
|
||||
}
|
||||
|
||||
spell_set.entries.push_back(entry);
|
||||
}
|
||||
|
||||
Bot_Spells_Cache.insert(std::make_pair(iDBSpellsID, spell_set));
|
||||
bot_spells_cache.insert(std::make_pair(bot_spell_id, spell_set));
|
||||
|
||||
return &Bot_Spells_Cache[iDBSpellsID];
|
||||
return &bot_spells_cache[bot_spell_id];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
@ -109,6 +109,26 @@ void Lua_Bot::SetExpansionBitmask(int expansion_bitmask, bool save) {
|
||||
self->SetExpansionBitmask(expansion_bitmask, save);
|
||||
}
|
||||
|
||||
bool Lua_Bot::ReloadBotDataBuckets() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->GetBotDataBuckets();
|
||||
}
|
||||
|
||||
bool Lua_Bot::ReloadBotOwnerDataBuckets() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->GetBotOwnerDataBuckets();
|
||||
}
|
||||
|
||||
bool Lua_Bot::ReloadBotSpells() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->AI_AddBotSpells(self->GetBotSpellID());
|
||||
}
|
||||
|
||||
void Lua_Bot::ReloadBotSpellSettings() {
|
||||
Lua_Safe_Call_Void();
|
||||
self->LoadBotSpellSettings();
|
||||
}
|
||||
|
||||
bool Lua_Bot::HasBotSpellEntry(uint16 spellid) {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->HasBotSpellEntry(spellid);
|
||||
@ -117,27 +137,31 @@ bool Lua_Bot::HasBotSpellEntry(uint16 spellid) {
|
||||
luabind::scope lua_register_bot() {
|
||||
return luabind::class_<Lua_Bot, Lua_Mob>("Bot")
|
||||
.def(luabind::constructor<>())
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16, bool)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16, bool, uint32)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16, bool, uint32, uint32)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16, bool, uint32, uint32, uint32)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16, bool, uint32, uint32, uint32, uint32)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16, bool, uint32, uint32, uint32, uint32, uint32)) & Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16, uint32, int16, bool, uint32, uint32, uint32, uint32, uint32, uint32)) & Lua_Bot::AddBotItem)
|
||||
.def("CountBotItem", (uint32(Lua_Bot::*)(uint32)) & Lua_Bot::CountBotItem)
|
||||
.def("GetBotItem", (Lua_ItemInst(Lua_Bot::*)(uint16)) & Lua_Bot::GetBotItem)
|
||||
.def("GetBotItemIDBySlot", (uint32(Lua_Bot::*)(uint16)) & Lua_Bot::GetBotItemIDBySlot)
|
||||
.def("GetExpansionBitmask", (int(Lua_Bot::*)(void)) & Lua_Bot::GetExpansionBitmask)
|
||||
.def("GetOwner", (Lua_Mob(Lua_Bot::*)(void)) & Lua_Bot::GetOwner)
|
||||
.def("HasBotItem", (bool(Lua_Bot::*)(uint32)) & Lua_Bot::HasBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16,bool))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16,bool,uint32))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16,bool,uint32,uint32))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16,bool,uint32,uint32,uint32))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16,bool,uint32,uint32,uint32,uint32))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16,bool,uint32,uint32,uint32,uint32,uint32))&Lua_Bot::AddBotItem)
|
||||
.def("AddBotItem", (void(Lua_Bot::*)(uint16,uint32,int16,bool,uint32,uint32,uint32,uint32,uint32,uint32))&Lua_Bot::AddBotItem)
|
||||
.def("CountBotItem", (uint32(Lua_Bot::*)(uint32))&Lua_Bot::CountBotItem)
|
||||
.def("GetBotItem", (Lua_ItemInst(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItem)
|
||||
.def("GetBotItemIDBySlot", (uint32(Lua_Bot::*)(uint16))&Lua_Bot::GetBotItemIDBySlot)
|
||||
.def("GetExpansionBitmask", (int(Lua_Bot::*)(void))&Lua_Bot::GetExpansionBitmask)
|
||||
.def("GetOwner", (Lua_Mob(Lua_Bot::*)(void))&Lua_Bot::GetOwner)
|
||||
.def("HasBotItem", (bool(Lua_Bot::*)(uint32))&Lua_Bot::HasBotItem)
|
||||
.def("HasBotSpellEntry", (bool(Lua_Bot::*)(uint16)) & Lua_Bot::HasBotSpellEntry)
|
||||
.def("OwnerMessage", (void(Lua_Bot::*)(std::string)) & Lua_Bot::OwnerMessage)
|
||||
.def("RemoveBotItem", (void(Lua_Bot::*)(uint32)) & Lua_Bot::RemoveBotItem)
|
||||
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int)) & Lua_Bot::SetExpansionBitmask)
|
||||
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int, bool)) & Lua_Bot::SetExpansionBitmask)
|
||||
.def("SignalBot", (void(Lua_Bot::*)(int)) & Lua_Bot::SignalBot);
|
||||
.def("OwnerMessage", (void(Lua_Bot::*)(std::string))&Lua_Bot::OwnerMessage)
|
||||
.def("ReloadBotDataBuckets", (bool(Lua_Bot::*)(void))&Lua_Bot::ReloadBotDataBuckets)
|
||||
.def("ReloadBotOwnerDataBuckets", (bool(Lua_Bot::*)(void))&Lua_Bot::ReloadBotOwnerDataBuckets)
|
||||
.def("ReloadBotSpells", (bool(Lua_Bot::*)(void))&Lua_Bot::ReloadBotSpells)
|
||||
.def("ReloadBotSpellSettings", (void(Lua_Bot::*)(void))&Lua_Bot::ReloadBotSpellSettings)
|
||||
.def("RemoveBotItem", (void(Lua_Bot::*)(uint32))&Lua_Bot::RemoveBotItem)
|
||||
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask)
|
||||
.def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask)
|
||||
.def("SignalBot", (void(Lua_Bot::*)(int))&Lua_Bot::SignalBot);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@ -43,6 +43,10 @@ public:
|
||||
Lua_Mob GetOwner();
|
||||
bool HasBotItem(uint32 item_id);
|
||||
void OwnerMessage(std::string message);
|
||||
bool ReloadBotDataBuckets();
|
||||
bool ReloadBotOwnerDataBuckets();
|
||||
bool ReloadBotSpells();
|
||||
void ReloadBotSpellSettings();
|
||||
void RemoveBotItem(uint32 item_id);
|
||||
void SetExpansionBitmask(int expansion_bitmask);
|
||||
void SetExpansionBitmask(int expansion_bitmask, bool save);
|
||||
|
||||
@ -106,6 +106,26 @@ void Perl_Bot_SetExpansionBitmask(Bot* self, int expansion_bitmask, bool save)
|
||||
self->SetExpansionBitmask(expansion_bitmask, save);
|
||||
}
|
||||
|
||||
bool Perl_Bot_ReloadBotDataBuckets(Bot* self)
|
||||
{
|
||||
return self->GetBotDataBuckets();
|
||||
}
|
||||
|
||||
bool Perl_Bot_ReloadBotOwnerDataBuckets(Bot* self)
|
||||
{
|
||||
return self->GetBotOwnerDataBuckets();
|
||||
}
|
||||
|
||||
bool Perl_Bot_ReloadBotSpells(Bot* self)
|
||||
{
|
||||
return self->AI_AddBotSpells(self->GetBotSpellID());
|
||||
}
|
||||
|
||||
void Perl_Bot_ReloadBotSpellSettings(Bot* self)
|
||||
{
|
||||
self->LoadBotSpellSettings();
|
||||
}
|
||||
|
||||
bool Perl_Bot_HasBotSpellEntry(Bot* self, uint16 spellid)
|
||||
{
|
||||
return self->HasBotSpellEntry(spellid);
|
||||
@ -134,6 +154,10 @@ void perl_register_bot()
|
||||
package.add("HasBotItem", &Perl_Bot_HasBotItem);
|
||||
package.add("HasBotSpellEntry", &Perl_Bot_HasBotSpellEntry);
|
||||
package.add("OwnerMessage", &Perl_Bot_OwnerMessage);
|
||||
package.add("ReloadBotDataBuckets", &Perl_Bot_ReloadBotDataBuckets);
|
||||
package.add("ReloadBotOwnerDataBuckets", &Perl_Bot_ReloadBotOwnerDataBuckets);
|
||||
package.add("ReloadBotSpells", &Perl_Bot_ReloadBotSpells);
|
||||
package.add("ReloadBotSpellSettings", &Perl_Bot_ReloadBotSpellSettings);
|
||||
package.add("RemoveBotItem", &Perl_Bot_RemoveBotItem);
|
||||
package.add("SetExpansionBitmask", (void(*)(Bot*, int))&Perl_Bot_SetExpansionBitmask);
|
||||
package.add("SetExpansionBitmask", (void(*)(Bot*, int, bool))&Perl_Bot_SetExpansionBitmask);
|
||||
|
||||
@ -533,8 +533,8 @@ public:
|
||||
const NPCType* LoadNPCTypesData(uint32 id, bool bulk_load = false);
|
||||
|
||||
/*Bots */
|
||||
DBbotspells_Struct* GetBotSpells(uint32 iDBSpellsID);
|
||||
void ClearBotSpells() { Bot_Spells_Cache.clear(); Bot_Spells_LoadTried.clear(); }
|
||||
DBbotspells_Struct* GetBotSpells(uint32 bot_spell_id);
|
||||
void ClearBotSpells() { bot_spells_cache.clear(); bot_spells_loadtried.clear(); }
|
||||
|
||||
/* Mercs */
|
||||
const NPCType* GetMercType(uint32 id, uint16 raceid, uint32 clientlevel);
|
||||
@ -637,8 +637,9 @@ protected:
|
||||
std::unordered_set<uint32> npc_spells_loadtried;
|
||||
DBnpcspellseffects_Struct** npc_spellseffects_cache;
|
||||
bool* npc_spellseffects_loadtried;
|
||||
std::unordered_map<uint32, DBbotspells_Struct> Bot_Spells_Cache;
|
||||
std::unordered_set<uint32> Bot_Spells_LoadTried;
|
||||
std::unordered_map<uint32, DBbotspells_Struct> bot_spells_cache;
|
||||
std::unordered_set<uint32> bot_spells_loadtried;
|
||||
|
||||
};
|
||||
|
||||
extern ZoneDatabase database;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user