[Bots] Add Quest API Support for Limits. (#2522)

* [Bots] Add Quest API Support for Limits.

# Perl
- Add `$client->GetBotCreationLimit()` to Perl.
- Add `$client->GetBotCreationLimit(class_id)` to Perl.
- Add `$client->GetBotRequiredLevel()` to Perl.
- Add `$client->GetBotRequiredLevel(class_id)` to Perl.
- Add `$client->GetBotSpawnLimit()` to Perl.
- Add `$client->GetBotSpawnLimit(class_id)` to Perl.
- Add `$client->SetBotCreationLimit(creation_limit)` to Perl.
- Add `$client->SetBotCreationLimit(creation_limit, class_id)` to Perl.
- Add `$client->SetBotRequiredLevel(required_level)` to Perl.
- Add `$client->SetBotRequiredLevel(required_level, class_id)` to Perl.
- Add `$client->SetBotSpawnLimit(spawn_limit)` to Perl.
- Add `$client->SetBotSpawnLimit(spawn_limit, class_id)` to Perl.
- Add `$entity_list->GetBotListByCharacterID(character_id, class_id)` to Perl.

# Lua
- Add `client:GetBotCreationLimit()` to Lua.
- Add `client:GetBotCreationLimit(class_id)` to Lua.
- Add `client:GetBotRequiredLevel()` to Lua.
- Add `client:GetBotRequiredLevel(class_id)` to Lua.
- Add `client:GetBotSpawnLimit()` to Lua.
- Add `client:GetBotSpawnLimit(class_id)` to Lua.
- Add `client:SetBotCreationLimit(creation_limit)` to Lua.
- Add `client:SetBotCreationLimit(creation_limit, class_id)` to Lua.
- Add `client:SetBotRequiredLevel(required_level)` to Lua.
- Add `client:SetBotRequiredLevel(required_level, class_id)` to Lua.
- Add `client:SetBotSpawnLimit(spawn_limit)` to Lua.
- Add `client:SetBotSpawnLimit(spawn_limit, class_id)` to Lua.
- Add `entity_list:GetBotListByCharacterID(character_id, class_id)` to Lua.

# Notes
- Allows operators to set creation and spawn limits based on class, as well as required level.
- Using the class-inspecific methods sets the global limit or required level.
- Global limits are checked prior to class-specific limits and if they are not met, creation or spawn is disallowed.
- Modified preexisting Quest API to make use of this new stuff under the hood.

* Update bot_command.cpp

* Add client bot file.
This commit is contained in:
Kinglykrab 2022-11-16 19:51:13 -05:00 committed by GitHub
parent ce4d96dc91
commit 7ea77ee027
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1264 additions and 289 deletions

View File

@ -16,6 +16,7 @@ SET(zone_sources
botspellsai.cpp
cheat_manager.cpp
client.cpp
client_bot.cpp
client_mods.cpp
client_packet.cpp
client_process.cpp

View File

@ -172,10 +172,25 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
SetRangerAutoWeaponSelect(false);
bool stance_flag = false;
if (!database.botdb.LoadStance(this, stance_flag) && bot_owner)
bot_owner->Message(Chat::White, "%s for '%s'", BotDatabase::fail::LoadStance(), GetCleanName());
if (!stance_flag && bot_owner)
bot_owner->Message(Chat::White, "Could not locate stance for '%s'", GetCleanName());
if (!database.botdb.LoadStance(this, stance_flag) && bot_owner) {
bot_owner->Message(
Chat::White,
fmt::format(
"Failed to load stance for '{}'.",
GetCleanName()
).c_str()
);
}
if (!stance_flag && bot_owner) {
bot_owner->Message(
Chat::White,
fmt::format(
"Could not locate stance for '{}'.",
GetCleanName()
).c_str()
);
}
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == EQ::constants::stanceAggressive));
SetPauseAI(false);
@ -1812,8 +1827,16 @@ bool Bot::Save()
bot_owner->Message(Chat::White, "%s for '%s'", BotDatabase::fail::SaveBuffs(), GetCleanName());
if (!database.botdb.SaveTimers(this))
bot_owner->Message(Chat::White, "%s for '%s'", BotDatabase::fail::SaveTimers(), GetCleanName());
if (!database.botdb.SaveStance(this))
bot_owner->Message(Chat::White, "%s for '%s'", BotDatabase::fail::SaveStance(), GetCleanName());
if (!database.botdb.SaveStance(this)) {
bot_owner->Message(
Chat::White,
fmt::format(
"Failed to save stance for '{}'.",
GetCleanName()
).c_str()
);
}
if (!SavePet())
bot_owner->Message(Chat::White, "Failed to save pet for '%s'", GetCleanName());
@ -4200,13 +4223,15 @@ bool Bot::GroupHasBot(Group* group) {
return Result;
}
uint32 Bot::SpawnedBotCount(uint32 botOwnerCharacterID) {
uint32 Result = 0;
if(botOwnerCharacterID > 0) {
std::list<Bot*> SpawnedBots = entity_list.GetBotsByBotOwnerCharacterID(botOwnerCharacterID);
Result = SpawnedBots.size();
uint32 Bot::SpawnedBotCount(const uint32 owner_id, uint8 class_id) {
uint32 spawned_bot_count = 0;
if (owner_id) {
const auto& sbl = entity_list.GetBotListByCharacterID(owner_id, class_id);
spawned_bot_count = sbl.size();
}
return Result;
return spawned_bot_count;
}
void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) {

View File

@ -375,7 +375,7 @@ public:
// Static Class Methods
//static void DestroyBotRaidObjects(Client* client); // Can be removed after bot raids are dumped
static Bot* LoadBot(uint32 botID);
static uint32 SpawnedBotCount(uint32 botOwnerCharacterID);
static uint32 SpawnedBotCount(const uint32 owner_id, uint8 class_id = 0);
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
//static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* error_message);
static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined);

File diff suppressed because it is too large Load Diff

View File

@ -189,38 +189,46 @@ bool BotDatabase::QueryNameAvailablity(const std::string& bot_name, bool& availa
return true;
}
bool BotDatabase::QueryBotCount(const uint32 owner_id, uint32& bot_count)
bool BotDatabase::QueryBotCount(const uint32 owner_id, int class_id, uint32& bot_count, uint32& bot_class_count)
{
if (!owner_id)
if (!owner_id) {
return false;
}
query = StringFormat("SELECT COUNT(`bot_id`) FROM `bot_data` WHERE `owner_id` = '%i'", owner_id);
query = fmt::format(
"SELECT COUNT(`bot_id`) FROM `bot_data` WHERE `owner_id` = {}",
owner_id
);
auto results = database.QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
if (!results.RowCount())
}
if (!results.RowCount()) {
return true;
}
auto row = results.begin();
bot_count = atoi(row[0]);
bot_count = std::stoul(row[0]);
return true;
}
if (EQ::ValueWithin(class_id, WARRIOR, BERSERKER)) {
query = fmt::format(
"SELECT COUNT(`bot_id`) FROM `bot_data` WHERE `owner_id` = {} AND `class` = {}",
owner_id,
class_id
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
bool BotDatabase::LoadQuestableSpawnCount(const uint32 owner_id, int& spawn_count)
{
if (!owner_id)
return false;
if (!results.RowCount()) {
return true;
}
query = StringFormat("SELECT `value` FROM `quest_globals` WHERE `name` = 'bot_spawn_limit' AND `charid` = '%i' LIMIT 1", owner_id);
auto results = database.QueryDatabase(query); // use 'database' for non-bot table calls
if (!results.Success())
return false;
if (!results.RowCount())
return true;
auto row = results.begin();
spawn_count = atoi(row[0]);
auto row = results.begin();
bot_class_count = std::stoul(row[0]);
}
return true;
}
@ -310,21 +318,55 @@ bool BotDatabase::LoadOwnerID(const uint32 bot_id, uint32& owner_id)
bool BotDatabase::LoadBotID(const uint32 owner_id, const std::string& bot_name, uint32& bot_id)
{
if (!owner_id || bot_name.empty())
if (!owner_id || bot_name.empty()) {
return false;
}
query = StringFormat(
"SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u' AND `name` = '%s' LIMIT 1",
owner_id, bot_name.c_str()
query = fmt::format(
"SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = {} AND `name` = '{}' LIMIT 1",
owner_id,
bot_name
);
auto results = database.QueryDatabase(query);
if (!results.Success())
if (!results.Success()) {
return false;
if (!results.RowCount())
}
if (!results.RowCount()) {
return true;
}
auto row = results.begin();
bot_id = atoi(row[0]);
bot_id = std::stoul(row[0]);
return true;
}
bool BotDatabase::LoadBotID(const uint32 owner_id, const std::string& bot_name, uint32& bot_id, uint8& bot_class_id)
{
if (!owner_id || bot_name.empty()) {
return false;
}
query = fmt::format(
"SELECT `bot_id`, `class` FROM `bot_data` WHERE `owner_id` = {} AND `name` = '{}' LIMIT 1",
owner_id,
bot_name
);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
if (!results.RowCount()) {
return true;
}
auto row = results.begin();
bot_id = std::stoul(row[0]);
bot_class_id = static_cast<uint8>(std::stoul(row[1]));
return true;
}
@ -3211,8 +3253,6 @@ bool BotDatabase::SaveExpansionBitmask(const uint32 bot_id, const int expansion_
}
/* fail::Bot functions */
const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; }
const char* BotDatabase::fail::QueryBotCount() { return "Failed to query bot count"; }
const char* BotDatabase::fail::LoadBotsList() { return "Failed to bots list"; }
const char* BotDatabase::fail::LoadOwnerID() { return "Failed to load owner ID"; }
const char* BotDatabase::fail::LoadBotID() { return "Failed to load bot ID"; }
@ -3223,8 +3263,6 @@ const char* BotDatabase::fail::DeleteBot() { return "Failed to delete bot"; }
const char* BotDatabase::fail::LoadBuffs() { return "Failed to load buffs"; }
const char* BotDatabase::fail::SaveBuffs() { return "Failed to save buffs"; }
const char* BotDatabase::fail::DeleteBuffs() { return "Failed to delete buffs"; }
const char* BotDatabase::fail::LoadStance() { return "Failed to load stance"; }
const char* BotDatabase::fail::SaveStance() { return "Failed to save stance"; }
const char* BotDatabase::fail::DeleteStance() { return "Failed to delete stance"; }
const char* BotDatabase::fail::LoadTimers() { return "Failed to load timers"; }
const char* BotDatabase::fail::SaveTimers() { return "Failed to save timers"; }
@ -3271,8 +3309,6 @@ const char* BotDatabase::fail::ToggleHelmAppearance() { return "Failed to save t
const char* BotDatabase::fail::ToggleAllHelmAppearances() { return "Failed to save toggle all helm appearance"; }
const char* BotDatabase::fail::SaveFollowDistance() { return "Failed to save follow distance"; }
const char* BotDatabase::fail::SaveAllFollowDistances() { return "Failed to save all follow distances"; }
const char* BotDatabase::fail::CreateCloneBot() { return "Failed to create clone bot"; }
const char* BotDatabase::fail::CreateCloneBotInventory() { return "Failed to create clone bot inventory"; }
const char* BotDatabase::fail::SaveStopMeleeLevel() { return "Failed to save stop melee level"; }
/* fail::Bot heal rotation functions */

View File

@ -50,13 +50,13 @@ public:
/* Bot functions */
bool QueryNameAvailablity(const std::string& bot_name, bool& available_flag);
bool QueryBotCount(const uint32 owner_id, uint32& bot_count);
bool LoadQuestableSpawnCount(const uint32 owner_id, int& spawn_count);
bool QueryBotCount(const uint32 owner_id, int class_id, uint32& bot_count, uint32& bot_class_count);
bool LoadBotsList(const uint32 owner_id, std::list<BotsAvailableList>& bots_list, bool ByAccount = false);
bool LoadOwnerID(const std::string& bot_name, uint32& owner_id);
bool LoadOwnerID(const uint32 bot_id, uint32& owner_id);
bool LoadBotID(const uint32 owner_id, const std::string& bot_name, uint32& bot_id);
bool LoadBotID(const uint32 owner_id, const std::string& bot_name, uint32& bot_id, uint8& bot_class_id);
bool LoadBot(const uint32 bot_id, Bot*& loaded_bot);
bool SaveNewBot(Bot* bot_inst, uint32& bot_id);
@ -196,9 +196,6 @@ public:
class fail {
public:
/* fail::Bot functions */
static const char* QueryNameAvailablity();
static const char* QueryBotCount();
static const char* LoadQuestableSpawnCount();
static const char* LoadBotsList();
static const char* LoadOwnerID();
static const char* LoadBotID();
@ -209,8 +206,6 @@ public:
static const char* LoadBuffs();
static const char* SaveBuffs();
static const char* DeleteBuffs();
static const char* LoadStance();
static const char* SaveStance();
static const char* DeleteStance();
static const char* LoadTimers();
static const char* SaveTimers();
@ -257,8 +252,6 @@ public:
static const char* ToggleAllHelmAppearances();
static const char* SaveFollowDistance();
static const char* SaveAllFollowDistances();
static const char* CreateCloneBot();
static const char* CreateCloneBotInventory();
static const char* SaveStopMeleeLevel();
/* fail::Bot bot-group functions */

View File

@ -9558,26 +9558,6 @@ void Client::SetLastPositionBeforeBulkUpdate(glm::vec4 in_last_position_before_b
Client::last_position_before_bulk_update = in_last_position_before_bulk_update;
}
#ifdef BOTS
bool Client::GetBotOption(BotOwnerOption boo) const {
if (boo < _booCount) {
return bot_owner_options[boo];
}
return false;
}
void Client::SetBotOption(BotOwnerOption boo, bool flag) {
if (boo < _booCount) {
bot_owner_options[boo] = flag;
}
}
#endif
void Client::SendToGuildHall()
{
std::string zone_short_name = "guildhall";

View File

@ -2031,6 +2031,13 @@ public:
bool GetBotPrecombat() { return m_bot_precombat; }
void SetBotPrecombat(bool flag = true) { m_bot_precombat = flag; }
int GetBotRequiredLevel(uint8 class_id = 0);
uint32 GetBotCreationLimit(uint8 class_id = 0);
int GetBotSpawnLimit(uint8 class_id = 0);
void SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id = 0);
void SetBotRequiredLevel(int new_required_level, uint8 class_id = 0);
void SetBotSpawnLimit(int new_spawn_limit, uint8 class_id = 0);
private:
bool bot_owner_options[_booCount];
bool m_bot_pulling;

159
zone/client_bot.cpp Normal file
View File

@ -0,0 +1,159 @@
#ifdef BOTS
#include "client.h"
bool Client::GetBotOption(BotOwnerOption boo) const {
if (boo < _booCount) {
return bot_owner_options[boo];
}
return false;
}
void Client::SetBotOption(BotOwnerOption boo, bool flag) {
if (boo < _booCount) {
bot_owner_options[boo] = flag;
}
}
uint32 Client::GetBotCreationLimit(uint8 class_id)
{
uint32 bot_creation_limit = RuleI(Bots, CreationLimit);
const auto bucket_name = fmt::format(
"bot_creation_limit{}",
(
class_id && IsPlayerClass(class_id) ?
fmt::format(
"_{}",
Strings::ToLower(GetClassIDName(class_id))
) :
""
)
);
auto bucket_value = GetBucket(bucket_name);
if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) {
bot_creation_limit = std::stoul(bucket_value);
}
return bot_creation_limit;
}
int Client::GetBotRequiredLevel(uint8 class_id)
{
int bot_character_level = RuleI(Bots, BotCharacterLevel);
const auto bucket_name = fmt::format(
"bot_required_level{}",
(
class_id && IsPlayerClass(class_id) ?
fmt::format(
"_{}",
Strings::ToLower(GetClassIDName(class_id))
) :
""
)
);
auto bucket_value = GetBucket(bucket_name);
if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) {
bot_character_level = std::stoi(bucket_value);
}
return bot_character_level;
}
int Client::GetBotSpawnLimit(uint8 class_id)
{
int bot_spawn_limit = RuleI(Bots, SpawnLimit);
const auto bucket_name = fmt::format(
"bot_spawn_limit{}",
(
class_id && IsPlayerClass(class_id) ?
fmt::format(
"_{}",
Strings::ToLower(GetClassIDName(class_id))
) :
""
)
);
auto bucket_value = GetBucket(bucket_name);
if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) {
bot_spawn_limit = std::stoi(bucket_value);
return bot_spawn_limit;
}
if (RuleB(Bots, QuestableSpawnLimit)) {
const auto query = fmt::format(
"SELECT `value` FROM `quest_globals` WHERE `name` = '{}' AND `charid` = {} LIMIT 1",
bucket_name,
CharacterID()
);
auto results = database.QueryDatabase(query); // use 'database' for non-bot table calls
if (!results.Success() || !results.RowCount()) {
return bot_spawn_limit;
}
auto row = results.begin();
bot_spawn_limit = std::stoi(row[0]);
}
return bot_spawn_limit;
}
void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id)
{
const auto bucket_name = fmt::format(
"bot_creation_limit{}",
(
class_id && IsPlayerClass(class_id) ?
fmt::format(
"_{}",
Strings::ToLower(GetClassIDName(class_id))
) :
""
)
);
SetBucket(bucket_name, std::to_string(new_creation_limit));
}
void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id)
{
const auto bucket_name = fmt::format(
"bot_required_level{}",
(
class_id && IsPlayerClass(class_id) ?
fmt::format(
"_{}",
Strings::ToLower(GetClassIDName(class_id))
) :
""
)
);
SetBucket(bucket_name, std::to_string(new_required_level));
}
void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id)
{
const auto bucket_name = fmt::format(
"bot_spawn_limit{}",
(
class_id && IsPlayerClass(class_id) ?
fmt::format(
"_{}",
Strings::ToLower(GetClassIDName(class_id))
) :
""
)
);
SetBucket(bucket_name, std::to_string(new_spawn_limit));
}
#endif

