diff --git a/common/ruletypes.h b/common/ruletypes.h index c89213368..71af49010 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -602,6 +602,9 @@ RULE_BOOL(Bots, BotLevelsWithOwner, false) // Auto-updates spawned bots as owner RULE_BOOL(Bots, BotCharacterLevelEnabled, false) // Enables required level to spawn bots RULE_INT(Bots, BotCharacterLevel, 0) // 0 as default (if level > this value you can spawn bots if BotCharacterLevelEnabled is true) 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_CATEGORY_END() #endif diff --git a/utils/sql/git/bots/optional/2018_08_10_bots_allowed_criteria_rules.sql b/utils/sql/git/bots/optional/2018_08_10_bots_allowed_criteria_rules.sql new file mode 100644 index 000000000..498f455df --- /dev/null +++ b/utils/sql/git/bots/optional/2018_08_10_bots_allowed_criteria_rules.sql @@ -0,0 +1,4 @@ +INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES +(1, 'Bots:AllowedClasses', '-1', 'Bitmask of allowed bot classes'), +(1, 'Bots:AllowedRaces', '-1', 'Bitmask of allowed bot races'), +(1, 'Bots:AllowedGenders', '3', 'Bitmask of allowed bot genders'); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 3344fd0fb..cc24609ec 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -4243,12 +4243,69 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep) void bot_subcommand_bot_create(Client *c, const Seperator *sep) { - const std::string msg_class = StringFormat("class: %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)", - WARRIOR, CLERIC, PALADIN, RANGER, SHADOWKNIGHT, DRUID, MONK, BARD, ROGUE, SHAMAN, NECROMANCER, WIZARD, MAGICIAN, ENCHANTER, BEASTLORD, BERSERKER); - const std::string msg_race = StringFormat("race: %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)", - HUMAN, BARBARIAN, ERUDITE, WOOD_ELF, HIGH_ELF, DARK_ELF, HALF_ELF, DWARF, TROLL, OGRE, HALFLING, GNOME, IKSAR, VAHSHIR, FROGLOK, DRAKKIN); - const std::string msg_gender = StringFormat("gender: %u(M), %u(F)", MALE, FEMALE); - + 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 + }; + + const std::string gender_substrs[2] = { + "%u(M)", "%u(F)", + }; + + std::string msg_class = "class:"; + std::string msg_race = "race:"; + std::string msg_gender = "gender:"; + std::string msg_separator; + + msg_separator = " "; + for (int i = 0; i <= 15; ++i) { + if (((1 << i) & RuleI(Bots, AllowedClasses)) == 0) + continue; + + msg_class.append(const_cast(msg_separator)); + msg_class.append(StringFormat(class_substrs[i + 1].c_str(), (i + 1))); + + msg_separator = ", "; + } + + msg_separator = " "; + for (int i = 0; i <= 15; ++i) { + if (((1 << i) & RuleI(Bots, AllowedRaces)) == 0) + continue; + + msg_race.append(const_cast(msg_separator)); + msg_race.append(StringFormat(race_substrs[i + 1].c_str(), race_values[i + 1])); + + msg_separator = ", "; + } + + msg_separator = " "; + for (int i = 0; i <= 1; ++i) { + if (((1 << i) & RuleI(Bots, AllowedGenders)) == 0) + continue; + + msg_gender.append(const_cast(msg_separator)); + msg_gender.append(StringFormat(gender_substrs[i].c_str(), i)); + + msg_separator = ", "; + } + if (helper_command_alias_fail(c, "bot_subcommand_bot_create", sep->arg[0], "botcreate")) return; if (helper_is_help_or_usage(sep->arg[1])) { @@ -7588,14 +7645,31 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas 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)) { bot_owner->Message(m_fail, "'%s'(%u):'%s'(%u) is an invalid race-class combination", Bot::RaceIdToString(bot_race).c_str(), bot_race, Bot::ClassIdToString(bot_class).c_str(), bot_class); return bot_id; } - if (bot_gender > FEMALE) { - bot_owner->Message(m_fail, "gender: %u(M), %u(F)", MALE, FEMALE); + if (bot_gender > FEMALE || (((1 << bot_gender) & RuleI(Bots, AllowedGenders)) == 0)) { + if (RuleI(Bots, AllowedGenders) == 3) + 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); return bot_id; }