mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-12 22:01:30 +00:00
[Bots] Remove hardcoded race-class combinations from bots. (#1375)
* [Bots] Remove hardcoded race-class combinations from bots. - Allows server operators to directly influence via a database table the classes a specific bot race can be. - Previously this was hardcoded and required a source modification to do. - Allowed races, classes, and genders have been removed due to redundancy at this point. * Remove const cast and modify saylink definition.
This commit is contained in:
parent
0461ac7912
commit
c3456ebea0
@ -563,9 +563,6 @@ RULE_BOOL(Bots, BotGroupXP, false, "Determines whether client gets experience fo
|
|||||||
RULE_BOOL(Bots, BotLevelsWithOwner, false, "Auto-updates spawned bots as owner levels/de-levels (false is original behavior)")
|
RULE_BOOL(Bots, BotLevelsWithOwner, false, "Auto-updates spawned bots as owner levels/de-levels (false is original behavior)")
|
||||||
RULE_INT(Bots, BotCharacterLevel, 0, "If level is greater that value player can spawn bots if BotCharacterLevelEnabled is true")
|
RULE_INT(Bots, BotCharacterLevel, 0, "If level is greater that value player can spawn bots if BotCharacterLevelEnabled is true")
|
||||||
RULE_INT(Bots, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks")
|
RULE_INT(Bots, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks")
|
||||||
RULE_INT(Bots, AllowedClasses, 0xFFFFFFFF, "Bitmask of allowed bot classes")
|
|
||||||
RULE_INT(Bots, AllowedRaces, 0xFFFFFFFF, "Bitmask of allowed bot races")
|
|
||||||
RULE_INT(Bots, AllowedGenders, 0x3, "Bitmask of allowed bot genders")
|
|
||||||
RULE_BOOL(Bots, AllowOwnerOptionAltCombat, true, "When option is enabled, bots will use an auto-/shared-aggro combat model")
|
RULE_BOOL(Bots, AllowOwnerOptionAltCombat, true, "When option is enabled, bots will use an auto-/shared-aggro combat model")
|
||||||
RULE_BOOL(Bots, AllowOwnerOptionAutoDefend, true, "When option is enabled, bots will defend their owner on enemy aggro")
|
RULE_BOOL(Bots, AllowOwnerOptionAutoDefend, true, "When option is enabled, bots will defend their owner on enemy aggro")
|
||||||
RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel from leash owner before being pulled back (squared value)")
|
RULE_REAL(Bots, LeashDistance, 562500.0f, "Distance a bot is allowed to travel from leash owner before being pulled back (squared value)")
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
#define CURRENT_BINARY_DATABASE_VERSION 9166
|
#define CURRENT_BINARY_DATABASE_VERSION 9166
|
||||||
|
|
||||||
#ifdef BOTS
|
#ifdef BOTS
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9027
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9028
|
||||||
#else
|
#else
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -26,6 +26,7 @@
|
|||||||
9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty|
|
9025|2019_08_26_bots_owner_option_spawn_message.sql|SELECT * FROM db_version WHERE bots_version >= 9025|empty|
|
||||||
9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty|
|
9026|2019_09_09_bots_owner_options_rework.sql|SHOW COLUMNS FROM `bot_owner_options` LIKE 'option_type'|empty|
|
||||||
9027|2020_03_30_bots_view_update.sql|SELECT * FROM db_version WHERE bots_version >= 9027|empty|
|
9027|2020_03_30_bots_view_update.sql|SELECT * FROM db_version WHERE bots_version >= 9027|empty|
|
||||||
|
9028|2021_06_04_bot_create_combinations.sql|SHOW TABLES LIKE 'bot_create_combinations'|empty|
|
||||||
|
|
||||||
# Upgrade conditions:
|
# Upgrade conditions:
|
||||||
# This won't be needed after this system is implemented, but it is used database that are not
|
# This won't be needed after this system is implemented, but it is used database that are not
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
SET NAMES utf8mb4;
|
||||||
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Table structure for bot_create_combinations
|
||||||
|
-- ----------------------------
|
||||||
|
DROP TABLE IF EXISTS `bot_create_combinations`;
|
||||||
|
CREATE TABLE `bot_create_combinations` (
|
||||||
|
`race` int UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
`classes` int UNSIGNED NOT NULL DEFAULT 0,
|
||||||
|
PRIMARY KEY (`race`) USING BTREE
|
||||||
|
) ENGINE = InnoDB CHARACTER SET = latin1 COLLATE = latin1_swedish_ci ROW_FORMAT = Compact;
|
||||||
|
|
||||||
|
-- ----------------------------
|
||||||
|
-- Records of bot_create_combinations
|
||||||
|
-- ----------------------------
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (1, 15871); -- Human
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (2, 49921); -- Barbarian
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (3, 15382); -- Erudite
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (4, 425); -- Wood Elf
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (5, 14342); -- High Elf
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (6, 15635); -- Dark Elf
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (7, 429); -- Half Elf
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (8, 33031); -- Dwarf
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (9, 49681); -- Troll
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (10, 49681); -- Ogre
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (11, 303); -- Halfling
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (12, 15639); -- Gnome
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (128, 18001); -- Iksar
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (130, 50049); -- Vah Shir
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (330, 3863); -- Froglok
|
||||||
|
INSERT INTO `bot_create_combinations` VALUES (522, 15871); -- Drakkin
|
||||||
|
|
||||||
|
SET FOREIGN_KEY_CHECKS = 1;
|
||||||
323
zone/bot.cpp
323
zone/bot.cpp
@ -1701,206 +1701,15 @@ bool Bot::IsValidRaceClassCombo()
|
|||||||
return Bot::IsValidRaceClassCombo(GetRace(), GetClass());
|
return Bot::IsValidRaceClassCombo(GetRace(), GetClass());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::IsValidRaceClassCombo(uint16 r, uint8 c)
|
bool Bot::IsValidRaceClassCombo(uint16 bot_race, uint8 bot_class)
|
||||||
{
|
{
|
||||||
switch (r) {
|
bool is_valid = false;
|
||||||
case HUMAN:
|
auto classes = database.botdb.GetRaceClassBitmask(bot_race);
|
||||||
switch (c) {
|
auto bot_class_bitmask = GetPlayerClassBit(bot_class);
|
||||||
case WARRIOR:
|
if (classes & bot_class_bitmask) {
|
||||||
case CLERIC:
|
is_valid = true;
|
||||||
case PALADIN:
|
|
||||||
case RANGER:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case DRUID:
|
|
||||||
case MONK:
|
|
||||||
case BARD:
|
|
||||||
case ROGUE:
|
|
||||||
case NECROMANCER:
|
|
||||||
case WIZARD:
|
|
||||||
case MAGICIAN:
|
|
||||||
case ENCHANTER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case BARBARIAN:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case ROGUE:
|
|
||||||
case SHAMAN:
|
|
||||||
case BEASTLORD:
|
|
||||||
case BERSERKER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case ERUDITE:
|
|
||||||
switch (c) {
|
|
||||||
case CLERIC:
|
|
||||||
case PALADIN:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case NECROMANCER:
|
|
||||||
case WIZARD:
|
|
||||||
case MAGICIAN:
|
|
||||||
case ENCHANTER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case WOOD_ELF:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case RANGER:
|
|
||||||
case DRUID:
|
|
||||||
case BARD:
|
|
||||||
case ROGUE:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HIGH_ELF:
|
|
||||||
switch (c) {
|
|
||||||
case CLERIC:
|
|
||||||
case PALADIN:
|
|
||||||
case WIZARD:
|
|
||||||
case MAGICIAN:
|
|
||||||
case ENCHANTER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DARK_ELF:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case CLERIC:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case ROGUE:
|
|
||||||
case NECROMANCER:
|
|
||||||
case WIZARD:
|
|
||||||
case MAGICIAN:
|
|
||||||
case ENCHANTER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HALF_ELF:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case PALADIN:
|
|
||||||
case RANGER:
|
|
||||||
case DRUID:
|
|
||||||
case BARD:
|
|
||||||
case ROGUE:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DWARF:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case CLERIC:
|
|
||||||
case PALADIN:
|
|
||||||
case ROGUE:
|
|
||||||
case BERSERKER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case TROLL:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case SHAMAN:
|
|
||||||
case BEASTLORD:
|
|
||||||
case BERSERKER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case OGRE:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case SHAMAN:
|
|
||||||
case BEASTLORD:
|
|
||||||
case BERSERKER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case HALFLING:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case CLERIC:
|
|
||||||
case PALADIN:
|
|
||||||
case RANGER:
|
|
||||||
case DRUID:
|
|
||||||
case ROGUE:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case GNOME:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case CLERIC:
|
|
||||||
case PALADIN:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case ROGUE:
|
|
||||||
case NECROMANCER:
|
|
||||||
case WIZARD:
|
|
||||||
case MAGICIAN:
|
|
||||||
case ENCHANTER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case IKSAR:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case MONK:
|
|
||||||
case SHAMAN:
|
|
||||||
case NECROMANCER:
|
|
||||||
case BEASTLORD:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case VAHSHIR:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case BARD:
|
|
||||||
case ROGUE:
|
|
||||||
case SHAMAN:
|
|
||||||
case BEASTLORD:
|
|
||||||
case BERSERKER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case FROGLOK:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case CLERIC:
|
|
||||||
case PALADIN:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case ROGUE:
|
|
||||||
case SHAMAN:
|
|
||||||
case NECROMANCER:
|
|
||||||
case WIZARD:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DRAKKIN:
|
|
||||||
switch (c) {
|
|
||||||
case WARRIOR:
|
|
||||||
case CLERIC:
|
|
||||||
case PALADIN:
|
|
||||||
case RANGER:
|
|
||||||
case SHADOWKNIGHT:
|
|
||||||
case DRUID:
|
|
||||||
case MONK:
|
|
||||||
case BARD:
|
|
||||||
case ROGUE:
|
|
||||||
case NECROMANCER:
|
|
||||||
case WIZARD:
|
|
||||||
case MAGICIAN:
|
|
||||||
case ENCHANTER:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
return is_valid;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::IsValidName()
|
bool Bot::IsValidName()
|
||||||
@ -4264,124 +4073,6 @@ void Bot::LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Bot::ClassIdToString(uint16 classId) {
|
|
||||||
std::string Result;
|
|
||||||
|
|
||||||
if(classId > 0 && classId < 17) {
|
|
||||||
switch(classId) {
|
|
||||||
case 1:
|
|
||||||
Result = std::string("Warrior");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Result = std::string("Cleric");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
Result = std::string("Paladin");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
Result = std::string("Ranger");
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
Result = std::string("Shadowknight");
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
Result = std::string("Druid");
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
Result = std::string("Monk");
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
Result = std::string("Bard");
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
Result = std::string("Rogue");
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
Result = std::string("Shaman");
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
Result = std::string("Necromancer");
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
Result = std::string("Wizard");
|
|
||||||
break;
|
|
||||||
case 13:
|
|
||||||
Result = std::string("Magician");
|
|
||||||
break;
|
|
||||||
case 14:
|
|
||||||
Result = std::string("Enchanter");
|
|
||||||
break;
|
|
||||||
case 15:
|
|
||||||
Result = std::string("Beastlord");
|
|
||||||
break;
|
|
||||||
case 16:
|
|
||||||
Result = std::string("Berserker");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string Bot::RaceIdToString(uint16 raceId) {
|
|
||||||
std::string Result;
|
|
||||||
|
|
||||||
if(raceId > 0) {
|
|
||||||
switch(raceId) {
|
|
||||||
case 1:
|
|
||||||
Result = std::string("Human");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
Result = std::string("Barbarian");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
Result = std::string("Erudite");
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
Result = std::string("Wood Elf");
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
Result = std::string("High Elf");
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
Result = std::string("Dark Elf");
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
Result = std::string("Half Elf");
|
|
||||||
break;
|
|
||||||
case 8:
|
|
||||||
Result = std::string("Dwarf");
|
|
||||||
break;
|
|
||||||
case 9:
|
|
||||||
Result = std::string("Troll");
|
|
||||||
break;
|
|
||||||
case 10:
|
|
||||||
Result = std::string("Ogre");
|
|
||||||
break;
|
|
||||||
case 11:
|
|
||||||
Result = std::string("Halfling");
|
|
||||||
break;
|
|
||||||
case 12:
|
|
||||||
Result = std::string("Gnome");
|
|
||||||
break;
|
|
||||||
case 128:
|
|
||||||
Result = std::string("Iksar");
|
|
||||||
break;
|
|
||||||
case 130:
|
|
||||||
Result = std::string("Vah Shir");
|
|
||||||
break;
|
|
||||||
case 330:
|
|
||||||
Result = std::string("Froglok");
|
|
||||||
break;
|
|
||||||
case 522:
|
|
||||||
Result = std::string("Drakkin");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color) {
|
void Bot::SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color) {
|
||||||
EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
|
EQApplicationPacket* outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
|
||||||
WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer;
|
WearChange_Struct* wc = (WearChange_Struct*)outapp->pBuffer;
|
||||||
|
|||||||
@ -358,8 +358,6 @@ public:
|
|||||||
static uint32 SpawnedBotCount(uint32 botOwnerCharacterID);
|
static uint32 SpawnedBotCount(uint32 botOwnerCharacterID);
|
||||||
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
|
static void LevelBotWithClient(Client* client, uint8 level, bool sendlvlapp);
|
||||||
//static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage);
|
//static bool SetBotOwnerCharacterID(uint32 botID, uint32 botOwnerCharacterID, std::string* errorMessage);
|
||||||
static std::string ClassIdToString(uint16 classId);
|
|
||||||
static std::string RaceIdToString(uint16 raceId);
|
|
||||||
static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined);
|
static bool IsBotAttackAllowed(Mob* attacker, Mob* target, bool& hasRuleDefined);
|
||||||
static Bot* GetBotByBotClientOwnerAndBotName(Client* c, std::string botName);
|
static Bot* GetBotByBotClientOwnerAndBotName(Client* c, std::string botName);
|
||||||
static void ProcessBotGroupInvite(Client* c, std::string botName);
|
static void ProcessBotGroupInvite(Client* c, std::string botName);
|
||||||
|
|||||||
@ -1119,7 +1119,7 @@ private:
|
|||||||
for (bcst_levels::iterator levels_iter = bot_levels.begin(); levels_iter != bot_levels.end(); ++levels_iter) {
|
for (bcst_levels::iterator levels_iter = bot_levels.begin(); levels_iter != bot_levels.end(); ++levels_iter) {
|
||||||
if (levels_iter->second < test_iter->second)
|
if (levels_iter->second < test_iter->second)
|
||||||
test_iter = levels_iter;
|
test_iter = levels_iter;
|
||||||
if (strcasecmp(Bot::ClassIdToString(levels_iter->first).c_str(), Bot::ClassIdToString(test_iter->first).c_str()) < 0 && levels_iter->second <= test_iter->second)
|
if (strcasecmp(GetClassIDName(levels_iter->first), GetClassIDName(test_iter->first)) < 0 && levels_iter->second <= test_iter->second)
|
||||||
test_iter = levels_iter;
|
test_iter = levels_iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1131,8 +1131,8 @@ private:
|
|||||||
else
|
else
|
||||||
bot_segment = " or %s(%u)";
|
bot_segment = " or %s(%u)";
|
||||||
|
|
||||||
required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second));
|
required_bots_map[type_index].append(StringFormat(bot_segment.c_str(), GetClassIDName(test_iter->first), test_iter->second));
|
||||||
required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", Bot::ClassIdToString(test_iter->first).c_str(), test_iter->second);
|
required_bots_map_by_class[type_index][test_iter->first] = StringFormat("%s(%u)", GetClassIDName(test_iter->first), test_iter->second);
|
||||||
bot_levels.erase(test_iter);
|
bot_levels.erase(test_iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1428,6 +1428,7 @@ int bot_command_init(void)
|
|||||||
bot_command_add("suspend", "Suspends a bot's AI processing until released", 0, bot_command_suspend) ||
|
bot_command_add("suspend", "Suspends a bot's AI processing until released", 0, bot_command_suspend) ||
|
||||||
bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) ||
|
bot_command_add("taunt", "Toggles taunt use by a bot", 0, bot_command_taunt) ||
|
||||||
bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) ||
|
bot_command_add("track", "Orders a capable bot to track enemies", 0, bot_command_track) ||
|
||||||
|
bot_command_add("viewcombos", "Views bot race class combinations", 0, bot_command_view_combos) ||
|
||||||
bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing)
|
bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", 0, bot_command_water_breathing)
|
||||||
) {
|
) {
|
||||||
bot_command_deinit();
|
bot_command_deinit();
|
||||||
@ -5107,6 +5108,68 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep)
|
|||||||
c->Message(m_action, "Bot '%s' was successfully cloned to bot '%s'", my_bot->GetCleanName(), bot_name.c_str());
|
c->Message(m_action, "Bot '%s' was successfully cloned to bot '%s'", my_bot->GetCleanName(), bot_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void bot_command_view_combos(Client *c, const Seperator *sep)
|
||||||
|
{
|
||||||
|
const std::string class_substrs[17] = { "",
|
||||||
|
"%u (WAR)", "%u (CLR)", "%u (PAL)", "%u (RNG)",
|
||||||
|
"%u (SHD)", "%u (DRU)", "%u (MNK)", "%u (BRD)",
|
||||||
|
"%u (ROG)", "%u (SHM)", "%u (NEC)", "%u (WIZ)",
|
||||||
|
"%u (MAG)", "%u (ENC)", "%u (BST)", "%u (BER)"
|
||||||
|
};
|
||||||
|
|
||||||
|
const std::string race_substrs[17] = { "",
|
||||||
|
"%u (HUM)", "%u (BAR)", "%u (ERU)", "%u (ELF)",
|
||||||
|
"%u (HIE)", "%u (DEF)", "%u (HEF)", "%u (DWF)",
|
||||||
|
"%u (TRL)", "%u (OGR)", "%u (HFL)", "%u (GNM)",
|
||||||
|
"%u (IKS)", "%u (VAH)", "%u (FRG)", "%u (DRK)"
|
||||||
|
};
|
||||||
|
|
||||||
|
const uint16 race_values[17] = { 0,
|
||||||
|
HUMAN, BARBARIAN, ERUDITE, WOOD_ELF,
|
||||||
|
HIGH_ELF, DARK_ELF, HALF_ELF, DWARF,
|
||||||
|
TROLL, OGRE, HALFLING, GNOME,
|
||||||
|
IKSAR, VAHSHIR, FROGLOK, DRAKKIN
|
||||||
|
};
|
||||||
|
if (helper_command_alias_fail(c, "bot_command_view_combos", sep->arg[0], "viewcombos"))
|
||||||
|
return;
|
||||||
|
if (helper_is_help_or_usage(sep->arg[1])) {
|
||||||
|
std::string window_title = "Bot Races";
|
||||||
|
std::string window_text;
|
||||||
|
std::string message_separator = " ";
|
||||||
|
c->Message(m_usage, "Usage: %s [bot_race]", sep->arg[0]);
|
||||||
|
window_text.append("<c \"#FFFFFF\">Races:<c \"#FFFF\">");
|
||||||
|
for (int race_id = 0; race_id <= 15; ++race_id) {
|
||||||
|
window_text.append(message_separator);
|
||||||
|
window_text.append(StringFormat(race_substrs[race_id + 1].c_str(), race_values[race_id + 1]));
|
||||||
|
message_separator = ", ";
|
||||||
|
}
|
||||||
|
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sep->arg[1][0] == '\0' || !sep->IsNumber(1)) {
|
||||||
|
c->Message(m_fail, "Invalid Race!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
uint16 bot_race = atoi(sep->arg[1]);
|
||||||
|
auto classes_bitmask = database.botdb.GetRaceClassBitmask(bot_race);
|
||||||
|
auto race_name = GetRaceIDName(bot_race);
|
||||||
|
std::string window_title = "Bot Classes";
|
||||||
|
std::string window_text;
|
||||||
|
std::string message_separator = " ";
|
||||||
|
c->Message(m_usage, "%s can be these classes.", race_name);
|
||||||
|
window_text.append("<c \"#FFFFFF\">Classes:<c \"#FFFF\">");
|
||||||
|
for (int class_id = 0; class_id <= 15; ++class_id) {
|
||||||
|
if (classes_bitmask & GetPlayerClassBit(class_id)) {
|
||||||
|
window_text.append(message_separator);
|
||||||
|
window_text.append(StringFormat(class_substrs[class_id].c_str(), class_id));
|
||||||
|
message_separator = ", ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c->SendPopupToClient(window_title.c_str(), window_text.c_str());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
void bot_subcommand_bot_create(Client *c, const Seperator *sep)
|
void bot_subcommand_bot_create(Client *c, const Seperator *sep)
|
||||||
{
|
{
|
||||||
const std::string class_substrs[17] = { "",
|
const std::string class_substrs[17] = { "",
|
||||||
@ -5148,10 +5211,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
|
|||||||
message_separator = " ";
|
message_separator = " ";
|
||||||
object_count = 1;
|
object_count = 1;
|
||||||
for (int i = 0; i <= 15; ++i) {
|
for (int i = 0; i <= 15; ++i) {
|
||||||
if (((1 << i) & RuleI(Bots, AllowedClasses)) == 0)
|
window_text.append(message_separator);
|
||||||
continue;
|
|
||||||
|
|
||||||
window_text.append(const_cast<const std::string&>(message_separator));
|
|
||||||
if (object_count >= object_max) {
|
if (object_count >= object_max) {
|
||||||
window_text.append("<br>");
|
window_text.append("<br>");
|
||||||
object_count = 0;
|
object_count = 0;
|
||||||
@ -5166,10 +5226,7 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
|
|||||||
message_separator = " ";
|
message_separator = " ";
|
||||||
object_count = 1;
|
object_count = 1;
|
||||||
for (int i = 0; i <= 15; ++i) {
|
for (int i = 0; i <= 15; ++i) {
|
||||||
if (((1 << i) & RuleI(Bots, AllowedRaces)) == 0)
|
window_text.append(message_separator);
|
||||||
continue;
|
|
||||||
|
|
||||||
window_text.append(const_cast<const std::string&>(message_separator));
|
|
||||||
if (object_count >= object_max) {
|
if (object_count >= object_max) {
|
||||||
window_text.append("<br>");
|
window_text.append("<br>");
|
||||||
object_count = 0;
|
object_count = 0;
|
||||||
@ -5183,12 +5240,8 @@ void bot_subcommand_bot_create(Client *c, const Seperator *sep)
|
|||||||
window_text.append("<c \"#FFFFFF\">Genders:<c \"#FFFF\">");
|
window_text.append("<c \"#FFFFFF\">Genders:<c \"#FFFF\">");
|
||||||
message_separator = " ";
|
message_separator = " ";
|
||||||
for (int i = 0; i <= 1; ++i) {
|
for (int i = 0; i <= 1; ++i) {
|
||||||
if (((1 << i) & RuleI(Bots, AllowedGenders)) == 0)
|
window_text.append(message_separator);
|
||||||
continue;
|
|
||||||
|
|
||||||
window_text.append(const_cast<const std::string&>(message_separator));
|
|
||||||
window_text.append(StringFormat(gender_substrs[i].c_str(), i));
|
window_text.append(StringFormat(gender_substrs[i].c_str(), i));
|
||||||
|
|
||||||
message_separator = ", ";
|
message_separator = ", ";
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -5802,9 +5855,9 @@ void bot_subcommand_bot_list(Client *c, const Seperator *sep)
|
|||||||
c->Message(Chat::White, "[%s] is a level %u %s %s %s who is owned by %s",
|
c->Message(Chat::White, "[%s] is a level %u %s %s %s who is owned by %s",
|
||||||
((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQ::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)),
|
((c->CharacterID() == bots_iter.Owner_ID) && (!botCheckNotOnline) ? (EQ::SayLinkEngine::GenerateQuestSaylink(botspawn_saylink, false, bots_iter.Name).c_str()) : (bots_iter.Name)),
|
||||||
bots_iter.Level,
|
bots_iter.Level,
|
||||||
Bot::RaceIdToString(bots_iter.Race).c_str(),
|
GetRaceIDName(bots_iter.Race),
|
||||||
((bots_iter.Gender == FEMALE) ? ("Female") : ((bots_iter.Gender == MALE) ? ("Male") : ("Neuter"))),
|
((bots_iter.Gender == FEMALE) ? ("Female") : ((bots_iter.Gender == MALE) ? ("Male") : ("Neuter"))),
|
||||||
Bot::ClassIdToString(bots_iter.Class).c_str(),
|
GetClassIDName(bots_iter.Class),
|
||||||
bots_iter.Owner
|
bots_iter.Owner
|
||||||
);
|
);
|
||||||
if (c->CharacterID() == bots_iter.Owner_ID) { ++bots_owned; }
|
if (c->CharacterID() == bots_iter.Owner_ID) { ++bots_owned; }
|
||||||
@ -5977,7 +6030,7 @@ void bot_subcommand_bot_report(Client *c, const Seperator *sep)
|
|||||||
if (!bot_iter)
|
if (!bot_iter)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::string report_msg = StringFormat("%s %s reports", Bot::ClassIdToString(bot_iter->GetClass()).c_str(), bot_iter->GetCleanName());
|
std::string report_msg = StringFormat("%s %s reports", GetClassIDName(bot_iter->GetClass()), bot_iter->GetCleanName());
|
||||||
report_msg.append(StringFormat(": %3.1f%% health", bot_iter->GetHPRatio()));
|
report_msg.append(StringFormat(": %3.1f%% health", bot_iter->GetHPRatio()));
|
||||||
if (!IsNonSpellFighterClass(bot_iter->GetClass()))
|
if (!IsNonSpellFighterClass(bot_iter->GetClass()))
|
||||||
report_msg.append(StringFormat(": %3.1f%% mana", bot_iter->GetManaRatio()));
|
report_msg.append(StringFormat(": %3.1f%% mana", bot_iter->GetManaRatio()));
|
||||||
@ -8672,33 +8725,25 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
|
|||||||
return bot_id;
|
return bot_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto class_bit = GetPlayerClassBit(bot_class);
|
|
||||||
if ((class_bit & RuleI(Bots, AllowedClasses)) == PLAYER_CLASS_UNKNOWN_BIT) {
|
|
||||||
bot_owner->Message(m_fail, "Class '%s' bots are not allowed on this server", GetPlayerClassName(bot_class));
|
|
||||||
return bot_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto race_bit = GetPlayerRaceBit(bot_race);
|
|
||||||
if ((race_bit & RuleI(Bots, AllowedRaces)) == PLAYER_RACE_UNKNOWN_BIT) {
|
|
||||||
bot_owner->Message(m_fail, "Race '%s' bots are not allowed on this server", GetPlayerRaceName(bot_class));
|
|
||||||
return bot_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Bot::IsValidRaceClassCombo(bot_race, bot_class)) {
|
if (!Bot::IsValidRaceClassCombo(bot_race, bot_class)) {
|
||||||
bot_owner->Message(m_fail, "'%s'(%u):'%s'(%u) is an invalid race-class combination",
|
const char* bot_race_name = GetRaceIDName(bot_race);
|
||||||
Bot::RaceIdToString(bot_race).c_str(), bot_race, Bot::ClassIdToString(bot_class).c_str(), bot_class);
|
const char* bot_class_name = GetClassIDName(bot_class);
|
||||||
|
std::string view_saylink = EQ::SayLinkEngine::GenerateQuestSaylink(fmt::format("^viewcombos {}", bot_race), false, "view");
|
||||||
|
bot_owner->Message(
|
||||||
|
m_fail,
|
||||||
|
fmt::format(
|
||||||
|
"{} {} is an invalid race-class combination, would you like to {} proper combinations for {}?",
|
||||||
|
bot_race_name,
|
||||||
|
bot_class_name,
|
||||||
|
view_saylink,
|
||||||
|
bot_race_name
|
||||||
|
).c_str()
|
||||||
|
);
|
||||||
return bot_id;
|
return bot_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bot_gender > FEMALE || (((1 << bot_gender) & RuleI(Bots, AllowedGenders)) == 0)) {
|
if (bot_gender > FEMALE) {
|
||||||
if (RuleI(Bots, AllowedGenders) == 3)
|
bot_owner->Message(m_fail, "gender: %u (M), %u (F)", MALE, FEMALE);
|
||||||
bot_owner->Message(m_fail, "gender: %u(M), %u(F)", MALE, FEMALE);
|
|
||||||
else if (RuleI(Bots, AllowedGenders) == 2)
|
|
||||||
bot_owner->Message(m_fail, "gender: %u(F)", FEMALE);
|
|
||||||
else if (RuleI(Bots, AllowedGenders) == 1)
|
|
||||||
bot_owner->Message(m_fail, "gender: %u(M)", MALE);
|
|
||||||
else
|
|
||||||
bot_owner->Message(m_fail, "gender: ERROR - No valid genders exist");
|
|
||||||
return bot_id;
|
return bot_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -8710,7 +8755,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
|
|||||||
return bot_id;
|
return bot_id;
|
||||||
}
|
}
|
||||||
if (bot_count >= max_bot_count) {
|
if (bot_count >= max_bot_count) {
|
||||||
bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots", max_bot_count);
|
bot_owner->Message(m_fail, "You have reached the maximum limit of %i bots.", max_bot_count);
|
||||||
return bot_id;
|
return bot_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -593,6 +593,7 @@ void bot_command_summon_corpse(Client *c, const Seperator *sep);
|
|||||||
void bot_command_suspend(Client *c, const Seperator *sep);
|
void bot_command_suspend(Client *c, const Seperator *sep);
|
||||||
void bot_command_taunt(Client *c, const Seperator *sep);
|
void bot_command_taunt(Client *c, const Seperator *sep);
|
||||||
void bot_command_track(Client *c, const Seperator *sep);
|
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);
|
void bot_command_water_breathing(Client *c, const Seperator *sep);
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -2952,6 +2952,20 @@ uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_ind
|
|||||||
return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint16 BotDatabase::GetRaceClassBitmask(uint16 bot_race)
|
||||||
|
{
|
||||||
|
std::string query = fmt::format(
|
||||||
|
"SELECT `classes` FROM `bot_create_combinations` WHERE `race` = {}",
|
||||||
|
bot_race
|
||||||
|
);
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
uint16 classes = 0;
|
||||||
|
if (results.RowCount() == 1) {
|
||||||
|
auto row = results.begin();
|
||||||
|
classes = atoi(row[0]);
|
||||||
|
}
|
||||||
|
return classes;
|
||||||
|
}
|
||||||
|
|
||||||
/* fail::Bot functions */
|
/* fail::Bot functions */
|
||||||
const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; }
|
const char* BotDatabase::fail::QueryNameAvailablity() { return "Failed to query name availability"; }
|
||||||
|
|||||||
@ -186,6 +186,7 @@ public:
|
|||||||
/* Bot miscellaneous functions */
|
/* Bot miscellaneous functions */
|
||||||
uint8 GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index);
|
uint8 GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index);
|
||||||
|
|
||||||
|
uint16 GetRaceClassBitmask(uint16 bot_race);
|
||||||
|
|
||||||
class fail {
|
class fail {
|
||||||
public:
|
public:
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user