View File

@ -1065,11 +1065,21 @@ int Perl__createbotcount()
return quest_manager.createbotcount();
}
int Perl__createbotcount(uint8 class_id)
{
return quest_manager.createbotcount(class_id);
}
int Perl__spawnbotcount()
{
return quest_manager.spawnbotcount();
}
int Perl__spawnbotcount(uint8 class_id)
{
return quest_manager.spawnbotcount(class_id);
}
bool Perl__botquest()
{
return quest_manager.botquest();
@ -3847,8 +3857,10 @@ void perl_register_quest()
#ifdef BOTS
package.add("botquest", &Perl__botquest);
package.add("spawnbotcount", &Perl__spawnbotcount);
package.add("createbotcount", &Perl__createbotcount);
package.add("spawnbotcount", (int(*)())&Perl__spawnbotcount);
package.add("spawnbotcount", (int(*)(uint8))&Perl__spawnbotcount);
package.add("createbotcount", (int(*)())&Perl__createbotcount);
package.add("createbotcount", (int(*)(uint8))&Perl__createbotcount);
package.add("createBot", &Perl__createBot);
#endif //BOTS

View File

@ -5164,7 +5164,7 @@ void EntityList::GetBotList(std::list<Bot *> &b_list)
}
}
std::vector<Bot *> EntityList::GetBotListByCharacterID(uint32 character_id)
std::vector<Bot *> EntityList::GetBotListByCharacterID(uint32 character_id, uint8 class_id)
{
std::vector<Bot *> client_bot_list;
@ -5173,7 +5173,14 @@ std::vector<Bot *> EntityList::GetBotListByCharacterID(uint32 character_id)
}
for (auto bot : bot_list) {
if (bot->GetOwner() && bot->GetBotOwnerCharacterID() == character_id) {
if (
bot->GetOwner() &&
bot->GetBotOwnerCharacterID() == character_id &&
(
!class_id ||
bot->GetClass() == class_id
)
) {
client_bot_list.push_back(bot);
}
}

View File

@ -535,7 +535,7 @@ public:
inline const std::unordered_map<uint16, Client *> &GetClientList() { return client_list; }
#ifdef BOTS
inline const std::list<Bot *> &GetBotList() { return bot_list; }
std::vector<Bot *> GetBotListByCharacterID(uint32 character_id);
std::vector<Bot *> GetBotListByCharacterID(uint32 character_id, uint8 class_id = 0);
std::vector<Bot *> GetBotListByClientName(std::string client_name);
#endif
inline const std::unordered_map<uint16, Corpse *> &GetCorpseList() { return corpse_list; }

View File

@ -2613,6 +2613,82 @@ void Lua_Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in
self->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message);
}
#ifdef BOTS
int Lua_Client::GetBotRequiredLevel()
{
Lua_Safe_Call_Int();
return self->GetBotRequiredLevel();
}
int Lua_Client::GetBotRequiredLevel(uint8 class_id)
{
Lua_Safe_Call_Int();
return self->GetBotRequiredLevel(class_id);
}
uint32 Lua_Client::GetBotCreationLimit()
{
Lua_Safe_Call_Int();
return self->GetBotCreationLimit();
}
uint32 Lua_Client::GetBotCreationLimit(uint8 class_id)
{
Lua_Safe_Call_Int();
return self->GetBotCreationLimit(class_id);
}
int Lua_Client::GetBotSpawnLimit()
{
Lua_Safe_Call_Int();
return self->GetBotSpawnLimit();
}
int Lua_Client::GetBotSpawnLimit(uint8 class_id)
{
Lua_Safe_Call_Int();
return self->GetBotSpawnLimit(class_id);
}
void Lua_Client::SetBotRequiredLevel(int new_required_level)
{
Lua_Safe_Call_Void();
self->SetBotRequiredLevel(new_required_level);
}
void Lua_Client::SetBotRequiredLevel(int new_required_level, uint8 class_id)
{
Lua_Safe_Call_Void();
self->SetBotRequiredLevel(new_required_level, class_id);
}
void Lua_Client::SetBotCreationLimit(uint32 new_creation_limit)
{
Lua_Safe_Call_Void();
self->SetBotCreationLimit(new_creation_limit);
}
void Lua_Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id)
{
Lua_Safe_Call_Void();
self->SetBotCreationLimit(new_creation_limit, class_id);
}
void Lua_Client::SetBotSpawnLimit(int new_spawn_limit)
{
Lua_Safe_Call_Void();
self->SetBotSpawnLimit(new_spawn_limit);
}
void Lua_Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id)
{
Lua_Safe_Call_Void();
self->SetBotSpawnLimit(new_spawn_limit, class_id);
}
#endif
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@ -2733,6 +2809,18 @@ luabind::scope lua_register_client() {
.def("GetBindZ", (float(Lua_Client::*)(void))&Lua_Client::GetBindZ)
.def("GetBindZoneID", (uint32(Lua_Client::*)(int))&Lua_Client::GetBindZoneID)
.def("GetBindZoneID", (uint32(Lua_Client::*)(void))&Lua_Client::GetBindZoneID)
#ifdef BOTS
.def("GetBotCreationLimit", (uint32(Lua_Client::*)(void))&Lua_Client::GetBotCreationLimit)
.def("GetBotCreationLimit", (uint32(Lua_Client::*)(uint8))&Lua_Client::GetBotCreationLimit)
.def("GetBotRequiredLevel", (int(Lua_Client::*)(void))&Lua_Client::GetBotRequiredLevel)
.def("GetBotRequiredLevel", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotRequiredLevel)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(void))&Lua_Client::GetBotSpawnLimit)
.def("GetBotSpawnLimit", (int(Lua_Client::*)(uint8))&Lua_Client::GetBotSpawnLimit)
#endif
.def("GetCarriedMoney", (uint64(Lua_Client::*)(void))&Lua_Client::GetCarriedMoney)
.def("GetCarriedPlatinum", (uint32(Lua_Client::*)(void))&Lua_Client::GetCarriedPlatinum)
.def("GetCharacterFactionLevel", (int(Lua_Client::*)(int))&Lua_Client::GetCharacterFactionLevel)
@ -2968,6 +3056,18 @@ luabind::scope lua_register_client() {
.def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float))&Lua_Client::SetBindPoint)
.def("SetBindPoint", (void(Lua_Client::*)(int,int,float,float,float,float))&Lua_Client::SetBindPoint)
.def("SetBindPoint", (void(Lua_Client::*)(void))&Lua_Client::SetBindPoint)
#ifdef BOTS
.def("SetBotCreationLimit", (void(Lua_Client::*)(uint32))&Lua_Client::SetBotCreationLimit)
.def("SetBotCreationLimit", (void(Lua_Client::*)(uint32,uint8))&Lua_Client::SetBotCreationLimit)
.def("SetBotRequiredLevel", (void(Lua_Client::*)(int))&Lua_Client::SetBotRequiredLevel)
.def("SetBotRequiredLevel", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotRequiredLevel)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int))&Lua_Client::SetBotSpawnLimit)
.def("SetBotSpawnLimit", (void(Lua_Client::*)(int,uint8))&Lua_Client::SetBotSpawnLimit)
#endif
.def("SetClientMaxLevel", (void(Lua_Client::*)(int))&Lua_Client::SetClientMaxLevel)
.def("SetConsumption", (void(Lua_Client::*)(int, int))&Lua_Client::SetConsumption)
.def("SetDeity", (void(Lua_Client::*)(int))&Lua_Client::SetDeity)

View File

@ -451,6 +451,23 @@ public:
bool SendGMCommand(std::string message);
bool SendGMCommand(std::string message, bool ignore_status);
#ifdef BOTS
int GetBotRequiredLevel();
int GetBotRequiredLevel(uint8 class_id);
uint32 GetBotCreationLimit();
uint32 GetBotCreationLimit(uint8 class_id);
int GetBotSpawnLimit();
int GetBotSpawnLimit(uint8 class_id);
void SetBotRequiredLevel(int new_required_level);
void SetBotRequiredLevel(int new_required_level, uint8 class_id);
void SetBotCreationLimit(uint32 new_creation_limit);
void SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id);
void SetBotSpawnLimit(int new_spawn_limit);
void SetBotSpawnLimit(int new_spawn_limit, uint8 class_id);
#endif
void DialogueWindow(std::string markdown);
Lua_Expedition CreateExpedition(luabind::object expedition_info);

View File

@ -413,6 +413,20 @@ Lua_Bot_List Lua_EntityList::GetBotListByCharacterID(uint32 character_id) {
return ret;
}
Lua_Bot_List Lua_EntityList::GetBotListByCharacterID(uint32 character_id, uint8 class_id) {
Lua_Safe_Call_Class(Lua_Bot_List);
Lua_Bot_List ret;
auto bot_list = self->GetBotListByCharacterID(character_id, class_id);
if (bot_list.size()) {
for (auto bot : bot_list) {
ret.entries.push_back(Lua_Bot(bot));
}
}
return ret;
}
Lua_Bot_List Lua_EntityList::GetBotListByClientName(std::string client_name) {
Lua_Safe_Call_Class(Lua_Bot_List);
Lua_Bot_List ret;
@ -607,6 +621,7 @@ luabind::scope lua_register_entity_list() {
.def("GetBotByName", (Lua_Bot(Lua_EntityList::*)(std::string))&Lua_EntityList::GetBotByName)
.def("GetBotList", (Lua_Bot_List(Lua_EntityList::*)(void))&Lua_EntityList::GetBotList)
.def("GetBotListByCharacterID", (Lua_Bot_List(Lua_EntityList::*)(uint32))&Lua_EntityList::GetBotListByCharacterID)
.def("GetBotListByCharacterID", (Lua_Bot_List(Lua_EntityList::*)(uint32,uint8))&Lua_EntityList::GetBotListByCharacterID)
.def("GetBotListByClientName", (Lua_Bot_List(Lua_EntityList::*)(std::string))&Lua_EntityList::GetBotListByClientName)
#endif
.def("GetClientByAccID", (Lua_Client(Lua_EntityList::*)(uint32))&Lua_EntityList::GetClientByAccID)

View File

@ -135,6 +135,7 @@ public:
Lua_Bot GetBotByName(std::string bot_name);
Lua_Bot_List GetBotList();
Lua_Bot_List GetBotListByCharacterID(uint32 character_id);
Lua_Bot_List GetBotListByCharacterID(uint32 character_id, uint8 class_id);
Lua_Bot_List GetBotListByClientName(std::string client_name);
Lua_Bot GetRandomBot();
Lua_Bot GetRandomBot(float x, float y, float z, float distance);

View File

@ -2507,6 +2507,70 @@ void Perl_Client_SendMarqueeMessage(Client* self, uint32 type, uint32 priority,
self->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, message);
}
#ifdef BOTS
int Perl_Client_GetBotRequiredLevel(Client* self)
{
return self->GetBotRequiredLevel();
}
int Perl_Client_GetBotRequiredLevel(Client* self, uint8 class_id)
{
return self->GetBotRequiredLevel(class_id);
}
uint32 Perl_Client_GetBotCreationLimit(Client* self)
{
return self->GetBotCreationLimit();
}
uint32 Perl_Client_GetBotCreationLimit(Client* self, uint8 class_id)
{
return self->GetBotCreationLimit(class_id);
}
int Perl_Client_GetBotSpawnLimit(Client* self)
{
return self->GetBotSpawnLimit();
}
int Perl_Client_GetBotSpawnLimit(Client* self, uint8 class_id)
{
return self->GetBotSpawnLimit(class_id);
}
void Perl_Client_SetBotRequiredLevel(Client* self, int new_required_level)
{
self->SetBotRequiredLevel(new_required_level);
}
void Perl_Client_SetBotRequiredLevel(Client* self, int new_required_level, uint8 class_id)
{
self->SetBotRequiredLevel(new_required_level, class_id);
}
void Perl_Client_SetBotCreationLimit(Client* self, uint32 new_creation_limit)
{
self->SetBotCreationLimit(new_creation_limit);
}
void Perl_Client_SetBotCreationLimit(Client* self, uint32 new_creation_limit, uint8 class_id)
{
self->SetBotCreationLimit(new_creation_limit, class_id);
}
void Perl_Client_SetBotSpawnLimit(Client* self, int new_spawn_limit)
{
self->SetBotSpawnLimit(new_spawn_limit);
}
void Perl_Client_SetBotSpawnLimit(Client* self, int new_spawn_limit, uint8 class_id)
{
self->SetBotSpawnLimit(new_spawn_limit, class_id);
}
#endif
void perl_register_client()
{
perl::interpreter perl(PERL_GET_THX);
@ -2626,6 +2690,18 @@ void perl_register_client()
package.add("GetBindZ", (float(*)(Client*, int))&Perl_Client_GetBindZ);
package.add("GetBindZoneID", (uint32_t(*)(Client*))&Perl_Client_GetBindZoneID);
package.add("GetBindZoneID", (uint32_t(*)(Client*, int))&Perl_Client_GetBindZoneID);
#ifdef BOTS
package.add("GetBotCreationLimit", (uint32(*)(Client*))&Perl_Client_GetBotCreationLimit);
package.add("GetBotCreationLimit", (uint32(*)(Client*, uint8))&Perl_Client_GetBotCreationLimit);
package.add("GetBotRequiredLevel", (int(*)(Client*))&Perl_Client_GetBotRequiredLevel);
package.add("GetBotRequiredLevel", (int(*)(Client*, uint8))&Perl_Client_GetBotRequiredLevel);
package.add("GetBotSpawnLimit", (int(*)(Client*))&Perl_Client_GetBotSpawnLimit);
package.add("GetBotSpawnLimit", (int(*)(Client*, uint8))&Perl_Client_GetBotSpawnLimit);
#endif
package.add("GetCarriedMoney", &Perl_Client_GetCarriedMoney);
package.add("GetCarriedPlatinum", &Perl_Client_GetCarriedPlatinum);
package.add("GetCharacterFactionLevel", &Perl_Client_GetCharacterFactionLevel);
@ -2863,6 +2939,18 @@ void perl_register_client()
package.add("SetBindPoint", (void(*)(Client*, int, int, float, float))&Perl_Client_SetBindPoint);
package.add("SetBindPoint", (void(*)(Client*, int, int, float, float, float))&Perl_Client_SetBindPoint);
package.add("SetBindPoint", (void(*)(Client*, int, int, float, float, float, float))&Perl_Client_SetBindPoint);
#ifdef BOTS
package.add("SetBotCreationLimit", (void(*)(Client*, uint32))&Perl_Client_SetBotCreationLimit);
package.add("SetBotCreationLimit", (void(*)(Client*, uint32, uint8))&Perl_Client_SetBotCreationLimit);
package.add("SetBotRequiredLevel", (void(*)(Client*, int))&Perl_Client_SetBotRequiredLevel);
package.add("SetBotRequiredLevel", (void(*)(Client*, int, uint8))&Perl_Client_SetBotRequiredLevel);
package.add("SetBotSpawnLimit", (void(*)(Client*, int))&Perl_Client_SetBotSpawnLimit);
package.add("SetBotSpawnLimit", (void(*)(Client*, int, uint8))&Perl_Client_SetBotSpawnLimit);
#endif
package.add("SetClientMaxLevel", &Perl_Client_SetClientMaxLevel);
package.add("SetConsumption", &Perl_Client_SetConsumption);
package.add("SetCustomItemData", &Perl_Client_SetCustomItemData);

View File

@ -428,6 +428,17 @@ perl::array Perl_EntityList_GetBotListByCharacterID(EntityList* self, uint32_t c
return result;
}
perl::array Perl_EntityList_GetBotListByCharacterID(EntityList* self, uint32_t character_id, uint8_t class_id) // @categories Script Utility, Bot
{
perl::array result;
auto current_bot_list = self->GetBotListByCharacterID(character_id, class_id);
for (int i = 0; i < current_bot_list.size(); ++i)
{
result.push_back(current_bot_list[i]);
}
return result;
}
perl::array Perl_EntityList_GetBotListByClientName(EntityList* self, std::string client_name) // @categories Script Utility, Bot
{
perl::array result;
@ -581,7 +592,8 @@ void perl_register_entitylist()
package.add("GetBotByID", &Perl_EntityList_GetBotByID);
package.add("GetBotByName", &Perl_EntityList_GetBotByName);
package.add("GetBotList", &Perl_EntityList_GetBotList);
package.add("GetBotListByCharacterID", &Perl_EntityList_GetBotListByCharacterID);
package.add("GetBotListByCharacterID", (perl::array(*)(EntityList*, uint32))&Perl_EntityList_GetBotListByCharacterID);
package.add("GetBotListByCharacterID", (perl::array(*)(EntityList*, uint32, uint8))&Perl_EntityList_GetBotListByCharacterID);
package.add("GetBotListByClientName", &Perl_EntityList_GetBotListByClientName);
#endif
package.add("GetClientByAccID", &Perl_EntityList_GetClientByAccID);

View File

@ -2212,11 +2212,21 @@ void QuestManager::popup(const char *title, const char *text, uint32 popupid, ui
#ifdef BOTS
int QuestManager::createbotcount() {
int QuestManager::createbotcount(uint8 class_id) {
QuestManagerCurrentQuestVars();
if (initiator) {
return initiator->GetBotCreationLimit(class_id);
}
return RuleI(Bots, CreationLimit);
}
int QuestManager::spawnbotcount() {
int QuestManager::spawnbotcount(uint8 class_id) {
QuestManagerCurrentQuestVars();
if (initiator) {
return initiator->GetBotSpawnLimit(class_id);
}
return RuleI(Bots, SpawnLimit);
}
@ -2228,49 +2238,167 @@ bool QuestManager::botquest()
bool QuestManager::createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender)
{
QuestManagerCurrentQuestVars();
uint32 MaxBotCreate = RuleI(Bots, CreationLimit);
if (initiator)
{
if(Bot::SpawnedBotCount(initiator->CharacterID()) >= MaxBotCreate)
{
initiator->Message(Chat::Yellow,"You have the maximum number of bots allowed.");
if (initiator) {
auto bot_creation_limit = initiator->GetBotCreationLimit();
auto bot_creation_limit_class = initiator->GetBotCreationLimit(botclass);
auto bot_spawn_limit = initiator->GetBotSpawnLimit();
auto bot_spawn_limit_class = initiator->GetBotSpawnLimit(botclass);
uint32 bot_count = 0;
uint32 bot_class_count = 0;
if (!database.botdb.QueryBotCount(initiator->CharacterID(), botclass, bot_count, bot_class_count)) {
initiator->Message(Chat::White, "Failed to query bot count.");
return false;
}
if (bot_creation_limit >= 0 && bot_count >= bot_creation_limit) {
std::string message;
if (bot_creation_limit) {
message = fmt::format(
"You cannot create anymore than {} bot{}.",
bot_creation_limit,
bot_creation_limit != 1 ? "s" : ""
);
} else {
message = "You cannot create any bots.";
}
initiator->Message(Chat::White, message.c_str());
return false;
}
if (bot_creation_limit_class >= 0 && bot_class_count >= bot_creation_limit_class) {
std::string message;
if (bot_creation_limit_class) {
message = fmt::format(
"You cannot create anymore than {} {} bot{}.",
bot_creation_limit_class,
GetClassIDName(botclass),
bot_creation_limit_class != 1 ? "s" : ""
);
} else {
message = fmt::format(
"You cannot create any {} bots.",
GetClassIDName(botclass)
);
}
initiator->Message(Chat::White, message.c_str());
return false;
}
auto spawned_bot_count = Bot::SpawnedBotCount(initiator->CharacterID());
if (
bot_spawn_limit >= 0 &&
spawned_bot_count >= bot_spawn_limit &&
!initiator->GetGM()
) {
std::string message;
if (bot_spawn_limit) {
message = fmt::format(
"You cannot have more than {} spawned bot{}.",
bot_spawn_limit,
bot_spawn_limit != 1 ? "s" : ""
);
} else {
message = "You are not currently allowed to spawn any bots.";
}
initiator->Message(Chat::White, message.c_str());
return false;
}
auto spawned_bot_count_class = Bot::SpawnedBotCount(initiator->CharacterID(), botclass);
if (
bot_spawn_limit_class >= 0 &&
spawned_bot_count_class >= bot_spawn_limit_class &&
!initiator->GetGM()
) {
std::string message;
if (bot_spawn_limit_class) {
message = fmt::format(
"You cannot have more than {} spawned {} bot{}.",
bot_spawn_limit_class,
GetClassIDName(botclass),
bot_spawn_limit_class != 1 ? "s" : ""
);
} else {
message = fmt::format(
"You are not currently allowed to spawn any {} bots.",
GetClassIDName(botclass)
);
}
initiator->Message(Chat::White, message.c_str());
return false;
}
std::string test_name = name;
bool available_flag = false;
if(!database.botdb.QueryNameAvailablity(test_name, available_flag)) {
initiator->Message(Chat::White, "%s for '%s'", BotDatabase::fail::QueryNameAvailablity(), (char*)name);
if (!database.botdb.QueryNameAvailablity(test_name, available_flag)) {
initiator->Message(
Chat::White,
fmt::format(
"Failed to query name availability for '{}'.",
test_name
).c_str()
);
return false;
}
if (!available_flag) {
initiator->Message(Chat::White, "The name %s is already being used or is invalid. Please choose a different name.", (char*)name);
initiator->Message(
Chat::White,
fmt::format(
"The name {} is already being used or is invalid. Please choose a different name.",
test_name
).c_str()
);
return false;
}
Bot* NewBot = new Bot(Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender), initiator);
Bot* new_bot = new Bot(Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender), initiator);
if(NewBot)
{
if(!NewBot->IsValidRaceClassCombo()) {
if (new_bot) {
if (!new_bot->IsValidRaceClassCombo()) {
initiator->Message(Chat::White, "That Race/Class combination cannot be created.");
return false;
}
if(!NewBot->IsValidName()) {
initiator->Message(Chat::White, "%s has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name.", NewBot->GetCleanName());
if (!new_bot->IsValidName()) {
initiator->Message(
Chat::White,
fmt::format(
"{} has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name.",
new_bot->GetCleanName()
).c_str()
);
return false;
}
// Now that all validation is complete, we can save our newly created bot
if(!NewBot->Save())
{
initiator->Message(Chat::White, "Unable to save %s as a bot.", NewBot->GetCleanName());
}
else
{
initiator->Message(Chat::White, "%s saved as bot %u.", NewBot->GetCleanName(), NewBot->GetBotID());
if (!new_bot->Save()) {
initiator->Message(
Chat::White,
fmt::format(
"Unable to save {} as a bot.",
new_bot->GetCleanName()
).c_str()
);
} else {
initiator->Message(
Chat::White,
fmt::format(
"{} saved as bot ID {}.",
new_bot->GetCleanName(),
new_bot->GetBotID()
).c_str()
);
return true;
}
}

View File

@ -355,8 +355,8 @@ public:
inline bool ProximitySayInUse() { return HaveProximitySays; }
#ifdef BOTS
int createbotcount();
int spawnbotcount();
int createbotcount(uint8 class_id = 0);
int spawnbotcount(uint8 class_id = 0);
bool botquest();
bool createBot(const char *name, const char *lastname, uint8 level, uint16 race, uint8 botclass, uint8 gender);
#endif