From 32e37660d8066b16fe18e6e70ab289ed409e5a0f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 26 Apr 2024 22:38:56 -0500 Subject: [PATCH 001/394] Bot Rework --- common/classes.h | 2 + .../database_update_manifest_bots.cpp | 340 ++ common/database_schema.h | 1 + common/eqemu_logsys.h | 16 +- common/eqemu_logsys_log_aliases.h | 70 + .../base/base_bot_data_repository.h | 85 +- .../base/base_bot_settings_repository.h | 464 ++ common/repositories/bot_data_repository.h | 40 - common/repositories/bot_settings_repository.h | 50 + common/ruletypes.h | 113 + common/spdat.cpp | 783 ++- common/spdat.h | 100 +- common/version.h | 2 +- zone/aggro.cpp | 119 + zone/attack.cpp | 7 +- zone/bot.cpp | 4230 +++++++++++++---- zone/bot.h | 342 +- zone/bot_command.cpp | 229 +- zone/bot_command.h | 67 +- zone/bot_commands/actionable.cpp | 69 +- zone/bot_commands/aggressive.cpp | 24 +- zone/bot_commands/appearance.cpp | 2 +- zone/bot_commands/apply_poison.cpp | 1 + zone/bot_commands/attack.cpp | 9 +- zone/bot_commands/behind_mob.cpp | 173 + zone/bot_commands/bind_affinity.cpp | 1 + zone/bot_commands/bot.cpp | 369 +- zone/bot_commands/bot_settings.cpp | 43 + zone/bot_commands/cast.cpp | 301 ++ zone/bot_commands/caster_range.cpp | 9 +- zone/bot_commands/class_race_list.cpp | 113 + zone/bot_commands/click_item.cpp | 4 +- zone/bot_commands/copy_settings.cpp | 366 ++ zone/bot_commands/default_settings.cpp | 473 ++ zone/bot_commands/defensive.cpp | 11 +- zone/bot_commands/follow.cpp | 100 +- zone/bot_commands/guard.cpp | 6 +- zone/bot_commands/hold.cpp | 6 +- zone/bot_commands/illusion_block.cpp | 172 + zone/bot_commands/inventory.cpp | 21 +- zone/bot_commands/max_melee_range.cpp | 172 + zone/bot_commands/mesmerize.cpp | 44 +- zone/bot_commands/pet.cpp | 47 +- zone/bot_commands/pull.cpp | 18 +- zone/bot_commands/release.cpp | 2 +- zone/bot_commands/sit_hp_percent.cpp | 172 + zone/bot_commands/sit_in_combat.cpp | 172 + zone/bot_commands/sit_mana_percent.cpp | 172 + zone/bot_commands/spell.cpp | 4 +- zone/bot_commands/spell_aggro_checks.cpp | 236 + zone/bot_commands/spell_delays.cpp | 242 + zone/bot_commands/spell_engaged_priority.cpp | 240 + zone/bot_commands/spell_holds.cpp | 229 + zone/bot_commands/spell_idle_priority.cpp | 240 + zone/bot_commands/spell_max_hp_pct.cpp | 236 + zone/bot_commands/spell_max_mana_pct.cpp | 236 + zone/bot_commands/spell_max_thresholds.cpp | 243 + zone/bot_commands/spell_min_hp_pct.cpp | 236 + zone/bot_commands/spell_min_mana_pct.cpp | 236 + zone/bot_commands/spell_min_thresholds.cpp | 244 + zone/bot_commands/spell_pursue_priority.cpp | 240 + zone/bot_commands/spell_target_count.cpp | 236 + zone/bot_commands/suspend.cpp | 2 +- zone/bot_commands/taunt.cpp | 15 +- zone/bot_commands/timer.cpp | 9 +- zone/bot_database.cpp | 343 +- zone/bot_database.h | 26 +- zone/bot_structs.h | 5 + zone/botspellsai.cpp | 3483 ++++++-------- zone/client.cpp | 275 +- zone/client.h | 32 + zone/client_bot.cpp | 8 + zone/client_mods.cpp | 6 +- zone/client_packet.cpp | 13 +- zone/command.cpp | 10 + zone/command.h | 5 + zone/entity.cpp | 1 + zone/entity.h | 2 +- zone/exp.cpp | 1 + zone/gm_commands/illusion_block.cpp | 31 + zone/gm_commands/spell_delays.cpp | 215 + zone/gm_commands/spell_holds.cpp | 218 + zone/gm_commands/spell_max_thresholds.cpp | 216 + zone/gm_commands/spell_min_thresholds.cpp | 216 + zone/lua_bot.cpp | 6 - zone/lua_bot.h | 1 - zone/merc.cpp | 4 +- zone/mob.cpp | 825 +++- zone/mob.h | 103 + zone/perl_bot.cpp | 6 - zone/perl_client.cpp | 4 +- zone/perl_npc.cpp | 2 +- zone/questmgr.cpp | 2 +- zone/spell_effects.cpp | 10 +- zone/spells.cpp | 203 +- 95 files changed, 15748 insertions(+), 3780 deletions(-) create mode 100644 common/repositories/base/base_bot_settings_repository.h create mode 100644 common/repositories/bot_settings_repository.h create mode 100644 zone/bot_commands/behind_mob.cpp create mode 100644 zone/bot_commands/bot_settings.cpp create mode 100644 zone/bot_commands/cast.cpp create mode 100644 zone/bot_commands/class_race_list.cpp create mode 100644 zone/bot_commands/copy_settings.cpp create mode 100644 zone/bot_commands/default_settings.cpp create mode 100644 zone/bot_commands/illusion_block.cpp create mode 100644 zone/bot_commands/max_melee_range.cpp create mode 100644 zone/bot_commands/sit_hp_percent.cpp create mode 100644 zone/bot_commands/sit_in_combat.cpp create mode 100644 zone/bot_commands/sit_mana_percent.cpp create mode 100644 zone/bot_commands/spell_aggro_checks.cpp create mode 100644 zone/bot_commands/spell_delays.cpp create mode 100644 zone/bot_commands/spell_engaged_priority.cpp create mode 100644 zone/bot_commands/spell_holds.cpp create mode 100644 zone/bot_commands/spell_idle_priority.cpp create mode 100644 zone/bot_commands/spell_max_hp_pct.cpp create mode 100644 zone/bot_commands/spell_max_mana_pct.cpp create mode 100644 zone/bot_commands/spell_max_thresholds.cpp create mode 100644 zone/bot_commands/spell_min_hp_pct.cpp create mode 100644 zone/bot_commands/spell_min_mana_pct.cpp create mode 100644 zone/bot_commands/spell_min_thresholds.cpp create mode 100644 zone/bot_commands/spell_pursue_priority.cpp create mode 100644 zone/bot_commands/spell_target_count.cpp create mode 100644 zone/gm_commands/illusion_block.cpp create mode 100644 zone/gm_commands/spell_delays.cpp create mode 100644 zone/gm_commands/spell_holds.cpp create mode 100644 zone/gm_commands/spell_max_thresholds.cpp create mode 100644 zone/gm_commands/spell_min_thresholds.cpp diff --git a/common/classes.h b/common/classes.h index 96e5cad263..dc586ceebf 100644 --- a/common/classes.h +++ b/common/classes.h @@ -131,6 +131,8 @@ static std::map class_names = { #define ARMOR_TYPE_LAST ARMOR_TYPE_PLATE #define ARMOR_TYPE_COUNT 5 +#define BOT_CLASS_BASE_ID_PREFIX 3000 + const char* GetClassIDName(uint8 class_id, uint8 level = 0); diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 48c9aa7f85..afaa9f23ea 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -161,6 +161,346 @@ ADD COLUMN `extra_haste` mediumint(8) NOT NULL DEFAULT 0 AFTER `wis`; .sql = R"( ALTER TABLE `bot_spells_entries` CHANGE COLUMN `spellid` `spell_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_spells_id`; +)" + }, + ManifestEntry{ + .version = 9046, + .description = "2024_05_18_bot_settings.sql", + .check = "SHOW TABLES LIKE 'bot_settings'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `bot_settings` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `char_id` INT UNSIGNED NOT NULL, + `bot_id` INT UNSIGNED NOT NULL, + `setting_id` INT UNSIGNED NOT NULL, + `setting_type` INT UNSIGNED NOT NULL, + `value` INT UNSIGNED NOT NULL, + `category_name` VARCHAR(64) NULL DEFAULT '', + `setting_name` VARCHAR(64) NULL DEFAULT '', + PRIMARY KEY (`id`) USING BTREE +) +COLLATE='utf8mb4_general_ci'; + +INSERT INTO bot_settings SELECT NULL, 0, bd.`bot_id`, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd +JOIN rule_values rv +WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' +AND bd.expansion_bitmask != rv.rule_value; + +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, `follow_distance`, 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; + +INSERT INTO bot_settings +SELECT NULL, 0, `bot_id`, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' +FROM ( + SELECT `bot_id`, + (CASE + WHEN (`class` IN (2, 6, 10, 11, 12, 13, 14)) THEN 13 + ELSE 255 + END) AS `sml`, + `stop_melee_level` + FROM bot_data +) AS `subquery` +WHERE `sml` != `stop_melee_level`; + +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; + +INSERT INTO bot_settings +SELECT NULL, 0, `bot_id`, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' +FROM ( + SELECT `bot_id`, + (CASE + WHEN (`class` IN (1, 7, 19, 16)) THEN 0 + WHEN `class` = 8 THEN 0 + ELSE 90 + END) AS `casterRange`, + `caster_range` + FROM bot_data +) AS `subquery` +WHERE `casterRange` != `caster_range`; + +ALTER TABLE `bot_data` + DROP COLUMN `show_helm`; +ALTER TABLE `bot_data` + DROP COLUMN `follow_distance`; +ALTER TABLE `bot_data` + DROP COLUMN `stop_melee_level`; +ALTER TABLE `bot_data` + DROP COLUMN `expansion_bitmask`; +ALTER TABLE `bot_data` + DROP COLUMN `enforce_spell_settings`; +ALTER TABLE `bot_data` + DROP COLUMN `archery_setting`; +ALTER TABLE `bot_data` + DROP COLUMN `caster_range`; + +UPDATE `bot_command_settings` SET `aliases`= 'bh' WHERE `bot_command`='behindmob'; +UPDATE `bot_command_settings` SET `aliases`= 'bs|settings' WHERE `bot_command`='botsettings'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ranged|toggleranged|btr') ELSE 'ranged|toggleranged|btr' END WHERE `bot_command`='bottoggleranged' AND `aliases` NOT LIKE '%ranged|toggleranged|btr%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|cr') ELSE 'cr' END WHERE `bot_command`='casterrange' AND `aliases` NOT LIKE '%cr%'; +UPDATE `bot_command_settings` SET `aliases`= 'copy' WHERE `bot_command`='copysettings'; +UPDATE `bot_command_settings` SET `aliases`= 'default' WHERE `bot_command`='defaultsettings'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|enforce') ELSE 'enforce' END WHERE `bot_command`='enforcespellsettings' AND `aliases` NOT LIKE '%enforce%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ib') ELSE 'ib' END WHERE `bot_command`='illusionblock' AND `aliases` NOT LIKE '%ib%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ig') ELSE 'invgive|ig' END WHERE `bot_command`='inventorygive' AND `aliases` NOT LIKE '%ig%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|il') ELSE 'invlist|il' END WHERE `bot_command`='inventorylist' AND `aliases` NOT LIKE '%il%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ir') ELSE 'invremove|ir' END WHERE `bot_command`='inventoryremove' AND `aliases` NOT LIKE '%ir%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|iw') ELSE 'invwindow|iw' END WHERE `bot_command`='inventorywindow' AND `aliases` NOT LIKE '%iw%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|iu') ELSE 'iu' END WHERE `bot_command`='itemuse' AND `aliases` NOT LIKE '%iu%'; +UPDATE `bot_command_settings` SET `aliases`= 'mmr' WHERE `bot_command`='maxmeleerange'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|pp') ELSE 'pp' END WHERE `bot_command`='pickpocket' AND `aliases` NOT LIKE '%pp%'; +UPDATE `bot_command_settings` SET `aliases`= 'sithp' WHERE `bot_command`='sithppercent'; +UPDATE `bot_command_settings` SET `aliases`= 'sitcombat' WHERE `bot_command`='sitincombat'; +UPDATE `bot_command_settings` SET `aliases`= 'sitmana' WHERE `bot_command`='sitmanapercent'; +UPDATE `bot_command_settings` SET `aliases`= 'aggrochecks' WHERE `bot_command`='spellaggrochecks'; +UPDATE `bot_command_settings` SET `aliases`= 'delays' WHERE `bot_command`='spelldelays'; +UPDATE `bot_command_settings` SET `aliases`= 'engagedpriority' WHERE `bot_command`='spellengagedpriority'; +UPDATE `bot_command_settings` SET `aliases`= 'holds' WHERE `bot_command`='spellholds'; +UPDATE `bot_command_settings` SET `aliases`= 'idlepriority' WHERE `bot_command`='spellidlepriority'; +UPDATE `bot_command_settings` SET `aliases`= 'maxhp' WHERE `bot_command`='spellmaxhppct'; +UPDATE `bot_command_settings` SET `aliases`= 'maxmana' WHERE `bot_command`='spellmaxmanapct'; +UPDATE `bot_command_settings` SET `aliases`= 'maxthresholds' WHERE `bot_command`='spellmaxthresholds'; +UPDATE `bot_command_settings` SET `aliases`= 'minhp' WHERE `bot_command`='spellminhppct'; +UPDATE `bot_command_settings` SET `aliases`= 'minmana' WHERE `bot_command`='spellminmanapct'; +UPDATE `bot_command_settings` SET `aliases`= 'minthresholds' WHERE `bot_command`='spellminthresholds'; +UPDATE `bot_command_settings` SET `aliases`= 'pursuepriority' WHERE `bot_command`='spellpursuepriority'; +UPDATE `bot_command_settings` SET `aliases`= 'targetcount' WHERE `bot_command`='spelltargetcount'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|vc') ELSE 'vc' END WHERE `bot_command`='viewcombos' AND `aliases` NOT LIKE '%vc%'; +)" + }, + ManifestEntry{ + .version = 9047, + .description = "2024_05_18_bot_update_spell_types.sql", + .check = "SELECT * FROM `bot_spells_entries` WHERE `type` > 21", + .condition = "not_empty", + .match = "", + .sql = R"( +UPDATE `bot_spells_entries` SET `type` = 0 WHERE `type` = 1; +UPDATE `bot_spells_entries` SET `type` = 1 WHERE `type` = 2; +UPDATE `bot_spells_entries` SET `type` = 2 WHERE `type` = 4; +UPDATE `bot_spells_entries` SET `type` = 3 WHERE `type` = 8; +UPDATE `bot_spells_entries` SET `type` = 4 WHERE `type` = 16; +UPDATE `bot_spells_entries` SET `type` = 5 WHERE `type` = 32; +UPDATE `bot_spells_entries` SET `type` = 6 WHERE `type` = 64; +UPDATE `bot_spells_entries` SET `type` = 7 WHERE `type` = 128; +UPDATE `bot_spells_entries` SET `type` = 8 WHERE `type` = 256; +UPDATE `bot_spells_entries` SET `type` = 9 WHERE `type` = 512; +UPDATE `bot_spells_entries` SET `type` = 10 WHERE `type` = 1024; +UPDATE `bot_spells_entries` SET `type` = 11 WHERE `type` = 2048; +UPDATE `bot_spells_entries` SET `type` = 12 WHERE `type` = 4096; +UPDATE `bot_spells_entries` SET `type` = 13 WHERE `type` = 8192; +UPDATE `bot_spells_entries` SET `type` = 14 WHERE `type` = 16384; +UPDATE `bot_spells_entries` SET `type` = 15 WHERE `type` = 32768; +UPDATE `bot_spells_entries` SET `type` = 16 WHERE `type` = 65536; +UPDATE `bot_spells_entries` SET `type` = 17 WHERE `type` = 131072; +UPDATE `bot_spells_entries` SET `type` = 18 WHERE `type` = 262144; +UPDATE `bot_spells_entries` SET `type` = 19 WHERE `type` = 524288; +UPDATE `bot_spells_entries` SET `type` = 20 WHERE `type` = 1048576; +UPDATE `bot_spells_entries` SET `type` = 21 WHERE `type` = 2097152; +)" + }, + ManifestEntry{ + .version = 9048, + .description = "2024_05_18_bot_fear_spell_type.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 22", + .condition = "empty", + .match = "", + .sql = R"( +UPDATE bot_spells_entries b, spells_new s +SET b.`type` = 22 +WHERE b.spellid = s.id +AND ( + s.`effectid1` = 23 OR + s.`effectid2` = 23 OR + s.`effectid3` = 23 OR + s.`effectid4` = 23 OR + s.`effectid5` = 23 OR + s.`effectid6` = 23 OR + s.`effectid7` = 23 OR + s.`effectid8` = 23 OR + s.`effectid9` = 23 OR + s.`effectid10` = 23 OR + s.`effectid11` = 23 OR + s.`effectid12` = 23 + ); +)" + }, + ManifestEntry{ + .version = 9049, + .description = "2024_05_18_correct_bot_spell_entries_types.sql", + .check = "SELECT * FROM `bot_spells_entries` where `npc_spells_id` = 3002 AND `spellid` = 14312", + .condition = "empty", + .match = "", + .sql = R"( +-- Class fixes +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14312; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14313; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14314; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15186; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15187; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15188; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14446; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14447; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14467; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14468; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14469; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14955; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14956; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14387; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14388; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14389; + +-- Minlevel fixes +UPDATE bot_spells_entries SET `minlevel` = 34 WHERE `spellid` = 1445 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 2 WHERE `spellid` = 229 AND `npc_spells_id` = 3011; +UPDATE bot_spells_entries SET `minlevel` = 13 WHERE `spellid` = 333 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 29 WHERE `spellid` = 106 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 38 WHERE `spellid` = 754 AND `npc_spells_id` = 3010; +UPDATE bot_spells_entries SET `minlevel` = 58 WHERE `spellid` = 2589 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 67 WHERE `spellid` = 5305 AND `npc_spells_id` = 3004; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 23 WHERE `spellid` = 738 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 51 WHERE `spellid` = 1751 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 7 WHERE `spellid` = 734 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 5 WHERE `spellid` = 717 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15186 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15187 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15188 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14955 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14956 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 78 WHERE `spellid` = 14387 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14388 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14389 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; + +-- Maxlevel fixes +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; + +-- Type fixes +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 201; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 752; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 2117; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2542; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2544; +UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 2115; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1403; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1405; +UPDATE bot_spells_entries SET `type` = 9 WHERE `spellid` = 289; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 294; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 302; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 521; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 185; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 450; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 4074; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 195; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1712; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1703; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 3229; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 3345; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5509; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6826; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 281; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 505; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 526; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 110; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 506; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 162; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 111; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 507; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 527; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 163; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 112; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1588; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1573; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1592; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1577; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1578; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 1576; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3386; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3387; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 4900; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3395; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5394; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5392; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6827; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 5416; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1437; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1436; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5348; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 8008; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 2571; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 370; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1741; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1296; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2634; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2942; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 3462; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6828; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14312; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14313; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14314; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18392; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18393; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18394; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 15186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15187; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15188; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14446; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14447; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14467; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14468; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14469; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14267; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14268; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14269; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14955; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14956; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 14387; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14388; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14389; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 10436; + +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3440; -- Ro's Illumination [#3440] from DoT [#8] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 303; -- Whirl till you hurl [#303] from Nuke [#0] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 619; -- Dyn's Dizzying Draught [#619] from Nuke [#0] to Debuff [#14] [Should be 0] + +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 74; -- Mana Sieve [#74] from Nuke [#0] to Debuff [#14] +-- UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 1686; -- Theft of Thought [#1686] from Nuke [#0] to Lifetap [#6] + +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 3694; -- Stoicism [#3694] from In-Combat Buff [#10] to Regular Heal [#1] +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 4899; -- Breath of Trushar [#4899] from In-Combat Buff [#10] to Regular Heal [#1] + +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1748; -- Angstlich's Assonance [#1748] from Slow [#13] to DoT [#8] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] + )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/database_schema.h b/common/database_schema.h index e0b87b94d3..6bf75b6d1e 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -415,6 +415,7 @@ namespace DatabaseSchema { "bot_pet_buffs", "bot_pet_inventories", "bot_pets", + "bot_settings", "bot_spell_casting_chances", "bot_spell_settings", "bot_spells_entries", diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 8bf474f349..0fa85b75ab 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -142,6 +142,13 @@ namespace Logs { EqTime, Corpses, XTargets, + BotSettings, + BotPreChecks, + BotHoldChecks, + BotDelayChecks, + BotThresholdChecks, + BotSpellTypeChecks, + TestDebug, MaxCategoryID /* Don't Remove this */ }; @@ -242,7 +249,14 @@ namespace Logs { "Zoning", "EqTime", "Corpses", - "XTargets" + "XTargets", + "Bot Settings", + "Bot Pre Checks", + "Bot Hold Checks", + "Bot Delay Checks", + "Bot Threshold Checks", + "Bot Spell Type Checks", + "Test Debug" }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 10c9cd98a2..fdd33be14d 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -844,6 +844,76 @@ OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogBotSettings(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotSettings))\ + OutF(LogSys, Logs::General, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotSettingsDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSettings))\ + OutF(LogSys, Logs::Detail, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotPreChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotPreChecks))\ + OutF(LogSys, Logs::General, Logs::BotPreChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotPreChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotPreChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotPreChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotHoldChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotHoldChecks))\ + OutF(LogSys, Logs::General, Logs::BotHoldChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotHoldChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotHoldChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotHoldChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotDelayChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotDelayChecks))\ + OutF(LogSys, Logs::General, Logs::BotDelayChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotDelayChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotDelayChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotDelayChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotThresholdChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotThresholdChecks))\ + OutF(LogSys, Logs::General, Logs::BotThresholdChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotThresholdChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotThresholdChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotThresholdChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotSpellTypeChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotSpellTypeChecks))\ + OutF(LogSys, Logs::General, Logs::BotSpellTypeChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotSpellTypeChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSpellTypeChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotSpellTypeChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTestDebug(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::TestDebug))\ + OutF(LogSys, Logs::General, Logs::TestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTestDebugDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::TestDebug))\ + OutF(LogSys, Logs::Detail, Logs::TestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.IsLogEnabled(debug_level, log_category))\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/repositories/base/base_bot_data_repository.h b/common/repositories/base/base_bot_data_repository.h index 96622c2db8..eb0cdcb991 100644 --- a/common/repositories/base/base_bot_data_repository.h +++ b/common/repositories/base/base_bot_data_repository.h @@ -64,13 +64,6 @@ class BaseBotDataRepository { int16_t poison; int16_t disease; int16_t corruption; - uint32_t show_helm; - uint32_t follow_distance; - uint8_t stop_melee_level; - int32_t expansion_bitmask; - uint8_t enforce_spell_settings; - uint8_t archery_setting; - uint32_t caster_range; }; static std::string PrimaryKey() @@ -126,13 +119,6 @@ class BaseBotDataRepository { "poison", "disease", "corruption", - "show_helm", - "follow_distance", - "stop_melee_level", - "expansion_bitmask", - "enforce_spell_settings", - "archery_setting", - "caster_range", }; } @@ -184,13 +170,6 @@ class BaseBotDataRepository { "poison", "disease", "corruption", - "show_helm", - "follow_distance", - "stop_melee_level", - "expansion_bitmask", - "enforce_spell_settings", - "archery_setting", - "caster_range", }; } @@ -276,13 +255,7 @@ class BaseBotDataRepository { e.poison = 0; e.disease = 0; e.corruption = 0; - e.show_helm = 0; - e.follow_distance = 200; - e.stop_melee_level = 255; - e.expansion_bitmask = -1; - e.enforce_spell_settings = 0; - e.archery_setting = 0; - e.caster_range = 300; + return e; } @@ -364,13 +337,6 @@ class BaseBotDataRepository { e.poison = row[42] ? static_cast(atoi(row[42])) : 0; e.disease = row[43] ? static_cast(atoi(row[43])) : 0; e.corruption = row[44] ? static_cast(atoi(row[44])) : 0; - e.show_helm = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; - e.follow_distance = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 200; - e.stop_melee_level = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 255; - e.expansion_bitmask = row[48] ? static_cast(atoi(row[48])) : -1; - e.enforce_spell_settings = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; - e.archery_setting = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; - e.caster_range = row[51] ? static_cast(strtoul(row[51], nullptr, 10)) : 300; return e; } @@ -448,13 +414,6 @@ class BaseBotDataRepository { v.push_back(columns[42] + " = " + std::to_string(e.poison)); v.push_back(columns[43] + " = " + std::to_string(e.disease)); v.push_back(columns[44] + " = " + std::to_string(e.corruption)); - v.push_back(columns[45] + " = " + std::to_string(e.show_helm)); - v.push_back(columns[46] + " = " + std::to_string(e.follow_distance)); - v.push_back(columns[47] + " = " + std::to_string(e.stop_melee_level)); - v.push_back(columns[48] + " = " + std::to_string(e.expansion_bitmask)); - v.push_back(columns[49] + " = " + std::to_string(e.enforce_spell_settings)); - v.push_back(columns[50] + " = " + std::to_string(e.archery_setting)); - v.push_back(columns[51] + " = " + std::to_string(e.caster_range)); auto results = db.QueryDatabase( fmt::format( @@ -521,13 +480,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); auto results = db.QueryDatabase( fmt::format( @@ -602,13 +554,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -687,13 +632,6 @@ class BaseBotDataRepository { e.poison = row[42] ? static_cast(atoi(row[42])) : 0; e.disease = row[43] ? static_cast(atoi(row[43])) : 0; e.corruption = row[44] ? static_cast(atoi(row[44])) : 0; - e.show_helm = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; - e.follow_distance = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 200; - e.stop_melee_level = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 255; - e.expansion_bitmask = row[48] ? static_cast(atoi(row[48])) : -1; - e.enforce_spell_settings = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; - e.archery_setting = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; - e.caster_range = row[51] ? static_cast(strtoul(row[51], nullptr, 10)) : 300; all_entries.push_back(e); } @@ -763,13 +701,6 @@ class BaseBotDataRepository { e.poison = row[42] ? static_cast(atoi(row[42])) : 0; e.disease = row[43] ? static_cast(atoi(row[43])) : 0; e.corruption = row[44] ? static_cast(atoi(row[44])) : 0; - e.show_helm = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; - e.follow_distance = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 200; - e.stop_melee_level = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 255; - e.expansion_bitmask = row[48] ? static_cast(atoi(row[48])) : -1; - e.enforce_spell_settings = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; - e.archery_setting = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; - e.caster_range = row[51] ? static_cast(strtoul(row[51], nullptr, 10)) : 300; all_entries.push_back(e); } @@ -889,13 +820,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); auto results = db.QueryDatabase( fmt::format( @@ -963,13 +887,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/base/base_bot_settings_repository.h b/common/repositories/base/base_bot_settings_repository.h new file mode 100644 index 0000000000..2018177dc9 --- /dev/null +++ b/common/repositories/base/base_bot_settings_repository.h @@ -0,0 +1,464 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H +#define EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseBotSettingsRepository { +public: + struct BotSettings { + uint32_t id; + uint32_t char_id; + uint32_t bot_id; + uint16_t setting_id; + uint8_t setting_type; + int32_t value; + std::string category_name; + std::string setting_name; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "char_id", + "bot_id", + "setting_id", + "setting_type", + "value", + "category_name", + "setting_name", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "char_id", + "bot_id", + "setting_id", + "setting_type", + "value", + "category_name", + "setting_name", + }; + } + + 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_settings"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static BotSettings NewEntity() + { + BotSettings e{}; + + e.id = 0; + e.char_id = 0; + e.bot_id = 0; + e.setting_id = 0; + e.setting_type = 0; + e.value = 0; + e.category_name = ""; + e.setting_name = ""; + + return e; + } + + static BotSettings GetBotSettings( + const std::vector &bot_settingss, + int bot_settings_id + ) + { + for (auto &bot_settings : bot_settingss) { + if (bot_settings.id == bot_settings_id) { + return bot_settings; + } + } + + return NewEntity(); + } + + static BotSettings FindOne( + Database& db, + int bot_settings_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + bot_settings_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + BotSettings e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int bot_settings_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + bot_settings_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const BotSettings &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = " + std::to_string(e.id)); + v.push_back(columns[1] + " = " + std::to_string(e.char_id)); + v.push_back(columns[2] + " = " + std::to_string(e.bot_id)); + v.push_back(columns[3] + " = " + std::to_string(e.setting_id)); + v.push_back(columns[4] + " = " + std::to_string(e.setting_type)); + v.push_back(columns[5] + " = " + std::to_string(e.value)); + v.push_back(columns[6] + " = '" + Strings::Escape(e.category_name) + "'"); + v.push_back(columns[7] + " = '" + Strings::Escape(e.setting_name) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.bot_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static BotSettings InsertOne( + Database& db, + BotSettings e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + 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 &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + BotSettings e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector 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) { + BotSettings e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const BotSettings &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H diff --git a/common/repositories/bot_data_repository.h b/common/repositories/bot_data_repository.h index f6508479f1..e8d0038a44 100644 --- a/common/repositories/bot_data_repository.h +++ b/common/repositories/bot_data_repository.h @@ -44,46 +44,6 @@ class BotDataRepository: public BaseBotDataRepository { */ // Custom extended repository methods here - static bool SaveAllHelmAppearances(Database& db, const uint32 owner_id, const bool show_flag) - { - auto results = db.QueryDatabase( - fmt::format( - "UPDATE `{}` SET `show_helm` = {} WHERE `owner_id` = {}", - TableName(), - show_flag ? 1 : 0, - owner_id - ) - ); - - return results.Success(); - } - - static bool ToggleAllHelmAppearances(Database& db, const uint32 owner_id) - { - auto results = db.QueryDatabase( - fmt::format( - "UPDATE `{}` SET `show_helm` = (`show_helm` XOR '1') WHERE `owner_id` = {}", - TableName(), - owner_id - ) - ); - - return results.Success(); - } - - static bool SaveAllFollowDistances(Database& db, const uint32 owner_id, const uint32 follow_distance) - { - auto results = db.QueryDatabase( - fmt::format( - "UPDATE `{}` SET `follow_distance` = {} WHERE `owner_id` = {}", - TableName(), - follow_distance, - owner_id - ) - ); - - return results.Success(); - } }; #endif //EQEMU_BOT_DATA_REPOSITORY_H diff --git a/common/repositories/bot_settings_repository.h b/common/repositories/bot_settings_repository.h new file mode 100644 index 0000000000..7cf1d0dbd1 --- /dev/null +++ b/common/repositories/bot_settings_repository.h @@ -0,0 +1,50 @@ +#ifndef EQEMU_BOT_SETTINGS_REPOSITORY_H +#define EQEMU_BOT_SETTINGS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_bot_settings_repository.h" + +class BotSettingsRepository: public BaseBotSettingsRepository { +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 + * + * BotSettingsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * BotSettingsRepository::GetWhereNeverExpires() + * BotSettingsRepository::GetWhereXAndY() + * BotSettingsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_BOT_SETTINGS_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index a428325271..aec9bcb8b1 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -377,6 +377,9 @@ RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining wheth RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging") RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply") RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position") +RULE_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.") +RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.") +RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.") RULE_CATEGORY_END() RULE_CATEGORY(Pathing) @@ -747,6 +750,7 @@ RULE_INT(Bots, CommandSpellRank, 1, "Filters bot command spells by rank. 1, 2 an RULE_INT(Bots, CreationLimit, 150, "Number of bots that each account can create") RULE_BOOL(Bots, FinishBuffing, false, "Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat") RULE_BOOL(Bots, GroupBuffing, false, "Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB") +RULE_BOOL(Bots, RaidBuffing, false, "Bots will cast single target buffs as raid buffs, default is false for single. Does not make single target buffs work for MGB") RULE_INT(Bots, HealRotationMaxMembers, 24, "Maximum number of heal rotation members") RULE_INT(Bots, HealRotationMaxTargets, 12, "Maximum number of heal rotation targets") RULE_REAL(Bots, ManaRegen, 2.0, "Adjust mana regen. Acts as a final multiplier, stacks with Rule Character:ManaRegenMultiplier.") @@ -776,6 +780,91 @@ RULE_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to cl RULE_BOOL(Bots, BotsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.") RULE_INT(Bots, BotsHasteCap, 100, "Haste cap for non-v3(over haste) haste") RULE_INT(Bots, BotsHastev3Cap, 25, "Haste cap for v3(over haste) haste") +RULE_BOOL(Bots, CrossRaidBuffingAndHealing, true, "If True, bots will be able to cast on all raid members rather than just their raid group members. Default true.") +RULE_BOOL(Bots, CanCastIllusionsOnPets, false, "If True, bots will be able to cast spells that have an illusion effect on pets. Default false.") +RULE_BOOL(Bots, CanCastPetOnlyOnOthersPets, false, "If True, bots will be able to cast pet only spells on other's pets. Default false.") +RULE_BOOL(Bots, RequirePetAffinity, true, "If True, bots will be need to have the Pet Affinity AA to allow their pets to be hit with group spells.") +RULE_INT(Bots, SpellResistLimit, 150, "150 Default. This is the resist cap where bots will refuse to cast spells on enemies due to a high resist chance.") +RULE_INT(Bots, StunCastChanceIfCasting, 50, "50 Default. Chance for non-Paladins to cast a stun spell if the target is casting.") +RULE_INT(Bots, StunCastChanceNormal, 15, "15 Default. Chance for non-Paladins to cast a stun spell on the target.") +RULE_INT(Bots, StunCastChancePaladins, 75, "75 Default. Chance for Paladins to cast a stun spell if the target is casting.") +RULE_INT(Bots, PercentChanceToCastNuke, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") +RULE_INT(Bots, PercentChanceToCastRoot, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastBuff, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") +RULE_INT(Bots, PercentChanceToCastEscape, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastLifetap, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastSnare, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastDOT, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastHateRedux, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastFear, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastOtherType, 90, "The chance for a bot to attempt to cast the remaining spell types in combat. Default 0-%.") +RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 250, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2000ms.") +RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 125, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 200ms.") +RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MezChance, 35, "35 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.") +RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.") +RULE_INT(Bots, MezSuccessDelay, 3500, "3500 (3.5 sec) Default. Delay between successful Mez attempts.") +RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.") +RULE_INT(Bots, MezFailDelay, 2000, "2000 (2 sec) Default. Delay between failed Mez attempts.") +RULE_INT(Bots, MezAEFailDelay, 4000, "4000 (4 sec) Default. Delay between failed AEMez attempts.") +RULE_INT(Bots, MinGroupHealTargets, 3, "Minimum number of targets in valid range that are required for a group heal to cast. Default 3.") +RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range that are required for a cure heal to cast. Default 3.") +RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.") +RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.") +RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.") +RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks") +RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel") +RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") +RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") +RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.") +RULE_BOOL(Bots, AllowSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") +RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots") +RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") +RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") +RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") +RULE_INT(Bots, StackSizeMin, 100, "100 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") +RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") +RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") +RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") +RULE_REAL(Bots, PercentMinMeleeDistance, 0.60, "Multiplier of the max melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") +RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.") +RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .") +RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.25, "Multiplier of max melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") +RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.") +RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.") +RULE_BOOL(Bots, CastersStayJustOutOfMeleeRange, true, "True Default. If true, caster bots will stay just out of melee range. Otherwise they use Bots:PercentMinCasterRangeDistance.") +RULE_REAL(Bots, PercentMinCasterRangeDistance, 0.60, "Multiplier of the closest caster range at which a bot will stand while casting before trying to adjust. 0.60 Recommended.") +RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.") +RULE_REAL(Bots, DistanceTauntingBotsStickMainHate, 25.00, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") +RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, false, "False Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") +RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.") +RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.") +RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.") +RULE_BOOL(Bots, PreventBotSpawnOnFD, true, "True Default. If true, players will not be able to spawn bots while feign death.") +RULE_BOOL(Bots, PreventBotSpawnOnEngaged, true, "True Default. If true, players will not be able to spawn bots while you, your group or raid are engaged.") +RULE_BOOL(Bots, PreventBotCampOnEngaged, true, "True Default. If true, players will not be able to camp bots while you, your group or raid are engaged.") +RULE_BOOL(Bots, CopySettingsOwnBotsOnly, true, "Determines whether a bot you are copying settings from must be a bot you own or not, default true.") +RULE_BOOL(Bots, AllowCopySettingsAnon, true, "If player's are allowed to copy settings of bots owned by anonymous players.") +RULE_BOOL(Bots, AllowCharmedPetBuffs, true, "Whether or not bots are allowed to cast buff charmed pets, default true.") +RULE_BOOL(Bots, AllowCharmedPetHeals, true, "Whether or not bots are allowed to cast heal charmed pets, default true.") +RULE_BOOL(Bots, AllowCharmedPetCures, true, "Whether or not bots are allowed to cast cure charmed pets, default true.") +RULE_BOOL(Bots, ShowResistMessagesToOwner, true, "Default True. If enabled, when a bot's spell is resisted it will send a spell failure to their owner.") +RULE_BOOL(Bots, BotBuffLevelRestrictions, true, "Buffs will not land on low level bots like live players") +RULE_BOOL(Bots, BotsUseLiveBlockedMessage, false, "Setting whether detailed spell block messages should be used for bots as players do on the live servers") +RULE_BOOL(Bots, BotSoftDeletes, true, "When bots are deleted, they are only soft deleted") +RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass the anti-spam system") +RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Default 120.") +RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass the anti-spam system") +RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. Default 120.") +RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) @@ -1001,6 +1090,30 @@ RULE_CATEGORY_END() RULE_CATEGORY(Command) RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.") RULE_BOOL(Command, HideMeCommandDisablesTells, true, "Disable this to allow tells to be received when using #hideme.") +RULE_INT(Command, MaxHelpLineLength, 53, "Maximum length of a line before splitting it in to new lines for DiaWind. Default 53.") +RULE_STRING(Command, DescriptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, DescriptionHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, AltDescriptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, NoteColor, "dark_orange", "Color for command help windows") +RULE_STRING(Command, NoteHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, AltNoteColor, "dark_orange", "Color for command help windows") +RULE_STRING(Command, ExampleColor, "goldenrod", "Color for command help windows") +RULE_STRING(Command, ExampleHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, SubExampleColor, "slate_blue", "Color for command help windows") +RULE_STRING(Command, AltExampleColor, "goldenrod", "Color for command help windows") +RULE_STRING(Command, SubAltExampleColor, "goldenrod", "Color for command help windows") +RULE_STRING(Command, OptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, OptionHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, SubOptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, AltOptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, SubAltOptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, ActionableColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, ActionableHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, AltActionableColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, HeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, SecondaryHeaderColor, "slate_blue", "Color for command help windows") +RULE_STRING(Command, AltHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, FillerLineColor, "dark_grey", "Color for command help windows") RULE_CATEGORY_END() RULE_CATEGORY(Doors) diff --git a/common/spdat.cpp b/common/spdat.cpp index 3acac1c8b9..53bf03f6d3 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -125,9 +125,26 @@ bool IsMesmerizeSpell(uint16 spell_id) return IsEffectInSpell(spell_id, SE_Mez); } +bool SpellBreaksMez(uint16 spell_id) +{ + if (IsDetrimentalSpell(spell_id) && IsAnyDamageSpell(spell_id)) { + return true; + } + + return false; +} + bool IsStunSpell(uint16 spell_id) { - return IsEffectInSpell(spell_id, SE_Stun); + if (IsEffectInSpell(spell_id, SE_Stun)) { + return true; + } + + if (IsEffectInSpell(spell_id, SE_SpinTarget)) { + return true; + } + + return false; } bool IsSummonSpell(uint16 spell_id) @@ -160,6 +177,27 @@ bool IsDamageSpell(uint16 spell_id) const auto& spell = spells[spell_id]; + for (int i = 0; i < EFFECT_COUNT; i++) { + const auto effect_id = spell.effect_id[i]; + if ( + spell.base_value[i] < 0 && + (effect_id == SE_CurrentHPOnce || effect_id == SE_CurrentHP) + ) { + return true; + } + } + + return false; +} + +bool IsAnyDamageSpell(uint16 spell_id) +{ + if (IsLifetapSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + for (int i = 0; i < EFFECT_COUNT; i++) { const auto effect_id = spell.effect_id[i]; if ( @@ -169,8 +207,8 @@ bool IsDamageSpell(uint16 spell_id) ( effect_id == SE_CurrentHP && spell.buff_duration < 1 + ) ) - ) ) { return true; } @@ -179,6 +217,35 @@ bool IsDamageSpell(uint16 spell_id) return false; } +bool IsDamageOverTimeSpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + if (IsLifetapSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + if (spell.good_effect || !spell.buff_duration_formula) { + return false; + } + + for (int i = 0; i < EFFECT_COUNT; i++) { + const auto effect_id = spell.effect_id[i]; + if ( + spell.base_value[i] < 0 && + effect_id == SE_CurrentHP && + spell.buff_duration > 1 + ) { + return true; + } + } + + return false; +} bool IsFearSpell(uint16 spell_id) { @@ -409,7 +476,8 @@ bool IsSummonPetSpell(uint16 spell_id) return ( IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_SummonBSTPet) || - IsEffectInSpell(spell_id, SE_Familiar) + IsEffectInSpell(spell_id, SE_Familiar) || + IsEffectInSpell(spell_id, SE_NecPet) ); } @@ -560,12 +628,11 @@ bool IsPBAENukeSpell(uint16 spell_id) if ( IsPureNukeSpell(spell_id) && - spell.aoe_range > 0 && - spell.target_type == ST_AECaster + !IsTargetRequiredForSpell(spell_id) ) { return true; } - + return false; } @@ -588,6 +655,137 @@ bool IsAERainNukeSpell(uint16 spell_id) return false; } +bool IsAnyNukeOrStunSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if (IsSelfConversionSpell(spell_id) || IsEscapeSpell(spell_id)) { + return false; + } + + if ( + IsPBAENukeSpell(spell_id) || + IsAERainNukeSpell(spell_id) || + IsPureNukeSpell(spell_id) || + IsStunSpell(spell_id) || + (IsDamageSpell(spell_id) && !IsDamageOverTimeSpell(spell_id)) + ) { + return true; + } + + return false; +} + +bool IsAnyAESpell(uint16 spell_id) { + //if ( + // spells[spell_id].target_type == ST_Target || + // spells[spell_id].target_type == ST_Self || + // spells[spell_id].target_type == ST_Animal || + // spells[spell_id].target_type == ST_Undead || + // spells[spell_id].target_type == ST_Summoned || + // spells[spell_id].target_type == ST_Tap || + // spells[spell_id].target_type == ST_Pet || + // spells[spell_id].target_type == ST_Corpse || + // spells[spell_id].target_type == ST_Plant || + // spells[spell_id].target_type == ST_Giant || + // spells[spell_id].target_type == ST_Dragon || + // spells[spell_id].target_type == ST_HateList || + // spells[spell_id].target_type == ST_LDoNChest_Cursed || + // spells[spell_id].target_type == ST_Muramite || + // spells[spell_id].target_type == ST_SummonedPet || + // spells[spell_id].target_type == ST_TargetsTarget || + // spells[spell_id].target_type == ST_PetMaster //|| + // //spells[spell_id].target_type == ST_AEBard //TODO needed? + //) { + // return false; + //} + + if (IsAESpell(spell_id) || IsPBAENukeSpell(spell_id) || IsPBAESpell(spell_id) || IsAERainSpell(spell_id) || IsAERainNukeSpell(spell_id) || IsAEDurationSpell(spell_id)) { + return true; + } + + return false; +} + +bool IsAESpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + switch (spells[spell_id].target_type) { + case ST_TargetOptional: + case ST_GroupTeleport : + case ST_Target: + case ST_Self: + case ST_Animal: + case ST_Undead: + case ST_Summoned: + case ST_Tap: + case ST_Pet: + case ST_Corpse: + case ST_Plant: + case ST_Giant: + case ST_Dragon: + case ST_LDoNChest_Cursed: + case ST_Muramite: + case ST_SummonedPet: + case ST_GroupNoPets: + case ST_Group: + case ST_GroupClientAndPet: + case ST_TargetsTarget: + case ST_PetMaster: + return false; + default: + break; + } + + if ( + spells[spell_id].aoe_range > 0 + ) { + return true; + } + + return false; +} + +bool IsPBAESpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + if ( + spell.aoe_range > 0 && + spell.target_type == ST_AECaster + ) { + return true; + } + + return false; +} + +bool IsAERainSpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + if ( + spell.aoe_range > 0 && + spell.aoe_duration > 1000 + ) { + return true; + } + + return false; +} + bool IsPartialResistableSpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) { @@ -644,7 +842,9 @@ bool IsGroupSpell(uint16 spell_id) return ( spell.target_type == ST_AEBard || spell.target_type == ST_Group || - spell.target_type == ST_GroupTeleport + spell.target_type == ST_GroupTeleport || + spell.target_type == ST_GroupNoPets || + spell.target_type == ST_GroupClientAndPet ); } @@ -1265,6 +1465,7 @@ bool IsCompleteHealSpell(uint16 spell_id) } return false; + } bool IsFastHealSpell(uint16 spell_id) @@ -1386,20 +1587,61 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id) return false; } -bool IsRegularGroupHealSpell(uint16 spell_id) +bool IsRegularPetHealSpell(uint16 spell_id) { spell_id = ( IsEffectInSpell(spell_id, SE_CurrentHP) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHP) - ); + ); if (!spell_id) { spell_id = ( IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) + ); + } + + if (spell_id) { + if ( + spells[spell_id].target_type == ST_Pet && + !IsCompleteHealSpell(spell_id) && + !IsHealOverTimeSpell(spell_id) && + !IsGroupSpell(spell_id) + ) { + for (int i = 0; i < EFFECT_COUNT; i++) { + if ( + spells[spell_id].base_value[i] > 0 && + spells[spell_id].buff_duration == 0 && + ( + spells[spell_id].effect_id[i] == SE_CurrentHP || + spells[spell_id].effect_id[i] == SE_CurrentHPOnce + ) + ) { + return true; + } + } + } + } + + return false; +} + +bool IsRegularGroupHealSpell(uint16 spell_id) +{ + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHP) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHP) ); + + if (!spell_id) { + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) + ); } if (spell_id) { @@ -1415,8 +1657,8 @@ bool IsRegularGroupHealSpell(uint16 spell_id) ( spells[spell_id].effect_id[i] == SE_CurrentHP || spells[spell_id].effect_id[i] == SE_CurrentHPOnce - ) - ) { + ) + ) { return true; } } @@ -1429,9 +1671,14 @@ bool IsRegularGroupHealSpell(uint16 spell_id) bool IsGroupCompleteHealSpell(uint16 spell_id) { if ( - IsGroupSpell(spell_id) && - IsCompleteHealSpell(spell_id) - ) { + ( + spell_id == SPELL_COMPLETE_HEAL || + IsEffectInSpell(spell_id, SE_CompleteHeal) || + IsPercentalHealSpell(spell_id) || + GetSpellTriggerSpellID(spell_id, SE_CompleteHeal) + ) && + IsGroupSpell(spell_id) + ) { return true; } @@ -1441,9 +1688,92 @@ bool IsGroupCompleteHealSpell(uint16 spell_id) bool IsGroupHealOverTimeSpell(uint16 spell_id) { if ( - IsGroupSpell(spell_id) && - IsHealOverTimeSpell(spell_id) && - spells[spell_id].buff_duration < 10 + ( + IsEffectInSpell(spell_id, SE_HealOverTime) || + GetSpellTriggerSpellID(spell_id, SE_HealOverTime) + ) && + IsGroupSpell(spell_id) + ) { + return true; + } + + return false; +} + +bool IsAnyHealSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if (spell_id == SPELL_NATURES_RECOVERY) { + return false; + } + + //spell_id != SPELL_ADRENALINE_SWELL && + //spell_id != SPELL_ADRENALINE_SWELL_RK2 && + //spell_id != SPELL_ADRENALINE_SWELL_RK3 && + if ( + IsHealOverTimeSpell(spell_id) || + IsGroupHealOverTimeSpell(spell_id) || + IsFastHealSpell(spell_id) || + IsVeryFastHealSpell(spell_id) || + IsRegularSingleTargetHealSpell(spell_id) || + IsRegularGroupHealSpell(spell_id) || + IsCompleteHealSpell(spell_id) || + IsGroupCompleteHealSpell(spell_id) || + IsRegularPetHealSpell(spell_id) + ) { + return true; + } + + return false; +} + +bool IsAnyBuffSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + spell_id == SPELL_NATURES_RECOVERY || + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + !IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + return true; + } + + return false; +} +bool IsDispelSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + IsEffectInSpell(spell_id, SE_CancelMagic) || + IsEffectInSpell(spell_id, SE_DispelBeneficial) || + IsEffectInSpell(spell_id, SE_DispelBeneficial) + ) { + return true; + } + + return false; +} + +bool IsEscapeSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + IsInvulnerabilitySpell(spell_id) || + IsEffectInSpell(spell_id, SE_FeignDeath) || + IsEffectInSpell(spell_id, SE_DeathSave) || + IsEffectInSpell(spell_id, SE_Destroy) || + (IsEffectInSpell(spell_id, SE_WipeHateList) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_WipeHateList)] > 0) ) { return true; } @@ -1464,7 +1794,8 @@ bool IsDebuffSpell(uint16 spell_id) IsEffectInSpell(spell_id, SE_CancelMagic) || IsEffectInSpell(spell_id, SE_MovementSpeed) || IsFearSpell(spell_id) || - IsEffectInSpell(spell_id, SE_InstantHate) + IsEffectInSpell(spell_id, SE_InstantHate) || + IsEffectInSpell(spell_id, SE_TossUp) ) { return false; } @@ -1472,6 +1803,22 @@ bool IsDebuffSpell(uint16 spell_id) return true; } +bool IsHateReduxSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] < 0) || + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] < 0) || + (IsEffectInSpell(spell_id, SE_ReduceHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_ReduceHate)] < 0) + ) { + return true; + } + + return false; +} + bool IsResistDebuffSpell(uint16 spell_id) { if ( @@ -2383,7 +2730,7 @@ bool AegolismStackingIsSymbolSpell(uint16 spell_id) { if ((i < 2 && spells[spell_id].effect_id[i] != SE_CHA) || i > 3 && spells[spell_id].effect_id[i] != SE_Blank) { - return 0;; + return 0; } if (i == 2 && spells[spell_id].effect_id[i] == SE_TotalHP) { @@ -2430,3 +2777,401 @@ bool AegolismStackingIsArmorClassSpell(uint16 spell_id) { return 0; } + +int8 SpellEffectsCount(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + int8 i = 0; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (!IsBlankSpellEffect(spell_id, i)) { + ++i; + } + } + + return i; +} + +bool IsLichSpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + GetSpellTargetType(spell_id) == ST_Self && + IsEffectInSpell(spell_id, SE_CurrentMana) && + IsEffectInSpell(spell_id, SE_CurrentHP) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0 && + spells[spell_id].buff_duration > 0 + ) { + return true; + } + + return false; +} + +bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Root: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Mez: + case BotSpellTypes::Charm: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::HateRedux: + case BotSpellTypes::Fear: + case BotSpellTypes::Stun: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEMez: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + return true; + case BotSpellTypes::InCombatBuff: + if (cls == Class::ShadowKnight) { + return true; + } + + return false; + default: + return false; + } + + return false; +} + +bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::Resurrect: + return true; + case BotSpellTypes::InCombatBuff: + if (cls == Class::ShadowKnight) { + return false; + } + + return true; + default: + return false; + } + + return false; +} + +bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + return true; + default: + return false; + } + + return false; +} + +bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::Charm: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return true; + default: + return false; + } + + return false; +} + +bool IsBotSpellType(uint16 spellType) { + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && BOT_SPELL_TYPES_BENEFICIAL(spellType) && BOT_SPELL_TYPES_INNATE(spellType)) { + return true; + } + + return false; +} + +bool IsAEBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEMez: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AEDoT: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AELifetap: + case BotSpellTypes::AERoot: + return true; + default: + return false; + } + + return false; +} + +bool IsGroupBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::GroupCures: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsGroupTargetOnlyBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::GroupCures: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsPetBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsClientBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + return true; + default: + return false; + } + + return false; +} + +bool IsHealBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { + if (IsAEBotSpellType(spellType)) { // These gather their own targets later + return false; + } + + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return false; + case BotSpellTypes::InCombatBuff: + if (cls && cls == Class::ShadowKnight) { + return true; + } + + return false; + default: + return true; + } + + return true; +} + +bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls) { + switch (spellType) { + case BotSpellTypes::Escape: + if (cls == Class::ShadowKnight) { + return false; + } + + return true; + case BotSpellTypes::Pet: + return false; + default: + return true; + } + + return true; +} + +bool IsValidSpellAndLoS(uint32 spell_id, bool hasLoS) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if (!hasLoS && IsTargetRequiredForSpell(spell_id)) { + return false; + } + + return true; +} + +bool IsInstantHealSpell(uint32 spell_id) { + if (IsRegularSingleTargetHealSpell(spell_id) || IsRegularGroupHealSpell(spell_id) || IsRegularPetHealSpell(spell_id) || IsRegularGroupHealSpell(spell_id) || spell_id == SPELL_COMPLETE_HEAL) { + return true; + } + + return false; +} + +bool IsResurrectSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_Revive); +} + +bool RequiresStackCheck(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::GroupCompleteHeals: + return false; + default: + return true; + } + + return true; +} diff --git a/common/spdat.h b/common/spdat.h index d597c79faf..432774b95b 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -213,6 +213,10 @@ #define SPELL_BLOODTHIRST 8476 #define SPELL_AMPLIFICATION 2603 #define SPELL_DIVINE_REZ 2738 +#define SPELL_NATURES_RECOVERY 2520 +#define SPELL_ADRENALINE_SWELL 14445 +#define SPELL_ADRENALINE_SWELL_RK2 14446 +#define SPELL_ADRENALINE_SWELL_RK3 14447 // discipline IDs. #define DISC_UNHOLY_AURA 4520 @@ -647,14 +651,84 @@ enum SpellTypes : uint32 SpellType_PreCombatBuffSong = (1 << 21) }; -const uint32 SPELL_TYPE_MIN = (SpellType_Nuke << 1) - 1; -const uint32 SPELL_TYPE_MAX = (SpellType_PreCombatBuffSong << 1) - 1; -const uint32 SPELL_TYPE_ANY = 0xFFFFFFFF; +namespace BotSpellTypes +{ + constexpr uint16 Nuke = 0; + constexpr uint16 RegularHeal = 1; + constexpr uint16 Root = 2; + constexpr uint16 Buff = 3; + constexpr uint16 Escape = 4; + constexpr uint16 Pet = 5; + constexpr uint16 Lifetap = 6; + constexpr uint16 Snare = 7; + constexpr uint16 DOT = 8; + constexpr uint16 Dispel = 9; + constexpr uint16 InCombatBuff = 10; + constexpr uint16 Mez = 11; + constexpr uint16 Charm = 12; + constexpr uint16 Slow = 13; + constexpr uint16 Debuff = 14; + constexpr uint16 Cure = 15; + constexpr uint16 Resurrect = 16; + constexpr uint16 HateRedux = 17; + constexpr uint16 InCombatBuffSong = 18; + constexpr uint16 OutOfCombatBuffSong = 19; + constexpr uint16 PreCombatBuff = 20; + constexpr uint16 PreCombatBuffSong = 21; + constexpr uint16 Fear = 22; + constexpr uint16 Stun = 23; + constexpr uint16 GroupCures = 24; + constexpr uint16 CompleteHeal = 25; + constexpr uint16 FastHeals = 26; + constexpr uint16 VeryFastHeals = 27; + constexpr uint16 GroupHeals = 28; + constexpr uint16 GroupCompleteHeals = 29; + constexpr uint16 GroupHoTHeals = 30; + constexpr uint16 HoTHeals = 31; + constexpr uint16 AENukes = 32; + constexpr uint16 AERains = 33; + constexpr uint16 AEMez = 34; + constexpr uint16 AEStun = 35; + constexpr uint16 AEDebuff = 36; + constexpr uint16 AESlow = 37; + constexpr uint16 AESnare = 38; + constexpr uint16 AEFear = 39; + constexpr uint16 AEDispel = 40; + constexpr uint16 AERoot = 41; + constexpr uint16 AEDoT = 42; + constexpr uint16 AELifetap = 43; + constexpr uint16 PBAENuke = 44; + constexpr uint16 PetBuffs = 45; + constexpr uint16 PetRegularHeals = 46; + constexpr uint16 PetCompleteHeals = 47; + constexpr uint16 PetFastHeals = 48; + constexpr uint16 PetVeryFastHeals = 49; + constexpr uint16 PetHoTHeals = 50; + constexpr uint16 DamageShields = 51; + constexpr uint16 ResistBuffs = 52; + + constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this + constexpr uint16 END = BotSpellTypes::ResistBuffs; // Do not remove this, increment as needed +} const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); +bool BOT_SPELL_TYPES_DETRIMENTAL (uint16 spellType, uint8 cls = 0); +bool BOT_SPELL_TYPES_BENEFICIAL (uint16 spellType, uint8 cls = 0); +bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType); +bool BOT_SPELL_TYPES_INNATE (uint16 spellType); +bool IsBotSpellType (uint16 spellType); +bool IsAEBotSpellType(uint16 spellType); +bool IsGroupBotSpellType(uint16 spellType); +bool IsGroupTargetOnlyBotSpellType(uint16 spellType); +bool IsPetBotSpellType(uint16 spellType); +bool IsClientBotSpellType(uint16 spellType); +bool IsHealBotSpellType(uint16 spellType); +bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); +bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); + // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only // TODO: import sai list @@ -1503,6 +1577,7 @@ bool IsTargetableAESpell(uint16 spell_id); bool IsSacrificeSpell(uint16 spell_id); bool IsLifetapSpell(uint16 spell_id); bool IsMesmerizeSpell(uint16 spell_id); +bool SpellBreaksMez(uint16 spell_id); bool IsStunSpell(uint16 spell_id); bool IsSlowSpell(uint16 spell_id); bool IsHasteSpell(uint16 spell_id); @@ -1536,6 +1611,11 @@ bool IsPureNukeSpell(uint16 spell_id); bool IsAENukeSpell(uint16 spell_id); bool IsPBAENukeSpell(uint16 spell_id); bool IsAERainNukeSpell(uint16 spell_id); +bool IsAnyNukeOrStunSpell(uint16 spell_id); +bool IsAnyAESpell(uint16 spell_id); +bool IsAESpell(uint16 spell_id); +bool IsPBAESpell(uint16 spell_id); +bool IsAERainSpell(uint16 spell_id); bool IsPartialResistableSpell(uint16 spell_id); bool IsResistableSpell(uint16 spell_id); bool IsGroupSpell(uint16 spell_id); @@ -1545,8 +1625,11 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id); uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id); bool IsBlankSpellEffect(uint16 spell_id, int effect_index); bool IsValidSpell(uint32 spell_id); +bool IsValidSpellAndLoS(uint32 spell_id, bool hasLoS = true); bool IsSummonSpell(uint16 spell_id); bool IsDamageSpell(uint16 spell_id); +bool IsAnyDamageSpell(uint16 spell_id); +bool IsDamageOverTimeSpell(uint16 spell_i); bool IsFearSpell(uint16 spell_id); bool IsCureSpell(uint16 spell_id); bool IsHarmTouchSpell(uint16 spell_id); @@ -1585,10 +1668,16 @@ bool IsCompleteHealSpell(uint16 spell_id); bool IsFastHealSpell(uint16 spell_id); bool IsVeryFastHealSpell(uint16 spell_id); bool IsRegularSingleTargetHealSpell(uint16 spell_id); +bool IsRegularPetHealSpell(uint16 spell_id); bool IsRegularGroupHealSpell(uint16 spell_id); bool IsGroupCompleteHealSpell(uint16 spell_id); bool IsGroupHealOverTimeSpell(uint16 spell_id); +bool IsAnyHealSpell(uint16 spell_id); +bool IsAnyBuffSpell(uint16 spell_id); +bool IsDispelSpell(uint16 spell_id); +bool IsEscapeSpell(uint16 spell_id); bool IsDebuffSpell(uint16 spell_id); +bool IsHateReduxSpell(uint16 spell_id); bool IsResistDebuffSpell(uint16 spell_id); bool IsSelfConversionSpell(uint16 spell_id); bool IsBuffSpell(uint16 spell_id); @@ -1628,5 +1717,10 @@ bool IsCastRestrictedSpell(uint16 spell_id); bool IsAegolismSpell(uint16 spell_id); bool AegolismStackingIsSymbolSpell(uint16 spell_id); bool AegolismStackingIsArmorClassSpell(uint16 spell_id); +int8 SpellEffectsCount(uint16 spell_id); +bool IsLichSpell(uint16 spell_id); +bool IsInstantHealSpell(uint32 spell_id); +bool IsResurrectSpell(uint16 spell_id); +bool RequiresStackCheck(uint16 spellType); #endif diff --git a/common/version.h b/common/version.h index fbbcd0a7f0..7cf293612d 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9284 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9049 //TODO update as needed #endif diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 1bc7efa768..1a8c7b9290 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1278,6 +1278,39 @@ bool Mob::CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarge return zone->zonemap->CheckLoS(posWatcher, posTarget); } +bool Mob::CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ) { + if (zone->zonemap == nullptr) { + //not sure what the best return is on error + //should make this a database variable, but im lazy today +#ifdef LOS_DEFAULT_CAN_SEE + return(true); +#else + return(false); +#endif + } + + if (!other) { + return(true); + } + glm::vec3 myloc; + glm::vec3 oloc; + +#define LOS_DEFAULT_HEIGHT 6.0f + + oloc.x = other->GetX(); + oloc.y = other->GetY(); + oloc.z = other->GetZ() + (other->GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : other->GetSize()) / 2 * SEE_POSITION; + + myloc.x = posX; + myloc.y = posY; + myloc.z = posZ + (GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : GetSize()) / 2 * HEAD_POSITION; + +#if LOSDEBUG>=5 + LogDebug("LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}])", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize); +#endif + return zone->zonemap->CheckLoS(myloc, oloc); +} + //offensive spell aggro int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool is_proc) { @@ -1659,3 +1692,89 @@ void Mob::RogueEvade(Mob *other) return; } +bool Mob::DoLosChecks(Mob* who, Mob* other) { + if (!who->CheckLosFN(other) || !who->CheckWaterLoS(other)) { + if (who->CheckLosCheatExempt(who, other)) { + return true; + } + + if (CheckLosCheat(who, other)) { + return true; + } + + return false; + } + + return true; +} + +bool Mob::CheckLosCheat(Mob* who, Mob* other) { + if (RuleB(Map, CheckForLoSCheat)) { + auto& door_list = entity_list.GetDoorsList(); + for (auto itr : door_list) { + Doors* door = itr.second; + if (door && !door->IsDoorOpen() && (door->GetTriggerType() == 255 || door->GetLockpick() != 0 || door->GetKeyItem() != 0 || door->GetNoKeyring() != 0)) { + if (DistanceNoZ(who->GetPosition(), door->GetPosition()) <= 50) { + auto who_to_door = DistanceNoZ(who->GetPosition(), door->GetPosition()); + auto other_to_door = DistanceNoZ(other->GetPosition(), door->GetPosition()); + auto who_to_other = DistanceNoZ(who->GetPosition(), other->GetPosition()); + auto distance_difference = who_to_other - (who_to_door + other_to_door); + if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) { + LogTestDebug("CheckLosCheat failed at Door [{}], TriggerType [{}], GetLockpick [{}], GetKeyItem [{}], GetNoKeyring [{}]", door->GetDoorID(), door->GetTriggerType(), door->GetLockpick(), door->GetKeyItem(), door->GetNoKeyring()); //deleteme + return false; + } + } + } + } + } + + //if (RuleB(Map, CheckForLoSCheat)) { + // uint8 zone_id = zone->GetZoneID(); + // // ZoneID, target XYZ, my range from target + // //float zone_basic_checks[] = { 6, 36 }; + // //float zone_basic_x_coord[] = { -295, -179.908 }; + // //float zone_basic_y_coord[] = { -18, -630.708 }; + // //float zone_basic_y_coord[] = { 50.97, -69.971 }; + // //float zone_basic_range_check[] = { 21, 10 }; + // //if door and target infront, fail + // //if door and target behind, fail + // + // if (zone_id == 103) { + // Doors* door_to_check = entity_list.FindDoor(8); + // TestDebug("Entered LoSCheat for ZoneID: [{}]", zone_id); //deleteme + // glm::vec4 who_check; who_check.x = 1202; who_check.y = 559; who_check.z = -158.94; + // glm::vec4 other_check; other_check.x = 1291; other_check.y = 559; other_check.z = -158.19; + // float my_distance = DistanceNoZ(who->GetPosition(), who_check); + // float tar_distance = DistanceNoZ(other->GetPosition(), other_check); + // float my_range = 16; + // float tar_range = 75; + // if (my_distance <= my_range && tar_distance <= tar_range && !quest_manager.isdooropen(8)) { + // TestDebug("Door is NOT open"); //deleteme + // TestDebug("LoSCheat failed"); //deleteme + // return false; + // } + // TestDebug("LoS Check for ZoneID: [{}] was [{}] units for [{}], [{}] units for [{}]", zone_id, my_distance, who->GetCleanName(), tar_distance, other->GetCleanName()); //deleteme + // } + //} + return true; +} + +bool Mob::CheckLosCheatExempt(Mob* who, Mob* other) { + if (RuleB(Map, EnableLoSCheatExemptions)) { + glm::vec4 exempt_check_who; + glm::vec4 exempt_check_other; + /* This is an exmaple of how to configure exemptions for LoS checks. + if (zone->GetZoneID() == 222) { //PoEarthB + exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns + //check to be sure the player and the target are in the pit to PoEarthB + //if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) + //otherwise they can pass LoS checks even if they don't have true LoS + if (who->GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(who->GetPosition(), exempt_check_who) <= 800) { + return true; + } + } + */ + } + + return false; +} diff --git a/zone/attack.cpp b/zone/attack.cpp index 6ffe44099e..4a2e03bbf2 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1246,7 +1246,12 @@ int64 Mob::GetWeaponDamage(Mob *against, const EQ::ItemInstance *weapon_item, in return 0; } - if (!weapon_item->IsClassEquipable(GetClass())) { + if (!weapon_item->IsClassEquipable(GetClass()) && + ( + !IsBot() || + (IsBot() && !RuleB(Bots, AllowBotEquipAnyClassGear)) + ) + ) { return 0; } diff --git a/zone/bot.cpp b/zone/bot.cpp index ce7edcc822..f634eb43e7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -69,23 +69,20 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm RestRegenHP = 0; RestRegenMana = 0; RestRegenEndurance = 0; - m_enforce_spell_settings = false; - m_bot_archery_setting = false; - m_expansion_bitmask = -1; - m_bot_caster_range = 0; + SetBotID(0); SetBotSpellID(0); SetSpawnStatus(false); - SetBotCharmer(false); - SetPetChooser(false); - SetRangerAutoWeaponSelect(false); - SetTaunting(GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight); + SetBotCharmer(false); SetDefaultBotStance(); + SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == Stance::Aggressive)); - SetAltOutOfCombatBehavior(GetClass() == Class::Bard); // will need to be updated if more classes make use of this flag - SetShowHelm(true); SetPauseAI(false); + m_combat_jitter_timer.Disable(); + auto_save_timer.Disable(); + m_rogue_evade_timer.Disable(); + m_monk_evade_timer.Disable(); m_auto_defend_timer.Disable(); SetGuardFlag(false); SetHoldFlag(false); @@ -98,12 +95,12 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm m_previous_pet_order = SPO_Guard; rest_timer.Disable(); - ping_timer.Disable(); - SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT); - if (IsCasterClass(GetClass())) - SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel)); - else - SetStopMeleeLevel(255); + ping_timer.Disable(); + + LoadDefaultBotSettings(); + SetCastedSpellType(UINT16_MAX); + SetCommandedSpell(false); + //DisableBotSpellTimers(); // Do this once and only in this constructor GenerateAppearance(); @@ -131,8 +128,7 @@ Bot::Bot( uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, - NPCType *npcTypeData, - int32 expansion_bitmask + NPCType *npcTypeData ) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) { @@ -175,13 +171,12 @@ Bot::Bot( RestRegenHP = 0; RestRegenMana = 0; RestRegenEndurance = 0; - m_expansion_bitmask = expansion_bitmask; SetBotID(botID); SetBotSpellID(botSpellsID); SetSpawnStatus(false); SetBotCharmer(false); - SetPetChooser(false); - SetRangerAutoWeaponSelect(false); + SetCastedSpellType(UINT16_MAX); + SetCommandedSpell(false); bool stance_flag = false; if (!database.botdb.LoadStance(this, stance_flag) && bot_owner) { @@ -207,6 +202,10 @@ Bot::Bot( SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == Stance::Aggressive)); SetPauseAI(false); + m_combat_jitter_timer.Disable(); + auto_save_timer.Disable(); + m_rogue_evade_timer.Disable(); + m_monk_evade_timer.Disable(); m_auto_defend_timer.Disable(); SetGuardFlag(false); SetHoldFlag(false); @@ -220,11 +219,6 @@ Bot::Bot( rest_timer.Disable(); ping_timer.Disable(); - SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT); - if (IsCasterClass(GetClass())) - SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel)); - else - SetStopMeleeLevel(255); strcpy(name, GetCleanName()); @@ -235,7 +229,10 @@ Bot::Bot( EquipBot(); if (GetClass() == Class::Rogue) { - m_evade_timer.Start(); + m_rogue_evade_timer.Start(); + } + if (GetClass() == Class::Monk) { + m_monk_evade_timer.Start(); } m_CastingRoles.GroupHealer = false; @@ -251,6 +248,10 @@ Bot::Bot( LoadAAs(); + LoadDefaultBotSettings(); + + database.botdb.LoadBotSettings(this); + if (database.botdb.LoadBuffs(this)) { //reapply some buffs uint32 buff_count = GetMaxBuffSlots(); @@ -269,6 +270,10 @@ Bot::Bot( switch (spell.effect_id[x1]) { case SE_IllusionCopy: case SE_Illusion: { + if (GetIllusionBlock()) { + break; + } + if (spell.base_value[x1] == -1) { if (gender == Gender::Female) { gender = Gender::Male; @@ -538,39 +543,51 @@ void Bot::SetSuffix(std::string_view bot_suffix) { } } -uint32 Bot::GetBotArcheryRange() { +uint32 Bot::GetBotRangedValue() { const EQ::ItemInstance *range_inst = GetBotItem(EQ::invslot::slotRange); const EQ::ItemInstance *ammo_inst = GetBotItem(EQ::invslot::slotAmmo); - if (!range_inst || !ammo_inst) + if (!range_inst) return 0; const EQ::ItemData *range_item = range_inst->GetItem(); - const EQ::ItemData *ammo_item = ammo_inst->GetItem(); - if (!range_item || !ammo_item || range_item->ItemType != EQ::item::ItemTypeBow || ammo_item->ItemType != EQ::item::ItemTypeArrow) + const EQ::ItemData *ammo_item = nullptr; + + if (ammo_inst) { + ammo_item = ammo_inst->GetItem(); + } + + // Bow requires arrows + if (range_item && range_item->ItemType == EQ::item::ItemTypeBow && (!ammo_item || ammo_item->ItemType != EQ::item::ItemTypeArrow)) { return 0; + } + + // Throwing items + if (range_item && (range_item->ItemType == EQ::item::ItemTypeSmallThrowing || range_item->ItemType == EQ::item::ItemTypeLargeThrowing)) { + return range_item->Range; + } - // everything is good! - return (range_item->Range + ammo_item->Range); + // Bows and arrows + if (range_item && ammo_item && range_item->ItemType == EQ::item::ItemTypeBow && ammo_item->ItemType == EQ::item::ItemTypeArrow) { + return (range_item->Range + ammo_item->Range); + } + + return 0; } -void Bot::ChangeBotArcherWeapons(bool isArcher) { - if ((GetClass()==Class::Warrior) || (GetClass()==Class::Paladin) || (GetClass()==Class::Ranger) || (GetClass()==Class::ShadowKnight) || (GetClass()==Class::Rogue)) { - if (!isArcher) { - BotAddEquipItem(EQ::invslot::slotPrimary, GetBotItemBySlot(EQ::invslot::slotPrimary)); - BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotSecondary)); - SetAttackTimer(); - BotGroupSay(this, "My blade is ready"); - } else { - BotRemoveEquipItem(EQ::invslot::slotPrimary); - BotRemoveEquipItem(EQ::invslot::slotSecondary); - BotAddEquipItem(EQ::invslot::slotAmmo, GetBotItemBySlot(EQ::invslot::slotAmmo)); - BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotRange)); - SetAttackTimer(); - BotGroupSay(this, "My bow is true and ready"); - } +void Bot::ChangeBotRangedWeapons(bool isRanged) { + if (!isRanged) { + BotAddEquipItem(EQ::invslot::slotPrimary, GetBotItemBySlot(EQ::invslot::slotPrimary)); + BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotSecondary)); + SetAttackTimer(); + BotGroupSay(this, "My blade is ready"); + } else { + BotRemoveEquipItem(EQ::invslot::slotPrimary); + BotRemoveEquipItem(EQ::invslot::slotSecondary); + BotAddEquipItem(EQ::invslot::slotAmmo, GetBotItemBySlot(EQ::invslot::slotAmmo)); + BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotRange)); + SetAttackTimer(); + BotGroupSay(this, "My bow is true and ready"); //TODO bot rewrite - make this say throwing or bow } - else - BotGroupSay(this, "I don't know how to use a bow"); } void Bot::Sit() { @@ -1304,10 +1321,13 @@ bool Bot::IsValidName() bool Bot::IsValidName(std::string& name) { - if (name.length() < 4) + if (name.length() < 4 || name.length() > 15) { return false; - if (!isupper(name[0])) + } + + if (!isupper(name[0])) { return false; + } for (char c : name.substr(1)) { if (!RuleB(Bots, AllowCamelCaseNames) && !islower(c)) { @@ -1344,6 +1364,7 @@ bool Bot::Save() database.botdb.SaveBuffs(this); database.botdb.SaveTimers(this); database.botdb.SaveStance(this); + database.botdb.SaveBotSettings(this); if (!SavePet()) bot_owner->Message(Chat::White, "Failed to save pet for '%s'", GetCleanName()); @@ -1389,24 +1410,30 @@ bool Bot::DeleteBot() RemoveBotFromRaid(this); } - if (!database.botdb.DeleteItems(GetBotID())) { - return false; - } + if (!RuleB(Bots, BotSoftDeletes)) { + if (!database.botdb.DeleteItems(GetBotID())) { + return false; + } - if (!database.botdb.DeleteTimers(GetBotID())) { - return false; - } + if (!database.botdb.DeleteTimers(GetBotID())) { + return false; + } - if (!database.botdb.DeleteBuffs(GetBotID())) { - return false; - } + if (!database.botdb.DeleteBuffs(GetBotID())) { + return false; + } - if (!database.botdb.DeleteStance(GetBotID())) { - return false; - } + if (!database.botdb.DeleteStance(GetBotID())) { + return false; + } - if (!database.botdb.DeleteBot(GetBotID())) { - return false; + if (!database.botdb.DeleteBotSettings(GetBotID())) { + return false; + } + + if (!database.botdb.DeleteBot(GetBotID())) { + return false; + } } return true; @@ -1705,94 +1732,137 @@ void Bot::AI_Bot_Init() } void Bot::SpellProcess() { - if (spellend_timer.Check(false)) { + if (spellend_timer.Check(false)) { NPC::SpellProcess(); if (GetClass() == Class::Bard && casting_spell_id != 0) casting_spell_id = 0; } } void Bot::BotMeditate(bool isSitting) { - if (isSitting) { - if (GetManaRatio() < 99.0f || GetHPRatio() < 99.0f) { - if (!IsEngaged() && !IsSitting()) { - Sit(); - } - } else { - if (IsSitting()) { - Stand(); - } + if (GetManaRatio() < GetManaWhenToMed() || (GetHPRatio() < GetHPWhenToMed() && GetLevel() < GetStopMeleeLevel())) { + if ((!IsEngaged() || (IsEngaged() && GetMedInCombat() && !HasTargetReflection())) && !isSitting) { + Sit(); } - } else { - if (IsSitting()) { + } + else { + if (isSitting) { Stand(); } } } -void Bot::BotRangedAttack(Mob* other) { - //make sure the attack and ranged timers are up - //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow - if ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())) { - LogCombatDetail("Bot Archery attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); +void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { + if (!TargetValidation(other)) { return; } + + if (!CanDoubleAttack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { + LogCombatDetail("Bot ranged attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; } const auto rangedItem = GetBotItem(EQ::invslot::slotRange); const EQ::ItemData* RangeWeapon = nullptr; - if (rangedItem) + if (rangedItem) { RangeWeapon = rangedItem->GetItem(); + } + + if (!RangeWeapon) { + return; + } const auto ammoItem = GetBotItem(EQ::invslot::slotAmmo); const EQ::ItemData* Ammo = nullptr; if (ammoItem) Ammo = ammoItem->GetItem(); - if (!RangeWeapon || !Ammo) - return; + // Bow requires arrows + if ( + !Ammo || + (RangeWeapon && + ( + (RangeWeapon->ItemType != EQ::item::ItemTypeBow && RangeWeapon->ItemType != EQ::item::ItemTypeSmallThrowing && RangeWeapon->ItemType != EQ::item::ItemTypeLargeThrowing) || + (RangeWeapon->ItemType == EQ::item::ItemTypeBow && (Ammo->ItemType != EQ::item::ItemTypeArrow)) || + ( + (RangeWeapon->ItemType == EQ::item::ItemTypeSmallThrowing || RangeWeapon->ItemType == EQ::item::ItemTypeLargeThrowing) && + ammoItem->GetCharges() < 1 || + ( + (RuleI(Bots, StackSizeMin) != -1 && rangedItem->GetCharges() != RangeWeapon->StackSize) || + rangedItem->GetCharges() < RuleI(Bots, StackSizeMin) + ) + ) + ) + ) + ) { + if (!Ammo || ammoItem->GetCharges() < 1) { + GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + } - LogCombatDetail("Shooting [{}] with bow [{}] ([{}]) and arrow [{}] ([{}])", other->GetCleanName(), RangeWeapon->Name, RangeWeapon->ID, Ammo->Name, Ammo->ID); - if (!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) return; + } - SendItemAnimation(other, Ammo, EQ::skills::SkillArchery); - DoArcheryAttackDmg(other, rangedItem, ammoItem); // watch + LogCombatDetail("Ranged attacking [{}] with {} [{}] ([{}]){}{}{}", + other->GetCleanName(), + (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? "bow" : "throwing"), + RangeWeapon->Name, + RangeWeapon->ID, + (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? " and arrow " : ""), + (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? Ammo->Name : ""), + (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? std::to_string(Ammo->ID) : "") + ); - //break invis when you attack - if (invisible) { - LogCombatDetail("Removing invisibility due to melee attack"); - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; + if (!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { + return; } - if (invisible_undead) { - LogCombatDetail("Removing invisibility vs. undead due to melee attack"); - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } + SendItemAnimation(other, Ammo, (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? EQ::skills::SkillArchery : EQ::skills::SkillThrowing)); + if (RangeWeapon->ItemType == EQ::item::ItemTypeBow) { + DoArcheryAttackDmg(other, rangedItem, ammoItem); // watch + //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. + int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; - if (invisible_animals) { - LogCombatDetail("Removing invisibility vs. animals due to melee attack"); - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } + // Consume Ammo, unless Ammo Consumption is disabled or player has Endless Quiver + bool consumes_ammo = RuleB(Bots, BotArcheryConsumesAmmo); + if ( + consumes_ammo && + ( + RangeWeapon->ExpendableArrow || + !ChanceAvoidConsume || + (ChanceAvoidConsume < 100 && zone->random.Int(0, 99) > ChanceAvoidConsume) + ) + ) { + ammoItem->SetCharges((ammoItem->GetCharges() - 1)); + LogCombat("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); - if (spellbonuses.NegateIfCombat) - BuffFadeByEffect(SE_NegateIfCombat); + if (ammoItem->GetCharges() < 1) { + RemoveBotItemBySlot(EQ::invslot::slotAmmo); + BotRemoveEquipItem(EQ::invslot::slotAmmo); + } + } + else if (!consumes_ammo) { + LogCombat("Archery Ammo Consumption is disabled."); + } + else { + LogCombat("Endless Quiver prevented Ammo Consumption."); + } + } + else { + DoThrowingAttackDmg(other, rangedItem); // watch + // Consume Ammo, unless Ammo Consumption is disabled + if (RuleB(Bots, BotThrowingConsumesAmmo)) { + ammoItem->SetCharges((ammoItem->GetCharges() - 1)); + LogCombat("Consumed Throwing Ammo from slot {}.", EQ::invslot::slotAmmo); - if (hidden || improved_hidden) { - hidden = false; - improved_hidden = false; - auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - auto sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); + if (ammoItem->GetCharges() < 1) { + RemoveBotItemBySlot(EQ::invslot::slotAmmo); + BotRemoveEquipItem(EQ::invslot::slotAmmo); + } + } + else { + LogCombat("Throwing Ammo Consumption is disabled."); + } } + + CommonBreakInvisibleFromCombat(); } bool Bot::CheckBotDoubleAttack(bool tripleAttack) { @@ -1829,6 +1899,53 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) { return false; } +bool Bot::CheckTripleAttack() +{ + int chance; + + if (RuleB(Combat, ClassicTripleAttack)) { + if ( + GetLevel() >= 60 && + ( + GetClass() == Class::Warrior || + GetClass() == Class::Ranger || + GetClass() == Class::Monk || + GetClass() == Class::Berserker + ) + ) { + switch (GetClass()) { + case Class::Warrior: + chance = RuleI(Combat, ClassicTripleAttackChanceWarrior); + break; + case Class::Ranger: + chance = RuleI(Combat, ClassicTripleAttackChanceRanger); + break; + case Class::Monk: + chance = RuleI(Combat, ClassicTripleAttackChanceMonk); + break; + case Class::Berserker: + chance = RuleI(Combat, ClassicTripleAttackChanceBerserker); + break; + default: + break; + } + } + } + else { + chance = GetSkill(EQ::skills::SkillTripleAttack); + } + + if (chance < 1) { + return false; + } + + int inc = aabonuses.TripleAttackChance + spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; + chance = static_cast(chance * (1 + inc / 100.0f)); + chance = (chance * 100) / (chance + 800); + + return zone->random.Int(1, 100) <= chance; +} + void Bot::SetTarget(Mob *mob) { if (mob != this) { @@ -1836,13 +1953,6 @@ void Bot::SetTarget(Mob *mob) } } -void Bot::SetStopMeleeLevel(uint8 level) { - if (IsCasterClass(GetClass()) || IsHybridClass(GetClass())) - _stopMeleeLevel = level; - else - _stopMeleeLevel = 255; -} - void Bot::SetGuardMode() { StopMoving(); @@ -1861,25 +1971,6 @@ void Bot::SetHoldMode() { // AI Processing for the Bot object -constexpr float MAX_CASTER_DISTANCE[Class::PLAYER_CLASS_COUNT] = { - 0, - (34 * 34), - (24 * 24), - (28 * 28), - (26 * 26), - (42 * 42), - 0, - (30 * 30), - 0, - (38 * 38), - (54 * 54), - (48 * 48), - (52 * 52), - (50 * 50), - (32 * 32), - 0 -}; - void Bot::AI_Process() { @@ -1937,6 +2028,13 @@ void Bot::AI_Process() return; } + if (raid && r_group == RAID_GROUPLESS) { + glm::vec3 Goal(0, 0, 0); + TryNonCombatMovementChecks(bot_owner, follow_mob, Goal); + + return; + } + float fm_distance = DistanceSquared(m_Position, follow_mob->GetPosition()); float lo_distance = DistanceSquared(m_Position, leash_owner->GetPosition()); float leash_distance = RuleR(Bots, LeashDistance); @@ -1948,7 +2046,6 @@ void Bot::AI_Process() } // HEAL ROTATION CASTING CHECKS - HealRotationChecks(); if (GetAttackFlag()) { // Push owner's target onto our hate list @@ -1959,12 +2056,10 @@ void Bot::AI_Process() } //ALT COMBAT (ACQUIRE HATE) - glm::vec3 Goal(0, 0, 0); // We have aggro to choose from if (IsEngaged()) { - if (rest_timer.Enabled()) { rest_timer.Disable(); } @@ -1988,7 +2083,6 @@ void Bot::AI_Process() // DEFAULT (ACQUIRE TARGET) // VERIFY TARGET AND STANCE - auto tar = GetBotTarget(bot_owner); if (!tar) { return; @@ -2003,7 +2097,6 @@ void Bot::AI_Process() float tar_distance = DistanceSquared(m_Position, tar->GetPosition()); // TARGET VALIDATION - if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) { return; } @@ -2023,76 +2116,136 @@ void Bot::AI_Process() SendAddPlayerState(PlayerState::Aggressive); } +// COMBAT RANGE CALCS + + bool atCombatRange = false; + bool behindMob = false; + uint8 stopMeleeLevel = GetStopMeleeLevel(); + const EQ::ItemInstance* p_item; + const EQ::ItemInstance* s_item; + float melee_distance_min = 0.0f; + float melee_distance_max = 0.0f; + float melee_distance = 0.0f; + + CheckCombatRange(tar, sqrt(tar_distance), atCombatRange, behindMob, p_item, s_item, melee_distance_min, melee_distance_max, melee_distance, stopMeleeLevel); + // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { + //TODO bot rewrite - add ways to here to determine if throw stone is allowed, then check for ranged/spell/melee + if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { + return; + } - constexpr size_t PULL_AGGRO = 5225; // spells[5225]: 'Throw Stone' - 0 cast time + if (RuleB(Bots, AllowSpellPulling)) { + uint16 pullSpell = RuleI(Bots, PullSpellID); - if (tar_distance <= (spells[PULL_AGGRO].range * spells[PULL_AGGRO].range)) { + if (tar_distance <= (spells[pullSpell].range * spells[pullSpell].range)) { + StopMoving(); - StopMoving(); - CastSpell(PULL_AGGRO, tar->GetID()); - return; + if (!TargetValidation(tar)) { return; } + + CastSpell(pullSpell, tar->GetID()); + + return; + } } - } + else { + if (atCombatRange && IsBotRanged()){ + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); -// COMBAT RANGE CALCS + TryRangedAttack(tar); - bool atCombatRange; - const EQ::ItemInstance* p_item; - const EQ::ItemInstance* s_item; - CheckCombatRange(tar, tar_distance, atCombatRange, p_item, s_item); + if (!TargetValidation(tar)) { return; } + + if (CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } + + return; + } + } + } // ENGAGED AT COMBAT RANGE // We can fight if (atCombatRange) { + bool jitterCooldown = false; - if (IsMoving()) { - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - return; + if (m_combat_jitter_timer.GetRemainingTime() > 1 && m_combat_jitter_timer.Enabled()) { + jitterCooldown = true; } - if (AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { - - if (TryEvade(tar)) { - return; + if (IsMoving() || GetCombatJitterFlag() || GetCombatOutOfRangeJitterFlag()) { + if (!GetCombatJitterFlag() || !IsMoving() || GetCombatOutOfRangeJitterFlag()) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); } - if (TryFacingTarget(tar)) { + return; + } + + if (!jitterCooldown && AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { + DoCombatPositioning(tar, Goal, stopMeleeLevel, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behindMob); + return; + } + else { + if (!IsSitting() && !IsFacingMob(tar)) { + FaceTarget(tar); return; } } - if (!IsBotNonSpellFighter() && AI_EngagedCastCheck()) { + if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { return; } - // Up to this point, GetTarget() has been safe to dereference since the initial - // if (!GetTarget() || GetAppearance() == eaDead) { return false; } call. Due to the chance of the target dying and our pointer - // being nullified, we need to test it before dereferencing to avoid crashes - - if (IsBotArcher() && TryRangedAttack(tar)) { + if (IsMoving()) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); return; } - if (!IsBotArcher() && GetLevel() < GetStopMeleeLevel()) { - if (!TryClassAttacks(tar)) { + if (IsBotRanged() && ranged_timer.Check(false)) { // Can shoot mezzed, stunned and dead!? + TryRangedAttack(tar); + + if (!TargetValidation(tar)) { return; } + + if (CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } + } + else if (!IsBotRanged() && GetLevel() < stopMeleeLevel) { + if (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())) { return; } - if (!TryPrimaryWeaponAttacks(tar, p_item)) { - return; + if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) { + DoClassAttacks(tar); } - if (!TrySecondaryWeaponAttacks(tar, s_item)) { - return; + if (!TargetValidation(tar)) { return; } + + if (attack_timer.Check()) { + TryCombatProcs(p_item, tar, EQ::invslot::slotPrimary); + TriggerDefensiveProcs(tar, EQ::invslot::slotPrimary, false); + DoAttackRounds(tar, EQ::invslot::slotPrimary); + + if (TryDoubleMeleeRoundEffect()) { + DoAttackRounds(tar, EQ::invslot::slotPrimary); + } } - } - if (GetAppearance() == eaDead) { - return; + if (!TargetValidation(tar)) { return; } + + if (attack_dw_timer.Check()) { + if (CanThisClassDualWield() && CastToClient()->CheckDualWield()) { + TryCombatProcs(s_item, tar, EQ::invslot::slotSecondary); + DoAttackRounds(tar, EQ::invslot::slotSecondary); + } + } + + if (!TargetValidation(tar)) { return; } + } } @@ -2104,12 +2257,9 @@ void Bot::AI_Process() // End not in combat range - if (TryMeditate()) { - return; - } + TryMeditate(); } else { // Out-of-combat behavior - SetAttackFlag(false); SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { @@ -2142,16 +2292,19 @@ void Bot::AI_Process() if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { return; } - if (TryIdleChecks(fm_distance)) { + if (!HOLDING && AI_HasSpells() && TryIdleChecks(fm_distance)) { return; } - if (TryBardMovementCasts()) { + if (!HOLDING && AI_HasSpells() && TryBardMovementCasts()) { return; } } } bool Bot::TryBardMovementCasts() {// Basically, bard bots get a chance to cast idle spells while moving + if (HOLDING) { + return false; + } if (GetClass() == Class::Bard && IsMoving() && NOT_PASSIVE && !spellend_timer.Enabled() && AI_think_timer->Check()) { @@ -2162,7 +2315,6 @@ bool Bot::TryBardMovementCasts() {// Basically, bard bots get a chance to cast i } bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal) {// Non-engaged movement checks - if (AI_movement_timer->Check() && (!IsCasting() || GetClass() == Class::Bard)) { if (GUARDING) { Goal = GetGuardPoint(); @@ -2193,6 +2345,7 @@ bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, g } } } + return false; } @@ -2207,18 +2360,10 @@ bool Bot::TryIdleChecks(float fm_distance) { !spellend_timer.Enabled() ) { - if (NOT_PASSIVE) { - - if (!AI_IdleCastCheck() && !IsCasting() && GetClass() != Class::Bard) { - BotMeditate(true); - } - - } else { - if (GetClass() != Class::Bard) { - BotMeditate(true); - } - + if (!AI_IdleCastCheck() && !IsCasting()) { + BotMeditate(IsSitting()); } + return true; } return false; @@ -2280,30 +2425,35 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { bool Bot::TryMeditate() { if (!IsMoving() && !spellend_timer.Enabled()) { - if (GetTarget() && AI_EngagedCastCheck()) { - BotMeditate(false); - } else if (GetArchetype() == Archetype::Caster) { - BotMeditate(true); + + if (IsEngaged() && HasOrMayGetAggro(IsSitting())) { + if (IsSitting()) { + Stand(); + return false; + } } + BotMeditate(IsSitting()); + if (!(GetPlayerState() & static_cast(PlayerState::Aggressive))) { SendAddPlayerState(PlayerState::Aggressive); } return true; } + return false; } // This code actually gets processed when we are too far away from target and have not engaged yet bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { - if (AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { if (GetTarget() && !IsRooted()) { LogAIDetail("Pursuing [{}] while engaged", GetTarget()->GetCleanName()); Goal = GetTarget()->GetPosition(); + if (DistanceSquared(m_Position, Goal) <= leash_distance) { RunTo(Goal.x, Goal.y, Goal.z); - + SetCombatOutOfRangeJitter(); } else { WipeHateList(); SetTarget(nullptr); @@ -2313,8 +2463,8 @@ bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { GetPet()->SetTarget(nullptr); } } - return true; + return true; } else { if (IsMoving()) { StopMoving(); @@ -2330,252 +2480,206 @@ bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { } // This is a mob that is fleeing either because it has been feared or is low on hitpoints - AI_PursueCastCheck(); + if (!HOLDING && AI_HasSpells()) { + AI_PursueCastCheck(); + } return true; } + return false; } -bool Bot::TrySecondaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* s_item) { +void Bot::DoAttackRounds(Mob* target, int hand) { + if (!target || (target && target->IsCorpse())) { + return; + } - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (attack_dw_timer.Check() && CanThisClassDualWield()) { - const EQ::ItemData* s_itemdata = nullptr; + Attack(target, hand, false, false); - // Can only dual wield without a weapon if you're a monk - if (s_item || (GetClass() == Class::Monk)) { + bool candouble = CanThisClassDoubleAttack(); + // extra off hand non-sense, can only double with skill of 150 or above + // or you have any amount of GiveDoubleAttack + if (candouble && hand == EQ::invslot::slotSecondary) + candouble = + GetSkill(EQ::skills::SkillDoubleAttack) > 149 || + (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack) > 0; - if (s_item) { - s_itemdata = s_item->GetItem(); - } + if (candouble) { + if (CastToClient()->CheckDoubleAttack()) { + Attack(target, hand, false, false); - if (!s_itemdata) { - return false; - } + if (hand == EQ::invslot::slotPrimary) { - bool use_fist = true; - if (s_itemdata) { - use_fist = false; + if (HasTwoHanderEquipped()) { + auto extraattackchance = aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE]; + if (extraattackchance && zone->random.Roll(extraattackchance)) { + auto extraattackamt = std::max({ aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); + for (int i = 0; i < extraattackamt; i++) { + Attack(target, hand, false, false); + } + } + } + else { + auto extraattackchance_primary = aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE]; + if (extraattackchance_primary && zone->random.Roll(extraattackchance_primary)) { + auto extraattackamt_primary = std::max({ aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); + for (int i = 0; i < extraattackamt_primary; i++) { + Attack(target, hand, false, false); + } + } + } } - if (use_fist || !s_itemdata->IsType2HWeapon()) { - - float DualWieldProbability = 0.0f; - - int32 Ambidexterity = (aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity); - DualWieldProbability = ((GetSkill(EQ::skills::SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f); // 78.0 max chance - - int32 DWBonus = (spellbonuses.DualWieldChance + itembonuses.DualWieldChance); - DualWieldProbability += (DualWieldProbability * float(DWBonus) / 100.0f); + if (hand == EQ::invslot::slotSecondary) { + auto extraattackchance_secondary = aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE]; + if (extraattackchance_secondary && zone->random.Roll(extraattackchance_secondary)) { + auto extraattackamt_secondary = std::max({ aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); + for (int i = 0; i < extraattackamt_secondary; i++) { + Attack(target, hand, false, false); + } + } + } - float random = zone->random.Real(0, 1); - if (random < DualWieldProbability) { // Max 78% for DW chance - Attack(tar, EQ::invslot::slotSecondary); // Single attack with offhand + // you can only triple from the main hand + if (hand == EQ::invslot::slotPrimary && CanThisClassTripleAttack()) { + if (CheckTripleAttack()) { + Attack(target, hand, false, false); + int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance + + itembonuses.FlurryChance; - if (GetAppearance() == eaDead) { return false; } - TryCombatProcs(s_item, tar, EQ::invslot::slotSecondary); + if (flurry_chance && zone->random.Roll(flurry_chance)) { + Attack(target, hand, false, false); - if (GetAppearance() == eaDead) { return false; } - if (CanThisClassDoubleAttack() && CheckBotDoubleAttack() && tar->GetHP() > -10) { - Attack(tar, EQ::invslot::slotSecondary); // Single attack with offhand + if (zone->random.Roll(flurry_chance)) { + Attack(target, hand, false, false); + } + //MessageString(Chat::NPCFlurry, YOU_FLURRY); //TODO bot rewrite - add output to others hits with flurry message } } } } } - return true; } -bool Bot::TryPrimaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* p_item) { - - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (attack_timer.Check()) { // Process primary weapon attacks +bool Bot::TryRangedAttack(Mob* tar) { - Attack(tar, EQ::invslot::slotPrimary); + if (IsBotRanged() && ranged_timer.Check(false)) { - if (GetAppearance() == eaDead) { return false; } - TriggerDefensiveProcs(tar, EQ::invslot::slotPrimary, false); + if (!TargetValidation(tar)) { return false; } - if (GetAppearance() == eaDead) { return false; } - TryCombatProcs(p_item, tar, EQ::invslot::slotPrimary); + if (GetPullingFlag() || GetTarget()->GetHPRatio() <= 99.0f) { + BotRangedAttack(tar); + } - if (GetAppearance() == eaDead) { return false; } - if (CanThisClassDoubleAttack()) { + return true; + } - if (CheckBotDoubleAttack()) { - Attack(tar, EQ::invslot::slotPrimary, true); - } + return false; +} - if (GetAppearance() == eaDead) { return false; } - if (GetSpecialAbility(SpecialAbility::TripleAttack) && CheckBotDoubleAttack(true)) { +bool Bot::TryFacingTarget(Mob* tar) { + if (!IsSitting() && !IsFacingMob(tar)) { + FaceTarget(tar); + return true; + } + return false; +} - Attack(tar, EQ::invslot::slotPrimary, true); - } - if (GetAppearance() == eaDead) { return false; } - // quad attack, does this belong here?? - if (GetSpecialAbility(SpecialAbility::QuadrupleAttack) && CheckBotDoubleAttack(true)) { - Attack(tar, EQ::invslot::slotPrimary, true); - } - } +bool Bot::TryEvade(Mob* tar) { + if (HasTargetReflection() && !tar->IsFeared() && !tar->IsStunned()) { + if (GetClass() == Class::Rogue && !GetSpellHold(BotSpellTypes::Escape)) { + if (m_rogue_evade_timer.Check(false)) { + int timer_duration = (HideReuseTime - GetSkillReuseTime(EQ::skills::SkillHide)) * 1000; - if (GetAppearance() == eaDead) { return false; } - // Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - if (int32 flurrychance = (aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance)) { + if (timer_duration < 0) { + timer_duration = 0; + } - if (zone->random.Int(0, 100) < flurrychance) { + m_rogue_evade_timer.Start(timer_duration); + BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); - MessageString(Chat::NPCFlurry, YOU_FLURRY); - Attack(tar, EQ::invslot::slotPrimary, false); + if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) { + //SendAppearancePacket(AT_Invis, Invisibility::Invisible); + RogueEvade(tar); + } - if (GetAppearance() == eaDead) { return false; } - Attack(tar, EQ::invslot::slotPrimary, false); + //SendAppearancePacket(AT_Invis, Invisibility::Visible); + return true; } } + else if (GetClass() == Class::Monk && GetLevel() >= 17 && !GetSpellHold(BotSpellTypes::Escape)) { + if (m_monk_evade_timer.Check(false)) { + int timer_duration = (FeignDeathReuseTime - GetSkillReuseTime(EQ::skills::SkillFeignDeath)) * 1000; - if (GetAppearance() == eaDead) { return false; } - auto ExtraAttackChanceBonus = - (spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] + - aabonuses.ExtraAttackChance[0]); - - if ( - ExtraAttackChanceBonus && - p_item && - p_item->GetItem()->IsType2HWeapon() && - zone->random.Int(0, 100) < ExtraAttackChanceBonus - ) { - Attack(tar, EQ::invslot::slotPrimary, false); - } - } - return true; -} - -// We can't fight if we don't have a target, are stun/mezzed or dead.. -bool Bot::TryClassAttacks(Mob* tar) { - -// Stop attacking if the target is enraged - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())) { - return false; - } - - // First, special attack per class (kick, backstab etc..) - DoClassAttacks(tar); - return true; -} + if (timer_duration < 0) { + timer_duration = 0; + } -bool Bot::TryRangedAttack(Mob* tar) { + m_monk_evade_timer.Start(timer_duration); + BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); - if (IsBotArcher() && ranged_timer.Check(false)) { + if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillFeignDeath)) { + //SendAppearancePacket(AT_Anim, ANIM_DEATH); + entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); + } + else { + SetFeigned(true); + //SendAppearancePacket(AT_Anim, ANIM_DEATH); + } - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (GetTarget()->GetHPRatio() <= 99.0f) { - BotRangedAttack(tar); + //SendAppearancePacket(AT_Anim, ANIM_STAND); + SetFeigned(false); + return true; + } } - return true; } - return false; -} - -bool Bot::TryFacingTarget(Mob* tar) { - if (!IsSitting() && !IsFacingMob(tar)) { - FaceTarget(tar); - return true; - } return false; } +void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance_max, float& melee_distance, uint8 stopMeleeLevel) { + atCombatRange= false; -bool Bot::TryEvade(Mob* tar) { - - if ( - !IsRooted() && - HasTargetReflection() && - !tar->IsFeared() && - !tar->IsStunned() && - GetClass() == Class::Rogue && - m_evade_timer.Check(false) - ) { - int timer_duration = (HideReuseTime - GetSkillReuseTime(EQ::skills::SkillHide)) * 1000; - - if (timer_duration < 0) { - timer_duration = 0; - } - - m_evade_timer.Start(timer_duration); - if (zone->random.Int(0, 260) < (int) GetSkill(EQ::skills::SkillHide)) { - RogueEvade(tar); - } - return true; - } - - return false; -} - -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item) { + p_item = GetBotItem(EQ::invslot::slotPrimary); + s_item = GetBotItem(EQ::invslot::slotSecondary); - atCombatRange= false; - p_item= GetBotItem(EQ::invslot::slotPrimary); - s_item= GetBotItem(EQ::invslot::slotSecondary); - bool behind_mob = false; bool backstab_weapon = false; - if (GetClass() == Class::Rogue) { - behind_mob = BehindMob(tar, GetX(), GetY()); // Can be separated for other future use - backstab_weapon = p_item && p_item->GetItemBackstabDamage(); + if (GetBehindMob()) { + behindMob = BehindMob(tar, GetX(), GetY()); // Can be separated for other future use + if (GetClass() == Class::Rogue) { + backstab_weapon = p_item && p_item->GetItemBackstabDamage(); + } } // Calculate melee distances - float melee_distance_max = 0.0f; - float melee_distance = 0.0f; - - CalcMeleeDistances(tar, p_item, s_item, behind_mob, backstab_weapon, melee_distance_max, melee_distance); - - float caster_distance_max = GetBotCasterMaxRange(melee_distance_max); - - bool atArcheryRange = IsArcheryRange(tar); - - SetRangerCombatWeapon(atArcheryRange); - - bool stop_melee_level = GetLevel() >= GetStopMeleeLevel(); - if (IsBotArcher() && atArcheryRange) { - atCombatRange = true; - } - else if (caster_distance_max && tar_distance <= caster_distance_max && stop_melee_level) { - atCombatRange = true; - } - else if (tar_distance <= melee_distance) { + CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_max, melee_distance, melee_distance_min, stopMeleeLevel); + + //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be {} [{} - {}] away." + // , GetCleanName() + // , (tar_distance <= melee_distance ? "within range of" : "too far away from") + // , tar->GetCleanName() + // , tar_distance + // , (tar_distance <= melee_distance ? "but only needed" : "but need to be") + // , melee_distance + // , melee_distance_min + // , melee_distance_max + //); //deleteme + + if (tar_distance <= melee_distance) { atCombatRange = true; } } -void Bot::SetRangerCombatWeapon(bool atArcheryRange) { - - if (GetRangerAutoWeaponSelect()) { - bool changeWeapons = false; - - if (atArcheryRange && !IsBotArcher()) { - SetBotArcherySetting(true); - changeWeapons = true; - } - - else if (!atArcheryRange && IsBotArcher()) { - SetBotArcherySetting(false); - changeWeapons = true; - } - - if (changeWeapons) { - ChangeBotArcherWeapons(IsBotArcher()); - } - } -} - -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behind_mob, bool backstab_weapon, float& melee_distance_max, float& melee_distance) const { - +void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_max, float& melee_distance, float& melee_distance_min, uint8 stopMeleeLevel) { float size_mod = GetSize(); float other_size_mod = tar->GetSize(); + bool resquareDistance = false; // For races with a fixed size if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) { @@ -2611,58 +2715,115 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it size_mod *= (size_mod * 4.0f); } + if (tar->GetRace() == Race::VeliousDragon) // Lord Vyemm and other velious dragons + { + size_mod *= 1.75; + } + if (tar->GetRace() == Race::DragonSkeleton) // Dracoliche in Fear. Skeletal Dragon + { + size_mod *= 2.25; + } + + size_mod *= RuleR(Combat, HitBoxMod); // used for testing sizemods on different races. + // Prevention of ridiculously sized hit boxes if (size_mod > 10000.0f) { size_mod = (size_mod / 7.0f); } melee_distance_max = size_mod; + if (!RuleB(Bots, UseFlatNormalMeleeRange)) { + switch (GetClass()) { + case Class::Warrior: + case Class::Paladin: + case Class::ShadowKnight: + if (p_item && p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.45f; + } + else if ((s_item && s_item->GetItem()->IsTypeShield()) || (!p_item && !s_item)) { + melee_distance = melee_distance_max * 0.35f; + } + else { + melee_distance = melee_distance_max * 0.40f; + } + break; + case Class::Necromancer: + case Class::Wizard: + case Class::Magician: + case Class::Enchanter: + if (p_item && p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.95f; + } + else { + melee_distance = melee_distance_max * 0.75f; + } + break; + case Class::Rogue: + if (behindMob && backstab_weapon) { + if (p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.30f; + } + else { + melee_distance = melee_distance_max * 0.25f; + } + break; + } + // Fall-through + default: + if (p_item && p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.70f; + } + else { + melee_distance = melee_distance_max * 0.50f; + } - switch (GetClass()) { - case Class::Warrior: - case Class::Paladin: - case Class::ShadowKnight: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.45f; - } - else if ((s_item && s_item->GetItem()->IsTypeShield()) || (!p_item && !s_item)) { - melee_distance = melee_distance_max * 0.35f; - } - else { - melee_distance = melee_distance_max * 0.40f; - } - break; - case Class::Necromancer: - case Class::Wizard: - case Class::Magician: - case Class::Enchanter: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.95f; - } - else { - melee_distance = melee_distance_max * 0.75f; - } - break; - case Class::Rogue: - if (behind_mob && backstab_weapon) { - if (p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.30f; - } - else { - melee_distance = melee_distance_max * 0.25f; - } - break; + break; } - // Fall-through - default: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.70f; + + melee_distance = sqrt(melee_distance); + melee_distance_max = sqrt(melee_distance_max); + } + else { + melee_distance_max = sqrt(melee_distance_max); + melee_distance = melee_distance_max * RuleR(Bots, NormalMeleeRangeDistance); + } + + if (melee_distance > RuleR(Bots, MaxDistanceForMelee)) { + melee_distance = RuleR(Bots, MaxDistanceForMelee); + } + + melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMeleeDistance); + + if (taunting) { + melee_distance_min = melee_distance_max * RuleR(Bots, PercentTauntMinMeleeDistance); + melee_distance = melee_distance_max * RuleR(Bots, TauntNormalMeleeRangeDistance); + } + + if (!taunting && !IsBotRanged() && GetMaxMeleeRange()) { + melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); + melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); + } + + + /* Caster Range Checks */ + bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; + if (isStopMeleeLevel) { + melee_distance = GetBotCasterMaxRange(melee_distance_max); + if (RuleB(Bots, CastersStayJustOutOfMeleeRange)) { + melee_distance_min = melee_distance_max + 1; } else { - melee_distance = melee_distance_max * 0.50f; + melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinCasterRangeDistance); } + } - break; + /* Archer Checks*/ + if (IsBotRanged()) { + float archeryRange = GetBotRangedValue(); + float casterRange = GetBotCasterRange(); + float minArcheryRange = RuleI(Combat, MinRangedAttackDist); + melee_distance = std::min(archeryRange, (casterRange * 2)); + melee_distance_min = std::max(std::max(minArcheryRange, (melee_distance_max + 1)), std::min(casterRange, archeryRange)); } } @@ -2682,10 +2843,11 @@ bool Bot::IsValidTarget( bool invalid_target_state = false; if (HOLDING || !tar->IsNPC() || - tar->IsMezzed() || + (tar->IsMezzed() && !HasBotAttackFlag(tar)) || + (!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) || lo_distance > leash_distance || tar_distance > leash_distance || - (!GetAttackingFlag() && !CheckLosFN(tar) && !leash_owner->CheckLosFN(tar)) || + (!GetAttackingFlag() && !CheckLosCheat(this, tar) && !CheckLosCheat(leash_owner, tar)) || !IsAttackAllowed(tar) ) { invalid_target_state = true; @@ -2750,9 +2912,7 @@ Mob* Bot::GetBotTarget(Client* bot_owner) } } - if (GetArchetype() == Archetype::Caster) { - BotMeditate(true); - } + BotMeditate(IsSitting()); } return t; @@ -2912,13 +3072,11 @@ bool Bot::CheckIfIncapacitated() { void Bot::SetBerserkState() {// Berserk updates should occur if primary AI criteria are met if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) { - - if (!berserk && GetHP() > 0 && GetHPRatio() < 30.0f) { + if (!berserk && GetHPRatio() < RuleI(Combat, BerserkerFrenzyStart)) { entity_list.MessageCloseString(this, false, 200, 0, BERSERK_START, GetName()); berserk = true; } - - if (berserk && GetHPRatio() >= 30.0f) { + if (berserk && GetHPRatio() > RuleI(Combat, BerserkerFrenzyEnd)) { entity_list.MessageCloseString(this, false, 200, 0, BERSERK_END, GetName()); berserk = false; } @@ -2966,10 +3124,9 @@ void Bot::SetOwnerTarget(Client* bot_owner) { bot_owner->SetBotPulling(false); if (NOT_HOLDING && NOT_PASSIVE) { - auto attack_target = bot_owner->GetTarget(); - if (attack_target && HasBotAttackFlag(attack_target)) { + if (attack_target && HasBotAttackFlag(attack_target)) { InterruptSpell(); WipeHateList(); AddToHateList(attack_target, 1); @@ -2996,7 +3153,7 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { auto pull_target = bot_owner->GetTarget(); if (pull_target) { if (raid) { - const auto msg = fmt::format("Pulling {} to the group..", pull_target->GetCleanName()); + const auto msg = fmt::format("Pulling {}.", pull_target->GetCleanName()); raid->RaidSay(msg.c_str(), GetCleanName(), 0, 100); } else { BotGroupSay( @@ -3017,8 +3174,9 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { GetPet()->WipeHateList(); - GetPet()->SetTarget(nullptr); + GetPet()->SetTarget(nullptr); m_previous_pet_order = GetPet()->GetPetOrder(); + GetPet()->CastToNPC()->SaveGuardSpot(GetPosition()); GetPet()->SetPetOrder(SPO_Guard); } } @@ -3104,6 +3262,22 @@ bool Bot::Spawn(Client* botCharacterOwner) { SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); ping_timer.Start(8000); auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); + + pDontHealMeBefore = 0; + pDontGroupHealMeBefore = 0; + pDontGroupHoTHealMeBefore = 0; + pDontBuffMeBefore = Timer::GetCurrentTime() + 400; + pDontDotMeBefore = 0; + pDontRootMeBefore = 0; + pDontSnareMeBefore = 0; + pDontCureMeBefore = 0; + pDontRegularHealMeBefore = 0; + pDontVeryFastHealMeBefore = 0; + pDontFastHealMeBefore = 0; + pDontCompleteHealMeBefore = 0; + pDontGroupCompleteHealMeBefore = 0; + pDontHotHealMeBefore = 0; + // there is something askew with spawn struct appearance fields... // I re-enabled this until I can sort it out const auto& m = GetBotItemSlots(); @@ -3140,6 +3314,11 @@ bool Bot::Spawn(Client* botCharacterOwner) { } } + if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) { + OwnerMessage("Running SpellType checks. There may be some spells that are flagged as incorrect but actually are correct. Use this as a guideline."); + CheckBotSpells(); //This runs through a serious of checks and outputs any spells that are set to the wrong spell type in the database + } + return true; } @@ -3423,7 +3602,6 @@ void Bot::LevelBotWithClient(Client* c, uint8 new_level, bool send_appearance) { parse->EventBot(EVENT_LEVEL_UP, e, nullptr, std::to_string(levels_change), 0); } - e->SetPetChooser(false); // not sure what this does, but was in bot 'update' code e->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); if (send_appearance) { @@ -3820,23 +3998,37 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* return; } - if (trade_instance->IsStackable() && trade_instance->GetCharges() < trade_instance->GetItem()->StackSize) { // temp until partial stacks are implemented - if (trade_event_exists) { - event_trade.push_back(ClientTrade(trade_instance, trade_index)); - continue; - } - else { - client->Message( - Chat::Yellow, - fmt::format( - "{} is only a partially stacked item, the trade has been cancelled!", - item_link - ).c_str() - ); - client->ResetTrade(); - return; + if (RuleI(Bots, StackSizeMin) != -1) { + if (trade_instance->IsStackable() && trade_instance->GetCharges() < RuleI(Bots, StackSizeMin)) { // temp until partial stacks are implemented + if (trade_event_exists) { + event_trade.push_back(ClientTrade(trade_instance, trade_index)); + continue; + } + else { + client->Message( + Chat::Yellow, + fmt::format( + "{} is too small of a stack, you need atleast {}, the trade has been cancelled!", + item_link, + RuleI(Bots, StackSizeMin) + ).c_str() + ); + client->ResetTrade(); + return; + } } } + else if (trade_instance->IsStackable() && trade_instance->GetCharges() < trade_instance->GetItem()->StackSize) { + client->Message( + Chat::Yellow, + fmt::format( + "{} is only a partially stacked item, the trade has been cancelled!", + item_link + ).c_str() + ); + client->ResetTrade(); + return; + } for (int m = EQ::invaug::SOCKET_BEGIN; m <= EQ::invaug::SOCKET_END; ++m) { const auto augment = trade_instance->GetAugment(m); @@ -4775,8 +4967,9 @@ void Bot::RogueAssassinate(Mob* other) { } void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { - if (!target || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) + if (!target || GetAppearance() == eaDead || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) { return; + } bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); @@ -4844,58 +5037,58 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if (ma_time) { switch (GetClass()) { - case Class::Monk: { - int reuse = (MonkSpecialAttack(target, EQ::skills::SkillTigerClaw) - 1); - - // Live AA - Technique of Master Wu - int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - - if (wuchance) { - const int MonkSPA[5] = { - EQ::skills::SkillFlyingKick, - EQ::skills::SkillDragonPunch, - EQ::skills::SkillEagleStrike, - EQ::skills::SkillTigerClaw, - EQ::skills::SkillRoundKick - }; - int extra = 0; - // always 1/4 of the double attack chance, 25% at rank 5 (100/4) - while (wuchance > 0) { - if (zone->random.Roll(wuchance)) { - ++extra; - } - else { - break; + case Class::Monk: { + int reuse = (MonkSpecialAttack(target, EQ::skills::SkillTigerClaw) - 1); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + + if (wuchance) { + const int MonkSPA[5] = { + EQ::skills::SkillFlyingKick, + EQ::skills::SkillDragonPunch, + EQ::skills::SkillEagleStrike, + EQ::skills::SkillTigerClaw, + EQ::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; } - wuchance /= 4; - } - Mob* bo = GetBotOwner(); - if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { - bo->Message( - GENERIC_EMOTE, - "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", - GetCleanName(), - GetCleanName(), - extra - ); - } + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } - auto classic = RuleB(Combat, ClassicMasterWu); - while (extra) { - MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQ::skills::SkillTigerClaw)); - --extra; + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQ::skills::SkillTigerClaw)); + --extra; + } } - } - float HasteModifier = (GetHaste() * 0.01f); - monkattack_timer.Start((reuse * 1000) / HasteModifier); + float HasteModifier = (GetHaste() * 0.01f); + monkattack_timer.Start((reuse * 1000) / HasteModifier); - break; - } - default: - break; + break; + } + default: + break; } } @@ -5347,7 +5540,7 @@ bool Bot::CastSpell( ) { bool Result = false; if (zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - // LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); + // LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", GetSpellName(spell_id), spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); if (casting_spell_id == spell_id) { ZeroCastingVars(); @@ -5632,12 +5825,10 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe if ( spellTarget && - IsGrouped() && - ( - spellTarget->IsBot() || - spellTarget->IsClient() - ) && - RuleB(Bots, GroupBuffing) + GetClass() != Class::Bard && + (IsGrouped() || (IsRaidGrouped() && GetRaid()->GetGroup(GetCleanName()) != RAID_GROUPLESS)) && + (spellTarget->IsBot() || spellTarget->IsClient()) && + (RuleB(Bots, GroupBuffing) || RuleB(Bots, CrossRaidBuffingAndHealing)) ) { bool noGroupSpell = false; uint16 thespell = spell_id; @@ -5645,9 +5836,9 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe int j = BotGetSpells(i); int spelltype = BotGetSpellType(i); bool spellequal = (j == thespell); - bool spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32)); - bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].target_type == ST_Self)); - bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == Class::Shaman)); + bool spelltypeequal = ((spelltype == BotSpellTypes::RegularHeal) || (spelltype == BotSpellTypes::Escape) || (spelltype == BotSpellTypes::Pet)); + bool spelltypetargetequal = ((spelltype == BotSpellTypes::Buff) && (spells[thespell].target_type == ST_Self)); + bool spelltypeclassequal = ((spelltype == BotSpellTypes::InCombatBuff) && (GetClass() == Class::Shaman)); bool slotequal = (slot == EQ::spells::CastingSlot::Item); if (spellequal || slotequal) { if ((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { @@ -5665,24 +5856,42 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe } if (!noGroupSpell) { - Group *g = GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if ((g->members[i]->GetClass() == Class::Necromancer) && (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune))) { - } - else - SpellOnTarget(thespell, g->members[i]); + std::vector v; + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = GatherSpellTargets(true); + } + else { + v = GatherGroupSpellTargets(); + } - if (g->members[i] && g->members[i]->GetPetID()) - SpellOnTarget(thespell, g->members[i]->GetPet()); + for (Mob* m : v) { + if (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune)) { + for (int i = 0; i < m->GetMaxTotalSlots(); i++) { + uint32 buff_count = m->GetMaxTotalSlots(); + + for (unsigned int j = 0; j < buff_count; j++) { + if (IsValidSpell(m->GetBuffs()[j].spellid)) { + if (IsLichSpell(m->GetBuffs()[j].spellid)) { + continue; + } + } + } } } - SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (g->GroupCount() - 1))); + + SpellOnTarget(thespell, m); + + if (m->GetPetID() && (!RuleB(Bots, RequirePetAffinity) || m->HasPetAffinity())) { + SpellOnTarget(thespell, m->GetPet()); + } + + SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (v.size() - 1))); } } + stopLogic = true; } + return true; } @@ -5701,30 +5910,26 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel SpellOnTarget(spell_id, this); entity_list.AESpell(this, this, spell_id, true); } - else if (raid) - { - std::vector raid_group_members = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); - for (auto iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member) { - SpellOnTarget(spell_id, iter->member); - if (iter->member && iter->member->GetPetID()) - SpellOnTarget(spell_id, iter->member ->GetPet()); - } + else { + if (spellTarget != this) { + SpellOnTarget(spell_id, this); } - } - else - { - Group *g = GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { - if (g->members[i]) { - SpellOnTarget(spell_id, g->members[i]); - if (g->members[i] && g->members[i]->GetPetID()) - SpellOnTarget(spell_id, g->members[i]->GetPet()); + + if (spellTarget->IsOfClientBotMerc()) { + const std::vector v = GatherGroupSpellTargets(spellTarget); + for (Mob* m : v) { + SpellOnTarget(spell_id, m); + + if (m->GetPetID() && (!RuleB(Bots, RequirePetAffinity) || m->HasPetAffinity())) { + SpellOnTarget(spell_id, m->GetPet()); } } } + else if (spellTarget->IsPet() && !spellTarget->IsFamiliar() && spellTarget->GetOwner() && (!RuleB(Bots, RequirePetAffinity) || spellTarget->GetOwner()->HasPetAffinity())) { + SpellOnTarget(spell_id, spellTarget); + } } + stopLogic = true; return true; } @@ -5793,9 +5998,9 @@ int32 Bot::GetMaxStat() { int32 Bot::GetMaxResist() { int level = GetLevel(); int32 base = 500; - if (level > 60) - base += ((level - 60) * 5); - + if (level > 65) { + base += ((level - 65) * 5); + } return base; } @@ -6172,24 +6377,52 @@ int64 Bot::CalcManaRegen() { uint8 level = GetLevel(); uint8 botclass = GetClass(); int32 regen = 0; - if (IsSitting()) { - BuffFadeBySitModifier(); - if (botclass != Class::Warrior && botclass != Class::Monk && botclass != Class::Rogue && botclass != Class::Berserker) { - regen = ((((GetSkill(EQ::skills::SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4); - regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen); - } else + + if (GetClass() == Class::Bard) { + if (IsSitting()) { + BuffFadeBySitModifier(); + regen = 2; + regen += (itembonuses.ManaRegen + aabonuses.ManaRegen); + } + else { + regen = 1; + regen += (itembonuses.ManaRegen + aabonuses.ManaRegen); + } + } + else { + if (IsSitting()) { + BuffFadeBySitModifier(); + if (GetArchetype() != Archetype::Melee) { + regen = ((((GetSkill(EQ::skills::SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4); + regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen); + } + else + regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); + } + else regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); - } else - regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); - regen += aabonuses.ManaRegen + itembonuses.heroic_mana_regen; + if (IsHeroicINTCasterClass(GetClass())) { + regen += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25; + } + else if (IsHeroicWISCasterClass(GetClass())) { + regen += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25; + } + else { + regen = 0; + } + + regen += aabonuses.ManaRegen; + regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100); + float mana_regen_rate = RuleR(Bots, ManaRegen); - regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100); - float mana_regen_rate = RuleR(Bots, ManaRegen); - if (mana_regen_rate < 0.0f) - mana_regen_rate = 0.0f; + if (mana_regen_rate < 0.0f) { + mana_regen_rate = 0.0f; + } + + regen = (regen * mana_regen_rate); + } - regen = (regen * mana_regen_rate); return regen; } @@ -6373,29 +6606,52 @@ void Bot::DoEnduranceUpkeep() { void Bot::Camp(bool save_to_database) { - if (IsEngaged() || GetBotOwner()->IsEngaged()) { - GetBotOwner()->Message( - Chat::White, - fmt::format( - "You cannot camp your bots while in combat" - ).c_str() - ); + if (RuleB(Bots, PreventBotCampOnFD) && GetBotOwner()->GetFeigned()) { + GetBotOwner()->Message(Chat::White, "You cannot camp bots while feigned."); return; } - Sit(); - - LeaveHealRotationMemberPool(); + if (RuleB(Bots, PreventBotCampOnEngaged)) { + Raid* raid = entity_list.GetRaidByClient(GetBotOwner()->CastToClient()); + if (raid && raid->IsEngaged()) { + GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); + return; + } - if (save_to_database) { - Save(); - } + auto* owner_group = GetBotOwner()->GetGroup(); + if (owner_group) { + std::list member_list; + owner_group->GetClientList(member_list); + member_list.remove(nullptr); - if (HasGroup() || HasRaid()) { - Zone(); - } else { - Depop(); - } + for (auto member_iter : member_list) { + if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { + GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); + return; + } + } + } + else { + if (GetBotOwner()->CastToClient()->GetAggroCount() > 0) { + GetBotOwner()->Message(Chat::White, "You cannot spawn bots while you are engaged,"); + return; + } + } + } + + Sit(); + + LeaveHealRotationMemberPool(); + + if (save_to_database) { + Save(); + } + + if (HasGroup() || HasRaid()) { + Zone(); + } else { + Depop(); + } } void Bot::Zone() { @@ -6410,10 +6666,10 @@ void Bot::Zone() { Depop(); } -bool Bot::IsArcheryRange(Mob *target) { +bool Bot::IsAtRange(Mob *target) { bool result = false; if (target) { - float range = (GetBotArcheryRange() + 5.0); + float range = (GetBotRangedValue() + 5.0); range *= range; float targetDistance = DistanceSquaredNoZ(m_Position, target->GetPosition()); float minRuleDistance = (RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist)); @@ -6809,349 +7065,64 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { +bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType) { - if ((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, caster->GetClass())) { LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); return false; } - if (!caster || !caster->AI_HasSpells()) { - return false; - } + std::vector v; - if (iChance < 100) { - uint8 tmp = zone->random.Int(1, 100); - if (tmp > iChance) - return false; + if (IsGroupTargetOnlyBotSpellType(spellType)) { + v = caster->GatherGroupSpellTargets(); } - - uint8 botCasterClass = caster->GetClass(); - - if (iSpellTypes == SpellType_Heal) { - if (botCasterClass == Class::Cleric || botCasterClass == Class::Druid || botCasterClass == Class::Shaman) { - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && !iter->member->qglobal) { - if (iter->member->IsClient() && iter->member->GetHPRatio() < 90) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - else if ((iter->member->GetClass() == Class::Warrior || iter->member->GetClass() == Class::Paladin || iter->member->GetClass() == Class::ShadowKnight) && iter->member->GetHPRatio() < 95) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - else if (iter->member->GetClass() == Class::Enchanter && iter->member->GetHPRatio() < 80) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - else if (iter->member->GetHPRatio() < 70) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - - } - - if (iter->member && !iter->member->qglobal && iter->member->HasPet() && iter->member->GetPet()->GetHPRatio() < 50) { - if (iter->member->GetPet()->GetOwner() != caster && caster->IsEngaged() && iter->member->IsCasting() && iter->member->GetClass() != Class::Enchanter) - continue; - - if (caster->AICastSpell(iter->member->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } - - else if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && !g->members[i]->qglobal) { - if (g->members[i]->IsClient() && g->members[i]->GetHPRatio() < 90) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if ((g->members[i]->GetClass() == Class::Warrior || g->members[i]->GetClass() == Class::Paladin || g->members[i]->GetClass() == Class::ShadowKnight) && g->members[i]->GetHPRatio() < 95) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetClass() == Class::Enchanter && g->members[i]->GetHPRatio() < 80) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetHPRatio() < 70) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } - } - - if (g->members[i] && !g->members[i]->qglobal && g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < 50) { - if (g->members[i]->GetPet()->GetOwner() != caster && caster->IsEngaged() && g->members[i]->IsCasting() && g->members[i]->GetClass() != Class::Enchanter ) - continue; - - if (caster->AICastSpell(g->members[i]->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } + else { + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = caster->GatherSpellTargets(true); } - - if ((botCasterClass == Class::Paladin || botCasterClass == Class::Beastlord || botCasterClass == Class::Ranger) && (caster->HasGroup() || caster->IsRaidGrouped())) { - float hpRatioToHeal = 25.0f; - switch(caster->GetBotStance()) { - case Stance::Reactive: - case Stance::Balanced: - hpRatioToHeal = 50.0f; - break; - case Stance::Burn: - case Stance::AEBurn: - hpRatioToHeal = 20.0f; - break; - case Stance::Aggressive: - case Stance::Efficient: - default: - hpRatioToHeal = 25.0f; - break; - } - if (caster->IsRaidGrouped()) { - if (auto raid = entity_list.GetRaidByBotName(caster->GetName())) { - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); - iter != raid_group_members.end(); ++iter) { - if (iter->member && !iter->member->qglobal) { - if (iter->member->IsClient() && iter->member->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } else if ( - (iter->member->GetClass() == Class::Warrior || iter->member->GetClass() == Class::Paladin || - iter->member->GetClass() == Class::ShadowKnight) && - iter->member->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } else if (iter->member->GetClass() == Class::Enchanter && - iter->member->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } else if (iter->member->GetHPRatio() < hpRatioToHeal / 2) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - } - - if (iter->member && !iter->member->qglobal && iter->member->HasPet() && - iter->member->GetPet()->GetHPRatio() < 25) { - if (iter->member->GetPet()->GetOwner() != caster && caster->IsEngaged() && - iter->member->IsCasting() && iter->member->GetClass() != Class::Enchanter) - continue; - - if (caster->AICastSpell(iter->member->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } - } - else if (caster->HasGroup()) { - if (auto g = caster->GetGroup()) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && !g->members[i]->qglobal) { - if (g->members[i]->IsClient() && g->members[i]->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if ((g->members[i]->GetClass() == Class::Warrior || g->members[i]->GetClass() == Class::Paladin || - g->members[i]->GetClass() == Class::ShadowKnight) && - g->members[i]->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetClass() == Class::Enchanter && - g->members[i]->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetHPRatio() < hpRatioToHeal / 2) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } - } - - if (g->members[i] && !g->members[i]->qglobal && g->members[i]->HasPet() && - g->members[i]->GetPet()->GetHPRatio() < 25) { - if (g->members[i]->GetPet()->GetOwner() != caster && caster->IsEngaged() && - g->members[i]->IsCasting() && g->members[i]->GetClass() != Class::Enchanter) - continue; - - if (caster->AICastSpell(g->members[i]->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } + else { + v = caster->GatherGroupSpellTargets(); } + v = caster->GatherSpellTargets(); } - if (iSpellTypes == SpellType_Buff) { - uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(SpellType_Buff) : 100; - if (botCasterClass == Class::Bard) { - if (caster->AICastSpell(caster, chanceToCast, SpellType_Buff)) { - return true; - } else - return false; - } + Mob* tar = nullptr; - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 g = raid->GetGroup(caster->GetName()); - if (g < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(g); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member) { - if (caster->AICastSpell(iter->member, chanceToCast, SpellType_Buff) || caster->AICastSpell(iter->member->GetPet(), chanceToCast, SpellType_Buff)) - return true; - } - } - } - } - if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if (caster->AICastSpell(g->members[i], chanceToCast, SpellType_Buff) || caster->AICastSpell(g->members[i]->GetPet(), chanceToCast, SpellType_Buff)) - return true; - } - } - } + for (Mob* m : v) { + tar = m; + + if (!tar) { + continue; } - } - if (iSpellTypes == SpellType_Cure) { - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && caster->GetNeedsCured(iter->member)) { - if (caster->AICastSpell(iter->member, caster->GetChanceToCastBySpellType(SpellType_Cure), SpellType_Cure)) - return true; - else if (botCasterClass == Class::Bard) { - return false; - } - } + uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(spellType) : 100; - if (iter->member && iter->member->GetPet() && caster->GetNeedsCured(iter->member->GetPet())) { - if (caster->AICastSpell(iter->member->GetPet(), (int)caster->GetChanceToCastBySpellType(SpellType_Cure) / 4, SpellType_Cure)) - return true; - } - } - } + if (!caster->PrecastChecks(tar, spellType)) { + continue; } - else if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && caster->GetNeedsCured(g->members[i])) { - if (caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_Cure), SpellType_Cure)) - return true; - else if (botCasterClass == Class::Bard) - return false; - } - if (g->members[i] && g->members[i]->GetPet() && caster->GetNeedsCured(g->members[i]->GetPet())) { - if (caster->AICastSpell(g->members[i]->GetPet(), (int)caster->GetChanceToCastBySpellType(SpellType_Cure)/4, SpellType_Cure)) - return true; - } - } - } + if (caster->AICastSpell(tar, chanceToCast, spellType)) { + return true; } - } - if (iSpellTypes == SpellType_HateRedux) { - if (!caster->IsEngaged()) - return false; + if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { + tar = m->GetPet(); - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && caster->GetNeedsHateRedux(iter->member)) { - if (caster->AICastSpell(iter->member, caster->GetChanceToCastBySpellType(SpellType_HateRedux), SpellType_HateRedux)) - return true; - } - } - } - } - else if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && caster->GetNeedsHateRedux(g->members[i])) { - if (caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_HateRedux), SpellType_HateRedux)) - return true; - } - } + if (!tar) { + continue; } - } - } - if (iSpellTypes == SpellType_PreCombatBuff) { - if (botCasterClass == Class::Bard || caster->IsEngaged()) - return false; - - //added raid check - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 g = raid->GetGroup(caster->GetName()); - if (g < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(g); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && - (caster->AICastSpell(iter->member, iChance, SpellType_PreCombatBuff) || - caster->AICastSpell(iter->member->GetPet(), iChance, SpellType_PreCombatBuff)) - ) { - return true; - } - } - } - } else if (caster->HasGroup()) { - const auto g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if (caster->AICastSpell(g->members[i], iChance, SpellType_PreCombatBuff) || caster->AICastSpell(g->members[i]->GetPet(), iChance, SpellType_PreCombatBuff)) - return true; - } - } + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + continue; } - } - } - if (iSpellTypes == SpellType_InCombatBuff) { - if (botCasterClass == Class::Bard) { - if (caster->AICastSpell(caster, iChance, SpellType_InCombatBuff)) { - return true; - } - else { - return false; + if (!caster->PrecastChecks(tar, spellType)) { + continue; } - } - if (caster->HasGroup()) { - Group* g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if (caster->AICastSpell(g->members[i], iChance, SpellType_InCombatBuff) || caster->AICastSpell(g->members[i]->GetPet(), iChance, SpellType_InCombatBuff)) { - return true; - } - } - } + if (caster->AICastSpell(tar, chanceToCast, spellType)) { + return true; } } } @@ -7448,7 +7419,7 @@ uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid need_healed++; } - if (includePets && member->GetPet() && member->GetPet()->GetHPRatio() <= hpr) { + if (includePets && member->GetPet() && !member->GetPet()->IsFamiliar() && member->GetPet()->GetHPRatio() <= hpr) { need_healed++; } } @@ -7578,69 +7549,74 @@ int Bot::GroupLeadershipAAOffenseEnhancement() { bool Bot::GetNeedsCured(Mob *tar) { bool needCured = false; + if (tar) { if (tar->FindType(SE_PoisonCounter) || tar->FindType(SE_DiseaseCounter) || tar->FindType(SE_CurseCounter) || tar->FindType(SE_CorruptionCounter)) { uint32 buff_count = tar->GetMaxTotalSlots(); - int buffsWithCounters = 0; - needCured = true; + for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { if (CalculateCounters(tar->GetBuffs()[j].spellid) > 0) { - buffsWithCounters++; - if (buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) { - needCured = false; - break; + if (tar->GetBuffs()[j].ticsremaining < 1) { + continue; } + + needCured = true; } } } } } + return needCured; } bool Bot::GetNeedsHateRedux(Mob *tar) { - // This really should be a scalar function based in class Mob that returns 'this' state..but, is inline with current Bot coding... - // TODO: Good starting point..but, can be refined.. - // TODO: Still awaiting bot spell rework.. - if (!tar || !tar->IsEngaged() || !tar->HasTargetReflection() || !tar->GetTarget()->IsNPC()) + if (!tar || !tar->IsEngaged() || !tar->HasTargetReflection() || !tar->GetTarget()->IsNPC() || (tar->IsBot() && tar->CastToBot()->IsTaunting())) { return false; + } if (tar->IsBot()) { - switch (tar->GetClass()) { - case Class::Rogue: - if (tar->CanFacestab() || tar->CastToBot()->m_evade_timer.Check(false)) - return false; - case Class::Cleric: - case Class::Druid: - case Class::Shaman: - case Class::Necromancer: - case Class::Wizard: - case Class::Magician: - case Class::Enchanter: + if (tar->GetHPRatio() > GetUltimateSpellMinThreshold(BotSpellTypes::HateRedux, tar) && tar->GetHPRatio() < GetUltimateSpellMaxThreshold(BotSpellTypes::HateRedux, tar)) { return true; - default: - return false; } } return false; } -bool Bot::HasOrMayGetAggro() { +bool Bot::HasOrMayGetAggro(bool SitAggro, uint32 spell_id) { bool mayGetAggro = false; + if (GetTarget() && GetTarget()->GetHateTop()) { - Mob *topHate = GetTarget()->GetHateTop(); - if (topHate == this) + Mob* topHate = GetTarget()->GetHateTop(); + if (topHate == this) { mayGetAggro = true; + } else { uint32 myHateAmt = GetTarget()->GetHateAmount(this); uint32 topHateAmt = GetTarget()->GetHateAmount(topHate); - if (myHateAmt > 0 && topHateAmt > 0 && (uint8)((myHateAmt / topHateAmt) * 100) > 90) + if (SitAggro && !spell_id) { + myHateAmt *= (1 + (RuleI(Aggro, SittingAggroMod) / 100)); + } + + if (spell_id && IsValidSpell(spell_id) && GetTarget()) { + myHateAmt += CheckAggroAmount(spell_id, GetTarget()); + } + + if ( + topHateAmt < 1 || + ( + myHateAmt > 0 && + (uint8)((myHateAmt / topHateAmt) * 100) > RuleI(Bots, HasOrMayGetAggroThreshold) + ) + ) { mayGetAggro = true; + } } } + return mayGetAggro; } @@ -8000,51 +7976,24 @@ bool Bot::CheckDataBucket(std::string bucket_name, const std::string& bucket_val int Bot::GetExpansionBitmask() { - if (m_expansion_bitmask >= 0) { - return m_expansion_bitmask; + if (_expansionBitmask >= 0) { + return _expansionBitmask; } return RuleI(Bots, BotExpansionSettings); } -void Bot::SetExpansionBitmask(int expansion_bitmask, bool save) +void Bot::SetExpansionBitmask(int expansionBitmask) { - m_expansion_bitmask = expansion_bitmask; - - if (save) { - if (!database.botdb.SaveExpansionBitmask(GetBotID(), expansion_bitmask)) { - if (GetBotOwner() && GetBotOwner()->IsClient()) { - GetBotOwner()->CastToClient()->Message( - Chat::White, - fmt::format( - "Failed to save expansion bitmask for {}.", - GetCleanName() - ).c_str() - ); - } - } - } + _expansionBitmask = expansionBitmask; LoadAAs(); } -void Bot::SetBotEnforceSpellSetting(bool enforce_spell_settings, bool save) +void Bot::SetBotEnforceSpellSetting(bool enforceSpellSettings) { - m_enforce_spell_settings = enforce_spell_settings; + _enforceSpellSettings = enforceSpellSettings; - if (save) { - if (!database.botdb.SaveEnforceSpellSetting(GetBotID(), enforce_spell_settings)) { - if (GetBotOwner() && GetBotOwner()->IsClient()) { - GetBotOwner()->CastToClient()->Message( - Chat::White, - fmt::format( - "Failed to save enforce spell settings for {}.", - GetCleanName() - ).c_str() - ); - } - } - } LoadBotSpellSettings(); AI_AddBotSpells(GetBotSpellID()); } @@ -8294,24 +8243,6 @@ std::string Bot::GetHPString(int8 min_hp, int8 max_hp) return hp_string; } -void Bot::SetBotArcherySetting(bool bot_archer_setting, bool save) -{ - m_bot_archery_setting = bot_archer_setting; - if (save) { - if (!database.botdb.SaveBotArcherSetting(GetBotID(), bot_archer_setting)) { - if (GetBotOwner() && GetBotOwner()->IsClient()) { - GetBotOwner()->CastToClient()->Message( - Chat::White, - fmt::format( - "Failed to save archery settings for {}.", - GetCleanName() - ).c_str() - ); - } - } - } -} - std::vector Bot::GetApplySpellList( ApplySpellType apply_type, bool allow_pets, @@ -8489,16 +8420,20 @@ float Bot::GetBotCasterMaxRange(float melee_distance_max) {// Calculate caster d float caster_distance_min = 0.0f; float caster_distance = 0.0f; - caster_distance_max = GetBotCasterRange() * GetBotCasterRange(); + caster_distance_max = GetBotCasterRange(); + if (!GetBotCasterRange() && GetLevel() >= GetStopMeleeLevel() && GetClass() >= Class::Warrior && GetClass() <= Class::Berserker) { - caster_distance_max = MAX_CASTER_DISTANCE[GetClass() - 1]; + caster_distance_max = GetDefaultBotBaseSetting(BotBaseSettings::CasterRange); } + if (caster_distance_max) { caster_distance_min = melee_distance_max; + if (caster_distance_max <= caster_distance_min) { caster_distance_max = caster_distance_min * 1.25f; } } + return caster_distance_max; } @@ -8510,33 +8445,36 @@ int32 Bot::CalcItemATKCap() bool Bot::CheckSpawnConditions(Client* c) { - if (c->GetFeigned()) { - c->Message(Chat::White, "You cannot spawn a bot-group while feigned."); + if (RuleB(Bots, PreventBotSpawnOnFD) && c->GetFeigned()) { + c->Message(Chat::White, "You cannot spawn bots while feigned."); return false; } - Raid* raid = entity_list.GetRaidByClient(c); - if (raid && raid->IsEngaged()) { - c->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); - return false; - } + if (RuleB(Bots, PreventBotSpawnOnEngaged)) { + Raid* raid = entity_list.GetRaidByClient(c); + if (raid && raid->IsEngaged()) { + c->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); + return false; + } - auto* owner_group = c->GetGroup(); - if (owner_group) { - std::list member_list; - owner_group->GetClientList(member_list); - member_list.remove(nullptr); + auto* owner_group = c->GetGroup(); + if (owner_group) { + std::list member_list; + owner_group->GetClientList(member_list); + member_list.remove(nullptr); - for (auto member_iter : member_list) { - if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); - return false; + for (auto member_iter : member_list) { + if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { + c->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); + return false; + } } } - } else { - if (c->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while you are engaged,"); - return false; + else { + if (c->GetAggroCount() > 0) { + c->Message(Chat::White, "You cannot spawn bots while you are engaged,"); + return false; + } } } @@ -8609,12 +8547,12 @@ void Bot::SetSpellRecastTimer(uint16 spell_id, int32 recast_delay) { if (CheckSpellRecastTimer(spell_id)) { BotTimer_Struct t; - t.timer_id = spells[ spell_id ].timer_id; + t.timer_id = spells[spell_id].timer_id; t.timer_value = (Timer::GetCurrentTime() + recast_delay); t.recast_time = recast_delay; t.is_spell = true; t.is_disc = false; - t.spell_id = spells[ spell_id ].id; + t.spell_id = spells[spell_id].id; t.is_item = false; t.item_id = 0; @@ -8627,7 +8565,7 @@ void Bot::SetSpellRecastTimer(uint16 spell_id, int32 recast_delay) { ( ( spells[spell_id].timer_id != 0 && - spells[spell_id].timer_id == bot_timers[ i ].timer_id + spells[spell_id].timer_id == bot_timers[i].timer_id ) || bot_timers[i].spell_id == spell_id ) @@ -8715,12 +8653,12 @@ void Bot::SetDisciplineReuseTimer(uint16 spell_id, int32 reuse_timer) if (CheckDisciplineReuseTimer(spell_id)) { BotTimer_Struct t; - t.timer_id = spells[ spell_id ].timer_id; + t.timer_id = spells[spell_id].timer_id; t.timer_value = (Timer::GetCurrentTime() + reuse_timer); t.recast_time = reuse_timer; t.is_spell = false; t.is_disc = true; - t.spell_id = spells[ spell_id ].id; + t.spell_id = spells[spell_id].id; t.is_item = false; t.item_id = 0; @@ -9220,3 +9158,2275 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) } uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 }; + +std::vector Bot::GatherGroupSpellTargets(Mob* target, bool noClients, bool noBots) { + std::vector valid_spell_targets; + + if (IsRaidGrouped()) { + if (auto raid = entity_list.GetRaidByBotName(GetName())) { + std::vector raidGroupMembers; + if (target) { + auto raidGroup = raid->GetGroup(target->GetName()); + + if (raidGroup != RAID_GROUPLESS) { + raidGroupMembers = raid->GetRaidGroupMembers(raidGroup); + } + else { + return valid_spell_targets; + } + } + else { + auto raidGroup = raid->GetGroup(GetName()); + + if (raidGroup != RAID_GROUPLESS) { + raidGroupMembers = raid->GetRaidGroupMembers(raidGroup); + } + else { + return valid_spell_targets; + } + } + + for (const auto& m : raidGroupMembers) { + if ( + m.member && m.group_number != RAID_GROUPLESS && + ( + (m.member->IsClient() && !noClients) || + (m.member->IsBot() && !noBots) + ) + ) { + valid_spell_targets.emplace_back(m.member); + } + } + } + } + else if (IsGrouped()) { + Group* group = GetGroup(); + if (group) { + for (const auto& m : group->members) { + if ( + m && + ( + (m->IsClient() && !noClients) || + (m->IsBot() && !noBots) + ) + ) { + valid_spell_targets.emplace_back(m); + } + } + } + } + else { + valid_spell_targets.emplace_back(this); + } + + return valid_spell_targets; +} + +std::vector Bot::GatherSpellTargets(bool entireRaid, bool noClients, bool noBots, bool noPets) { + std::vector valid_spell_targets; + + if (IsRaidGrouped()) { + if (auto raid = entity_list.GetRaidByBotName(GetName())) { + if (entireRaid) { + for (const auto& m : raid->members) { + if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m.member); + } + } + } + else { + std::vector raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); + + for (const auto& m : raidGroup) { + if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m.member); + } + } + } + } + } + else if (IsGrouped()) { + Group* group = GetGroup(); + if (group) { + for (const auto& m : group->members) { + if (m && ((m->IsClient() && !noClients) || (m->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m); + } + } + } + } + else { + valid_spell_targets.emplace_back(this); + } + + return valid_spell_targets; +} + +bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { + if (!tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + + LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + + if (GetUltimateSpellHold(spellType, tar)) { + LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + if (GetHPRatio() < GetSpellTypeMinHPLimit(spellType) || GetHPRatio() > GetSpellTypeMaxHPLimit(spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + if (!GetUltimateSpellDelayCheck(spellType, tar)) { + LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + switch (spellType) { //This will skip Threshold Checks during Precast for specific SpellTypes that are checked when acquiring new targets + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + return true; + default: + if (GetHPRatioForSpellType(spellType, tar) < GetUltimateSpellMinThreshold(spellType, tar) || GetHPRatioForSpellType(spellType, tar) > GetUltimateSpellMaxThreshold(spellType, tar)) { + LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + } + + return true; +} + +bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { + if (!tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + + if (doPrechecks) { + if (spells[spellid].target_type == ST_Self && tar != this) { + tar = this; + } + + if (!PrecastChecks(tar, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); //deleteme + return false; + } + } + + LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + + if (!IsValidSpell(spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme + return false; + } + + if (spells[spellid].target_type == ST_Self && tar != this) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!CheckSpellRecastTimer(spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (!BotHasEnoughMana(spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (zone->IsSpellBlocked(spellid, glm::vec3(GetPosition()))) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (!zone->CanLevitate() && IsEffectInSpell(spellid, SE_Levitate)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (spells[spellid].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (spells[spellid].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (spells[spellid].zone_type == 1 && !zone->CanCastOutdoor()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (!AECheck && !IsValidSpellRange(spellid, tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), tar->GetBodyType())) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsImmuneToBotSpell(spellid, this)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!DoResistCheckBySpellType(tar, spellid, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell() && !taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if ( + (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spellid) != 0)) + && + tar->CanBuffStack(spellid, GetLevel(), true) < 0 + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!CanCastSpellType(spellType, spellid, tar)) { + return false; + } + + return true; +} + +bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { + if (!spellid || !tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spellid ? GetSpellName(spellid) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme + return false; + } + + uint8 botClass = GetClass(); + //uint8 botLevel = GetLevel(); + + switch (spellType) { + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + if ( + !( + spells[spellid].target_type == ST_Target || + spells[spellid].target_type == ST_Pet || + (tar == this && spells[spellid].target_type != ST_TargetsTarget) || + spells[spellid].target_type == ST_Group || + spells[spellid].target_type == ST_GroupTeleport //|| + //(botClass == Class::Bard && spells[spellid].target_type == ST_AEBard) //TODO bot rewrite - is this needed? + ) + ) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsBlockedBuff(spellid)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (IsEffectInSpell(spellid, SE_Teleport) || IsEffectInSpell(spellid, SE_Succor)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spellid, SE_Illusion)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (spells[spellid].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if ((IsGroupSpell(spellid) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell()) { + switch (tar->GetArchetype()) { + case Archetype::Caster: + if ( + tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && + ( + IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || + (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) + ) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Melee: + if ( + IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || + IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || + IsEffectInSpell(spellid, SE_CurrentMana) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Hybrid: //Hybrids get all buffs + default: + break; + } + } + + // Differences for each type + if (spellType != BotSpellTypes::InCombatBuff) { + if (IsEffectInSpell(spellid, SE_AbsorbMagicAtt) || IsEffectInSpell(spellid, SE_Rune)) { + for (int i = 0; i < tar->GetMaxTotalSlots(); i++) { + uint32 buff_count = tar->GetMaxTotalSlots(); + + for (unsigned int j = 0; j < buff_count; j++) { + if (IsValidSpell(tar->GetBuffs()[j].spellid)) { + if (IsLichSpell(tar->GetBuffs()[j].spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + } + } + } + } + } + + break; + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + //switch (spells[spellid].target_type) { //TODO bot rewrite - is this needed? + // case ST_AEBard: + // case ST_AECaster: + // case ST_GroupTeleport: + // case ST_Group: + // case ST_Self: + // break; + // default: + // LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + // return false; + //} + + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell()) { + switch (tar->GetArchetype()) { + case Archetype::Caster: + if ( + tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && + ( + IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || + (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) + ) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Melee: + if ( + IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || + IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || + IsEffectInSpell(spellid, SE_CurrentMana) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Hybrid: //Hybrids get all buffs + default: + break; + } + } + + break; + default: + break; + } + + LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return true; +} + +bool Bot::BotHasEnoughMana(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + //int32 manaCost = GetActSpellCost(spell_id, spells[spell_id].mana); + int32 manaCost = spells[spell_id].mana; + + if (GetMana() < manaCost) { + return false; + } + + return true; +} + +bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { + + if (!tar || !spellid) { + return true; + } + + if (IsNPC() && CastToNPC()->GetSwarmOwner()) { + return true; + } + + const std::vector v = GatherSpellTargets(); + for (Mob* m : v) { + if ( + m->IsBot() && + m->IsCasting() && + m->CastToBot()->casting_spell_targetid && + entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(tar->GetID()) && + m->CastingSpellID() == spellid + ) { + + return true; + } + else { + if (IsGroupSpell(spellid)) { + if ( + m->IsBot() && + m->IsCasting() && + m->CastToBot()->casting_spell_targetid && + m->CastingSpellID() == spellid + ) { + + const std::vector v = GatherGroupSpellTargets(); + for (Mob* m : v) { + if (entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(m->GetID())) { + return true; + } + } + } + } + } + } + + return false; +} + +bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { + + if (!tar || spellid == 0) { + return false; + } + + int32 resist_difficulty = -spells[spellid].resist_difficulty; + int32 level_mod = (tar->GetLevel() - GetLevel()) * (tar->GetLevel() - GetLevel()) / 2; + + if (tar->GetLevel() - GetLevel() < 0) { + level_mod = -level_mod; + } + + int32 targetResist = 0; + + switch (GetSpellResistType(spellid)) { + case RESIST_NONE: + return true; + case RESIST_MAGIC: + targetResist = tar->GetMR(); + break; + case RESIST_COLD: + targetResist = tar->GetCR(); + break; + case RESIST_FIRE: + targetResist = tar->GetFR(); + break; + case RESIST_POISON: + targetResist = tar->GetPR(); + break; + case RESIST_DISEASE: + targetResist = tar->GetDR(); + break; + case RESIST_CORRUPTION: + targetResist = tar->GetCorrup(); + break; + default: + return true; + } + //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spellid), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) + if ((targetResist + level_mod - resist_difficulty) > resist_limit) { + return false; + } + + return true; +} + +bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType) { + if (!tar || !IsValidSpell(spellid)) { + return false; + } + + if (GetSpellTypeResistLimit(spellType) == 0) { + return true; + } + + return DoResistCheck(tar, spellid, GetSpellTypeResistLimit(spellType)); +} + +bool Bot::IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType) { + if (!spellid) { + return false; + } + + switch (targetType) { + case ST_Undead: + if (bodyType == BodyType::Undead || bodyType == BodyType::SummonedUndead || bodyType == BodyType::Vampire) { + return true; + } + + break; + case ST_Summoned: + if (bodyType == BodyType::Summoned || bodyType == BodyType::Summoned2 || bodyType == BodyType::Summoned3) { + return true; + } + + break; + case ST_Animal: + if (bodyType == BodyType::Animal) { + return true; + } + + break; + case ST_Plant: + if (bodyType == BodyType::Plant) { + return true; + } + + break; + case ST_Giant: + if (bodyType == BodyType::Giant || bodyType == BodyType::RaidGiant) { + return true; + } + + break; + case ST_Dragon: + if (bodyType == BodyType::Dragon || bodyType == BodyType::VeliousDragon) { + return true; + } + + break; + default: + return true; + } + + return false; +} + +bool Bot::IsMobEngagedByAnyone(Mob* tar) { + if (!tar) { + return false; + } + + const std::vector v = GatherSpellTargets(true); + + for (Mob* m : v) { + if (m->GetTarget() == tar) { + if ( + m->IsBot() && + !m->CastToBot()->GetHoldFlag() && + m->IsEngaged() && + ( + !m->CastToBot()->IsBotNonSpellFighter() || + ( + m->GetLevel() >= m->CastToBot()->GetStopMeleeLevel() && + !m->IsCasting() + ) + ) + ) { + return true; + } + + if (m->IsCasting() && SpellBreaksMez(m->CastingSpellID())) { + return true; + } + + if (m->IsClient() && (m->CastToClient()->AutoAttackEnabled() || m->CastToClient()->AutoFireEnabled())) { + return true; + } + } + } + + return false; +} + +bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid) { + if (npc->GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { + return false; + } + + if (!npc->CastToNPC()->IsOnHatelist(owner)) { + return false; + } + + if (npc->IsMezzed() || HasBotAttackFlag(npc)) { + return false; + } + + if (npc->HasOwner() && npc->GetOwner() && npc->GetOwner()->IsOfClientBotMerc()) { + return false; + } + + if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), npc->GetBodyType())) { + return false; + } + + if (!IsAttackAllowed(GetTarget())) { + return false; + } + + if (!DoLosChecks(this, npc)) { + return false; + } + + if (IsMobEngagedByAnyone(npc)) { + return false; + } + + int buff_count = npc->GetMaxTotalSlots(); + auto npc_buffs = npc->GetBuffs(); + + for (int i = 0; i < buff_count; i++) { + if (IsDetrimentalSpell(npc_buffs[i].spellid) && IsEffectInSpell(npc_buffs[i].spellid, SE_CurrentHP)) { + return false; + } + } + + return true; +} + +void Bot::SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + SetBotBaseSetting(botSetting, settingValue); + break; + case BotSettingCategories::SpellHold: + SetSpellHold(botSetting, settingValue); + break; + case BotSettingCategories::SpellDelay: + SetSpellDelay(botSetting, settingValue); + break; + case BotSettingCategories::SpellMinThreshold: + SetSpellMinThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellMaxThreshold: + SetSpellMaxThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeAggroCheck: + SetSpellTypeAggroCheck(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMinManaPct: + SetSpellTypeMinManaLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMaxManaPct: + SetSpellTypeMaxManaLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMinHPPct: + SetSpellTypeMinHPLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMaxHPPct: + SetSpellTypeMaxHPLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeIdlePriority: + SetSpellTypePriority(botSetting, BotPriorityCategories::Idle, settingValue); + break; + case BotSettingCategories::SpellTypeEngagedPriority: + SetSpellTypePriority(botSetting, BotPriorityCategories::Engaged, settingValue); + break; + case BotSettingCategories::SpellTypePursuePriority: + SetSpellTypePriority(botSetting, BotPriorityCategories::Pursue, settingValue); + break; + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + SetSpellTypeAEOrGroupTargetCount(botSetting, settingValue); + break; + } +} + +void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { + switch (botSetting) { + case BotBaseSettings::ExpansionBitmask: + SetExpansionBitmask(settingValue); + break; + case BotBaseSettings::ShowHelm: + SetShowHelm(settingValue); + break; + case BotBaseSettings::FollowDistance: + SetFollowDistance(EQ::Clamp(static_cast(settingValue), static_cast(1), BOT_FOLLOW_DISTANCE_DEFAULT_MAX)); + break; + case BotBaseSettings::StopMeleeLevel: + SetStopMeleeLevel(settingValue); + break; + case BotBaseSettings::EnforceSpellSettings: + SetBotEnforceSpellSetting(settingValue); + break; + case BotBaseSettings::RangedSetting: + SetBotRangedSetting(settingValue); + break; + case BotBaseSettings::PetSetTypeSetting: + SetPetChooserID(settingValue); + break; + case BotBaseSettings::BehindMob: + SetBehindMob(settingValue); + break; + case BotBaseSettings::CasterRange: + SetBotCasterRange(settingValue); + break; + case BotBaseSettings::IllusionBlock: + SetIllusionBlock(settingValue); + break; + case BotBaseSettings::MaxMeleeRange: + SetMaxMeleeRange(settingValue); + break; + case BotBaseSettings::MedInCombat: + SetMedInCombat(settingValue); + break; + case BotBaseSettings::HPWhenToMed: + SetHPWhenToMed(settingValue); + break; + case BotBaseSettings::ManaWhenToMed: + SetManaWhenToMed(settingValue); + break; + default: + break; + } +} + +int Bot::GetBotBaseSetting(uint16 botSetting) { + switch (botSetting) { + case BotBaseSettings::ExpansionBitmask: + //LogBotSettingsDetail("Returning current GetExpansionBitmask of [{}] for [{}]", GetExpansionBitmask(), GetCleanName()); //deleteme + return GetExpansionBitmask(); + case BotBaseSettings::ShowHelm: + //LogBotSettingsDetail("Returning current GetShowHelm of [{}] for [{}]", GetShowHelm(), GetCleanName()); //deleteme + return GetShowHelm(); + case BotBaseSettings::FollowDistance: + //LogBotSettingsDetail("Returning current GetFollowDistance of [{}] for [{}]", GetFollowDistance(), GetCleanName()); //deleteme + return GetFollowDistance(); + case BotBaseSettings::StopMeleeLevel: + //LogBotSettingsDetail("Returning current GetStopMeleeLevel of [{}] for [{}]", GetStopMeleeLevel(), GetCleanName()); //deleteme + return GetStopMeleeLevel(); + case BotBaseSettings::EnforceSpellSettings: + //LogBotSettingsDetail("Returning current GetBotEnforceSpellSetting of [{}] for [{}]", GetBotEnforceSpellSetting(), GetCleanName()); //deleteme + return GetBotEnforceSpellSetting(); + case BotBaseSettings::RangedSetting: + //LogBotSettingsDetail("Returning current IsBotRanged of [{}] for [{}]", IsBotRanged(), GetCleanName()); //deleteme + return IsBotRanged(); + case BotBaseSettings::PetSetTypeSetting: + //LogBotSettingsDetail("Returning current GetPetChooserID of [{}] for [{}]", GetPetChooserID(), GetCleanName()); //deleteme + return GetPetChooserID(); + case BotBaseSettings::BehindMob: + //LogBotSettingsDetail("Returning current GetBehindMob of [{}] for [{}]", GetBehindMob(), GetCleanName()); //deleteme + return GetBehindMob(); + case BotBaseSettings::CasterRange: + //LogBotSettingsDetail("Returning current GetBotCasterRange of [{}] for [{}]", GetBotCasterRange(), GetCleanName()); //deleteme + return GetBotCasterRange(); + case BotBaseSettings::IllusionBlock: + //LogBotSettingsDetail("Returning current GetIllusionBlock of [{}] for [{}]", GetIllusionBlock(), GetCleanName()); //deleteme + return GetIllusionBlock(); + case BotBaseSettings::MaxMeleeRange: + //LogBotSettingsDetail("Returning current MaxMeleeRange of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetMaxMeleeRange(); + case BotBaseSettings::MedInCombat: + //LogBotSettingsDetail("Returning current GetMedInCombate of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetMedInCombat(); + case BotBaseSettings::HPWhenToMed: + //LogBotSettingsDetail("Returning current GetHPWhenToMed of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetHPWhenToMed(); + case BotBaseSettings::ManaWhenToMed: + //LogBotSettingsDetail("Returning current GetManaWhenToMed of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetManaWhenToMed(); + default: + return true; + } + + return true; +} + +int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { + switch (botSetting) { + case BotBaseSettings::ExpansionBitmask: + return RuleI(Bots, BotExpansionSettings); + case BotBaseSettings::ShowHelm: + return true; + case BotBaseSettings::FollowDistance: + return BOT_FOLLOW_DISTANCE_DEFAULT; + case BotBaseSettings::StopMeleeLevel: + if (IsCasterClass(GetClass())) { + return RuleI(Bots, CasterStopMeleeLevel); + } + else { + return 255; + } + case BotBaseSettings::PetSetTypeSetting: + return 0; + case BotBaseSettings::BehindMob: + if (GetClass() == Class::Rogue) { + return true; + } + else { + return false; + } + case BotBaseSettings::CasterRange: + switch (GetClass()) { + case Class::Warrior: + case Class::Monk: + case Class::Rogue: + case Class::Berserker: + return 0; + case Class::Bard: + return 30; + default: + return 90; + } + case BotBaseSettings::MedInCombat: + if (IsCasterClass(GetClass())) { + return true; + } + + return false; + case BotBaseSettings::HPWhenToMed: + case BotBaseSettings::ManaWhenToMed: + return 80; + case BotBaseSettings::EnforceSpellSettings: + case BotBaseSettings::RangedSetting: + case BotBaseSettings::IllusionBlock: + case BotBaseSettings::MaxMeleeRange: + default: + return false; + } + + return true; +} + + +void Bot::LoadDefaultBotSettings() { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i)); //deleteme + } + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + BotSpellSettings_Struct t; + + t.spellType = i; + t.shortName = GetSpellTypeShortNameByID(i); + t.name = GetSpellTypeNameByID(i); + t.hold = GetDefaultSpellHold(i); + t.delay = GetDefaultSpellDelay(i); + t.minThreshold = GetDefaultSpellMinThreshold(i); + t.maxThreshold = GetDefaultSpellMaxThreshold(i); + t.resistLimit = GetDefaultSpellTypeResistLimit(i); + t.aggroCheck = GetDefaultSpellTypeAggroCheck(i); + t.minManaPct = GetDefaultSpellTypeMinManaLimit(i); + t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i); + t.minHPPct = GetDefaultSpellTypeMinHPLimit(i); + t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i); + t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass()); + t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass()); + t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass()); + t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i); + t.recastTimer.Start(); + + _spellSettings.push_back(t); + + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); //deleteme + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); //deleteme + LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i), GetDefaultSpellTypeMinManaLimit(i), GetDefaultSpellTypeMaxManaLimit(i), GetDefaultSpellTypeMinHPLimit(i), GetDefaultSpellTypeMaxHPLimit(i)); //deleteme + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}] | recastTimer = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass()), GetDefaultSpellTypeEngagedPriority(i, GetClass()), GetDefaultSpellTypePursuePriority(i, GetClass()), GetDefaultSpellTypeAEOrGroupTargetCount(i), t.recastTimer.GetRemainingTime()); //deleteme + } +} + +void Bot::SetBotSpellRecastTimer(uint16 spellType, Mob* tar, bool preCast) { + if (!tar) { + return; + } + + if (!preCast && BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + return; + } + + uint32 addedDelay = 0; + + switch (spellType) { //Additional delays + case BotSpellTypes::Mez: + addedDelay = RuleI(Bots, MezSuccessDelay); + break; + case BotSpellTypes::AEMez: + addedDelay = RuleI(Bots, AEMezSuccessDelay); + break; + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + } + else if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + tar->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + } + else { + SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + } +} + +BotSpell Bot::GetSpellByHealType(uint16 spellType, Mob* tar) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return GetBestBotSpellForVeryFastHeal(this, tar, spellType); + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return GetBestBotSpellForFastHeal(this, tar, spellType); + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return GetBestBotSpellForRegularSingleTargetHeal(this, tar, spellType); + case BotSpellTypes::GroupHeals: + return GetBestBotSpellForGroupHeal(this, tar, spellType); + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetCompleteHeals: + return GetBestBotSpellForPercentageHeal(this, tar, spellType); + case BotSpellTypes::GroupCompleteHeals: + return GetBestBotSpellForGroupCompleteHeal(this, tar, spellType); + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return GetBestBotSpellForHealOverTime(this, tar, spellType); + case BotSpellTypes::GroupHoTHeals: + return GetBestBotSpellForGroupHealOverTime(this, tar, spellType); + } +} + +uint16 Bot::GetSpellTypePriority(uint16 spellType, uint8 priorityType) { + switch (priorityType) { + case BotPriorityCategories::Idle: + return _spellSettings[spellType].idlePriority; + case BotPriorityCategories::Engaged: + return _spellSettings[spellType].engagedPriority; + case BotPriorityCategories::Pursue: + return _spellSettings[spellType].pursuePriority; + default: + return 0; + } +} + +int Bot::GetDefaultSetting(uint16 settingCategory, uint16 settingType) { + switch (settingCategory) { + case BotSettingCategories::BaseSetting: + return GetDefaultBotBaseSetting(settingType); + case BotSettingCategories::SpellHold: + return GetDefaultSpellHold(settingType); + case BotSettingCategories::SpellDelay: + return GetDefaultSpellDelay(settingType); + case BotSettingCategories::SpellMinThreshold: + return GetDefaultSpellMinThreshold(settingType); + case BotSettingCategories::SpellMaxThreshold: + return GetDefaultSpellMinThreshold(settingType); + case BotSettingCategories::SpellTypeAggroCheck: + return GetDefaultSpellTypeAggroCheck(settingType); + case BotSettingCategories::SpellTypeMinManaPct: + return GetDefaultSpellTypeMinManaLimit(settingType); + case BotSettingCategories::SpellTypeMaxManaPct: + return GetDefaultSpellTypeMaxManaLimit(settingType); + case BotSettingCategories::SpellTypeMinHPPct: + return GetDefaultSpellTypeMinHPLimit(settingType); + case BotSettingCategories::SpellTypeMaxHPPct: + return GetDefaultSpellTypeMaxHPLimit(settingType); + case BotSettingCategories::SpellTypeIdlePriority: + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Idle, GetClass()); + case BotSettingCategories::SpellTypeEngagedPriority: + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Engaged, GetClass()); + case BotSettingCategories::SpellTypePursuePriority: + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Pursue, GetClass()); + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + return GetDefaultSpellTypeAEOrGroupTargetCount(settingType); + default: + break; + } +} + +int Bot::GetSetting(uint16 settingCategory, uint16 settingType) { + switch (settingCategory) { + case BotSettingCategories::BaseSetting: + return GetBotBaseSetting(settingType); + case BotSettingCategories::SpellHold: + return GetSpellHold(settingType); + case BotSettingCategories::SpellDelay: + return GetSpellDelay(settingType); + case BotSettingCategories::SpellMinThreshold: + return GetSpellMinThreshold(settingType); + case BotSettingCategories::SpellMaxThreshold: + return GetSpellMinThreshold(settingType); + case BotSettingCategories::SpellTypeAggroCheck: + return GetSpellTypeAggroCheck(settingType); + case BotSettingCategories::SpellTypeMinManaPct: + return GetSpellTypeMinManaLimit(settingType); + case BotSettingCategories::SpellTypeMaxManaPct: + return GetSpellTypeMaxManaLimit(settingType); + case BotSettingCategories::SpellTypeMinHPPct: + return GetSpellTypeMinHPLimit(settingType); + case BotSettingCategories::SpellTypeMaxHPPct: + return GetSpellTypeMaxHPLimit(settingType); + case BotSettingCategories::SpellTypeIdlePriority: + return GetSpellTypePriority(settingType, BotPriorityCategories::Idle); + case BotSettingCategories::SpellTypeEngagedPriority: + return GetSpellTypePriority(settingType, BotPriorityCategories::Engaged); + case BotSettingCategories::SpellTypePursuePriority: + return GetSpellTypePriority(settingType, BotPriorityCategories::Pursue); + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + return GetSpellTypeAEOrGroupTargetCount(settingType); + default: + break; + } +} + +uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass) { + switch (priorityType) { + case BotPriorityCategories::Idle: + return GetDefaultSpellTypeIdlePriority(spellType, botClass); + case BotPriorityCategories::Engaged: + return GetDefaultSpellTypeEngagedPriority(spellType, botClass); + case BotPriorityCategories::Pursue: + return GetDefaultSpellTypePursuePriority(spellType, botClass); + default: + return 0; + } +} + +uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { + if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, botClass)) { + return 0; + } + + uint16 priority = 0; + + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + priority = 1; + + break; + case BotSpellTypes::FastHeals: + priority = 2; + + break; + case BotSpellTypes::GroupHeals: + priority = 3; + + break; + case BotSpellTypes::RegularHeal: + priority = 4; + + break; + case BotSpellTypes::GroupCompleteHeals: + priority = 5; + + break; + case BotSpellTypes::CompleteHeal: + priority = 6; + + break; + case BotSpellTypes::GroupHoTHeals: + priority = 7; + + break; + case BotSpellTypes::HoTHeals: + priority = 8; + + break; + case BotSpellTypes::GroupCures: + priority = 9; + + break; + case BotSpellTypes::Cure: + priority = 10; + + break; + case BotSpellTypes::InCombatBuff: // this has a check at the end to decrement everything below if it's not a SK (SK use InCombatBuffs as it is their hate line so it's not use when Idle) + priority = 11; + + break; + case BotSpellTypes::PetVeryFastHeals: + priority = 12; + + break; + case BotSpellTypes::PetFastHeals: + priority = 13; + + break; + case BotSpellTypes::PetRegularHeals: + priority = 14; + + break; + case BotSpellTypes::PetCompleteHeals: + priority = 15; + + break; + case BotSpellTypes::PetHoTHeals: + priority = 16; + + break; + case BotSpellTypes::Pet: + priority = 17; + + break; + case BotSpellTypes::Buff: + priority = 18; + + break; + case BotSpellTypes::OutOfCombatBuffSong: + priority = 19; + + break; + case BotSpellTypes::ResistBuffs: + priority = 20; + + break; + case BotSpellTypes::DamageShields: + priority = 21; + + break; + case BotSpellTypes::PetBuffs: + priority = 22; + + break; + case BotSpellTypes::PreCombatBuff: + priority = 23; + + break; + case BotSpellTypes::PreCombatBuffSong: + priority = 24; + + break; + default: + priority = 0; //unused + + break; + } + + if ( + priority >= 11 && + botClass && botClass == Class::ShadowKnight && + spellType != BotSpellTypes::InCombatBuff + ) { + --priority; + } + + return priority; +} + +uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass) { + switch (spellType) { + case BotSpellTypes::Escape: + return 1; + case BotSpellTypes::VeryFastHeals: + return 2; + case BotSpellTypes::FastHeals: + return 3; + case BotSpellTypes::GroupHeals: + return 4; + case BotSpellTypes::RegularHeal: + return 5; + case BotSpellTypes::GroupCompleteHeals: + return 6; + case BotSpellTypes::CompleteHeal: + return 7; + case BotSpellTypes::GroupHoTHeals: + return 8; + case BotSpellTypes::HoTHeals: + return 9; + case BotSpellTypes::GroupCures: + return 10; + case BotSpellTypes::Cure: + return 11; + case BotSpellTypes::PetVeryFastHeals: + return 12; + case BotSpellTypes::PetFastHeals: + return 13; + case BotSpellTypes::PetRegularHeals: + return 14; + case BotSpellTypes::PetCompleteHeals: + return 15; + case BotSpellTypes::PetHoTHeals: + return 16; + case BotSpellTypes::AELifetap: + return 17; + case BotSpellTypes::Lifetap: + return 18; + case BotSpellTypes::HateRedux: + return 19; + case BotSpellTypes::AEMez: + return 20; + case BotSpellTypes::Mez: + return 21; + case BotSpellTypes::AEDispel: + return 22; + case BotSpellTypes::Dispel: + return 23; + case BotSpellTypes::AEDebuff: + return 24; + case BotSpellTypes::Debuff: + return 25; + case BotSpellTypes::AESnare: + return 26; + case BotSpellTypes::Snare: + return 27; + case BotSpellTypes::AEFear: + return 28; + case BotSpellTypes::Fear: + return 29; + case BotSpellTypes::AESlow: + return 30; + case BotSpellTypes::Slow: + return 31; + case BotSpellTypes::AERoot: + return 32; + case BotSpellTypes::Root: + return 33; + case BotSpellTypes::AEDoT: + return 34; + case BotSpellTypes::DOT: + return 35; + case BotSpellTypes::AEStun: + return 36; + case BotSpellTypes::PBAENuke: + return 37; + case BotSpellTypes::AENukes: + return 38; + case BotSpellTypes::AERains: + return 39; + case BotSpellTypes::Stun: + return 40; + case BotSpellTypes::Nuke: + return 41; + case BotSpellTypes::InCombatBuff: + return 42; + case BotSpellTypes::InCombatBuffSong: + return 43; + case BotSpellTypes::Pet: + return 44; + default: + return 0; + } +} + +uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass) { + switch (spellType) { + case BotSpellTypes::Escape: + return 1; + case BotSpellTypes::VeryFastHeals: + return 2; + case BotSpellTypes::FastHeals: + return 3; + case BotSpellTypes::GroupHeals: + return 4; + case BotSpellTypes::RegularHeal: + return 5; + case BotSpellTypes::GroupCompleteHeals: + return 6; + case BotSpellTypes::CompleteHeal: + return 7; + case BotSpellTypes::GroupHoTHeals: + return 8; + case BotSpellTypes::HoTHeals: + return 9; + case BotSpellTypes::GroupCures: + return 10; + case BotSpellTypes::Cure: + return 11; + case BotSpellTypes::Snare: + return 12; + case BotSpellTypes::Lifetap: + return 13; + case BotSpellTypes::Dispel: + return 14; + case BotSpellTypes::Stun: + return 15; + case BotSpellTypes::Nuke: + return 16; + case BotSpellTypes::DOT: + return 17; + case BotSpellTypes::PetVeryFastHeals: + return 18; + case BotSpellTypes::PetFastHeals: + return 19; + case BotSpellTypes::PetRegularHeals: + return 20; + case BotSpellTypes::PetCompleteHeals: + return 21; + case BotSpellTypes::PetHoTHeals: + return 22; + default: + return 0; + } +} + +uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType) { + + if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + return RuleI(Bots, SpellResistLimit); + } + else { + return 0; + } +} + +bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + return true; + default: + return false; + } +} + +uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spellType) { + return 0; +} + +uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::InCombatBuff: + if (GetClass() == Class::Shaman) { + return 75; + } + + break; + default: + break; + } + + return 100; +} + +uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::InCombatBuff: + if (GetClass() == Class::Shaman) { + return 40; + } + + break; + default: + break; + } + + return 0; +} + +uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spellType) { + return 100; +} + +uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType) { + if (IsAEBotSpellType(spellType)) { + return RuleI(Bots, MinTargetsForAESpell); + } + + if (IsGroupBotSpellType(spellType)) { + return RuleI(Bots, MinTargetsForGroupSpell); + } + + return 0; +} + +void Bot::SetSpellTypePriority(uint16 spellType, uint8 priorityType, uint16 priority) { + switch (priorityType) { + case BotPriorityCategories::Idle: + _spellSettings[spellType].idlePriority = priority; + break; + case BotPriorityCategories::Engaged: + _spellSettings[spellType].engagedPriority = priority; + break; + case BotPriorityCategories::Pursue: + _spellSettings[spellType].pursuePriority = priority; + break; + default: + return; + } +} + +void Bot::SetSpellTypeResistLimit(uint16 spellType, uint16 resistLimit) { + _spellSettings[spellType].resistLimit = resistLimit; +} + +void Bot::SetSpellTypeAggroCheck(uint16 spellType, bool aggroCheck) { + _spellSettings[spellType].aggroCheck = aggroCheck; +} + +void Bot::SetSpellTypeMinManaLimit(uint16 spellType, uint8 manaLimit) { + _spellSettings[spellType].minManaPct = manaLimit; +} + +void Bot::SetSpellTypeMaxManaLimit(uint16 spellType, uint8 manaLimit) { + _spellSettings[spellType].maxManaPct = manaLimit; +} + +void Bot::SetSpellTypeMinHPLimit(uint16 spellType, uint8 hpLimit) { + _spellSettings[spellType].minHPPct = hpLimit; +} + +void Bot::SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit) { + _spellSettings[spellType].maxHPPct = hpLimit; +} + +void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount) { + _spellSettings[spellType].AEOrGroupTargetCount = targetCount; +} + +std::string Bot::GetBotSettingCategoryName(uint8 setting_type) { + switch (setting_type) { + case BotBaseSettings::ExpansionBitmask: + return "ExpansionBitmask"; + case BotBaseSettings::ShowHelm: + return "ShowHelm"; + case BotBaseSettings::FollowDistance: + return "FollowDistance"; + case BotBaseSettings::StopMeleeLevel: + return "StopMeleeLevel"; + case BotBaseSettings::EnforceSpellSettings: + return "EnforceSpellSettings"; + case BotBaseSettings::RangedSetting: + return "RangedSetting"; + case BotBaseSettings::PetSetTypeSetting: + return "PetSetTypeSetting"; + case BotBaseSettings::BehindMob: + return "BehindMob"; + case BotBaseSettings::CasterRange: + return "CasterRange"; + case BotBaseSettings::IllusionBlock: + return "IllusionBlock"; + case BotBaseSettings::MaxMeleeRange: + return "MaxMeleeRange"; + case BotBaseSettings::MedInCombat: + return "MedInCombat"; + case BotBaseSettings::HPWhenToMed: + return "HPWhenToMed"; + case BotBaseSettings::ManaWhenToMed: + return "ManaWhenToMed"; + default: + return "Null"; + } + + return "Null"; +} + +std::string Bot::GetBotSpellCategoryName(uint8 setting_type) { + switch (setting_type) { + case BotSettingCategories::BaseSetting: + return "BaseSetting"; + case BotSettingCategories::SpellHold: + return "SpellHold"; + case BotSettingCategories::SpellDelay: + return "SpellDelay"; + case BotSettingCategories::SpellMinThreshold: + return "SpellMinThreshold"; + case BotSettingCategories::SpellMaxThreshold: + return "SpellMaxThreshold"; + case BotSettingCategories::SpellTypeAggroCheck: + return "SpellTypeAggroCheck"; + case BotSettingCategories::SpellTypeMinManaPct: + return "SpellTypeMinManaPct"; + case BotSettingCategories::SpellTypeMaxManaPct: + return "SpellTypeMaxManaPct"; + case BotSettingCategories::SpellTypeMinHPPct: + return "SpellTypeMinHPPct"; + case BotSettingCategories::SpellTypeMaxHPPct: + return "SpellTypeMaxHPPct"; + case BotSettingCategories::SpellTypeIdlePriority: + return "SpellTypeIdlePriority"; + case BotSettingCategories::SpellTypeEngagedPriority: + return "SpellTypeEngagedPriority"; + case BotSettingCategories::SpellTypePursuePriority: + return "SpellTypePursuePriority"; + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + return "SpellTypeAEOrGroupTargetCount"; + case BotSettingCategories::SpellTypeRecastDelay: + return "SpellTypeRecastDelay"; + default: + return "Null"; + } + + return "Null"; +} + +std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { + std::list castOrder; + std::list tempCastOrder; + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; i++) { + BotSpellTypeOrder typeSettings = { + .spellType = i, + .priority = GetSpellTypePriority(i, priorityType) + }; + + castOrder.emplace_back(typeSettings); + } + + for (auto& currentType : castOrder) { + if (currentType.priority != 0) { + tempCastOrder.emplace_back(currentType); + } + } + + castOrder = tempCastOrder; + + if (castOrder.size() > 1) { + castOrder.sort( + [](BotSpellTypeOrder const& l, BotSpellTypeOrder const& r) { + return l.priority < r.priority; + } + ); + } + + return castOrder; +} + +bool Bot::AttemptAICastSpell(uint16 spellType) { + bool result = false; + + Mob* tar = GetTarget(); + + if (!taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme + return result; + } + + if (BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + if (!PrecastChecks(this, spellType) || !AICastSpell(this, GetChanceToCastBySpellType(spellType), spellType)) { + if (GetClass() == Class::Bard) { + return result; + } + + if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), BotAISpellRange, spellType)) { + return result; + } + } + } + } + else { + if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + return result; + } + } + + result = true; + + return result; +} + +uint16 Bot::GetSpellListSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEStun: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::Stun: + return BotSpellTypes::Nuke; + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return BotSpellTypes::RegularHeal; + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::Buff; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return BotSpellTypes::Mez; + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + return BotSpellTypes::Debuff; + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + return BotSpellTypes::Slow; + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + return BotSpellTypes::Snare; + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + return BotSpellTypes::Fear; + case BotSpellTypes::GroupCures: + case BotSpellTypes::Cure: + return BotSpellTypes::Cure; + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + return BotSpellTypes::Root; + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + return BotSpellTypes::Dispel; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + return BotSpellTypes::DOT; + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + return BotSpellTypes::Lifetap; + case BotSpellTypes::Charm: + case BotSpellTypes::Escape: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::Resurrect: + default: + return spellType; + } + + return spellType; +} + +bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { + if (IsAEBotSpellType(spellType) && !IsAnyAESpell(spellid)) { + return false; + } + + if (IsGroupBotSpellType(spellType) && !IsGroupSpell(spellid)) { + return false; + } + + switch (spellType) { //TODO bot rewrite - fix Buff/ResistBuff + case BotSpellTypes::Buff: + if (IsEffectInSpell(spellid, SE_DamageShield)) { + return false; + } + + if ( + IsEffectInSpell(spellid, SE_ResistMagic) || + IsEffectInSpell(spellid, SE_ResistFire) || + IsEffectInSpell(spellid, SE_ResistCold) || + IsEffectInSpell(spellid, SE_ResistPoison) || + IsEffectInSpell(spellid, SE_ResistDisease) || + IsEffectInSpell(spellid, SE_ResistCorruption) || + IsEffectInSpell(spellid, SE_ResistAll) + ) { + return false; + } + + return true; + case BotSpellTypes::ResistBuffs: + if ( + IsEffectInSpell(spellid, SE_ResistMagic) || + IsEffectInSpell(spellid, SE_ResistFire) || + IsEffectInSpell(spellid, SE_ResistCold) || + IsEffectInSpell(spellid, SE_ResistPoison) || + IsEffectInSpell(spellid, SE_ResistDisease) || + IsEffectInSpell(spellid, SE_ResistCorruption) || + IsEffectInSpell(spellid, SE_ResistAll) + ) { + return true; + } + + return false; + case BotSpellTypes::DamageShields: + if (IsEffectInSpell(spellid, SE_DamageShield)) { + return true; + } + + return false; + case BotSpellTypes::PBAENuke: + if (IsPBAENukeSpell(spellid) && !IsStunSpell(spellid)) { + return true; + } + + return false; + case BotSpellTypes::AERains: + if (IsAERainNukeSpell(spellid) && !IsStunSpell(spellid)) { + return true; + } + return false; + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + if (IsStunSpell(spellid)) { + return true; + } + + return false; + case BotSpellTypes::AENukes: + case BotSpellTypes::Nuke: + if (!IsStunSpell(spellid)) { + return true; + } + + return false; + default: + return true; + } + + return true; +} + +void Bot::SetCastedSpellType(uint16 spellType) { + _castedSpellType = spellType; +} + +void Bot::DoFaceCheckWithJitter(Mob* tar) { + if (!tar) { + return; + } + + if (IsMoving()) { + return; + } + + SetCombatJitter(); + if (!IsFacingMob(tar)) { + FaceTarget(tar); + return; + } + return; +} + +void Bot::DoFaceCheckNoJitter(Mob* tar) { + if (!tar) { + return; + } + + if (IsMoving()) { + return; + } + + if (!IsFacingMob(tar)) { + FaceTarget(tar); + return; + } + return; +} + +void Bot::RunToGoalWithJitter(glm::vec3 Goal) { + RunTo(Goal.x, Goal.y, Goal.z); + SetCombatJitter(); +} + +void Bot::SetCombatOutOfRangeJitter() { + SetCombatOutOfRangeJitterFlag(); + + if (RuleI(Bots, MaxJitterTimer) > 0) { + m_combat_jitter_timer.Start(zone->random.Int(RuleI(Bots, MinJitterTimer), RuleI(Bots, MaxJitterTimer)), true); + } +} + +void Bot::SetCombatJitter() { + SetCombatJitterFlag(); + + if (RuleI(Bots, MaxJitterTimer) > 0) { + m_combat_jitter_timer.Start(zone->random.Int(RuleI(Bots, MinJitterTimer), RuleI(Bots, MaxJitterTimer)), true); + } +} + +void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob) { + if (HasTargetReflection()) { + if (!tar->IsFeared() && !tar->IsStunned()) { + if (TryEvade(tar)) { + return; + } + } + + if (tar->IsRooted() && !taunting) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay + if (tar_distance <= melee_distance_max) { + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, false, true)) { + RunToGoalWithJitter(Goal); + return; + } + } + } + + if (taunting && tar_distance < melee_distance_min) { // Back up any taunting bots that are too close + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, taunting)) { + RunToGoalWithJitter(Goal); + return; + } + } + } + else { + if (!tar->IsFeared()) { + if (taunting) { // Taunting adjustments + Mob* mobTar = tar->GetTarget(); + if (!mobTar || mobTar == nullptr) { + DoFaceCheckNoJitter(tar); + return; + } + + if (RuleB(Bots, TauntingBotsFollowTopHate)) { // If enabled, taunting bots will stick to top hate + if ((DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + Goal = mobTar->GetPosition(); + RunToGoalWithJitter(Goal); + return; + } + } + else { // Otherwise, stick to any other bots that are taunting + if (mobTar->IsBot() && mobTar->CastToBot()->taunting && (DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + Goal = mobTar->GetPosition(); + RunToGoalWithJitter(Goal); + return; + } + } + } + else if (tar_distance < melee_distance_min || (GetBehindMob() && !behindMob) || !HasRequiredLoSForPositioning(tar)) { // Regular adjustment + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), taunting)) { + RunToGoalWithJitter(Goal); + return; + } + //else { + // if (stopMeleeLevel || IsBotArcher()) { + // if (IsBotArcher()) { + // float minArcheryRange = RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist); + // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, minArcheryRange, melee_distance, false, taunting)) { + // RunToGoalWithJitter(Goal); + // return; + // } + // } + // else { + // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_max + 1, melee_distance, false, taunting)) { + // RunToGoalWithJitter(Goal); + // return; + // } + // } + // } + // DoFaceCheckWithJitter(tar); + // return; + //} + } + } + } + + DoFaceCheckNoJitter(tar); + return; +} + +bool Bot::CheckDoubleRangedAttack() { + int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; + if (chance && zone->random.Roll(chance)) + return true; + + return false; +} + +bool Bot::RequiresLoSForPositioning() { + if (GetLevel() < GetStopMeleeLevel()) { + return true; + } + else if (GetClass() == Class::Bard) { + return false; + } + else if (GetClass() == Class::Cleric) { //TODO bot rewrite - add check to see if spell requires los + return false; + } + + return true; +} + +bool Bot::HasRequiredLoSForPositioning(Mob* tar) { + if (!tar) { + return true; + } + + if (GetClass() == Class::Cleric) { //add check to see if spell requires los + return true; + } + else if (GetClass() == Class::Bard && GetLevel() >= GetStopMeleeLevel()) { + return true; + } + if (!DoLosChecks(this, tar)) { + return false; + } + + return true; +} + +bool Bot::IsInGroupOrRaid(bool announce) { + if (!GetOwner()) { + return false; + } + + Mob* c = GetOwner(); + + if ( + (!GetRaid() && !GetGroup()) || + (!c->GetRaid() && !c->GetGroup()) + ) { + return false; + } + + if ( + c->GetRaid() && + ( + !GetRaid() || + c->GetRaid() != GetRaid() || + GetRaid()->GetGroup(GetCleanName()) == RAID_GROUPLESS + ) + ) { + return false; + } + + if ( + c->GetGroup() && + ( + !GetGroup() || + c->GetGroup() != GetGroup() + ) + ) { + return false; + } + + + if (announce) { + c->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I am not currently in your group or raid.", + GetCleanName() + ).c_str() + ); + } + + return true; +} + +bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar) { + int spellRange = botCaster->GetActSpellRange(spellid, spells[spellid].range); + int spellAERange = botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range); + int targetCount = 0; + + for (auto& close_mob : botCaster->m_close_mobs) { + Mob* m = close_mob.second; + + if (tar == m) { + continue; + } + + switch (spellType) { + case BotSpellTypes::AEDispel: + if (m->GetSpecialAbility(SpecialAbility::DispellImmunity)) { + continue; + } + + break; + case BotSpellTypes::AEFear: + if (m->GetSpecialAbility(SpecialAbility::FearImmunity)) { + continue; + } + + break; + case BotSpellTypes::AESnare: + if (m->GetSpecialAbility(SpecialAbility::SnareImmunity)) { + continue; + } + + break; + case BotSpellTypes::AESlow: + if (m->GetSpecialAbility(SpecialAbility::SlowImmunity)) { + continue; + } + + break; + default: + break; + } + + if (!m->IsNPC() || !m->CastToNPC()->IsOnHatelist(botCaster->GetOwner())) { + continue; + } + + if (SpellBreaksMez(spellid) && m->IsMezzed()) { + continue; + } + + if (IsPBAESpell(spellid)) { + if ( + spellAERange >= Distance(botCaster->GetPosition(), m->GetPosition()) && + botCaster->CastChecks(spellid, m, spellType, true, true) + ) { + ++targetCount; + } + } + else { + if (!tar || spellRange < Distance(botCaster->GetPosition(), tar->GetPosition()) || !DoLosChecks(this, m)) { + continue; + } + + if ( + spellAERange >= Distance(tar->GetPosition(), m->GetPosition()) && + botCaster->CastChecks(spellid, m, spellType, true, true) + ) { + ++targetCount; + } + } + } + + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + return false; + } + + SetHasLoS(true); + + return true; +} + +void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + to->SetBotBaseSetting(i, GetBotBaseSetting(i)); + } + + break; + case BotSettingCategories::SpellHold: + if (spellType != UINT16_MAX) { + to->SetSpellHold(spellType, GetSpellHold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellHold(i, GetSpellHold(i)); + } + } + + break; + case BotSettingCategories::SpellDelay: + if (spellType != UINT16_MAX) { + to->SetSpellDelay(spellType, GetSpellDelay(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellDelay(i, GetSpellDelay(i)); + } + } + + break; + case BotSettingCategories::SpellMinThreshold: + if (spellType != UINT16_MAX) { + to->SetSpellMinThreshold(spellType, GetSpellMinThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellMinThreshold(i, GetSpellMinThreshold(i)); + } + } + + break; + case BotSettingCategories::SpellMaxThreshold: + if (spellType != UINT16_MAX) { + to->SetSpellMaxThreshold(spellType, GetSpellMaxThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellMaxThreshold(i, GetSpellMaxThreshold(i)); + } + } + + break; + case BotSettingCategories::SpellTypeAggroCheck: + if (spellType != UINT16_MAX) { + to->SetSpellTypeAggroCheck(spellType, GetSpellTypeAggroCheck(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeAggroCheck(i, GetSpellTypeAggroCheck(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMinManaPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMinManaLimit(spellType, GetSpellTypeMinManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMinManaLimit(i, GetSpellTypeMinManaLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMaxManaPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMaxManaLimit(spellType, GetSpellTypeMaxManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMaxManaLimit(i, GetSpellTypeMaxManaLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMinHPPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMinHPLimit(spellType, GetSpellTypeMinHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMinHPLimit(i, GetSpellTypeMinHPLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMaxHPPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMaxHPLimit(spellType, GetSpellTypeMaxHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMaxHPLimit(i, GetSpellTypeMaxHPLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeIdlePriority: + if (spellType != UINT16_MAX) { + to->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, GetSpellTypePriority(spellType, BotPriorityCategories::Idle)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypePriority(i, BotPriorityCategories::Idle, GetSpellTypePriority(i, BotPriorityCategories::Idle)); + } + } + + break; + case BotSettingCategories::SpellTypeEngagedPriority: + if (spellType != UINT16_MAX) { + to->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, GetSpellTypePriority(spellType, BotPriorityCategories::Engaged)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypePriority(i, BotPriorityCategories::Engaged, GetSpellTypePriority(i, BotPriorityCategories::Engaged)); + } + } + + break; + case BotSettingCategories::SpellTypePursuePriority: + if (spellType != UINT16_MAX) { + to->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, GetSpellTypePriority(spellType, BotPriorityCategories::Pursue)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypePriority(i, BotPriorityCategories::Pursue, GetSpellTypePriority(i, BotPriorityCategories::Pursue)); + } + } + + break; + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + if (spellType != UINT16_MAX) { + to->SetSpellTypeAEOrGroupTargetCount(spellType, GetSpellTypeAEOrGroupTargetCount(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeAEOrGroupTargetCount(i, GetSpellTypeAEOrGroupTargetCount(i)); + } + } + + break; + } +} + +void Bot::CopyBotSpellSettings(Bot* to) +{ + to->ResetBotSpellSettings(); + to->bot_spell_settings.clear(); + + auto s = BotSpellSettingsRepository::GetWhere(content_db, fmt::format("bot_id = {}", GetBotID())); + if (s.empty()) { + return; + } + + auto* spell_list = content_db.GetBotSpells(to->GetBotSpellID()); + + for (const auto& e : s) { + BotSpellSetting b; + + b.priority = e.priority; + b.min_hp = e.min_hp; + b.max_hp = e.max_hp; + b.is_enabled = e.is_enabled; + + if (IsSpellInBotList(spell_list, e.spell_id)) { + for (auto& se : spell_list->entries) { + if (se.spellid == e.spell_id) { + if (EQ::ValueWithin(to->GetLevel(), se.minlevel, se.maxlevel) && se.spellid) { + to->AddBotSpellSetting(e.spell_id, &b); + } + } + } + } + } + + to->LoadBotSpellSettings(); + to->AI_AddBotSpells(to->GetBotSpellID()); + to->SetBotEnforceSpellSetting(GetBotEnforceSpellSetting()); +} + +void Bot::ResetBotSpellSettings() +{ + auto s = BotSpellSettingsRepository::GetWhere(content_db, fmt::format("bot_id = {}", GetBotID())); + if (s.empty()) { + return; + } + + for (const auto& e : s) { + DeleteBotSpellSetting(e.spell_id); + } + + LoadBotSpellSettings(); + AI_AddBotSpells(GetBotSpellID()); + SetBotEnforceSpellSetting(false); +} diff --git a/zone/bot.h b/zone/bot.h index 68b027f052..97d987e567 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -42,6 +42,9 @@ constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500; // as DSq value (50 uni constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds +constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 1500; // 1.5 seconds +constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MAX = 3000; // 3 seconds + constexpr uint32 MAG_EPIC_1_0 = 28034; extern WorldServer worldserver; @@ -49,6 +52,13 @@ extern WorldServer worldserver; constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this constexpr int NegativeItemReuse = -1; // Unlinked timer for items +constexpr uint8 SumWater = 1; +constexpr uint8 SumFire = 2; +constexpr uint8 SumAir = 3; +constexpr uint8 SumEarth = 4; +constexpr uint8 MonsterSum = 5; +constexpr uint8 SumMageMultiElement = 6; + // nHSND negative Healer/Slower/Nuker/Doter // pH positive Healer // pS positive Slower @@ -87,6 +97,58 @@ enum BotCastingChanceConditional : uint8 cntHSND = 16 }; +namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed + constexpr uint8 BaseSetting = 0; + constexpr uint8 SpellHold = 1; + constexpr uint8 SpellDelay = 2; + constexpr uint8 SpellMinThreshold = 3; + constexpr uint8 SpellMaxThreshold = 4; + constexpr uint8 SpellTypeAggroCheck = 5; + constexpr uint8 SpellTypeMinManaPct = 6; + constexpr uint8 SpellTypeMaxManaPct = 7; + constexpr uint8 SpellTypeMinHPPct = 8; + constexpr uint8 SpellTypeMaxHPPct = 9; + constexpr uint8 SpellTypeIdlePriority = 10; + constexpr uint8 SpellTypeEngagedPriority = 11; + constexpr uint8 SpellTypePursuePriority = 12; + constexpr uint8 SpellTypeAEOrGroupTargetCount = 13; + constexpr uint8 SpellTypeRecastDelay = 14; + + constexpr uint16 START = BotSettingCategories::BaseSetting; + constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; + constexpr uint16 START_CLIENT = BotSettingCategories::SpellHold; + constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; + constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; // Increment as needed +}; + +namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed + constexpr uint8 Idle = 0; + constexpr uint8 Engaged = 1; + constexpr uint8 Pursue = 2; + + constexpr uint16 START = BotPriorityCategories::Idle; + constexpr uint16 END = BotPriorityCategories::Pursue; // Increment as needed +}; + +namespace BotBaseSettings { + constexpr uint16 ExpansionBitmask = 0; + constexpr uint16 ShowHelm = 1; + constexpr uint16 FollowDistance = 2; + constexpr uint16 StopMeleeLevel = 3; + constexpr uint16 EnforceSpellSettings = 4; + constexpr uint16 RangedSetting = 5; + constexpr uint16 PetSetTypeSetting = 6; + constexpr uint16 BehindMob = 7; + constexpr uint16 CasterRange = 8; + constexpr uint16 IllusionBlock = 9; + constexpr uint16 MaxMeleeRange = 10; + constexpr uint16 MedInCombat = 11; + constexpr uint16 HPWhenToMed = 12; + constexpr uint16 ManaWhenToMed = 13; + + constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players + constexpr uint16 END = BotBaseSettings::ManaWhenToMed; // Increment as needed +}; class Bot : public NPC { friend class Mob; @@ -128,7 +190,7 @@ class Bot : public NPC { // Class Constructors Bot(NPCType *npcTypeData, Client* botOwner); - Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData, int32 expansion_bitmask); + Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData); //abstract virtual override function implementations requird by base abstract class bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false) override; @@ -199,8 +261,8 @@ class Bot : public NPC { void Camp(bool save_to_database = true); void SetTarget(Mob* mob) override; void Zone(); - bool IsArcheryRange(Mob* target); - void ChangeBotArcherWeapons(bool isArcher); + bool IsAtRange(Mob* target); + void ChangeBotRangedWeapons(bool isRanged); void Sit(); void Stand(); bool IsSitting() const override; @@ -228,7 +290,7 @@ class Bot : public NPC { uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid); bool GetNeedsCured(Mob *tar); bool GetNeedsHateRedux(Mob *tar); - bool HasOrMayGetAggro(); + bool HasOrMayGetAggro(bool SitAggro, uint32 spell_id = 0); void SetDefaultBotStance(); void SetSurname(std::string_view bot_surname); void SetTitle(std::string_view bot_title); @@ -327,18 +389,20 @@ class Bot : public NPC { void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); // AI Methods - bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes); + bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType); + bool AttemptAICastSpell(uint16 spellType); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; bool AIHealRotation(Mob* tar, bool useFastHeals); bool GetPauseAI() const { return _pauseAI; } void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; } - uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } - void SetStopMeleeLevel(uint8 level); + bool IsCommandedSpell() const { return _commandedSpell; } + void SetCommandedSpell(bool value) { _commandedSpell = value; } + void SetGuardMode(); void SetHoldMode(); - uint32 GetBotCasterRange() const { return m_bot_caster_range; } + bool IsValidSpellRange(uint16 spell_id, Mob const* tar); // Bot AI Methods @@ -377,6 +441,97 @@ class Bot : public NPC { inline bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override { return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); } + void DoAttackRounds(Mob* target, int hand); + + std::vector GatherGroupSpellTargets(Mob* target = nullptr, bool noClients = false, bool noBots = false); + std::vector GatherSpellTargets(bool entireRaid = false, bool noClients = false, bool noBots = false, bool noPets = false); + + bool PrecastChecks(Mob* tar, uint16 spellType); + bool CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); + bool CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar); + bool BotHasEnoughMana(uint16 spell_id); + bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid); + bool DoResistCheck(Mob* target, uint16 spellid, int32 resist_limit); + bool DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType); + bool IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType); + bool IsMobEngagedByAnyone(Mob* tar); + void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); + void CopySettings(Bot* to, uint8 settingType, uint16 spellType = UINT16_MAX); + void CopyBotSpellSettings(Bot* to); + void ResetBotSpellSettings(); + int GetBotBaseSetting(uint16 botSetting); + int GetDefaultBotBaseSetting(uint16 botSetting); + void SetBotBaseSetting(uint16 botSetting, int settingValue); + void LoadDefaultBotSettings(); + void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); + BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); + + std::string GetBotSpellCategoryName(uint8 setting_type); + std::string GetBotSettingCategoryName(uint8 setting_type); + + int GetDefaultSetting(uint16 settingCategory, uint16 settingType); + uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass); + uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass); + uint16 GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass); + uint16 GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass); + uint16 GetDefaultSpellTypeResistLimit(uint16 spellType); + bool GetDefaultSpellTypeAggroCheck(uint16 spellType); + uint8 GetDefaultSpellTypeMinManaLimit(uint16 spellType); + uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spellType); + uint8 GetDefaultSpellTypeMinHPLimit(uint16 spellType); + uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spellType); + uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType); + + int GetSetting(uint16 settingCategory, uint16 settingType); + uint16 GetSpellTypePriority(uint16 spellType, uint8 priorityType); + void SetSpellTypePriority(uint16 spellType, uint8 priorityType, uint16 priority); + inline uint16 GetSpellTypeResistLimit(uint16 spellType) const { return _spellSettings[spellType].resistLimit; } + void SetSpellTypeResistLimit(uint16 spellType, uint16 resistLimit); + inline bool GetSpellTypeAggroCheck(uint16 spellType) const { return _spellSettings[spellType].aggroCheck; } + void SetSpellTypeAggroCheck(uint16 spellType, bool AggroCheck); + inline uint8 GetSpellTypeMinManaLimit(uint16 spellType) const { return _spellSettings[spellType].minManaPct; } + inline uint8 GetSpellTypeMaxManaLimit(uint16 spellType) const { return _spellSettings[spellType].maxManaPct; } + void SetSpellTypeMinManaLimit(uint16 spellType, uint8 manaLimit); + void SetSpellTypeMaxManaLimit(uint16 spellType, uint8 manaLimit); + inline uint8 GetSpellTypeMinHPLimit(uint16 spellType) const { return _spellSettings[spellType].minHPPct; } + inline uint8 GetSpellTypeMaxHPLimit(uint16 spellType) const { return _spellSettings[spellType].maxHPPct; } + void SetSpellTypeMinHPLimit(uint16 spellType, uint8 hpLimit); + void SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit); + inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spellType) const { return _spellSettings[spellType].AEOrGroupTargetCount; } + void SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount); + + bool GetShowHelm() const { return _showHelm; } + void SetShowHelm(bool showHelm) { _showHelm = showHelm; } + bool GetBehindMob() const { return _behindMobStatus; } + void SetBehindMob(bool value) { _behindMobStatus = value; } + bool GetMaxMeleeRange() const { return _maxMeleeRangeStatus; } + void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; } + uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } + void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; } + uint32 GetBotCasterRange() const { return _casterRange; } + void SetBotCasterRange(uint32 casterRange) { _casterRange = casterRange; } + bool GetMedInCombat() const { return _medInCombat; } + void SetMedInCombat(bool value) { _medInCombat = value; } + uint8 GetHPWhenToMed() const { return _HPWhenToMed; } + void SetHPWhenToMed(uint8 value) { _HPWhenToMed = value; } + uint8 GetManaWhenToMed() const { return _ManaWhenToMed; } + void SetManaWhenToMed(uint8 value) { _ManaWhenToMed = value; } + void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } + bool HasLoS() const { return _hasLoS; } + + bool IsInGroupOrRaid(bool announce = false); + void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false); + + std::list GetSpellTypesPrioritized(uint8 priorityType); + uint16 GetSpellListSpellType(uint16 spellType); + bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid); + inline uint16 GetCastedSpellType() const { return _castedSpellType; } + void SetCastedSpellType(uint16 spellType); + + bool HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar); + + void CheckBotSpells(); + [[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; } [[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; } @@ -423,33 +578,36 @@ class Bot : public NPC { ProcessBotGroupAdd(Group* group, Raid* raid, Client* client = nullptr, bool new_raid = false, bool initial = false); - static std::list GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect); - static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType); - static std::list GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType); - static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType); - - static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType); - static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster); - static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster); - static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* botCaster); - static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForMagicBasedSlow(Bot* botCaster); - static BotSpell GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster); - - static Mob* GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell); - static BotSpell GetBestBotSpellForMez(Bot* botCaster); - static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster); + static std::list GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect); + static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType); + static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); + static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false); + + static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); + static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + + static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE = false); + bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid); + static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); + static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); static std::string GetBotMagicianPetType(Bot* botCaster); - static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType); - static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType); - static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target, uint16 spellType); static BotSpell GetDebuffBotSpell(Bot* botCaster, Mob* target); - static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target, uint16 spellType); static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellType); + static BotSpell GetBestBotSpellForCharm(Bot* botCaster, Mob* target, uint16 spellType); static NPCType *CreateDefaultNPCTypeStructForBot( const std::string& botName, @@ -472,12 +630,11 @@ class Bot : public NPC { uint32 GetBotOwnerCharacterID() const { return _botOwnerCharacterID; } uint32 GetBotSpellID() const { return npc_spells_id; } Mob* GetBotOwner() { return this->_botOwner; } - uint32 GetBotArcheryRange(); + uint32 GetBotRangedValue(); EQ::ItemInstance* GetBotItem(uint16 slot_id); bool GetSpawnStatus() { return _spawnStatus; } uint8 GetPetChooserID() { return _petChooserID; } - bool IsPetChooser() { return _petChooser; } - bool IsBotArcher() { return m_bot_archery_setting; } + bool IsBotRanged() { return _botRangedSetting; } bool IsBotCharmer() { return _botCharmer; } bool IsBot() const override { return true; } bool IsOfClientBot() const override { return true; } @@ -485,8 +642,7 @@ class Bot : public NPC { bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; } uint8 GetBotStance() { return _botStance; } - uint8 GetChanceToCastBySpellType(uint32 spellType); - bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; } + uint8 GetChanceToCastBySpellType(uint16 spellType); float GetBotCasterMaxRange(float melee_distance_max); bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; } bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; } @@ -527,8 +683,6 @@ class Bot : public NPC { std::shared_ptr* MemberOfHealRotation() { return &m_member_of_heal_rotation; } - bool GetAltOutOfCombatBehavior() const { return _altoutofcombatbehavior;} - bool GetShowHelm() const { return _showhelm; } inline int32 GetSTR() const override { return STR; } inline int32 GetSTA() const override { return STA; } inline int32 GetDEX() const override { return DEX; } @@ -600,13 +754,11 @@ class Bot : public NPC { void SetBotSpellID(uint32 newSpellID); void SetSpawnStatus(bool spawnStatus) { _spawnStatus = spawnStatus; } void SetPetChooserID(uint8 id) { _petChooserID = id; } - void SetBotArcherySetting(bool bot_archer_setting, bool save = false); + void SetBotRangedSetting(bool botRangedSetting) { _botRangedSetting = botRangedSetting; } void SetBotCharmer(bool c) { _botCharmer = c; } - void SetPetChooser(bool p) { _petChooser = p; } void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; } void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; } void SetBotStance(uint8 stance_id) { _botStance = Stance::IsValid(stance_id) ? stance_id : Stance::Passive; } - void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; } uint32 GetSpellRecastTimer(uint16 spell_id = 0); bool CheckSpellRecastTimer(uint16 spell_id = 0); uint32 GetSpellRecastRemainingTime(uint16 spell_id = 0); @@ -624,8 +776,6 @@ class Bot : public NPC { void ClearSpellRecastTimer(uint16 spell_id = 0); uint32 GetItemReuseRemainingTime(uint32 item_id = 0); void ClearExpiredTimers(); - void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;} - void SetShowHelm(bool showhelm) { _showhelm = showhelm; } void SetBeardColor(uint8 value) { beardcolor = value; } void SetBeard(uint8 value) { beard = value; } void SetEyeColor1(uint8 value) { eyecolor1 = value; } @@ -639,7 +789,7 @@ class Bot : public NPC { bool DyeArmor(int16 slot_id, uint32 rgb, bool all_flag = false, bool save_flag = true); int GetExpansionBitmask(); - void SetExpansionBitmask(int expansion_bitmask, bool save = true); + void SetExpansionBitmask(int expansionBitmask); void ListBotSpells(uint8 min_level); @@ -651,15 +801,12 @@ class Bot : public NPC { void ListBotSpellSettings(); void LoadBotSpellSettings(); bool UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs); - void SetBotEnforceSpellSetting(bool enforcespellsettings, bool save = false); - bool GetBotEnforceSpellSetting() const { return m_enforce_spell_settings; } + void SetBotEnforceSpellSetting(bool enforceSpellSettings); + bool GetBotEnforceSpellSetting() { return _enforceSpellSettings; } // Class Destructors ~Bot() override; - // Publicized protected functions - void BotRangedAttack(Mob* other); - // Publicized private functions static NPCType *FillNPCTypeStruct( uint32 botSpellsID, @@ -750,24 +897,11 @@ class Bot : public NPC { static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND]; - bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid); - bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid); - bool BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los); - bool BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass); - bool BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iSpellTypes); - bool BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los); - bool BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const bool& checked_los); - bool BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell); - bool BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass); - bool BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes); - bool BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes); - bool BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const bool& checked_los); - bool BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los, Raid* raid); - bool BotCastDebuff(Mob* tar, uint8 botLevel, BotSpell& botSpell, bool checked_los); - bool BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, Raid* raid); - bool BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpell); - bool BotCastCombatSong(Mob* tar, uint8 botLevel); - bool BotCastSong(Mob* tar, uint8 botLevel); + bool BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); bool CheckIfIncapacitated(); bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid); @@ -796,39 +930,65 @@ class Bot : public NPC { const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, - bool behind_mob, + bool behindMob, bool backstab_weapon, float& melee_distance_max, - float& melee_distance - ) const; + float& melee_distance, + float& melee_distance_min, + uint8 stopMeleeLevel + ); // Combat Checks void SetBerserkState(); bool CheckIfCasting(float fm_distance); void HealRotationChecks(); - void CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item); + void CheckCombatRange( + Mob* tar, + float tar_distance, + bool& atCombatRange, + bool& behindMob, + const EQ::ItemInstance*& p_item, + const EQ::ItemInstance*& s_item, + float& melee_distance_min, + float& melee_distance_max, + float& melee_distance, + uint8 stopMeleeLevel + ); + bool GetCombatJitterFlag() { return m_combat_jitter_flag; } + void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; } + bool GetCombatOutOfRangeJitterFlag() { return m_combat_out_of_range_jitter_flag; } + void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; } + void SetCombatJitter(); + void SetCombatOutOfRangeJitter(); + void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob); + void DoFaceCheckWithJitter(Mob* tar); + void DoFaceCheckNoJitter(Mob* tar); + void RunToGoalWithJitter(glm::vec3 Goal); + bool RequiresLoSForPositioning(); + bool HasRequiredLoSForPositioning(Mob* tar); // Try Combat Methods bool TryEvade(Mob* tar); bool TryFacingTarget(Mob* tar); bool TryRangedAttack(Mob* tar); - bool TryClassAttacks(Mob* tar); - bool TryPrimaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* p_item); - bool TrySecondaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* s_item); bool TryPursueTarget(float leash_distance, glm::vec3& Goal); bool TryMeditate(); bool TryAutoDefend(Client* bot_owner, float leash_distance); bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); bool TryBardMovementCasts(); - void SetRangerCombatWeapon(bool atArcheryRange); + void BotRangedAttack(Mob* other, bool CanDoubleAttack = false); + bool CheckDoubleRangedAttack(); // Public "Refactor" Methods static bool CheckSpawnConditions(Client* c); + inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); } + protected: void BotMeditate(bool isSitting); bool CheckBotDoubleAttack(bool Triple = false); + bool CheckTripleAttack(); void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client); bool AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = nullptr) override; @@ -849,9 +1009,7 @@ class Bot : public NPC { uint32 _botOwnerCharacterID; bool _spawnStatus; Mob* _botOwner; - bool m_bot_archery_setting; bool _botCharmer; - bool _petChooser; uint8 _petChooserID; bool berserk; EQ::InventoryProfile m_inv; @@ -876,9 +1034,15 @@ class Bot : public NPC { int32 max_end; int32 end_regen; - Timer m_evade_timer; // can be moved to pTimers at some point + Timer m_rogue_evade_timer; // Rogue evade timer + Timer m_monk_evade_timer; // Monk evade FD timer Timer m_auto_defend_timer; Timer auto_save_timer; + + Timer m_combat_jitter_timer; + bool m_combat_jitter_flag; + bool m_combat_out_of_range_jitter_flag; + bool m_dirtyautohaters; bool m_guard_flag; bool m_hold_flag; @@ -888,7 +1052,7 @@ class Bot : public NPC { bool m_pulling_flag; bool m_returning_flag; bool is_using_item_click; - uint32 m_bot_caster_range; + BotCastingRoles m_CastingRoles; std::map bot_spell_settings; @@ -896,12 +1060,22 @@ class Bot : public NPC { std::shared_ptr m_member_of_heal_rotation; InspectMessage_Struct _botInspectMessage; - bool _altoutofcombatbehavior; - bool _showhelm; bool _pauseAI; + + int _expansionBitmask; + bool _enforceSpellSettings; + bool _showHelm; + bool _botRangedSetting; uint8 _stopMeleeLevel; - int m_expansion_bitmask; - bool m_enforce_spell_settings; + uint32 _casterRange; + bool _behindMobStatus; + bool _maxMeleeRangeStatus; + bool _medInCombat; + uint8 _HPWhenToMed; + uint8 _ManaWhenToMed; + uint16 _castedSpellType; + bool _hasLoS; + bool _commandedSpell; // Private "base stats" Members int32 _baseMR; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index b15531dd75..3f6dfef5b1 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1249,8 +1249,8 @@ int bot_command_init(void) bot_command_add("actionable", "Lists actionable command arguments and use descriptions", AccountStatus::Player, bot_command_actionable) || bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", AccountStatus::Player, bot_command_aggressive) || bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) || - bot_command_add("applypotion", "Applies cursor-held potion to a bot's effects", AccountStatus::Player, bot_command_apply_potion) || bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) || + bot_command_add("behindmob", "Toggles whether or not your bot tries to stay behind a mob", AccountStatus::Player, bot_command_behind_mob) || bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", AccountStatus::Player, bot_command_bind_affinity) || bot_command_add("bot", "Lists the available bot management [subcommands]", AccountStatus::Player, bot_command_bot) || bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", AccountStatus::Player, bot_command_appearance) || @@ -1270,8 +1270,8 @@ int bot_command_init(void) bot_command_add("botheritage", "Changes the Drakkin heritage of a bot", AccountStatus::Player, bot_command_heritage) || bot_command_add("botinspectmessage", "Changes the inspect message of a bot", AccountStatus::Player, bot_command_inspect_message) || bot_command_add("botlist", "Lists the bots that you own", AccountStatus::Player, bot_command_list_bots) || - bot_command_add("botoutofcombat", "Toggles your bot between standard and out-of-combat spell/skill use - if any specialized behaviors exist", AccountStatus::Player, bot_command_out_of_combat) || bot_command_add("botreport", "Orders a bot to report its readiness", AccountStatus::Player, bot_command_report) || + bot_command_add("botsettings", "Lists settings related to spell types and bot combat", AccountStatus::Player, bot_command_bot_settings) || bot_command_add("botspawn", "Spawns a created bot", AccountStatus::Player, bot_command_spawn) || bot_command_add("botstance", "Changes the stance of a bot", AccountStatus::Player, bot_command_stance) || bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", AccountStatus::Player, bot_command_stop_melee_level) || @@ -1279,16 +1279,20 @@ int bot_command_init(void) bot_command_add("botsummon", "Summons bot(s) to your location", AccountStatus::Player, bot_command_summon) || bot_command_add("botsurname", "Sets a bots surname (last name)", AccountStatus::Player, bot_command_surname) || bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", AccountStatus::Player, bot_command_tattoo) || - bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", AccountStatus::Player, bot_command_toggle_archer) || bot_command_add("bottogglehelm", "Toggles the helm visibility of a bot between shown and hidden", AccountStatus::Player, bot_command_toggle_helm) || + bot_command_add("bottoggleranged", "Toggles a ranged bot between melee and ranged weapon use", AccountStatus::Player, bot_command_toggle_ranged) || bot_command_add("bottitle", "Sets a bots title", AccountStatus::Player, bot_command_title) || bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_command_update) || bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_command_woad) || + bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || bot_command_add("casterrange", "Controls the range casters will try to stay away from a mob (if too far, they will skip spells that are out-of-range)", AccountStatus::Player, bot_command_caster_range) || bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_circle) || + bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || + bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) || + bot_command_add("defaultsettings", "Restores a bot back to default settings", AccountStatus::Player, bot_command_default_settings) || bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || bot_command_add("enforcespellsettings", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || @@ -1320,6 +1324,7 @@ int bot_command_init(void) bot_command_add("help", "List available commands and their description - specify partial command as argument to search", AccountStatus::Player, bot_command_help) || bot_command_add("hold", "Prevents a bot from attacking until released", AccountStatus::Player, bot_command_hold) || bot_command_add("identify", "Orders a bot to cast an item identification spell", AccountStatus::Player, bot_command_identify) || + bot_command_add("illusionblock", "Control whether or not illusion effects will land on the bot if casted by another player or bot", AccountStatus::Player, bot_command_illusion_block) || bot_command_add("inventory", "Lists the available bot inventory [subcommands]", AccountStatus::Player, bot_command_inventory) || bot_command_add("inventorygive", "Gives the item on your cursor to a bot", AccountStatus::Player, bot_command_inventory_give) || bot_command_add("inventorylist", "Lists all items in a bot's inventory", AccountStatus::Player, bot_command_inventory_list) || @@ -1329,6 +1334,7 @@ int bot_command_init(void) bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", AccountStatus::Player, bot_command_item_use) || bot_command_add("levitation", "Orders a bot to cast a levitation spell", AccountStatus::Player, bot_command_levitation) || bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || + bot_command_add("maxmeleerange", "Toggles whether your bot is at max melee range or not. This will disable all special abilities, including taunt.", AccountStatus::Player, bot_command_max_melee_range) || bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", AccountStatus::Player, bot_command_mesmerize) || bot_command_add("movementspeed", "Orders a bot to cast a movement speed enhancement spell", AccountStatus::Player, bot_command_movement_speed) || bot_command_add("owneroption", "Sets options available to bot owners", AccountStatus::Player, bot_command_owner_option) || @@ -1346,7 +1352,23 @@ int bot_command_init(void) bot_command_add("resurrect", "Orders a bot to resurrect a player's (players') corpse(s)", AccountStatus::Player, bot_command_resurrect) || 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("sithppercent", "HP threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_hp_percent) || + bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || + bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || + bot_command_add("size", "Orders a bot to change a player's size", AccountStatus::Player, bot_command_size) || + bot_command_add("spellaggrochecks", "Toggles whether or not bots will cast a spell type if they think it will get them aggro", AccountStatus::Player, bot_command_spell_aggro_checks) || + bot_command_add("spellengagedpriority", "Controls the order of casts by spell type when engaged in combat", AccountStatus::Player, bot_command_spell_engaged_priority) || + bot_command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, bot_command_spell_delays) || + bot_command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, bot_command_spell_holds) || + bot_command_add("spellidlepriority", "Controls the order of casts by spell type when out of combat", AccountStatus::Player, bot_command_spell_idle_priority) || + bot_command_add("spellmaxhppct", "Controls at what HP percent a bot will stop casting different spell types", AccountStatus::Player, bot_command_spell_max_hp_pct) || + bot_command_add("spellmaxmanapct", "Controls at what mana percent a bot will stop casting different spell types", AccountStatus::Player, bot_command_spell_max_mana_pct) || + bot_command_add("spellmaxthresholds", "Controls the minimum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, bot_command_spell_max_thresholds) || + bot_command_add("spellminhppct", "Controls at what HP percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_hp_pct) || + bot_command_add("spellminmanapct", "Controls at what mana percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_mana_pct) || + bot_command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, bot_command_spell_min_thresholds) || + bot_command_add("spellpursuepriority", "Controls the order of casts by spell type when pursuing in combat", AccountStatus::Player, bot_command_spell_pursue_priority) || + bot_command_add("spelltargetcount", "Sets the required target amount for group/AE spells by spell type", AccountStatus::Player, bot_command_spell_target_count) || bot_command_add("spellinfo", "Opens a dialogue window with spell info", AccountStatus::Player, bot_spell_info_dialogue_window) || bot_command_add("spells", "Lists all Spells learned by the Bot.", AccountStatus::Player, bot_command_spell_list) || bot_command_add("spellsettings", "Lists a bot's spell setting entries", AccountStatus::Player, bot_command_spell_settings_list) || @@ -1360,7 +1382,7 @@ int bot_command_init(void) bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || bot_command_add("track", "Orders a capable bot to track enemies", AccountStatus::Player, bot_command_track) || bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) || - bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing) + bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing) ) { bot_command_deinit(); return -1; @@ -1613,7 +1635,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->Message( Chat::White, fmt::format( - "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z'. Mixed case {} allowed.", + "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z' and it must be between 4 and 15 characters. Mixed case {} allowed.", bot_name, RuleB(Bots, AllowCamelCaseNames) ? "is" : "is not" ).c_str() ); @@ -1625,7 +1647,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->Message( Chat::White, fmt::format( - "Failed to query name availability for '{}'.", + "'{}' is already in use or an invalid name.", bot_name ).c_str() ); @@ -1806,40 +1828,6 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } -void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot) -{ - if (!bot_owner || !my_bot) - return; - - switch (my_bot->GetClass()) { - case Class::Warrior: - case Class::Cleric: - case Class::Paladin: - case Class::Ranger: - case Class::ShadowKnight: - case Class::Druid: - case Class::Monk: - bot_owner->Message(Chat::White, "%s has no out-of-combat behavior defined", my_bot->GetCleanName()); - break; - case Class::Bard: - bot_owner->Message(Chat::White, "%s will %s use out-of-combat behavior for bard songs", my_bot->GetCleanName(), ((my_bot->GetAltOutOfCombatBehavior()) ? ("now") : ("no longer"))); - break; - case Class::Rogue: - case Class::Shaman: - case Class::Necromancer: - case Class::Wizard: - case Class::Magician: - case Class::Enchanter: - case Class::Beastlord: - case Class::Berserker: - bot_owner->Message(Chat::White, "%s has no out-of-combat behavior defined", my_bot->GetCleanName()); - break; - default: - break; - bot_owner->Message(Chat::White, "Undefined bot class for %s", my_bot->GetCleanName()); - } -} - int helper_bot_follow_option_chain(Client* bot_owner) { if (!bot_owner) { @@ -2085,8 +2073,11 @@ void helper_send_available_subcommands(Client *bot_owner, const char* command_si bot_owner->Message( Chat::White, fmt::format( - "^{} - {}", - subcommand_iter, + "{} - {}", + Saylink::Silent( + fmt::format("^{} help", subcommand_iter), + fmt::format("^{}", subcommand_iter) + ), find_iter != bot_command_list.end() ? find_iter->second->desc : "No Description" ).c_str() ); @@ -2126,18 +2117,152 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp return false; } +void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt) { + if (helpPrompt) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {}, {}, {} for a list of spell types by ID", + Saylink::Silent( + fmt::format("{} listid 0-19", arg0) + ), + Saylink::Silent( + fmt::format("{} listid 20-39", arg0) + ), + Saylink::Silent( + fmt::format("{} listid 40+", arg0) + ) + ).c_str() + ); + + c->Message( + Chat::Yellow, + fmt::format( + "Use {}, {}, {} for a list of spell types by short name", + Saylink::Silent( + fmt::format("{} listname 0-19", arg0) + ), + Saylink::Silent( + fmt::format("{} listname 20-39", arg0) + ), + Saylink::Silent( + fmt::format("{} listname 40+", arg0) + ) + ).c_str() + ); + + return; + } + + uint8 minCount = 0; + uint8 maxCount = 0; + + if (BotSpellTypes::END <= 19) { + minCount = BotSpellTypes::START; + maxCount = BotSpellTypes::END; + } + else if (!arg2.compare("0-19")) { + minCount = BotSpellTypes::START; + maxCount = 19; + } + else if (!arg2.compare("20-39")) { + minCount = std::min(static_cast(20), static_cast(BotSpellTypes::END)); + maxCount = std::min(static_cast(39), static_cast(BotSpellTypes::END)); + } + else if (!arg2.compare("40+")) { + minCount = std::min(static_cast(40), static_cast(BotSpellTypes::END)); + maxCount = BotSpellTypes::END; + } + else { + c->Message(Chat::Yellow, "You must choose a valid range option"); + + return; + } + + const std::string& indian_red = "indian_red"; + const std::string& gold = "gold"; + const std::string& slate_blue = "slate_blue"; + const std::string& forest_green = "forest_green"; + const std::string& goldenrod = "goldenrod"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(goldenrod, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(gold, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(gold, fillerLine) + ) + ) + ); + + for (int i = minCount; i <= maxCount; ++i) { + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(forest_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(forest_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); +} + + #include "bot_commands/actionable.cpp" #include "bot_commands/aggressive.cpp" #include "bot_commands/appearance.cpp" #include "bot_commands/apply_poison.cpp" #include "bot_commands/apply_potion.cpp" #include "bot_commands/attack.cpp" +#include "bot_commands/behind_mob.cpp" #include "bot_commands/bind_affinity.cpp" #include "bot_commands/bot.cpp" +#include "bot_commands/bot_settings.cpp" +#include "bot_commands/cast.cpp" #include "bot_commands/caster_range.cpp" #include "bot_commands/charm.cpp" +#include "bot_commands/class_race_list.cpp" #include "bot_commands/click_item.cpp" +#include "bot_commands/copy_settings.cpp" #include "bot_commands/cure.cpp" +#include "bot_commands/default_settings.cpp" #include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" #include "bot_commands/escape.cpp" @@ -2148,11 +2273,13 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp #include "bot_commands/help.cpp" #include "bot_commands/hold.cpp" #include "bot_commands/identify.cpp" +#include "bot_commands/illusion_block.cpp" #include "bot_commands/inventory.cpp" #include "bot_commands/invisibility.cpp" #include "bot_commands/item_use.cpp" #include "bot_commands/levitation.cpp" #include "bot_commands/lull.cpp" +#include "bot_commands/max_melee_range.cpp" #include "bot_commands/mesmerize.cpp" #include "bot_commands/movement_speed.cpp" #include "bot_commands/name.cpp" @@ -2167,8 +2294,24 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp #include "bot_commands/resurrect.cpp" #include "bot_commands/rune.cpp" #include "bot_commands/send_home.cpp" +#include "bot_commands/sit_hp_percent.cpp" +#include "bot_commands/sit_in_combat.cpp" +#include "bot_commands/sit_mana_percent.cpp" #include "bot_commands/size.cpp" #include "bot_commands/spell.cpp" +#include "bot_commands/spell_aggro_checks.cpp" +#include "bot_commands/spell_delays.cpp" +#include "bot_commands/spell_engaged_priority.cpp" +#include "bot_commands/spell_holds.cpp" +#include "bot_commands/spell_idle_priority.cpp" +#include "bot_commands/spell_max_hp_pct.cpp" +#include "bot_commands/spell_max_mana_pct.cpp" +#include "bot_commands/spell_max_thresholds.cpp" +#include "bot_commands/spell_min_hp_pct.cpp" +#include "bot_commands/spell_min_mana_pct.cpp" +#include "bot_commands/spell_min_thresholds.cpp" +#include "bot_commands/spell_pursue_priority.cpp" +#include "bot_commands/spell_target_count.cpp" #include "bot_commands/summon.cpp" #include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 74dfa14017..53cb2776bc 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -666,6 +666,27 @@ namespace MyBots UniquifySBL(sbl); } } + + static void PopulateSBL_ByAtMMR(Client* bot_owner, std::list& sbl, bool clear_list = true) { + if (clear_list) { + sbl.clear(); + } + + if (!bot_owner) { + return; + } + + auto selectable_bot_list = entity_list.GetBotsByBotOwnerCharacterID(bot_owner->CharacterID()); + for (auto bot_iter : selectable_bot_list) { + if (!bot_iter->GetMaxMeleeRange()) { + continue; + } + sbl.push_back(bot_iter); + } + if (!clear_list) { + UniquifySBL(sbl); + } + } }; namespace ActionableTarget @@ -875,6 +896,7 @@ namespace ActionableBots ABT_HealRotation, ABT_HealRotationMembers, ABT_HealRotationTargets, + ABT_MMR, ABT_Class, ABT_Race, ABT_Spawned, @@ -892,6 +914,7 @@ namespace ActionableBots ABM_HealRotation = (1 << (ABT_HealRotation - 1)), ABM_HealRotationMembers = (1 << (ABT_HealRotationMembers - 1)), ABM_HealRotationTargets = (1 << (ABT_HealRotationTargets - 1)), + ABM_MMR = (1 << (ABT_MMR - 1)), ABM_Class = (1 << (ABT_Class - 1)), ABM_Race = (1 << (ABT_Race - 1)), ABM_Spawned = (1 << (ABT_Spawned - 1)), @@ -899,8 +922,8 @@ namespace ActionableBots ABM_Spawned_All = (3 << (ABT_Spawned - 1)), ABM_NoFilter = ~0, // grouped values - ABM_Type1 = (ABM_Target | ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_TargetGroup | ABM_NamesGroup | ABM_HealRotationTargets | ABM_Spawned | ABM_Class | ABM_Race), - ABM_Type2 = (ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_NamesGroup | ABM_HealRotation | ABM_Spawned | ABM_Class | ABM_Race) + ABM_Type1 = (ABM_Target | ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_TargetGroup | ABM_NamesGroup | ABM_HealRotationTargets | ABM_Spawned | ABM_MMR | ABM_Class | ABM_Race), + ABM_Type2 = (ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_NamesGroup | ABM_HealRotation | ABM_Spawned | ABM_MMR | ABM_Class | ABM_Race) }; // Populates 'sbl' @@ -941,6 +964,9 @@ namespace ActionableBots else if (!ab_type_arg.compare("healrotationtargets")) { ab_type = ABT_HealRotationTargets; } + else if (!ab_type_arg.compare("mmr")) { + ab_type = ABT_MMR; + } else if (!ab_type_arg.compare("byclass")) { ab_type = ABT_Class; } @@ -1004,6 +1030,11 @@ namespace ActionableBots MyBots::PopulateSBL_ByHealRotationTargets(bot_owner, sbl, name, clear_list); } break; + case ABT_MMR: + if (ab_mask & ABM_MMR) { + MyBots::PopulateSBL_ByAtMMR(bot_owner, sbl, clear_list); + } + break; case ABT_Class: if (ab_mask & ABM_Class) { MyBots::PopulateSBL_BySpawnedBotsClass(bot_owner, sbl, classrace, clear_list); @@ -1256,9 +1287,9 @@ namespace ActionableBots sbl.remove_if([min_level](const Bot* l) { return (l->GetLevel() < min_level); }); } - static void Filter_ByArcher(Client* bot_owner, std::list& sbl) { + static void Filter_ByRanged(Client* bot_owner, std::list& sbl) { sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); - sbl.remove_if([bot_owner](Bot* l) { return (!l->IsBotArcher()); }); + sbl.remove_if([bot_owner](Bot* l) { return (!l->IsBotRanged()); }); } static void Filter_ByHighestSkill(Client* bot_owner, std::list& sbl, EQ::skills::SkillType skill_type, float& skill_value) { @@ -1637,12 +1668,18 @@ void bot_command_aggressive(Client *c, const Seperator *sep); void bot_command_apply_poison(Client *c, const Seperator *sep); void bot_command_apply_potion(Client* c, const Seperator* sep); void bot_command_attack(Client *c, const Seperator *sep); +void bot_command_behind_mob(Client* c, const Seperator* sep); void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep); +void bot_command_bot_settings(Client* c, const Seperator* sep); +void bot_command_cast(Client* c, const Seperator* sep); void bot_command_caster_range(Client* c, const Seperator* sep); void bot_command_charm(Client *c, const Seperator *sep); +void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); +void bot_command_copy_settings(Client* c, const Seperator* sep); void bot_command_cure(Client *c, const Seperator *sep); +void bot_command_default_settings(Client* c, const Seperator* sep); void bot_command_defensive(Client *c, const Seperator *sep); void bot_command_depart(Client *c, const Seperator *sep); void bot_command_escape(Client *c, const Seperator *sep); @@ -1653,11 +1690,13 @@ void bot_command_heal_rotation(Client *c, const Seperator *sep); void bot_command_help(Client *c, const Seperator *sep); void bot_command_hold(Client *c, const Seperator *sep); void bot_command_identify(Client *c, const Seperator *sep); +void bot_command_illusion_block(Client* c, const Seperator* sep); void bot_command_inventory(Client *c, const Seperator *sep); void bot_command_invisibility(Client *c, const Seperator *sep); void bot_command_item_use(Client *c, const Seperator *sep); void bot_command_levitation(Client *c, const Seperator *sep); void bot_command_lull(Client *c, const Seperator *sep); +void bot_command_max_melee_range(Client* c, const Seperator* sep); void bot_command_mesmerize(Client *c, const Seperator *sep); void bot_command_movement_speed(Client *c, const Seperator *sep); void bot_command_owner_option(Client *c, const Seperator *sep); @@ -1671,7 +1710,23 @@ void bot_command_resistance(Client *c, const Seperator *sep); 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_sit_hp_percent(Client* c, const Seperator* sep); +void bot_command_sit_in_combat(Client* c, const Seperator* sep); +void bot_command_sit_mana_percent(Client* c, const Seperator* sep); void bot_command_size(Client *c, const Seperator *sep); +void bot_command_spell_aggro_checks(Client* c, const Seperator* sep); +void bot_command_spell_delays(Client* c, const Seperator* sep); +void bot_command_spell_engaged_priority(Client* c, const Seperator* sep); +void bot_command_spell_holds(Client* c, const Seperator* sep); +void bot_command_spell_idle_priority(Client* c, const Seperator* sep); +void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep); +void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep); +void bot_command_spell_max_thresholds(Client* c, const Seperator* sep); +void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep); +void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep); +void bot_command_spell_min_thresholds(Client* c, const Seperator* sep); +void bot_command_spell_pursue_priority(Client* c, const Seperator* sep); +void bot_command_spell_target_count(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); @@ -1706,7 +1761,6 @@ void bot_command_hairstyle(Client *c, const Seperator *sep); void bot_command_heritage(Client *c, const Seperator *sep); void bot_command_inspect_message(Client *c, const Seperator *sep); void bot_command_list_bots(Client *c, const Seperator *sep); -void bot_command_out_of_combat(Client *c, const Seperator *sep); void bot_command_report(Client *c, const Seperator *sep); void bot_command_spawn(Client *c, const Seperator *sep); void bot_command_stance(Client *c, const Seperator *sep); @@ -1716,8 +1770,8 @@ void bot_command_summon(Client *c, const Seperator *sep); void bot_command_surname(Client *c, const Seperator *sep); void bot_command_tattoo(Client *c, const Seperator *sep); void bot_command_title(Client *c, const Seperator *sep); -void bot_command_toggle_archer(Client *c, const Seperator *sep); void bot_command_toggle_helm(Client *c, const Seperator *sep); +void bot_command_toggle_ranged(Client* c, const Seperator* sep); void bot_command_update(Client *c, const Seperator *sep); void bot_command_woad(Client *c, const Seperator *sep); @@ -1757,7 +1811,6 @@ bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, BCEnum::AFType f void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot); void helper_bot_appearance_form_update(Bot *my_bot); uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender); -void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot); int helper_bot_follow_option_chain(Client *bot_owner); bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr); bool helper_command_disabled(Client *bot_owner, bool rule_value, const char *command); diff --git a/zone/bot_commands/actionable.cpp b/zone/bot_commands/actionable.cpp index 84965b57d1..73ec9ef6c6 100644 --- a/zone/bot_commands/actionable.cpp +++ b/zone/bot_commands/actionable.cpp @@ -3,22 +3,61 @@ void bot_command_actionable(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_actionable", sep->arg[0], "actionable")) { + c->Message(Chat::White, "note: Lists actionable command arguments and use descriptions"); return; } - c->Message(Chat::White, "Actionable command arguments:"); - c->Message(Chat::White, "target - selects target as single bot .. use ^command [target] or imply by empty actionable argument"); - c->Message(Chat::White, "byname [name] - selects single bot by name"); - c->Message(Chat::White, "ownergroup - selects all bots in the owner's group"); - c->Message(Chat::White, "ownerraid - selects all bots in the owner's raid"); - c->Message(Chat::White, "targetgroup - selects all bots in target's group"); - c->Message(Chat::White, "namesgroup [name] - selects all bots in name's group"); - c->Message(Chat::White, "healrotation [name] - selects all member and target bots of a heal rotation where name is a member"); - c->Message(Chat::White, "healrotationmembers [name] - selects all member bots of a heal rotation where name is a member"); - c->Message(Chat::White, "healrotationtargets [name] - selects all target bots of a heal rotation where name is a member"); - c->Message(Chat::White, "byclass - selects all bots of the chosen class"); - c->Message(Chat::White, "byrace - selects all bots of the chosen rsce"); - c->Message(Chat::White, "spawned - selects all spawned bots"); - c->Message(Chat::White, "all - selects all spawned bots .. argument use indicates en masse database updating"); - c->Message(Chat::White, "You may only select your bots as actionable"); + std::vector description = + { + "Lists actionable command arguments and use descriptions." + }; + + std::vector notes = + { + "[target] - uses the command on the target. Some commands will default to target if no actionable is selected.", + "[byname] [name] - selects a bot by name their name.", + "[ownergroup] - selects all bots in the owner's group.", + "[ownerraid] - selects all bots in the owner's raid.", + "[targetgroup] - selects all bots in the target's group.", + "[namesgroup] [name] - selects all bots in [name]'s group.", + "[healrotation] [name] - selects all member and target bots of a heal rotation where [name] is a member.", + "[healrotationmembers] [name] - selects all member bots of a heal rotation where [name] is a member.", + "[healrotationtargets] [name] - selects all target bots of a heal rotation where [name] is a member.", + "[mmr] - selects all bots that are currently at max melee range.", + "[byclass] - selects all bots of the chosen class.", + "[byrace] - selects all bots of the chosen race.", + "[spawned] - selects all spawned bots.", + "[all] - selects all spawned bots.", + "
", + "You may only select your bots as actionable" + }; + + std::vector example_format = { }; + std::vector examples_one = { }; + std::vector examples_two = { }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + return; } diff --git a/zone/bot_commands/aggressive.cpp b/zone/bot_commands/aggressive.cpp index 6a3881d98c..b40bf71232 100644 --- a/zone/bot_commands/aggressive.cpp +++ b/zone/bot_commands/aggressive.cpp @@ -7,32 +7,26 @@ void bot_command_aggressive(Client* c, const Seperator* sep) helper_command_alias_fail(c, "bot_command_aggressive", sep->arg[0], "aggressive")) { return; } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message( - Chat::White, - "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", - sep->arg[0] - ); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "note: Orders a bot to use a aggressive discipline"); helper_send_usage_required_bots(c, BCEnum::SpT_Stance); return; } + const int ab_mask = ActionableBots::ABM_Type1; + int ab_arg = 1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; - std::string class_race_arg = sep->arg[1]; - bool class_race_check = false; if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL( - c, - sep->arg[1], - sbl, - ab_mask, - !class_race_check ? sep->arg[2] : nullptr, - class_race_check ? atoi(sep->arg[2]) : 0 - ) == ActionableBots::ABT_None) { + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } diff --git a/zone/bot_commands/appearance.cpp b/zone/bot_commands/appearance.cpp index fcaec025cd..c6aed9e08f 100644 --- a/zone/bot_commands/appearance.cpp +++ b/zone/bot_commands/appearance.cpp @@ -158,7 +158,7 @@ void bot_command_dye_armor(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Usage: {} [Material Slot] [Red: 0-255] [Green: 0-255] [Blue: 0-255] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", + "Usage: {} [Material Slot] [Red: 0-255] [Green: 0-255] [Blue: 0-255] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0] ).c_str() ); diff --git a/zone/bot_commands/apply_poison.cpp b/zone/bot_commands/apply_poison.cpp index cfffff2cb6..444e370ed9 100644 --- a/zone/bot_commands/apply_poison.cpp +++ b/zone/bot_commands/apply_poison.cpp @@ -11,6 +11,7 @@ void bot_command_apply_poison(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s", sep->arg[0]); + c->Message(Chat::White, "note: Applies cursor-held poison to a rogue bot's weapon"); return; } diff --git a/zone/bot_commands/attack.cpp b/zone/bot_commands/attack.cpp index 60cad8cd8b..8cce85db1b 100644 --- a/zone/bot_commands/attack.cpp +++ b/zone/bot_commands/attack.cpp @@ -5,14 +5,17 @@ void bot_command_attack(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) { return; } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: %s [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "note: Orders bots to attack a designated target"); return; } - const int ab_mask = ActionableBots::ABM_Type2; + const int ab_mask = ActionableBots::ABM_Type2; Mob* target_mob = ActionableTarget::AsSingle_ByAttackable(c); + if (!target_mob) { c->Message(Chat::White, "You must an enemy to use this command"); @@ -26,11 +29,13 @@ void bot_command_attack(Client *c, const Seperator *sep) std::string class_race_arg(sep->arg[1]); bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; + if (ActionableBots::PopulateSBL(c, ab_arg.c_str(), sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { return; } diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp new file mode 100644 index 0000000000..5761c4c319 --- /dev/null +++ b/zone/bot_commands/behind_mob.cpp @@ -0,0 +1,173 @@ +#include "../bot_command.h" + +void bot_command_behind_mob(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_behind_mob", sep->arg[0], "behindmob")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will stay behind the mob during combat." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + + std::vector examples_one = + { + "To set Monks to stay behind the mob:", + fmt::format( + "{} 1 byclass 7", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the behind mob status for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay behind mobs.'", + my_bot->GetCleanName(), + my_bot->GetBehindMob() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetBehindMob(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay behind mobs.'", + first_found->GetCleanName(), + first_found->GetBehindMob() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} stay behind mobs.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/bind_affinity.cpp b/zone/bot_commands/bind_affinity.cpp index e6d01fe4c3..a59c5947ae 100644 --- a/zone/bot_commands/bind_affinity.cpp +++ b/zone/bot_commands/bind_affinity.cpp @@ -7,6 +7,7 @@ void bot_command_bind_affinity(Client *c, const Seperator *sep) return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: () %s", sep->arg[0]); + c->Message(Chat::White, "note: Orders a bot to attempt an affinity binding", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_BindAffinity); return; } diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 5d2bfe18e5..7da91504d4 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -18,7 +18,7 @@ void bot_command_bot(Client *c, const Seperator *sep) subcommand_list.push_back("botstance"); subcommand_list.push_back("botstopmeleelevel"); subcommand_list.push_back("botsummon"); - subcommand_list.push_back("bottogglearcher"); + subcommand_list.push_back("bottoggleranged"); subcommand_list.push_back("bottogglehelm"); subcommand_list.push_back("botupdate"); @@ -33,7 +33,7 @@ void bot_command_camp(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_Type1; @@ -452,8 +452,8 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_follow_distance", sep->arg[0], "botfollowdistance")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [set] [distance] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "usage: %s [clear] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [set [1 to %i]] [distance] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0], BOT_FOLLOW_DISTANCE_DEFAULT_MAX); + c->Message(Chat::White, "usage: %s [clear] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; @@ -464,7 +464,7 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) if (!strcasecmp(sep->arg[1], "set")) { if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A numeric [distance] is required to use this command"); + c->Message(Chat::White, "A numeric [distance] is required to use this command. [1 to %i]", BOT_FOLLOW_DISTANCE_DEFAULT_MAX); return; } @@ -492,22 +492,15 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) continue; bot_iter->SetFollowDistance(bfd); - if (ab_type != ActionableBots::ABT_All && !database.botdb.SaveFollowDistance(bot_iter->GetBotID(), bfd)) { - return; - } ++bot_count; } if (ab_type == ActionableBots::ABT_All) { - if (!database.botdb.SaveAllFollowDistances(c->CharacterID(), bfd)) { - return; - } - - c->Message(Chat::White, "%s all of your bot follow distances", set_flag ? "Set" : "Cleared"); + c->Message(Chat::White, "%s all of your bot follow distances to %i", set_flag ? "Set" : "Cleared", bfd); } else { - c->Message(Chat::White, "%s %i of your spawned bot follow distances", (set_flag ? "Set" : "Cleared"), bot_count); + c->Message(Chat::White, "%s %i of your spawned bot follow distances to %i", (set_flag ? "Set" : "Cleared"), bot_count, bfd); } } @@ -516,7 +509,7 @@ void bot_command_inspect_message(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_inspect_message", sep->arg[0], "botinspectmessage")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [set | clear] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [set | clear] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "Notes:"); if (c->ClientVersion() >= EQ::versions::ClientVersion::SoF) { c->Message(Chat::White, "- Self-inspect and type your bot's inspect message"); @@ -764,75 +757,31 @@ void bot_command_list_bots(Client *c, const Seperator *sep) } } -void bot_command_out_of_combat(Client *c, const Seperator *sep) +void bot_command_report(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_out_of_combat", sep->arg[0], "botoutofcombat")) + if (helper_command_alias_fail(c, "bot_command_report", sep->arg[0], "botreport")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } - const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); + + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; - - bool behavior_state = false; - bool toggle_behavior = true; int ab_arg = 1; - if (!arg1.compare("on")) { - behavior_state = true; - toggle_behavior = false; - ab_arg = 2; - } - else if (!arg1.compare("off")) { - toggle_behavior = false; - ab_arg = 2; - } - std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]) == ActionableBots::ABT_None) - return; - - for (auto bot_iter : sbl) { - if (!bot_iter) - continue; - - if (toggle_behavior) - bot_iter->SetAltOutOfCombatBehavior(!bot_iter->GetAltOutOfCombatBehavior()); - else - bot_iter->SetAltOutOfCombatBehavior(behavior_state); - - helper_bot_out_of_combat(c, bot_iter); - } -} - -void bot_command_report(Client *c, const Seperator *sep) -{ - if (helper_command_alias_fail(c, "bot_command_report", sep->arg[0], "botreport")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); - return; - } - const int ab_mask = ActionableBots::ABM_NoFilter; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; - std::string ab_type_arg = sep->arg[1]; - if (ab_type_arg.empty()) { - auto t = c->GetTarget(); - if (t && t->IsClient()) { - if (t->CastToClient() == c) { - ab_type_arg = "ownergroup"; - } else { - ab_type_arg = "targetgroup"; - } - } else { - ab_type_arg = "spawned"; - } + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, ab_type_arg, sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; + } for (auto bot_iter : sbl) { if (!bot_iter) @@ -1062,65 +1011,74 @@ void bot_command_spawn(Client *c, const Seperator *sep) void bot_command_stance(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_stance", sep->arg[0], "botstance")) + if (helper_command_alias_fail(c, "bot_command_stance", sep->arg[0], "botstance")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message( Chat::White, fmt::format( - "Value: {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({})", + "Value: {} ({}), {} ({}), {} ({})", Stance::Passive, Stance::GetName(Stance::Passive), Stance::Balanced, Stance::GetName(Stance::Balanced), - Stance::Efficient, - Stance::GetName(Stance::Efficient), - Stance::Reactive, - Stance::GetName(Stance::Reactive), Stance::Aggressive, - Stance::GetName(Stance::Aggressive), - Stance::Assist, - Stance::GetName(Stance::Assist), - Stance::Burn, - Stance::GetName(Stance::Burn), - Stance::Efficient2, - Stance::GetName(Stance::Efficient2), - Stance::AEBurn, - Stance::GetName(Stance::AEBurn) + Stance::GetName(Stance::Aggressive) ).c_str() ); return; } - int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); - bool current_flag = false; - uint8 bst = Stance::Unknown; + const int ab_mask = ActionableBots::ABM_Type1; - if (!strcasecmp(sep->arg[1], "current")) - current_flag = true; - else if (sep->IsNumber(1)) { - bst = static_cast(Strings::ToUnsignedInt(sep->arg[1])); - if (!Stance::IsValid(bst)) { - bst = Stance::Unknown; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + bool current_check = false; + uint32 value = 0; + + if (sep->IsNumber(1)) { + ++ab_arg; + value = atoi(sep->arg[1]); + if (value < 0 || value > 300) { + c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); + return; } } - - if (!current_flag && bst == Stance::Unknown) { - c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command"); + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); return; } + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None) + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; + } + + if (!current_check && (value == Stance::Unknown || (value != Stance::Passive && value != Stance::Balanced && value != Stance::Aggressive))) { + c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command"); + return; + } for (auto bot_iter : sbl) { if (!bot_iter) continue; - if (!current_flag) { - bot_iter->SetBotStance(bst); + if (!current_check) { + bot_iter->SetBotStance(value); bot_iter->Save(); } @@ -1135,50 +1093,138 @@ void bot_command_stance(Client *c, const Seperator *sep) } } -void bot_command_stop_melee_level(Client *c, const Seperator *sep) +void bot_command_stop_melee_level(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_stop_melee_level", sep->arg[0], "botstopmeleelevel")) + if (helper_command_alias_fail(c, "bot_command_stop_melee_level", sep->arg[0], "botstopmeleelevel")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | reset | sync | value: 0-255]", sep->arg[0]); + c->Message(Chat::White, "usage: %s [current | reset | sync | value: 0-255] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "note: Only caster or hybrid class bots may be modified"); c->Message(Chat::White, "note: Use [reset] to set stop melee level to server rule"); c->Message(Chat::White, "note: Use [sync] to set stop melee level to current bot level"); return; } - auto my_bot = ActionableBots::AsTarget_ByBot(c); - if (!my_bot) { - c->Message(Chat::White, "You must a bot that you own to use this command"); - return; - } - if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { - c->Message(Chat::White, "You must a caster or hybrid class bot to use this command"); - return; - } + const int ab_mask = ActionableBots::ABM_Type1; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; uint8 sml = RuleI(Bots, CasterStopMeleeLevel); + bool sync_sml = false; + bool reset_sml = false; + bool current_check = false; if (sep->IsNumber(1)) { + ab_arg = 2; sml = Strings::ToInt(sep->arg[1]); + if (sml <= 0 || sml > 255) { + c->Message(Chat::White, "You must provide a value between 0-255."); + return; + } } else if (!strcasecmp(sep->arg[1], "sync")) { - sml = my_bot->GetLevel(); + ab_arg = 2; + sync_sml = true; } - else if (!strcasecmp(sep->arg[1], "current")) { - c->Message(Chat::White, "My current melee stop level is %u", my_bot->GetStopMeleeLevel()); + else if (!arg1.compare("current")) { + ab_arg = 2; + current_check = true; + } + else if (!strcasecmp(sep->arg[1], "reset")) { + ab_arg = 2; + reset_sml = true; + } + else { + c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); return; } - else if (strcasecmp(sep->arg[1], "reset")) { - c->Message(Chat::White, "A [current] or [reset] argument, or numeric [value] is required to use this command"); + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - // [reset] falls through with initialization value - my_bot->SetStopMeleeLevel(sml); - database.botdb.SaveStopMeleeLevel(my_bot->GetBotID(), sml); + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + + for (auto my_bot : sbl) { + if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { + c->Message( + Chat::White, + fmt::format( + "{} says, 'This command only works on caster or hybrid classes.'", + my_bot->GetCleanName() + ).c_str() + ); + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (sync_sml) { + sml = my_bot->GetLevel(); + } - c->Message(Chat::White, "Successfully set stop melee level for %s to %u", my_bot->GetCleanName(), sml); + if (reset_sml) { + sml = my_bot->GetDefaultBotBaseSetting(BotBaseSettings::StopMeleeLevel); + } + + if (current_check) { + c->Message( + Chat::White, + fmt::format( + "{} says, 'My current stop melee level is {}.'", + my_bot->GetCleanName(), + my_bot->GetStopMeleeLevel() + ).c_str() + ); + continue; + } + else { + my_bot->SetStopMeleeLevel(sml); + ++success_count; + } + } + + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::White, + fmt::format( + "{} says, 'My stop melee level was {} to {}.'", + first_found->GetCleanName(), + reset_sml ? "reset" : "set", + sml + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "{} of your bots {} their stop melee{}'", // level to {}. + success_count, + reset_sml ? "reset" : "set", + fmt::format("{}", reset_sml ? "." : fmt::format(" level to {}.", sml).c_str()).c_str(), + sml + ).c_str() + ); + } + } } void bot_command_summon(Client *c, const Seperator *sep) @@ -1188,19 +1234,24 @@ void bot_command_summon(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } + const int ab_mask = ActionableBots::ABM_Type1; - std::string class_race_arg = sep->arg[1]; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -1245,34 +1296,44 @@ void bot_command_summon(Client *c, const Seperator *sep) } } -void bot_command_toggle_archer(Client *c, const Seperator *sep) +void bot_command_toggle_ranged(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_toggle_archer", sep->arg[0], "bottogglearcher")) { + if (helper_command_alias_fail(c, "bot_command_toggle_ranged", sep->arg[0], "bottoggleranged")) { return; } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "note: Toggles a ranged bot between melee and ranged weapon use"); return; } - const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); + + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; - bool archer_state = false; - bool toggle_archer = true; + bool ranged_state = false; + bool toggle_ranged = true; int ab_arg = 1; if (!arg1.compare("on")) { - archer_state = true; - toggle_archer = false; - ab_arg = 2; + ranged_state = true; + toggle_ranged = false; + ++ab_arg; } else if (!arg1.compare("off")) { - toggle_archer = false; - ab_arg = 2; + toggle_ranged = false; + ++ab_arg; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]) == ActionableBots::ABT_None) { + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -1281,17 +1342,18 @@ void bot_command_toggle_archer(Client *c, const Seperator *sep) continue; } - if (toggle_archer) { - bot_iter->SetBotArcherySetting(!bot_iter->IsBotArcher(), true); - } - else { - bot_iter->SetBotArcherySetting(archer_state, true); + if (bot_iter->GetBotRangedValue() < RuleI(Combat, MinRangedAttackDist)) { + c->Message(Chat::Yellow, "%s does not have proper weapons or ammo to be at range.", bot_iter->GetCleanName()); + continue; } - bot_iter->ChangeBotArcherWeapons(bot_iter->IsBotArcher()); - if (bot_iter->GetClass() == Class::Ranger && bot_iter->GetLevel() >= 61) { - bot_iter->SetRangerAutoWeaponSelect(bot_iter->IsBotArcher()); + if (toggle_ranged) { + bot_iter->SetBotRangedSetting(!bot_iter->IsBotRanged()); } + else { + bot_iter->SetBotRangedSetting(ranged_state); + } + bot_iter->ChangeBotRangedWeapons(bot_iter->IsBotRanged()); } } @@ -1300,7 +1362,7 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_toggle_helm", sep->arg[0], "bottogglehelm")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; @@ -1313,11 +1375,11 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) if (!arg1.compare("on")) { helm_state = true; toggle_helm = false; - ab_arg = 2; + ++ab_arg; } else if (!arg1.compare("off")) { toggle_helm = false; - ab_arg = 2; + ++ab_arg; } std::list sbl; @@ -1336,9 +1398,6 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) bot_iter->SetShowHelm(helm_state); if (ab_type != ActionableBots::ABT_All) { - if (!database.botdb.SaveHelmAppearance(bot_iter->GetBotID(), bot_iter->GetShowHelm())) { - return; - } EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); SpawnAppearance_Struct* saptr = (SpawnAppearance_Struct*)outapp->pBuffer; @@ -1355,13 +1414,6 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) } if (ab_type == ActionableBots::ABT_All) { - if (toggle_helm) { - database.botdb.ToggleAllHelmAppearances(c->CharacterID()); - } - else { - database.botdb.SaveAllHelmAppearances(c->CharacterID(), helm_state); - } - c->Message(Chat::White, "%s all of your bot show helm flags", toggle_helm ? "Toggled" : (helm_state ? "Set" : "Cleared")); } else { @@ -1439,7 +1491,6 @@ void bot_command_update(Client *c, const Seperator *sep) if (!bot_iter || bot_iter->IsEngaged() || bot_iter->GetLevel() == c->GetLevel()) continue; - bot_iter->SetPetChooser(false); bot_iter->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); bot_iter->SendAppearancePacket(AppearanceType::WhoLevel, bot_iter->GetLevel(), true, true); ++bot_count; diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp new file mode 100644 index 0000000000..e94bea5f1a --- /dev/null +++ b/zone/bot_commands/bot_settings.cpp @@ -0,0 +1,43 @@ +#include "../bot_command.h" + +void bot_command_bot_settings(Client* c, const Seperator* sep) +{ + std::list subcommand_list; + subcommand_list.push_back("behindmob"); + subcommand_list.push_back("casterrange"); + subcommand_list.push_back("copysettings"); + subcommand_list.push_back("defaultsettings"); + subcommand_list.push_back("enforcespelllist"); + subcommand_list.push_back("follow"); + subcommand_list.push_back("followdistance"); + subcommand_list.push_back("illusionblock"); + subcommand_list.push_back("maxmeleerange"); + subcommand_list.push_back("owneroption"); + subcommand_list.push_back("petsettype"); + subcommand_list.push_back("sithppercent"); + subcommand_list.push_back("sitincombat"); + subcommand_list.push_back("sitmanapercent"); + subcommand_list.push_back("sithppercent"); + subcommand_list.push_back("spellaggrocheck"); + subcommand_list.push_back("spelldelays"); + subcommand_list.push_back("spellengagedpriority"); + subcommand_list.push_back("spellholds"); + subcommand_list.push_back("spellidlepriority"); + subcommand_list.push_back("spellmaxhppct"); + subcommand_list.push_back("spellmaxmanapct"); + subcommand_list.push_back("spellmaxthresholds"); + subcommand_list.push_back("spellminhppct"); + subcommand_list.push_back("spellminmanapct"); + subcommand_list.push_back("spellminthresholds"); + subcommand_list.push_back("spellpursuepriority"); + subcommand_list.push_back("spelltargetcount"); + subcommand_list.push_back("spelllist"); + subcommand_list.push_back("stance"); + subcommand_list.push_back("togglehelm"); + subcommand_list.push_back("bottoggleranged"); + + if (helper_command_alias_fail(c, "bot_command_bot_settings", sep->arg[0], "botsettings")) + return; + + helper_send_available_subcommands(c, "botsettings", subcommand_list); +} diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp new file mode 100644 index 0000000000..77d3ab23bd --- /dev/null +++ b/zone/bot_commands/cast.cpp @@ -0,0 +1,301 @@ +#include "../bot_command.h" + +void bot_command_cast(Client* c, const Seperator* sep) +{ + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc)" + }; + + std::vector notes = + { + "- This will interrupt any spell currently being cast by bots told to use the command.", + "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells" + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To tell everyone to Nuke the target:", + fmt::format( + "{} {} spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + std::vector examples_two = + { + "To tell all Enchanters to slow the target:", + fmt::format( + "{} {} byclass {}", + sep->arg[0], + Class::Enchanter, + c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) + ), + fmt::format( + "{} {} byclass {}", + sep->arg[0], + Class::Enchanter, + BotSpellTypes::Slow + ) + }; + std::vector examples_three = + { + "To tell Clrbot to resurrect the targeted corpse:", + fmt::format( + "{} {} byname Clrbot", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Resurrect) + ), + fmt::format( + "{} {} byname Clrbot", + sep->arg[0], + BotSpellTypes::Resurrect + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 2; + uint16 spellType = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if ( + spellType == BotSpellTypes::PetBuffs || + spellType == BotSpellTypes::PetCompleteHeals || + spellType == BotSpellTypes::PetFastHeals || + spellType == BotSpellTypes::PetHoTHeals || + spellType == BotSpellTypes::PetRegularHeals || + spellType == BotSpellTypes::PetVeryFastHeals + ) { + c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); + return; + } + + Mob* tar = c->GetTarget(); + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { + if (!tar) { + c->Message(Chat::Yellow, "You need a target for that."); + return; + } + + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { + c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); + return; + } + + if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + c->Message(Chat::Yellow, "[%s] is an invalid target.", tar->GetCleanName()); + return; + } + } + } + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + switch (spellType) { + case BotSpellTypes::Stun: + case BotSpellTypes::AEStun: + if (tar->GetSpecialAbility(SpecialAbility::StunImmunity)) { + c->Message(Chat::Yellow, "[%s] is immune to stuns.", tar->GetCleanName()); + return; + } + + break; + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + c->Message(Chat::Yellow, "[%s] is an invalid target. I can only resurrect player corpses.", tar->GetCleanName()); + return; + } + + break; + default: + break; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + BotSpell botSpell; + botSpell.SpellId = 0; + botSpell.SpellIndex = 0; + botSpell.ManaCost = 0; + bool isSuccess = false; + uint16 successCount = 0; + Bot* firstFound = nullptr; + + for (auto bot_iter : sbl) { + if (!bot_iter->IsInGroupOrRaid()) { + continue; + } + + /* + TODO bot rewrite - + FIX: Snares, Group Cures, OOC Song, Precombat, HateRedux, Fear/AE Fear + ICB (SK) casting hate on friendly but not hostile? + NEED TO CHECK: precombat, AE Dispel, AE Lifetap + DO I NEED A PBAE CHECK??? + */ + if (bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { + continue; + } + + Mob* newTar = tar; + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { + newTar = bot_iter; + } + + if (!newTar) { + continue; + } + + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { + bot_iter->BotGroupSay( + bot_iter, + fmt::format( + "I cannot attack [{}].", + newTar->GetCleanName() + ).c_str() + ); + + continue; + } + + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + + bot_iter->SetCommandedSpell(true); + + if (bot_iter->AICastSpell(newTar, 100, spellType)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + + bot_iter->SetCommandedSpell(false); + continue; + } + + if (!isSuccess) { + c->Message( + Chat::Yellow, + fmt::format( + "No bots are capable of casting [{}] on {}.", + c->GetSpellTypeNameByID(spellType), + tar ? tar->GetCleanName() : "your target" + ).c_str() + ); + } + else { + c->Message( Chat::Yellow, + fmt::format( + "{} {} [{}]{}", + ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), + ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), + c->GetSpellTypeNameByID(spellType), + tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." + ).c_str() + ); + } +} diff --git a/zone/bot_commands/caster_range.cpp b/zone/bot_commands/caster_range.cpp index 2c1faf639a..447c739db7 100644 --- a/zone/bot_commands/caster_range.cpp +++ b/zone/bot_commands/caster_range.cpp @@ -7,7 +7,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "note: Can only be used for Casters or Hybrids."); c->Message(Chat::White, "note: Use [current] to check the current setting."); c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target."); @@ -15,6 +15,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) c->Message(Chat::White, "note: This is set to (90) units by default."); return; } + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; @@ -23,7 +24,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) uint32 crange = 0; if (sep->IsNumber(1)) { - ab_arg = 2; + ++ab_arg; crange = atoi(sep->arg[1]); if (crange < 0 || crange > 300) { c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); @@ -31,7 +32,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) } } else if (!arg1.compare("current")) { - ab_arg = 2; + ++ab_arg; current_check = true; } else { @@ -78,8 +79,6 @@ void bot_command_caster_range(Client* c, const Seperator* sep) else { my_bot->SetBotCasterRange(crange); ++success_count; - - database.botdb.SaveBotCasterRange(my_bot->GetBotID(), crange); } } if (!current_check) { diff --git a/zone/bot_commands/class_race_list.cpp b/zone/bot_commands/class_race_list.cpp new file mode 100644 index 0000000000..84c611ab38 --- /dev/null +++ b/zone/bot_commands/class_race_list.cpp @@ -0,0 +1,113 @@ +#include "../bot_command.h" + +void bot_command_class_race_list(Client* c, const Seperator* sep) +{ + const std::string class_substrs[17] = { + "", + "WAR", "CLR", "PAL", "RNG", + "SHD", "DRU", "MNK", "BRD", + "ROG", "SHM", "NEC", "WIZ", + "MAG", "ENC", "BST", "BER" + }; + + const std::string race_substrs[17] = { + "", + "HUM", "BAR", "ERU", "ELF", + "HIE", "DEF", "HEF", "DWF", + "TRL", "OGR", "HFL", "GNM", + "IKS", "VAH", "FRG", "DRK" + }; + + const uint16 race_values[17] = { + Race::Doug, + Race::Human, Race::Barbarian, Race::Erudite, Race::WoodElf, + Race::HighElf, Race::DarkElf, Race::HalfElf, Race::Dwarf, + Race::Troll, Race::Ogre, Race::Halfling, Race::Gnome, + Race::Iksar, Race::VahShir, Race::Froglok2, Race::Drakkin + }; + + std::string window_text; + std::string message_separator; + int object_count = 0; + const int object_max = 4; + + window_text.append( + fmt::format( + "Classes{}", + DialogueWindow::Break() + ) + ); + + window_text.append( + fmt::format( + "--------------------------------------------------------------------", + DialogueWindow::Break() + ) + ); + + window_text.append(DialogueWindow::Break()); + + message_separator = " "; + object_count = 0; + for (int i = 0; i <= 15; ++i) { + window_text.append(message_separator); + + if (object_count >= object_max) { + window_text.append(DialogueWindow::Break()); + object_count = 0; + } + + window_text.append( + fmt::format("{} ({})", + class_substrs[i + 1], + (i + 1) + ) + ); + + ++object_count; + message_separator = ", "; + } + + window_text.append(DialogueWindow::Break(2)); + + window_text.append( + fmt::format( + "Races{}", + DialogueWindow::Break() + ) + ); + + window_text.append( + fmt::format( + "--------------------------------------------------------------------", + DialogueWindow::Break() + ) + ); + + window_text.append(DialogueWindow::Break()); + + message_separator = " "; + object_count = 0; + for (int i = 0; i <= 15; ++i) { + window_text.append(message_separator); + + if (object_count >= object_max) { + window_text.append(DialogueWindow::Break()); + object_count = 0; + } + + window_text.append( + fmt::format("{} ({})", + race_substrs[i + 1], + race_values[i + 1] + ) + ); + + ++object_count; + message_separator = ", "; + } + + c->SendPopupToClient("Bot Creation Options", window_text.c_str()); + + return; +} diff --git a/zone/bot_commands/click_item.cpp b/zone/bot_commands/click_item.cpp index b236815d8a..80375b52c0 100644 --- a/zone/bot_commands/click_item.cpp +++ b/zone/bot_commands/click_item.cpp @@ -8,7 +8,7 @@ void bot_command_click_item(Client* c, const Seperator* sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "This will cause the selected bots to click the item in the given slot ID."); c->Message(Chat::White, "Use ^invlist to see their items along with slot IDs."); return; @@ -25,7 +25,7 @@ void bot_command_click_item(Client* c, const Seperator* sep) uint32 slot_id = 0; if (sep->IsNumber(1)) { - ab_arg = 2; + ++ab_arg; slot_id = atoi(sep->arg[1]); if (slot_id < EQ::invslot::EQUIPMENT_BEGIN || slot_id > EQ::invslot::EQUIPMENT_END) { c->Message(Chat::Yellow, "You must specify a valid inventory slot from 0 to 22. Use %s help for more information", sep->arg[0]); diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp new file mode 100644 index 0000000000..fafead14c1 --- /dev/null +++ b/zone/bot_commands/copy_settings.cpp @@ -0,0 +1,366 @@ +#include "../bot_command.h" + +void bot_command_copy_settings(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_copy_settings", sep->arg[0], "copysettings")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Copies settings from one bot to another bot" + }; + + std::vector notes = + { + "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" + }; + + std::vector example_format = + { + fmt::format( + "{} [from] [to] [option]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To copy all settings from BotA to BotB:", + fmt::format( + "{} BotA BotB all", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To copy only Nuke spelltypesettings from BotA to BotB:", + fmt::format( + "{} BotA BotB spelltypesettings {}", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} BotA BotB spelltypesettings {}", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + }; + std::vector examples_three = { }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid", + "targetgroup, namesgroup, healrotationtargets", + "mmr, byclass, byrace, spawned" + }; + + std::vector options = + { + "all, misc, spellsettings, spelltypesettings", + "holds, delays, minthresholds, maxthresholds", + "minmanapct, maxmanapct, minhppct, maxhppct", + "idlepriority, engagedpriority, pursuepriority", + "aggrochecks, targetcounts" + }; + std::vector options_one = + { + "[spellsettings] will copy ^spellsettings options", + "[spelltypesettings] copies all spell type settings", + "[all] copies all settings" + }; + std::vector options_two = + { + "[misc] copies all miscellaneous options such as:", + "- ^showhelm, ^followd, ^stopmeleelevel", + "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", + "- ^behindmob, ^casterrange, ^illusionblock", + "- ^sitincombat, ^sithppercent and ^sitmanapercent", + + }; + std::vector options_three = + { + "The remaining options copy that specific type" + }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 2; + bool validOption = false; + uint16 spellType = UINT16_MAX; + std::vector options = + { + "all", + "misc" + "spellsettings", + "spelltypesettings", + "holds", + "delays", + "minthresholds", + "maxthresholds", + "aggrochecks", + "minmanapct", + "maxmanapct", + "minhppct", + "maxhppct", + "idlepriority", + "engagedpriority", + "pursuepriority", + "targetcounts" + }; + + for (int i = 0; i < options.size(); i++) { + if (sep->arg[3] == options[i]) { + if (options[i] != "all" && options[i] != "misc" && options[i] != "spellsettings") { + + if (sep->IsNumber(4) || c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX) { + if (sep->IsNumber(4)) { + spellType = atoi(sep->arg[4]); + } + + if (c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(sep->arg[4]); + } + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + } + else if ( + (options[i] == "all" || options[i] == "misc" || options[i] == "spellsettings") && + ((sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX)) + ) { + break; + } + + validOption = true; + break; + } + } + + if (!validOption) { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + auto from = entity_list.GetBotByBotName(sep->arg[1]); + + if (!from) { + c->Message(Chat::Yellow, "Could not find %s.", sep->arg[1]); + return; + } + + if (!from->IsBot()) { + c->Message(Chat::Yellow, "%s is not a bot.", from->GetCleanName()); + return; + } + + if (!from->GetOwner()) { + c->Message(Chat::Yellow, "Could not find %s's owner.", from->GetCleanName()); + } + + if (RuleB(Bots, CopySettingsOwnBotsOnly) && from->GetOwner() != c) { + c->Message(Chat::Yellow, "You name a bot you own to use this command."); + return; + } + + if (!RuleB(Bots, AllowCopySettingsAnon) && from->GetOwner() != c && from->GetOwner()->CastToClient()->GetAnon()) { + c->Message(Chat::Yellow, "You name a bot you own to use this command."); + return; + } + + auto to = ActionableBots::AsNamed_ByBot(c, sep->arg[2]); + + if (!to) { + c->Message(Chat::Yellow, "Could not find %s.", sep->arg[1]); + return; + } + + if (!to->IsBot()) { + c->Message(Chat::Yellow, "%s is not a bot.", to->GetCleanName()); + return; + } + + if (!to->GetOwner()) { + c->Message(Chat::Yellow, "Could not find %s's owner.", to->GetCleanName()); + } + + if (to->GetOwner() != c) { + c->Message(Chat::Yellow, "You must name a spawned bot that you own to use this command."); + return; + } + + if (to == from) { + c->Message(Chat::Yellow, "You cannot copy to the same bot that you're copying from."); + return; + } + + std::string output = ""; + + if (!strcasecmp(sep->arg[3], "misc")) { + from->CopySettings(to, BotSettingCategories::BaseSetting); + output = "Miscellaneous"; + } + else if (!strcasecmp(sep->arg[3], "holds")) { + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellHold); + } + else if (!strcasecmp(sep->arg[3], "delays")) { + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellDelay); + } + else if (!strcasecmp(sep->arg[3], "minthresholds")) { + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellMinThreshold); + } + else if (!strcasecmp(sep->arg[3], "maxthresholds")) { + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellMaxThreshold); + } + else if (!strcasecmp(sep->arg[3], "aggrochecks")) { + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeAggroCheck); + } + else if (!strcasecmp(sep->arg[3], "minmanapct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMinManaPct); + } + else if (!strcasecmp(sep->arg[3], "maxmanapct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMaxManaPct); + } + else if (!strcasecmp(sep->arg[3], "minhppct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMinHPPct); + } + else if (!strcasecmp(sep->arg[3], "maxhppct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMaxHPPct); + } + else if (!strcasecmp(sep->arg[3], "idlepriority")) { + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeIdlePriority); + } + else if (!strcasecmp(sep->arg[3], "engagedpriority")) { + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeEngagedPriority); + } + else if (!strcasecmp(sep->arg[3], "pursuepriority")) { + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypePursuePriority); + } + else if (!strcasecmp(sep->arg[3], "targetcounts")) { + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeAEOrGroupTargetCount); + } + else if (!strcasecmp(sep->arg[3], "spellsettings")) { + from->CopyBotSpellSettings(to); + output = "^spellsettings"; + } + else if (!strcasecmp(sep->arg[3], "spelltypesettings")) { + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + output = "spell type"; + } + else if (!strcasecmp(sep->arg[3], "all")) { + from->CopySettings(to, BotSettingCategories::BaseSetting); + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + from->CopyBotSpellSettings(to); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + c->Message( + Chat::Green, + fmt::format( + "{}'s{}{} settings were copied to {}.", + from->GetCleanName(), + ( + spellType != UINT16_MAX ? + fmt::format(" [{}] ", + c->GetSpellTypeNameByID(spellType) + ) + : " " + ), + output, + to->GetCleanName() + ).c_str() + ); +} diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp new file mode 100644 index 0000000000..1f7c854cad --- /dev/null +++ b/zone/bot_commands/default_settings.cpp @@ -0,0 +1,473 @@ +#include "../bot_command.h" + +void bot_command_default_settings(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_default_settings", sep->arg[0], "defaultsettings")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Restores a bot's setting(s) to defaults" + }; + + std::vector notes = + { + "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" + }; + + std::vector example_format = + { + fmt::format( + "{} [option] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To restore delays for Clerics:", + fmt::format( + "{} delays byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To restore only Snare delays for BotA:", + fmt::format( + "{} delays {} byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} delays {} byname BotA", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_three = { }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = + { + "all, misc, spellsettings, spelltypesettings, holds, delays, minthresholds, maxthresholds minmanapct, maxmanapct, minhppct, maxhppct, idlepriority, engagedpriority, pursuepriority, aggrocheck, targetcounts" + }; + std::vector options_one = + { + "[spellsettings] will restore ^spellsettings options", + "[spelltypesettings] restores all spell type settings", + "[all] restores all settings" + }; + std::vector options_two = + { + "[misc] restores all miscellaneous options such as:", + "- ^showhelm, ^followd, ^stopmeleelevel", + "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", + "- ^behindmob, ^casterrange, ^illusionblock", + "- ^sitincombat, ^sithppercent and ^sitmanapercent", + + }; + std::vector options_three = + { + "The remaining options restore that specific type" + }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 2; + bool validOption = false; + uint16 spellType = UINT16_MAX; + std::vector options = + { + "all", + "misc" + "spellsettings", + "spelltypesettings", + "holds", + "delays", + "minthresholds", + "maxthresholds", + "aggrocheck", + "minmanapct", + "maxmanapct", + "minhppct", + "maxhppct", + "idlepriority", + "engagedpriority", + "pursuepriority", + "targetcounts" + }; + + for (int i = 0; i < options.size(); i++) { + if (sep->arg[1] == options[i]) { + if (options[i] != "all" && options[i] != "misc" && options[i] != "spellsettings") { + + if (sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { + if (sep->IsNumber(2)) { + spellType = atoi(sep->arg[2]); + } + + if (c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(sep->arg[2]); + } + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + + ++ab_arg; + } + } + else if ( + (options[i] == "all" || options[i] == "misc" || options[i] == "spellsettings") && + ((sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX)) + ) { + break; + } + + validOption = true; + break; + } + } + + if (!validOption) { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + std::string output = ""; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + + if (!strcasecmp(sep->arg[1], "misc")) { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + output = "miscellanous settings"; + } + } + else if (!strcasecmp(sep->arg[1], "holds")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + } + } + + output = "hold settings"; + } + else if (!strcasecmp(sep->arg[1], "delays")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + } + } + + output = "delay settings"; + } + else if (!strcasecmp(sep->arg[1], "minthresholds")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + } + } + + output = "minimum threshold settings"; + } + else if (!strcasecmp(sep->arg[1], "maxthresholds")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + } + } + + output = "maximum threshold settings"; + } + else if (!strcasecmp(sep->arg[1], "aggrochecks")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + } + } + + output = "aggro check settings"; + } + else if (!strcasecmp(sep->arg[1], "minmanapct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + } + } + + output = "min mana settings"; + } + else if (!strcasecmp(sep->arg[1], "maxmanapct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + } + } + + output = "max mana settings"; + } + else if (!strcasecmp(sep->arg[1], "minhppct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + } + } + + output = "min hp settings"; + } + else if (!strcasecmp(sep->arg[1], "maxhppct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + } + } + + output = "max hp settings"; + } + else if (!strcasecmp(sep->arg[1], "idlepriority")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + } + } + + output = "idle priority settings"; + } + else if (!strcasecmp(sep->arg[1], "engagedpriority")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + } + } + + output = "engaged priority settings"; + } + else if (!strcasecmp(sep->arg[1], "pursuepriority")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + } + } + + output = "pursue priority settings"; + } + else if (!strcasecmp(sep->arg[1], "targetcounts")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + } + } + + output = "ae/group count settings"; + } + else if (!strcasecmp(sep->arg[1], "spellsettings")) { + my_bot->ResetBotSpellSettings(); + output = "^spellsettings"; + } + else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); + my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); + my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); + my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); + my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); + my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); + my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); + my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); + my_bot->SetSpellTypeAEOrGroupTargetCount(spellType, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + } + } + + output = "spell type settings"; + } + else if (!strcasecmp(sep->arg[1], "all")) { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + } + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + }; + + my_bot->ResetBotSpellSettings(); + + output = "settings"; + + } + + ++success_count; + } + + if (success_count == 1) { + c->Message( + Chat::Green, + fmt::format( + "{} says, '{}{} were restored.'", + first_found->GetCleanName(), + ( + spellType != UINT16_MAX ? + fmt::format("My [{}] ", + c->GetSpellTypeNameByID(spellType) + ) + : "My " + ), + output + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bot's{}{} were restored.", + success_count, + ( + spellType != UINT16_MAX ? + fmt::format(" [{}] ", + c->GetSpellTypeNameByID(spellType) + ) + : " " + ), + output + ).c_str() + ); + } +} diff --git a/zone/bot_commands/defensive.cpp b/zone/bot_commands/defensive.cpp index a2edc3e6e9..081e07f342 100644 --- a/zone/bot_commands/defensive.cpp +++ b/zone/bot_commands/defensive.cpp @@ -6,22 +6,27 @@ void bot_command_defensive(Client *c, const Seperator *sep) if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Stance) || helper_command_alias_fail(c, "bot_command_defensive", sep->arg[0], "defensive")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_Stance); return; } const int ab_mask = ActionableBots::ABM_Type1; - std::string class_race_arg = sep->arg[1]; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); int success_count = 0; diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 65ec73f8b0..3ff0cb44ba 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -1,11 +1,11 @@ #include "../bot_command.h" -void bot_command_follow(Client *c, const Seperator *sep) +void bot_command_follow(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | mmr | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); c->Message(Chat::White, "usage: %s chain", sep->arg[0]); return; } @@ -26,13 +26,22 @@ void bot_command_follow(Client *c, const Seperator *sep) } else if (!optional_arg.compare("reset")) { reset = true; - ab_arg = 2; - name_arg = 3; + ++ab_arg; + ++name_arg ; } else { - target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single); + //target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single); + target_mob = c->GetTarget(); if (!target_mob) { - c->Message(Chat::White, "You must a friendly mob to use this command"); + c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); + return; + } + else if (!target_mob->IsBot() && !target_mob->IsClient()) { + c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); + return; + } + else if ((target_mob->GetGroup() && target_mob->GetGroup() != c->GetGroup()) || (target_mob->GetRaid() && target_mob->GetRaid() != c->GetRaid())) { + c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); return; } } @@ -51,29 +60,54 @@ void bot_command_follow(Client *c, const Seperator *sep) sbl.remove(nullptr); for (auto bot_iter : sbl) { bot_iter->WipeHateList(); - auto my_group = bot_iter->GetGroup(); - if (my_group) { - if (reset) { - if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter) - bot_iter->SetFollowID(c->GetID()); - else - bot_iter->SetFollowID(my_group->GetLeader()->GetID()); + if (!bot_iter->GetGroup() && !bot_iter->GetRaid()) { + bot_iter->SetFollowID(0); + bot_iter->SetManualFollow(false); + } + else { + if (reset) { + bot_iter->SetFollowID(c->GetID()); bot_iter->SetManualFollow(false); } else { - if (bot_iter == target_mob) - bot_iter->SetFollowID(c->GetID()); - else + if (target_mob->IsGrouped() || target_mob->IsRaidGrouped()) { bot_iter->SetFollowID(target_mob->GetID()); - - bot_iter->SetManualFollow(true); + bot_iter->SetManualFollow(true); + } + else if (bot_iter == target_mob) { + bot_iter->SetFollowID(c->GetID()); + bot_iter->SetManualFollow(true); + } + else { + bot_iter->SetFollowID(0); + bot_iter->SetManualFollow(false); + } } } - else { - bot_iter->SetFollowID(0); - bot_iter->SetManualFollow(false); - } + //auto my_group = bot_iter->GetGroup(); + //if (my_group) { + // if (reset) { + // if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter) + // bot_iter->SetFollowID(c->GetID()); + // else + // bot_iter->SetFollowID(my_group->GetLeader()->GetID()); + // + // bot_iter->SetManualFollow(false); + // } + // else { + // if (bot_iter == target_mob) + // bot_iter->SetFollowID(c->GetID()); + // else + // bot_iter->SetFollowID(target_mob->GetID()); + // + // bot_iter->SetManualFollow(true); + // } + //} + //else { + // bot_iter->SetFollowID(0); + // bot_iter->SetManualFollow(false); + //} if (!bot_iter->GetPet()) continue; @@ -81,31 +115,37 @@ void bot_command_follow(Client *c, const Seperator *sep) bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); } + Mob* follow_mob = nullptr; if (sbl.size() == 1) { - Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); - Bot::BotGroupSay( - sbl.front(), + follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + c->Message( + Chat::White, fmt::format( "Following {}.", - follow_mob ? follow_mob->GetCleanName() : "no one" + follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); - } else { + } + else { if (reset) { c->Message( Chat::White, fmt::format( - "{} of your bots are following their default assignments.", + "{} of your bots are following you.", sbl.size() ).c_str() ); - } else { + } + else { + if (!sbl.empty()) { + follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + } c->Message( Chat::White, fmt::format( "{} of your bots are following {}.", sbl.size(), - target_mob->GetCleanName() + follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); } diff --git a/zone/bot_commands/guard.cpp b/zone/bot_commands/guard.cpp index 27a9ed3430..1cacd27426 100644 --- a/zone/bot_commands/guard.cpp +++ b/zone/bot_commands/guard.cpp @@ -7,7 +7,7 @@ void bot_command_guard(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); return; } const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2); @@ -20,8 +20,8 @@ void bot_command_guard(Client *c, const Seperator *sep) if (!clear_arg.compare("clear")) { clear = true; - ab_arg = 2; - name_arg = 3; + ++ab_arg; + ++name_arg; } std::string class_race_arg = sep->arg[ab_arg]; diff --git a/zone/bot_commands/hold.cpp b/zone/bot_commands/hold.cpp index 49f0f516fb..0fad872dc7 100644 --- a/zone/bot_commands/hold.cpp +++ b/zone/bot_commands/hold.cpp @@ -7,7 +7,7 @@ void bot_command_hold(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); return; } const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2); @@ -20,8 +20,8 @@ void bot_command_hold(Client *c, const Seperator *sep) if (!clear_arg.compare("clear")) { clear = true; - ab_arg = 2; - name_arg = 3; + ++ab_arg; + ++name_arg; } std::string class_race_arg = sep->arg[ab_arg]; diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp new file mode 100644 index 0000000000..0a3625046a --- /dev/null +++ b/zone/bot_commands/illusion_block.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_illusion_block(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_illusion_block", sep->arg[0], "illusionblock")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will block the illusion effects of spells cast by players or bots." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set BotA to block illusions:", + fmt::format( + "{} 1 byname BotA", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the illusion block status for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} block illusions.'", + my_bot->GetCleanName(), + my_bot->GetIllusionBlock() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetIllusionBlock(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} block illusions.'", + first_found->GetCleanName(), + first_found->GetIllusionBlock() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} block illusions.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/inventory.cpp b/zone/bot_commands/inventory.cpp index b934f96a25..7371475ed3 100644 --- a/zone/bot_commands/inventory.cpp +++ b/zone/bot_commands/inventory.cpp @@ -217,9 +217,18 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep) } const auto* itm = inst->GetItem(); + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemInst); + linker.SetItemInst(inst); if (inst && itm && c->CheckLoreConflict(itm)) { - c->MessageString(Chat::White, PICK_LORE); + c->Message( + Chat::White, + fmt::format( + "You cannot pick up {} because it is a lore item you already possess.", + linker.GenerateLink() + ).c_str() + ); return; } @@ -233,7 +242,13 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep) continue; } - c->MessageString(Chat::White, PICK_LORE); + c->Message( + Chat::White, + fmt::format( + "You cannot pick up {} because it is a lore item you already possess.", + linker.GenerateLink() + ).c_str() + ); return; } @@ -247,7 +262,7 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep) slot_id == EQ::invslot::slotRange || slot_id == EQ::invslot::slotAmmo ) { - my_bot->SetBotArcherySetting(false, true); + my_bot->SetBotRangedSetting(false); } my_bot->RemoveBotItemBySlot(slot_id); diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp new file mode 100644 index 0000000000..e876a97591 --- /dev/null +++ b/zone/bot_commands/max_melee_range.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_max_melee_range(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_max_melee_range", sep->arg[0], "maxmeleerange")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will stay at max melee range during combat." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set BotA to stay at max melee range:", + fmt::format( + "{} 1 byname BotA", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the max melee range status for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay at max melee range.'", + my_bot->GetCleanName(), + my_bot->GetMaxMeleeRange() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetMaxMeleeRange(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay at max melee range.'", + first_found->GetCleanName(), + first_found->GetMaxMeleeRange() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} stay at max melee range.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/mesmerize.cpp b/zone/bot_commands/mesmerize.cpp index 532a3c4049..1f51a70d39 100644 --- a/zone/bot_commands/mesmerize.cpp +++ b/zone/bot_commands/mesmerize.cpp @@ -2,42 +2,40 @@ void bot_command_mesmerize(Client *c, const Seperator *sep) { - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Mesmerize]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Mesmerize) || helper_command_alias_fail(c, "bot_command_mesmerize", sep->arg[0], "mesmerize")) - return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_Mesmerize); return; } - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; + bool isSuccess = false; std::list sbl; MyBots::PopulateSBL_BySpawnedBots(c, sbl); - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY); - if (!target_mob) - continue; + for (auto bot_iter : sbl) { + std::list botSpellList = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Mez, c->GetTarget(), IsAEBotSpellType(BotSpellTypes::Mez)); - if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) - continue; + for (const auto& s : botSpellList) { + if (!IsValidSpell(s.SpellId)) { + continue; + } - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; + if (!bot_iter->IsInGroupOrRaid()) { + continue; + } - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); + if (!bot_iter->CastChecks(s.SpellId, c->GetTarget(), BotSpellTypes::Mez, false, false)) { + continue; + } - break; + if (bot_iter->CommandedDoSpellCast(s.SpellIndex, c->GetTarget(), s.ManaCost)) { + bot_iter->BotGroupSay(bot_iter, "Casting %s [%s] on %s.", GetSpellName(s.SpellId), bot_iter->GetSpellTypeNameByID(BotSpellTypes::Mez), c->GetTarget()->GetCleanName()); + isSuccess = true; + } + } } - helper_no_available_bots(c, my_bot); + if (!isSuccess) { + helper_no_available_bots(c); + } } diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index f6c6ae21dd..c8b48368c4 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -19,7 +19,7 @@ void bot_command_pet_get_lost(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_pet_get_lost", sep->arg[0], "petgetlost")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } int ab_mask = ActionableBots::ABM_NoFilter; @@ -98,7 +98,8 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_pet_set_type", sep->arg[0], "petsettype")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [type: water | fire | air | earth | monster] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [type: auto | water | fire | air | earth | monster | epic] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "if set to 'auto', bots will choose their own pet type"); c->Message(Chat::White, "requires one of the following bot classes:"); c->Message(Chat::White, "Magician(1)"); return; @@ -109,29 +110,41 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) uint8 pet_type = 255; uint8 level_req = 255; - if (!pet_arg.compare("water")) { + if (!pet_arg.compare("auto")) { pet_type = 0; level_req = 1; } - else if (!pet_arg.compare("fire")) { + else if (!pet_arg.compare("water")) { pet_type = 1; + level_req = 1; + } + else if (!pet_arg.compare("fire")) { + pet_type = 2; level_req = 3; } else if (!pet_arg.compare("air")) { - pet_type = 2; + pet_type = 3; level_req = 4; } else if (!pet_arg.compare("earth")) { - pet_type = 3; + pet_type = 4; level_req = 5; } else if (!pet_arg.compare("monster")) { - pet_type = 4; + pet_type = 5; level_req = 30; } + else if (!pet_arg.compare("epic")) { + pet_type = 6; + if (!RuleB(Bots, AllowMagicianEpicPet)) { + c->Message(Chat::Yellow, "Epic pets are currently disabled for bots."); + return; + } + level_req = RuleI(Bots,AllowMagicianEpicPetLevel); + } if (pet_type == 255) { - c->Message(Chat::White, "You must specify a pet [type: water | fire | air | earth | monster]"); + c->Message(Chat::White, "You must specify a pet [type: auto | water | fire | air | earth | monster | epic]"); return; } @@ -157,7 +170,23 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) if (!bot_iter) continue; - bot_iter->SetPetChooser(true); + if (RuleI(Bots, RequiredMagicianEpicPetItemID) > 0) { + bool has_item = bot_iter->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX; + + if (!has_item) { + c->Message( + Chat::Say, + fmt::format( + "{} says, 'I require {} to cast an epic pet which I do not currently possess.'", + bot_iter->GetCleanName(), + (database.GetItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) ? database.CreateItemLink(RuleI(Bots, RequiredMagicianEpicPetItemID)) : "an item") + ).c_str() + ); + + continue; + } + } + bot_iter->SetPetChooserID(pet_type); if (bot_iter->GetPet()) { auto pet_id = bot_iter->GetPetID(); diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index 6265d141e7..fa222fcfad 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -7,15 +7,27 @@ void bot_command_pull(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } - int ab_mask = ActionableBots::ABM_OwnerGroup; // existing behavior - need to add c->IsGrouped() check and modify code if different behavior is desired + + const int ab_mask = ActionableBots::ABM_Type1; + + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } std::list sbl; - if (ActionableBots::PopulateSBL(c, "ownergroup", sbl, ab_mask) == ActionableBots::ABT_None) { + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single); diff --git a/zone/bot_commands/release.cpp b/zone/bot_commands/release.cpp index 1872d43d61..bf8741d33d 100644 --- a/zone/bot_commands/release.cpp +++ b/zone/bot_commands/release.cpp @@ -5,7 +5,7 @@ void bot_command_release(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_release", sep->arg[0], "release")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: ] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp new file mode 100644 index 0000000000..ac94a13fd8 --- /dev/null +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_sit_hp_percent(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_sit_hp_percent", sep->arg[0], "sithppercent")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "HP % threshold when bots will sit in combat if allowed." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set Clerics to sit at 45% HP:", + fmt::format( + "{} 45 byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the HP threshold for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I sit in combat whem at or below [{}%%] HP.'", + my_bot->GetCleanName(), + my_bot->GetHPWhenToMed() + ).c_str() + ); + } + else { + my_bot->SetHPWhenToMed(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I will now sit in combat whem at or below [{}%%] HP.'", + first_found->GetCleanName(), + first_found->GetHPWhenToMed() + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots will now sit in combat whem at or below [{}%%] HP.'", + success_count, + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp new file mode 100644 index 0000000000..ffcf4d8878 --- /dev/null +++ b/zone/bot_commands/sit_in_combat.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_sit_in_combat(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_sit_in_combat", sep->arg[0], "sitincombat")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will sit in combat to heal or med." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set Clerics to sit in combat:", + fmt::format( + "{} 1 byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the sit in combat state for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} sit in combat.'", + my_bot->GetCleanName(), + my_bot->GetMedInCombat() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetMedInCombat(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} sit in combat.'", + first_found->GetCleanName(), + first_found->GetMedInCombat() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} sit in combat.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp new file mode 100644 index 0000000000..85afc8047e --- /dev/null +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_sit_mana_percent(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_sit_mana_percent", sep->arg[0], "sitmanapercent")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Mana % threshold when bots will sit in combat if allowed." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set Clerics to sit at 45% mana:", + fmt::format( + "{} 45 byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the mana threshold for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I sit in combat whem at or below [{}%%] mana.'", + my_bot->GetCleanName(), + my_bot->GetManaWhenToMed() + ).c_str() + ); + } + else { + my_bot->SetManaWhenToMed(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I will now sit in combat whem at or below [{}%%] mana.'", + first_found->GetCleanName(), + first_found->GetManaWhenToMed() + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots will now sit in combat whem at or below [{}%%] mana.'", + success_count, + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell.cpp b/zone/bot_commands/spell.cpp index 45d9ec6ebc..833c8ef7ab 100644 --- a/zone/bot_commands/spell.cpp +++ b/zone/bot_commands/spell.cpp @@ -503,7 +503,7 @@ void bot_spell_info_dialogue_window(Client* c, const Seperator *sep) auto results = database.QueryDatabase( fmt::format( "SELECT value FROM db_str WHERE id = {} and type = 6 LIMIT 1", - spells[spell_id].effect_description_id + spells[spell_id].description_id ) ); @@ -557,7 +557,7 @@ void bot_command_enforce_spell_list(Client* c, const Seperator *sep) } bool enforce_state = (sep->argnum > 0) ? Strings::ToBool(sep->arg[1]) : !my_bot->GetBotEnforceSpellSetting(); - my_bot->SetBotEnforceSpellSetting(enforce_state, true); + my_bot->SetBotEnforceSpellSetting(enforce_state); c->Message( Chat::White, diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp new file mode 100644 index 0000000000..e9ae709eee --- /dev/null +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_aggro_checks", sep->arg[0], "spellaggrochecks")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will cast a spell type if they think it will get them aggro" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to check aggro on nukes:", + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + std::vector examples_two = + { + "To set Shadowknights to ignore aggro checks on snares:", + fmt::format( + "{} {} 0 byclass 5", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 0 byclass 5", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_three = + { + "To check the current DoT aggro check on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] aggro check is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeAggroCheck(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + my_bot->SetSpellTypeAggroCheck(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] aggro check was [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeAggroCheck(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots [{}] their [{}] aggro check.", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue ? "enabled" : "disabled" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp new file mode 100644 index 0000000000..3c3b153ca8 --- /dev/null +++ b/zone/bot_commands/spell_delays.cpp @@ -0,0 +1,242 @@ +#include "../bot_command.h" + +void bot_command_spell_delays(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_delays", sep->arg[0], "spelldelays")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls how long a bot will wait between casts of different spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Necromancers to an 8s DoT delay:", + fmt::format( + "{} {} 8000 byclass 11", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} 8000 byclass 11", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_two = + { + "To set all Warriors to receive Fast Heals every 2.5s:", + fmt::format( + "{} {} 2500 byclass 1", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 2500 byclass 1", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Nuke delay on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 1 || typeValue > 60000) { + c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell delay is currently [{}] seconds.'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } + else { + my_bot->SetSpellDelay(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell delay was set to [{}] seconds.'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] spell delay to [{}] seconds.", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue / 1000.00 + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp new file mode 100644 index 0000000000..03c151c3ae --- /dev/null +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -0,0 +1,240 @@ +#include "../bot_command.h" + +void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_engaged_priority", sep->arg[0], "spellengagedpriority")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets the order of spell casts when engaged in combat by spell type" + }; + + std::vector notes = + { + "-Setting a spell type to 0 will prevent that type from being cast.", + "-If 2 or more are set to the same priority they will sort by spell type ID." + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Shaman to cast slows first:", + fmt::format( + "{} {} 1 byclass 10", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) + ), + fmt::format( + "{} {} 1 byclass 10", + sep->arg[0], + BotSpellTypes::Slow + ) + }; + std::vector examples_two = + { + "To set all bots to not cast snares:", + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_three = + { + "To check the current engaged priority of dispels on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Dispel) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Dispel + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] engaged cast priority is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Engaged) + ).c_str() + ); + } + else { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] engaged cast priority was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Engaged) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] engaged cast priority to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp new file mode 100644 index 0000000000..effd97a7ae --- /dev/null +++ b/zone/bot_commands/spell_holds.cpp @@ -0,0 +1,229 @@ +#include "../bot_command.h" + +void bot_command_spell_holds(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_holds", sep->arg[0], "spellholds")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots can cast or receive certain spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to hold DoTs:", + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_two = + { + "To check the current DoT settings on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_three = { }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell hold is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + my_bot->SetSpellHold(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell hold was [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots [{}] their [{}] spell hold.", + success_count, + typeValue ? "enabled" : "disabled", + c->GetSpellTypeNameByID(spellType) + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp new file mode 100644 index 0000000000..9bb95cf467 --- /dev/null +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -0,0 +1,240 @@ +#include "../bot_command.h" + +void bot_command_spell_idle_priority(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_idle_priority", sep->arg[0], "spellidlepriority")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets the order of spell casts when not in combat by spell type" + }; + + std::vector notes = + { + "-Setting a spell type to 0 will prevent that type from being cast.", + "-If 2 or more are set to the same priority they will sort by spell type ID." + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Clerics to cast fast heals third:", + fmt::format( + "{} {} 3 byclass 2", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 3 byclass 2", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_two = + { + "To set all bots to not cast cures:", + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + ), + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + BotSpellTypes::Cure + ) + }; + std::vector examples_three = + { + "To check the current idle priority of buffs on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Buff) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Buff + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] idle cast priority is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Idle) + ).c_str() + ); + } + else { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] idle cast priority was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Idle) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] idle cast priority to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp new file mode 100644 index 0000000000..9d94fd722c --- /dev/null +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_max_hp_pct", sep->arg[0], "spellmaxhppct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what health percentage a bot will start casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to start snaring when their health is at or below 100% HP:", + fmt::format( + "{} {} 100 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to start casting fast heals at 30% HP:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun max HP percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum HP is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMaxHPLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMaxHPLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum HP was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMaxHPLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] maximum HP to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp new file mode 100644 index 0000000000..9e22617b04 --- /dev/null +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_max_mana_pct", sep->arg[0], "spellmaxmanapct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what mana percentage a bot will stop casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to start snaring when their mana is at or below 100% HP:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to start casting fast heals at 30% mana:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun max mana percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum mana is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMaxManaLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMaxManaLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum mana was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMaxManaLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] maximum mana to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp new file mode 100644 index 0000000000..8ded61c1f1 --- /dev/null +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -0,0 +1,243 @@ +#include "../bot_command.h" + +void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_max_thresholds", sep->arg[0], "spellmaxthresholds")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what target HP % the bot will start casting different spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included)", + "are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to start snaring at 99%:", + fmt::format( + "{} {} 99 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 99 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set bot Enchbot to start casting Debuffs at 99%:", + fmt::format( + "{} {} 99 byname Enchbot", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + ), + fmt::format( + "{} {} 99 byname Enchbot", + sep->arg[0], + BotSpellTypes::Debuff + ) + }; + std::vector examples_three = + { + "To check the current Nuke max threshold on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum threshold is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellMaxThreshold(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum threshold was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] maximum threshold to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp new file mode 100644 index 0000000000..739ab0d1cd --- /dev/null +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_min_hp_pct", sep->arg[0], "spellminhppct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what health percentage a bot will stop casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to stop snaring when their health is below 10% HP:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to stop casting fast heals at 30% HP:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun min HP percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum HP is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMinHPLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMinHPLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum HP was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMinHPLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] minimum HP to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp new file mode 100644 index 0000000000..e83362f762 --- /dev/null +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_min_mana_pct", sep->arg[0], "spellminmanapct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what mana percentage a bot will stop casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to stop snaring when their mana is below 10% HP:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to stop casting fast heals at 30% mana:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun min mana percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum mana is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMinManaLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMinManaLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum mana was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMinManaLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] minimum mana to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp new file mode 100644 index 0000000000..6df8fc9993 --- /dev/null +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -0,0 +1,244 @@ +#include "../bot_command.h" + +void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_min_thresholds", sep->arg[0], "spellminthresholds")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what target HP % the bot will stop casting different spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to stop debuffing at 10%:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Debuff + ) + }; + std::vector examples_two = + { + "To set all Druids to stop casting DoTs at 15%:", + fmt::format( + "{} {} 15 byclass 6", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} 15 byclass 6", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_three = + { + "To check the current Fast Heal min threshold on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid", + "targetgroup, namesgroup, healrotationtargets", + "mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum threshold is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellMinThreshold(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellMinThreshold(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum threshold was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellMinThreshold(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] minimum threshold to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp new file mode 100644 index 0000000000..593606437c --- /dev/null +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -0,0 +1,240 @@ +#include "../bot_command.h" + +void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_pursue_priority", sep->arg[0], "spellpursuepriority")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets the order of spell casts when the mob is fleeing in combat by spell type" + }; + + std::vector notes = + { + "- Setting a spell type to 0 will prevent that type from being cast.", + "- If 2 or more are set to the same priority they will sort by spell type ID." + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to cast nukes first:", + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_two = + { + "To set all Shaman to not cast cures:", + fmt::format( + "{} {} 0 byclass 10", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + ), + fmt::format( + "{} {} 0 byclass 10", + sep->arg[0], + BotSpellTypes::Cure + ) + }; + std::vector examples_three = + { + "To check the current pursue priority of buffs on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Buff) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Buff + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] pursue cast priority is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Pursue) + ).c_str() + ); + } + else { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] pursue cast priority was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Pursue) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] pursue cast priority to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp new file mode 100644 index 0000000000..f87e232eeb --- /dev/null +++ b/zone/bot_commands/spell_target_count.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_target_count(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_target_count", sep->arg[0], "spelltargetcount")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Decides how many eligible targets are required for an AE or group spell to cast by spell type" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to AEMez with 5 or more targets:", + fmt::format( + "{} {} 5 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::AEMez) + ), + fmt::format( + "{} {} 5 spawned", + sep->arg[0], + BotSpellTypes::AEMez + ) + }; + std::vector examples_two = + { + "To set Wizards to require 5 targets for AENukes:", + fmt::format( + "{} {} 3 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::AENukes) + ), + fmt::format( + "{} {} 3 byname BotA", + sep->arg[0], + BotSpellTypes::AENukes + ) + }; + std::vector examples_three = + { + "To check the current AESlow count on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::AESlow) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::AESlow + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 1 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 1-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] target count is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeAEOrGroupTargetCount(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeAEOrGroupTargetCount(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] target count was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeAEOrGroupTargetCount(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] target count to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/suspend.cpp b/zone/bot_commands/suspend.cpp index e3198e79e9..287db739cd 100644 --- a/zone/bot_commands/suspend.cpp +++ b/zone/bot_commands/suspend.cpp @@ -6,7 +6,7 @@ void bot_command_suspend(Client *c, const Seperator *sep) return; } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: ] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; diff --git a/zone/bot_commands/taunt.cpp b/zone/bot_commands/taunt.cpp index a15d29a5bd..866e9899c4 100644 --- a/zone/bot_commands/taunt.cpp +++ b/zone/bot_commands/taunt.cpp @@ -2,12 +2,15 @@ void bot_command_taunt(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) + if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; @@ -15,26 +18,30 @@ void bot_command_taunt(Client *c, const Seperator *sep) bool taunt_state = false; bool toggle_taunt = true; int ab_arg = 1; + if (!arg1.compare("on")) { taunt_state = true; toggle_taunt = false; - ab_arg = 2; + ++ab_arg; } else if (!arg1.compare("off")) { toggle_taunt = false; - ab_arg = 2; + ++ab_arg; } std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[(ab_arg + 1)] : nullptr, class_race_check ? atoi(sep->arg[(ab_arg + 1)]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); int taunting_count = 0; diff --git a/zone/bot_commands/timer.cpp b/zone/bot_commands/timer.cpp index 93eab687d6..30b495d3ab 100644 --- a/zone/bot_commands/timer.cpp +++ b/zone/bot_commands/timer.cpp @@ -2,10 +2,12 @@ void bot_command_timer(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer")) + if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] [actionable].", sep->arg[0]); + c->Message(Chat::White, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name])).", sep->arg[0]); c->Message(Chat::White, "When setting, you can leave the value blank to use the default for the item or specify a value in ms to set the timer to."); c->Message(Chat::White, "Returns or sets the provided timer(s) for the selected bot(s) or clears the selected timer(s) for the selected bot(s)."); return; @@ -88,14 +90,17 @@ void bot_command_timer(Client* c, const Seperator* sep) std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); for (auto my_bot : sbl) { diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index a24cd0c308..20715d0d7f 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -35,6 +35,7 @@ #include "../common/repositories/bot_pet_buffs_repository.h" #include "../common/repositories/bot_pet_inventories_repository.h" #include "../common/repositories/bot_spell_casting_chances_repository.h" +#include "../common/repositories/bot_settings_repository.h" #include "../common/repositories/bot_stances_repository.h" #include "../common/repositories/bot_timers_repository.h" #include "../common/repositories/character_data_repository.h" @@ -400,28 +401,13 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) e.spells_id, e.time_spawned, e.zone_id, - t, - e.expansion_bitmask + t ); if (loaded_bot) { loaded_bot->SetSurname(e.last_name); loaded_bot->SetTitle(e.title); loaded_bot->SetSuffix(e.suffix); - - loaded_bot->SetShowHelm(e.show_helm); - - auto bfd = EQ::Clamp(e.follow_distance, static_cast(1), BOT_FOLLOW_DISTANCE_DEFAULT_MAX); - - loaded_bot->SetFollowDistance(bfd); - - loaded_bot->SetStopMeleeLevel(e.stop_melee_level); - - loaded_bot->SetBotEnforceSpellSetting(e.enforce_spell_settings); - - loaded_bot->SetBotArcherySetting(e.archery_setting); - - loaded_bot->SetBotCasterRange(e.caster_range); } return true; @@ -476,13 +462,6 @@ bool BotDatabase::SaveNewBot(Bot* b, uint32& bot_id) e.poison = b->GetBasePR(); e.disease = b->GetBaseDR(); e.corruption = b->GetBaseCorrup(); - e.show_helm = b->GetShowHelm() ? 1 : 0; - e.follow_distance = b->GetFollowDistance(); - e.stop_melee_level = b->GetStopMeleeLevel(); - e.expansion_bitmask = b->GetExpansionBitmask(); - e.enforce_spell_settings = b->GetBotEnforceSpellSetting(); - e.archery_setting = b->IsBotArcher() ? 1 : 0; - e.caster_range = b->GetBotCasterRange(); e = BotDataRepository::InsertOne(database, e); @@ -547,12 +526,6 @@ bool BotDatabase::SaveBot(Bot* b) e.poison = b->GetBasePR(); e.disease = b->GetBaseDR(); e.corruption = b->GetBaseCorrup(); - e.show_helm = b->GetShowHelm() ? 1 : 0; - e.follow_distance = b->GetFollowDistance(); - e.stop_melee_level = b->GetStopMeleeLevel(); - e.expansion_bitmask = b->GetExpansionBitmask(); - e.enforce_spell_settings = b->GetBotEnforceSpellSetting(); - e.archery_setting = b->IsBotArcher() ? 1 : 0; return BotDataRepository::UpdateOne(database, e); } @@ -868,7 +841,7 @@ bool BotDatabase::SaveTimers(Bot* b) std::vector l; if (!v.empty()) { - for (auto & bot_timer : v) { + for (auto& bot_timer : v) { if (bot_timer.timer_value <= Timer::GetCurrentTime()) { continue; } @@ -1670,59 +1643,6 @@ bool BotDatabase::SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_val return BotInventoriesRepository::SaveAllArmorColors(database, owner_id, rgb_value); } -bool BotDatabase::SaveHelmAppearance(const uint32 bot_id, const bool show_flag) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - e.show_helm = show_flag ? 1 : 0; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag) -{ - if (!owner_id) { - return false; - } - - return BotDataRepository::SaveAllHelmAppearances(database, owner_id, show_flag); -} - -bool BotDatabase::ToggleAllHelmAppearances(const uint32 owner_id) -{ - if (!owner_id) { - return false; - } - - return BotDataRepository::ToggleAllHelmAppearances(database, owner_id); -} - -bool BotDatabase::SaveFollowDistance(const uint32 bot_id, const uint32 follow_distance) -{ - if (!bot_id || !follow_distance) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - e.follow_distance = follow_distance; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance) -{ - if (!owner_id || !follow_distance) { - return false; - } - - return BotDataRepository::SaveAllFollowDistances(database, owner_id, follow_distance); -} - bool BotDatabase::CreateCloneBot(const uint32 bot_id, const std::string& clone_name, uint32& clone_id) { if (!bot_id || clone_name.empty()) { @@ -1771,19 +1691,6 @@ bool BotDatabase::CreateCloneBotInventory(const uint32 bot_id, const uint32 clon return BotInventoriesRepository::InsertMany(database, l); } -bool BotDatabase::SaveStopMeleeLevel(const uint32 bot_id, const uint8 sml_value) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - e.stop_melee_level = sml_value; - - return BotDataRepository::UpdateOne(database, e); -} - bool BotDatabase::LoadOwnerOptions(Client* c) { if (!c || !c->CharacterID()) { @@ -2224,74 +2131,6 @@ uint32 BotDatabase::GetRaceClassBitmask(uint32 bot_race) return e.race ? e.classes : 0; } -bool BotDatabase::SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.expansion_bitmask = expansion_bitmask; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.enforce_spell_settings = enforce_spell_setting ? 1 : 0; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveBotArcherSetting(const uint32 bot_id, const bool bot_archer_setting) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.archery_setting = bot_archer_setting ? 1 : 0; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveBotCasterRange(const uint32 bot_id, const uint32 bot_caster_range_value) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.caster_range = bot_caster_range_value; - - return BotDataRepository::UpdateOne(database, e); -} - const uint8 BotDatabase::GetBotClassByID(const uint32 bot_id) { const auto& e = BotDataRepository::FindOne(database, bot_id); @@ -2322,7 +2161,7 @@ std::vector BotDatabase::GetBotIDsByCharacterID(const uint32 character_i class_id ) : "" - ) + ) ) ); @@ -2360,3 +2199,177 @@ const int BotDatabase::GetBotExtraHasteByID(const uint32 bot_id) return e.bot_id ? e.extra_haste : 0; } + +bool BotDatabase::LoadBotSettings(Mob* m) +{ + if (!m) { + return false; + } + + if (!m->IsOfClientBot()) { + return false; + } + + uint32 mobID = (m->IsClient() ? m->CastToClient()->CharacterID() : m->CastToBot()->GetBotID()); + + std::string query = ""; + + if (m->IsClient()) { + query = fmt::format("`char_id` = {}", mobID); + } + else { + query = fmt::format("`bot_id` = {}", mobID); + } + + const auto& l = BotSettingsRepository::GetWhere(database, query); + + if (l.empty()) { + return true; + } + + for (const auto& e : l) { + LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." + , m->GetCleanName() + , (e.setting_type == BotSettingCategories::BaseSetting ? m->CastToBot()->GetBotSettingCategoryName(e.setting_id) : m->CastToBot()->GetBotSpellCategoryName(e.setting_id)) + , e.setting_id + , e.value + ); //deleteme + + m->SetBotSetting(e.setting_type, e.setting_id, e.value); + } + + return true; +} + +bool BotDatabase::SaveBotSettings(Mob* m) +{ + if (!m) { + return false; + } + + if (!m->IsOfClientBot()) { + return false; + } + + uint32 botID = (m->IsClient() ? 0 : m->CastToBot()->GetBotID()); + uint32 charID = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); + + std::string query = ""; + + if (m->IsClient()) { + query = fmt::format("`char_id` = {}", charID); + } + else { + query = fmt::format("`bot_id` = {}", botID); + } + + BotSettingsRepository::DeleteWhere(database, query); + + std::vector v; + + if (m->IsBot()) { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i)) { + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(i), + .setting_type = static_cast(BotSettingCategories::BaseSetting), + .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), + .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->CastToBot()->GetBotSettingCategoryName(i) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); //deleteme + } + } + + for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) { + for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { + if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x)) { + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(x), + .setting_type = static_cast(i), + .value = m->CastToBot()->GetSetting(i, x), + .category_name = m->CastToBot()->GetBotSpellCategoryName(i), + .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x)); //deleteme + } + } + } + } + + if (m->IsClient()) { + if (m->CastToClient()->GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock) != m->GetIllusionBlock()) { // Only illusion block supported + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(BotBaseSettings::IllusionBlock), + .setting_type = static_cast(BotSettingCategories::BaseSetting), + .value = m->GetIllusionBlock(), + .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->GetIllusionBlock()); //deleteme + } + + for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) { + for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { + LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + if (IsClientBotSpellType(x) && m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(x), + .setting_type = static_cast(i), + .value = m->CastToClient()->GetBotSetting(i, x), + .category_name = m->CastToBot()->GetBotSpellCategoryName(i), + .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + } + } + } + } + + if (!v.empty()) { + const int inserted = BotSettingsRepository::ReplaceMany(database, v); + + if (!inserted) { + return false; + } + } + + return true; +} + +bool BotDatabase::DeleteBotSettings(const uint32 bot_id) +{ + if (!bot_id) { + return false; + } + + BotSettingsRepository::DeleteWhere( + database, + fmt::format( + "`bot_id` = {}", + bot_id + ) + ); + + return true; +} diff --git a/zone/bot_database.h b/zone/bot_database.h index 139ebc643f..3539db5c6a 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -88,10 +88,6 @@ class BotDatabase bool SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, const uint32 color); - bool SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask); - bool SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting); - - /* Bot pet functions */ bool LoadPetIndex(const uint32 bot_id, uint32& pet_index); bool LoadPetSpellID(const uint32 bot_id, uint32& pet_spell_id); @@ -120,26 +116,16 @@ class BotDatabase bool SaveAllArmorColorBySlot(const uint32 owner_id, const int16 slot_id, const uint32 rgb_value); bool SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_value); - bool SaveHelmAppearance(const uint32 bot_id, const bool show_flag = true); - bool SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag = true); - - bool ToggleAllHelmAppearances(const uint32 owner_id); - - bool SaveFollowDistance(const uint32 bot_id, const uint32 follow_distance); - bool SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance); - bool CreateCloneBot(const uint32 bot_id, const std::string& clone_name, uint32& clone_id); bool CreateCloneBotInventory(const uint32 bot_id, const uint32 clone_id); - bool SaveStopMeleeLevel(const uint32 bot_id, const uint8 sml_value); - - bool SaveBotArcherSetting(const uint32 bot_id, const bool bot_archer_setting); - bool LoadOwnerOptions(Client *owner); bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag); bool SaveOwnerOption(const uint32 owner_id, const std::pair type, const std::pair flag); - bool SaveBotCasterRange(const uint32 bot_id, const uint32 bot_caster_range_value); + bool LoadBotSettings(Mob* m); + bool SaveBotSettings(Mob* m); + bool DeleteBotSettings(const uint32 bot_id); /* Bot group functions */ bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list& group_list); @@ -210,12 +196,6 @@ class BotDatabase static const char* SaveAllInspectMessages(); static const char* SaveAllArmorColorBySlot(); static const char* SaveAllArmorColors(); - static const char* SaveAllHelmAppearances(); - static const char* ToggleAllHelmAppearances(); - static const char* SaveFollowDistance(); - static const char* SaveAllFollowDistances(); - static const char* SaveStopMeleeLevel(); - static const char* SaveBotCasterRange(); /* fail::Bot heal rotation functions */ static const char* LoadHealRotation(); diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 50e319e9e9..0cfc5913ea 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -92,4 +92,9 @@ struct BotTimer_Struct { uint32 item_id; }; +struct BotSpellTypeOrder { + uint16 spellType; + uint16 priority; +}; + #endif // BOT_STRUCTS diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 0050a58b20..ee3aaf3a37 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -21,1226 +21,528 @@ #include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/npc_spells_repository.h" -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { - - // Bot AI - Raid* raid = entity_list.GetRaidByBotName(GetName()); - +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { if (!tar) { return false; } - if (!AI_HasSpells()) { + LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + + if ( + !AI_HasSpells() || + (spellType == BotSpellTypes::Pet && tar != this) || + (IsPetBotSpellType(spellType) && !tar->IsPet()) || + (spellType == BotSpellTypes::Buff && tar->IsPet()) || + (spellType == BotSpellTypes::InCombatBuffSong && tar->IsPet()) || + (spellType == BotSpellTypes::OutOfCombatBuffSong && tar->IsPet()) || + (spellType == BotSpellTypes::PreCombatBuffSong && tar->IsPet()) || + (!RuleB(Bots, AllowBuffingHealingFamiliars) && tar->IsFamiliar()) || + (tar->IsPet() && tar->IsCharmed() && spellType == BotSpellTypes::PetBuffs && !RuleB(Bots, AllowCharmedPetBuffs)) || + (tar->IsPet() && tar->IsCharmed() && (spellType == BotSpellTypes::Cure || spellType == BotSpellTypes::GroupCures) && !RuleB(Bots, AllowCharmedPetCures)) || + (tar->IsPet() && tar->IsCharmed() && IsHealBotSpellType(spellType) && !RuleB(Bots, AllowCharmedPetHeals)) + ) { return false; } - + if (iChance < 100 && zone->random.Int(0, 100) > iChance) { return false; } - if (tar->GetAppearance() == eaDead && ((tar->IsClient() && !tar->CastToClient()->GetFeigned()) || tar->IsBot())) { - return false; + if (spellType != BotSpellTypes::Resurrect && tar->GetAppearance() == eaDead) { + if (!((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot())) { + return false; + } } uint8 botClass = GetClass(); - uint8 botLevel = GetLevel(); - bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. + SetCastedSpellType(UINT16_MAX); BotSpell botSpell; botSpell.SpellId = 0; botSpell.SpellIndex = 0; botSpell.ManaCost = 0; - switch (iSpellTypes) { - case SpellType_Mez: - return BotCastMez(tar, botLevel, checked_los, botSpell, raid); - case SpellType_Heal: - return BotCastHeal(tar, botLevel, botClass, botSpell, raid); - case SpellType_Root: - return BotCastRoot(tar, botLevel, iSpellTypes, botSpell, checked_los); - case SpellType_Buff: - return BotCastBuff(tar, botLevel, botClass); - case SpellType_Escape: - return BotCastEscape(tar, botClass, botSpell, iSpellTypes); - case SpellType_Nuke: - return BotCastNuke(tar, botLevel, botClass, botSpell, checked_los); - case SpellType_Dispel: - return BotCastDispel(tar, botSpell, iSpellTypes, checked_los); - case SpellType_Pet: - return BotCastPet(tar, botClass, botSpell); - case SpellType_InCombatBuff: - return BotCastCombatBuff(tar, botLevel, botClass); - case SpellType_Lifetap: - return BotCastLifetap(tar, botLevel, botSpell, checked_los, iSpellTypes); - case SpellType_Snare: - return BotCastSnare(tar, botLevel, botSpell, checked_los, iSpellTypes); - case SpellType_DOT: - return BotCastDOT(tar, botLevel, botSpell, checked_los); - case SpellType_Slow: - return BotCastSlow(tar, botLevel, botClass, botSpell, checked_los, raid); - case SpellType_Debuff: - return BotCastDebuff(tar, botLevel, botSpell, checked_los); - case SpellType_Cure: - return BotCastCure(tar, botClass, botSpell, raid); - case SpellType_HateRedux: - return BotCastHateReduction(tar, botLevel, botSpell); - case SpellType_InCombatBuffSong: - return BotCastCombatSong(tar, botLevel); - case SpellType_OutOfCombatBuffSong: - return BotCastSong(tar, botLevel); - case SpellType_Resurrect: - case SpellType_PreCombatBuff: - case SpellType_PreCombatBuffSong: - default: - return false; + if (SpellTypeRequiresLoS(spellType, botClass) && tar != this) { + SetHasLoS(DoLosChecks(this, tar)); + } + else { + SetHasLoS(true); } - return false; -} - -bool Bot::BotCastSong(Mob* tar, uint8 botLevel) { - bool casted_spell = false; - if (GetClass() != Class::Bard || tar != this || IsEngaged()) // Out-of-Combat songs can not be cast in combat - return casted_spell; + switch (spellType) { + case BotSpellTypes::Slow: + if (tar->GetSpecialAbility(SpecialAbility::SlowImmunity)) { + return false; + } - for (auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_OutOfCombatBuffSong); - auto iter : botSongList) { - if (!iter.SpellId) - continue; - if (!CheckSpellRecastTimer(iter.SpellId)) - continue; - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) - continue; - switch (spells[iter.SpellId].target_type) { - case ST_AEBard: - case ST_AECaster: - case ST_GroupTeleport: - case ST_Group: - case ST_Self: break; - default: - continue; - } - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) - continue; + case BotSpellTypes::Snare: + if (tar->GetSpecialAbility(SpecialAbility::SnareImmunity)) { + return false; + } - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); - if (casted_spell) break; - } - - return casted_spell; -} + //SpecialAbility::PacifyImmunity -- TODO bot rewrite + case BotSpellTypes::Fear: + if (tar->GetSpecialAbility(SpecialAbility::FearImmunity)) { + return false; + } -bool Bot::BotCastCombatSong(Mob* tar, uint8 botLevel) { - bool casted_spell = false; - if (tar != this) { // In-Combat songs can be cast Out-of-Combat in preparation for battle - return casted_spell; - } - for (auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_InCombatBuffSong); - auto iter : botSongList) { - if (!iter.SpellId) - continue; - if (!CheckSpellRecastTimer(iter.SpellId)) - continue; - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) - continue; - switch (spells[iter.SpellId].target_type) { - case ST_AEBard: - case ST_AECaster: - case ST_GroupTeleport: - case ST_Group: - case ST_Self: - break; - default: - continue; - } - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) - continue; + if (!IsCommandedSpell() && (tar->IsRooted() || tar->GetSnaredAmount() == -1)) { + return false; + } - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); - if (casted_spell) break; - } + case BotSpellTypes::Dispel: + if (tar->GetSpecialAbility(SpecialAbility::DispellImmunity)) { + return false; + } - return casted_spell; -} + if (!IsCommandedSpell() && tar->CountDispellableBuffs() <= 0) { + return false; + } -bool Bot::BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpell) { - bool casted_spell = false; - if (GetClass() == Class::Bard) { - std::list botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_HateRedux); - for (auto iter : botSongList) { - if (!iter.SpellId) - continue; - if (!CheckSpellRecastTimer(iter.SpellId)) - continue; - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) - continue; - if (spells[iter.SpellId].target_type != ST_Target) - continue; - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) - continue; + break; + case BotSpellTypes::HateRedux: + if (!IsCommandedSpell() && !GetNeedsHateRedux(tar)) { + return false; + } - if (IsValidSpellRange(iter.SpellId, tar)) { - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); + break; + case BotSpellTypes::InCombatBuff: + if (GetClass() == Class::ShadowKnight && (tar->IsOfClientBot() || (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()))) { + return false; } - if (casted_spell) { - BotGroupSay( - this, - fmt::format( - "Attempting to reduce hate on {} with {}.", - tar->GetCleanName(), - spells[iter.SpellId].name - ).c_str() - ); + + if (!IsCommandedSpell() && GetClass() != Class::Shaman && spellType == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { + return false; } - } - } - return casted_spell; -} + break; + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + return false; + } -bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, Raid* raid) { - bool casted_spell = false; - if ( - GetNeedsCured(tar) && - (tar->DontCureMeBefore() < Timer::GetCurrentTime()) && - GetNumberNeedingHealedInGroup(25, false, raid) <= 0 && - GetNumberNeedingHealedInGroup(40, false, raid) <= 2 - ) { - botSpell = GetBestBotSpellForCure(this, tar); + break; + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + if (!IsCommandedSpell() && (IsEngaged() || tar->IsEngaged())) { // Out-of-Combat songs can not be cast in combat + return false; + } - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } + break; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return BotCastMez(tar, botClass, botSpell, spellType); + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + return BotCastNuke(tar, botClass, botSpell, spellType); + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + return false; + } - uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); + return BotCastHeal(tar, botClass, botSpell, spellType); + case BotSpellTypes::GroupCures: + case BotSpellTypes::Cure: + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + return false; + } - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontCureMeBeforeTime); + return BotCastCure(tar, botClass, botSpell, spellType); + case BotSpellTypes::Pet: + if (HasPet() || IsBotCharmer()) { + return false; + } - if (casted_spell && botClass != Class::Bard) { - if (IsGroupSpell(botSpell.SpellId)) { - if (HasGroup()) { - Group const* group = GetGroup(); - if (group) { - for (auto member : group->members) { - if ( - member && - !member->qglobal && - TempDontCureMeBeforeTime != tar->DontCureMeBefore() - ) { - member->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - } else if (IsRaidGrouped()) { - uint32 r_group = raid->GetGroup(GetName()); - if (r_group) { - std::vector raid_group_members = raid->GetRaidGroupMembers(r_group); - for (auto& iter: raid_group_members) { - if ( - iter.member && - !iter.member->qglobal && - TempDontCureMeBeforeTime != tar->DontCureMeBefore() - ) { - iter.member->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - } - } else if (TempDontCureMeBeforeTime != tar->DontCureMeBefore()) { - tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); + return BotCastPet(tar, botClass, botSpell, spellType); + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + return false; } - } - } - return casted_spell; -} -bool Bot::BotCastDebuff(Mob* tar, uint8 botLevel, BotSpell& botSpell, bool checked_los) { - bool casted_spell = false; - if ((tar->GetHPRatio() <= 99.0f) && (tar->GetHPRatio() > 20.0f)) - { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } + break; + case BotSpellTypes::Charm: + if (tar->IsCharmed() || !tar->IsNPC() || tar->GetSpecialAbility(SpecialAbility::CharmImmunity)) { + return false; + } - botSpell = GetBestBotSpellForResistDebuff(this, tar); + break; + default: + break; + } - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetDebuffBotSpell(this, tar); - } + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } + for (const auto& s : botSpellList) { - if (! - ( - !tar->IsImmuneToSpell(botSpell.SpellId, this) && - (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0) - ) - ) { - return casted_spell; + if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { + continue; } - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + if (IsInvulnerabilitySpell(s.SpellId)) { + tar = this; //target self for invul type spells } - } - return casted_spell; -} -bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los, Raid* raid) { - bool casted_spell = false; - if (tar->GetHPRatio() <= 99.0f) { - - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; + if (IsCommandedSpell() && IsCasting()) { + BotGroupSay( + this, + fmt::format( + "Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.", + CastingSpellID() ? spells[CastingSpellID()].name : "my spell", + GetSpellTypeNameByID(spellType), + spells[s.SpellId].name, + tar->GetCleanName() + ).c_str() + ); + + InterruptSpell(); } - switch (botClass) { - case Class::Bard: { - // probably needs attackable check - for ( - auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_Slow); - auto iter : botSongList - ) { - - if (!iter.SpellId) { - continue; - } - - if (!CheckSpellRecastTimer(iter.SpellId)) { - continue; - } - - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) { - continue; - } - - if (spells[iter.SpellId].target_type != ST_Target) { - continue; - } - - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) { - continue; - } - - if (IsValidSpellRange(iter.SpellId, tar)) { - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); - } + if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + SetCastedSpellType(UINT16_MAX); - if (casted_spell) { - return casted_spell; - } + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); } - - break; - } - case Class::Enchanter: { - botSpell = GetBestBotSpellForMagicBasedSlow(this); - break; } - case Class::Shaman: - case Class::Beastlord: { - botSpell = GetBestBotSpellForDiseaseBasedSlow(this); - - if (botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].resist_difficulty))) - botSpell = GetBestBotSpellForMagicBasedSlow(this); - break; + else { + SetCastedSpellType(spellType); } - } - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) { - return casted_spell; - } - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); - } - - if (casted_spell && GetClass() != Class::Bard) { - if (raid) { - const auto msg = fmt::format("Attempting to slow {}.", tar->GetCleanName()); - raid->RaidSay(msg.c_str(), GetCleanName(), Language::CommonTongue, Language::MaxValue); - } else { + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { BotGroupSay( this, fmt::format( - "Attempting to slow {} with {}.", - tar->GetCleanName(), - spells[botSpell.SpellId].name + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spellType), + (tar == this ? "myself" : tar->GetCleanName()) ).c_str() ); } + + return true; } } - return casted_spell; + + return false; } -bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const bool& checked_los) { - bool casted_spell = false; +bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); - if ((tar->GetHPRatio() <= 98.0f) && (tar->DontDotMeBefore() < Timer::GetCurrentTime()) && (tar->GetHPRatio() > 15.0f)) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; + for (const auto& s : botSpellList) { + if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { + continue; } - if (GetClass() == Class::Bard) { - std::list dotList = GetPrioritizedBotSpellsBySpellType(this, SpellType_DOT); + if (!IsCommandedSpell()) { + Mob* addMob = GetFirstIncomingMobToMez(this, s.SpellId, spellType, IsAEBotSpellType(spellType)); - const int maxDotSelect = 5; - int dotSelectCounter = 0; + if (!addMob) { + return false; + } - for (const auto& s : dotList) { + tar = addMob; + } - if (!IsValidSpell(s.SpellId)) { - continue; + if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + SetCastedSpellType(UINT16_MAX); + + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); } + } + else { + SetCastedSpellType(spellType); + } - if (CheckSpellRecastTimer(s.SpellId)) - { - - if (!(!tar->IsImmuneToSpell(s.SpellId, this) && tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) { - continue; - } - - uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + BotGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spellType), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontDotMeBefore); + return true; + } + } - if (TempDontDotMeBefore != tar->DontDotMeBefore()) { - tar->SetDontDotMeBefore(TempDontDotMeBefore); - } - } + return false; +} - dotSelectCounter++; +bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + uint32 currentTime = Timer::GetCurrentTime(); + uint32 nextCureTime = tar->DontCureMeBefore(); - if ((dotSelectCounter == maxDotSelect) || casted_spell) { - break; - } - } + if (!IsCommandedSpell()) { + if ((nextCureTime > currentTime) || !GetNeedsCured(tar)) { + return false; } - else { - std::list dotList = GetBotSpellsBySpellType(this, SpellType_DOT); - - const int maxDotSelect = 5; - int dotSelectCounter = 0; - - for (const auto& s : dotList) { + } - if (!IsValidSpell(s.SpellId)) { - continue; - } + botSpell = GetBestBotSpellForCure(this, tar, spellType); - if (CheckSpellRecastTimer(s.SpellId)) { + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + return false; + } - if (!(!tar->IsImmuneToSpell(s.SpellId, this) && - tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) { - continue; - } + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (IsGroupSpell(botSpell.SpellId)) { + BotGroupSay( + this, + fmt::format( + "Curing the group with {}.", + GetSpellName(botSpell.SpellId) + ).c_str() + ); - uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + const std::vector v = GatherGroupSpellTargets(tar); - if (IsValidSpellRange(s.SpellId, tar)) { - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontDotMeBefore); - } - if (TempDontDotMeBefore != tar->DontDotMeBefore()) { - tar->SetDontDotMeBefore(TempDontDotMeBefore); + if (!IsCommandedSpell()) { + for (Mob* m : v) { + SetBotSpellRecastTimer(spellType, m, true); } } + } + else { + BotGroupSay( + this, + fmt::format( + "Curing {} with {}.", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(botSpell.SpellId) + ).c_str() + ); - dotSelectCounter++; - - if ((dotSelectCounter == maxDotSelect) || casted_spell) { - return casted_spell; + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); } + + return true; } } } - return casted_spell; + + return false; } -bool Bot::BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) { - bool casted_spell = false; - if (tar->DontSnareMeBefore() < Timer::GetCurrentTime()) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; +bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + if (botClass == Class::Wizard) { + auto buffs_max = GetMaxBuffSlots(); + auto my_buffs = GetBuffs(); + int familiar_buff_slot = -1; + if (buffs_max && my_buffs) { + for (int index = 0; index < buffs_max; ++index) { + if (IsEffectInSpell(my_buffs[index].spellid, SE_Familiar)) { + MakePet(my_buffs[index].spellid, spells[my_buffs[index].spellid].teleport_zone); + familiar_buff_slot = index; + break; + } + } } - - - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; + if (GetPetID()) { + return false; } - - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) { - return casted_spell; + if (familiar_buff_slot >= 0) { + BuffFadeBySlot(familiar_buff_slot); + return false; } - uint32 TempDontSnareMeBefore = tar->DontSnareMeBefore(); - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontSnareMeBefore); - } + botSpell = GetFirstBotSpellBySpellType(this, spellType); + } + else if (botClass == Class::Magician) { + botSpell = GetBestBotMagicianPetSpell(this, spellType); + } + else { + botSpell = GetFirstBotSpellBySpellType(this, spellType); + } - if (TempDontSnareMeBefore != tar->DontSnareMeBefore()) { - tar->SetDontSnareMeBefore(TempDontSnareMeBefore); - } + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + return false; } - return casted_spell; -} -bool Bot::BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) { - bool casted_spell = false; - if (GetHPRatio() < 90.0f) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + SetCastedSpellType(spellType); + BotGroupSay( + this, + fmt::format( + "Summoning a pet [{}].", + GetSpellName(botSpell.SpellId) + ).c_str() + ); + + return true; + } + return false; +} - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); +bool Bot::BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + if (spellType == BotSpellTypes::Stun || spellType == BotSpellTypes::AEStun) { + uint8 stunChance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal)); - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; + if (botClass == Class::Paladin) { + stunChance = RuleI(Bots, StunCastChancePaladins); } - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))) { - return casted_spell; + if ( + !tar->GetSpecialAbility(SpecialAbility::StunImmunity) && + ( + IsCommandedSpell() || + (!tar->IsStunned() && (zone->random.Int(1, 100) <= stunChance)) + ) + ) { + botSpell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spellType, IsAEBotSpellType(spellType), tar); } - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + return false; } } - return casted_spell; -} -bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) { + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + botSpell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spellType, IsAEBotSpellType(spellType), tar); + } - bool casted_spell = false; - if (tar->DontBuffMeBefore() < Timer::GetCurrentTime()) { - std::list buffSpellList = GetBotSpellsBySpellType(this, SpellType_InCombatBuff); + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS()) && spellType == BotSpellTypes::Nuke && botClass == Class::Wizard) { + botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spellType); + } - for (const auto& s : buffSpellList) { + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); - if (!IsValidSpell(s.SpellId)) { - continue; - } - // no buffs with illusions.. use #bot command to cast illusions - if (IsEffectInSpell(s.SpellId, SE_Illusion) && tar != this) { - continue; - } - //no teleport spells use #bot command to cast teleports - if (IsEffectInSpell(s.SpellId, SE_Teleport) || IsEffectInSpell(s.SpellId, SE_Succor)) { - continue; - } - // can not cast buffs for your own pet only on another pet that isn't yours - if ((spells[s.SpellId].target_type == ST_Pet) && (tar != GetPet())) { + for (const auto& s : botSpellList) { + if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { continue; } - //Conversion Spells - if ( - IsSelfConversionSpell(s.SpellId) && - ( - GetManaRatio() > 90.0f || - GetHPRatio() < 50.0f || - GetHPRatio() < (GetManaRatio() + 10.0f) - ) - ) { - break; //don't cast if low hp, lots of mana, or if mana is higher than hps - } + if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { + SetCastedSpellType(spellType); - // Validate target - // TODO: Add ST_TargetsTarget support for Buffing. - if ( - !( - ( - spells[s.SpellId].target_type == ST_Target || - spells[s.SpellId].target_type == ST_Pet || - (tar == this && spells[s.SpellId].target_type != ST_TargetsTarget) || - spells[s.SpellId].target_type == ST_Group || - spells[s.SpellId].target_type == ST_GroupTeleport || - (botClass == Class::Bard && spells[s.SpellId].target_type == ST_AEBard) - ) && - !tar->IsImmuneToSpell(s.SpellId, this) && - tar->CanBuffStack(s.SpellId, botLevel, true) >= 0 - ) - ) { - continue; - } - - // Put the zone levitate and movement check here since bots are able to bypass the client casting check - if ( - ((IsEffectInSpell(s.SpellId, SE_Levitate) && !zone->CanLevitate()) || - (IsEffectInSpell(s.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor())) && - (botClass != Class::Bard || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType())) - ) { - continue; - } - - if (!IsGroupSpell(s.SpellId)) { - //Only check archetype if spell is not a group spell - //Hybrids get all buffs - switch (tar->GetArchetype()) { - case Archetype::Caster: - //TODO: probably more caster specific spell effects in here - if ( - ( - IsEffectInSpell(s.SpellId, SE_AttackSpeed) || - IsEffectInSpell(s.SpellId, SE_ATK) || - IsEffectInSpell(s.SpellId, SE_STR) || - IsEffectInSpell(s.SpellId, SE_ReverseDS) || - IsEffectInSpell(s.SpellId, SE_DamageShield) - ) && - spells[s.SpellId].target_type != ST_Self - ) { - continue; - } - break; - case Archetype::Melee: - if ( - ( - IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) || - IsEffectInSpell(s.SpellId, SE_ManaPool) || - IsEffectInSpell(s.SpellId, SE_CastingLevel) || - IsEffectInSpell(s.SpellId, SE_ManaRegen_v2) || - IsEffectInSpell(s.SpellId, SE_CurrentMana) - ) && - spells[s.SpellId].target_type != ST_Self - ) { - continue; - } - break; - default: - break; - } - } - // TODO: Add TriggerSpell Support for Exchanter Runes - if (botClass == Class::Enchanter && IsEffectInSpell(s.SpellId, SE_Rune)) { - float manaRatioToCast = 75.0f; - - switch(GetBotStance()) { - case Stance::Efficient: - manaRatioToCast = 90.0f; - break; - case Stance::Balanced: - case Stance::Aggressive: - manaRatioToCast = 75.0f; - break; - case Stance::Reactive: - case Stance::Burn: - case Stance::AEBurn: - manaRatioToCast = 50.0f; - break; - default: - manaRatioToCast = 75.0f; - break; - } - - //If we're at specified mana % or below, don't rune as enchanter - if (GetManaRatio() <= manaRatioToCast) { - break; - } - } - - if (CheckSpellRecastTimer(s.SpellId)) { - uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore(); - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontBuffMeBefore); - if (TempDontBuffMeBefore != tar->DontBuffMeBefore()) { - tar->SetDontBuffMeBefore(TempDontBuffMeBefore); + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + BotGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spellType), + tar->GetCleanName() + ).c_str() + ); } - } - - if (casted_spell) { - return casted_spell; - } - } - } - return casted_spell; -} -bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell) { - bool casted_spell = false; - if (!IsPet() && !GetPetID() && !IsBotCharmer()) { - if (botClass == Class::Wizard) { - auto buffs_max = GetMaxBuffSlots(); - auto my_buffs = GetBuffs(); - int familiar_buff_slot = -1; - if (buffs_max && my_buffs) { - for (int index = 0; index < buffs_max; ++index) { - if (IsEffectInSpell(my_buffs[index].spellid, SE_Familiar)) { - MakePet(my_buffs[index].spellid, spells[my_buffs[index].spellid].teleport_zone); - familiar_buff_slot = index; - break; - } - } - } - if (GetPetID()) { - return casted_spell; - } - if (familiar_buff_slot >= 0) { - BuffFadeBySlot(familiar_buff_slot); - return casted_spell; + return true; } - - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet); - } - else if (botClass == Class::Magician) { - botSpell = GetBestBotMagicianPetSpell(this); - } - else { - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet); - } - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); - } - return casted_spell; -} - -bool Bot::BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const bool& checked_los) { - - bool casted_spell = false; - if (tar->GetHPRatio() > 95.0f) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } - - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - // TODO: Check target to see if there is anything to dispel - - if (tar->CountDispellableBuffs() > 0 && IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); } } - return casted_spell; -} - -bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los) { - - bool casted_spell = false; - if ((tar->GetHPRatio() <= 95.0f) || ((botClass == Class::Bard) || (botClass == Class::Shaman) || (botClass == Class::Enchanter) || (botClass == Class::Paladin) || (botClass == Class::ShadowKnight) || (botClass == Class::Warrior))) - { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } - - if (botClass == Class::Cleric || botClass == Class::Enchanter) - { - float manaRatioToCast = 75.0f; - - switch(GetBotStance()) { - case Stance::Efficient: - manaRatioToCast = 90.0f; - break; - case Stance::Balanced: - manaRatioToCast = 75.0f; - break; - case Stance::Reactive: - case Stance::Aggressive: - manaRatioToCast = 50.0f; - break; - case Stance::Burn: - case Stance::AEBurn: - manaRatioToCast = 25.0f; - break; - default: - manaRatioToCast = 50.0f; - break; - } - - //If we're at specified mana % or below, don't nuke as cleric or enchanter - if (GetManaRatio() <= manaRatioToCast) - return casted_spell; - } - - if (botClass == Class::Magician || botClass == Class::ShadowKnight || botClass == Class::Necromancer || botClass == Class::Paladin || botClass == Class::Ranger || botClass == Class::Druid || botClass == Class::Cleric) { - if (tar->GetBodyType() == BodyType::Undead || tar->GetBodyType() == BodyType::SummonedUndead || tar->GetBodyType() == BodyType::Vampire) - botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Undead); - else if (tar->GetBodyType() == BodyType::Summoned || tar->GetBodyType() == BodyType::Summoned2 || tar->GetBodyType() == BodyType::Summoned3) - botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Summoned); - } - - if ((botClass == Class::Paladin || botClass == Class::Druid || botClass == Class::Cleric || botClass == Class::Enchanter || botClass == Class::Wizard) && !IsValidSpell(botSpell.SpellId)) { - uint8 stunChance = (tar->IsCasting() ? 30: 15); - - if (botClass == Class::Paladin) { - stunChance = 50; - } + else { + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + SetCastedSpellType(spellType); - if (!tar->GetSpecialAbility(SpecialAbility::StunImmunity) && !tar->IsStunned() && (zone->random.Int(1, 100) <= stunChance)) { - botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target); + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + BotGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(botSpell.SpellId), + GetSpellTypeNameByID(spellType), + tar->GetCleanName() + ).c_str() + ); } - } - if (botClass == Class::Wizard && botSpell.SpellId == 0) { - botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar); - } - - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Target); - } - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))) { - return casted_spell; - } - if (IsFearSpell(botSpell.SpellId) && tar->GetSnaredAmount() == -1 && !tar->IsRooted()) { - return casted_spell; - } - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); - } - } - return casted_spell; -} - -bool Bot::BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iSpellTypes) { - - bool casted_spell = false; - auto hpr = (uint8) GetHPRatio(); - bool mayGetAggro = false; - - if (hpr > 15 && ((botClass == Class::Wizard) || (botClass == Class::Enchanter) || (botClass == Class::Ranger))) { - mayGetAggro = HasOrMayGetAggro(); - } - - if (hpr <= 15 || mayGetAggro) - { - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - - if (IsInvulnerabilitySpell(botSpell.SpellId)) { - tar = this; //target self for invul type spells - } - - if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == Class::Bard) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + return true; } } - return casted_spell; -} - -bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) { - bool casted_spell = false; - if (tar->DontBuffMeBefore() < Timer::GetCurrentTime()) { - std::list buffSpellList = GetBotSpellsBySpellType(this, SpellType_Buff); - - for(const auto& s : buffSpellList) { - - if (!IsValidSpell(s.SpellId)) { - continue; - } - - // no buffs with illusions.. use #bot command to cast illusions - if (IsEffectInSpell(s.SpellId, SE_Illusion) && tar != this) { - continue; - } - - //no teleport spells use #bot command to cast teleports - if (IsEffectInSpell(s.SpellId, SE_Teleport) || IsEffectInSpell(s.SpellId, SE_Succor)) { - continue; - } - // can not cast buffs for your own pet only on another pet that isn't yours - if ((spells[s.SpellId].target_type == ST_Pet) && (tar != GetPet())) { - continue; - } - - // Validate target - // TODO: Add ST_TargetsTarget support for Buffing. - if ( - !( - ( - spells[s.SpellId].target_type == ST_Target || - spells[s.SpellId].target_type == ST_Pet || - (tar == this && spells[s.SpellId].target_type != ST_TargetsTarget) || - spells[s.SpellId].target_type == ST_Group || - spells[s.SpellId].target_type == ST_GroupTeleport || - (botClass == Class::Bard && spells[s.SpellId].target_type == ST_AEBard) - ) && - !tar->IsImmuneToSpell(s.SpellId, this) && - tar->CanBuffStack(s.SpellId, botLevel, true) >= 0 - ) - ) { - continue; - } - - // Put the zone levitate and movement check here since bots are able to bypass the client casting check - if ( - ( - (IsEffectInSpell(s.SpellId, SE_Levitate) && !zone->CanLevitate()) || - (IsEffectInSpell(s.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor()) - ) && - (botClass != Class::Bard || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType())) - ) { - continue; - } - - switch (tar->GetArchetype()) - { - case Archetype::Caster: - //TODO: probably more caster specific spell effects in here - if (IsEffectInSpell(s.SpellId, SE_AttackSpeed) || IsEffectInSpell(s.SpellId, SE_ATK) || - IsEffectInSpell(s.SpellId, SE_STR) || IsEffectInSpell(s.SpellId, SE_ReverseDS)) - { - continue; - } - break; - case Archetype::Melee: - if (IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(s.SpellId, SE_ManaPool) || - IsEffectInSpell(s.SpellId, SE_CastingLevel) || IsEffectInSpell(s.SpellId, SE_ManaRegen_v2) || - IsEffectInSpell(s.SpellId, SE_CurrentMana)) - { - continue; - } - break; - default: //Hybrids get all buffs - break; - } - if (botClass == Class::Enchanter && IsEffectInSpell(s.SpellId, SE_Rune)) - { - float manaRatioToCast = 75.0f; - - switch (GetBotStance()) { - case Stance::Efficient: - manaRatioToCast = 90.0f; - break; - case Stance::Balanced: - case Stance::Aggressive: - manaRatioToCast = 75.0f; - break; - case Stance::Reactive: - case Stance::Burn: - case Stance::AEBurn: - manaRatioToCast = 50.0f; - break; - default: - manaRatioToCast = 75.0f; - break; - } - - //If we're at specified mana % or below, don't rune as enchanter - if (GetManaRatio() <= manaRatioToCast) { - return casted_spell; - } - } - - if (CheckSpellRecastTimer(s.SpellId)) - { - uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore(); - - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontBuffMeBefore); - - if (TempDontBuffMeBefore != tar->DontBuffMeBefore()) { - tar->SetDontBuffMeBefore(TempDontBuffMeBefore); - } - } - } - } - return casted_spell; + return false; } -bool Bot::BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los) { - bool casted_spell = false; - if (!tar->IsRooted() && tar->DontRootMeBefore() < Timer::GetCurrentTime()) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } +bool Bot::BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + botSpell = GetSpellByHealType(spellType, tar); - // TODO: If there is a ranger in the group then don't allow root spells - - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - if (tar->CanBuffStack(botSpell.SpellId, botLevel, true) == 0) { - return casted_spell; - } - uint32 TempDontRootMeBefore = tar->DontRootMeBefore(); - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore); - } - - if (TempDontRootMeBefore != tar->DontRootMeBefore()) { - tar->SetDontRootMeBefore(TempDontRootMeBefore); - } + if (!IsValidSpell(botSpell.SpellId)) { + return false; } - return casted_spell; -} - -bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid) { - bool casted_spell = false; - if (tar->DontHealMeBefore() < Timer::GetCurrentTime()) { - auto hpr = (uint8)tar->GetHPRatio(); - bool hasAggro = false; - bool isPrimaryHealer = false; - - if (HasGroup() || IsRaidGrouped()) { - isPrimaryHealer = IsGroupHealer(); - } - - if (hpr < 95 || tar->IsClient() || botClass == Class::Bard) { - if (tar->GetClass() == Class::Necromancer && hpr >= 40) { - return false; - } - - if (tar->GetClass() == Class::Shaman && hpr >= 80) { - return false; - } - - // Evaluate the situation - if ((IsEngaged()) && ((botClass == Class::Cleric) || (botClass == Class::Druid) || (botClass == Class::Shaman) || (botClass == Class::Paladin))) { - if (tar->GetTarget() && tar->GetTarget()->GetHateTop() && tar->GetTarget()->GetHateTop() == tar) { - hasAggro = true; - } - - if (hpr < 35) { - botSpell = GetBestBotSpellForFastHeal(this); - } - else if (hpr < 70) { - if (GetNumberNeedingHealedInGroup(60, false, raid) >= 3) { - botSpell = GetBestBotSpellForGroupHeal(this); - } - - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - } - else if (hpr < 95) { - if (GetNumberNeedingHealedInGroup(80, false, raid) >= 3) { - botSpell = GetBestBotSpellForGroupHealOverTime(this); - } - - if (hasAggro) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - } - else { - if (!tar->FindType(SE_HealOverTime)) { - botSpell = GetBestBotSpellForHealOverTime(this); - } - } - } - else if ((botClass == Class::Cleric) || (botClass == Class::Druid) || (botClass == Class::Shaman) || (botClass == Class::Paladin)) { - if (GetNumberNeedingHealedInGroup(40, true, raid) >= 2) { - botSpell = GetBestBotSpellForGroupCompleteHeal(this); - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForGroupHeal(this); - } + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (IsGroupSpell(botSpell.SpellId)) { + BotGroupSay( + this, + fmt::format( + "Healing the group with {} [{}].", + GetSpellName(botSpell.SpellId), + GetSpellTypeNameByID(spellType) + ).c_str() + ); - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForGroupHealOverTime(this); - } + if (botClass != Class::Bard) { + const std::vector v = GatherGroupSpellTargets(tar); - if (hpr < 40 && !IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForPercentageHeal(this); + if (!IsCommandedSpell()) { + for (Mob* m : v) { + SetBotSpellRecastTimer(spellType, m, true); + } } } - else if (GetNumberNeedingHealedInGroup(60, true, raid) >= 2) { - botSpell = GetBestBotSpellForGroupHeal(this); - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForGroupHealOverTime(this); - } - - if (hpr < 40 && !IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - } - else if (hpr < 40) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - else if (hpr < 75) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); - } - else { - if (hpr < 90 && !tar->FindType(SE_HealOverTime)) { - botSpell = GetBestBotSpellForHealOverTime(this); - } - } } else { - float hpRatioToCast = 0.0f; - - switch (GetBotStance()) { - case Stance::Efficient: - case Stance::Aggressive: - hpRatioToCast = isPrimaryHealer ? 90.0f : 50.0f; - break; - case Stance::Balanced: - hpRatioToCast = isPrimaryHealer ? 95.0f : 75.0f; - break; - case Stance::Reactive: - hpRatioToCast = isPrimaryHealer ? 100.0f : 90.0f; - break; - case Stance::Burn: - case Stance::AEBurn: - hpRatioToCast = isPrimaryHealer ? 75.0f : 25.0f; - break; - default: - hpRatioToCast = isPrimaryHealer ? 100.0f : 0.0f; - break; - } - - //If we're at specified mana % or below, don't heal as hybrid - if (tar->GetHPRatio() <= hpRatioToCast) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); - } - } - - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); - } - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetFirstBotSpellForSingleTargetHeal(this); - } - if (botSpell.SpellId == 0 && botClass == Class::Bard) { - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); - } - - if (!IsValidSpell(botSpell.SpellId)) { - return false; - } - // Can we cast this spell on this target? - if (!(spells[botSpell.SpellId].target_type==ST_GroupTeleport || spells[botSpell.SpellId].target_type == ST_Target || tar == this) - && tar->CanBuffStack(botSpell.SpellId, botLevel, true) < 0) { - return false; - } - - uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore(); - - if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == Class::Bard) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); - } - - if (casted_spell && botClass != Class::Bard) { - if (IsGroupSpell(botSpell.SpellId)) { - if (HasGroup()) { - Group *group = GetGroup(); - if (group) { - BotGroupSay( - this, - fmt::format( - "Casting {}.", - spells[botSpell.SpellId].name - ).c_str() - ); - - for (const auto& member : group->members) { - if (member && !member->qglobal) { - member->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000); - } - } - } else if (IsRaidGrouped()) { - uint32 r_group = raid->GetGroup(GetName()); - const auto msg = fmt::format("Casting {}.", spells[botSpell.SpellId].name); - raid->RaidGroupSay(msg.c_str(), GetCleanName(), Language::CommonTongue, Language::MaxValue); - std::vector raid_group_members = raid->GetRaidGroupMembers(r_group); - for (const auto& rgm : raid_group_members) { - if (rgm.member && !rgm.member->qglobal) { - rgm.member->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000); - } - } - } - } else { - if (tar != this) { //we don't need spam of bots healing themselves - BotGroupSay( - this, - fmt::format( - "Casting {} on {}.", - spells[botSpell.SpellId].name, - tar->GetCleanName() - ).c_str() - ); - } - } - } - tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 2000); - } - } - } - return casted_spell; -} - -bool Bot::BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid) { - bool casted_spell = false; - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return false; - } - - //TODO - //Check if single target or AoE mez is best - //if (TARGETS ON MT IS => 3 THEN botSpell = AoEMez) - //if (TARGETS ON MT IS <= 2 THEN botSpell = BestMez) - - botSpell = GetBestBotSpellForMez(this); - - if (!IsValidSpell(botSpell.SpellId)) { - return false; - } - - Mob* addMob = GetFirstIncomingMobToMez(this, botSpell); - - if (!addMob) { - return false; - } - - if (!(!addMob->IsImmuneToSpell(botSpell.SpellId, this) && addMob->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) { - return false; - } + BotGroupSay( + this, + fmt::format( + "Healing {} with {} [{}].", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(botSpell.SpellId), + GetSpellTypeNameByID(spellType) + ).c_str() + ); - if (IsValidSpellRange(botSpell.SpellId, addMob)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost); - } - if (casted_spell) { - if (raid) { - raid->RaidSay( - GetCleanName(), - fmt::format( - "Attempting to mesmerize {} with {}.", - addMob->GetCleanName(), - spells[botSpell.SpellId].name - ).c_str(), - 0, - 100 - ); - } else { - BotGroupSay( - this, - fmt::format( - "Attempting to mesmerize {} with {}.", - addMob->GetCleanName(), - spells[botSpell.SpellId].name - ).c_str() - ); + if (botClass != Class::Bard) { + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); + } + } + } } + + return true; } - return casted_spell; + + return false; } bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { @@ -1260,14 +562,14 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain // Allow bots to cast buff spells even if they are out of mana if ( RuleB(Bots, FinishBuffing) && - manaCost > hasMana && AIBot_spells[i].type & SpellType_Buff + manaCost > hasMana && AIBot_spells[i].type == BotSpellTypes::Buff ) { SetMana(manaCost); } float dist2 = 0; - if (AIBot_spells[i].type & SpellType_Escape) { + if (AIBot_spells[i].type == BotSpellTypes::Escape) { dist2 = 0; } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); @@ -1276,7 +578,7 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain ( ( ( - (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == SpellType_Heal) || + (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || spells[AIBot_spells[i].spellid].target_type ==ST_Group || spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || @@ -1305,32 +607,49 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain if (!result) { SetMana(hasMana); } - else { - if (CalcSpellRecastTimer(AIBot_spells[i].spellid) > 0) { - SetSpellRecastTimer(AIBot_spells[i].spellid); - } - } return result; } bool Bot::AI_PursueCastCheck() { + if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + return false; + } + bool result = false; - if (AIautocastspell_timer->Check(false)) { + if (GetTarget() && AIautocastspell_timer->Check(false)) { + + LogAIDetail("Bot Pursue autocast check triggered: [{}]", GetCleanName()); + LogBotPreChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); //deleteme AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - LogAIDetail("Bot Engaged (pursuing) autocast check triggered. Trying to cast offensive spells"); + if (!IsAttackAllowed(GetTarget())) { + return false; + } + + auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Pursue); + + for (auto& currentCast : castOrder) { + if (currentCast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + continue; + } + + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + continue; + } + + result = AttemptAICastSpell(currentCast.spellType); - if (!AICastSpell(GetTarget(), 100, SpellType_Snare)) { - if (!AICastSpell(GetTarget(), 100, SpellType_Lifetap) && !AICastSpell(GetTarget(), 100, SpellType_Nuke)) { + if (result) { + break; } - result = true; } if (!AIautocastspell_timer->Enabled()) { - AIautocastspell_timer->Start(RandomTimer(100, 250), false); + AIautocastspell_timer->Start(RandomTimer(RuleI(Bots, MinDelayBetweenInCombatCastAttempts), RuleI(Bots, MaxDelayBetweenInCombatCastAttempts)), false); } } @@ -1338,453 +657,104 @@ bool Bot::AI_PursueCastCheck() { } bool Bot::AI_IdleCastCheck() { + if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + return false; + } + bool result = false; if (AIautocastspell_timer->Check(false)) { + LogAIDetail("Bot Non-Engaged autocast check triggered: [{}]", GetCleanName()); + LogBotPreChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); //deleteme + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - bool pre_combat = false; + bool preCombat = false; Client* test_against = nullptr; if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient()) { test_against = GetGroup()->GetLeader()->CastToClient(); - } else if (GetOwner() && GetOwner()->IsClient()) { + } + else if (GetOwner() && GetOwner()->IsClient()) { test_against = GetOwner()->CastToClient(); } if (test_against) { - pre_combat = test_against->GetBotPrecombat(); - } - - //Ok, IdleCastCheck depends of class. - switch (GetClass()) { - // Healers WITHOUT pets will check if a heal is needed before buffing. - case Class::Cleric: - case Class::Paladin: - case Class::Ranger: { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - - result = true; - break; + preCombat = test_against->GetBotPrecombat(); } - case Class::Monk: - case Class::Rogue: - case Class::Warrior: - case Class::Berserker: { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - result = true; - break; - } - // Pets class will first cast their pet, then buffs - - case Class::Magician: - case Class::ShadowKnight: - case Class::Necromancer: - case Class::Enchanter: { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(GetPet(), 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(GetPet(), 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - } + auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Idle); - result = true; - break; - } - case Class::Druid: - case Class::Shaman: - case Class::Beastlord: { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(GetPet(), 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - } + for (auto& currentCast : castOrder) { + if (currentCast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + continue; } - result = true; - break; - } - case Class::Wizard: { // This can eventually be move into the Class::Beastlord case handler once pre-combat is fully implemented - if (pre_combat) { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_PreCombatBuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - } - } - else { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } + if (!preCombat && (currentCast.spellType == BotSpellTypes::PreCombatBuff || currentCast.spellType == BotSpellTypes::PreCombatBuffSong)) { + continue; } - result = true; - break; - } - case Class::Bard: { - if (pre_combat) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(this, 100, SpellType_PreCombatBuffSong)) { - if (!AICastSpell(this, 100, SpellType_InCombatBuffSong)) { - } - } - } - } - } - else { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(this, 100, SpellType_OutOfCombatBuffSong)) { - if (!AICastSpell(this, 100, SpellType_InCombatBuffSong)) { - } - } - } - } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + continue; } - result = true; - break; - } - default: - break; + result = AttemptAICastSpell(currentCast.spellType); + + if (result) { + break; + } } - if (!AIautocastspell_timer->Enabled()) - AIautocastspell_timer->Start(RandomTimer(500, 2000), false); // avg human response is much less than 5 seconds..even for non-combat situations... + if (!AIautocastspell_timer->Enabled()) { + AIautocastspell_timer->Start(RandomTimer(RuleI(Bots, MinDelayBetweenOutCombatCastAttempts), RuleI(Bots, MaxDelayBetweenOutCombatCastAttempts)), false); + } } return result; } bool Bot::AI_EngagedCastCheck() { + if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + return false; + } + bool result = false; bool failedToCast = false; if (GetTarget() && AIautocastspell_timer->Check(false)) { + LogAIDetail("Bot Engaged autocast check triggered: [{}]", GetCleanName()); + LogBotPreChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); //deleteme + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - uint8 botClass = GetClass(); - bool mayGetAggro = HasOrMayGetAggro(); - - LogAIDetail("Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells"); - - if (botClass == Class::Cleric) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - //AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die - failedToCast = true; - } - } - } - } - } - } - } - else if (botClass == Class::Druid) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - //AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die - failedToCast = true; - } - } - } - } - } - } - } - } - else if (botClass == Class::Shaman) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - failedToCast = true; - } - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Ranger) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - failedToCast = true; - } - } - } - } - } - } - } - else if (botClass == Class::Beastlord) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - failedToCast = true; - } - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Wizard) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - else if (botClass == Class::Paladin) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - else if (botClass == Class::ShadowKnight) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - else if (botClass == Class::Magician) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - else if (botClass == Class::Necromancer) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Enchanter) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Mez), SpellType_Mez)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - } - } - else if (botClass == Class::Bard) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {// Bards will use their escape songs - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_HateRedux), BotAISpellRange, SpellType_HateRedux)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro ? 0 : GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {// Bards will use their dot songs - if (!AICastSpell(GetTarget(), mayGetAggro ? 0 : GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {// Bards will use their nuke songs - failedToCast = true; - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Berserker) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - } + if (!IsAttackAllowed(GetTarget())) { + return false; } - else if (botClass == Class::Monk) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } + + auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Engaged); + + for (auto& currentCast : castOrder) { + if (currentCast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + continue; } - } - else if (botClass == Class::Rogue) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } + + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + continue; } - } - else if (botClass == Class::Warrior) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } + + result = AttemptAICastSpell(currentCast.spellType); + + if (result) { + break; } } if (!AIautocastspell_timer->Enabled()) { - AIautocastspell_timer->Start(RandomTimer(150, 300), false); - } - - if (!failedToCast) { - result = true; + AIautocastspell_timer->Start(RandomTimer(RuleI(Bots, MinDelayBetweenInCombatCastAttempts), RuleI(Bots, MaxDelayBetweenInCombatCastAttempts)), false); } } @@ -1819,22 +789,22 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { botSpell.ManaCost = 0; if (useFastHeals) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this, tar); if (!IsValidSpell(botSpell.SpellId)) - botSpell = GetBestBotSpellForFastHeal(this); + botSpell = GetBestBotSpellForFastHeal(this, tar); } else { - botSpell = GetBestBotSpellForPercentageHeal(this); + botSpell = GetBestBotSpellForPercentageHeal(this, tar); if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this, tar); } if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetFirstBotSpellForSingleTargetHeal(this); + botSpell = GetFirstBotSpellForSingleTargetHeal(this, tar); } if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); + botSpell = GetFirstBotSpellBySpellType(this, BotSpellTypes::RegularHeal); } } @@ -1879,7 +849,7 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { return castedSpell; } -std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect) { +std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect) { std::list result; if (!botCaster) { @@ -1894,13 +864,16 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEff std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect)) { + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && + (IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect)) + ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; botSpell.SpellIndex = i; @@ -1914,7 +887,7 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEff return result; } -std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType) { +std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType) { std::list result; if (!botCaster) { @@ -1929,18 +902,19 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && ( IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect) ) && - spells[botSpellList[i].spellid].target_type == targetType + (targetType == ST_TargetOptional || spells[botSpellList[i].spellid].target_type == targetType) ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; @@ -1954,7 +928,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, return result; } -std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType) { +std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType) { std::list result; if (!botCaster) { @@ -1969,13 +943,15 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellTyp std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (botSpellList[i].type & spellType) { + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; botSpell.SpellIndex = i; @@ -1989,27 +965,59 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellTyp return result; } -std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType) { +std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE) { std::list result; if (botCaster && botCaster->AI_HasSpells()) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (botSpellList[i].type & spellType) { - BotSpell_wPriority botSpell; - botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = i; - botSpell.ManaCost = botSpellList[i].manacost; - botSpell.Priority = botSpellList[i].priority; + if (spellType == BotSpellTypes::HateRedux && botCaster->GetClass() == Class::Bard) { + if (spells[botSpellList[i].spellid].target_type != ST_Target) { + continue; + } + } - result.push_back(botSpell); + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + ) { + if (!AE && IsAnyAESpell(botSpellList[i].spellid) && !IsGroupSpell(botSpellList[i].spellid)) { + continue; + } + else if (AE && !IsAnyAESpell(botSpellList[i].spellid)) { + continue; + } + + if ( + ( + !botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez)) + ) + && (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsGroupBotSpellType(spellType))) // TODO bot rewrite - needed for ae spells? + ) { + continue; + } + + if ( + botCaster->IsCommandedSpell() || + !AE || + (spellType == BotSpellTypes::GroupCures) || + (spellType == BotSpellTypes::AEMez) || + (AE && botCaster->HasValidAETarget(botCaster, botSpellList[i].spellid, spellType, tar)) + ) { + BotSpell_wPriority botSpell; + botSpell.SpellId = botSpellList[i].spellid; + botSpell.SpellIndex = i; + botSpell.ManaCost = botSpellList[i].manacost; + botSpell.Priority = botSpellList[i].priority; + + result.push_back(botSpell); + } } } @@ -2025,7 +1033,7 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa return result; } -BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { +BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2036,16 +1044,44 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if ((botSpellList[i].type & spellType) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { - result.SpellId = botSpellList[i].spellid; - result.SpellIndex = i; - result.ManaCost = botSpellList[i].manacost; + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + ) { + result.SpellId = botSpellList[i].spellid; + result.SpellIndex = i; + result.ManaCost = botSpellList[i].manacost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + + for (auto botSpellListItr : botSpellList) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsVeryFastHealSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { + result.SpellId = botSpellListItr.SpellId; + result.SpellIndex = botSpellListItr.SpellIndex; + result.ManaCost = botSpellListItr.ManaCost; break; } @@ -2055,7 +1091,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { return result; } -BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) { +BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2063,11 +1099,11 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); for (auto botSpellListItr : botSpellList) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsFastHealSpell(botSpellListItr.SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId)) { + if (IsFastHealSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { result.SpellId = botSpellListItr.SpellId; result.SpellIndex = botSpellListItr.SpellIndex; result.ManaCost = botSpellListItr.ManaCost; @@ -2080,7 +1116,7 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2088,29 +1124,14 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime); - std::vector botSpellList = botCaster->AIBot_spells; + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); - for (auto botSpellListItr : botHoTSpellList) { + for (auto botSpellListItr : botSpellList) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsHealOverTimeSpell(botSpellListItr.SpellId)) { - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if ( - botSpellList[i].spellid == botSpellListItr.SpellId && - (botSpellList[i].type & SpellType_Heal) && - botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId) - ) { - result.SpellId = botSpellListItr.SpellId; - result.SpellIndex = botSpellListItr.SpellIndex; - result.ManaCost = botSpellListItr.ManaCost; - } - } + if (IsHealOverTimeSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { + result.SpellId = botSpellListItr.SpellId; + result.SpellIndex = botSpellListItr.SpellIndex; + result.ManaCost = botSpellListItr.ManaCost; break; } @@ -2120,7 +1141,7 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) { +BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2129,18 +1150,21 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) { if (botCaster && botCaster->AI_HasSpells()) { std::vector botSpellList = botCaster->AIBot_spells; - for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here continue; } - if (IsCompleteHealSpell(botSpellList[i].spellid) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { + if ( + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && + IsCompleteHealSpell(botSpellList[i].spellid) && + botCaster->CastChecks(botSpellList[i].spellid, tar, spellType) + ) { result.SpellId = botSpellList[i].spellid; result.SpellIndex = i; result.ManaCost = botSpellList[i].manacost; + break; } } @@ -2149,7 +1173,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2157,14 +1181,15 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { + if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType)) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2173,7 +1198,7 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) { +BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2181,20 +1206,15 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ( - ( - IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) || - IsFastHealSpell(botSpellListItr->SpellId) - ) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { + if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType)) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2203,7 +1223,7 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2211,17 +1231,31 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + const std::vector v = botCaster->GatherSpellTargets(); + int targetCount = 0; for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsRegularGroupHealSpell(botSpellListItr->SpellId) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { + if (IsRegularGroupHealSpell(botSpellListItr->SpellId)) { + if (!botCaster->IsCommandedSpell()) { + targetCount = 0; + + for (Mob* m : v) { + if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + ++targetCount; + } + } + + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; + } + } + result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2230,7 +1264,7 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2238,31 +1272,31 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime); - std::vector botSpellList = botCaster->AIBot_spells; + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); + const std::vector v = botCaster->GatherSpellTargets(); + int targetCount = 0; - for (std::list::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); ++botSpellListItr) { + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order if (IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) { + if (!botCaster->IsCommandedSpell()) { + targetCount = 0; - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; + for (Mob* m : v) { + if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + ++targetCount; + } } - if ( - botSpellList[i].spellid == botSpellListItr->SpellId && - (botSpellList[i].type & SpellType_Heal) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; } } + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2271,7 +1305,7 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2279,17 +1313,31 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CompleteHeal); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); + const std::vector v = botCaster->GatherSpellTargets(); + int targetCount = 0; for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsGroupCompleteHealSpell(botSpellListItr->SpellId) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { + if (IsGroupCompleteHealSpell(botSpellListItr->SpellId)) { + if (!botCaster->IsCommandedSpell()) { + targetCount = 0; + + for (Mob* m : v) { + if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + ++targetCount; + } + } + + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; + } + } + result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2298,7 +1346,7 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2306,7 +1354,7 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_Mez); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Mez); for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order @@ -2326,102 +1374,101 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) { - BotSpell result; +Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE) { + Mob* result = nullptr; - result.SpellId = 0; - result.SpellIndex = 0; - result.ManaCost = 0; + if (botCaster && botCaster->GetOwner()) { + int spellRange = (!AE ? botCaster->GetActSpellRange(spellid, spells[spellid].range) : botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range)); + int buff_count = 0; + NPC* npc = nullptr; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed); + for (auto& close_mob : botCaster->m_close_mobs) { + buff_count = 0; + npc = close_mob.second->CastToNPC(); - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsSlowSpell(botSpellListItr->SpellId) && - spells[botSpellListItr->SpellId].resist_type == RESIST_MAGIC && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; - break; + if (!npc) { + continue; } - } - } - return result; -} + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), npc, spellid)) { + continue; + } -BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) { - BotSpell result; + if (AE) { + int targetCount = 0; - result.SpellId = 0; - result.SpellIndex = 0; - result.ManaCost = 0; + for (auto& close_mob : botCaster->m_close_mobs) { + Mob* m = close_mob.second; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed); + if (npc == m) { + continue; + } - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsSlowSpell(botSpellListItr->SpellId) && - spells[botSpellListItr->SpellId].resist_type == RESIST_DISEASE && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), m, spellid)) { + continue; + } - break; - } - } - } + if (IsPBAESpell(spellid)) { + if (spellRange < Distance(botCaster->GetPosition(), m->GetPosition())) { + continue; + } + } + else { + if (spellRange < Distance(m->GetPosition(), npc->GetPosition())) { + continue; + } + } - return result; -} + if (botCaster->CastChecks(spellid, m, spellType, true, true)) { + ++targetCount; + } + + if (targetCount >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + break; + } + } -Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell) { - Mob* result = 0; + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; + } - if (botCaster && IsMesmerizeSpell(botSpell.SpellId)) { + if (zone->random.Int(1, 100) < RuleI(Bots, AEMezChance)) { + botCaster->SetSpellTypeRecastTimer(spellType, RuleI(Bots, MezFailDelay)); + return result; + } - std::list npc_list; - entity_list.GetNPCList(npc_list); + result = npc; + } + else { + if (spellRange < Distance(botCaster->GetPosition(), npc->GetPosition())) { + continue; + } - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* npc = *itr; + if (!botCaster->CastChecks(spellid, npc, spellType, true)) { + continue; + } - if (DistanceSquaredNoZ(npc->GetPosition(), botCaster->GetPosition()) <= botCaster->GetActSpellRange(botSpell.SpellId, spells[botSpell.SpellId].range)) { - if (!npc->IsMezzed()) { - if (botCaster->HasGroup()) { - Group* g = botCaster->GetGroup(); + if (zone->random.Int(1, 100) < RuleI(Bots, MezChance)) { + botCaster->SetSpellTypeRecastTimer(spellType, RuleI(Bots, MezAEFailDelay)); - if (g) { - for (int counter = 0; counter < g->GroupCount(); counter++) { - if ( - npc->IsOnHatelist(g->members[counter]) && - g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) { - result = npc; - break; - } - } - } - } + return result; } + + result = npc; } - if (result) - break; + if (result) { + botCaster->SetHasLoS(true); + + return result; + } } } return result; } -BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) { +BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2429,20 +1476,21 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_SummonPet); - + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_SummonPet); std::string petType = GetBotMagicianPetType(botCaster); for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsSummonPetSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { - if (!strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length())) { + if ( + IsSummonPetSpell(botSpellListItr->SpellId) && + botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) && + !strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length()) + ) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; break; - } } } } @@ -2454,76 +1502,101 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { std::string result; if (botCaster) { - if (botCaster->IsPetChooser()) { - switch(botCaster->GetPetChooserID()) { - case 0: + bool epicAllowed = false; + if (RuleB(Bots, AllowMagicianEpicPet)) { + if (botCaster->GetLevel() >= RuleI(Bots, AllowMagicianEpicPetLevel)) { + if (!RuleI(Bots, RequiredMagicianEpicPetItemID)) { + epicAllowed = true; + } + else { + bool has_item = botCaster->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX; + + if (has_item) { + epicAllowed = true; + } + } + } + } + + if (botCaster->GetPetChooserID() > 0) { + switch (botCaster->GetPetChooserID()) { + case SumWater: result = std::string("SumWater"); break; - case 1: + case SumFire: result = std::string("SumFire"); break; - case 2: + case SumAir: result = std::string("SumAir"); break; - case 3: + case SumEarth: result = std::string("SumEarth"); break; - default: + case MonsterSum: result = std::string("MonsterSum"); break; + case SumMageMultiElement: + if (epicAllowed) { + result = std::string(RuleS(Bots, EpicPetSpellName)); + + if (result.empty()) { + result = "SumMageMultiElement"; + } + } + else { + result = std::string("MonsterSum"); + } + break; } } else { - if (botCaster->GetLevel() == 2) - result = std::string("SumWater"); - else if (botCaster->GetLevel() == 3) - result = std::string("SumFire"); - else if (botCaster->GetLevel() == 4) - result = std::string("SumAir"); - else if (botCaster->GetLevel() == 5) - result = std::string("SumEarth"); - else if (botCaster->GetLevel() < 30) { - // Under level 30 - int counter = zone->random.Int(0, 3); - - switch(counter) { - case 0: - result = std::string("SumWater"); - break; - case 1: - result = std::string("SumFire"); - break; - case 2: - result = std::string("SumAir"); - break; - case 3: - result = std::string("SumEarth"); - break; - default: - result = std::string("MonsterSum"); - break; + if (epicAllowed) { + result = std::string(RuleS(Bots, EpicPetSpellName)); + + if (result.empty()) { + result = "SumMageMultiElement"; } } else { - // Over level 30 - int counter = zone->random.Int(0, 4); - - switch(counter) { - case 0: + if (botCaster->GetLevel() == 2) { result = std::string("SumWater"); - break; - case 1: + } + else if (botCaster->GetLevel() == 3) { result = std::string("SumFire"); - break; - case 2: + } + else if (botCaster->GetLevel() == 4) { result = std::string("SumAir"); - break; - case 3: + } + else if (botCaster->GetLevel() == 5) { result = std::string("SumEarth"); - break; - default: - result = std::string("MonsterSum"); - break; + } + else { + int counter; + + if (botCaster->GetLevel() < 30) { + counter = zone->random.Int(1, 4); + } + else { + counter = zone->random.Int(1, 5); + } + + switch (counter) { + case 1: + result = std::string("SumWater"); + break; + case 2: + result = std::string("SumFire"); + break; + case 3: + result = std::string("SumAir"); + break; + case 4: + result = std::string("SumEarth"); + break; + default: + result = std::string("MonsterSum"); + break; + } } } } @@ -2532,24 +1605,46 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType) { +BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE, Mob* tar) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; + if (tar == nullptr) { + tar = botCaster->GetTarget(); + } + if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, targetType); + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_CurrentHP, targetType); for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ((IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) { + if (!AE && IsAnyAESpell(botSpellListItr->SpellId) && !IsGroupSpell(botSpellListItr->SpellId)) { + continue; + } + else if (AE && !IsAnyAESpell(botSpellListItr->SpellId)) { + continue; + } - break; + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + continue; + } + + + if ( + botCaster->IsCommandedSpell() || + !AE || + (AE && botCaster->HasValidAETarget(botCaster, botSpellListItr->SpellId, spellType, tar)) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } } } } @@ -2557,7 +1652,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType return result; } -BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType) +BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE, Mob* tar) { BotSpell result; @@ -2565,19 +1660,40 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType result.SpellIndex = 0; result.ManaCost = 0; + if (tar == nullptr) { + tar = botCaster->GetTarget(); + } + if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType); + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_Stun, targetType); for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsStunSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) - { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; - break; + if (IsStunSpell(botSpellListItr->SpellId)) { + if (!AE && IsAnyAESpell(botSpellListItr->SpellId) && !IsGroupSpell(botSpellListItr->SpellId)) { + continue; + } + else if (AE && !IsAnyAESpell(botSpellListItr->SpellId)) { + continue; + } + + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + continue; + } + + if ( + botCaster->IsCommandedSpell() || + !AE || + (AE && botCaster->HasValidAETarget(botCaster, botSpellListItr->SpellId, spellType, tar)) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } } } } @@ -2585,7 +1701,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType return result; } -BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target) { +BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2593,50 +1709,74 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ result.ManaCost = 0; if (botCaster && target) { + const int lureResisValue = -100; - const int maxTargetResistValue = 300; + + int32 level_mod = (target->GetLevel() - botCaster->GetLevel()) * (target->GetLevel() - botCaster->GetLevel()) / 2; + + if (target->GetLevel() - botCaster->GetLevel() < 0) { + level_mod = -level_mod; + } + const int maxTargetResistValue = botCaster->GetSpellTypeResistLimit(spellType); bool selectLureNuke = false; - if ((target->GetMR() > maxTargetResistValue) && (target->GetCR() > maxTargetResistValue) && (target->GetFR() > maxTargetResistValue)) + if (((target->GetMR() + level_mod) > maxTargetResistValue) && ((target->GetCR() + level_mod) > maxTargetResistValue) && ((target->GetFR() + level_mod) > maxTargetResistValue)) { selectLureNuke = true; + } - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, ST_Target); + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_CurrentHP, ST_Target); BotSpell firstWizardMagicNukeSpellFound; firstWizardMagicNukeSpellFound.SpellId = 0; firstWizardMagicNukeSpellFound.SpellIndex = 0; firstWizardMagicNukeSpellFound.ManaCost = 0; + bool spellSelected = false; - for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - bool spellSelected = false; + if (!botCaster->IsValidSpellRange(botSpellListItr->SpellId, target)) { + continue; + } - if (botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { - if (selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) { + if (selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) { + if (botCaster->CastChecks(botSpellListItr->SpellId, target, spellType)) { spellSelected = true; } - else if (IsPureNukeSpell(botSpellListItr->SpellId)) { - if (((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) - && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) - { - spellSelected = true; - } - else if (((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD) - && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) - { - spellSelected = true; - } - else if (((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE) - && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) - { - spellSelected = true; - } - else if ((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) { - firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId; - firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex; - firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost; - } + } + else if (!selectLureNuke && IsPureNukeSpell(botSpellListItr->SpellId)) { + if ( + ((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + spellSelected = true; + } + else if ( + ((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + spellSelected = true; + } + else if ( + ((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + spellSelected = true; + } + else if ( + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId; + firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex; + firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost; } } @@ -2645,7 +1785,26 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; - break; + break; + } + } + + if (!spellSelected) { + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + // Assuming all the spells have been loaded into this list by level and in descending order + + if (botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { + if (botCaster->CastChecks(botSpellListItr->SpellId, target, spellType)) { + spellSelected = true; + } + } + if (spellSelected) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } } } @@ -2671,13 +1830,11 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (((botSpellList[i].type & SpellType_Debuff) || IsDebuffSpell(botSpellList[i].spellid)) + if (((botSpellList[i].type == BotSpellTypes::Debuff) || IsDebuffSpell(botSpellList[i].spellid)) && (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster) && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { @@ -2719,13 +1876,11 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (((botSpellList[i].type & SpellType_Debuff) || IsResistDebuffSpell(botSpellList[i].spellid)) + if (((botSpellList[i].type == BotSpellTypes::Debuff) || IsResistDebuffSpell(botSpellList[i].spellid)) && ((needsMagicResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistMagic)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) || (needsColdResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistCold)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) || (needsFireResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistFire)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) @@ -2746,109 +1901,82 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { return result; } -BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { +BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell_wPriority result; - bool spellSelected = false; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (!tar) + if (!tar) { return result; + } - int countNeedsCured = 0; - bool isPoisoned = tar->FindType(SE_PoisonCounter); - bool isDiseased = tar->FindType(SE_DiseaseCounter); - bool isCursed = tar->FindType(SE_CurseCounter); - bool isCorrupted = tar->FindType(SE_CorruptionCounter); - - if (botCaster && botCaster->AI_HasSpells()) { - std::list cureList = GetPrioritizedBotSpellsBySpellType(botCaster, SpellType_Cure); - - if (tar->HasGroup()) { - Group *g = tar->GetGroup(); - - if (g) { - for( int i = 0; imembers[i] && !g->members[i]->qglobal) { - if (botCaster->GetNeedsCured(g->members[i])) - countNeedsCured++; - } + if (botCaster) { + std::list botSpellListItr = GetPrioritizedBotSpellsBySpellType(botCaster, spellType, tar); + + if (IsGroupBotSpellType(spellType)) { + const std::vector v = botCaster->GatherGroupSpellTargets(tar); + int countNeedsCured = 0; + uint16 countPoisoned = 0; + uint16 countDiseased = 0; + uint16 countCursed = 0; + uint16 countCorrupted = 0; + + for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + if (!IsValidSpell(itr->SpellId) || !IsGroupSpell(itr->SpellId)) { + continue; } - } - } - //Check for group cure first - if (countNeedsCured > 2) { - for (std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { - BotSpell selectedBotSpell = *itr; - - if (IsGroupSpell(itr->SpellId) && botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) { - if (selectedBotSpell.SpellId == 0) - continue; - - if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) { - spellSelected = true; - } - else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) { - spellSelected = true; - } - else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) { - spellSelected = true; - } - else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) { - spellSelected = true; - } - else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) { - spellSelected = true; + for (Mob* m : v) { + if (botCaster->IsCommandedSpell() || botCaster->GetNeedsCured(m)) { + if (botCaster->CastChecks(itr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + if (m->FindType(SE_PoisonCounter)) { + ++countPoisoned; + } + if (m->FindType(SE_DiseaseCounter)) { + ++countDiseased; + } + if (m->FindType(SE_CurseCounter)) { + ++countCursed; + } + if (m->FindType(SE_CorruptionCounter)) { + ++countCorrupted; + } + } } + } - if (spellSelected) - { - result.SpellId = selectedBotSpell.SpellId; - result.SpellIndex = selectedBotSpell.SpellIndex; - result.ManaCost = selectedBotSpell.ManaCost; + if ( + (countPoisoned >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) || + (countDiseased >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) || + (countCursed >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) || + (countCorrupted >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) + ) { + result.SpellId = itr->SpellId; + result.SpellIndex = itr->SpellIndex; + result.ManaCost = itr->ManaCost; - break; - } + break; } } } + else { + for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + if (!IsValidSpell(itr->SpellId) || IsGroupSpell(itr->SpellId)) { + continue; + } - //no group cure for target- try to find single target spell - if (!spellSelected) { - for(std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { - BotSpell selectedBotSpell = *itr; - - if (botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) { - if (selectedBotSpell.SpellId == 0) - continue; - - if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) { - spellSelected = true; - } - else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) { - spellSelected = true; - } - else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) { - spellSelected = true; - } - else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) { - spellSelected = true; - } - else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) { - spellSelected = true; - } - - if (spellSelected) - { - result.SpellId = selectedBotSpell.SpellId; - result.SpellIndex = selectedBotSpell.SpellIndex; - result.ManaCost = selectedBotSpell.ManaCost; - - break; - } + if ( + tar->FindType(SE_PoisonCounter) && IsEffectInSpell(itr->SpellId, SE_PoisonCounter) || + tar->FindType(SE_DiseaseCounter) && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter) || + tar->FindType(SE_CurseCounter) && IsEffectInSpell(itr->SpellId, SE_CurseCounter) || + tar->FindType(SE_CorruptionCounter) && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter) + ) { + result.SpellId = itr->SpellId; + result.SpellIndex = itr->SpellIndex; + result.ManaCost = itr->ManaCost; + break; } } } @@ -2857,108 +1985,69 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { return result; } -uint8 Bot::GetChanceToCastBySpellType(uint32 spellType) +uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adjust, move AEs to own rule? { - uint8 spell_type_index = SPELL_TYPE_COUNT; switch (spellType) { - case SpellType_Nuke: - spell_type_index = spellTypeIndexNuke; - break; - case SpellType_Heal: - spell_type_index = spellTypeIndexHeal; - break; - case SpellType_Root: - spell_type_index = spellTypeIndexRoot; - break; - case SpellType_Buff: - spell_type_index = spellTypeIndexBuff; - break; - case SpellType_Escape: - spell_type_index = spellTypeIndexEscape; - break; - case SpellType_Pet: - spell_type_index = spellTypeIndexPet; - break; - case SpellType_Lifetap: - spell_type_index = spellTypeIndexLifetap; - break; - case SpellType_Snare: - spell_type_index = spellTypeIndexSnare; - break; - case SpellType_DOT: - spell_type_index = spellTypeIndexDot; - break; - case SpellType_Dispel: - spell_type_index = spellTypeIndexDispel; - break; - case SpellType_InCombatBuff: - spell_type_index = spellTypeIndexInCombatBuff; - break; - case SpellType_Mez: - spell_type_index = spellTypeIndexMez; - break; - case SpellType_Charm: - spell_type_index = spellTypeIndexCharm; - break; - case SpellType_Slow: - spell_type_index = spellTypeIndexSlow; - break; - case SpellType_Debuff: - spell_type_index = spellTypeIndexDebuff; - break; - case SpellType_Cure: - spell_type_index = spellTypeIndexCure; - break; - case SpellType_Resurrect: - spell_type_index = spellTypeIndexResurrect; - break; - case SpellType_HateRedux: - spell_type_index = spellTypeIndexHateRedux; - break; - case SpellType_InCombatBuffSong: - spell_type_index = spellTypeIndexInCombatBuffSong; - break; - case SpellType_OutOfCombatBuffSong: - spell_type_index = spellTypeIndexOutOfCombatBuffSong; - break; - case SpellType_PreCombatBuff: - spell_type_index = spellTypeIndexPreCombatBuff; - break; - case SpellType_PreCombatBuffSong: - spell_type_index = spellTypeIndexPreCombatBuffSong; - break; - default: - break; - } - - if (spell_type_index >= SPELL_TYPE_COUNT) - return 0; - - uint8 class_index = GetClass(); - if (class_index > Class::Berserker || class_index < Class::Warrior) - return 0; - --class_index; - - uint32 stance_id = GetBotStance(); - if (!Stance::IsValid(stance_id)) { - return 0; - } - - uint8 stance_index = Stance::GetIndex(stance_id); - uint8 type_index = nHSND; - - if (HasGroup()) { - if (IsGroupHealer()/* || IsRaidHealer()*/) - type_index |= pH; - if (IsGroupSlower()/* || IsRaidSlower()*/) - type_index |= pS; - if (IsGroupNuker()/* || IsRaidNuker()*/) - type_index |= pN; - if (IsGroupDoter()/* || IsRaidDoter()*/) - type_index |= pD; - } - - return database.botdb.GetSpellCastingChance(spell_type_index, class_index, stance_index, type_index); + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEStun: + case BotSpellTypes::Nuke: + return RuleI(Bots, PercentChanceToCastNuke); + case BotSpellTypes::Root: + return RuleI(Bots, PercentChanceToCastRoot); + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::DamageShields: + return RuleI(Bots, PercentChanceToCastBuff); + case BotSpellTypes::Escape: + return RuleI(Bots, PercentChanceToCastEscape); + case BotSpellTypes::Lifetap: + return RuleI(Bots, PercentChanceToCastLifetap); + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + return RuleI(Bots, PercentChanceToCastSnare); + case BotSpellTypes::DOT: + return RuleI(Bots, PercentChanceToCastDOT); + case BotSpellTypes::Dispel: + return RuleI(Bots, PercentChanceToCastDispel); + case BotSpellTypes::InCombatBuff: + return RuleI(Bots, PercentChanceToCastInCombatBuff); + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return RuleI(Bots, PercentChanceToCastMez); + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + return RuleI(Bots, PercentChanceToCastSlow); + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + return RuleI(Bots, PercentChanceToCastDebuff); + case BotSpellTypes::Cure: + return RuleI(Bots, PercentChanceToCastCure); + case BotSpellTypes::HateRedux: + return RuleI(Bots, PercentChanceToCastHateRedux); + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + return RuleI(Bots, PercentChanceToCastFear); + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return RuleI(Bots, PercentChanceToCastHeal); + default: + return RuleI(Bots, PercentChanceToCastOtherType); + } + + return RuleI(Bots, PercentChanceToCastOtherType); } bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { @@ -3340,7 +2429,7 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id) 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) { + if (!BOT_SPELL_TYPES_INNATE(entry.type) && entry.priority == 0) { entry.priority = 1; } @@ -3504,6 +2593,460 @@ bool Bot::IsValidSpellRange(uint16 spell_id, Mob const* tar) { if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { return true; } + + spellrange = (GetActSpellRange(spell_id, spells[spell_id].aoe_range) * GetActSpellRange(spell_id, spells[spell_id].aoe_range)); + if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { + return true; + } } + return false; } + +BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE, Mob* tar) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (!botCaster || !bodyType) { + return result; + } + + if (tar == nullptr) { + tar = botCaster->GetTarget(); + } + + switch (bodyType) { + case BodyType::Undead: + case BodyType::SummonedUndead: + case BodyType::Vampire: + result = GetBestBotSpellForNukeByTargetType(botCaster, (!AE ? ST_Undead : ST_UndeadAE), spellType, AE, tar); + break; + case BodyType::Summoned: + case BodyType::Summoned2: + case BodyType::Summoned3: + result = GetBestBotSpellForNukeByTargetType(botCaster, (!AE ? ST_Summoned : ST_SummonedAE), spellType, AE, tar); + break; + case BodyType::Animal: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Animal, spellType, AE, tar); + break; + case BodyType::Plant: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Plant, spellType, AE, tar); + break; + case BodyType::Giant: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Giant, spellType, AE, tar); + break; + case BodyType::Dragon: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Dragon, spellType, AE, tar); + break; + default: + break; + } + + return result; +} + +void Bot::CheckBotSpells() { + bool valid = false; + uint16 correctType; + auto spellList = BotSpellsEntriesRepository::All(content_db); + uint16 spell_id; + + for (const auto& s : spellList) { + if (!IsValidSpell(s.spell_id)) { + LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); //deleteme + continue; + } + + spell_id = s.spell_id; + + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] >= 255) { + LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); //deleteme + } + else { + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.minlevel) { + LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}." + , GetSpellName(spell_id) + , spell_id + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + , s.minlevel + ); //deleteme + + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell_id + , s.npc_spells_id + , GetSpellName(spell_id) + , spell_id + , s.minlevel + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + ); //deleteme + } + + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] < s.minlevel) { + LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}." + , GetSpellName(spell_id) + , spell_id + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + , s.minlevel + ); //deleteme + + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell_id + , s.npc_spells_id + , GetSpellName(spell_id) + , spell_id + , s.minlevel + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + ); //deleteme + } + + + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.maxlevel) { + LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}." + , GetSpellName(spell_id) + , spell_id + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + , s.maxlevel + ); //deleteme + } + } + + correctType = UINT16_MAX; + valid = false; + + + switch (s.type) { + case BotSpellTypes::Nuke: //DONE + if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::RegularHeal: //DONE + //if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { + if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Root: //DONE + if (IsEffectInSpell(spell_id, SE_Root)) { + valid = true; + break; + } + break; + case BotSpellTypes::Buff: //DONE + if (IsAnyBuffSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Pet: //DONE + if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { + valid = true; + break; + } + break; + case BotSpellTypes::Lifetap: //DONE + if (IsLifetapSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Snare: //DONE + if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::DOT: //DONE + if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Dispel: //DONE + if (IsDispelSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::InCombatBuff: //DONE + if ( + IsSelfConversionSpell(spell_id) || + IsAnyBuffSpell(spell_id) || + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::Mez: //DONE + if (IsMesmerizeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Charm: //DONE + if (IsCharmSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Slow: //DONE + if (IsSlowSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Debuff: //DONE + if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Cure: //DONE + if (IsCureSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::PreCombatBuff: //DONE + if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + !IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::InCombatBuffSong: //DONE + case BotSpellTypes::OutOfCombatBuffSong: //DONE + case BotSpellTypes::PreCombatBuffSong: //DONE + if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::Fear: //DONE + if (IsFearSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Escape: + if (IsEscapeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::HateRedux: + if (IsHateReduxSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Resurrect: + if (IsEffectInSpell(spell_id, SE_Revive)) { + valid = true; + break; + } + break; + default: + break; + + } + + if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { + correctType = BotSpellTypes::Nuke; + } + //else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { + else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { + correctType = BotSpellTypes::RegularHeal; + } + else if (IsEffectInSpell(spell_id, SE_Root)) { + correctType = BotSpellTypes::Root; + } + else if (IsAnyBuffSpell(spell_id)) { + correctType = BotSpellTypes::Buff; + } + else if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { + correctType = BotSpellTypes::Pet; + } + else if (IsLifetapSpell(spell_id)) { + correctType = BotSpellTypes::Lifetap; + } + else if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { + correctType = BotSpellTypes::Snare; + } + else if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { + correctType = BotSpellTypes::DOT; + } + else if (IsDispelSpell(spell_id)) { + correctType = BotSpellTypes::Dispel; + } + else if ( + IsSelfConversionSpell(spell_id) || + IsAnyBuffSpell(spell_id) || + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) + ) { + correctType = BotSpellTypes::InCombatBuff; + } + else if (IsMesmerizeSpell(spell_id)) { + correctType = BotSpellTypes::Mez; + } + else if (IsCharmSpell(spell_id)) { + correctType = BotSpellTypes::Charm; + } + else if (IsSlowSpell(spell_id)) { + correctType = BotSpellTypes::Slow; + } + else if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { + correctType = BotSpellTypes::Debuff; + } + else if (IsCureSpell(spell_id)) { + correctType = BotSpellTypes::Cure; + } + else if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + if ( + s.type == BotSpellTypes::InCombatBuffSong || + s.type == BotSpellTypes::OutOfCombatBuffSong || + s.type == BotSpellTypes::PreCombatBuffSong + ) { + correctType = s.type; + } + else { + correctType = BotSpellTypes::OutOfCombatBuffSong; + } + } + else if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + !IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + correctType = BotSpellTypes::PreCombatBuff; + } + else if (IsFearSpell(spell_id)) { + correctType = BotSpellTypes::Fear; + } + else if (IsEscapeSpell(spell_id)) { + correctType = BotSpellTypes::Escape; + } + else if (IsHateReduxSpell(spell_id)) { + correctType = BotSpellTypes::HateRedux; + } + else if (IsEffectInSpell(spell_id, SE_Revive)) { + correctType = BotSpellTypes::Resurrect; + } + + if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { + LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" + , GetSpellName(spell_id) + , spell_id + , GetSpellTypeNameByID(s.type) + , s.type + , GetSpellTypeNameByID(correctType) + , correctType + ); //deleteme + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spellid` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" + , correctType + , spell_id + , GetSpellName(spell_id) + , spell_id + , GetSpellTypeNameByID(s.type) + , s.type + , GetSpellTypeNameByID(correctType) + , correctType + ); //deleteme + } + } +} + +BotSpell Bot::GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Revive); + + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsResurrectSpell(botSpellListItr->SpellId) && + botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForCharm(Bot* botCaster, Mob* target, uint16 spellType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Charm); + + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsCharmSpell(botSpellListItr->SpellId) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} diff --git a/zone/client.cpp b/zone/client.cpp index d0a8d97b0b..a5bc2f3873 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -51,6 +51,7 @@ extern volatile bool RunLoops; #include "water_map.h" #include "bot_command.h" #include "string_ids.h" +#include "dialogue_window.h" #include "guild_mgr.h" #include "quest_parser_collection.h" @@ -775,6 +776,8 @@ bool Client::Save(uint8 iCommitNow) { database.SaveCharacterEXPModifier(this); + database.botdb.SaveBotSettings(this); + return true; } @@ -5871,7 +5874,7 @@ void Client::SuspendMinion(int value) { m_suspendedminion.SpellID = SpellID; - m_suspendedminion.HP = CurrentPet->GetHP();; + m_suspendedminion.HP = CurrentPet->GetHP(); m_suspendedminion.Mana = CurrentPet->GetMana(); m_suspendedminion.petpower = CurrentPet->GetPetPower(); @@ -12896,11 +12899,11 @@ void Client::AddMoneyToPPWithOverflow(uint64 copper, bool update_client) SaveCurrency(); LogDebug("Client::AddMoneyToPPWithOverflow() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]", - GetName(), - m_pp.platinum, - m_pp.gold, - m_pp.silver, - m_pp.copper + GetName(), + m_pp.platinum, + m_pp.gold, + m_pp.silver, + m_pp.copper ); } @@ -13056,8 +13059,8 @@ void Client::ClientToNpcAggroProcess() { if (zone->CanDoCombat() && !GetFeigned() && m_client_npc_aggro_scan_timer.Check()) { int npc_scan_count = 0; - for (auto &close_mob: GetCloseMobList()) { - Mob *mob = close_mob.second; + for (auto& close_mob : GetCloseMobList()) { + Mob* mob = close_mob.second; if (!mob) { continue; } @@ -13075,3 +13078,259 @@ void Client::ClientToNpcAggroProcess() LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count); } } + +void Client::LoadDefaultBotSettings() { + // Only illusion block supported currently + SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + BotSpellSettings_Struct t; + + t.spellType = i; + t.shortName = GetSpellTypeShortNameByID(i); + t.name = GetSpellTypeNameByID(i); + t.hold = GetDefaultSpellHold(i); + t.delay = GetDefaultSpellDelay(i); + t.minThreshold = GetDefaultSpellMinThreshold(i); + t.maxThreshold = GetDefaultSpellMaxThreshold(i); + + _spellSettings.push_back(t); + + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); //deleteme + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); //deleteme + } +} + +int Client::GetDefaultBotSettings(uint8 settingType, uint16 botSetting) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + return false; + case BotSettingCategories::SpellHold: + return GetDefaultSpellHold(botSetting); + case BotSettingCategories::SpellDelay: + return GetDefaultSpellDelay(botSetting); + case BotSettingCategories::SpellMinThreshold: + return GetDefaultSpellMinThreshold(botSetting); + case BotSettingCategories::SpellMaxThreshold: + return GetDefaultSpellMaxThreshold(botSetting); + } +} + +int Client::GetBotSetting(uint8 settingType, uint16 botSetting) { + switch (settingType) { + case BotSettingCategories::SpellHold: + return GetSpellHold(botSetting); + case BotSettingCategories::SpellDelay: + return GetSpellDelay(botSetting); + case BotSettingCategories::SpellMinThreshold: + return GetSpellMinThreshold(botSetting); + case BotSettingCategories::SpellMaxThreshold: + return GetSpellMaxThreshold(botSetting); + } +} + +void Client::SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + SetBaseSetting(botSetting, settingValue); + break; + case BotSettingCategories::SpellHold: + SetSpellHold(botSetting, settingValue); + break; + case BotSettingCategories::SpellDelay: + SetSpellDelay(botSetting, settingValue); + break; + case BotSettingCategories::SpellMinThreshold: + SetSpellMinThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellMaxThreshold: + SetSpellMaxThreshold(botSetting, settingValue); + break; + } +} + +std::string Client::SendCommandHelpWindow( + Client* c, + std::vector description, + std::vector notes, + std::vector example_format, + std::vector examples_one, std::vector examples_two, std::vector examples_three, + std::vector actionables, + std::vector options, + std::vector options_one, std::vector options_two, std::vector options_three +) { + + unsigned stringLength = 0; + unsigned currentPlace = 0; + uint16 maxLength = RuleI(Command, MaxHelpLineLength); //character length of a line before splitting in to multiple lines + const std::string& description_color = RuleS(Command, DescriptionColor); + const std::string& description_header_color = RuleS(Command, DescriptionHeaderColor); + const std::string& alt_description_color = RuleS(Command, AltDescriptionColor); + const std::string& note_color = RuleS(Command, NoteColor); + const std::string& note_header_color = RuleS(Command, NoteHeaderColor); + const std::string& alt_note_color = RuleS(Command, AltNoteColor); + const std::string& example_color = RuleS(Command, ExampleColor); + const std::string& example_header_color = RuleS(Command, ExampleHeaderColor); + const std::string& sub_example_color = RuleS(Command, SubExampleColor); + const std::string& alt_example_color = RuleS(Command, AltExampleColor); + const std::string& sub_alt_example_color = RuleS(Command, SubAltExampleColor); + const std::string& option_color = RuleS(Command, OptionColor); + const std::string& option_header_color = RuleS(Command, OptionHeaderColor); + const std::string& sub_option_color = RuleS(Command, SubOptionColor); + const std::string& alt_option_color = RuleS(Command, AltOptionColor); + const std::string& sub_alt_option_color = RuleS(Command, SubAltOptionColor); + const std::string& actionable_color = RuleS(Command, ActionableColor); + const std::string& actionable_header_color = RuleS(Command, ActionableHeaderColor); + const std::string& alt_actionable_color = RuleS(Command, AltActionableColor); + const std::string& header_color = RuleS(Command, HeaderColor); + const std::string& secondary_header_color = RuleS(Command, SecondaryHeaderColor); + const std::string& alt_header_color = RuleS(Command, AltHeaderColor); + const std::string& filler_line_color = RuleS(Command, FillerLineColor); + + + + std::string fillerLine = "--------------------------------------------------------------------"; + std::string fillerDia = DialogueWindow::TableRow(DialogueWindow::TableCell(fmt::format("{}", DialogueWindow::ColorMessage(filler_line_color, fillerLine)))); + std::string breakLine = DialogueWindow::Break(); + std::string indent = "        "; + std::string bullet = "- "; + std::string popup_text = ""; + + /* + maxLength is how long you want lines to be before splitting them. This will look for the last space before the count and split there so words are not split mid sentence + Any SplitCommandHelpText can be be have the first string from a vector differ in color from the next strings by setting secondColor to true and assigning a color. + ex: SplitCommandHelpText(examples_one, example_color, maxLength, true, alt_example_color) + - This will make the first string from examples_one vector be the color of example_color and all following strings the color of alt_example_color + ex: SplitCommandHelpText(examples_one, example_color, maxLength) + - This will apply the color example_color to everything in examples_one vector + */ + + if (!description.empty()) { + popup_text += GetCommandHelpHeader(description_header_color, "[Description]"); + popup_text += SplitCommandHelpText(description, description_color, maxLength, true, alt_description_color); + } + + if (!notes.empty()) { + popup_text += breakLine; + popup_text += breakLine; + popup_text += GetCommandHelpHeader(note_header_color, "[Notes]"); + popup_text += SplitCommandHelpText(notes, note_color, maxLength, true, alt_note_color); + } + + if (!example_format.empty()) { + popup_text += fillerDia; + popup_text += GetCommandHelpHeader(example_header_color, "[Examples]"); + popup_text += SplitCommandHelpText(example_format, example_color, maxLength, true, alt_example_color); + } + + if (!examples_one.empty()) { + popup_text += breakLine; + popup_text += breakLine; + popup_text += SplitCommandHelpText(examples_one, sub_example_color, maxLength, true, sub_alt_example_color); + } + + if (!examples_two.empty()) { + popup_text += SplitCommandHelpText(examples_two, sub_example_color, maxLength, true, sub_alt_example_color); + } + + if (!examples_three.empty()) { + popup_text += SplitCommandHelpText(examples_three, sub_example_color, maxLength, true, sub_alt_example_color); + } + + if (!options.empty()) { + popup_text += fillerDia; + popup_text += GetCommandHelpHeader(option_header_color, "[Options]"); + popup_text += SplitCommandHelpText(options, option_color, maxLength, true, alt_option_color); + } + + if (!options_one.empty()) { + popup_text += breakLine; + popup_text += breakLine; + popup_text += SplitCommandHelpText(options_one, sub_option_color, maxLength, true, sub_alt_option_color); + } + + if (!options_two.empty()) { + popup_text += SplitCommandHelpText(options_two, sub_option_color, maxLength, true, sub_alt_option_color); + } + + if (!options_three.empty()) { + popup_text += SplitCommandHelpText(options_three, secondary_header_color, maxLength, true, sub_alt_option_color); + } + + if (!actionables.empty()) { + popup_text += fillerDia; + popup_text += GetCommandHelpHeader(actionable_header_color, "[Actionables]"); + popup_text += SplitCommandHelpText(actionables, actionable_color, maxLength, true, alt_actionable_color); + } + + popup_text = DialogueWindow::Table(popup_text); + + return popup_text; +} + +std::string Client::GetCommandHelpHeader(std::string color, std::string header) { + std::string returnText = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(color, header) + ) + ) + ); + + return returnText; +} + +std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor, std::string secondaryColor) { + std::string returnText; + + for (int i = 0; i < msg.size(); i++) { + std::vector msg_split; + int stringLength = msg[i].length() + 1; + int endCount = 0; + int newCount = 0; + int splitCount = 0; + + for (int x = 0; x < stringLength; x = endCount) { + endCount = std::min(int(stringLength), (int(x) + std::min(int(stringLength), int(maxLength)))); + + if ((stringLength - (x + 1)) > maxLength) { + for (int y = endCount; y >= x; --y) { + if (msg[i][y] == ' ') { + splitCount = y - x; + msg_split.emplace_back(msg[i].substr(x, splitCount)); + endCount = y + 1; + + break; + } + + if (y == x) { + msg_split.emplace_back(msg[i].substr(x, maxLength)); + + break; + } + } + } + else { + msg_split.emplace_back(msg[i].substr(x, (stringLength - 1) - x)); + + break; + } + } + + for (const auto& s : msg_split) { + returnText += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(((secondColor && i == 0) ? color : secondaryColor), s) + ) + ) + ); + + } + } + + return returnText; +} diff --git a/zone/client.h b/zone/client.h index 5acc388b3e..71310dd84f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1250,6 +1250,20 @@ class Client : public Mob PendingTranslocate_Struct PendingTranslocateData; void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID); + // Help Window + std::string SendCommandHelpWindow( + Client* c, + std::vector description, + std::vector notes, + std::vector example_format, + std::vector examples_one, std::vector examples_two, std::vector examples_three, + std::vector actionables, + std::vector options, + std::vector options_one, std::vector options_two, std::vector options_three + ); + std::string GetCommandHelpHeader(std::string color, std::string header); + std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor = false, std::string secondaryColor = ""); + // Task System Methods void LoadClientTaskState(); void RemoveClientTaskState(); @@ -2219,11 +2233,29 @@ class Client : public Mob void CampAllBots(uint8 class_id = Class::None); void SpawnRaidBotsOnConnect(Raid* raid); + void LoadDefaultBotSettings(); + int GetDefaultBotSettings(uint8 settingType, uint16 botSetting); + int GetBotSetting(uint8 settingType, uint16 botSetting); + void SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue); + private: bool bot_owner_options[_booCount]; bool m_bot_pulling; bool m_bot_precombat; + uint8 fast_heal_threshold; + uint8 heal_threshold; + uint8 complete_heal_threshold; + uint8 hot_heal_threshold; + uint32 fast_heal_delay; + uint32 heal_delay; + uint32 complete_heal_delay; + uint32 hot_heal_delay; + uint32 cure_delay; + uint8 cure_min_threshold; + uint8 cure_threshold; + bool illusion_block; + bool CanTradeFVNoDropItem(); void SendMobPositions(); void PlayerTradeEventLog(Trade *t, Trade *t2); diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 1f3f809dd0..bfe4cdb58f 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -19,6 +19,10 @@ uint32 Client::GetBotCreationLimit(uint8 class_id) { uint32 bot_creation_limit = RuleI(Bots, CreationLimit); + if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { + return RuleI(Bots, MinStatusToBypassCreateLimit); + } + const auto bucket_name = fmt::format( "bot_creation_limit{}", ( @@ -67,6 +71,10 @@ int Client::GetBotSpawnLimit(uint8 class_id) { int bot_spawn_limit = RuleI(Bots, SpawnLimit); + if (Admin() >= RuleI(Bots, MinStatusToBypassSpawnLimit)) { + return RuleI(Bots, MinStatusToBypassSpawnLimit); + } + const auto bucket_name = fmt::format( "bot_spawn_limit{}", ( diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 0387b795b5..23ef134541 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -812,7 +812,7 @@ int32 Client::CalcSTR() int32 Client::CalcSTA() { - int32 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect();; + int32 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect(); int32 mod = aabonuses.STA; STA = val + mod; if (STA < 1) { @@ -827,7 +827,7 @@ int32 Client::CalcSTA() int32 Client::CalcAGI() { - int32 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect();; + int32 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect(); int32 mod = aabonuses.AGI; int32 str = GetSTR(); //Encumbered penalty @@ -852,7 +852,7 @@ int32 Client::CalcAGI() int32 Client::CalcDEX() { - int32 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect();; + int32 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect(); int32 mod = aabonuses.DEX; DEX = val + mod; if (DEX < 1) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index c880e310f8..67e49d815e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -412,7 +412,7 @@ void MapOpcodes() ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; - ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura;; + ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura; ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; @@ -667,6 +667,10 @@ void Client::CompleteConnect() for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effect_id[x1]) { case SE_Illusion: { + if (GetIllusionBlock()) { + break; + } + if (buffs[j1].persistant_buff) { Mob *caster = entity_list.GetMobID(buffs[j1].casterid); ApplySpellEffectIllusion(spell.id, caster, j1, spell.base_value[x1], spell.limit_value[x1], spell.max_value[x1]); @@ -1487,6 +1491,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) LogError("Error loading AA points for [{}]", GetName()); } + /* Load Bots */ + + LoadDefaultBotSettings(); + + database.botdb.LoadBotSettings(this); + if (SPDAT_RECORDS > 0) { for (uint32 z = 0; z < EQ::spells::SPELL_GEM_COUNT; z++) { if (m_pp.mem_spells[z] >= (uint32)SPDAT_RECORDS) @@ -1583,7 +1593,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) LFG = false; } - /* Load Bots */ if (RuleB(Bots, Enabled)) { database.botdb.LoadOwnerOptions(this); // TODO: mod below function for loading spawned botgroups diff --git a/zone/command.cpp b/zone/command.cpp index 513cd02180..21f57a7099 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -147,6 +147,7 @@ int command_init(void) command_add("help", "[Search Criteria] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", AccountStatus::GMImpossible, command_hotfix) || command_add("hp", "Refresh your HP bar from the server.", AccountStatus::Player, command_hp) || + command_add("illusionblock", "Controls whether or not illusion effects will land on you when cast by other players or bots", AccountStatus::Player, command_illusion_block) || command_add("instance", "Modify Instances", AccountStatus::GMMgmt, command_instance) || command_add("interrogateinv", "use [help] argument for available options", AccountStatus::Player, command_interrogateinv) || command_add("interrupt", "[Message ID] [Color] - Interrupt your casting. Arguments are optional.", AccountStatus::Guide, command_interrupt) || @@ -217,6 +218,10 @@ int command_init(void) command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", AccountStatus::Steward, command_spawn) || command_add("spawneditmass", "[Search Criteria] [Edit Option] [Edit Value] [Apply] Mass editing spawn command (Apply is optional, 0 = False, 1 = True, default is False)", AccountStatus::GMLeadAdmin, command_spawneditmass) || command_add("spawnfix", "Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || + command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, command_spell_delays) || + command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, command_spell_holds) || + command_add("spellmaxthresholds", "Controls the minimum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_max_thresholds) || + command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_min_thresholds) || command_add("stun", "[duration] - Stuns you or your target for duration", AccountStatus::GMAdmin, command_stun) || command_add("summon", "[Character Name] - Summons your corpse, NPC, or player target, or by character name if specified", AccountStatus::QuestTroupe, command_summon) || command_add("summonburiedplayercorpse", "Summons the target's oldest buried corpse, if any exist.", AccountStatus::GMAdmin, command_summonburiedplayercorpse) || @@ -841,6 +846,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/grid.cpp" #include "gm_commands/guild.cpp" #include "gm_commands/hp.cpp" +#include "gm_commands/illusion_block.cpp" #include "gm_commands/instance.cpp" #include "gm_commands/interrogateinv.cpp" #include "gm_commands/interrupt.cpp" @@ -909,6 +915,10 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/spawn.cpp" #include "gm_commands/spawneditmass.cpp" #include "gm_commands/spawnfix.cpp" +#include "gm_commands/spell_delays.cpp" +#include "gm_commands/spell_holds.cpp" +#include "gm_commands/spell_max_thresholds.cpp" +#include "gm_commands/spell_min_thresholds.cpp" #include "gm_commands/faction_association.cpp" #include "gm_commands/stun.cpp" #include "gm_commands/summon.cpp" diff --git a/zone/command.h b/zone/command.h index d59fdf9a5e..aa5771da99 100644 --- a/zone/command.h +++ b/zone/command.h @@ -100,6 +100,7 @@ void command_guild(Client *c, const Seperator *sep); void command_help(Client *c, const Seperator *sep); void command_hotfix(Client *c, const Seperator *sep); void command_hp(Client *c, const Seperator *sep); +void command_illusion_block(Client* c, const Seperator* sep); void command_instance(Client *c, const Seperator *sep); void command_interrogateinv(Client *c, const Seperator *sep); void command_interrupt(Client *c, const Seperator *sep); @@ -170,6 +171,10 @@ void command_shutdown(Client *c, const Seperator *sep); void command_spawn(Client *c, const Seperator *sep); void command_spawneditmass(Client *c, const Seperator *sep); void command_spawnfix(Client *c, const Seperator *sep); +void command_spell_delays(Client* c, const Seperator* sep); +void command_spell_holds(Client* c, const Seperator* sep); +void command_spell_max_thresholds(Client* c, const Seperator* sep); +void command_spell_min_thresholds(Client* c, const Seperator* sep); void command_stun(Client *c, const Seperator *sep); void command_summon(Client *c, const Seperator *sep); void command_summonburiedplayercorpse(Client *c, const Seperator *sep); diff --git a/zone/entity.cpp b/zone/entity.cpp index c21856d371..f0b33363cc 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2951,6 +2951,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) for (auto &e : mob_list) { auto mob = e.second; + if (mob->GetID() <= 0) { continue; } diff --git a/zone/entity.h b/zone/entity.h index 33313f14ee..814a5feb5c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -628,7 +628,7 @@ class EntityList Client* GetBotOwnerByBotID(const uint32 bot_id); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); - bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate + bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); diff --git a/zone/exp.cpp b/zone/exp.cpp index be6b8af2b8..70f72cacb5 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -1004,6 +1004,7 @@ void Client::SetLevel(uint8 set_level, bool command) } } else { SetHP(CalcMaxHP()); // Why not, lets give them a free heal + SetMana(CalcMaxMana()); } if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) { diff --git a/zone/gm_commands/illusion_block.cpp b/zone/gm_commands/illusion_block.cpp new file mode 100644 index 0000000000..cd05b965ad --- /dev/null +++ b/zone/gm_commands/illusion_block.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +void command_illusion_block(Client* c, const Seperator* sep) +{ + int arguments = sep->argnum; + if (!arguments || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "usage: #illusionblock [help | current | value]."); + c->Message(Chat::White, "note: Used to control whether or not illusion effects will land on you."); + c->Message(Chat::White, "note: A value of 0 is disabled (Allow Illusions), 1 is enabled (Block Illusions)."); + c->Message(Chat::White, "note: Use [current] to check the current setting."); + return; + } + + if (sep->IsNumber(1)) { + int setStatus = atoi(sep->arg[1]); + if (setStatus == 0 || setStatus == 1) { + c->SetIllusionBlock(setStatus); + c->Message(Chat::White, "Your Illusion Block has been %s.", (setStatus ? "enabled" : "disabled")); + } + else { + c->Message(Chat::White, "You must enter 0 for disabled or 1 for enabled."); + return; + } + } + else if (!strcasecmp(sep->arg[1], "current")) { + c->Message(Chat::White, "You're currently %s illusions.", (c->GetIllusionBlock() ? "blocking" : "allowing")); + } + else { + c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + } +} diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp new file mode 100644 index 0000000000..09b2b0c5d5 --- /dev/null +++ b/zone/gm_commands/spell_delays.cpp @@ -0,0 +1,215 @@ +#include "../command.h" + +void command_spell_delays(Client* c, const Seperator* sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 4000] or [%s cures 4000] would allow bots to cast cures on you every 4 seconds.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 1 || typeValue > 60000) { + c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current {} delay is {} seconds.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } + else { + c->SetSpellDelay(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your {} delay was set to {} seconds.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } +} diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp new file mode 100644 index 0000000000..90a725dfbb --- /dev/null +++ b/zone/gm_commands/spell_holds.cpp @@ -0,0 +1,218 @@ +#include "../command.h" + +void command_spell_holds(Client *c, const Seperator *sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 1] or [%s cures 1] would prevent bots from casting cures on you.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message(Chat::White, "note: Set to 0 to unhold the given spell type."); + c->Message(Chat::White, "note: Set to 1 to hold the given spell type."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + // Enable/Disable/Current checks + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current Hold {}s status is {}.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + c->SetSpellHold(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your Hold {}s status was {}.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } +} diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp new file mode 100644 index 0000000000..a67660d2e8 --- /dev/null +++ b/zone/gm_commands/spell_max_thresholds.cpp @@ -0,0 +1,216 @@ +#include "../command.h" + +void command_spell_max_thresholds(Client* c, const Seperator* sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 95] or [%s cures 95] would allow bots to cast cures on you when you are under 95%% health.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + // Enable/Disable/Current checks + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 150) { + c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current max threshold for {}s is {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } + else { + c->SetSpellMaxThreshold(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your max threshold for {}s was set to {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } +} diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp new file mode 100644 index 0000000000..1b0fcfdf2a --- /dev/null +++ b/zone/gm_commands/spell_min_thresholds.cpp @@ -0,0 +1,216 @@ +#include "../command.h" + +void command_spell_min_thresholds(Client* c, const Seperator* sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 15] or [%s cures 15] would prevent bots from casting cures on you when you are under 15%% health.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + // Enable/Disable/Current checks + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 150) { + c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current min threshold for {}s is {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMinThreshold(spellType) + ).c_str() + ); + } + else { + c->SetSpellMinThreshold(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your min threshold for {}s was set to {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMinThreshold(spellType) + ).c_str() + ); + } +} diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index 2cf0e7db3f..8003e13881 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -105,11 +105,6 @@ void Lua_Bot::SetExpansionBitmask(int expansion_bitmask) { self->SetExpansionBitmask(expansion_bitmask); } -void Lua_Bot::SetExpansionBitmask(int expansion_bitmask, bool save) { - Lua_Safe_Call_Void(); - self->SetExpansionBitmask(expansion_bitmask, save); -} - bool Lua_Bot::ReloadBotDataBuckets() { Lua_Safe_Call_Bool(); return DataBucket::GetDataBuckets(self); @@ -762,7 +757,6 @@ luabind::scope lua_register_bot() { .def("SetBucket", (void(Lua_Bot::*)(std::string,std::string))&Lua_Bot::SetBucket) .def("SetBucket", (void(Lua_Bot::*)(std::string,std::string,std::string))&Lua_Bot::SetBucket) .def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask) - .def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask) .def("SetDisciplineReuseTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::SetDisciplineReuseTimer) .def("SetDisciplineReuseTimer", (void(Lua_Bot::*)(uint16, uint32))&Lua_Bot::SetDisciplineReuseTimer) .def("SetItemReuseTimer", (void(Lua_Bot::*)(uint32))&Lua_Bot::SetItemReuseTimer) diff --git a/zone/lua_bot.h b/zone/lua_bot.h index b07d14abce..7a038b7dc3 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -52,7 +52,6 @@ class Lua_Bot : public Lua_Mob void ReloadBotSpellSettings(); void RemoveBotItem(uint32 item_id); void SetExpansionBitmask(int expansion_bitmask); - void SetExpansionBitmask(int expansion_bitmask, bool save); void Signal(int signal_id); bool HasBotSpellEntry(uint16 spellid); void SendPayload(int payload_id); diff --git a/zone/merc.cpp b/zone/merc.cpp index 6200e5b895..d66501efad 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1500,7 +1500,7 @@ bool Merc::AI_IdleCastCheck() { bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { - if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { + if (BOT_SPELL_TYPES_DETRIMENTAL(iSpellTypes)) { //according to live, you can buff and heal through walls... //now with PCs, this only applies if you can TARGET the target, but // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. @@ -1569,7 +1569,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon float dist2 = 0; - if (mercSpell.type & SpellType_Escape) { + if (mercSpell.type == SpellType_Escape) { dist2 = 0; } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); diff --git a/zone/mob.cpp b/zone/mob.cpp index a1d9b5d380..7182bbfb4c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1692,6 +1692,11 @@ void Mob::StopMoving() void Mob::StopMoving(float new_heading) { + if (IsBot()) { + CastToBot()->SetCombatJitterFlag(false); + CastToBot()->SetCombatOutOfRangeJitterFlag(false); + } + StopNavigation(); RotateTo(new_heading); @@ -4664,7 +4669,7 @@ bool Mob::CanThisClassDoubleAttack(void) const bool Mob::CanThisClassTripleAttack() const { - if (!IsClient()) { + if (!IsOfClientBot()) { return false; // When they added the real triple attack skill, mobs lost the ability to triple } else { if (RuleB(Combat, ClassicTripleAttack)) { @@ -4678,7 +4683,12 @@ bool Mob::CanThisClassTripleAttack() const ) ); } else { - return CastToClient()->HasSkill(EQ::skills::SkillTripleAttack); + if (IsClient()) { + return CastToClient()->HasSkill(EQ::skills::SkillTripleAttack); + } + else { + return GetSkill(EQ::skills::SkillTripleAttack) > 0; + } } } } @@ -4812,6 +4822,88 @@ bool Mob::PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, fl return Result; } +bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behindOnly, bool frontOnly, bool bypassLoS) { + bool Result = false; + + if (target) { + float look_heading = 0; + + min_distance = min_distance; + max_distance = max_distance; + float tempX = 0; + float tempY = 0; + float tempZ = target->GetZ(); + float bestZ = 0; + auto offset = GetZOffset(); + const float tarX = target->GetX(); + const float tarY = target->GetY(); + float tar_distance = 0; + + glm::vec3 temp_z_Position; + glm::vec4 temp_m_Position; + + const uint16 maxIterationsAllowed = 50; + uint16 counter = 0; + //LogTestDebug("Plotting for {} - Min: [{}] - Max: [{}] - BehindMob: [{}] - Taunt [{}] -- LosReq [{}]", GetCleanName(), min_distance, max_distance, behindOnly, frontOnly, bypassLoS ? "bypassed" : CastToBot()->RequiresLoSForPositioning() ? "true" : "false"); //deleteme + while (counter < maxIterationsAllowed) { + tempX = tarX + zone->random.Real(-max_distance, max_distance); + tempY = tarY + zone->random.Real(-max_distance, max_distance); + + temp_z_Position.x = tempX; + temp_z_Position.y = tempY; + temp_z_Position.z = tempZ; + bestZ = GetFixedZ(temp_z_Position); + + if (bestZ != BEST_Z_INVALID) { + tempZ = bestZ; + } + else { + //LogTestDebug("{} - Plot Failed GetFixedZ - Try #[{}].", GetCleanName(), (counter + 1)); //deleteme + counter++; + continue; + } + + temp_m_Position.x = tempX; + temp_m_Position.y = tempY; + temp_m_Position.z = tempZ; + //tar_distance = DistanceNoZ(target->GetPosition(), temp_m_Position); + tar_distance = Distance(target->GetPosition(), temp_m_Position); + + if (tar_distance > max_distance || tar_distance < min_distance) { + //LogTestDebug("{} - Plot Failed Distance - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + if (frontOnly && !InFrontMob(target, tempX, tempY)) { + //LogTestDebug("{} - Plot Failed frontOnly - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + else if (behindOnly && !BehindMob(target, tempX, tempY)) { + //LogTestDebug("{} - Plot Failed BehindMob - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + if (!bypassLoS && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, tempX, tempY, tempZ)) { + //LogTestDebug("{} - Plot Failed LoS - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + //LogTestDebug("{} - Plot PASSED! - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + Result = true; + break; + } + + if (Result) { + x_dest = tempX; + y_dest = tempY; + z_dest = tempZ; + } + } + + return Result; +} + bool Mob::HateSummon() { // check if mob has ability to summon // 97% is the offical % that summoning starts on live, not 94 @@ -8581,8 +8673,9 @@ bool Mob::HasBotAttackFlag(Mob* tar) { return false; } + const uint16 scan_close_mobs_timer_moving = 6000; // 6 seconds -const uint16 scan_close_mobs_timer_idle = 60000; // 60 seconds +const uint16 scan_close_mobs_timer_idle = 60000; // 60 seconds void Mob::CheckScanCloseMobsMovingTimer() { @@ -8621,6 +8714,732 @@ void Mob::ScanCloseMobProcess() } } +uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!Strings::ToLower(spellTypeString).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + + return UINT16_MAX; +} + +std::string Mob::GetSpellTypeNameByID(uint16 spellType) { + std::string spellTypeName = "null"; + + switch (spellType) { + case BotSpellTypes::Nuke: + spellTypeName = "Nuke"; + break; + case BotSpellTypes::RegularHeal: + spellTypeName = "Regular Heal"; + break; + case BotSpellTypes::Root: + spellTypeName = "Root"; + break; + case BotSpellTypes::Buff: + spellTypeName = "Buff"; + break; + case BotSpellTypes::Escape: + spellTypeName = "Escape"; + break; + case BotSpellTypes::Pet: + spellTypeName = "Pet"; + break; + case BotSpellTypes::Lifetap: + spellTypeName = "Lifetap"; + break; + case BotSpellTypes::Snare: + spellTypeName = "Snare"; + break; + case BotSpellTypes::DOT: + spellTypeName = "DoT"; + break; + case BotSpellTypes::Dispel: + spellTypeName = "Dispel"; + break; + case BotSpellTypes::InCombatBuff: + spellTypeName = "In-Combat Buff"; + break; + case BotSpellTypes::Mez: + spellTypeName = "Mez"; + break; + case BotSpellTypes::Charm: + spellTypeName = "Charm"; + break; + case BotSpellTypes::Slow: + spellTypeName = "Slow"; + break; + case BotSpellTypes::Debuff: + spellTypeName = "Debuff"; + break; + case BotSpellTypes::Cure: + spellTypeName = "Cure"; + break; + case BotSpellTypes::GroupCures: + spellTypeName = "Group Cure"; + break; + case BotSpellTypes::Resurrect: + spellTypeName = "Resurrect"; + break; + case BotSpellTypes::HateRedux: + spellTypeName = "Hate Reduction"; + break; + case BotSpellTypes::InCombatBuffSong: + spellTypeName = "In-Combat Buff Song"; + break; + case BotSpellTypes::OutOfCombatBuffSong: + spellTypeName = "Out-of-Combat Buff Song"; + break; + case BotSpellTypes::PreCombatBuff: + spellTypeName = "Pre-Combat Buff"; + break; + case BotSpellTypes::PreCombatBuffSong: + spellTypeName = "Pre-Combat Buff Song"; + break; + case BotSpellTypes::Fear: + spellTypeName = "Fear"; + break; + case BotSpellTypes::Stun: + spellTypeName = "Stun"; + break; + case BotSpellTypes::CompleteHeal: + spellTypeName = "Complete Heal"; + break; + case BotSpellTypes::FastHeals: + spellTypeName = "Fast Heal"; + break; + case BotSpellTypes::VeryFastHeals: + spellTypeName = "Very Fast Heal"; + break; + case BotSpellTypes::GroupHeals: + spellTypeName = "Group Heal"; + break; + case BotSpellTypes::GroupCompleteHeals: + spellTypeName = "Group Complete Heal"; + break; + case BotSpellTypes::GroupHoTHeals: + spellTypeName = "Group HoT Heal"; + break; + case BotSpellTypes::HoTHeals: + spellTypeName = "HoT Heal"; + break; + case BotSpellTypes::AENukes: + spellTypeName = "AE Nuke"; + break; + case BotSpellTypes::AERains: + spellTypeName = "AE Rain"; + break; + case BotSpellTypes::AEMez: + spellTypeName = "AE Mez"; + break; + case BotSpellTypes::AEStun: + spellTypeName = "AE Stun"; + break; + case BotSpellTypes::AEDebuff: + spellTypeName = "AE Debuff"; + break; + case BotSpellTypes::AESlow: + spellTypeName = "AE Slow"; + break; + case BotSpellTypes::AESnare: + spellTypeName = "AE Snare"; + break; + case BotSpellTypes::AEFear: + spellTypeName = "AE Fear"; + break; + case BotSpellTypes::AEDispel: + spellTypeName = "AE Dispel"; + break; + case BotSpellTypes::AERoot: + spellTypeName = "AE Root"; + break; + case BotSpellTypes::AEDoT: + spellTypeName = "AE DoT"; + break; + case BotSpellTypes::AELifetap: + spellTypeName = "AE Lifetap"; + break; + case BotSpellTypes::PBAENuke: + spellTypeName = "PBAE Nuke"; + break; + case BotSpellTypes::PetBuffs: + spellTypeName = "Pet Buff"; + break; + case BotSpellTypes::PetRegularHeals: + spellTypeName = "Pet Regular Heal"; + break; + case BotSpellTypes::PetCompleteHeals: + spellTypeName = "Pet Complete Heal"; + break; + case BotSpellTypes::PetFastHeals: + spellTypeName = "Pet Fast Heal"; + break; + case BotSpellTypes::PetVeryFastHeals: + spellTypeName = "Pet Very Fast Heal"; + break; + case BotSpellTypes::PetHoTHeals: + spellTypeName = "Pet HoT Heal"; + break; + case BotSpellTypes::DamageShields: + spellTypeName = "Damage Shield"; + break; + case BotSpellTypes::ResistBuffs: + spellTypeName = "Resist Buff"; + break; + default: + break; + } + + return spellTypeName; +} + +std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { + std::string spellTypeName = "null"; + + switch (spellType) { + case BotSpellTypes::Nuke: + spellTypeName = "nukes"; + break; + case BotSpellTypes::RegularHeal: + spellTypeName = "regularheals"; + break; + case BotSpellTypes::Root: + spellTypeName = "roots"; + break; + case BotSpellTypes::Buff: + spellTypeName = "buffs"; + break; + case BotSpellTypes::Escape: + spellTypeName = "escapes"; + break; + case BotSpellTypes::Pet: + spellTypeName = "pets"; + break; + case BotSpellTypes::Lifetap: + spellTypeName = "lifetaps"; + break; + case BotSpellTypes::Snare: + spellTypeName = "snares"; + break; + case BotSpellTypes::DOT: + spellTypeName = "dots"; + break; + case BotSpellTypes::Dispel: + spellTypeName = "dispels"; + break; + case BotSpellTypes::InCombatBuff: + spellTypeName = "incombatbuffs"; + break; + case BotSpellTypes::Mez: + spellTypeName = "mez"; + break; + case BotSpellTypes::Charm: + spellTypeName = "charms"; + break; + case BotSpellTypes::Slow: + spellTypeName = "slows"; + break; + case BotSpellTypes::Debuff: + spellTypeName = "debuffs"; + break; + case BotSpellTypes::Cure: + spellTypeName = "cures"; + break; + case BotSpellTypes::GroupCures: + spellTypeName = "groupcures"; + break; + case BotSpellTypes::Resurrect: + spellTypeName = "resurrect"; + break; + case BotSpellTypes::HateRedux: + spellTypeName = "hateredux"; + break; + case BotSpellTypes::InCombatBuffSong: + spellTypeName = "incombatbuffsongs"; + break; + case BotSpellTypes::OutOfCombatBuffSong: + spellTypeName = "outofcombatbuffsongs"; + break; + case BotSpellTypes::PreCombatBuff: + spellTypeName = "precombatbuffs"; + break; + case BotSpellTypes::PreCombatBuffSong: + spellTypeName = "precombatbuffsongs"; + break; + case BotSpellTypes::Fear: + spellTypeName = "fears"; + break; + case BotSpellTypes::Stun: + spellTypeName = "stuns"; + break; + case BotSpellTypes::CompleteHeal: + spellTypeName = "completeheals"; + break; + case BotSpellTypes::FastHeals: + spellTypeName = "fastheals"; + break; + case BotSpellTypes::VeryFastHeals: + spellTypeName = "veryfastheals"; + break; + case BotSpellTypes::GroupHeals: + spellTypeName = "groupheals"; + break; + case BotSpellTypes::GroupCompleteHeals: + spellTypeName = "groupcompleteheals"; + break; + case BotSpellTypes::GroupHoTHeals: + spellTypeName = "grouphotheals"; + break; + case BotSpellTypes::HoTHeals: + spellTypeName = "hotheals"; + break; + case BotSpellTypes::AENukes: + spellTypeName = "aenukes"; + break; + case BotSpellTypes::AERains: + spellTypeName = "aerains"; + break; + case BotSpellTypes::AEMez: + spellTypeName = "aemez"; + break; + case BotSpellTypes::AEStun: + spellTypeName = "aestuns"; + break; + case BotSpellTypes::AEDebuff: + spellTypeName = "aedebuffs"; + break; + case BotSpellTypes::AESlow: + spellTypeName = "aeslows"; + break; + case BotSpellTypes::AESnare: + spellTypeName = "aesnares"; + break; + case BotSpellTypes::AEFear: + spellTypeName = "aefears"; + break; + case BotSpellTypes::AEDispel: + spellTypeName = "aedispels"; + break; + case BotSpellTypes::AERoot: + spellTypeName = "aeroots"; + break; + case BotSpellTypes::AEDoT: + spellTypeName = "aedots"; + break; + case BotSpellTypes::AELifetap: + spellTypeName = "aelifetaps"; + break; + case BotSpellTypes::PBAENuke: + spellTypeName = "pbaenukes"; + break; + case BotSpellTypes::PetBuffs: + spellTypeName = "petbuffs"; + break; + case BotSpellTypes::PetRegularHeals: + spellTypeName = "petregularheals"; + break; + case BotSpellTypes::PetCompleteHeals: + spellTypeName = "petcompleteheals"; + break; + case BotSpellTypes::PetFastHeals: + spellTypeName = "petfastheals"; + break; + case BotSpellTypes::PetVeryFastHeals: + spellTypeName = "petveryfastheals"; + break; + case BotSpellTypes::PetHoTHeals: + spellTypeName = "pethotheals"; + break; + case BotSpellTypes::DamageShields: + spellTypeName = "damageshields"; + break; + case BotSpellTypes::ResistBuffs: + spellTypeName = "resistbuffs"; + break; + default: + break; + } + + return spellTypeName; +} + +bool Mob::GetDefaultSpellHold(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEMez: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + return true; + case BotSpellTypes::Snare: + if (GetClass() == Class::Wizard) { + return true; + } + else { + return false; + } + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: + if (GetClass() == Class::Bard) { + return true; + } + else { + return false; + } + default: + return false; + } +} + +uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return 1500; + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return 2500; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return 4000; + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + return 6000; + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 8000; + case BotSpellTypes::Fear: + case BotSpellTypes::AEFear: + return 15000; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return 22000; + default: + return 1; + } +} + +uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + return 20; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + return 35; + case BotSpellTypes::Charm: + return 50; + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + return 85; + default: + return 0; + } +} + +uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Escape: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return 25; + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return 40; + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return 60; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 80; + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + return 99; + case BotSpellTypes::Buff: + case BotSpellTypes::Charm: + case BotSpellTypes::Cure: + case BotSpellTypes::DamageShields: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::Resurrect: + return 100; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + if (GetClass() == Class::Necromancer || GetClass() == Class::Shaman) { + return 60; + } + else { + return 90; + } + default: + return 100; + } +} + +void Mob::SetSpellHold(uint16 spellType, bool holdStatus) { + _spellSettings[spellType].hold = holdStatus; +} + +void Mob::SetSpellDelay(uint16 spellType, uint16 delayValue) { + _spellSettings[spellType].delay = delayValue; +} + +void Mob::SetSpellMinThreshold(uint16 spellType, uint8 thresholdValue) { + _spellSettings[spellType].minThreshold = thresholdValue; +} + +void Mob::SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue) { + _spellSettings[spellType].maxThreshold = thresholdValue; +} + +void Mob::SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime) { + _spellSettings[spellType].recastTimer.Start(recastTime); + LogBotDelayChecksDetail("{} says, 'My {} Delay was to {} seconds.'" + , GetCleanName() + , GetSpellTypeNameByID(spellType) + , (recastTime / 1000.00) + ); //deleteme +} + +void Mob::StartBotSpellTimers() { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + _spellSettings[i].recastTimer.Start(); + } +} + +void Mob::DisableBotSpellTimers() { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + _spellSettings[i].recastTimer.Disable(); + } +} + +bool Mob::GetUltimateSpellHold(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellHold(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellHold(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellHold(spellType); + } + + return GetSpellHold(spellType); +} + +uint16 Mob::GetUltimateSpellDelay(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellDelay(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellDelay(spellType); + } + + return GetSpellDelay(spellType); +} + +bool Mob::GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar) { + if (!tar) { + return SpellTypeRecastCheck(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->SpellTypeRecastCheck(spellType); + } + + return SpellTypeRecastCheck(spellType); +} + +uint8 Mob::GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellMinThreshold(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellMinThreshold(spellType); + } + + return GetSpellMinThreshold(spellType); +} + +uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellMaxThreshold(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellMaxThreshold(spellType); + } + + return GetSpellMaxThreshold(spellType); +} + +uint16 Mob::GetPetSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + default: + break; + } + + return spellType; +} + +uint8 Mob::GetHPRatioForSpellType(uint16 spellType, Mob* tar) { + switch (spellType) { + case BotSpellTypes::Escape: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + return GetHPRatio(); + default: + return tar->GetHPRatio(); + } + + return tar->GetHPRatio(); +} + +void Mob::SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue) { + if (!IsOfClientBot()) { + return; + } + + if (IsClient()) { + CastToClient()->SetBotSetting(settingType, botSetting, settingValue); + return; + } + + if (IsBot()) { + CastToBot()->SetBotSetting(settingType, botSetting, settingValue); + return; + } + + return; +} + +void Mob::SetBaseSetting(uint16 baseSetting, int settingValue) { + switch (baseSetting) { + case BotBaseSettings::IllusionBlock: + SetIllusionBlock(settingValue); + break; + default: + break; + } +} + +bool Mob::TargetValidation(Mob* other) { + if (!other || GetAppearance() == eaDead) { + return false; + } + + return true; +} + std::unordered_map &Mob::GetCloseMobList(float distance) { return entity_list.GetCloseMobList(this, distance); diff --git a/zone/mob.h b/zone/mob.h index 8914534cb7..65b37d3c14 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -93,6 +93,28 @@ struct AppearanceStruct { uint8 texture = UINT8_MAX; }; +struct BotSpellSettings_Struct +{ + uint16 spellType; // type ID of bot category + std::string shortName; // type short name of bot category + std::string name; // type name of bot category + bool hold; // 0 = allow spell type, 1 = hold spell type + uint16 delay; // delay between casts of spell type, 1ms-60,000ms + uint8 minThreshold; // minimum target health threshold to allow casting of spell type + uint8 maxThreshold; // maximum target health threshold to allow casting of spell type + uint16 resistLimit; // resist limit to skip spell type + bool aggroCheck; // whether or not to check for possible aggro before casting + uint8 minManaPct; // lower mana percentage limit to allow spell cast + uint8 maxManaPct; // upper mana percentage limit to allow spell cast + uint8 minHPPct; // lower HP percentage limit to allow spell cast + uint8 maxHPPct; // upper HP percentage limit to allow spell cast + uint16 idlePriority; // idle priority of the spell type + uint16 engagedPriority; // engaged priority of the spell type + uint16 pursuePriority; // pursue priority of the spell type + uint16 AEOrGroupTargetCount; // require target count to cast an AE or Group spell type + Timer recastTimer; // recast timer based off delay +}; + class DataBucketKey; class Mob : public Entity { public: @@ -208,6 +230,8 @@ class Mob : public Entity { // Bot attack flag Timer bot_attack_flag_timer; + std::vector _spellSettings; + //Somewhat sorted: needs documenting! //Attack @@ -403,6 +427,51 @@ class Mob : public Entity { virtual bool CheckFizzle(uint16 spell_id); virtual bool CheckSpellLevelRestriction(Mob *caster, uint16 spell_id); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); + + virtual bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); + + inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient() ? true : _spellSettings[spellType].recastTimer.GetRemainingTime() > 0 ? false : true); } + + uint16 GetSpellTypeIDByShortName(std::string spellTypeString); + + std::string GetSpellTypeNameByID(uint16 spellType); + std::string GetSpellTypeShortNameByID(uint16 spellType); + + bool GetDefaultSpellHold(uint16 spellType); + uint16 GetDefaultSpellDelay(uint16 spellType); + uint8 GetDefaultSpellMinThreshold(uint16 spellType); + uint8 GetDefaultSpellMaxThreshold(uint16 spellType); + + inline bool GetSpellHold(uint16 spellType) const { return _spellSettings[spellType].hold; } + void SetSpellHold(uint16 spellType, bool holdStatus); + inline uint16 GetSpellDelay(uint16 spellType) const { return _spellSettings[spellType].delay; } + void SetSpellDelay(uint16 spellType, uint16 delayValue); + inline uint8 GetSpellMinThreshold(uint16 spellType) const { return _spellSettings[spellType].minThreshold; } + void SetSpellMinThreshold(uint16 spellType, uint8 thresholdValue); + inline uint8 GetSpellMaxThreshold(uint16 spellType) const { return _spellSettings[spellType].maxThreshold; } + void SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue); + + inline uint16 GetSpellTypeRecastTimer(uint16 spellType) { return _spellSettings[spellType].recastTimer.GetRemainingTime(); } + void SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime); + + uint8 GetHPRatioForSpellType(uint16 spellType, Mob* tar); + bool GetUltimateSpellHold(uint16 spellType, Mob* tar); + uint16 GetUltimateSpellDelay(uint16 spellType, Mob* tar); + bool GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar); + uint8 GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar); + uint8 GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar); + + uint16 GetPetSpellType(uint16 spellType); + + void DisableBotSpellTimers(); + void StartBotSpellTimers(); + + void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); + void SetBaseSetting(uint16 baseSetting, int settingValue); + + void SetIllusionBlock(bool value) { _illusionBlock = value; } + bool GetIllusionBlock() const { return _illusionBlock; } + virtual float GetAOERange(uint16 spell_id); void InterruptSpell(uint16 spellid = SPELL_UNKNOWN); void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN); @@ -791,6 +860,11 @@ class Mob : public Entity { bool CheckLosFN(float posX, float posY, float posZ, float mobSize); static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget); virtual bool CheckWaterLoS(Mob* m); + bool CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ); + bool CheckLosCheat(Mob* who, Mob* other); + bool CheckLosCheatExempt(Mob* who, Mob* other); + bool DoLosChecks(Mob* who, Mob* other); + bool TargetValidation(Mob* other); inline void SetLastLosState(bool value) { last_los_check = value; } inline bool CheckLastLosState() const { return last_los_check; } std::string GetMobDescription(); @@ -854,6 +928,7 @@ class Mob : public Entity { void ShowStats(Client* client); void ShowBuffs(Client* c); bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true); + bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behindOnly = false, bool frontOnly = false, bool bypassLoS = false); virtual int GetKillExpMod() const { return 100; } // aura functions @@ -1252,13 +1327,30 @@ class Mob : public Entity { void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; } + inline uint32 DontGroupHealMeBefore() const { return pDontGroupHealMeBefore; } + inline uint32 DontGroupHoTHealMeBefore() const { return pDontGroupHoTHealMeBefore; } + inline uint32 DontRegularHealMeBefore() const { return pDontRegularHealMeBefore; } + inline uint32 DontVeryFastHealMeBefore() const { return pDontVeryFastHealMeBefore; } + inline uint32 DontFastHealMeBefore() const { return pDontFastHealMeBefore; } + inline uint32 DontCompleteHealMeBefore() const { return pDontCompleteHealMeBefore; } + inline uint32 DontGroupCompleteHealMeBefore() const { return pDontGroupCompleteHealMeBefore; } + inline uint32 DontHotHealMeBefore() const { return pDontHotHealMeBefore; } inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; } inline uint32 DontDotMeBefore() const { return pDontDotMeBefore; } inline uint32 DontRootMeBefore() const { return pDontRootMeBefore; } inline uint32 DontSnareMeBefore() const { return pDontSnareMeBefore; } inline uint32 DontCureMeBefore() const { return pDontCureMeBefore; } + void SetDontRootMeBefore(uint32 time) { pDontRootMeBefore = time; } void SetDontHealMeBefore(uint32 time) { pDontHealMeBefore = time; } + void SetDontGroupHealMeBefore(uint32 time) { pDontGroupHealMeBefore = time; } + void SetDontGroupHoTHealMeBefore(uint32 time) { pDontGroupHoTHealMeBefore = time; } + void SetDontRegularHealMeBefore(uint32 time) { pDontRegularHealMeBefore = time; } + void SetDontVeryFastHealMeBefore(uint32 time) { pDontVeryFastHealMeBefore = time; } + void SetDontFastHealMeBefore(uint32 time) { pDontFastHealMeBefore = time; } + void SetDontCompleteHealMeBefore(uint32 time) { pDontCompleteHealMeBefore = time; } + void SetDontGroupCompleteHealMeBefore(uint32 time) { pDontGroupCompleteHealMeBefore = time; } + void SetDontHotHealMeBefore(uint32 time) { pDontHotHealMeBefore = time; } void SetDontBuffMeBefore(uint32 time) { pDontBuffMeBefore = time; } void SetDontDotMeBefore(uint32 time) { pDontDotMeBefore = time; } void SetDontSnareMeBefore(uint32 time) { pDontSnareMeBefore = time; } @@ -1857,6 +1949,14 @@ class Mob : public Entity { bool pause_timer_complete; bool DistractedFromGrid; uint32 pDontHealMeBefore; + uint32 pDontGroupHealMeBefore; + uint32 pDontGroupHoTHealMeBefore; + uint32 pDontRegularHealMeBefore; + uint32 pDontVeryFastHealMeBefore; + uint32 pDontFastHealMeBefore; + uint32 pDontCompleteHealMeBefore; + uint32 pDontGroupCompleteHealMeBefore; + uint32 pDontHotHealMeBefore; uint32 pDontBuffMeBefore; uint32 pDontDotMeBefore; uint32 pDontRootMeBefore; @@ -1879,6 +1979,9 @@ class Mob : public Entity { //bot attack flags std::vector bot_attack_flags; + //bot related settings + bool _illusionBlock; + glm::vec3 m_TargetRing; GravityBehavior flymode; diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index 86f1045653..f24134c6a9 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -425,11 +425,6 @@ void Perl_Bot_SetExpansionBitmask(Bot* self, int expansion_bitmask) self->SetExpansionBitmask(expansion_bitmask); } -void Perl_Bot_SetExpansionBitmask(Bot* self, int expansion_bitmask, bool save) -{ - self->SetExpansionBitmask(expansion_bitmask, save); -} - void Perl_Bot_SetSpellDuration(Bot* self, int spell_id) { self->SetSpellDuration(spell_id); @@ -716,7 +711,6 @@ void perl_register_bot() package.add("SendPayload", (void(*)(Bot*, int, std::string))&Perl_Bot_SendPayload); package.add("SendSpellAnim", &Perl_Bot_SendSpellAnim); package.add("SetExpansionBitmask", (void(*)(Bot*, int))&Perl_Bot_SetExpansionBitmask); - package.add("SetExpansionBitmask", (void(*)(Bot*, int, bool))&Perl_Bot_SetExpansionBitmask); package.add("SetDisciplineReuseTimer", (void(*)(Bot*, uint16))&Perl_Bot_SetDisciplineReuseTimer); package.add("SetDisciplineReuseTimer", (void(*)(Bot*, uint16, uint32))&Perl_Bot_SetDisciplineReuseTimer); package.add("SetItemReuseTimer", (void(*)(Bot*, uint32))&Perl_Bot_SetItemReuseTimer); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 8bb39afda2..528445d419 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1144,7 +1144,7 @@ void Perl_Client_SetStartZone(Client* self, uint32 zone_id, float x, float y, fl void Perl_Client_KeyRingAdd(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items { - self->KeyRingAdd(item_id);; + self->KeyRingAdd(item_id); } bool Perl_Client_KeyRingCheck(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items @@ -1154,7 +1154,7 @@ bool Perl_Client_KeyRingCheck(Client* self, uint32 item_id) // @categories Accou void Perl_Client_AddPVPPoints(Client* self, uint32 points) // @categories Currency and Points { - self->AddPVPPoints(points);; + self->AddPVPPoints(points); } void Perl_Client_AddCrystals(Client* self, uint32 radiant_count, uint32 ebon_count) // @categories Currency and Points diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index b47b8c0e8d..4f0999376c 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -892,7 +892,7 @@ void perl_register_npc() package.add("IsGuarding", &Perl_NPC_IsGuarding); package.add("IsLDoNLocked", &Perl_NPC_IsLDoNLocked); package.add("IsLDoNTrapped", &Perl_NPC_IsLDoNTrapped); - package.add("IsLDoNTrapDetected", &Perl_NPC_IsLDoNTrapDetected);; + package.add("IsLDoNTrapDetected", &Perl_NPC_IsLDoNTrapDetected); package.add("IsOnHatelist", &Perl_NPC_IsOnHatelist); package.add("IsRaidTarget", &Perl_NPC_IsRaidTarget); package.add("IsRareSpawn", &Perl_NPC_IsRareSpawn); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index cf77e503e0..afae9367b5 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2896,7 +2896,7 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level initiator->Message( Chat::White, fmt::format( - "{} has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name.", + "{} has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name and it must be between 4 and 15 characters long.", new_bot->GetCleanName() ).c_str() ); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 93721f8c97..6e9f33d052 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1483,6 +1483,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); #endif + if (caster && caster->IsOfClientBot() && GetIllusionBlock()) { + break; + } + ApplySpellEffectIllusion(spell_id, caster, buffslot, spells[spell_id].base_value[i], spells[spell_id].limit_value[i], spells[spell_id].max_value[i]); break; } @@ -1492,6 +1496,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Illusion Copy"); #endif + if (caster && caster->IsOfClientBot() && GetIllusionBlock()) { + break; + } + if(caster && caster->GetTarget()){ SendIllusionPacket ( @@ -10658,7 +10666,7 @@ int Mob::GetBuffStatValueBySlot(uint8 slot, const char* stat_identifier) if (id == "caster_level") { return buffs[slot].casterlevel; } else if (id == "spell_id") { return buffs[slot].spellid; } - else if (id == "caster_id") { return buffs[slot].spellid;; } + else if (id == "caster_id") { return buffs[slot].spellid; } else if (id == "ticsremaining") { return buffs[slot].ticsremaining; } else if (id == "counters") { return buffs[slot].counters; } else if (id == "hit_number") { return buffs[slot].hit_number; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 7b644cd3ac..375ab1571c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -473,7 +473,6 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust); // return true; } - // ok we know it has a cast time so we can start the timer now spellend_timer.Start(cast_time); @@ -1280,6 +1279,17 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) EQApplicationPacket *outapp = nullptr; uint16 message_other; bool bard_song_mode = false; //has the bard song gone to auto repeat mode + + if (IsBot()) { + CastToBot()->SetCastedSpellType(UINT16_MAX); + } + + if (IsBot() && IsValidSpell(spellid)) { + if (CastToBot()->CheckSpellRecastTimer(spellid)) { + CastToBot()->ClearSpellRecastTimer(spellid); + } + } + if (!IsValidSpell(spellid)) { if (bardsong) { spellid = bardsong; @@ -2088,7 +2098,17 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce } case ST_Pet: { - spell_target = GetPet(); + if ( + !( + IsBot() && + spell_target && + spell_target->GetOwner() != this && + RuleB(Bots, CanCastPetOnlyOnOthersPets) + ) + ) { + + spell_target = GetPet(); + } if(!spell_target) { LogSpells("Spell [{}] canceled: invalid target (no pet)", spell_id); @@ -2835,6 +2855,13 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in } } } + + if (IsBot() && !isproc && !IsFromTriggeredSpell(slot, inventory_slot) && IsValidSpell(spell_id)) { + if (spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { + CastToBot()->SetSpellRecastTimer(spell_id); + } + } + /* Set Recast Timer on item clicks, including augmenets. */ @@ -3426,12 +3453,17 @@ bool Mob::CheckSpellLevelRestriction(Mob *caster, uint16 spell_id) bool can_cast = true; // NON GM clients might be restricted by rule setting - if (caster->IsClient()) { + if (caster->IsOfClientBot()) { if (IsClient()) { // Only restrict client on client for this rule if (RuleB(Spells, BuffLevelRestrictions)) { check_for_restrictions = true; } } + else if (IsBot()) { + if (RuleB(Bots, BotBuffLevelRestrictions)) { + check_for_restrictions = true; + } + } } // NPCS might be restricted by rule setting else if (RuleB(Spells, NPCBuffLevelRestrictions)) { @@ -3578,6 +3610,19 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid ); } + if (caster->IsBot() && RuleB(Bots, BotsUseLiveBlockedMessage) && caster->GetClass() != Class::Bard) { + caster->GetOwner()->Message( + Chat::Red, + fmt::format( + "{}'s {} did not take hold on {}. (Blocked by {}.)", + caster->GetCleanName(), + spells[spell_id].name, + GetName(), + spells[curbuf.spellid].name + ).c_str() + ); + } + std::function f = [&]() { return fmt::format( "{} {}", @@ -3772,9 +3817,15 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) continue; } - if(curbuf.spellid == spellid) - return(-1); //do not recast a buff we already have on, we recast fast enough that we dont need to refresh our buffs - + if (IsBot() && (GetClass() == Class::Bard) && curbuf.spellid == spellid && curbuf.ticsremaining == 0 && curbuf.casterid == GetID()) { + LogAI("Bard check for song, spell [{}] has [{}] ticks remaining.", spellid, curbuf.ticsremaining); + firstfree = i; + return firstfree; + } + else { + if (curbuf.spellid == spellid) + return(-1); //do not recast a buff we already have on, we recast fast enough that we dont need to refresh our buffs + } // there's a buff in this slot ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spellid, caster_level, nullptr, nullptr, i); if(ret == 1) { @@ -4424,6 +4475,18 @@ bool Mob::SpellOnTarget( } else { MessageString(Chat::SpellFailure, TARGET_RESISTED, spells[spell_id].name); spelltar->MessageString(Chat::SpellFailure, YOU_RESIST, spells[spell_id].name); + + if (IsBot() && RuleB(Bots, ShowResistMessagesToOwner)) { + CastToBot()->GetBotOwner()->Message + (Chat::SpellFailure, + fmt::format( + "{} resisted {}'s spell: {}.", + spelltar->GetCleanName(), + GetCleanName(), + spells[spell_id].name + ).c_str() + ); + } } if (spelltar->IsAIControlled()) { @@ -4642,6 +4705,14 @@ bool Mob::SpellOnTarget( LogSpells("Cast of [{}] by [{}] on [{}] complete successfully", spell_id, GetName(), spelltar->GetName()); + if (IsBot() && (CastToBot()->GetCastedSpellType() != UINT16_MAX)) { + if (!CastToBot()->IsCommandedSpell()) { + CastToBot()->SetBotSpellRecastTimer(CastToBot()->GetCastedSpellType(), spelltar); + } + + CastToBot()->SetCastedSpellType(UINT16_MAX); + } + return true; } @@ -7444,3 +7515,123 @@ bool Mob::CheckWaterLoS(Mob* m) zone->watermap->InLiquid(m->GetPosition()) ); } + +bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) +{ + int effect_index; + + if (caster == nullptr) + return(false); + + //TODO: this function loops through the effect list for + //this spell like 10 times, this could easily be consolidated + //into one loop through with a switch statement. + + LogSpells("Checking to see if we are immune to spell [{}] cast by [{}]", spell_id, caster->GetName()); + + if (!IsValidSpell(spell_id)) + return true; + + if (IsBeneficialSpell(spell_id) && (caster->GetNPCTypeID())) //then skip the rest, stop NPCs aggroing each other with buff spells. 2013-03-05 + return false; + + if (IsMesmerizeSpell(spell_id)) + { + if (GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { + return true; + } + + // check max level for spell + effect_index = GetSpellEffectIndex(spell_id, SE_Mez); + assert(effect_index >= 0); + // NPCs get to ignore the max level + if ((GetLevel() > spells[spell_id].max_value[effect_index]) && + (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))) + { + return true; + } + } + + // slow and haste spells + if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) + { + return true; + } + + // client vs client fear + if (IsEffectInSpell(spell_id, SE_Fear)) + { + effect_index = GetSpellEffectIndex(spell_id, SE_Fear); + if (GetSpecialAbility(SpecialAbility::FearImmunity)) { + return true; + } + else if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) + { + LogSpells("Clients cannot fear eachother!"); + caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up + return true; + } + else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) + { + return true; + } + else if (CheckAATimer(aaTimerWarcry)) + { + return true; + } + } + + if (IsCharmSpell(spell_id)) + { + if (GetSpecialAbility(SpecialAbility::CharmImmunity)) + { + return true; + } + + if (this == caster) + { + return true; + } + + //let npcs cast whatever charm on anyone + if (!caster->IsNPC()) + { + // check level limit of charm spell + effect_index = GetSpellEffectIndex(spell_id, SE_Charm); + assert(effect_index >= 0); + if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) + { + return true; + } + } + } + + if + ( + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) + { + if (GetSpecialAbility(SpecialAbility::SnareImmunity)) { + return true; + } + } + + if (IsLifetapSpell(spell_id)) + { + if (this == caster) + { + return true; + } + } + + if (IsSacrificeSpell(spell_id)) + { + if (this == caster) + { + return true; + } + } + + return false; +} From 4aa7a18b4fb99fa30147b705cde06ae7f8d19f62 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:32:16 -0500 Subject: [PATCH 002/394] More fixes TGB, ^cast, group/ae checks, in group/raid checks, inviting others bots to group, group disband fix, prevent rogue bs spam, ^follow fixes and cleanup, follow owner only by default when joining raid/group, group buff fixes for bots, range fixes for group buffs --- common/ruletypes.h | 4 +- common/spdat.cpp | 76 ++- common/spdat.h | 3 + zone/bot.cpp | 891 +++++++++++++++----------------- zone/bot.h | 8 +- zone/bot_commands/bot.cpp | 23 +- zone/bot_commands/cast.cpp | 12 +- zone/bot_commands/follow.cpp | 250 ++++++--- zone/bot_commands/item_use.cpp | 8 +- zone/bot_commands/mesmerize.cpp | 2 +- zone/bot_raid.cpp | 12 +- zone/botspellsai.cpp | 73 ++- zone/groups.cpp | 138 ++++- zone/groups.h | 2 + zone/mob.cpp | 158 +++--- zone/mob.h | 1 + zone/spells.cpp | 48 +- 17 files changed, 1046 insertions(+), 663 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index aec9bcb8b1..576d5e4744 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -830,7 +830,7 @@ RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will b RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") -RULE_INT(Bots, StackSizeMin, 100, "100 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") +RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") @@ -865,6 +865,8 @@ RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Def RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass the anti-spam system") RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. Default 120.") RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") +RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.") +RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/common/spdat.cpp b/common/spdat.cpp index 53bf03f6d3..48b6351fb2 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2782,15 +2782,16 @@ int8 SpellEffectsCount(uint16 spell_id) { if (!IsValidSpell(spell_id)) { return false; } - int8 i = 0; + + int8 x = 0; for (int i = 0; i < EFFECT_COUNT; i++) { if (!IsBlankSpellEffect(spell_id, i)) { - ++i; + ++x; } } - return i; + return x; } bool IsLichSpell(uint16 spell_id) @@ -3175,3 +3176,72 @@ bool RequiresStackCheck(uint16 spellType) { return true; } + +bool IsResistanceOnlySpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (IsBlankSpellEffect(spell_id, i)) { + continue; + } + + if ( + spell.effect_id[i] == SE_ResistFire || + spell.effect_id[i] == SE_ResistCold || + spell.effect_id[i] == SE_ResistPoison || + spell.effect_id[i] == SE_ResistDisease || + spell.effect_id[i] == SE_ResistMagic || + spell.effect_id[i] == SE_ResistCorruption || + spell.effect_id[i] == SE_ResistAll + ) { + continue; + } + + return false; + } + + return true; +} + +bool IsDamageShieldOnlySpell(uint16 spell_id) { + if (SpellEffectsCount(spell_id) == 1 && IsEffectInSpell(spell_id, SE_DamageShield)) { + return true; + } + + return false; +} + +bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (IsBlankSpellEffect(spell_id, i)) { + continue; + } + + if ( + spell.effect_id[i] == SE_DamageShield || + spell.effect_id[i] == SE_ResistFire || + spell.effect_id[i] == SE_ResistCold || + spell.effect_id[i] == SE_ResistPoison || + spell.effect_id[i] == SE_ResistDisease || + spell.effect_id[i] == SE_ResistMagic || + spell.effect_id[i] == SE_ResistCorruption || + spell.effect_id[i] == SE_ResistAll + ) { + continue; + } + + return false; + } + + return true; +} diff --git a/common/spdat.h b/common/spdat.h index 432774b95b..b07bf7d966 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1722,5 +1722,8 @@ bool IsLichSpell(uint16 spell_id); bool IsInstantHealSpell(uint32 spell_id); bool IsResurrectSpell(uint16 spell_id); bool RequiresStackCheck(uint16 spellType); +bool IsResistanceOnlySpell(uint16 spell_id); +bool IsDamageShieldOnlySpell(uint16 spell_id); +bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id); #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index f634eb43e7..16f4b9db3d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -87,6 +87,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -210,6 +211,7 @@ Bot::Bot( SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -268,146 +270,146 @@ Bot::Bot( for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effect_id[x1]) { - case SE_IllusionCopy: - case SE_Illusion: { - if (GetIllusionBlock()) { - break; - } - - if (spell.base_value[x1] == -1) { - if (gender == Gender::Female) { - gender = Gender::Male; - } else if (gender == Gender::Male) { - gender = Gender::Female; + case SE_IllusionCopy: + case SE_Illusion: { + if (GetIllusionBlock()) { + break; } - SendIllusionPacket( - AppearanceStruct{ - .gender_id = gender, - .race_id = GetRace(), + if (spell.base_value[x1] == -1) { + if (gender == Gender::Female) { + gender = Gender::Male; + } else if (gender == Gender::Male) { + gender = Gender::Female; } - ); - } else if (spell.base_value[x1] == -2) // WTF IS THIS - { - if (GetRace() == IKSAR || GetRace() == VAHSHIR || GetRace() <= GNOME) { + SendIllusionPacket( AppearanceStruct{ - .gender_id = GetGender(), - .helmet_texture = static_cast(spell.max_value[x1]), + .gender_id = gender, .race_id = GetRace(), + } + ); + } else if (spell.base_value[x1] == -2) // WTF IS THIS + { + if (GetRace() == IKSAR || GetRace() == VAHSHIR || GetRace() <= GNOME) { + SendIllusionPacket( + AppearanceStruct{ + .gender_id = GetGender(), + .helmet_texture = static_cast(spell.max_value[x1]), + .race_id = GetRace(), + .texture = static_cast(spell.limit_value[x1]), + } + ); + } + } else if (spell.max_value[x1] > 0) { + SendIllusionPacket( + AppearanceStruct{ + .helmet_texture = static_cast(spell.max_value[x1]), + .race_id = static_cast(spell.base_value[x1]), + .texture = static_cast(spell.limit_value[x1]), + } + ); + } else { + SendIllusionPacket( + AppearanceStruct{ + .helmet_texture = static_cast(spell.max_value[x1]), + .race_id = static_cast(spell.base_value[x1]), .texture = static_cast(spell.limit_value[x1]), } ); } - } else if (spell.max_value[x1] > 0) { - SendIllusionPacket( - AppearanceStruct{ - .helmet_texture = static_cast(spell.max_value[x1]), - .race_id = static_cast(spell.base_value[x1]), - .texture = static_cast(spell.limit_value[x1]), - } - ); - } else { - SendIllusionPacket( - AppearanceStruct{ - .helmet_texture = static_cast(spell.max_value[x1]), - .race_id = static_cast(spell.base_value[x1]), - .texture = static_cast(spell.limit_value[x1]), - } - ); - } - switch (spell.base_value[x1]) { - case OGRE: - SendAppearancePacket(AppearanceType::Size, 9); + switch (spell.base_value[x1]) { + case OGRE: + SendAppearancePacket(AppearanceType::Size, 9); + break; + case TROLL: + SendAppearancePacket(AppearanceType::Size, 8); + break; + case VAHSHIR: + case BARBARIAN: + SendAppearancePacket(AppearanceType::Size, 7); + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + SendAppearancePacket(AppearanceType::Size, 5); + break; + case DWARF: + SendAppearancePacket(AppearanceType::Size, 4); + break; + case HALFLING: + case GNOME: + SendAppearancePacket(AppearanceType::Size, 3); + break; + default: + SendAppearancePacket(AppearanceType::Size, 6); + break; + } break; - case TROLL: - SendAppearancePacket(AppearanceType::Size, 8); + } + case SE_Silence: + { + Silence(true); break; - case VAHSHIR: - case BARBARIAN: - SendAppearancePacket(AppearanceType::Size, 7); + } + case SE_Amnesia: + { + Amnesia(true); break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - SendAppearancePacket(AppearanceType::Size, 5); + } + case SE_DivineAura: + { + invulnerable = true; break; - case DWARF: - SendAppearancePacket(AppearanceType::Size, 4); + } + case SE_Invisibility2: + case SE_Invisibility: + { + invisible = true; + SendAppearancePacket(AppearanceType::Invisibility, 1); break; - case HALFLING: - case GNOME: - SendAppearancePacket(AppearanceType::Size, 3); + } + case SE_Levitate: + { + if (!zone->CanLevitate()) + { + SendAppearancePacket(AppearanceType::FlyMode, 0); + BuffFadeByEffect(SE_Levitate); + } + else { + SendAppearancePacket(AppearanceType::FlyMode, 2); + } break; - default: - SendAppearancePacket(AppearanceType::Size, 6); + } + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = true; break; } - break; - } - case SE_Silence: - { - Silence(true); - break; - } - case SE_Amnesia: - { - Amnesia(true); - break; - } - case SE_DivineAura: - { - invulnerable = true; - break; - } - case SE_Invisibility2: - case SE_Invisibility: - { - invisible = true; - SendAppearancePacket(AppearanceType::Invisibility, 1); - break; - } - case SE_Levitate: - { - if (!zone->CanLevitate()) + case SE_InvisVsAnimals: { - SendAppearancePacket(AppearanceType::FlyMode, 0); - BuffFadeByEffect(SE_Levitate); + invisible_animals = true; + break; } - else { - SendAppearancePacket(AppearanceType::FlyMode, 2); + case SE_AddMeleeProc: + case SE_WeaponProc: + { + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel); + break; + } + case SE_DefensiveProc: + { + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); + break; + } + case SE_RangedProc: + { + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); + break; } - break; - } - case SE_InvisVsUndead2: - case SE_InvisVsUndead: - { - invisible_undead = true; - break; - } - case SE_InvisVsAnimals: - { - invisible_animals = true; - break; - } - case SE_AddMeleeProc: - case SE_WeaponProc: - { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel); - break; - } - case SE_DefensiveProc: - { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); - break; - } - case SE_RangedProc: - { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); - break; - } } } } @@ -1794,7 +1796,9 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { ) ) { if (!Ammo || ammoItem->GetCharges() < 1) { - GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + if (!GetCombatRoundForAlerts()) { + GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + } } return; @@ -1914,20 +1918,20 @@ bool Bot::CheckTripleAttack() ) ) { switch (GetClass()) { - case Class::Warrior: - chance = RuleI(Combat, ClassicTripleAttackChanceWarrior); - break; - case Class::Ranger: - chance = RuleI(Combat, ClassicTripleAttackChanceRanger); - break; - case Class::Monk: - chance = RuleI(Combat, ClassicTripleAttackChanceMonk); - break; - case Class::Berserker: - chance = RuleI(Combat, ClassicTripleAttackChanceBerserker); - break; - default: - break; + case Class::Warrior: + chance = RuleI(Combat, ClassicTripleAttackChanceWarrior); + break; + case Class::Ranger: + chance = RuleI(Combat, ClassicTripleAttackChanceRanger); + break; + case Class::Monk: + chance = RuleI(Combat, ClassicTripleAttackChanceMonk); + break; + case Class::Berserker: + chance = RuleI(Combat, ClassicTripleAttackChanceBerserker); + break; + default: + break; } } } @@ -2011,15 +2015,26 @@ void Bot::AI_Process() return; } - auto leash_owner = SetLeashOwner(bot_owner, bot_group, raid, r_group); + Client* leash_owner = bot_owner; if (!leash_owner) { return; } - SetFollowID(leash_owner->GetID()); + Mob* follow_mob = nullptr; - auto follow_mob = SetFollowMob(leash_owner); + if (!GetFollowID()) { + follow_mob = leash_owner; + } + else { + follow_mob = entity_list.GetMob(GetFollowID()); + + if (!follow_mob || !IsInGroupOrRaid(follow_mob)) { + follow_mob = leash_owner; + } + } + + SetFollowID(follow_mob->GetID()); SetBerserkState(); @@ -2091,6 +2106,7 @@ void Bot::AI_Process() // ATTACKING FLAG (HATE VALIDATION) if (GetAttackingFlag() && tar->CheckAggro(this)) { + SetCombatRoundForAlerts(true); SetAttackingFlag(false); } @@ -2261,6 +2277,7 @@ void Bot::AI_Process() } else { // Out-of-combat behavior SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { @@ -2405,6 +2422,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { AddToHateList(hater, 1); SetTarget(hater); SetAttackingFlag(); + SetCombatRoundForAlerts(); if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->AddToHateList(hater, 1); @@ -2866,6 +2884,7 @@ bool Bot::IsValidTarget( SetTarget(nullptr); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (PULLING_BOT) { @@ -2899,6 +2918,7 @@ Mob* Bot::GetBotTarget(Client* bot_owner) WipeHateList(); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (PULLING_BOT) { @@ -3117,6 +3137,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { } SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -3131,6 +3152,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { WipeHateList(); AddToHateList(attack_target, 1); SetTarget(attack_target); + SetCombatRoundForAlerts(); SetAttackingFlag(); if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->WipeHateList(); @@ -3143,6 +3165,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -3834,7 +3857,6 @@ bool Bot::AddBotToGroup(Bot* bot, Group* group) { if (bot && group->AddMember(bot)) { if (group->GetLeader()) { - bot->SetFollowID(group->GetLeader()->GetID()); // Need to send this only once when a group is formed with a bot so the client knows it is also the group leader if (group->GroupCount() == 2 && group->GetLeader()->IsClient()) { group->UpdateGroupAAs(); @@ -4631,15 +4653,15 @@ float Bot::GetProcChances(float ProcBonus, uint16 hand) { float ProcChance = 0.0f; uint32 weapon_speed = 0; switch (hand) { - case EQ::invslot::slotPrimary: - weapon_speed = attack_timer.GetDuration(); - break; - case EQ::invslot::slotSecondary: - weapon_speed = attack_dw_timer.GetDuration(); - break; - case EQ::invslot::slotRange: - weapon_speed = ranged_timer.GetDuration(); - break; + case EQ::invslot::slotPrimary: + weapon_speed = attack_timer.GetDuration(); + break; + case EQ::invslot::slotSecondary: + weapon_speed = attack_dw_timer.GetDuration(); + break; + case EQ::invslot::slotRange: + weapon_speed = ranged_timer.GetDuration(); + break; } if (weapon_speed < RuleI(Combat, MinHastedDelay)) @@ -4748,84 +4770,84 @@ int Bot::GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target) int base = EQ::skills::GetBaseDamage(skill); auto skill_level = GetSkill(skill); switch (skill) { - case EQ::skills::SkillDragonPunch: - case EQ::skills::SkillEagleStrike: - case EQ::skills::SkillTigerClaw: - if (skill_level >= 25) - base++; - if (skill_level >= 75) - base++; - if (skill_level >= 125) - base++; - if (skill_level >= 175) - base++; - return base; - case EQ::skills::SkillFrenzy: - if (GetBotItem(EQ::invslot::slotPrimary)) { - if (GetLevel() > 15) - base += GetLevel() - 15; - if (base > 23) - base = 23; - if (GetLevel() > 50) - base += 2; - if (GetLevel() > 54) + case EQ::skills::SkillDragonPunch: + case EQ::skills::SkillEagleStrike: + case EQ::skills::SkillTigerClaw: + if (skill_level >= 25) base++; - if (GetLevel() > 59) + if (skill_level >= 75) base++; + if (skill_level >= 125) + base++; + if (skill_level >= 175) + base++; + return base; + case EQ::skills::SkillFrenzy: + if (GetBotItem(EQ::invslot::slotPrimary)) { + if (GetLevel() > 15) + base += GetLevel() - 15; + if (base > 23) + base = 23; + if (GetLevel() > 50) + base += 2; + if (GetLevel() > 54) + base++; + if (GetLevel() > 59) + base++; + } + return base; + case EQ::skills::SkillFlyingKick: { + float skill_bonus = skill_level / 9.0f; + float ac_bonus = 0.0f; + auto inst = GetBotItem(EQ::invslot::slotFeet); + if (inst) + ac_bonus = inst->GetItemArmorClass(true) / 25.0f; + if (ac_bonus > skill_bonus) + ac_bonus = skill_bonus; + return static_cast(ac_bonus + skill_bonus); + } + case EQ::skills::SkillKick: { + float skill_bonus = skill_level / 10.0f; + float ac_bonus = 0.0f; + auto inst = GetBotItem(EQ::invslot::slotFeet); + if (inst) + ac_bonus = inst->GetItemArmorClass(true) / 25.0f; + if (ac_bonus > skill_bonus) + ac_bonus = skill_bonus; + return static_cast(ac_bonus + skill_bonus); + } + case EQ::skills::SkillBash: { + float skill_bonus = skill_level / 10.0f; + float ac_bonus = 0.0f; + const EQ::ItemInstance *inst = nullptr; + if (HasShieldEquipped()) + inst = GetBotItem(EQ::invslot::slotSecondary); + else if (HasTwoHanderEquipped()) + inst = GetBotItem(EQ::invslot::slotPrimary); + if (inst) + ac_bonus = inst->GetItemArmorClass(true) / 25.0f; + if (ac_bonus > skill_bonus) + ac_bonus = skill_bonus; + return static_cast(ac_bonus + skill_bonus); + } + case EQ::skills::SkillBackstab: { + float skill_bonus = static_cast(skill_level) * 0.02f; + auto inst = GetBotItem(EQ::invslot::slotPrimary); + if (inst && inst->GetItem() && inst->GetItem()->ItemType == EQ::item::ItemType1HPiercing) { + base = inst->GetItemBackstabDamage(true); + if (!inst->GetItemBackstabDamage()) + base += inst->GetItemWeaponDamage(true); + if (target) { + if (inst->GetItemElementalFlag(true) && inst->GetItemElementalDamage(true)) + base += target->ResistElementalWeaponDmg(inst); + if (inst->GetItemBaneDamageBody(true) || inst->GetItemBaneDamageRace(true)) + base += target->CheckBaneDamage(inst); + } + } + return static_cast(static_cast(base) * (skill_bonus + 2.0f)); } - return base; - case EQ::skills::SkillFlyingKick: { - float skill_bonus = skill_level / 9.0f; - float ac_bonus = 0.0f; - auto inst = GetBotItem(EQ::invslot::slotFeet); - if (inst) - ac_bonus = inst->GetItemArmorClass(true) / 25.0f; - if (ac_bonus > skill_bonus) - ac_bonus = skill_bonus; - return static_cast(ac_bonus + skill_bonus); - } - case EQ::skills::SkillKick: { - float skill_bonus = skill_level / 10.0f; - float ac_bonus = 0.0f; - auto inst = GetBotItem(EQ::invslot::slotFeet); - if (inst) - ac_bonus = inst->GetItemArmorClass(true) / 25.0f; - if (ac_bonus > skill_bonus) - ac_bonus = skill_bonus; - return static_cast(ac_bonus + skill_bonus); - } - case EQ::skills::SkillBash: { - float skill_bonus = skill_level / 10.0f; - float ac_bonus = 0.0f; - const EQ::ItemInstance *inst = nullptr; - if (HasShieldEquipped()) - inst = GetBotItem(EQ::invslot::slotSecondary); - else if (HasTwoHanderEquipped()) - inst = GetBotItem(EQ::invslot::slotPrimary); - if (inst) - ac_bonus = inst->GetItemArmorClass(true) / 25.0f; - if (ac_bonus > skill_bonus) - ac_bonus = skill_bonus; - return static_cast(ac_bonus + skill_bonus); - } - case EQ::skills::SkillBackstab: { - float skill_bonus = static_cast(skill_level) * 0.02f; - auto inst = GetBotItem(EQ::invslot::slotPrimary); - if (inst && inst->GetItem() && inst->GetItem()->ItemType == EQ::item::ItemType1HPiercing) { - base = inst->GetItemBackstabDamage(true); - if (!inst->GetItemBackstabDamage()) - base += inst->GetItemWeaponDamage(true); - if (target) { - if (inst->GetItemElementalFlag(true) && inst->GetItemElementalDamage(true)) - base += target->ResistElementalWeaponDmg(inst); - if (inst->GetItemBaneDamageBody(true) || inst->GetItemBaneDamageRace(true)) - base += target->CheckBaneDamage(inst); - } - } - return static_cast(static_cast(base) * (skill_bonus + 2.0f)); - } - default: - return 0; + default: + return 0; } } @@ -4892,7 +4914,10 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) { botpiercer = inst->GetItem(); if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { - BotGroupSay(this, "I can't backstab with this weapon!"); + if (!GetCombatRoundForAlerts()) { + BotGroupSay(this, "I can't backstab with this weapon!"); + } + return; } @@ -5828,7 +5853,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe GetClass() != Class::Bard && (IsGrouped() || (IsRaidGrouped() && GetRaid()->GetGroup(GetCleanName()) != RAID_GROUPLESS)) && (spellTarget->IsBot() || spellTarget->IsClient()) && - (RuleB(Bots, GroupBuffing) || RuleB(Bots, CrossRaidBuffingAndHealing)) + (RuleB(Bots, GroupBuffing) || RuleB(Bots, RaidBuffing)) ) { bool noGroupSpell = false; uint16 thespell = spell_id; @@ -5857,7 +5882,8 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe if (!noGroupSpell) { std::vector v; - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + + if (RuleB(Bots, RaidBuffing)) { v = GatherSpellTargets(true); } else { @@ -5916,8 +5942,20 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel } if (spellTarget->IsOfClientBotMerc()) { - const std::vector v = GatherGroupSpellTargets(spellTarget); + std::vector v; + + if (RuleB(Bots, RaidBuffing)) { + v = GatherSpellTargets(true); + } + else { + v = GatherGroupSpellTargets(spellTarget); + } + for (Mob* m : v) { + if (m == this && spellTarget != this) { + continue; + } + SpellOnTarget(spell_id, m); if (m->GetPetID() && (!RuleB(Bots, RequirePetAffinity) || m->HasPetAffinity())) { @@ -6605,40 +6643,6 @@ void Bot::DoEnduranceUpkeep() { } void Bot::Camp(bool save_to_database) { - - if (RuleB(Bots, PreventBotCampOnFD) && GetBotOwner()->GetFeigned()) { - GetBotOwner()->Message(Chat::White, "You cannot camp bots while feigned."); - return; - } - - if (RuleB(Bots, PreventBotCampOnEngaged)) { - Raid* raid = entity_list.GetRaidByClient(GetBotOwner()->CastToClient()); - if (raid && raid->IsEngaged()) { - GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); - return; - } - - auto* owner_group = GetBotOwner()->GetGroup(); - if (owner_group) { - std::list member_list; - owner_group->GetClientList(member_list); - member_list.remove(nullptr); - - for (auto member_iter : member_list) { - if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { - GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); - return; - } - } - } - else { - if (GetBotOwner()->CastToClient()->GetAggroCount() > 0) { - GetBotOwner()->Message(Chat::White, "You cannot spawn bots while you are engaged,"); - return; - } - } - } - Sit(); LeaveHealRotationMemberPool(); @@ -6710,107 +6714,107 @@ void Bot::UpdateGroupCastingRoles(const Group* group, bool disband) // GroupHealer switch (iter->GetClass()) { - case Class::Cleric: - if (!healer) - healer = iter; - else - switch (healer->GetClass()) { - case Class::Cleric: - break; - default: + case Class::Cleric: + if (!healer) healer = iter; - } + else + switch (healer->GetClass()) { + case Class::Cleric: + break; + default: + healer = iter; + } - break; - case Class::Druid: - if (!healer) - healer = iter; - else - switch (healer->GetClass()) { - case Class::Cleric: - case Class::Druid: - break; - default: + break; + case Class::Druid: + if (!healer) healer = iter; - } - break; - case Class::Shaman: - if (!healer) - healer = iter; - else - switch (healer->GetClass()) { - case Class::Cleric: - case Class::Druid: - case Class::Shaman: - break; - default: + else + switch (healer->GetClass()) { + case Class::Cleric: + case Class::Druid: + break; + default: + healer = iter; + } + break; + case Class::Shaman: + if (!healer) healer = iter; - } - break; - case Class::Paladin: - case Class::Ranger: - case Class::Beastlord: - if (!healer) - healer = iter; - break; - default: - break; - } + else + switch (healer->GetClass()) { + case Class::Cleric: + case Class::Druid: + case Class::Shaman: + break; + default: + healer = iter; + } + break; + case Class::Paladin: + case Class::Ranger: + case Class::Beastlord: + if (!healer) + healer = iter; + break; + default: + break; + } - // GroupSlower - switch (iter->GetClass()) { - case Class::Shaman: - if (!slower) - slower = iter; - else - switch (slower->GetClass()) { + // GroupSlower + switch (iter->GetClass()) { case Class::Shaman: + if (!slower) + slower = iter; + else + switch (slower->GetClass()) { + case Class::Shaman: + break; + default: + slower = iter; + } break; - default: - slower = iter; - } - break; - case Class::Enchanter: - if (!slower) - slower = iter; - else - switch (slower->GetClass()) { - case Class::Shaman: case Class::Enchanter: + if (!slower) + slower = iter; + else + switch (slower->GetClass()) { + case Class::Shaman: + case Class::Enchanter: + break; + default: + slower = iter; + } + break; + case Class::Beastlord: + if (!slower) + slower = iter; break; default: - slower = iter; - } - break; - case Class::Beastlord: - if (!slower) - slower = iter; - break; - default: - break; - } + break; + } - // GroupNuker - switch (iter->GetClass()) { - // wizard - // magician - // necromancer - // enchanter - // druid - // cleric - // shaman - // shadowknight - // paladin - // ranger - // beastlord - default: - break; - } + // GroupNuker + switch (iter->GetClass()) { + // wizard + // magician + // necromancer + // enchanter + // druid + // cleric + // shaman + // shadowknight + // paladin + // ranger + // beastlord + default: + break; + } - // GroupDoter - switch (iter->GetClass()) { - default: - break; + // GroupDoter + switch (iter->GetClass()) { + default: + break; } } @@ -6838,11 +6842,26 @@ Bot* Bot::GetBotByBotClientOwnerAndBotName(Client* c, const std::string& botName void Bot::ProcessBotGroupInvite(Client* c, std::string const& botName) { if (c && !c->HasRaid()) { - Bot* invitedBot = GetBotByBotClientOwnerAndBotName(c, botName); + Bot* invitedBot = entity_list.GetBotByBotName(botName); + if (!invitedBot) { return; } + if ( + invitedBot->GetBotOwnerCharacterID() != c->CharacterID() && + !( + c->GetGroup() && + !invitedBot->HasGroup() && + invitedBot->GetOwner()->HasGroup() && + c->GetGroup() == invitedBot->GetOwner()->GetGroup() + ) + ) { + c->Message(Chat::Red, "%s's owner needs to be in your group to be able to invite them.", invitedBot->GetCleanName()); + + return; + } + if (!invitedBot->HasGroup() && !invitedBot->HasRaid()) { if (!c->IsGrouped()) { auto g = new Group(c); @@ -7084,7 +7103,6 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl else { v = caster->GatherGroupSpellTargets(); } - v = caster->GatherSpellTargets(); } Mob* tar = nullptr; @@ -8443,17 +8461,19 @@ int32 Bot::CalcItemATKCap() return RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; } -bool Bot::CheckSpawnConditions(Client* c) { +bool Bot::CheckCampSpawnConditions(Client* c) { if (RuleB(Bots, PreventBotSpawnOnFD) && c->GetFeigned()) { - c->Message(Chat::White, "You cannot spawn bots while feigned."); + c->Message(Chat::White, "You cannot camp or spawn bots while feigned."); + return false; } if (RuleB(Bots, PreventBotSpawnOnEngaged)) { Raid* raid = entity_list.GetRaidByClient(c); if (raid && raid->IsEngaged()) { - c->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); + c->Message(Chat::White, "You cannot camp or spawn bots while your raid is engaged."); + return false; } @@ -8465,14 +8485,14 @@ bool Bot::CheckSpawnConditions(Client* c) { for (auto member_iter : member_list) { if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); + c->Message(Chat::White, "You cannot camp or spawn bots while your group is engaged,"); return false; } } } else { if (c->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while you are engaged,"); + c->Message(Chat::White, "You cannot camp or spawn bots while you are engaged,"); return false; } } @@ -9407,6 +9427,10 @@ bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrecheck return false; } + if (IsBeneficialSpell(spellid) && tar->BuffCount() >= tar->GetCurrentBuffSlots() && CalcBuffDuration(this, tar, spellid) != 0) { + return false; + } + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme if (!CanCastSpellType(spellType, spellid, tar)) { return false; @@ -9436,8 +9460,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { spells[spellid].target_type == ST_Pet || (tar == this && spells[spellid].target_type != ST_TargetsTarget) || spells[spellid].target_type == ST_Group || - spells[spellid].target_type == ST_GroupTeleport //|| - //(botClass == Class::Bard && spells[spellid].target_type == ST_AEBard) //TODO bot rewrite - is this needed? + spells[spellid].target_type == ST_GroupTeleport ) ) { LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme @@ -9481,9 +9504,9 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) - ) - ) { + (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + ) + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; } @@ -9526,18 +9549,6 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: - //switch (spells[spellid].target_type) { //TODO bot rewrite - is this needed? - // case ST_AEBard: - // case ST_AECaster: - // case ST_GroupTeleport: - // case ST_Group: - // case ST_Self: - // break; - // default: - // LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme - // return false; - //} - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; @@ -9550,9 +9561,9 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) - ) - ) { + (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + ) + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; } @@ -9607,7 +9618,15 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { return true; } - const std::vector v = GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = GatherSpellTargets(true); + } + else { + v = GatherGroupSpellTargets(); + } + for (Mob* m : v) { if ( m->IsBot() && @@ -9628,9 +9647,10 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { m->CastingSpellID() == spellid ) { - const std::vector v = GatherGroupSpellTargets(); - for (Mob* m : v) { - if (entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(m->GetID())) { + std::vector x = GatherGroupSpellTargets(); + + for (Mob* t : x) { + if (entity_list.GetMobID(t->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(t->GetID())) { return true; } } @@ -10273,61 +10293,57 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { case BotSpellTypes::Cure: priority = 10; - break; - case BotSpellTypes::InCombatBuff: // this has a check at the end to decrement everything below if it's not a SK (SK use InCombatBuffs as it is their hate line so it's not use when Idle) - priority = 11; - break; case BotSpellTypes::PetVeryFastHeals: - priority = 12; + priority = 11; break; case BotSpellTypes::PetFastHeals: - priority = 13; + priority = 12; break; case BotSpellTypes::PetRegularHeals: - priority = 14; + priority = 13; break; case BotSpellTypes::PetCompleteHeals: - priority = 15; + priority = 14; break; case BotSpellTypes::PetHoTHeals: - priority = 16; + priority = 15; break; case BotSpellTypes::Pet: - priority = 17; + priority = 16; break; case BotSpellTypes::Buff: - priority = 18; + priority = 17; break; case BotSpellTypes::OutOfCombatBuffSong: - priority = 19; + priority = 18; break; case BotSpellTypes::ResistBuffs: - priority = 20; + priority = 19; break; case BotSpellTypes::DamageShields: - priority = 21; + priority = 20; break; case BotSpellTypes::PetBuffs: - priority = 22; + priority = 21; break; case BotSpellTypes::PreCombatBuff: - priority = 23; + priority = 22; break; case BotSpellTypes::PreCombatBuffSong: - priority = 24; + priority = 23; break; default: @@ -10336,14 +10352,6 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { break; } - if ( - priority >= 11 && - botClass && botClass == Class::ShadowKnight && - spellType != BotSpellTypes::InCombatBuff - ) { - --priority; - } - return priority; } @@ -10849,39 +10857,19 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { switch (spellType) { //TODO bot rewrite - fix Buff/ResistBuff case BotSpellTypes::Buff: - if (IsEffectInSpell(spellid, SE_DamageShield)) { - return false; - } - - if ( - IsEffectInSpell(spellid, SE_ResistMagic) || - IsEffectInSpell(spellid, SE_ResistFire) || - IsEffectInSpell(spellid, SE_ResistCold) || - IsEffectInSpell(spellid, SE_ResistPoison) || - IsEffectInSpell(spellid, SE_ResistDisease) || - IsEffectInSpell(spellid, SE_ResistCorruption) || - IsEffectInSpell(spellid, SE_ResistAll) - ) { + if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return false; } return true; case BotSpellTypes::ResistBuffs: - if ( - IsEffectInSpell(spellid, SE_ResistMagic) || - IsEffectInSpell(spellid, SE_ResistFire) || - IsEffectInSpell(spellid, SE_ResistCold) || - IsEffectInSpell(spellid, SE_ResistPoison) || - IsEffectInSpell(spellid, SE_ResistDisease) || - IsEffectInSpell(spellid, SE_ResistCorruption) || - IsEffectInSpell(spellid, SE_ResistAll) - ) { + if (IsResistanceOnlySpell(spellid)) { return true; } return false; case BotSpellTypes::DamageShields: - if (IsEffectInSpell(spellid, SE_DamageShield)) { + if (IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return true; } @@ -11096,55 +11084,6 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } -bool Bot::IsInGroupOrRaid(bool announce) { - if (!GetOwner()) { - return false; - } - - Mob* c = GetOwner(); - - if ( - (!GetRaid() && !GetGroup()) || - (!c->GetRaid() && !c->GetGroup()) - ) { - return false; - } - - if ( - c->GetRaid() && - ( - !GetRaid() || - c->GetRaid() != GetRaid() || - GetRaid()->GetGroup(GetCleanName()) == RAID_GROUPLESS - ) - ) { - return false; - } - - if ( - c->GetGroup() && - ( - !GetGroup() || - c->GetGroup() != GetGroup() - ) - ) { - return false; - } - - - if (announce) { - c->Message( - Chat::Yellow, - fmt::format( - "{} says, 'I am not currently in your group or raid.", - GetCleanName() - ).c_str() - ); - } - - return true; -} - bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar) { int spellRange = botCaster->GetActSpellRange(spellid, spells[spellid].range); int spellAERange = botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range); diff --git a/zone/bot.h b/zone/bot.h index 97d987e567..5836af1335 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -278,6 +278,7 @@ class Bot : public NPC { void SetHoldFlag(bool flag = true) { m_hold_flag = flag; } bool GetAttackFlag() const { return m_attack_flag; } void SetAttackFlag(bool flag = true) { m_attack_flag = flag; } + bool GetCombatRoundForAlerts() const { return m_combat_round_alert_flag; } bool GetAttackingFlag() const { return m_attacking_flag; } bool GetPullFlag() const { return m_pull_flag; } void SetPullFlag(bool flag = true) { m_pull_flag = flag; } @@ -403,7 +404,7 @@ class Bot : public NPC { void SetGuardMode(); void SetHoldMode(); - bool IsValidSpellRange(uint16 spell_id, Mob const* tar); + bool IsValidSpellRange(uint16 spell_id, Mob* tar); // Bot AI Methods void AI_Bot_Init(); @@ -519,7 +520,6 @@ class Bot : public NPC { void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } bool HasLoS() const { return _hasLoS; } - bool IsInGroupOrRaid(bool announce = false); void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false); std::list GetSpellTypesPrioritized(uint8 priorityType); @@ -981,7 +981,7 @@ class Bot : public NPC { bool CheckDoubleRangedAttack(); // Public "Refactor" Methods - static bool CheckSpawnConditions(Client* c); + static bool CheckCampSpawnConditions(Client* c); inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); } @@ -1047,6 +1047,7 @@ class Bot : public NPC { bool m_guard_flag; bool m_hold_flag; bool m_attack_flag; + bool m_combat_round_alert_flag; bool m_attacking_flag; bool m_pull_flag; bool m_pulling_flag; @@ -1104,6 +1105,7 @@ class Bot : public NPC { int32 GenerateBaseManaPoints(); void GenerateSpecialAttacks(); void SetBotID(uint32 botID); + void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag; } void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; } void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; } void SetReturningFlag(bool flag = true) { m_returning_flag = flag; } diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 7da91504d4..ccf1e51ffc 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -30,12 +30,19 @@ void bot_command_bot(Client *c, const Seperator *sep) void bot_command_camp(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) + if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } + + if (!Bot::CheckCampSpawnConditions(c)) { + return; + } + const int ab_mask = ActionableBots::ABM_Type1; std::string class_race_arg = sep->arg[1]; @@ -49,8 +56,14 @@ void bot_command_camp(Client *c, const Seperator *sep) return; } - for (auto bot_iter : sbl) + uint16 campCount; + + for (auto bot_iter : sbl) { bot_iter->Camp(); + ++campCount; + } + + c->Message(Chat::White, "%i of your bots have been camped.", campCount); } void bot_command_clone(Client *c, const Seperator *sep) @@ -428,6 +441,10 @@ void bot_command_delete(Client *c, const Seperator *sep) return; } + if (!Bot::CheckCampSpawnConditions(c)) { + return; + } + auto my_bot = ActionableBots::AsTarget_ByBot(c); if (!my_bot) { c->Message(Chat::White, "You must a bot that you own to use this command"); @@ -829,7 +846,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) return; } - if (!Bot::CheckSpawnConditions(c)) { + if (!Bot::CheckCampSpawnConditions(c)) { return; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 77d3ab23bd..70b6e659cc 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -174,7 +174,7 @@ void bot_command_cast(Client* c, const Seperator* sep) if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { c->Message(Chat::Yellow, "[%s] is an invalid target.", tar->GetCleanName()); return; - } + } } } LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme @@ -223,7 +223,7 @@ void bot_command_cast(Client* c, const Seperator* sep) Bot* firstFound = nullptr; for (auto bot_iter : sbl) { - if (!bot_iter->IsInGroupOrRaid()) { + if (!bot_iter->IsInGroupOrRaid(c)) { continue; } @@ -248,6 +248,14 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } + if ( + BOT_SPELL_TYPES_BENEFICIAL(spellType) && + !RuleB(Bots, CrossRaidBuffingAndHealing) && + !bot_iter->IsInGroupOrRaid(newTar, true) + ) { + continue; + } + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { bot_iter->BotGroupSay( bot_iter, diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 3ff0cb44ba..9e7c33eafd 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -2,47 +2,112 @@ void bot_command_follow(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) + if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | mmr | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); - c->Message(Chat::White, "usage: %s chain", sep->arg[0]); + std::vector description = + { + "Sets bots of your choosing to follow your target, view their current following state or reset their following state." + }; + + std::vector notes = + { + "- You can only follow players, bots or mercenaries belonging to your group or raid." + }; + + std::vector example_format = + { + fmt::format( + "{} [optional] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Clerics to follow your target:", + fmt::format( + "{} byclass {}", + sep->arg[0], + Class::Cleric + ) + }; + std::vector examples_two = + { + "To check the current state of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To reset all bots:", + fmt::format( + "{} reset spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + return; } + const int ab_mask = ActionableBots::ABM_Type2; + bool chain = false; bool reset = false; + bool currentCheck = false; int ab_arg = 1; - int name_arg = 2; Mob* target_mob = nullptr; std::string optional_arg = sep->arg[1]; - if (!optional_arg.compare("chain")) { - - auto chain_count = helper_bot_follow_option_chain(c); - c->Message(Chat::White, "%i of your bots %s now chain following you", chain_count, (chain_count == 1 ? "is" : "are")); - - return; - } - else if (!optional_arg.compare("reset")) { + + if (!optional_arg.compare("reset")) { + target_mob = c; reset = true; ++ab_arg; - ++name_arg ; + } + else if (!optional_arg.compare("current")) { + currentCheck = true; + ++ab_arg; } else { - //target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single); target_mob = c->GetTarget(); - if (!target_mob) { - c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); - return; - } - else if (!target_mob->IsBot() && !target_mob->IsClient()) { - c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); + + if (!target_mob || !target_mob->IsOfClientBotMerc() || !c->IsInGroupOrRaid(target_mob)) { + c->Message(Chat::Yellow, "You must a friendly player, bot or merc within your group or raid to use this command"); return; } - else if ((target_mob->GetGroup() && target_mob->GetGroup() != c->GetGroup()) || (target_mob->GetRaid() && target_mob->GetRaid() != c->GetRaid())) { - c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); - return; + + if (!optional_arg.compare("chain")) { + chain = true; + ++ab_arg; } } @@ -53,12 +118,66 @@ void bot_command_follow(Client* c, const Seperator* sep) } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) { + //if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg] : nullptr, class_race_check ? atoi(sep->arg[ab_arg]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.remove(nullptr); + + auto botCount = sbl.size(); + Mob* follow_mob = nullptr; + std::list chainList; + std::list::const_iterator it = chainList.begin(); + uint16 count = 0; for (auto bot_iter : sbl) { + if (currentCheck) { + follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I am currently following {}.'", + bot_iter->GetCleanName(), + follow_mob ? follow_mob->GetCleanName() : "no one" + ).c_str() + ); + + if (!follow_mob && RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(28); + } + + continue; + } + + if (bot_iter == target_mob) { + if (botCount == 1) { + c->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I cannot follow myself, you want me to run circles?", + bot_iter->GetCleanName() + ).c_str() + ); + + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(60); + } + + return; + } + + bot_iter->WipeHateList(); + --botCount; + + continue; + } + + if (!bot_iter->IsInGroupOrRaid(target_mob)) { + --botCount; + + continue; + } + bot_iter->WipeHateList(); if (!bot_iter->GetGroup() && !bot_iter->GetRaid()) { @@ -71,57 +190,52 @@ void bot_command_follow(Client* c, const Seperator* sep) bot_iter->SetManualFollow(false); } else { - if (target_mob->IsGrouped() || target_mob->IsRaidGrouped()) { - bot_iter->SetFollowID(target_mob->GetID()); - bot_iter->SetManualFollow(true); - } - else if (bot_iter == target_mob) { - bot_iter->SetFollowID(c->GetID()); - bot_iter->SetManualFollow(true); + if (chain) { + Mob* nextTar = target_mob; + + if (count > 0) { + nextTar = *it; + + if (!nextTar) { + nextTar = target_mob; + } + } + LogTestDebug("{} is now following {}.", bot_iter->GetCleanName(), nextTar->GetCleanName()); //deleteme + chainList.push_back(bot_iter); + ++it; + ++count; + bot_iter->SetFollowID(nextTar->GetID()); } else { - bot_iter->SetFollowID(0); - bot_iter->SetManualFollow(false); + bot_iter->SetFollowID(target_mob->GetID()); } + + bot_iter->SetManualFollow(true); } } - //auto my_group = bot_iter->GetGroup(); - //if (my_group) { - // if (reset) { - // if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter) - // bot_iter->SetFollowID(c->GetID()); - // else - // bot_iter->SetFollowID(my_group->GetLeader()->GetID()); - // - // bot_iter->SetManualFollow(false); - // } - // else { - // if (bot_iter == target_mob) - // bot_iter->SetFollowID(c->GetID()); - // else - // bot_iter->SetFollowID(target_mob->GetID()); - // - // bot_iter->SetManualFollow(true); - // } - //} - //else { - // bot_iter->SetFollowID(0); - // bot_iter->SetManualFollow(false); - //} - if (!bot_iter->GetPet()) + + if (!bot_iter->GetPet()) { continue; + } bot_iter->GetPet()->WipeHateList(); bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); } - Mob* follow_mob = nullptr; - if (sbl.size() == 1) { + if (currentCheck || !botCount) { + return; + } + + follow_mob = target_mob; + + if (botCount == 1) { follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + c->Message( - Chat::White, + Chat::Green, fmt::format( - "Following {}.", + "{} says, 'Following {}.'", + sbl.front()->GetCleanName(), follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); @@ -129,22 +243,20 @@ void bot_command_follow(Client* c, const Seperator* sep) else { if (reset) { c->Message( - Chat::White, + Chat::Green, fmt::format( "{} of your bots are following you.", - sbl.size() + botCount ).c_str() ); } else { - if (!sbl.empty()) { - follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); - } c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} of your bots are following {}.", - sbl.size(), + "{} of your bots are {} {}.", + botCount, + chain ? "chain following" : "following", follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); diff --git a/zone/bot_commands/item_use.cpp b/zone/bot_commands/item_use.cpp index 1640ff0e33..975d30b90d 100644 --- a/zone/bot_commands/item_use.cpp +++ b/zone/bot_commands/item_use.cpp @@ -179,7 +179,9 @@ void bot_command_item_use(Client* c, const Seperator* sep) ).c_str() ); - bot_iter->DoAnim(29); + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(29); + } } else if (!equipped_item) { c->Message( @@ -204,7 +206,9 @@ void bot_command_item_use(Client* c, const Seperator* sep) ).c_str() ); - bot_iter->DoAnim(29); + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(29); + } } } } diff --git a/zone/bot_commands/mesmerize.cpp b/zone/bot_commands/mesmerize.cpp index 1f51a70d39..d86fba9f71 100644 --- a/zone/bot_commands/mesmerize.cpp +++ b/zone/bot_commands/mesmerize.cpp @@ -20,7 +20,7 @@ void bot_command_mesmerize(Client *c, const Seperator *sep) continue; } - if (!bot_iter->IsInGroupOrRaid()) { + if (!bot_iter->IsInGroupOrRaid(c)) { continue; } diff --git a/zone/bot_raid.cpp b/zone/bot_raid.cpp index 25d98ef867..458ae03c41 100644 --- a/zone/bot_raid.cpp +++ b/zone/bot_raid.cpp @@ -176,13 +176,7 @@ void Bot::ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite) { // If the Bot Owner is in our raid we need to be able to invite their Bots } else if (invitee->IsBot() && (invitee->CastToBot()->GetBotOwnerCharacterID() != invitor->CharacterID())) { - invitor->Message( - Chat::Red, - fmt::format( - "{} is not your Bot. You can only invite your own Bots, or Bots that belong to a Raid member.", - invitee->GetCleanName() - ).c_str() - ); + invitor->Message(Chat::Red, "%s's owner needs to be in your raid to be able to invite them.", invitee->GetCleanName()); return; } @@ -257,10 +251,6 @@ void Bot::CreateBotRaid(Mob* invitee, Client* invitor, bool group_invite, Raid* } else { raid->AddBot(b); } - - if (new_raid) { - invitee->SetFollowID(invitor->GetID()); - } } } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index ee3aaf3a37..d7e6338223 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -995,10 +995,27 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa } if ( + !RuleB(Bots, EnableBotTGB) && + IsGroupSpell(botSpellList[i].spellid) && + !IsTGBCompatibleSpell(botSpellList[i].spellid) && + !botCaster->IsInGroupOrRaid(tar, true) + ) { + continue; + } + + if ( + ( + !botCaster->IsCommandedSpell() || + ( + botCaster->IsCommandedSpell() && + (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez) + ) + ) + && ( - !botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez)) + !IsPBAESpell(botSpellList[i].spellid) && + !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType)) ) - && (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsGroupBotSpellType(spellType))) // TODO bot rewrite - needed for ae spells? ) { continue; } @@ -1232,7 +1249,15 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spell if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); - const std::vector v = botCaster->GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = botCaster->GatherSpellTargets(true); + } + else { + v = botCaster->GatherGroupSpellTargets(); + } + int targetCount = 0; for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1273,7 +1298,15 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); - const std::vector v = botCaster->GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = botCaster->GatherSpellTargets(true); + } + else { + v = botCaster->GatherGroupSpellTargets(); + } + int targetCount = 0; for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1314,7 +1347,15 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); - const std::vector v = botCaster->GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = botCaster->GatherSpellTargets(true); + } + else { + v = botCaster->GatherGroupSpellTargets(); + } + int targetCount = 0; for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1629,7 +1670,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType continue; } - if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } @@ -1679,7 +1720,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType continue; } - if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } @@ -2583,19 +2624,27 @@ bool Bot::HasBotSpellEntry(uint16 spellid) { return false; } -bool Bot::IsValidSpellRange(uint16 spell_id, Mob const* tar) { +bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { if (!IsValidSpell(spell_id)) { return false; } if (tar) { - int spellrange = (GetActSpellRange(spell_id, spells[spell_id].range) * GetActSpellRange(spell_id, spells[spell_id].range)); - if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { + float range = spells[spell_id].range; + + if (tar != this) { + range += GetRangeDistTargetSizeMod(tar); + } + + range = GetActSpellRange(spell_id, range); + + if (range >= Distance(m_Position, tar->GetPosition())) { return true; } - spellrange = (GetActSpellRange(spell_id, spells[spell_id].aoe_range) * GetActSpellRange(spell_id, spells[spell_id].aoe_range)); - if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { + range = GetActSpellRange(spell_id, spells[spell_id].aoe_range); + + if (range >= Distance(m_Position, tar->GetPosition())) { return true; } } diff --git a/zone/groups.cpp b/zone/groups.cpp index ad6972e2a6..e157b00638 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1017,6 +1017,27 @@ void Group::GetBotList(std::list& bot_list, bool clear_list) } } +void Group::GetRawBotList(std::list& bot_list, bool clear_list) +{ + if (clear_list) { + bot_list.clear(); + } + + const auto& l = GroupIdRepository::GetWhere( + database, + fmt::format( + "`group_id` = {}", + GetID() + ) + ); + + for (const auto& e : l) { + if (e.bot_id) { + bot_list.push_back(e.bot_id); + } + } +} + bool Group::Process() { if(disbandcheck && !GroupCount()) return false; @@ -1234,30 +1255,49 @@ void Group::GroupMessageString(Mob* sender, uint32 type, uint32 string_id, const void Client::LeaveGroup() { Group *g = GetGroup(); - if(g) - { + if (g) { int32 MemberCount = g->GroupCount(); // Account for both client and merc leaving the group - if (GetMerc() && g == GetMerc()->GetGroup()) - { + if (GetMerc() && g == GetMerc()->GetGroup()) { MemberCount -= 1; } - if(MemberCount < 3) - { + if (RuleB(Bots, Enabled)) { + std::list sbl; + g->GetRawBotList(sbl); + + for (auto botID : sbl) { + auto b = entity_list.GetBotByBotID(botID); + + if (b) { + if (b->GetBotOwnerCharacterID() == CharacterID()) { + MemberCount -= 1; + } + } + else { + if (database.botdb.GetOwnerID(botID) == CharacterID()) { + MemberCount -= 1; + } + } + } + } + + if (MemberCount < 3) { g->DisbandGroup(); } - else - { + else { g->DelMember(this); - if (GetMerc() != nullptr && g == GetMerc()->GetGroup() ) - { + + if (GetMerc() != nullptr && g == GetMerc()->GetGroup()) { GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); } + + if (RuleB(Bots, Enabled)) { + g->RemoveClientsBots(this); + } } } - else - { + else { //force things a little Group::RemoveFromGroup(this); @@ -2576,3 +2616,77 @@ void Group::AddToGroup(AddToGroupRequest r) } ); } + +void Group::RemoveClientsBots(Client* c) { + std::list sbl; + GetRawBotList(sbl); + + for (auto botID : sbl) { + auto b = entity_list.GetBotByBotID(botID); + + if (b) { + if (b->GetBotOwnerCharacterID() == c->CharacterID()) { + b->RemoveBotFromGroup(b, this); + } + } + else { + if (database.botdb.GetOwnerID(botID) == c->CharacterID()) { + auto botName = database.botdb.GetBotNameByID(botID); + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (membername[i] == botName) { + members[i] = nullptr; + membername[i][0] = '\0'; + memset(membername[i], 0, 64); + MemberRoles[i] = 0; + break; + } + } + + auto pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); + ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; + gl->gid = GetID(); + gl->zoneid = zone->GetZoneID(); + gl->instance_id = zone->GetInstanceID(); + strcpy(gl->member_name, botName.c_str()); + worldserver.SendPacket(pack); + safe_delete(pack); + + auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; + gu->action = groupActLeave; + strcpy(gu->membername, botName.c_str()); + strcpy(gu->yourname, botName.c_str()); + + gu->leader_aas = LeaderAbilities; + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] == nullptr) { + //if (DEBUG>=5) LogFile->write(EQEMuLog::Debug, "Group::DelMember() null member at slot %i", i); + continue; + } + + if (membername[i] != botName.c_str()) { + strcpy(gu->yourname, members[i]->GetCleanName()); + + if (members[i]->IsClient()) { + members[i]->CastToClient()->QueuePacket(outapp); + } + } + } + + safe_delete(outapp); + + DelMemberOOZ(botName.c_str()); + + GroupIdRepository::DeleteWhere( + database, + fmt::format( + "`bot_id` = {}", + botID + ) + ); + } + } + } +} diff --git a/zone/groups.h b/zone/groups.h index 28954ee75c..405478665e 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -70,6 +70,7 @@ class Group : public GroupIDConsumer { void GetMemberList(std::list& member_list, bool clear_list = true); void GetClientList(std::list& client_list, bool clear_list = true); void GetBotList(std::list& bot_list, bool clear_list = true); + void GetRawBotList(std::list& bot_list, bool clear_list = true); bool IsGroupMember(Mob* c); bool IsGroupMember(const char* name); bool Process(); @@ -155,6 +156,7 @@ class Group : public GroupIDConsumer { void AddToGroup(AddToGroupRequest r); void AddToGroup(Mob* m); static void RemoveFromGroup(Mob* m); + void RemoveClientsBots(Client* c); void SetGroupMentor(int percent, char *name); void ClearGroupMentor(); diff --git a/zone/mob.cpp b/zone/mob.cpp index 7182bbfb4c..fc255188dc 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -6119,18 +6119,17 @@ int32 Mob::GetVulnerability(Mob *caster, uint32 spell_id, uint32 ticsremaining, bool Mob::IsTargetedFocusEffect(int focus_type) { switch (focus_type) { - case focusSpellVulnerability: - case focusFcSpellDamagePctIncomingPC: - case focusFcDamageAmtIncoming: - case focusFcSpellDamageAmtIncomingPC: - case focusFcCastSpellOnLand: - case focusFcHealAmtIncoming: - case focusFcHealPctCritIncoming: - case focusFcHealPctIncoming: - return true; - default: - return false; - + case focusSpellVulnerability: + case focusFcSpellDamagePctIncomingPC: + case focusFcDamageAmtIncoming: + case focusFcSpellDamageAmtIncomingPC: + case focusFcCastSpellOnLand: + case focusFcHealAmtIncoming: + case focusFcHealPctCritIncoming: + case focusFcHealPctIncoming: + return true; + default: + return false; } } @@ -7261,56 +7260,56 @@ void Mob::SlowMitigation(Mob* caster) EQ::skills::SkillType Mob::GetSkillByItemType(int ItemType) { switch (ItemType) { - case EQ::item::ItemType1HSlash: - return EQ::skills::Skill1HSlashing; - case EQ::item::ItemType2HSlash: - return EQ::skills::Skill2HSlashing; - case EQ::item::ItemType1HPiercing: - return EQ::skills::Skill1HPiercing; - case EQ::item::ItemType1HBlunt: - return EQ::skills::Skill1HBlunt; - case EQ::item::ItemType2HBlunt: - return EQ::skills::Skill2HBlunt; - case EQ::item::ItemType2HPiercing: - if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2) + case EQ::item::ItemType1HSlash: + return EQ::skills::Skill1HSlashing; + case EQ::item::ItemType2HSlash: + return EQ::skills::Skill2HSlashing; + case EQ::item::ItemType1HPiercing: return EQ::skills::Skill1HPiercing; - else - return EQ::skills::Skill2HPiercing; - case EQ::item::ItemTypeBow: - return EQ::skills::SkillArchery; - case EQ::item::ItemTypeLargeThrowing: - case EQ::item::ItemTypeSmallThrowing: - return EQ::skills::SkillThrowing; - case EQ::item::ItemTypeMartial: - return EQ::skills::SkillHandtoHand; - default: - return EQ::skills::SkillHandtoHand; + case EQ::item::ItemType1HBlunt: + return EQ::skills::Skill1HBlunt; + case EQ::item::ItemType2HBlunt: + return EQ::skills::Skill2HBlunt; + case EQ::item::ItemType2HPiercing: + if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2) + return EQ::skills::Skill1HPiercing; + else + return EQ::skills::Skill2HPiercing; + case EQ::item::ItemTypeBow: + return EQ::skills::SkillArchery; + case EQ::item::ItemTypeLargeThrowing: + case EQ::item::ItemTypeSmallThrowing: + return EQ::skills::SkillThrowing; + case EQ::item::ItemTypeMartial: + return EQ::skills::SkillHandtoHand; + default: + return EQ::skills::SkillHandtoHand; } } uint8 Mob::GetItemTypeBySkill(EQ::skills::SkillType skill) { switch (skill) { - case EQ::skills::SkillThrowing: - return EQ::item::ItemTypeSmallThrowing; - case EQ::skills::SkillArchery: - return EQ::item::ItemTypeArrow; - case EQ::skills::Skill1HSlashing: - return EQ::item::ItemType1HSlash; - case EQ::skills::Skill2HSlashing: - return EQ::item::ItemType2HSlash; - case EQ::skills::Skill1HPiercing: - return EQ::item::ItemType1HPiercing; - case EQ::skills::Skill2HPiercing: // watch for undesired client behavior - return EQ::item::ItemType2HPiercing; - case EQ::skills::Skill1HBlunt: - return EQ::item::ItemType1HBlunt; - case EQ::skills::Skill2HBlunt: - return EQ::item::ItemType2HBlunt; - case EQ::skills::SkillHandtoHand: - return EQ::item::ItemTypeMartial; - default: - return EQ::item::ItemTypeMartial; + case EQ::skills::SkillThrowing: + return EQ::item::ItemTypeSmallThrowing; + case EQ::skills::SkillArchery: + return EQ::item::ItemTypeArrow; + case EQ::skills::Skill1HSlashing: + return EQ::item::ItemType1HSlash; + case EQ::skills::Skill2HSlashing: + return EQ::item::ItemType2HSlash; + case EQ::skills::Skill1HPiercing: + return EQ::item::ItemType1HPiercing; + case EQ::skills::Skill2HPiercing: // watch for undesired client behavior + return EQ::item::ItemType2HPiercing; + case EQ::skills::Skill1HBlunt: + return EQ::item::ItemType1HBlunt; + case EQ::skills::Skill2HBlunt: + return EQ::item::ItemType2HBlunt; + case EQ::skills::SkillHandtoHand: + return EQ::item::ItemTypeMartial; + default: + return EQ::item::ItemTypeMartial; } } @@ -7318,7 +7317,6 @@ uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) { uint16 weapon_speed = 0; switch (hand) { - case 13: weapon_speed = attack_timer.GetDuration(); break; @@ -9433,14 +9431,52 @@ void Mob::SetBaseSetting(uint16 baseSetting, int settingValue) { } bool Mob::TargetValidation(Mob* other) { - if (!other || GetAppearance() == eaDead) { - return false; - } + if (!other || GetAppearance() == eaDead) { + return false; + } - return true; + return true; } -std::unordered_map &Mob::GetCloseMobList(float distance) +std::unordered_map& Mob::GetCloseMobList(float distance) { return entity_list.GetCloseMobList(this, distance); } + +bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { + if (!other || !IsOfClientBotMerc() || !other->IsOfClientBotMerc()) { + return false; + } + + auto* r = GetRaid(); + auto* rO = other->GetRaid(); + + if (r) { + if (!rO || r != rO) { + return false; + } + + auto rG = r->GetGroup(GetCleanName()); + auto rOG = rO->GetGroup(other->GetCleanName()); + + if (rG == RAID_GROUPLESS || rOG == RAID_GROUPLESS || (sameRaidGroup && rG != rOG)) { + return false; + } + + return true; + } + else { + auto* g = GetGroup(); + auto* gO = other->GetGroup(); + + if (g) { + if (!gO || g != gO) { + return false; + } + + return true; + } + } + + return false; +} diff --git a/zone/mob.h b/zone/mob.h index 65b37d3c14..6da29429f4 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -774,6 +774,7 @@ class Mob : public Entity { virtual bool HasGroup() = 0; virtual Raid* GetRaid() = 0; virtual Group* GetGroup() = 0; + bool IsInGroupOrRaid(Mob* other, bool sameRaidGroup = false); //Faction virtual inline int32 GetPrimaryFaction() const { return 0; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 375ab1571c..df9bf3942e 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2196,14 +2196,42 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_Group: case ST_GroupNoPets: { - if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB))) { - if( (!target) || - (target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) || - (target->IsCorpse()) ) + if ( + IsClient() && CastToClient()->TGB() && + IsTGBCompatibleSpell(spell_id) && + (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)) + ) { + if ( + !target || + target->IsCorpse() || + ( + target->IsNPC() && + !(target->GetOwner() && target->GetOwner()->IsClient()) + ) + ) { spell_target = this; - else + } + else { spell_target = target; - } else { + } + } + else if ( + IsBot() && RuleB(Bots, EnableBotTGB) && + IsTGBCompatibleSpell(spell_id) && + (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)) + ) { + if ( + !spell_target || + spell_target->IsCorpse() || + ( + spell_target->IsNPC() && + !(spell_target->GetOwner() && spell_target->GetOwner()->IsOfClientBot()) + ) + ) { + spell_target = this; + } + } + else { spell_target = this; } @@ -2530,8 +2558,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in //range check our target, if we have one and it is not us float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target); - if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) + if ( + ( + (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB) + ) && + IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) + ) { range = spells[spell_id].aoe_range; + } range = GetActSpellRange(spell_id, range); if(IsClient() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){ From 8b5112a616249700e13bd998b98efed3b32e4b95 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:21:42 -0500 Subject: [PATCH 003/394] oopsies --- zone/bot.cpp | 16 ++++++---------- zone/bot.h | 2 +- zone/spells.cpp | 4 ++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 16f4b9db3d..abc0865fa3 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -87,7 +87,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -211,7 +211,7 @@ Bot::Bot( SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -2106,7 +2106,6 @@ void Bot::AI_Process() // ATTACKING FLAG (HATE VALIDATION) if (GetAttackingFlag() && tar->CheckAggro(this)) { - SetCombatRoundForAlerts(true); SetAttackingFlag(false); } @@ -2277,7 +2276,7 @@ void Bot::AI_Process() } else { // Out-of-combat behavior SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { @@ -2422,7 +2421,6 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { AddToHateList(hater, 1); SetTarget(hater); SetAttackingFlag(); - SetCombatRoundForAlerts(); if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->AddToHateList(hater, 1); @@ -2884,7 +2882,7 @@ bool Bot::IsValidTarget( SetTarget(nullptr); SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); if (PULLING_BOT) { @@ -2918,7 +2916,6 @@ Mob* Bot::GetBotTarget(Client* bot_owner) WipeHateList(); SetAttackFlag(false); - SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (PULLING_BOT) { @@ -3137,7 +3134,6 @@ void Bot::SetOwnerTarget(Client* bot_owner) { } SetAttackFlag(false); - SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -3152,7 +3148,6 @@ void Bot::SetOwnerTarget(Client* bot_owner) { WipeHateList(); AddToHateList(attack_target, 1); SetTarget(attack_target); - SetCombatRoundForAlerts(); SetAttackingFlag(); if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->WipeHateList(); @@ -3165,7 +3160,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -4915,6 +4910,7 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) { if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { if (!GetCombatRoundForAlerts()) { + SetCombatRoundForAlerts(); BotGroupSay(this, "I can't backstab with this weapon!"); } diff --git a/zone/bot.h b/zone/bot.h index 5836af1335..0ee67c5ce3 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -1105,7 +1105,7 @@ class Bot : public NPC { int32 GenerateBaseManaPoints(); void GenerateSpecialAttacks(); void SetBotID(uint32 botID); - void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag; } + void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag = flag; } void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; } void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; } void SetReturningFlag(bool flag = true) { m_returning_flag = flag; } diff --git a/zone/spells.cpp b/zone/spells.cpp index df9bf3942e..ce20413025 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2560,9 +2560,9 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target); if ( ( - (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB) + (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB)) ) && - IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) + IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id) ) { range = spells[spell_id].aoe_range; } From a4e6bc5c1902cd4df43d2796b0cc751dfb14975d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:26:51 -0500 Subject: [PATCH 004/394] remove commented lines for ^follow --- zone/bot_commands/follow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 9e7c33eafd..57e97a6075 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -118,7 +118,6 @@ void bot_command_follow(Client* c, const Seperator* sep) } std::list sbl; - //if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg] : nullptr, class_race_check ? atoi(sep->arg[ab_arg]) : 0) == ActionableBots::ABT_None) { if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } From f9762e041d37f3a725b721b9f1b956d3869f3989 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:56:49 -0500 Subject: [PATCH 005/394] command cleanup --- zone/bot_commands/actionable.cpp | 2 +- zone/bot_commands/behind_mob.cpp | 2 +- zone/bot_commands/spell_engaged_priority.cpp | 4 ++-- zone/bot_commands/spell_idle_priority.cpp | 4 ++-- zone/bot_commands/spell_max_thresholds.cpp | 3 +-- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/zone/bot_commands/actionable.cpp b/zone/bot_commands/actionable.cpp index 73ec9ef6c6..f66a5b60a2 100644 --- a/zone/bot_commands/actionable.cpp +++ b/zone/bot_commands/actionable.cpp @@ -29,7 +29,7 @@ void bot_command_actionable(Client* c, const Seperator* sep) "[spawned] - selects all spawned bots.", "[all] - selects all spawned bots.", "
", - "You may only select your bots as actionable" + "You may only select your own bots." }; std::vector example_format = { }; diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 5761c4c319..8e69158607 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -9,7 +9,7 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { std::vector description = { - "Toggles whether or not bots will stay behind the mob during combat." + "-Toggles whether or not bots will stay behind the mob during combat." }; std::vector notes = { }; diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 03c151c3ae..89d76f2b5b 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -14,8 +14,8 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) std::vector notes = { - "-Setting a spell type to 0 will prevent that type from being cast.", - "-If 2 or more are set to the same priority they will sort by spell type ID." + "- Setting a spell type to 0 will prevent that type from being cast.", + "- If 2 or more are set to the same priority they will sort by spell type ID." }; std::vector example_format = diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 9bb95cf467..566a3f46a6 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -14,8 +14,8 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) std::vector notes = { - "-Setting a spell type to 0 will prevent that type from being cast.", - "-If 2 or more are set to the same priority they will sort by spell type ID." + "- Setting a spell type to 0 will prevent that type from being cast.", + "- If 2 or more are set to the same priority they will sort by spell type ID." }; std::vector example_format = diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 8ded61c1f1..3b4e0e85ff 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -16,8 +16,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", - "- All Heals, Cures, Buffs (DS and resists included)", - "are based off the target's setting, not the caster", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", "- e.g., BotA is healing BotB using BotB's settings", }; From cd4ebb5a73f3a56b64768ecdd7264e136b72cb2a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:58:37 -0500 Subject: [PATCH 006/394] Rewrite ^followd command and remove squared values from command --- .../database_update_manifest_bots.cpp | 3 +- common/ruletypes.h | 2 + zone/bot.cpp | 8 +- zone/bot.h | 3 - zone/bot_commands/bot.cpp | 193 +++++++++++++++--- 5 files changed, 178 insertions(+), 31 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index afaa9f23ea..44083475b5 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -189,7 +189,7 @@ WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' AND bd.expansion_bitmask != rv.rule_value; INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, `follow_distance`, 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' @@ -238,6 +238,7 @@ ALTER TABLE `bot_data` UPDATE `bot_command_settings` SET `aliases`= 'bh' WHERE `bot_command`='behindmob'; UPDATE `bot_command_settings` SET `aliases`= 'bs|settings' WHERE `bot_command`='botsettings'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|followdistance') ELSE 'followd||followdistance' END WHERE `bot_command`='botfollowdistance' AND `aliases` NOT LIKE '%followdistance%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ranged|toggleranged|btr') ELSE 'ranged|toggleranged|btr' END WHERE `bot_command`='bottoggleranged' AND `aliases` NOT LIKE '%ranged|toggleranged|btr%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|cr') ELSE 'cr' END WHERE `bot_command`='casterrange' AND `aliases` NOT LIKE '%cr%'; UPDATE `bot_command_settings` SET `aliases`= 'copy' WHERE `bot_command`='copysettings'; diff --git a/common/ruletypes.h b/common/ruletypes.h index 576d5e4744..fcbe55874b 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -867,6 +867,8 @@ RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. De RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.") RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") +RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") +RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index abc0865fa3..4eb44d55df 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9904,7 +9904,7 @@ void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { SetShowHelm(settingValue); break; case BotBaseSettings::FollowDistance: - SetFollowDistance(EQ::Clamp(static_cast(settingValue), static_cast(1), BOT_FOLLOW_DISTANCE_DEFAULT_MAX)); + SetFollowDistance(EQ::Clamp(static_cast(settingValue * settingValue), static_cast(1), static_cast((RuleI(Bots, MaxFollowDistance) * RuleI(Bots, MaxFollowDistance))))); break; case BotBaseSettings::StopMeleeLevel: SetStopMeleeLevel(settingValue); @@ -9953,8 +9953,8 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { //LogBotSettingsDetail("Returning current GetShowHelm of [{}] for [{}]", GetShowHelm(), GetCleanName()); //deleteme return GetShowHelm(); case BotBaseSettings::FollowDistance: - //LogBotSettingsDetail("Returning current GetFollowDistance of [{}] for [{}]", GetFollowDistance(), GetCleanName()); //deleteme - return GetFollowDistance(); + //LogBotSettingsDetail("Returning current GetFollowDistance of [{}] for [{}]", sqrt(GetFollowDistance()), GetCleanName()); //deleteme + return sqrt(GetFollowDistance()); case BotBaseSettings::StopMeleeLevel: //LogBotSettingsDetail("Returning current GetStopMeleeLevel of [{}] for [{}]", GetStopMeleeLevel(), GetCleanName()); //deleteme return GetStopMeleeLevel(); @@ -10002,7 +10002,7 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { case BotBaseSettings::ShowHelm: return true; case BotBaseSettings::FollowDistance: - return BOT_FOLLOW_DISTANCE_DEFAULT; + return (RuleI(Bots, DefaultFollowDistance) * RuleI(Bots, DefaultFollowDistance)); case BotBaseSettings::StopMeleeLevel: if (IsCasterClass(GetClass())) { return RuleI(Bots, CasterStopMeleeLevel); diff --git a/zone/bot.h b/zone/bot.h index 0ee67c5ce3..4e40f3939e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -37,9 +37,6 @@ #include -constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT = 184; // as DSq value (~13.565 units) -constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500; // as DSq value (50 units) - constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 1500; // 1.5 seconds diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index ccf1e51ffc..e360d49ed2 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -466,58 +466,205 @@ void bot_command_delete(Client *c, const Seperator *sep) void bot_command_follow_distance(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_follow_distance", sep->arg[0], "botfollowdistance")) + if (helper_command_alias_fail(c, "bot_command_follow_distance", sep->arg[0], "botfollowdistance")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [set [1 to %i]] [distance] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0], BOT_FOLLOW_DISTANCE_DEFAULT_MAX); - c->Message(Chat::White, "usage: %s [clear] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + std::vector description = + { + "Sets or resets the follow distance of the selected bots." + }; + + std::vector notes = + { + fmt::format( + "[Default]: {}", + RuleI(Bots, MaxFollowDistance) + ), + + fmt::format( + "- You must use a value between 1 and {}.", + RuleI(Bots, MaxFollowDistance) + ) + }; + + std::vector example_format = + { + fmt::format( + "{} [reset]/[set [value]] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to follow at a distance of 25:", + fmt::format( + "{} set 25 spawned", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To check the curret following distance of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To reset the following distance of all Wizards:", + fmt::format( + "{} reset byclass {}", + sep->arg[0], + Class::Wizard + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + return; } - const int ab_mask = ActionableBots::ABM_NoFilter; - uint32 bfd = BOT_FOLLOW_DISTANCE_DEFAULT; + const int ab_mask = ActionableBots::ABM_Type2; + + uint32 bfd = RuleI(Bots, DefaultFollowDistance); bool set_flag = false; + bool currentCheck = false; int ab_arg = 2; - if (!strcasecmp(sep->arg[1], "set")) { + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("set")) { if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A numeric [distance] is required to use this command. [1 to %i]", BOT_FOLLOW_DISTANCE_DEFAULT_MAX); + c->Message(Chat::Yellow, "You must enter a value between 1 and %i.", RuleI(Bots, MaxFollowDistance)); + return; } bfd = Strings::ToInt(sep->arg[2]); - if (bfd < 1) - bfd = 1; - if (bfd > BOT_FOLLOW_DISTANCE_DEFAULT_MAX) - bfd = BOT_FOLLOW_DISTANCE_DEFAULT_MAX; + + if (bfd < 1) { + c->Message(Chat::Yellow, "You must enter a value between 1 and %i.", RuleI(Bots, MaxFollowDistance)); + + return; + } + + if (bfd > RuleI(Bots, MaxFollowDistance)) { + c->Message(Chat::Yellow, "You must enter a value between 1 and %i.", RuleI(Bots, MaxFollowDistance)); + + return; + } + set_flag = true; - ab_arg = 3; + ++ab_arg; } - else if (strcasecmp(sep->arg[1], "clear")) { - c->Message(Chat::White, "This command requires a [set | clear] argument"); + else if (!arg1.compare("current")) { + currentCheck = true; + } + else if (arg1.compare("reset")) { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + std::list sbl; - auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]); - if (ab_type == ActionableBots::ABT_None) + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; + } - int bot_count = 0; + sbl.remove(nullptr); + + int botCount = 0; for (auto bot_iter : sbl) { - if (!bot_iter) + if (!bot_iter) { continue; + } - bot_iter->SetFollowDistance(bfd); + if (currentCheck) { + Mob* follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); - ++bot_count; + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I am currently following {} at a distance of {}.'", + bot_iter->GetCleanName(), + follow_mob ? follow_mob->GetCleanName() : "no one", + sqrt(bot_iter->GetFollowDistance()) + ).c_str() + ); + } + else { + bot_iter->SetFollowDistance(bfd * bfd); + ++botCount; + } } - if (ab_type == ActionableBots::ABT_All) { - c->Message(Chat::White, "%s all of your bot follow distances to %i", set_flag ? "Set" : "Cleared", bfd); + if (currentCheck) { + return; + } + + if (botCount == 1) { + Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + + c->Message( + Chat::Green, + fmt::format( + "{} says, 'Following {} at a distance of {}.'", + sbl.front()->GetCleanName(), + follow_mob ? follow_mob->GetCleanName() : "you", + bfd + ).c_str() + ); } else { - c->Message(Chat::White, "%s %i of your spawned bot follow distances to %i", (set_flag ? "Set" : "Cleared"), bot_count, bfd); + c->Message( + Chat::Green, + fmt::format( + "{} of your bots are now following at a distance of {}.", + botCount, + bfd + ).c_str() + ); } } From c16e1e59546e00eede99432eb1249d939c40465f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 2 Nov 2024 22:05:55 -0500 Subject: [PATCH 007/394] misc cleanup --- zone/bot_commands/caster_range.cpp | 2 +- zone/bot_commands/follow.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/bot_commands/caster_range.cpp b/zone/bot_commands/caster_range.cpp index 447c739db7..87676c7d46 100644 --- a/zone/bot_commands/caster_range.cpp +++ b/zone/bot_commands/caster_range.cpp @@ -70,7 +70,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) c->Message( Chat::White, fmt::format( - "{} says, 'My current Caster Range is {}.'", + "{} says, 'My current caster range is {}.'", my_bot->GetCleanName(), my_bot->GetBotCasterRange() ).c_str() diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 57e97a6075..c5b7f8f780 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -132,6 +132,7 @@ void bot_command_follow(Client* c, const Seperator* sep) for (auto bot_iter : sbl) { if (currentCheck) { follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); + c->Message( Chat::Green, fmt::format( From 7d8f4d9849121f62696ff9685c591f0b97603c75 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 00:20:41 -0600 Subject: [PATCH 008/394] fix formatting in cazictouch --- zone/spells.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index ce20413025..1f80e30210 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2471,8 +2471,9 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in } } - if ((RuleB(Bots, CazicTouchBotsOwner) && spell_target && spell_target->IsBot()) && spell_id == (SPELL_CAZIC_TOUCH || spell_id == SPELL_TOUCH_OF_VINITRAS)) { + if ((RuleB(Bots, CazicTouchBotsOwner) && spell_target && spell_target->IsBot()) && (spell_id == SPELL_CAZIC_TOUCH || spell_id == SPELL_TOUCH_OF_VINITRAS)) { auto bot_owner = spell_target->GetOwner(); + if (bot_owner) { spell_target = bot_owner; } From 32a10f4219dfd3bfd20394f67c328254bd04a07b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 00:30:40 -0600 Subject: [PATCH 009/394] Implement and rewrite stances --- .../database_update_manifest_bots.cpp | 15 +- .../base/base_bot_settings_repository.h | 52 ++-- zone/bot.cpp | 145 +++++----- zone/bot.h | 26 +- zone/bot_commands/bot.cpp | 252 +++++++++++++++--- zone/bot_commands/default_settings.cpp | 146 +++++----- zone/bot_database.cpp | 62 +++-- zone/client.cpp | 32 +-- zone/mob.cpp | 208 ++++++++++++--- zone/mob.h | 8 +- 10 files changed, 655 insertions(+), 291 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 44083475b5..639d173b06 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -174,6 +174,7 @@ CREATE TABLE `bot_settings` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `char_id` INT UNSIGNED NOT NULL, `bot_id` INT UNSIGNED NOT NULL, + `stance` INT UNSIGNED NOT NULL, `setting_id` INT UNSIGNED NOT NULL, `setting_type` INT UNSIGNED NOT NULL, `value` INT UNSIGNED NOT NULL, @@ -183,16 +184,16 @@ CREATE TABLE `bot_settings` ( ) COLLATE='utf8mb4_general_ci'; -INSERT INTO bot_settings SELECT NULL, 0, bd.`bot_id`, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd +INSERT INTO bot_settings SELECT NULL, 0, bd.`bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd JOIN rule_values rv WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' AND bd.expansion_bitmask != rv.rule_value; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' +SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' FROM ( SELECT `bot_id`, (CASE @@ -204,11 +205,11 @@ FROM ( ) AS `subquery` WHERE `sml` != `stop_melee_level`; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' +SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' FROM ( SELECT `bot_id`, (CASE diff --git a/common/repositories/base/base_bot_settings_repository.h b/common/repositories/base/base_bot_settings_repository.h index 2018177dc9..92d4aba15f 100644 --- a/common/repositories/base/base_bot_settings_repository.h +++ b/common/repositories/base/base_bot_settings_repository.h @@ -22,6 +22,7 @@ class BaseBotSettingsRepository { uint32_t id; uint32_t char_id; uint32_t bot_id; + uint8_t stance; uint16_t setting_id; uint8_t setting_type; int32_t value; @@ -40,6 +41,7 @@ class BaseBotSettingsRepository { "id", "char_id", "bot_id", + "stance", "setting_id", "setting_type", "value", @@ -54,6 +56,7 @@ class BaseBotSettingsRepository { "id", "char_id", "bot_id", + "stance", "setting_id", "setting_type", "value", @@ -102,6 +105,7 @@ class BaseBotSettingsRepository { e.id = 0; e.char_id = 0; e.bot_id = 0; + e.stance = 0; e.setting_id = 0; e.setting_type = 0; e.value = 0; @@ -146,11 +150,12 @@ class BaseBotSettingsRepository { e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.category_name = row[6] ? row[6] : ""; - e.setting_name = row[7] ? row[7] : ""; + e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.category_name = row[7] ? row[7] : ""; + e.setting_name = row[8] ? row[8] : ""; return e; } @@ -187,11 +192,12 @@ class BaseBotSettingsRepository { v.push_back(columns[0] + " = " + std::to_string(e.id)); v.push_back(columns[1] + " = " + std::to_string(e.char_id)); v.push_back(columns[2] + " = " + std::to_string(e.bot_id)); - v.push_back(columns[3] + " = " + std::to_string(e.setting_id)); - v.push_back(columns[4] + " = " + std::to_string(e.setting_type)); - v.push_back(columns[5] + " = " + std::to_string(e.value)); - v.push_back(columns[6] + " = '" + Strings::Escape(e.category_name) + "'"); - v.push_back(columns[7] + " = '" + Strings::Escape(e.setting_name) + "'"); + v.push_back(columns[3] + " = " + std::to_string(e.stance)); + v.push_back(columns[4] + " = " + std::to_string(e.setting_id)); + v.push_back(columns[5] + " = " + std::to_string(e.setting_type)); + v.push_back(columns[6] + " = " + std::to_string(e.value)); + v.push_back(columns[7] + " = '" + Strings::Escape(e.category_name) + "'"); + v.push_back(columns[8] + " = '" + Strings::Escape(e.setting_name) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -216,6 +222,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); @@ -253,6 +260,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); @@ -294,11 +302,12 @@ class BaseBotSettingsRepository { e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.category_name = row[6] ? row[6] : ""; - e.setting_name = row[7] ? row[7] : ""; + e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.category_name = row[7] ? row[7] : ""; + e.setting_name = row[8] ? row[8] : ""; all_entries.push_back(e); } @@ -326,11 +335,12 @@ class BaseBotSettingsRepository { e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.category_name = row[6] ? row[6] : ""; - e.setting_name = row[7] ? row[7] : ""; + e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.category_name = row[7] ? row[7] : ""; + e.setting_name = row[8] ? row[8] : ""; all_entries.push_back(e); } @@ -408,6 +418,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); @@ -438,6 +449,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); diff --git a/zone/bot.cpp b/zone/bot.cpp index 4eb44d55df..e7d4737d7f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9995,14 +9995,14 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { return true; } -int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { +int Bot::GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance) { switch (botSetting) { case BotBaseSettings::ExpansionBitmask: return RuleI(Bots, BotExpansionSettings); case BotBaseSettings::ShowHelm: return true; case BotBaseSettings::FollowDistance: - return (RuleI(Bots, DefaultFollowDistance) * RuleI(Bots, DefaultFollowDistance)); + return RuleI(Bots, DefaultFollowDistance); case BotBaseSettings::StopMeleeLevel: if (IsCasterClass(GetClass())) { return RuleI(Bots, CasterStopMeleeLevel); @@ -10013,7 +10013,7 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { case BotBaseSettings::PetSetTypeSetting: return 0; case BotBaseSettings::BehindMob: - if (GetClass() == Class::Rogue) { + if (GetClass() == Class::Rogue || (IsPureMeleeClass() && GetClass() != Class::Warrior)) { return true; } else { @@ -10053,10 +10053,14 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { void Bot::LoadDefaultBotSettings() { + _spellSettings.clear(); + + uint8 botStance = GetBotStance(); + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i)); - LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i)); //deleteme - } + SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, botStance)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); //deleteme + } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { BotSpellSettings_Struct t; @@ -10064,28 +10068,28 @@ void Bot::LoadDefaultBotSettings() { t.spellType = i; t.shortName = GetSpellTypeShortNameByID(i); t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i); - t.delay = GetDefaultSpellDelay(i); - t.minThreshold = GetDefaultSpellMinThreshold(i); - t.maxThreshold = GetDefaultSpellMaxThreshold(i); - t.resistLimit = GetDefaultSpellTypeResistLimit(i); - t.aggroCheck = GetDefaultSpellTypeAggroCheck(i); - t.minManaPct = GetDefaultSpellTypeMinManaLimit(i); - t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i); - t.minHPPct = GetDefaultSpellTypeMinHPLimit(i); - t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i); - t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass()); - t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass()); - t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass()); - t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i); + t.hold = GetDefaultSpellHold(i, botStance); + t.delay = GetDefaultSpellDelay(i, botStance); + t.minThreshold = GetDefaultSpellMinThreshold(i, botStance); + t.maxThreshold = GetDefaultSpellMaxThreshold(i, botStance); + t.resistLimit = GetDefaultSpellTypeResistLimit(i, botStance); + t.aggroCheck = GetDefaultSpellTypeAggroCheck(i, botStance); + t.minManaPct = GetDefaultSpellTypeMinManaLimit(i, botStance); + t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i, botStance); + t.minHPPct = GetDefaultSpellTypeMinHPLimit(i, botStance); + t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i, botStance); + t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass(), botStance); + t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass(), botStance); + t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass(), botStance); + t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance); t.recastTimer.Start(); _spellSettings.push_back(t); - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); //deleteme - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); //deleteme - LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i), GetDefaultSpellTypeMinManaLimit(i), GetDefaultSpellTypeMaxManaLimit(i), GetDefaultSpellTypeMinHPLimit(i), GetDefaultSpellTypeMaxHPLimit(i)); //deleteme - LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}] | recastTimer = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass()), GetDefaultSpellTypeEngagedPriority(i, GetClass()), GetDefaultSpellTypePursuePriority(i, GetClass()), GetDefaultSpellTypeAEOrGroupTargetCount(i), t.recastTimer.GetRemainingTime()); //deleteme + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(botStance), botStance); //deleteme + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, botStance), GetDefaultSpellDelay(i, botStance), GetDefaultSpellMinThreshold(i, botStance), GetDefaultSpellMaxThreshold(i, botStance)); //deleteme + LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, botStance), GetDefaultSpellTypeMinManaLimit(i, botStance), GetDefaultSpellTypeMaxManaLimit(i, botStance), GetDefaultSpellTypeMinHPLimit(i, botStance), GetDefaultSpellTypeMaxHPLimit(i, botStance)); //deleteme + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), botStance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), botStance), GetDefaultSpellTypePursuePriority(i, GetClass(), botStance), GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); //deleteme } } @@ -10159,36 +10163,36 @@ uint16 Bot::GetSpellTypePriority(uint16 spellType, uint8 priorityType) { } } -int Bot::GetDefaultSetting(uint16 settingCategory, uint16 settingType) { +int Bot::GetDefaultSetting(uint16 settingCategory, uint16 settingType, uint8 stance) { switch (settingCategory) { case BotSettingCategories::BaseSetting: - return GetDefaultBotBaseSetting(settingType); + return GetDefaultBotBaseSetting(settingType, stance); case BotSettingCategories::SpellHold: - return GetDefaultSpellHold(settingType); + return GetDefaultSpellHold(settingType, stance); case BotSettingCategories::SpellDelay: - return GetDefaultSpellDelay(settingType); + return GetDefaultSpellDelay(settingType, stance); case BotSettingCategories::SpellMinThreshold: - return GetDefaultSpellMinThreshold(settingType); + return GetDefaultSpellMinThreshold(settingType, stance); case BotSettingCategories::SpellMaxThreshold: - return GetDefaultSpellMinThreshold(settingType); + return GetDefaultSpellMaxThreshold(settingType, stance); case BotSettingCategories::SpellTypeAggroCheck: - return GetDefaultSpellTypeAggroCheck(settingType); + return GetDefaultSpellTypeAggroCheck(settingType, stance); case BotSettingCategories::SpellTypeMinManaPct: - return GetDefaultSpellTypeMinManaLimit(settingType); + return GetDefaultSpellTypeMinManaLimit(settingType, stance); case BotSettingCategories::SpellTypeMaxManaPct: - return GetDefaultSpellTypeMaxManaLimit(settingType); + return GetDefaultSpellTypeMaxManaLimit(settingType, stance); case BotSettingCategories::SpellTypeMinHPPct: - return GetDefaultSpellTypeMinHPLimit(settingType); + return GetDefaultSpellTypeMinHPLimit(settingType, stance); case BotSettingCategories::SpellTypeMaxHPPct: - return GetDefaultSpellTypeMaxHPLimit(settingType); + return GetDefaultSpellTypeMaxHPLimit(settingType, stance); case BotSettingCategories::SpellTypeIdlePriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Idle, GetClass()); + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Idle, GetClass(), stance); case BotSettingCategories::SpellTypeEngagedPriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Engaged, GetClass()); + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Engaged, GetClass(), stance); case BotSettingCategories::SpellTypePursuePriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Pursue, GetClass()); + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Pursue, GetClass(), stance); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - return GetDefaultSpellTypeAEOrGroupTargetCount(settingType); + return GetDefaultSpellTypeAEOrGroupTargetCount(settingType, stance); default: break; } @@ -10205,7 +10209,7 @@ int Bot::GetSetting(uint16 settingCategory, uint16 settingType) { case BotSettingCategories::SpellMinThreshold: return GetSpellMinThreshold(settingType); case BotSettingCategories::SpellMaxThreshold: - return GetSpellMinThreshold(settingType); + return GetSpellMaxThreshold(settingType); case BotSettingCategories::SpellTypeAggroCheck: return GetSpellTypeAggroCheck(settingType); case BotSettingCategories::SpellTypeMinManaPct: @@ -10229,20 +10233,20 @@ int Bot::GetSetting(uint16 settingCategory, uint16 settingType) { } } -uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass, uint8 stance) { switch (priorityType) { case BotPriorityCategories::Idle: - return GetDefaultSpellTypeIdlePriority(spellType, botClass); + return GetDefaultSpellTypeIdlePriority(spellType, botClass, stance); case BotPriorityCategories::Engaged: - return GetDefaultSpellTypeEngagedPriority(spellType, botClass); + return GetDefaultSpellTypeEngagedPriority(spellType, botClass, stance); case BotPriorityCategories::Pursue: - return GetDefaultSpellTypePursuePriority(spellType, botClass); + return GetDefaultSpellTypePursuePriority(spellType, botClass, stance); default: return 0; } } -uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance) { if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, botClass)) { return 0; } @@ -10351,7 +10355,7 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { return priority; } -uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, uint8 stance) { switch (spellType) { case BotSpellTypes::Escape: return 1; @@ -10439,14 +10443,12 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass) return 42; case BotSpellTypes::InCombatBuffSong: return 43; - case BotSpellTypes::Pet: - return 44; default: return 0; } } -uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint8 stance) { switch (spellType) { case BotSpellTypes::Escape: return 1; @@ -10497,7 +10499,7 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass) } } -uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType) { +uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance) { if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { return RuleI(Bots, SpellResistLimit); @@ -10507,35 +10509,46 @@ uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType) { } } -bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType) { +bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance) { + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return false; + default: + break; + } + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Root: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::Fear: + case BotSpellTypes::Stun: case BotSpellTypes::AENukes: case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: case BotSpellTypes::AESnare: - case BotSpellTypes::Snare: + case BotSpellTypes::AEFear: case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: + case BotSpellTypes::AERoot: case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - case BotSpellTypes::AEStun: - case BotSpellTypes::Stun: + case BotSpellTypes::PBAENuke: return true; default: return false; } } -uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spellType, uint8 stance) { return 0; } -uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::InCombatBuff: if (GetClass() == Class::Shaman) { @@ -10550,7 +10563,7 @@ uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType) { return 100; } -uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::InCombatBuff: if (GetClass() == Class::Shaman) { @@ -10565,11 +10578,11 @@ uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType) { return 0; } -uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spellType, uint8 stance) { return 100; } -uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType) { +uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType, uint8 stance) { if (IsAEBotSpellType(spellType)) { return RuleI(Bots, MinTargetsForAESpell); } diff --git a/zone/bot.h b/zone/bot.h index 4e40f3939e..553148ee51 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -458,7 +458,7 @@ class Bot : public NPC { void CopyBotSpellSettings(Bot* to); void ResetBotSpellSettings(); int GetBotBaseSetting(uint16 botSetting); - int GetDefaultBotBaseSetting(uint16 botSetting); + int GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance = Stance::Balanced); void SetBotBaseSetting(uint16 botSetting, int settingValue); void LoadDefaultBotSettings(); void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); @@ -467,18 +467,18 @@ class Bot : public NPC { std::string GetBotSpellCategoryName(uint8 setting_type); std::string GetBotSettingCategoryName(uint8 setting_type); - int GetDefaultSetting(uint16 settingCategory, uint16 settingType); - uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass); - uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass); - uint16 GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass); - uint16 GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass); - uint16 GetDefaultSpellTypeResistLimit(uint16 spellType); - bool GetDefaultSpellTypeAggroCheck(uint16 spellType); - uint8 GetDefaultSpellTypeMinManaLimit(uint16 spellType); - uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spellType); - uint8 GetDefaultSpellTypeMinHPLimit(uint16 spellType); - uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spellType); - uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType); + int GetDefaultSetting(uint16 settingCategory, uint16 settingType, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance = Stance::Balanced); + bool GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMinManaLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMinHPLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType, uint8 stance = Stance::Balanced); int GetSetting(uint16 settingCategory, uint16 settingType); uint16 GetSpellTypePriority(uint16 spellType, uint8 priorityType); diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index e360d49ed2..d867b4e67a 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1180,43 +1180,188 @@ void bot_command_stance(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message( - Chat::White, + std::vector description = + { + "Change a bot's stance to control the way it behaves." + }; + + std::vector notes = + { + "- Changing a stance will reset all settings to match that stance type.", + "- Any changes made will only save to that stance for future use.", fmt::format( - "Value: {} ({}), {} ({}), {} ({})", - Stance::Passive, + "- {} (#{}) will tell Non-Warrior classes to taunt automatically.", + Stance::GetName(Stance::Aggressive), + Stance::Aggressive + ), + "
", + "Available stances:", + fmt::format( + "{} (#{}), {} (#{}), {} (#{}), {} (#{}), {} (#{}), {} (#{}), {} (#{})", Stance::GetName(Stance::Passive), - Stance::Balanced, + Stance::Passive, Stance::GetName(Stance::Balanced), + Stance::Balanced, + Stance::GetName(Stance::Efficient), + Stance::Efficient, + Stance::GetName(Stance::Aggressive), Stance::Aggressive, - Stance::GetName(Stance::Aggressive) - ).c_str() + Stance::GetName(Stance::Assist), + Stance::Assist, + Stance::GetName(Stance::Burn), + Stance::Burn, + Stance::GetName(Stance::AEBurn), + Stance::AEBurn + ), + "
", + fmt::format( + "- {} (#{}) [Default] - Overall balance and casts most spell types by default.", + Stance::GetName(Stance::Balanced), + Stance::Balanced + ), + fmt::format( + "- {} (#{}) - Idle. Does not cast or engage in combat.", + Stance::GetName(Stance::Passive), + Stance::Passive + ), + fmt::format( + "- {} (#{}) - More mana and aggro efficient (SKs will still cast hate line). Longer delays between detrimental spells, thresholds adjusted to cast less often.", + Stance::GetName(Stance::Efficient), + Stance::Efficient + ), + fmt::format( + "- {} (#{}) - Much more aggressive in their cast times and thresholds. More DPS, debuffs and slow but a higher risk of snagging aggro.", + Stance::GetName(Stance::Aggressive), + Stance::Aggressive + ), + fmt::format( + "- {} (#{}) - Support role. Most offensive spell types are disabled. Focused on heals, cures, CC, debuffs and slows.", + Stance::GetName(Stance::Assist), + Stance::Assist + ), + fmt::format( + "- {} (#{}) - Murder. Doesn't care about aggro, just wants to kill. DPS Machine.", + Stance::GetName(Stance::Burn), + Stance::Burn + ), + fmt::format( + "- {} (#{}) - Murder EVERYTHING. Doesn't care about aggro, casts AEs. Everything must die ASAP.", + Stance::GetName(Stance::AEBurn), + Stance::AEBurn + ) + }; + + std::vector example_format = + { + fmt::format( + "{} [current | value: {}-{}]", + sep->arg[0], + Stance::Passive, + Stance::AEBurn + ) + }; + std::vector examples_one = + { + "To set all bots to BurnAE:", + fmt::format( + "{} {} spawned {}", + sep->arg[0], + Stance::Aggressive, + Class::ShadowKnight + ) + }; + std::vector examples_two = + { + "To set all Shadowknights to Aggressive:", + fmt::format( + "{} {} byclass {}", + sep->arg[0], + Stance::Aggressive, + Class::ShadowKnight + ) + }; + std::vector examples_three = + { + "To check the current stances of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + return; } const int ab_mask = ActionableBots::ABM_Type1; - std::string arg1 = sep->arg[1]; + bool currentCheck = false; int ab_arg = 1; - bool current_check = false; uint32 value = 0; + std::string arg1 = sep->arg[1]; + if (sep->IsNumber(1)) { ++ab_arg; value = atoi(sep->arg[1]); - if (value < 0 || value > 300) { - c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); + if ( + value < Stance::Passive || + value > Stance::AEBurn || + value == Stance::Reactive || + value == Stance::Assist + ) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid stance ID, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } } else if (!arg1.compare("current")) { ++ab_arg; - current_check = true; + currentCheck = true; } else { - c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } @@ -1232,26 +1377,56 @@ void bot_command_stance(Client *c, const Seperator *sep) return; } - if (!current_check && (value == Stance::Unknown || (value != Stance::Passive && value != Stance::Balanced && value != Stance::Aggressive))) { - c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command"); - return; - } - + Bot* first_found = nullptr; + int success_count = 0; for (auto bot_iter : sbl) { - if (!bot_iter) - continue; + if (!first_found) { + first_found = bot_iter; + } + + if (currentCheck) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My current stance is {} ({}).'", + bot_iter->GetCleanName(), + Stance::GetName(bot_iter->GetBotStance()), + bot_iter->GetBotStance() + ).c_str() + ); - if (!current_check) { - bot_iter->SetBotStance(value); - bot_iter->Save(); + continue; } - Bot::BotGroupSay( - bot_iter, + bot_iter->Save(); + bot_iter->SetBotStance(value); + bot_iter->LoadDefaultBotSettings(); + database.botdb.LoadBotSettings(bot_iter); + bot_iter->Save(); + ++success_count; + } + + if (currentCheck) { + return; + } + + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I am now set to the stance [{}].'", + first_found->GetCleanName(), + Stance::GetName(value) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, fmt::format( - "My current stance is {} ({}).", - Stance::GetName(bot_iter->GetBotStance()), - bot_iter->GetBotStance() + "{} of your bots are now set to the stance [{}].", + success_count, + Stance::GetName(value) ).c_str() ); } @@ -1278,7 +1453,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) uint8 sml = RuleI(Bots, CasterStopMeleeLevel); bool sync_sml = false; bool reset_sml = false; - bool current_check = false; + bool currentCheck = false; if (sep->IsNumber(1)) { ab_arg = 2; @@ -1294,14 +1469,23 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) } else if (!arg1.compare("current")) { ab_arg = 2; - current_check = true; + currentCheck = true; } else if (!strcasecmp(sep->arg[1], "reset")) { ab_arg = 2; reset_sml = true; } else { - c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } @@ -1347,7 +1531,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) sml = my_bot->GetDefaultBotBaseSetting(BotBaseSettings::StopMeleeLevel); } - if (current_check) { + if (currentCheck) { c->Message( Chat::White, fmt::format( @@ -1364,7 +1548,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) } } - if (!current_check) { + if (!currentCheck) { if (success_count == 1 && first_found) { c->Message( Chat::White, diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 1f7c854cad..87bb31b0f0 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -199,24 +199,28 @@ void bot_command_default_settings(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; std::string output = ""; - for (auto my_bot : sbl) { + uint8 botStance = 2; + + for (auto myBot : sbl) { if (!first_found) { - first_found = my_bot; + first_found = myBot; } + botStance = myBot->GetBotStance(); + if (!strcasecmp(sep->arg[1], "misc")) { for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + myBot->SetBotBaseSetting(i, myBot->GetDefaultBotBaseSetting(i, botStance)); output = "miscellanous settings"; } } else if (!strcasecmp(sep->arg[1], "holds")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); + myBot->SetSpellHold(spellType, myBot->GetDefaultSpellHold(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); } } @@ -224,11 +228,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "delays")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); } } @@ -236,11 +240,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "minthresholds")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); + myBot->SetSpellMinThreshold(spellType, myBot->GetDefaultSpellMinThreshold(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); } } @@ -248,11 +252,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "maxthresholds")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); + myBot->SetSpellMaxThreshold(spellType, myBot->GetDefaultSpellMaxThreshold(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); } } @@ -260,11 +264,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "aggrochecks")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); + myBot->SetSpellTypeAggroCheck(spellType, myBot->GetDefaultSpellTypeAggroCheck(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); } } @@ -272,11 +276,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "minmanapct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); + myBot->SetSpellTypeMinManaLimit(spellType, myBot->GetDefaultSpellTypeMinManaLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); } } @@ -284,11 +288,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "maxmanapct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); + myBot->SetSpellTypeMaxManaLimit(spellType, myBot->GetDefaultSpellTypeMaxManaLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); } } @@ -296,11 +300,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "minhppct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); + myBot->SetSpellTypeMinHPLimit(spellType, myBot->GetDefaultSpellTypeMinHPLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); } } @@ -308,11 +312,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "maxhppct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); + myBot->SetSpellTypeMaxHPLimit(spellType, myBot->GetDefaultSpellTypeMaxHPLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); } } @@ -320,11 +324,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "idlepriority")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); } } @@ -332,11 +336,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "engagedpriority")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); } } @@ -344,11 +348,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "pursuepriority")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); } } @@ -356,51 +360,51 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "targetcounts")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); } } output = "ae/group count settings"; } else if (!strcasecmp(sep->arg[1], "spellsettings")) { - my_bot->ResetBotSpellSettings(); + myBot->ResetBotSpellSettings(); output = "^spellsettings"; } else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); - my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); - my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); - my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); - my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); - my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); - my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); - my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); - my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); - my_bot->SetSpellTypeAEOrGroupTargetCount(spellType, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType)); + myBot->SetSpellHold(spellType, myBot->GetDefaultSpellHold(spellType, botStance)); + myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); + myBot->SetSpellMinThreshold(spellType, myBot->GetDefaultSpellMinThreshold(spellType, botStance)); + myBot->SetSpellMaxThreshold(spellType, myBot->GetDefaultSpellMaxThreshold(spellType, botStance)); + myBot->SetSpellTypeAggroCheck(spellType, myBot->GetDefaultSpellTypeAggroCheck(spellType, botStance)); + myBot->SetSpellTypeMinManaLimit(spellType, myBot->GetDefaultSpellTypeMinManaLimit(spellType, botStance)); + myBot->SetSpellTypeMaxManaLimit(spellType, myBot->GetDefaultSpellTypeMaxManaLimit(spellType, botStance)); + myBot->SetSpellTypeMinHPLimit(spellType, myBot->GetDefaultSpellTypeMinHPLimit(spellType, botStance)); + myBot->SetSpellTypeMaxHPLimit(spellType, myBot->GetDefaultSpellTypeMaxHPLimit(spellType, botStance)); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + myBot->SetSpellTypeAEOrGroupTargetCount(spellType, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); + myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); + myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); + myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); + myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); + myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); + myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); + myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); + myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); } } @@ -408,26 +412,26 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "all")) { for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + myBot->SetBotBaseSetting(i, myBot->GetDefaultBotBaseSetting(i, botStance)); } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); + myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); + myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); + myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); + myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); + myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); + myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); + myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); + myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); }; - my_bot->ResetBotSpellSettings(); + myBot->ResetBotSpellSettings(); output = "settings"; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 20715d0d7f..e60a17961f 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2211,14 +2211,15 @@ bool BotDatabase::LoadBotSettings(Mob* m) } uint32 mobID = (m->IsClient() ? m->CastToClient()->CharacterID() : m->CastToBot()->GetBotID()); + uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {}", mobID); + query = fmt::format("`char_id` = {} AND `stance` = {}", mobID, stanceID); } else { - query = fmt::format("`bot_id` = {}", mobID); + query = fmt::format("`bot_id` = {} AND `stance` = {}", mobID, stanceID); } const auto& l = BotSettingsRepository::GetWhere(database, query); @@ -2228,12 +2229,24 @@ bool BotDatabase::LoadBotSettings(Mob* m) } for (const auto& e : l) { - LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." - , m->GetCleanName() - , (e.setting_type == BotSettingCategories::BaseSetting ? m->CastToBot()->GetBotSettingCategoryName(e.setting_id) : m->CastToBot()->GetBotSpellCategoryName(e.setting_id)) - , e.setting_id - , e.value - ); //deleteme + if (e.setting_type == BotSettingCategories::BaseSetting) { + LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." + , m->GetCleanName() + , m->CastToBot()->GetBotSettingCategoryName(e.setting_type) + , e.setting_type + , e.value + ); //deleteme + } + else { + LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}]." + , m->GetCleanName() + , m->CastToBot()->GetBotSpellCategoryName(e.setting_type) + , e.setting_type + , m->GetSpellTypeNameByID(e.setting_id) + , e.setting_id + , e.value + ); //deleteme + } m->SetBotSetting(e.setting_type, e.setting_id, e.value); } @@ -2251,16 +2264,17 @@ bool BotDatabase::SaveBotSettings(Mob* m) return false; } - uint32 botID = (m->IsClient() ? 0 : m->CastToBot()->GetBotID()); + uint32 botID = (m->IsBot() ? m->CastToBot()->GetBotID() : 0); uint32 charID = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); + uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {}", charID); + query = fmt::format("`char_id` = {} AND `stance` = {}", charID, stanceID); } else { - query = fmt::format("`bot_id` = {}", botID); + query = fmt::format("`bot_id` = {} AND `stance` = {}", botID, stanceID); } BotSettingsRepository::DeleteWhere(database, query); @@ -2268,16 +2282,19 @@ bool BotDatabase::SaveBotSettings(Mob* m) std::vector v; if (m->IsBot()) { + uint8 botStance = m->CastToBot()->GetBotStance(); + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i)) { + if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, botStance)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = charID, - .bot_id = botID, - .setting_id = static_cast(i), - .setting_type = static_cast(BotSettingCategories::BaseSetting), - .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), - .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), - .setting_name = m->CastToBot()->GetBotSettingCategoryName(i) + .char_id = charID, + .bot_id = botID, + .stance = stanceID, + .setting_id = static_cast(i), + .setting_type = static_cast(BotSettingCategories::BaseSetting), + .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), + .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->CastToBot()->GetBotSettingCategoryName(i) }; v.emplace_back(e); @@ -2288,10 +2305,11 @@ bool BotDatabase::SaveBotSettings(Mob* m) for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { - if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x)) { + if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x, botStance)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, + .stance = stanceID, .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToBot()->GetSetting(i, x), @@ -2301,7 +2319,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x)); //deleteme + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, botStance)); //deleteme } } } @@ -2312,6 +2330,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, + .stance = stanceID, .setting_id = static_cast(BotBaseSettings::IllusionBlock), .setting_type = static_cast(BotSettingCategories::BaseSetting), .value = m->GetIllusionBlock(), @@ -2331,6 +2350,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, + .stance = stanceID, .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToClient()->GetBotSetting(i, x), diff --git a/zone/client.cpp b/zone/client.cpp index a5bc2f3873..a03e6f9318 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13080,6 +13080,8 @@ void Client::ClientToNpcAggroProcess() } void Client::LoadDefaultBotSettings() { + _spellSettings.clear(); + // Only illusion block supported currently SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme @@ -13132,21 +13134,21 @@ int Client::GetBotSetting(uint8 settingType, uint16 botSetting) { void Client::SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue) { switch (settingType) { - case BotSettingCategories::BaseSetting: - SetBaseSetting(botSetting, settingValue); - break; - case BotSettingCategories::SpellHold: - SetSpellHold(botSetting, settingValue); - break; - case BotSettingCategories::SpellDelay: - SetSpellDelay(botSetting, settingValue); - break; - case BotSettingCategories::SpellMinThreshold: - SetSpellMinThreshold(botSetting, settingValue); - break; - case BotSettingCategories::SpellMaxThreshold: - SetSpellMaxThreshold(botSetting, settingValue); - break; + case BotSettingCategories::BaseSetting: + SetBaseSetting(botSetting, settingValue); + break; + case BotSettingCategories::SpellHold: + SetSpellHold(botSetting, settingValue); + break; + case BotSettingCategories::SpellDelay: + SetSpellDelay(botSetting, settingValue); + break; + case BotSettingCategories::SpellMinThreshold: + SetSpellMinThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellMaxThreshold: + SetSpellMaxThreshold(botSetting, settingValue); + break; } } diff --git a/zone/mob.cpp b/zone/mob.cpp index fc255188dc..d472eb7f7b 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9063,18 +9063,30 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { return spellTypeName; } -bool Mob::GetDefaultSpellHold(uint16 spellType) { +bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::DOT: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::Assist: + return true; + default: + return false; + } case BotSpellTypes::AENukes: case BotSpellTypes::AERains: - case BotSpellTypes::AEMez: case BotSpellTypes::AEStun: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: case BotSpellTypes::AEDoT: case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: + switch (stance) { + case Stance::AEBurn: + return false; + default: + return true; + } + case BotSpellTypes::AESnare: case BotSpellTypes::AERoot: case BotSpellTypes::Root: case BotSpellTypes::AEDispel: @@ -9082,28 +9094,46 @@ bool Mob::GetDefaultSpellHold(uint16 spellType) { case BotSpellTypes::AEFear: case BotSpellTypes::Fear: return true; - case BotSpellTypes::Snare: - if (GetClass() == Class::Wizard) { - return true; + case BotSpellTypes::AEMez: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::HateRedux: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; } - else { - return false; + case BotSpellTypes::Snare: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Assist: + return true; + default: + if (GetClass() == Class::Wizard) { + return true; + } + else { + return false; + } } case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: case BotSpellTypes::PreCombatBuffSong: if (GetClass() == Class::Bard) { - return true; + return false; } else { - return false; + return true; } default: return false; } } -uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { +uint16 Mob::GetDefaultSpellDelay(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: @@ -9111,12 +9141,31 @@ uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: return 2500; - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: return 4000; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 8000; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return 22000; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 2000; + case Stance::Efficient: + return 8000; + default: + return 4000; + } case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::PBAENuke: @@ -9129,43 +9178,68 @@ uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { case BotSpellTypes::Slow: case BotSpellTypes::AEStun: case BotSpellTypes::Stun: - return 6000; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 3000; + case Stance::Efficient: + return 10000; + default: + return 6000; + } case BotSpellTypes::AERoot: case BotSpellTypes::Root: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::PetCompleteHeals: - return 8000; + return 8000; case BotSpellTypes::Fear: case BotSpellTypes::AEFear: return 15000; - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetHoTHeals: - return 22000; default: return 1; } } -uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType) { +uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance) { switch (spellType) { - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: case BotSpellTypes::AEDebuff: case BotSpellTypes::Debuff: case BotSpellTypes::AEDispel: case BotSpellTypes::Dispel: case BotSpellTypes::AESlow: case BotSpellTypes::Slow: - return 20; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 20; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 5; + } case BotSpellTypes::AEDoT: case BotSpellTypes::DOT: - return 35; - case BotSpellTypes::Charm: - return 50; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + case Stance::Efficient: + return 40; + default: + return 25; + } case BotSpellTypes::Mez: case BotSpellTypes::AEMez: return 85; @@ -9174,25 +9248,60 @@ uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType) { } } -uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { +uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::Escape: case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: - return 25; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 40; + case Stance::Efficient: + default: + return 25; + } case BotSpellTypes::AELifetap: case BotSpellTypes::Lifetap: case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: - return 40; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 55; + case Stance::Efficient: + return 35; + default: + return 40; + } case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: - return 60; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 70; + case Stance::Efficient: + return 50; + default: + return 60; + } case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::PetCompleteHeals: - return 80; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 90; + case Stance::Efficient: + return 65; + default: + return 80; + } case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::PBAENuke: @@ -9212,7 +9321,17 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { case BotSpellTypes::AEDebuff: case BotSpellTypes::Debuff: case BotSpellTypes::Stun: - return 99; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 100; + case Stance::Aggressive: + return 100; + case Stance::Efficient: + return 90; + default: + return 99; + } case BotSpellTypes::Buff: case BotSpellTypes::Charm: case BotSpellTypes::Cure: @@ -9237,7 +9356,16 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { return 60; } else { - return 90; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 95; + case Stance::Efficient: + return 80; + default: + return 90; + } } default: return 100; diff --git a/zone/mob.h b/zone/mob.h index 6da29429f4..662798af57 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -437,10 +437,10 @@ class Mob : public Entity { std::string GetSpellTypeNameByID(uint16 spellType); std::string GetSpellTypeShortNameByID(uint16 spellType); - bool GetDefaultSpellHold(uint16 spellType); - uint16 GetDefaultSpellDelay(uint16 spellType); - uint8 GetDefaultSpellMinThreshold(uint16 spellType); - uint8 GetDefaultSpellMaxThreshold(uint16 spellType); + bool GetDefaultSpellHold(uint16 spellType, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellDelay(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance = Stance::Balanced); inline bool GetSpellHold(uint16 spellType) const { return _spellSettings[spellType].hold; } void SetSpellHold(uint16 spellType, bool holdStatus); From 2dfa1681c46f878130951de5b99522edc810c963 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:30:24 -0600 Subject: [PATCH 010/394] Make rogue/monk evade logic more accurate to players --- zone/bot.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e7d4737d7f..79b7a02f0d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2607,8 +2607,16 @@ bool Bot::TryFacingTarget(Mob* tar) { bool Bot::TryEvade(Mob* tar) { - if (HasTargetReflection() && !tar->IsFeared() && !tar->IsStunned()) { - if (GetClass() == Class::Rogue && !GetSpellHold(BotSpellTypes::Escape)) { + if (GetSpellHold(BotSpellTypes::Escape)) { + return false; + } + + switch (GetClass()) { + case Class::Rogue: { + if (GetSkill(EQ::skills::SkillHide) == 0) { + return false; + } + if (m_rogue_evade_timer.Check(false)) { int timer_duration = (HideReuseTime - GetSkillReuseTime(EQ::skills::SkillHide)) * 1000; @@ -2617,18 +2625,47 @@ bool Bot::TryEvade(Mob* tar) { } m_rogue_evade_timer.Start(timer_duration); - BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) { + + } + + float hidechance = ((GetSkill(EQ::skills::SkillHide) / 250.0f) + .25) * 100; + float random = zone->random.Real(0, 100); + + if (random < hidechance) { //SendAppearancePacket(AT_Invis, Invisibility::Invisible); + + if (spellbonuses.ShroudofStealth || aabonuses.ShroudofStealth || itembonuses.ShroudofStealth) { + improved_hidden = true; + hidden = true; + } + else { + hidden = true; + } + } + + if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) { + BotGroupSay(this, "I have momentarily ducked away from the main combat."); RogueEvade(tar); } + else { + BotGroupSay(this, "My attempts at ducking clear of combat fail."); + } //SendAppearancePacket(AT_Invis, Invisibility::Visible); + hidden = false; + return true; } + + break; } - else if (GetClass() == Class::Monk && GetLevel() >= 17 && !GetSpellHold(BotSpellTypes::Escape)) { + case Class::Monk: { + if (GetSkill(EQ::skills::SkillFeignDeath) == 0) { + return false; + } + if (m_monk_evade_timer.Check(false)) { int timer_duration = (FeignDeathReuseTime - GetSkillReuseTime(EQ::skills::SkillFeignDeath)) * 1000; @@ -2637,13 +2674,26 @@ bool Bot::TryEvade(Mob* tar) { } m_monk_evade_timer.Start(timer_duration); - BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); - if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillFeignDeath)) { + uint16 primfeign = GetSkill(EQ::skills::SkillFeignDeath); + uint16 secfeign = GetSkill(EQ::skills::SkillFeignDeath); + if (primfeign > 100) { + primfeign = 100; + secfeign = secfeign - 100; + secfeign = secfeign / 2; + } + else + secfeign = 0; + + uint16 totalfeign = primfeign + secfeign; + + if (zone->random.Real(0, 160) > totalfeign) { //SendAppearancePacket(AT_Anim, ANIM_DEATH); - entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); + BotGroupSay(this, "I have fallen to the ground."); + SetFeigned(false); } else { + BotGroupSay(this, "I have successfully feigned my death."); SetFeigned(true); //SendAppearancePacket(AT_Anim, ANIM_DEATH); } @@ -7110,6 +7160,10 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl continue; } + if (caster == tar) { + continue; + } + uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(spellType) : 100; if (!caster->PrecastChecks(tar, spellType)) { From 775511c935104cb3e73dc66b069432f7d232777e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:31:48 -0600 Subject: [PATCH 011/394] more target validation for bots to prevent pets from getting hit with AEs and pets trying to attack invalid targets --- zone/aggro.cpp | 21 +++++++++++++++++++-- zone/bot.cpp | 7 +++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 1a8c7b9290..4f1d658319 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -745,10 +745,27 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); - if(target_owner && target_owner == this) + Mob* target_ultimate_owner = target->GetUltimateOwner(); + Mob* our_ultimate_owner = GetUltimateOwner(); + + if (target_owner && target_owner == this) { + return false; + } + else if ( + IsBot() && target_ultimate_owner && + ( + (target_ultimate_owner == our_ultimate_owner) || + (target_ultimate_owner->IsOfClientBot()) + ) + ) { return false; - else if(our_owner && our_owner == target) + } + else if (our_owner && our_owner == target) { return false; + } + else if (IsBot() && our_ultimate_owner && our_ultimate_owner == target) { + return false; + } // invalidate for swarm pets for later on if their owner is a corpse if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner && diff --git a/zone/bot.cpp b/zone/bot.cpp index 79b7a02f0d..0737d09619 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2113,6 +2113,13 @@ void Bot::AI_Process() // TARGET VALIDATION if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) { + if (HasPet()) { + if (tar && GetPet()->GetTarget() && GetPet()->GetTarget() == tar) { + GetPet()->WipeHateList(); + GetPet()->SetTarget(nullptr); + } + } + return; } From c22d687d72a5ee6587ef40b64ca89ca733deb5da Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:33:59 -0600 Subject: [PATCH 012/394] misc command and rule cleanup --- common/ruletypes.h | 8 ++++---- zone/bot_commands/follow.cpp | 2 +- zone/bot_commands/illusion_block.cpp | 7 ------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index fcbe55874b..526373e9de 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -805,9 +805,9 @@ RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to RULE_INT(Bots, PercentChanceToCastHateRedux, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastFear, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastOtherType, 90, "The chance for a bot to attempt to cast the remaining spell types in combat. Default 0-%.") -RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 250, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 250, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 250ms.") RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2000ms.") -RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 125, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 200ms.") +RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 125, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 125ms.") RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 500ms.") RULE_INT(Bots, MezChance, 35, "35 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.") RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.") @@ -852,13 +852,13 @@ RULE_BOOL(Bots, PreventBotSpawnOnFD, true, "True Default. If true, players will RULE_BOOL(Bots, PreventBotSpawnOnEngaged, true, "True Default. If true, players will not be able to spawn bots while you, your group or raid are engaged.") RULE_BOOL(Bots, PreventBotCampOnEngaged, true, "True Default. If true, players will not be able to camp bots while you, your group or raid are engaged.") RULE_BOOL(Bots, CopySettingsOwnBotsOnly, true, "Determines whether a bot you are copying settings from must be a bot you own or not, default true.") -RULE_BOOL(Bots, AllowCopySettingsAnon, true, "If player's are allowed to copy settings of bots owned by anonymous players.") +RULE_BOOL(Bots, AllowCopySettingsAnon, false, "If player's are allowed to copy settings of bots owned by anonymous players.") RULE_BOOL(Bots, AllowCharmedPetBuffs, true, "Whether or not bots are allowed to cast buff charmed pets, default true.") RULE_BOOL(Bots, AllowCharmedPetHeals, true, "Whether or not bots are allowed to cast heal charmed pets, default true.") RULE_BOOL(Bots, AllowCharmedPetCures, true, "Whether or not bots are allowed to cast cure charmed pets, default true.") RULE_BOOL(Bots, ShowResistMessagesToOwner, true, "Default True. If enabled, when a bot's spell is resisted it will send a spell failure to their owner.") RULE_BOOL(Bots, BotBuffLevelRestrictions, true, "Buffs will not land on low level bots like live players") -RULE_BOOL(Bots, BotsUseLiveBlockedMessage, false, "Setting whether detailed spell block messages should be used for bots as players do on the live servers") +RULE_BOOL(Bots, BotsUseLiveBlockedMessage, true, "Setting whether detailed spell block messages should be used for bots as players do on the live servers") RULE_BOOL(Bots, BotSoftDeletes, true, "When bots are deleted, they are only soft deleted") RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass the anti-spam system") RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Default 120.") diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index c5b7f8f780..f970773d7b 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -200,7 +200,7 @@ void bot_command_follow(Client* c, const Seperator* sep) nextTar = target_mob; } } - LogTestDebug("{} is now following {}.", bot_iter->GetCleanName(), nextTar->GetCleanName()); //deleteme + chainList.push_back(bot_iter); ++it; ++count; diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 0a3625046a..3699fa0806 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -63,13 +63,6 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); return; } From 31a4f053b5eea7ee91182c5e22d5624cd1bd582a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:34:20 -0600 Subject: [PATCH 013/394] bot movement cleanup and tweaks, move casterrange to distanceranged --- .../database_update_manifest_bots.cpp | 8 +- common/ruletypes.h | 5 +- zone/bot.cpp | 127 ++++++------------ zone/bot.h | 15 +-- zone/bot_command.cpp | 4 +- zone/bot_command.h | 2 +- zone/bot_commands/bot_settings.cpp | 2 +- zone/bot_commands/copy_settings.cpp | 2 +- zone/bot_commands/default_settings.cpp | 2 +- .../{caster_range.cpp => distance_ranged.cpp} | 39 +++--- 10 files changed, 76 insertions(+), 130 deletions(-) rename zone/bot_commands/{caster_range.cpp => distance_ranged.cpp} (68%) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 639d173b06..a57a6f0753 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -209,18 +209,18 @@ INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM b INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' +SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'DistanceRanged' FROM ( SELECT `bot_id`, (CASE WHEN (`class` IN (1, 7, 19, 16)) THEN 0 WHEN `class` = 8 THEN 0 ELSE 90 - END) AS `casterRange`, + END) AS `DistanceRanged`, `caster_range` FROM bot_data ) AS `subquery` -WHERE `casterRange` != `caster_range`; +WHERE `DistanceRanged` != `caster_range`; ALTER TABLE `bot_data` DROP COLUMN `show_helm`; @@ -241,7 +241,7 @@ UPDATE `bot_command_settings` SET `aliases`= 'bh' WHERE `bot_command`='behindmob UPDATE `bot_command_settings` SET `aliases`= 'bs|settings' WHERE `bot_command`='botsettings'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|followdistance') ELSE 'followd||followdistance' END WHERE `bot_command`='botfollowdistance' AND `aliases` NOT LIKE '%followdistance%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ranged|toggleranged|btr') ELSE 'ranged|toggleranged|btr' END WHERE `bot_command`='bottoggleranged' AND `aliases` NOT LIKE '%ranged|toggleranged|btr%'; -UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|cr') ELSE 'cr' END WHERE `bot_command`='casterrange' AND `aliases` NOT LIKE '%cr%'; +UPDATE `bot_command_settings` SET `aliases`= 'distranged|dr' WHERE `bot_command`='distanceranged'; UPDATE `bot_command_settings` SET `aliases`= 'copy' WHERE `bot_command`='copysettings'; UPDATE `bot_command_settings` SET `aliases`= 'default' WHERE `bot_command`='defaultsettings'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|enforce') ELSE 'enforce' END WHERE `bot_command`='enforcespellsettings' AND `aliases` NOT LIKE '%enforce%'; diff --git a/common/ruletypes.h b/common/ruletypes.h index 526373e9de..c1e46b5aee 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -833,15 +833,13 @@ RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") -RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") +RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") RULE_REAL(Bots, PercentMinMeleeDistance, 0.60, "Multiplier of the max melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.") RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .") RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.25, "Multiplier of max melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.") RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.") -RULE_BOOL(Bots, CastersStayJustOutOfMeleeRange, true, "True Default. If true, caster bots will stay just out of melee range. Otherwise they use Bots:PercentMinCasterRangeDistance.") -RULE_REAL(Bots, PercentMinCasterRangeDistance, 0.60, "Multiplier of the closest caster range at which a bot will stand while casting before trying to adjust. 0.60 Recommended.") RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.") RULE_REAL(Bots, DistanceTauntingBotsStickMainHate, 25.00, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, false, "False Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") @@ -869,6 +867,7 @@ RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TG RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") +RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0737d09619..bd62fdc628 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2148,8 +2148,9 @@ void Bot::AI_Process() float melee_distance_min = 0.0f; float melee_distance_max = 0.0f; float melee_distance = 0.0f; + tar_distance = sqrt(tar_distance); - CheckCombatRange(tar, sqrt(tar_distance), atCombatRange, behindMob, p_item, s_item, melee_distance_min, melee_distance_max, melee_distance, stopMeleeLevel); + CheckCombatRange(tar, tar_distance, atCombatRange, behindMob, p_item, s_item, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); // PULLING FLAG (ACTIONABLE RANGE) @@ -2227,7 +2228,7 @@ void Bot::AI_Process() return; } - if (IsBotRanged() && ranged_timer.Check(false)) { // Can shoot mezzed, stunned and dead!? + if (IsBotRanged() && ranged_timer.Check(false)) { TryRangedAttack(tar); if (!TargetValidation(tar)) { return; } @@ -2237,10 +2238,6 @@ void Bot::AI_Process() } } else if (!IsBotRanged() && GetLevel() < stopMeleeLevel) { - if (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())) { - return; - } - if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) { DoClassAttacks(tar); } @@ -2715,7 +2712,7 @@ bool Bot::TryEvade(Mob* tar) { return false; } -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance_max, float& melee_distance, uint8 stopMeleeLevel) { +void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { atCombatRange= false; p_item = GetBotItem(EQ::invslot::slotPrimary); @@ -2731,16 +2728,16 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo } // Calculate melee distances - CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_max, melee_distance, melee_distance_min, stopMeleeLevel); + CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); - //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be {} [{} - {}] away." + //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be between [{} - {}] away. MMR is {}." // , GetCleanName() - // , (tar_distance <= melee_distance ? "within range of" : "too far away from") + // , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) // , tar->GetCleanName() // , tar_distance // , (tar_distance <= melee_distance ? "but only needed" : "but need to be") - // , melee_distance // , melee_distance_min + // , melee_distance // , melee_distance_max //); //deleteme @@ -2749,16 +2746,14 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo } } -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_max, float& melee_distance, float& melee_distance_min, uint8 stopMeleeLevel) { +void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { float size_mod = GetSize(); float other_size_mod = tar->GetSize(); - bool resquareDistance = false; // For races with a fixed size if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) { // size_mod = 60.0f; } - else if (size_mod < 6.0f) { size_mod = 8.0f; } @@ -2767,7 +2762,6 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it if (tar->GetRace() == Race::LavaDragon || tar->GetRace() == Race::Wurm || tar->GetRace() == Race::GhostDragon) { other_size_mod = 60.0f; } - else if (other_size_mod < 6.0f) { other_size_mod = 8.0f; } @@ -2779,11 +2773,9 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it if (size_mod > 29.0f) { size_mod *= size_mod; } - else if (size_mod > 19.0f) { size_mod *= (size_mod * 2.0f); } - else { size_mod *= (size_mod * 4.0f); } @@ -2792,6 +2784,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it { size_mod *= 1.75; } + if (tar->GetRace() == Race::DragonSkeleton) // Dracoliche in Fear. Skeletal Dragon { size_mod *= 2.25; @@ -2805,6 +2798,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it } melee_distance_max = size_mod; + if (!RuleB(Bots, UseFlatNormalMeleeRange)) { switch (GetClass()) { case Class::Warrior: @@ -2872,31 +2866,26 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it melee_distance = melee_distance_max * RuleR(Bots, TauntNormalMeleeRangeDistance); } - if (!taunting && !IsBotRanged() && GetMaxMeleeRange()) { + bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; + + if (!taunting && !IsBotRanged() && !isStopMeleeLevel && GetMaxMeleeRange()) { melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); } - - /* Caster Range Checks */ - bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; - if (isStopMeleeLevel) { - melee_distance = GetBotCasterMaxRange(melee_distance_max); - if (RuleB(Bots, CastersStayJustOutOfMeleeRange)) { - melee_distance_min = melee_distance_max + 1; - } - else { - melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinCasterRangeDistance); - } + if (isStopMeleeLevel && !IsBotRanged()) { + float desiredRange = GetBotDistanceRanged(); + melee_distance_min = std::min(melee_distance, (desiredRange / 2)); + melee_distance = std::max((melee_distance + 1), desiredRange); } /* Archer Checks*/ if (IsBotRanged()) { - float archeryRange = GetBotRangedValue(); - float casterRange = GetBotCasterRange(); - float minArcheryRange = RuleI(Combat, MinRangedAttackDist); - melee_distance = std::min(archeryRange, (casterRange * 2)); - melee_distance_min = std::max(std::max(minArcheryRange, (melee_distance_max + 1)), std::min(casterRange, archeryRange)); + float minDistance = RuleI(Combat, MinRangedAttackDist); + float maxDistance = GetBotRangedValue(); + float desiredRange = GetBotDistanceRanged(); + melee_distance_min = std::max(minDistance, (desiredRange / 2)); + melee_distance = std::min(maxDistance, desiredRange); } } @@ -8490,29 +8479,6 @@ void Bot::SendSpellAnim(uint16 target_id, uint16 spell_id) entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); } -float Bot::GetBotCasterMaxRange(float melee_distance_max) {// Calculate caster distances - float caster_distance_max = 0.0f; - float caster_distance_min = 0.0f; - float caster_distance = 0.0f; - - caster_distance_max = GetBotCasterRange(); - - if (!GetBotCasterRange() && GetLevel() >= GetStopMeleeLevel() && GetClass() >= Class::Warrior && GetClass() <= Class::Berserker) { - caster_distance_max = GetDefaultBotBaseSetting(BotBaseSettings::CasterRange); - } - - if (caster_distance_max) { - caster_distance_min = melee_distance_max; - - if (caster_distance_max <= caster_distance_min) { - caster_distance_max = caster_distance_min * 1.25f; - } - } - - return caster_distance_max; -} - - int32 Bot::CalcItemATKCap() { return RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; @@ -9982,8 +9948,8 @@ void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { case BotBaseSettings::BehindMob: SetBehindMob(settingValue); break; - case BotBaseSettings::CasterRange: - SetBotCasterRange(settingValue); + case BotBaseSettings::DistanceRanged: + SetBotDistanceRanged(settingValue); break; case BotBaseSettings::IllusionBlock: SetIllusionBlock(settingValue); @@ -10031,9 +9997,9 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { case BotBaseSettings::BehindMob: //LogBotSettingsDetail("Returning current GetBehindMob of [{}] for [{}]", GetBehindMob(), GetCleanName()); //deleteme return GetBehindMob(); - case BotBaseSettings::CasterRange: - //LogBotSettingsDetail("Returning current GetBotCasterRange of [{}] for [{}]", GetBotCasterRange(), GetCleanName()); //deleteme - return GetBotCasterRange(); + case BotBaseSettings::DistanceRanged: + //LogBotSettingsDetail("Returning current GetBotDistanceRanged of [{}] for [{}]", GetBotDistanceRanged(), GetCleanName()); //deleteme + return GetBotDistanceRanged(); case BotBaseSettings::IllusionBlock: //LogBotSettingsDetail("Returning current GetIllusionBlock of [{}] for [{}]", GetIllusionBlock(), GetCleanName()); //deleteme return GetIllusionBlock(); @@ -10080,7 +10046,7 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance) { else { return false; } - case BotBaseSettings::CasterRange: + case BotBaseSettings::DistanceRanged: switch (GetClass()) { case Class::Warrior: case Class::Monk: @@ -10717,8 +10683,8 @@ std::string Bot::GetBotSettingCategoryName(uint8 setting_type) { return "PetSetTypeSetting"; case BotBaseSettings::BehindMob: return "BehindMob"; - case BotBaseSettings::CasterRange: - return "CasterRange"; + case BotBaseSettings::DistanceRanged: + return "DistanceRanged"; case BotBaseSettings::IllusionBlock: return "IllusionBlock"; case BotBaseSettings::MaxMeleeRange: @@ -11036,7 +11002,7 @@ void Bot::SetCombatJitter() { void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob) { if (HasTargetReflection()) { - if (!tar->IsFeared() && !tar->IsStunned()) { + if (!taunting && !tar->IsFeared() && !tar->IsStunned()) { if (TryEvade(tar)) { return; } @@ -11044,14 +11010,14 @@ void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, flo if (tar->IsRooted() && !taunting) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay if (tar_distance <= melee_distance_max) { - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, false, true)) { + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, taunting)) { RunToGoalWithJitter(Goal); return; } } } - if (taunting && tar_distance < melee_distance_min) { // Back up any taunting bots that are too close + if (taunting && tar_distance < melee_distance_min) { // Back up any bots that are too close if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, taunting)) { RunToGoalWithJitter(Goal); return; @@ -11087,25 +11053,12 @@ void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, flo RunToGoalWithJitter(Goal); return; } - //else { - // if (stopMeleeLevel || IsBotArcher()) { - // if (IsBotArcher()) { - // float minArcheryRange = RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist); - // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, minArcheryRange, melee_distance, false, taunting)) { - // RunToGoalWithJitter(Goal); - // return; - // } - // } - // else { - // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_max + 1, melee_distance, false, taunting)) { - // RunToGoalWithJitter(Goal); - // return; - // } - // } - // } - // DoFaceCheckWithJitter(tar); - // return; - //} + } + else if (tar->IsEnraged() && !taunting && !stopMeleeLevel && !behindMob) { // Move non-taunting melee bots behind target during enrage + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) { + RunToGoalWithJitter(Goal); + return; + } } } } diff --git a/zone/bot.h b/zone/bot.h index 553148ee51..a2e0a8ffbc 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -136,7 +136,7 @@ namespace BotBaseSettings { constexpr uint16 RangedSetting = 5; constexpr uint16 PetSetTypeSetting = 6; constexpr uint16 BehindMob = 7; - constexpr uint16 CasterRange = 8; + constexpr uint16 DistanceRanged = 8; constexpr uint16 IllusionBlock = 9; constexpr uint16 MaxMeleeRange = 10; constexpr uint16 MedInCombat = 11; @@ -506,8 +506,8 @@ class Bot : public NPC { void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; } uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; } - uint32 GetBotCasterRange() const { return _casterRange; } - void SetBotCasterRange(uint32 casterRange) { _casterRange = casterRange; } + uint32 GetBotDistanceRanged() const { return _distanceRanged; } + void SetBotDistanceRanged(uint32 distanceRanged) { _distanceRanged = distanceRanged; } bool GetMedInCombat() const { return _medInCombat; } void SetMedInCombat(bool value) { _medInCombat = value; } uint8 GetHPWhenToMed() const { return _HPWhenToMed; } @@ -640,7 +640,6 @@ class Bot : public NPC { bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; } uint8 GetBotStance() { return _botStance; } uint8 GetChanceToCastBySpellType(uint16 spellType); - float GetBotCasterMaxRange(float melee_distance_max); bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; } bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; } bool IsGroupNuker() const { return m_CastingRoles.GroupNuker; } @@ -929,9 +928,9 @@ class Bot : public NPC { const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, - float& melee_distance_max, - float& melee_distance, float& melee_distance_min, + float& melee_distance, + float& melee_distance_max, uint8 stopMeleeLevel ); @@ -947,8 +946,8 @@ class Bot : public NPC { const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, - float& melee_distance_max, float& melee_distance, + float& melee_distance_max, uint8 stopMeleeLevel ); bool GetCombatJitterFlag() { return m_combat_jitter_flag; } @@ -1065,7 +1064,7 @@ class Bot : public NPC { bool _showHelm; bool _botRangedSetting; uint8 _stopMeleeLevel; - uint32 _casterRange; + uint32 _distanceRanged; bool _behindMobStatus; bool _maxMeleeRangeStatus; bool _medInCombat; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 3f6dfef5b1..80be595f89 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1285,7 +1285,7 @@ int bot_command_init(void) bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_command_update) || bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_command_woad) || bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || - bot_command_add("casterrange", "Controls the range casters will try to stay away from a mob (if too far, they will skip spells that are out-of-range)", AccountStatus::Player, bot_command_caster_range) || + bot_command_add("distanceranged", "Controls the range casters and ranged will try to stay away from a mob", AccountStatus::Player, bot_command_distance_ranged) || bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_circle) || bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || @@ -2256,7 +2256,6 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/bot.cpp" #include "bot_commands/bot_settings.cpp" #include "bot_commands/cast.cpp" -#include "bot_commands/caster_range.cpp" #include "bot_commands/charm.cpp" #include "bot_commands/class_race_list.cpp" #include "bot_commands/click_item.cpp" @@ -2265,6 +2264,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/default_settings.cpp" #include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" +#include "bot_commands/distance_ranged.cpp" #include "bot_commands/escape.cpp" #include "bot_commands/find_aliases.cpp" #include "bot_commands/follow.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 53cb2776bc..a8951a7cd4 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1673,7 +1673,7 @@ void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep); void bot_command_bot_settings(Client* c, const Seperator* sep); void bot_command_cast(Client* c, const Seperator* sep); -void bot_command_caster_range(Client* c, const Seperator* sep); +void bot_command_distance_ranged(Client* c, const Seperator* sep); void bot_command_charm(Client *c, const Seperator *sep); void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp index e94bea5f1a..4dc9babdf2 100644 --- a/zone/bot_commands/bot_settings.cpp +++ b/zone/bot_commands/bot_settings.cpp @@ -4,7 +4,7 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) { std::list subcommand_list; subcommand_list.push_back("behindmob"); - subcommand_list.push_back("casterrange"); + subcommand_list.push_back("distanceranged"); subcommand_list.push_back("copysettings"); subcommand_list.push_back("defaultsettings"); subcommand_list.push_back("enforcespelllist"); diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index fafead14c1..e2745442f7 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -74,7 +74,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) "[misc] copies all miscellaneous options such as:", "- ^showhelm, ^followd, ^stopmeleelevel", "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", - "- ^behindmob, ^casterrange, ^illusionblock", + "- ^behindmob, ^distanceranged, ^illusionblock", "- ^sitincombat, ^sithppercent and ^sitmanapercent", }; diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 87bb31b0f0..ddf809a751 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -68,7 +68,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) "[misc] restores all miscellaneous options such as:", "- ^showhelm, ^followd, ^stopmeleelevel", "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", - "- ^behindmob, ^casterrange, ^illusionblock", + "- ^behindmob, ^distanceranged, ^illusionblock", "- ^sitincombat, ^sithppercent and ^sitmanapercent", }; diff --git a/zone/bot_commands/caster_range.cpp b/zone/bot_commands/distance_ranged.cpp similarity index 68% rename from zone/bot_commands/caster_range.cpp rename to zone/bot_commands/distance_ranged.cpp index 87676c7d46..70d5e655b4 100644 --- a/zone/bot_commands/caster_range.cpp +++ b/zone/bot_commands/distance_ranged.cpp @@ -1,14 +1,13 @@ #include "../bot_command.h" -void bot_command_caster_range(Client* c, const Seperator* sep) +void bot_command_distance_ranged(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_caster_range", sep->arg[0], "casterrange")) { + if (helper_command_alias_fail(c, "bot_command_distance_ranged", sep->arg[0], "distanceranged")) { return; } if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "note: Can only be used for Casters or Hybrids."); c->Message(Chat::White, "note: Use [current] to check the current setting."); c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target."); c->Message(Chat::White, "note: If they are too far for a spell, it will be skipped."); @@ -21,13 +20,13 @@ void bot_command_caster_range(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; int ab_arg = 1; bool current_check = false; - uint32 crange = 0; + uint32 value = 0; if (sep->IsNumber(1)) { ++ab_arg; - crange = atoi(sep->arg[1]); - if (crange < 0 || crange > 300) { - c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); + value = atoi(sep->arg[1]); + if (value < 0 || value > RuleI(Bots, MaxDistanceRanged)) { + c->Message(Chat::Yellow, "You must enter a value within the range of 0 - 300."); return; } } @@ -36,7 +35,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) current_check = true; } else { - c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + c->Message(Chat::Yellow, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); return; } @@ -58,47 +57,43 @@ void bot_command_caster_range(Client* c, const Seperator* sep) int success_count = 0; for (auto my_bot : sbl) { - if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { - continue; - } - if (!first_found) { first_found = my_bot; } if (current_check) { c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} says, 'My current caster range is {}.'", + "{} says, 'My current Distance Ranged is {}.'", my_bot->GetCleanName(), - my_bot->GetBotCasterRange() + my_bot->GetBotDistanceRanged() ).c_str() ); } else { - my_bot->SetBotCasterRange(crange); + my_bot->SetBotDistanceRanged(value); ++success_count; } } if (!current_check) { if (success_count == 1 && first_found) { c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} says, 'My Caster Range was set to {}.'", + "{} says, 'My Distance Ranged was set to {}.'", first_found->GetCleanName(), - crange + value ).c_str() ); } else { c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} of your bots set their Caster Range to {}.", + "{} of your bots set their Distance Ranged to {}.", success_count, - crange + value ).c_str() ); } From 2d1b34d0cbef2cac0470d6d33974728acd071a5d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 8 Nov 2024 08:41:18 -0600 Subject: [PATCH 014/394] command cleanup --- zone/bot_commands/illusion_block.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 3699fa0806..e44ddf6011 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -63,6 +63,14 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + return; } From 57e9b62a48794b1e21a6815f9c43ee357391d42c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:59:01 -0600 Subject: [PATCH 015/394] Add viral, fear, stun, knockback, gravityeffect support to bots --- zone/bot.cpp | 54 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 47 insertions(+), 7 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index bd62fdc628..0a9a20639f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -588,7 +588,7 @@ void Bot::ChangeBotRangedWeapons(bool isRanged) { BotAddEquipItem(EQ::invslot::slotAmmo, GetBotItemBySlot(EQ::invslot::slotAmmo)); BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotRange)); SetAttackTimer(); - BotGroupSay(this, "My bow is true and ready"); //TODO bot rewrite - make this say throwing or bow + BotGroupSay(this, "My blades are sheathed"); } } @@ -1593,6 +1593,7 @@ bool Bot::Process() { if (IsStunned() && stunned_timer.Check()) { Mob::UnStun(); + spun_timer.Disable(); } if (!GetBotOwner()) { @@ -1600,7 +1601,6 @@ bool Bot::Process() } if (GetDepop()) { - _botOwner = nullptr; _botOwnerCharacterID = 0; @@ -1608,6 +1608,7 @@ bool Bot::Process() } ScanCloseMobProcess(); + CheckScanCloseMobsMovingTimer(); // TODO bot rewrite -- necessary for bots? SpellProcess(); if (tic_timer.Check()) { @@ -1620,7 +1621,7 @@ bool Bot::Process() BuffProcess(); CalcRestState(); - if (currently_fleeing) { + if (currently_fleeing || IsFeared()) { ProcessFlee(); } @@ -1655,6 +1656,16 @@ bool Bot::Process() } } + if (viral_timer.Check()) { // TODO bot rewrite -- necessary for bots? + VirusEffectProcess(); + } + + if (spellbonuses.GravityEffect == 1) { + if (gravity_timer.Check()) { + DoGravityEffect(); + } + } + if (GetAppearance() == eaDead && GetHP() > 0) { SetAppearance(eaStanding); } @@ -1663,7 +1674,6 @@ bool Bot::Process() ping_timer.Disable(); } else { - if (!ping_timer.Enabled()) { ping_timer.Start(BOT_KEEP_ALIVE_INTERVAL); } @@ -1684,7 +1694,19 @@ bool Bot::Process() auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); } - if (IsStunned() || IsMezzed()) { + if (ForcedMovement) { + ProcessForcedMovement(); + } + + if (IsMezzed()) { + return true; + } + + if (IsStunned()) { + if (spun_timer.Check()) { + Spin(); + } + return true; } @@ -2155,7 +2177,6 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { - //TODO bot rewrite - add ways to here to determine if throw stone is allowed, then check for ranged/spell/melee if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { return; } @@ -3130,6 +3151,25 @@ bool Bot::CheckIfIncapacitated() { return true; } + if (currently_fleeing) { + if (RuleB(Combat, EnableFearPathing) && AI_movement_timer->Check()) { + // Check if we have reached the last fear point + if (DistanceNoZ(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget) <= 5.0f) { + // Calculate a new point to run to + StopNavigation(); + CalculateNewFearpoint(); + } + + RunTo( + m_FearWalkTarget.x, + m_FearWalkTarget.y, + m_FearWalkTarget.z + ); + } + + return true; + } + return false; } @@ -10891,7 +10931,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { return false; } - switch (spellType) { //TODO bot rewrite - fix Buff/ResistBuff + switch (spellType) { case BotSpellTypes::Buff: if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return false; From 938c72619cb361c5a5afc14fab5f5030b64da0aa Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 9 Nov 2024 21:59:52 -0600 Subject: [PATCH 016/394] only load client spell types for clients --- zone/client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index a03e6f9318..9d01f181b2 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13087,6 +13087,10 @@ void Client::LoadDefaultBotSettings() { LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + BotSpellSettings_Struct t; t.spellType = i; From 0d970844d303d67299762485f32a081b855710fb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:00:20 -0600 Subject: [PATCH 017/394] add passive stance checks to commands and loading/saving. shouldn't be ` --- zone/bot.cpp | 19 ++++++++++++++++++- zone/bot.h | 3 ++- zone/bot_commands/behind_mob.cpp | 5 +++++ zone/bot_commands/bot.cpp | 10 +--------- zone/bot_commands/cast.cpp | 2 +- zone/bot_commands/click_item.cpp | 6 ++++++ zone/bot_commands/distance_ranged.cpp | 4 ++++ zone/bot_commands/illusion_block.cpp | 5 +++++ zone/bot_commands/max_melee_range.cpp | 5 +++++ zone/bot_commands/sit_hp_percent.cpp | 5 +++++ zone/bot_commands/sit_in_combat.cpp | 5 +++++ zone/bot_commands/sit_mana_percent.cpp | 5 +++++ zone/bot_commands/spell_aggro_checks.cpp | 5 +++++ zone/bot_commands/spell_delays.cpp | 5 +++++ zone/bot_commands/spell_engaged_priority.cpp | 5 +++++ zone/bot_commands/spell_idle_priority.cpp | 5 +++++ zone/bot_commands/spell_max_hp_pct.cpp | 5 +++++ zone/bot_commands/spell_max_mana_pct.cpp | 5 +++++ zone/bot_commands/spell_max_thresholds.cpp | 5 +++++ zone/bot_commands/spell_min_hp_pct.cpp | 5 +++++ zone/bot_commands/spell_min_mana_pct.cpp | 5 +++++ zone/bot_commands/spell_min_thresholds.cpp | 5 +++++ zone/bot_commands/spell_pursue_priority.cpp | 5 +++++ zone/bot_commands/spell_target_count.cpp | 5 +++++ zone/bot_database.cpp | 10 ++++++++++ 25 files changed, 132 insertions(+), 12 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0a9a20639f..e99cbd3e8d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9844,7 +9844,6 @@ bool Bot::IsMobEngagedByAnyone(Mob* tar) { if (m->GetTarget() == tar) { if ( m->IsBot() && - !m->CastToBot()->GetHoldFlag() && m->IsEngaged() && ( !m->CastToBot()->IsBotNonSpellFighter() || @@ -11432,3 +11431,21 @@ void Bot::ResetBotSpellSettings() AI_AddBotSpells(GetBotSpellID()); SetBotEnforceSpellSetting(false); } + +bool Bot::BotPassiveCheck() { + if (GetBotStance() == Stance::Passive) { + GetOwner()->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I am currently set to stance {} [#{}]. My settings cannot be modified.'", + GetCleanName(), + Stance::GetName(Stance::Passive), + Stance::Passive + ).c_str() + ); + + return true; + } + + return false; +} diff --git a/zone/bot.h b/zone/bot.h index a2e0a8ffbc..e0d857880e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -497,7 +497,8 @@ class Bot : public NPC { void SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit); inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spellType) const { return _spellSettings[spellType].AEOrGroupTargetCount; } void SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount); - + bool BotPassiveCheck(); + bool GetShowHelm() const { return _showHelm; } void SetShowHelm(bool showHelm) { _showHelm = showHelm; } bool GetBehindMob() const { return _behindMobStatus; } diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 8e69158607..a7710185f1 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -130,9 +130,14 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index d867b4e67a..ba0ae10dcb 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1440,7 +1440,6 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s [current | reset | sync | value: 0-255] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "note: Only caster or hybrid class bots may be modified"); c->Message(Chat::White, "note: Use [reset] to set stop melee level to server rule"); c->Message(Chat::White, "note: Use [sync] to set stop melee level to current bot level"); return; @@ -1508,14 +1507,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) int success_count = 0; for (auto my_bot : sbl) { - if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { - c->Message( - Chat::White, - fmt::format( - "{} says, 'This command only works on caster or hybrid classes.'", - my_bot->GetCleanName() - ).c_str() - ); + if (my_bot->BotPassiveCheck()) { continue; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 70b6e659cc..fd7dc5aa83 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -234,7 +234,7 @@ void bot_command_cast(Client* c, const Seperator* sep) NEED TO CHECK: precombat, AE Dispel, AE Lifetap DO I NEED A PBAE CHECK??? */ - if (bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { + if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { continue; } diff --git a/zone/bot_commands/click_item.cpp b/zone/bot_commands/click_item.cpp index 80375b52c0..469750f59f 100644 --- a/zone/bot_commands/click_item.cpp +++ b/zone/bot_commands/click_item.cpp @@ -40,12 +40,18 @@ void bot_command_click_item(Client* c, const Seperator* sep) } std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (RuleI(Bots, BotsClickItemsMinLvl) > my_bot->GetLevel()) { c->Message(Chat::White, "%s must be level %i to use clickable items.", my_bot->GetCleanName(), RuleI(Bots, BotsClickItemsMinLvl)); continue; diff --git a/zone/bot_commands/distance_ranged.cpp b/zone/bot_commands/distance_ranged.cpp index 70d5e655b4..7c7dec82a6 100644 --- a/zone/bot_commands/distance_ranged.cpp +++ b/zone/bot_commands/distance_ranged.cpp @@ -57,6 +57,10 @@ void bot_command_distance_ranged(Client* c, const Seperator* sep) int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index e44ddf6011..94faafc426 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -130,9 +130,14 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp index e876a97591..c9e32dec1b 100644 --- a/zone/bot_commands/max_melee_range.cpp +++ b/zone/bot_commands/max_melee_range.cpp @@ -129,9 +129,14 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index ac94a13fd8..fa4a183bf5 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -129,9 +129,14 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index ffcf4d8878..210372f562 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -129,9 +129,14 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 85afc8047e..a5751f9f43 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -129,9 +129,14 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index e9ae709eee..2dfeef4865 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -190,9 +190,14 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 3c3b153ca8..2a5de10174 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -196,9 +196,14 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 89d76f2b5b..70425ee114 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -194,9 +194,14 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 566a3f46a6..d6f0a0c359 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -194,9 +194,14 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 9d94fd722c..7f8cd150b4 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 9e22617b04..44cc7e8d53 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 3b4e0e85ff..7ff6515144 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -196,9 +196,14 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 739ab0d1cd..371b448201 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index e83362f762..b053c462cb 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index 6df8fc9993..d690dede25 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -198,9 +198,14 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 593606437c..872444f322 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -194,9 +194,14 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index f87e232eeb..2f78a74687 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -190,9 +190,14 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index e60a17961f..3f42d995dc 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2221,6 +2221,11 @@ bool BotDatabase::LoadBotSettings(Mob* m) else { query = fmt::format("`bot_id` = {} AND `stance` = {}", mobID, stanceID); } + + if (stanceID == Stance::Passive) { + LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive); + return true; + } const auto& l = BotSettingsRepository::GetWhere(database, query); @@ -2268,6 +2273,11 @@ bool BotDatabase::SaveBotSettings(Mob* m) uint32 charID = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); + if (stanceID == Stance::Passive) { + LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive); + return true; + } + std::string query = ""; if (m->IsClient()) { From 9f9c25c653ac0adf7b4d33991fea017ea9d8a6df Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:07:35 -0600 Subject: [PATCH 018/394] passivecheck response --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e99cbd3e8d..e6af415b38 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11437,7 +11437,7 @@ bool Bot::BotPassiveCheck() { GetOwner()->Message( Chat::Yellow, fmt::format( - "{} says, 'I am currently set to stance {} [#{}]. My settings cannot be modified.'", + "{} says, 'I am currently set to stance {} [#{}]. I cannot do commands and my settings cannot be modified.'", GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive From cf26a476fd98d8c9f5046915edba77c2cdc1e94d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 10:13:44 -0600 Subject: [PATCH 019/394] remove spelltype checks from clients causing bad data --- zone/bot_database.cpp | 2 +- zone/client.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 3f42d995dc..60f389a9b0 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2356,7 +2356,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme - if (IsClientBotSpellType(x) && m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { + if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, diff --git a/zone/client.cpp b/zone/client.cpp index 9d01f181b2..a03e6f9318 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13087,10 +13087,6 @@ void Client::LoadDefaultBotSettings() { LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - BotSpellSettings_Struct t; t.spellType = i; From d3d53c5a74cd4299c1f046f3c68bd59be01abe1b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 10:14:23 -0600 Subject: [PATCH 020/394] remove circle/teleport, tweak depart --- zone/bot_command.cpp | 3 - zone/bot_command.h | 2 - zone/bot_commands/depart.cpp | 74 +++++++++++++++----- zone/bot_commands/teleport.cpp | 119 --------------------------------- 4 files changed, 59 insertions(+), 139 deletions(-) delete mode 100644 zone/bot_commands/teleport.cpp diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 80be595f89..b47edd5e34 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1287,7 +1287,6 @@ int bot_command_init(void) bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || bot_command_add("distanceranged", "Controls the range casters and ranged will try to stay away from a mob", AccountStatus::Player, bot_command_distance_ranged) || bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || - bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_circle) || bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || @@ -1345,7 +1344,6 @@ int bot_command_init(void) bot_command_add("picklock", "Orders a capable bot to pick the lock of the closest door", AccountStatus::Player, bot_command_pick_lock) || bot_command_add("pickpocket", "Orders a capable bot to pickpocket a NPC", AccountStatus::Player, bot_command_pickpocket) || bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || - bot_command_add("portal", "Orders a Wizard bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_portal) || bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || bot_command_add("resistance", "Orders a bot to cast a specified resistance buff", AccountStatus::Player, bot_command_resistance) || @@ -2316,7 +2314,6 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" #include "bot_commands/taunt.cpp" -#include "bot_commands/teleport.cpp" #include "bot_commands/timer.cpp" #include "bot_commands/track.cpp" #include "bot_commands/view_combos.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index a8951a7cd4..373fbd1e54 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1775,7 +1775,6 @@ void bot_command_toggle_ranged(Client* c, const Seperator* sep); void bot_command_update(Client *c, const Seperator *sep); void bot_command_woad(Client *c, const Seperator *sep); -void bot_command_circle(Client *c, const Seperator *sep); void bot_command_heal_rotation_adaptive_targeting(Client *c, const Seperator *sep); void bot_command_heal_rotation_add_member(Client *c, const Seperator *sep); void bot_command_heal_rotation_add_target(Client *c, const Seperator *sep); @@ -1803,7 +1802,6 @@ void bot_command_inventory_window(Client *c, const Seperator *sep); void bot_command_pet_get_lost(Client *c, const Seperator *sep); void bot_command_pet_remove(Client *c, const Seperator *sep); void bot_command_pet_set_type(Client *c, const Seperator *sep); -void bot_command_portal(Client *c, const Seperator *sep); // bot command helpers diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index b337ef4042..d9b09d2d4e 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -1,59 +1,103 @@ #include "../bot_command.h" -void bot_command_depart(Client *c, const Seperator *sep) +void bot_command_depart(Client* c, const Seperator* sep) { bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) + if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_Depart); + return; } - bool single = false; + std::list sbl; + MyBots::PopulateSBL_BySpawnedBots(c, sbl); + ActionableTarget::Types actionable_targets; + Bot* my_bot = nullptr; bool single = false; std::string single_arg = sep->arg[2]; - if (!single_arg.compare("single")) + + if (!single_arg.compare("single")) { single = true; + } std::string destination = sep->arg[1]; + if (!destination.compare("list")) { - Bot* my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid); - Bot* my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard); + Bot* my_druid_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Druid); + Bot* my_wizard_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Wizard); + + if ( + (!my_druid_bot && !my_wizard_bot) || + (my_druid_bot && !my_druid_bot->IsInGroupOrRaid(c)) || + (my_wizard_bot && !my_wizard_bot->IsInGroupOrRaid(c)) + ) { + c->Message(Chat::Yellow, "No compatible bots found for %s.", sep->arg[0]); + return; + } + helper_command_depart_list(c, my_druid_bot, my_wizard_bot, local_list, single); + return; } else if (destination.empty()) { c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); + return; } - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; + my_bot = nullptr; + sbl.clear(); MyBots::PopulateSBL_BySpawnedBots(c, sbl); - bool cast_success = false; + for (auto list_iter : *local_list) { auto local_entry = list_iter->SafeCastToDepart(); - if (helper_spell_check_fail(local_entry)) + + if (helper_spell_check_fail(local_entry)) { continue; - if (local_entry->single != single) + } + + if (local_entry->single != single) { continue; - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) + } + + if (destination.compare(spells[local_entry->spell_id].teleport_zone)) { continue; + } auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); + if (!target_mob) continue; my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) + + if (!my_bot) { continue; + } + + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!my_bot->IsInGroupOrRaid(c)) { + continue; + } + + if (local_entry->spell_id != 0 && my_bot->GetMana() < spells[local_entry->spell_id].mana) { + continue; + } cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); + break; } - helper_no_available_bots(c, my_bot); + if (!cast_success) { + helper_no_available_bots(c, my_bot); + } } diff --git a/zone/bot_commands/teleport.cpp b/zone/bot_commands/teleport.cpp deleted file mode 100644 index 94e8ee2b05..0000000000 --- a/zone/bot_commands/teleport.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "../bot_command.h" - -void bot_command_circle(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_circle", sep->arg[0], "circle")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Druid); - return; - } - - bool single = false; - std::string single_arg = sep->arg[2]; - if (!single_arg.compare("single")) - single = true; - - std::string destination = sep->arg[1]; - if (!destination.compare("list")) { - auto my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid); - helper_command_depart_list(c, my_druid_bot, nullptr, local_list, single); - return; - } - else if (destination.empty()) { - c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToDepart(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->caster_class != Class::Druid) - continue; - if (local_entry->single != single) - continue; - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} - -void bot_command_portal(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_portal", sep->arg[0], "portal")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Wizard); - return; - } - - bool single = false; - std::string single_arg = sep->arg[2]; - if (!single_arg.compare("single")) - single = true; - - std::string destination = sep->arg[1]; - if (!destination.compare("list")) { - auto my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard); - helper_command_depart_list(c, nullptr, my_wizard_bot, local_list, single); - return; - } - else if (destination.empty()) { - c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToDepart(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->caster_class != Class::Wizard) - continue; - if (local_entry->single != single) - continue; - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} From 18b6fc2667fcc04a4b55c9ebd217d8ba6796f107 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:10:13 -0600 Subject: [PATCH 021/394] adjust spell hold checks to rely on caster and Implement pet resist buffs and pet damage shields --- common/spdat.cpp | 10 ++++++++-- common/spdat.h | 8 +++++--- zone/bot.cpp | 19 +++++++++++++++++-- zone/bot_commands/spell_holds.cpp | 7 ++----- zone/botspellsai.cpp | 4 ++++ zone/mob.cpp | 24 +++++++++++++++++++----- 6 files changed, 55 insertions(+), 17 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 48b6351fb2..8211ecfdcc 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2874,14 +2874,16 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { case BotSpellTypes::Buff: case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: - case BotSpellTypes::DamageShields: + case BotSpellTypes::DamageShields: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: case BotSpellTypes::Pet: case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: - case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: return true; case BotSpellTypes::InCombatBuff: @@ -2916,8 +2918,10 @@ bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: return true; default: return false; @@ -3050,8 +3054,10 @@ bool IsClientBotSpellType(uint16 spellType) { case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index b07bf7d966..60257a4014 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -705,10 +705,12 @@ namespace BotSpellTypes constexpr uint16 PetVeryFastHeals = 49; constexpr uint16 PetHoTHeals = 50; constexpr uint16 DamageShields = 51; - constexpr uint16 ResistBuffs = 52; + constexpr uint16 ResistBuffs = 52; + constexpr uint16 PetDamageShields = 53; + constexpr uint16 PetResistBuffs = 54; - constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::ResistBuffs; // Do not remove this, increment as needed + constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this + constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed } const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); diff --git a/zone/bot.cpp b/zone/bot.cpp index e6af415b38..ad8bbacc5f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9516,7 +9516,9 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::DamageShields: - case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: if ( !( spells[spellid].target_type == ST_Target || @@ -10411,6 +10413,14 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, ui case BotSpellTypes::PreCombatBuffSong: priority = 23; + break; + case BotSpellTypes::PetResistBuffs: + priority = 24; + + break; + case BotSpellTypes::PetDamageShields: + priority = 25; + break; default: priority = 0; //unused @@ -10872,7 +10882,9 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: case BotSpellTypes::DamageShields: - case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: return BotSpellTypes::Buff; case BotSpellTypes::AEMez: case BotSpellTypes::Mez: @@ -10932,18 +10944,21 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { switch (spellType) { case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return false; } return true; case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: if (IsResistanceOnlySpell(spellid)) { return true; } return false; case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: if (IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return true; } diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index effd97a7ae..fd17749d18 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -9,15 +9,12 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { std::vector description = { - "Toggles whether or not bots can cast or receive certain spell types" + "Toggles whether or not bots can cast certain spell types" }; std::vector notes = { - "- All pet types are based off the pet's owner's setting", - "- Any remaining types use the owner's setting when a pet is the target", - "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", - "- e.g., BotA is healing BotB using BotB's settings", + "- All pet types are based off the pet owner's setting when a pet is the target" }; std::vector example_format = diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index d7e6338223..b0ca5edf43 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -125,7 +125,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { return false; } @@ -2039,7 +2041,9 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: return RuleI(Bots, PercentChanceToCastBuff); case BotSpellTypes::Escape: return RuleI(Bots, PercentChanceToCastEscape); diff --git a/zone/mob.cpp b/zone/mob.cpp index d472eb7f7b..ef6659bed8 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8886,6 +8886,12 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::ResistBuffs: spellTypeName = "Resist Buff"; break; + case BotSpellTypes::PetDamageShields: + spellTypeName = "Pet Damage Shield"; + break; + case BotSpellTypes::PetResistBuffs: + spellTypeName = "Pet Resist Buff"; + break; default: break; } @@ -9056,6 +9062,12 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::ResistBuffs: spellTypeName = "resistbuffs"; break; + case BotSpellTypes::PetDamageShields: + spellTypeName = "petdamageshields"; + break; + case BotSpellTypes::PetResistBuffs: + spellTypeName = "petresistbuffs"; + break; default: break; } @@ -9335,7 +9347,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::Buff: case BotSpellTypes::Charm: case BotSpellTypes::Cure: - case BotSpellTypes::DamageShields: + case BotSpellTypes::DamageShields: case BotSpellTypes::HateRedux: case BotSpellTypes::InCombatBuff: case BotSpellTypes::InCombatBuffSong: @@ -9346,6 +9358,8 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: return 100; @@ -9418,10 +9432,6 @@ bool Mob::GetUltimateSpellHold(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellHold(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { - return tar->GetSpellHold(spellType); - } - return GetSpellHold(spellType); } @@ -9503,6 +9513,10 @@ uint16 Mob::GetPetSpellType(uint16 spellType) { return BotSpellTypes::PetHoTHeals; case BotSpellTypes::Buff: return BotSpellTypes::PetBuffs; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; default: break; } From 351ada12a52463e7c8f438cde42d870798b35613 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:13:04 -0600 Subject: [PATCH 022/394] correct GetBestBotMagicianPetSpell --- zone/bot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.h b/zone/bot.h index e0d857880e..f29d86235a 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -595,7 +595,7 @@ class Bot : public NPC { static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE = false); bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid); static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); - static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); + static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Pet); static std::string GetBotMagicianPetType(Bot* botCaster); static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); From 8fa429c02d5cfb8d8c7b67c94f4a9255dd2cbee3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:13:27 -0600 Subject: [PATCH 023/394] add sanity check to campCount on ^camp --- zone/bot_commands/bot.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index ba0ae10dcb..da2352a197 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -63,7 +63,9 @@ void bot_command_camp(Client *c, const Seperator *sep) ++campCount; } - c->Message(Chat::White, "%i of your bots have been camped.", campCount); + if (campCount) { + c->Message(Chat::White, "%i of your bots have been camped.", campCount); + } } void bot_command_clone(Client *c, const Seperator *sep) From c50d72ccf0c9eaaaff5961e595eb71f61c6dc5df Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:14:34 -0600 Subject: [PATCH 024/394] add PercentChanceToCast rules for AE and group spells --- common/ruletypes.h | 2 ++ zone/botspellsai.cpp | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index c1e46b5aee..068fd00447 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -788,7 +788,9 @@ RULE_INT(Bots, SpellResistLimit, 150, "150 Default. This is the resist cap where RULE_INT(Bots, StunCastChanceIfCasting, 50, "50 Default. Chance for non-Paladins to cast a stun spell if the target is casting.") RULE_INT(Bots, StunCastChanceNormal, 15, "15 Default. Chance for non-Paladins to cast a stun spell on the target.") RULE_INT(Bots, StunCastChancePaladins, 75, "75 Default. Chance for Paladins to cast a stun spell if the target is casting.") +RULE_INT(Bots, PercentChanceToCastAEs, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastNuke, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastGroupHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") RULE_INT(Bots, PercentChanceToCastHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") RULE_INT(Bots, PercentChanceToCastRoot, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastBuff, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b0ca5edf43..37decd7799 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2034,6 +2034,22 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::AEStun: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEMez: + case BotSpellTypes::AESlow: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::AERoot: + case BotSpellTypes::PBAENuke: + return RuleI(Bots, PercentChanceToCastAEs); + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::GroupCures: + return RuleI(Bots, PercentChanceToCastGroupHeal); case BotSpellTypes::Nuke: return RuleI(Bots, PercentChanceToCastNuke); case BotSpellTypes::Root: @@ -2049,7 +2065,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj return RuleI(Bots, PercentChanceToCastEscape); case BotSpellTypes::Lifetap: return RuleI(Bots, PercentChanceToCastLifetap); - case BotSpellTypes::AESnare: case BotSpellTypes::Snare: return RuleI(Bots, PercentChanceToCastSnare); case BotSpellTypes::DOT: @@ -2057,30 +2072,23 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::Dispel: return RuleI(Bots, PercentChanceToCastDispel); case BotSpellTypes::InCombatBuff: - return RuleI(Bots, PercentChanceToCastInCombatBuff); - case BotSpellTypes::AEMez: + return RuleI(Bots, PercentChanceToCastInCombatBuff); case BotSpellTypes::Mez: - return RuleI(Bots, PercentChanceToCastMez); - case BotSpellTypes::AESlow: + return RuleI(Bots, PercentChanceToCastMez); case BotSpellTypes::Slow: - return RuleI(Bots, PercentChanceToCastSlow); - case BotSpellTypes::AEDebuff: + return RuleI(Bots, PercentChanceToCastSlow); case BotSpellTypes::Debuff: return RuleI(Bots, PercentChanceToCastDebuff); case BotSpellTypes::Cure: return RuleI(Bots, PercentChanceToCastCure); case BotSpellTypes::HateRedux: - return RuleI(Bots, PercentChanceToCastHateRedux); - case BotSpellTypes::AEFear: + return RuleI(Bots, PercentChanceToCastHateRedux); case BotSpellTypes::Fear: return RuleI(Bots, PercentChanceToCastFear); case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: case BotSpellTypes::FastHeals: case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetRegularHeals: case BotSpellTypes::PetCompleteHeals: From 9dd11a598fea242810cf31fd31941b1fd5421612 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:15:08 -0600 Subject: [PATCH 025/394] Add AllowAIMez to allow bot auto mez to be toggled --- common/ruletypes.h | 1 + zone/botspellsai.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 068fd00447..5cbbbd6e91 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -870,6 +870,7 @@ RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") +RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 37decd7799..5adbdb3e98 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -639,6 +639,10 @@ bool Bot::AI_PursueCastCheck() { continue; } + if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + continue; + } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -698,6 +702,10 @@ bool Bot::AI_IdleCastCheck() { continue; } + if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + continue; + } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -744,6 +752,10 @@ bool Bot::AI_EngagedCastCheck() { continue; } + if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + continue; + } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } From 8989c6f21bce3645a8051cfcf6052f25d76230d0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:31:59 -0600 Subject: [PATCH 026/394] apply ranged setting on spawn to show correct weapons --- zone/bot.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index ad8bbacc5f..2653ebabd7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3395,6 +3395,10 @@ bool Bot::Spawn(Client* botCharacterOwner) { } } + if (IsBotRanged()) { + ChangeBotRangedWeapons(true); + } + if (auto raid = entity_list.GetRaidByBotName(GetName())) { // Safety Check to confirm we have a valid raid auto owner = GetBotOwner(); From 464c69190d8de89c9050fd12f7ee228583196274 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:35:28 -0600 Subject: [PATCH 027/394] correct and tweak all combat positioning and combat range --- common/ruletypes.h | 8 +-- zone/bot.cpp | 148 ++++++++++++++++++++++---------------- zone/bot.h | 4 +- zone/bot_commands/bot.cpp | 1 + 4 files changed, 95 insertions(+), 66 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5cbbbd6e91..65239a07cf 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -836,15 +836,15 @@ RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") -RULE_REAL(Bots, PercentMinMeleeDistance, 0.60, "Multiplier of the max melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") +RULE_REAL(Bots, PercentMinMeleeDistance, 0.75, "Multiplier of the their melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.") RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .") -RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.25, "Multiplier of max melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") +RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.40, "Multiplier of their melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.") RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.") RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.") -RULE_REAL(Bots, DistanceTauntingBotsStickMainHate, 25.00, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") -RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, false, "False Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") +RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") +RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "True Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.") RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.") RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.") diff --git a/zone/bot.cpp b/zone/bot.cpp index 2653ebabd7..c1d3e59ad5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2163,7 +2163,8 @@ void Bot::AI_Process() // COMBAT RANGE CALCS bool atCombatRange = false; - bool behindMob = false; + bool behindMob = BehindMob(tar, GetX(), GetY()); + bool frontMob = InFrontMob(tar, GetX(), GetY()); uint8 stopMeleeLevel = GetStopMeleeLevel(); const EQ::ItemInstance* p_item; const EQ::ItemInstance* s_item; @@ -2230,7 +2231,7 @@ void Bot::AI_Process() } if (!jitterCooldown && AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { - DoCombatPositioning(tar, Goal, stopMeleeLevel, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behindMob); + DoCombatPositioning(tar, Goal, stopMeleeLevel, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behindMob, frontMob); return; } else { @@ -2363,10 +2364,10 @@ bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, g else { Goal = follow_mob->GetPosition(); } + float destination_distance = DistanceSquared(GetPosition(), Goal); if ((!bot_owner->GetBotPulling() || PULLING_BOT) && (destination_distance > GetFollowDistance())) { - if (!IsRooted()) { if (rest_timer.Enabled()) { rest_timer.Disable(); @@ -2380,7 +2381,6 @@ bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, g else { if (IsMoving()) { - StopMoving(); return true; } @@ -2733,7 +2733,7 @@ bool Bot::TryEvade(Mob* tar) { return false; } -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { +void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { atCombatRange= false; p_item = GetBotItem(EQ::invslot::slotPrimary); @@ -2742,32 +2742,46 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo bool backstab_weapon = false; if (GetBehindMob()) { - behindMob = BehindMob(tar, GetX(), GetY()); // Can be separated for other future use if (GetClass() == Class::Rogue) { backstab_weapon = p_item && p_item->GetItemBackstabDamage(); } } // Calculate melee distances - CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); - - //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be between [{} - {}] away. MMR is {}." - // , GetCleanName() - // , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) - // , tar->GetCleanName() - // , tar_distance - // , (tar_distance <= melee_distance ? "but only needed" : "but need to be") - // , melee_distance_min - // , melee_distance - // , melee_distance_max - //); //deleteme + CalcMeleeDistances(tar, p_item, s_item, backstab_weapon, behindMob, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); + + if (!GetCombatRoundForAlerts()) { + SetCombatRoundForAlerts(); + LogTestDebugDetail("{} says, 'I'm {} {}. I am currently {} away {} to be between [{} - {}] away. MMR is {}.'" + , GetCleanName() + , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) + , tar->GetCleanName() + , tar_distance + , (tar_distance <= melee_distance ? "but only needed" : "but need to be") + , melee_distance_min + , melee_distance + , melee_distance_max + ); //deleteme + LogTestDebugDetail("{} says, 'My stance is {} #{}, I am {} taunting. I am set to {} {}, {} at MMR, distanceranged {}, sml {} [{}]'" + , GetCleanName() + , Stance::GetName(GetBotStance()) + , GetBotStance() + , (IsTaunting() ? "currently" : "not") + , (BehindMob() ? "stay behind" : "not stay behind") + , tar->GetCleanName() + , (GetMaxMeleeRange() ? "I stay" : "I do not stay") + , GetBotDistanceRanged() + , GetStopMeleeLevel() + , ((GetLevel() <= GetStopMeleeLevel()) ? "disabled" : "enabled") + ); //deleteme + } if (tar_distance <= melee_distance) { atCombatRange = true; } } -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { +void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool backstab_weapon, bool behindMob, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { float size_mod = GetSize(); float other_size_mod = tar->GetSize(); @@ -2880,27 +2894,26 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it melee_distance = RuleR(Bots, MaxDistanceForMelee); } - melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMeleeDistance); + melee_distance_min = melee_distance * RuleR(Bots, PercentMinMeleeDistance); - if (taunting) { - melee_distance_min = melee_distance_max * RuleR(Bots, PercentTauntMinMeleeDistance); - melee_distance = melee_distance_max * RuleR(Bots, TauntNormalMeleeRangeDistance); + if (IsTaunting()) { + melee_distance_min = melee_distance * RuleR(Bots, PercentTauntMinMeleeDistance); + melee_distance = melee_distance * RuleR(Bots, TauntNormalMeleeRangeDistance); } bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; - if (!taunting && !IsBotRanged() && !isStopMeleeLevel && GetMaxMeleeRange()) { - melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); + if (!IsTaunting() && !IsBotRanged() && !isStopMeleeLevel && GetMaxMeleeRange()) { melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); + melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); } if (isStopMeleeLevel && !IsBotRanged()) { float desiredRange = GetBotDistanceRanged(); - melee_distance_min = std::min(melee_distance, (desiredRange / 2)); + melee_distance_min = std::max(melee_distance, (desiredRange / 2)); melee_distance = std::max((melee_distance + 1), desiredRange); } - /* Archer Checks*/ if (IsBotRanged()) { float minDistance = RuleI(Combat, MinRangedAttackDist); float maxDistance = GetBotRangedValue(); @@ -3003,7 +3016,6 @@ Mob* Bot::GetBotTarget(Client* bot_owner) } bool Bot::ReturningFlagChecks(Client* bot_owner, float fm_distance) {// Need to make it back to group before clearing return flag - if (fm_distance <= GetFollowDistance()) { // Once we're back, clear blocking flags so everyone else can join in @@ -3022,13 +3034,12 @@ bool Bot::ReturningFlagChecks(Client* bot_owner, float fm_distance) {// Need to WipeHateList(); return false; } + return true; } bool Bot::PullingFlagChecks(Client* bot_owner) { - if (!GetTarget()) { - WipeHateList(); SetTarget(nullptr); SetPullingFlag(false); @@ -3042,7 +3053,6 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { return false; } else if (GetTarget()->GetHateList().size()) { - WipeHateList(); SetTarget(nullptr); SetPullingFlag(false); @@ -3064,7 +3074,6 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { } void Bot::HealRotationChecks() { - if (IsMyHealRotationSet()) { if (AIHealRotation(HealRotationTarget(), UseHealRotationFastHeals())) { m_member_of_heal_rotation->SetMemberIsCasting(this); @@ -3079,7 +3088,6 @@ void Bot::HealRotationChecks() { } bool Bot::IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid) { - if (!bot_owner || (!bot_group && !raid) || !IsAIControlled()) { return false; } @@ -3089,11 +3097,11 @@ bool Bot::IsAIProcessValid(const Client* bot_owner, const Group* bot_group, cons SetBotOwner(nullptr); return false; } + return true; } bool Bot::CheckIfCasting(float fm_distance) { - if (IsCasting()) { if (IsHealRotationMember() && m_member_of_heal_rotation->CastingOverride() && @@ -3105,12 +3113,10 @@ bool Bot::CheckIfCasting(float fm_distance) { InterruptSpell(); } else if (AmICastingForHealRotation() && m_member_of_heal_rotation->CastingMember() == this) { - AdvanceHealRotation(false); return true; } else if (GetClass() != Class::Bard) { - if (IsEngaged()) { return true; } @@ -3128,13 +3134,12 @@ bool Bot::CheckIfCasting(float fm_distance) { else if (IsHealRotationMember()) { m_member_of_heal_rotation->SetMemberIsCasting(this, false); } + return false; } bool Bot::CheckIfIncapacitated() { - if (GetPauseAI() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { - if (IsCasting()) { InterruptSpell(); } @@ -3143,6 +3148,7 @@ bool Bot::CheckIfIncapacitated() { AdvanceHealRotation(false); m_member_of_heal_rotation->SetMemberIsCasting(this, false); } + return true; } @@ -3188,29 +3194,29 @@ void Bot::SetBerserkState() {// Berserk updates should occur if primary AI crite Mob* Bot::SetFollowMob(Client* leash_owner) { Mob* follow_mob = entity_list.GetMob(GetFollowID()); - if (!follow_mob) { + if (!follow_mob) { follow_mob = leash_owner; SetFollowID(leash_owner->GetID()); } + return follow_mob; } Client* Bot::SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint32 r_group) const { - Client* leash_owner = nullptr; + if (raid && r_group < MAX_RAID_GROUPS && raid->GetGroupLeader(r_group)) { leash_owner = raid->GetGroupLeader(r_group) && raid->GetGroupLeader(r_group)->IsClient() ? raid->GetGroupLeader(r_group)->CastToClient() : bot_owner; - } else if (bot_group) { leash_owner = (bot_group->GetLeader() && bot_group->GetLeader()->IsClient() ? bot_group->GetLeader()->CastToClient() : bot_owner); - } else { leash_owner = bot_owner; } + return leash_owner; } @@ -3235,6 +3241,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { AddToHateList(attack_target, 1); SetTarget(attack_target); SetAttackingFlag(); + if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->WipeHateList(); GetPet()->AddToHateList(attack_target, 1); @@ -3275,8 +3282,8 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetTarget(pull_target); SetPullingFlag(); bot_owner->SetBotPulling(); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { + if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); m_previous_pet_order = GetPet()->GetPetOrder(); @@ -5132,7 +5139,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if (taunting && target->IsNPC() && taunt_time) { + if (IsTaunting() && target->IsNPC() && taunt_time) { if (GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { BotGroupSay( this, @@ -6758,6 +6765,7 @@ void Bot::Zone() { bool Bot::IsAtRange(Mob *target) { bool result = false; + if (target) { float range = (GetBotRangedValue() + 5.0); range *= range; @@ -7501,6 +7509,7 @@ void EntityList::ScanCloseClientMobs(std::unordered_map& close_mob } float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); + if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } @@ -9480,7 +9489,7 @@ bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrecheck return false; } - if (!IsCommandedSpell() && !taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { + if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; } @@ -10831,7 +10840,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { Mob* tar = GetTarget(); - if (!taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { + if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme return result; } @@ -11058,63 +11067,81 @@ void Bot::SetCombatJitter() { } } -void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob) { +void Bot::DoCombatPositioning( + Mob* tar, + glm::vec3 Goal, + bool stopMeleeLevel, + float tar_distance, + float melee_distance_min, + float melee_distance, + float melee_distance_max, + bool behindMob, + bool frontMob +) { + //LogTestDebug("{} says, 'DoCombatPositioning. {} #{}", GetCleanName(), __FILE__, __LINE__); //deleteme + if (HasTargetReflection()) { - if (!taunting && !tar->IsFeared() && !tar->IsStunned()) { + if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) { if (TryEvade(tar)) { return; } } - - if (tar->IsRooted() && !taunting) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay + else if (tar->IsRooted() && !IsTaunting()) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay if (tar_distance <= melee_distance_max) { - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, taunting)) { + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, IsTaunting())) { RunToGoalWithJitter(Goal); + return; } } } - - if (taunting && tar_distance < melee_distance_min) { // Back up any bots that are too close - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, taunting)) { + else if (IsTaunting() && ((tar_distance < melee_distance_min) || !frontMob)) { // Back up any bots that are too close + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, IsTaunting())) { RunToGoalWithJitter(Goal); + return; } } } else { if (!tar->IsFeared()) { - if (taunting) { // Taunting adjustments + if (IsTaunting()) { // Taunting adjustments Mob* mobTar = tar->GetTarget(); + if (!mobTar || mobTar == nullptr) { DoFaceCheckNoJitter(tar); + return; } if (RuleB(Bots, TauntingBotsFollowTopHate)) { // If enabled, taunting bots will stick to top hate - if ((DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + if (Distance(m_Position, mobTar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate)) { Goal = mobTar->GetPosition(); RunToGoalWithJitter(Goal); + return; } } else { // Otherwise, stick to any other bots that are taunting - if (mobTar->IsBot() && mobTar->CastToBot()->taunting && (DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + if (mobTar->IsBot() && mobTar->CastToBot()->IsTaunting() && (Distance(m_Position, mobTar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))) { Goal = mobTar->GetPosition(); RunToGoalWithJitter(Goal); + return; } } } - else if (tar_distance < melee_distance_min || (GetBehindMob() && !behindMob) || !HasRequiredLoSForPositioning(tar)) { // Regular adjustment - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), taunting)) { + else if (tar_distance < melee_distance_min || (GetBehindMob() && !behindMob) || (IsTaunting() && !frontMob)|| !HasRequiredLoSForPositioning(tar)) { // Regular adjustment + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), IsTaunting())) { RunToGoalWithJitter(Goal); + return; } } - else if (tar->IsEnraged() && !taunting && !stopMeleeLevel && !behindMob) { // Move non-taunting melee bots behind target during enrage + else if (tar->IsEnraged() && !IsTaunting() && !stopMeleeLevel && !behindMob) { // Move non-taunting melee bots behind target during enrage if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) { RunToGoalWithJitter(Goal); + return; } } @@ -11122,6 +11149,7 @@ void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, flo } DoFaceCheckNoJitter(tar); + return; } diff --git a/zone/bot.h b/zone/bot.h index f29d86235a..ff3c7c1296 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -943,7 +943,7 @@ class Bot : public NPC { Mob* tar, float tar_distance, bool& atCombatRange, - bool& behindMob, + bool behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, @@ -957,7 +957,7 @@ class Bot : public NPC { void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; } void SetCombatJitter(); void SetCombatOutOfRangeJitter(); - void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob); + void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob, bool frontMob); void DoFaceCheckWithJitter(Mob* tar); void DoFaceCheckNoJitter(Mob* tar); void RunToGoalWithJitter(glm::vec3 Goal); diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index da2352a197..f9a3ecfe91 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1695,6 +1695,7 @@ void bot_command_toggle_ranged(Client *c, const Seperator *sep) else { bot_iter->SetBotRangedSetting(ranged_state); } + bot_iter->ChangeBotRangedWeapons(bot_iter->IsBotRanged()); } } From 6d97536e38c049b4d6a75ae5b1c28b1975b77f5a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 12 Nov 2024 22:19:16 -0600 Subject: [PATCH 028/394] Add loregroup 0 bypass for lore conflicts for bots like clients --- zone/bot.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c1d3e59ad5..aa793bd8ac 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7168,8 +7168,9 @@ void Bot::CalcBotStats(bool showtext) { } bool Bot::CheckLoreConflict(const EQ::ItemData* item) { - if (!item || !(item->LoreFlag)) + if (!item || !(item->LoreFlag) || (item->LoreGroup == 0)) { return false; + } if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result return (m_inv.HasItem(item->ID, 0, invWhereWorn) != INVALID_INDEX); From 2e0840db72f50dd66ee701b471fa56fe07daf7dc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:10:47 -0600 Subject: [PATCH 029/394] add bot camp timer to prevent /camp exploits --- common/ruletypes.h | 1 + zone/client.cpp | 2 ++ zone/client.h | 1 + zone/client_packet.cpp | 2 ++ zone/client_process.cpp | 4 ++++ 5 files changed, 10 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 65239a07cf..ec8c60513e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -871,6 +871,7 @@ RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follo RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") +RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/client.cpp b/zone/client.cpp index a03e6f9318..9d54f0b3d7 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -148,6 +148,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( ), hpupdate_timer(2000), camp_timer(29000), + bot_camp_timer((RuleI(Bots, CampTimer) * 1000)), process_timer(100), consume_food_timer(CONSUMPTION_TIMER), zoneinpacket_timer(1000), @@ -252,6 +253,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( fishing_timer.Disable(); dead_timer.Disable(); camp_timer.Disable(); + bot_camp_timer.Disable(); autosave_timer.Disable(); GetMercTimer()->Disable(); instalog = false; diff --git a/zone/client.h b/zone/client.h index 71310dd84f..77cf9a574c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2036,6 +2036,7 @@ class Client : public Mob PTimerList p_timers; //persistent timers Timer hpupdate_timer; Timer camp_timer; + Timer bot_camp_timer; Timer process_timer; Timer consume_food_timer; Timer zoneinpacket_timer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 67e49d815e..648251a7bf 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4341,6 +4341,7 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app) return; } camp_timer.Start(29000, true); + bot_camp_timer.Start((RuleI(Bots, CampTimer) * 1000), true); return; } @@ -14760,6 +14761,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) SetFeigned(false); BindWound(this, false, true); camp_timer.Disable(); + bot_camp_timer.Disable(); } else if (sa->parameter == Animation::Sitting) { SetAppearance(eaSitting); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index d0dab4c27b..2043127540 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -193,6 +193,10 @@ bool Client::Process() { return false; //delete client } + if (bot_camp_timer.Check()) { + Bot::BotOrderCampAll(this); + } + if (camp_timer.Check()) { Raid *myraid = entity_list.GetRaidByClient(this); if (myraid) { From 1e7739012f4760aa952bfcd6d36bb28fe1fde9b0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 13 Nov 2024 22:19:11 -0600 Subject: [PATCH 030/394] Make command errors/failures yellow --- zone/bot_command.cpp | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index b47edd5e34..81f7b21a12 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1237,7 +1237,7 @@ LinkedList cleanup_bot_command_list; int bot_command_not_avail(Client *c, const char *message) { - c->Message(Chat::White, "Bot commands not available."); + c->Message(Chat::Yellow, "Bot commands not available."); return -1; } @@ -1540,7 +1540,7 @@ int bot_command_real_dispatch(Client *c, const char *message) BotCommandRecord *cur = bot_command_list[cstr]; if(c->Admin() < cur->access){ - c->Message(Chat::White, "Your access level is not high enough to use this bot command."); + c->Message(Chat::Yellow, "Your access level is not high enough to use this bot command."); return(-1); } @@ -1569,13 +1569,13 @@ bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, BCEnum::AFType f { switch (fail_type) { case BCEnum::AFT_Value: - bot_owner->Message(Chat::White, "Failed to change '%s' for %s due to invalid value for this command", type_desc, my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid value for this command", type_desc, my_bot->GetCleanName()); return true; case BCEnum::AFT_GenderRace: - bot_owner->Message(Chat::White, "Failed to change '%s' for %s due to invalid bot gender and/or race for this command", type_desc, my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot gender and/or race for this command", type_desc, my_bot->GetCleanName()); return true; case BCEnum::AFT_Race: - bot_owner->Message(Chat::White, "Failed to change '%s' for %s due to invalid bot race for this command", type_desc, my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot race for this command", type_desc, my_bot->GetCleanName()); return true; default: return false; @@ -1587,7 +1587,7 @@ void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot) if (!MyBots::IsMyBot(bot_owner, my_bot)) return; if (!my_bot->Save()) { - bot_owner->Message(Chat::White, "Failed to save appearance change for %s due to unknown cause...", my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to save appearance change for %s due to unknown cause...", my_bot->GetCleanName()); return; } @@ -1631,7 +1631,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas if (!Bot::IsValidName(bot_name)) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z' and it must be between 4 and 15 characters. Mixed case {} allowed.", bot_name, RuleB(Bots, AllowCamelCaseNames) ? "is" : "is not" @@ -1643,7 +1643,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bool available_flag = false; if (!database.botdb.QueryNameAvailablity(bot_name, available_flag)) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "'{}' is already in use or an invalid name.", bot_name @@ -1654,7 +1654,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas if (!available_flag) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "The name '{}' is already being used. Please choose a different name", bot_name @@ -1668,7 +1668,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas const std::string bot_class_name = GetClassIDName(bot_class); bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "{} {} is an invalid race-class combination, would you like to {} proper combinations for {}?", bot_race_name, @@ -1704,7 +1704,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas uint32 bot_count = 0; uint32 bot_class_count = 0; if (!database.botdb.QueryBotCount(bot_owner->CharacterID(), bot_class, bot_count, bot_class_count)) { - bot_owner->Message(Chat::White, "Failed to query bot count."); + bot_owner->Message(Chat::Yellow, "Failed to query bot count."); return bot_id; } @@ -1721,7 +1721,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas message = "You cannot create any bots."; } - bot_owner->Message(Chat::White, message.c_str()); + bot_owner->Message(Chat::Yellow, message.c_str()); return bot_id; } @@ -1742,7 +1742,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas ); } - bot_owner->Message(Chat::White, message.c_str()); + bot_owner->Message(Chat::Yellow, message.c_str()); return bot_id; } @@ -1753,7 +1753,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->GetLevel() < bot_character_level ) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "You must be level {} to use bots.", bot_character_level @@ -1769,7 +1769,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->GetLevel() < bot_character_level_class ) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "You must be level {} to use {} bots.", bot_character_level_class, @@ -1784,7 +1784,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas if (!my_bot->Save()) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "Failed to create '{}' due to unknown cause.", my_bot->GetCleanName() @@ -1918,7 +1918,7 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool helper_command_disabled(Client* bot_owner, bool rule_value, const char* command) { if (!rule_value) { - bot_owner->Message(Chat::White, "Bot command %s is not enabled on this server.", command); + bot_owner->Message(Chat::Yellow, "Bot command %s is not enabled on this server.", command); return true; } @@ -1929,7 +1929,7 @@ bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, c { auto alias_iter = bot_command_aliases.find(&alias[1]); if (alias_iter == bot_command_aliases.end() || alias_iter->second.compare(command)) { - bot_owner->Message(Chat::White, "Undefined linker usage in %s (%s)", command_handler, &alias[1]); + bot_owner->Message(Chat::Yellow, "Undefined linker usage in %s (%s)", command_handler, &alias[1]); return true; } @@ -1951,12 +1951,12 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b } if (!druid_bot && !wizard_bot) { - bot_owner->Message(Chat::White, "No bots are capable of performing this action"); + bot_owner->Message(Chat::Yellow, "No bots are capable of performing this action"); return; } if (!local_list) { - bot_owner->Message(Chat::White, "There are no destinations you can be taken to."); + bot_owner->Message(Chat::Yellow, "There are no destinations you can be taken to."); return; } @@ -2030,7 +2030,7 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b } if (!destination_count) { - bot_owner->Message(Chat::White, "There are no destinations you can be taken to."); + bot_owner->Message(Chat::Yellow, "There are no destinations you can be taken to."); } } @@ -2049,7 +2049,7 @@ bool helper_no_available_bots(Client *bot_owner, Bot *my_bot) if (!bot_owner) return true; if (!my_bot) { - bot_owner->Message(Chat::White, "No bots are capable of performing this action"); + bot_owner->Message(Chat::Yellow, "No bots are capable of performing this action"); return true; } @@ -2108,7 +2108,7 @@ bool helper_spell_check_fail(STBaseEntry* local_entry) bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type) { if (!spell_list || spell_list->empty()) { - bot_owner->Message(Chat::White, "%s", required_bots_map[spell_type].c_str()); + bot_owner->Message(Chat::Yellow, "%s", required_bots_map[spell_type].c_str()); return true; } From 677a9fdfe0ebf4d4bd4b3da5d63271b9cb368731 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:08:57 -0600 Subject: [PATCH 031/394] Add more checks to bot names to prevent spacing or invalid characters --- zone/bot.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index aa793bd8ac..2e310f2d53 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1323,7 +1323,7 @@ bool Bot::IsValidName() bool Bot::IsValidName(std::string& name) { - if (name.length() < 4 || name.length() > 15) { + if (name.empty() || name.length() < 4 || name.length() > 15) { return false; } @@ -1332,10 +1332,15 @@ bool Bot::IsValidName(std::string& name) } for (char c : name.substr(1)) { - if (!RuleB(Bots, AllowCamelCaseNames) && !islower(c)) { + if (c == '_') { + return false; + } + + if (!isalpha(c)) { return false; } - if (isdigit(c) || ispunct(c)) { + + if (!RuleB(Bots, AllowCamelCaseNames) && !islower(c)) { return false; } } From 5c73581f90a854212feaa6753534e6bd2dffdde5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:09:19 -0600 Subject: [PATCH 032/394] Add AllowBotEquipAnyClassGear to bot trades --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2e310f2d53..03abe808e1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4206,7 +4206,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* } if ( - !trade_instance->IsClassEquipable(GetClass()) || + (!trade_instance->IsClassEquipable(GetClass()) && !RuleB(Bots, AllowBotEquipAnyClassGear))|| GetLevel() < trade_instance->GetItem()->ReqLevel || (!trade_instance->IsRaceEquipable(GetBaseRace()) && !RuleB(Bots, AllowBotEquipAnyRaceGear)) ) { From e6aeb01ddf445ffd0bdcf387d46227fdd2d86da9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:26:00 -0600 Subject: [PATCH 033/394] update and expand ^itemuse options and add lore checks --- zone/bot_commands/item_use.cpp | 224 ++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 56 deletions(-) diff --git a/zone/bot_commands/item_use.cpp b/zone/bot_commands/item_use.cpp index 975d30b90d..0db5983dcb 100644 --- a/zone/bot_commands/item_use.cpp +++ b/zone/bot_commands/item_use.cpp @@ -4,16 +4,17 @@ void bot_command_item_use(Client* c, const Seperator* sep) { if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: [%s empty] will display only bots that can use the item in an empty slot.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s byclass classID] - Example: [%s byclass 7] will display only bots that match the class that can use the item. Example is a Monk, use [^create help] for a list of class IDs.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "usage: [%s casteronly] will display only caster bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s hybridonly] will display only hybrid bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s meleeonly] will display only melee bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s wiscasteronly] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s intcasteronly] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s plateonly] will display only Plate-wearing bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s chainonly] will display only Chain-wearing bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s leatheronly] will display only Leather-wearing bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s clothonly] will display only Cloth-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s caster] will display only caster bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s hybrid] will display only hybrid bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s melee] will display only melee bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s wiscaster] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s intcaster] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s plate] will display only Plate-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s chain] will display only Chain-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s leather] will display only Leather-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s cloth] will display only Cloth-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s haste] will display bots that have no or lesser haste than the item.", sep->arg[0]); + c->Message(Chat::White, "usage: You can also use empty or haste as an argument to narrow down further, for example [%s caster empty], [%s plate haste] or even [%s empty haste]", sep->arg[0], sep->arg[0], sep->arg[0]); return; } @@ -28,136 +29,252 @@ void bot_command_item_use(Client* c, const Seperator* sep) bool chain_only = false; bool leather_only = false; bool cloth_only = false; + bool haste_only = false; + int haste_value = 0; + int ab_arg = 2; std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; - if (arg1.compare("empty") == 0) { + + if (arg1.compare("empty") == 0 || arg2.compare("empty") == 0) { empty_only = true; + + if (arg2.compare("empty") == 0) { + ++ab_arg; + } } - else if (arg1.compare("byclass") == 0) { - if (Strings::IsNumber(sep->arg[2])) { - class_mask = Strings::ToUnsignedInt(sep->arg[2]); - if (!(class_mask >= Class::Warrior && class_mask <= Class::Berserker)) { - c->Message(Chat::White, "Invalid class range, you must choose between 1 (Warrior) and 15 (Beastlord)"); - return; - } + + if (arg1.compare("haste") == 0 || arg2.compare("haste") == 0) { + haste_only = true; + + if (arg2.compare("haste") == 0) { + ++ab_arg; } } - else if (arg1.compare("casteronly") == 0) { + + if (arg1.compare("caster") == 0) { caster_only = true; } - else if (arg1.compare("hybridonly") == 0) { + else if (arg1.compare("hybrid") == 0) { hybrid_only = true; } - else if (arg1.compare("meleeonly") == 0) { + else if (arg1.compare("melee") == 0) { melee_only = true; } - else if (arg1.compare("wiscasteronly") == 0) { + else if (arg1.compare("wiscaster") == 0) { wis_caster_only = true; } - else if (arg1.compare("intcasteronly") == 0) { + else if (arg1.compare("intcaster") == 0) { int_caster_only = true; } - else if (arg1.compare("plateonly") == 0) { + else if (arg1.compare("plate") == 0) { plate_only = true; } - else if (arg1.compare("chainonly") == 0) { + else if (arg1.compare("chain") == 0) { chain_only = true; } - else if (arg1.compare("leatheronly") == 0) { + else if (arg1.compare("leather") == 0) { leather_only = true; } - else if (arg1.compare("clothonly") == 0) { + else if (arg1.compare("cloth") == 0) { cloth_only = true; } - else if (!arg1.empty()) { - c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]); - return; + else { + if (arg1.empty()) { + --ab_arg; + } + else { + if (!(arg1.compare("empty") == 0) && !(arg1.compare("haste") == 0)) { + c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]); + + return; + } + } } + const auto item_instance = c->GetInv().GetItem(EQ::invslot::slotCursor); + if (!item_instance) { - c->Message(Chat::White, "No item found on cursor!"); + c->Message(Chat::Yellow, "No item found on cursor! For help use %s help.", sep->arg[0]); + return; } auto item_data = item_instance->GetItem(); + if (!item_data) { - c->Message(Chat::White, "No data found for cursor item!"); + c->Message(Chat::Yellow, "No data found for cursor item!"); + return; } if (item_data->ItemClass != EQ::item::ItemClassCommon || item_data->Slots == 0) { - c->Message(Chat::White, "'%s' is not an equipable item!", item_data->Name); + c->Message(Chat::Yellow, "'%s' is not an equipable item!", item_data->Name); + return; } std::vector equipable_slot_list; + for (int16 equipable_slot = EQ::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQ::invslot::EQUIPMENT_END; ++equipable_slot) { if (item_data->Slots & (1 << equipable_slot)) { equipable_slot_list.emplace_back(equipable_slot); } } - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemInst); + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - if (class_mask) { - ActionableBots::Filter_ByClasses(c, sbl, GetPlayerClassBit(class_mask)); + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; } + sbl.remove(nullptr); + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemData); + for (const auto& bot_iter : sbl) { if (!bot_iter) { continue; } + if (caster_only && !IsCasterClass(bot_iter->GetClass())) { continue; } + if (hybrid_only && !IsSpellFighterClass(bot_iter->GetClass())) { continue; } + if (melee_only && !IsNonSpellFighterClass(bot_iter->GetClass())) { continue; } + if (wis_caster_only && !IsWISCasterClass(bot_iter->GetClass())) { continue; } + if (int_caster_only && !IsINTCasterClass(bot_iter->GetClass())) { continue; } + if (plate_only && !IsPlateClass(bot_iter->GetClass())) { continue; } + if (chain_only && !IsChainClass(bot_iter->GetClass())) { continue; } + if (leather_only && !IsLeatherClass(bot_iter->GetClass())) { continue; } + if (cloth_only && !IsClothClass(bot_iter->GetClass())) { continue; } - if (((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace())) || ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) { + + if ( + (!RuleB(Bots, AllowBotEquipAnyRaceGear) && ((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace()))) || + (!RuleB(Bots, AllowBotEquipAnyClassGear) && ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) + ) { continue; } + std::list refined_equipable_slot_list; + bool skip_bot = false; + const EQ::ItemData* equipped_item = nullptr; + const EQ::ItemInstance* equipped_inst = nullptr; + for (const auto& slot_iter : equipable_slot_list) { // needs more failure criteria - this should cover the bulk for now if (slot_iter == EQ::invslot::slotSecondary && item_data->Damage && !bot_iter->CanThisClassDualWield()) { continue; } - auto equipped_item = bot_iter->GetInv()[slot_iter]; + if (item_data->ReqLevel > bot_iter->GetLevel()) { + continue; + } + + haste_value = 0; + equipped_item = nullptr; + equipped_inst = nullptr; + + + for (int16 equipable_slot = EQ::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQ::invslot::EQUIPMENT_END; ++equipable_slot) { + equipped_inst = bot_iter->GetInv()[equipable_slot]; + if (equipped_inst && equipped_inst->GetItem()) { + equipped_item = equipped_inst->GetItem(); + + if (item_data->CheckLoreConflict(equipped_item)) { + skip_bot = true; + break; + } + + if (haste_only) { + if (equipped_item->Haste > haste_value) { + haste_value = equipped_item->Haste; + } + } + } + } + + if (skip_bot) { + break; + } + + if (haste_only && item_data->Haste < haste_value) { + continue; + } + + equipped_inst = bot_iter->GetInv()[slot_iter]; + + if (equipped_inst && empty_only) { + continue; + } + + refined_equipable_slot_list.push_back(slot_iter); + } + + if (skip_bot) { + continue; + } + + if (refined_equipable_slot_list.empty()) { + continue; + } + + for (auto slot_iter : refined_equipable_slot_list) { + equipped_item = nullptr; + equipped_inst = nullptr; - if (equipped_item && !empty_only) { - linker.SetItemInst(equipped_item); + equipped_inst = bot_iter->GetInv()[slot_iter]; + + if (equipped_inst && equipped_inst->GetItem()) { + equipped_item = equipped_inst->GetItem(); + } + + if (equipped_item) { + linker.SetItemData(equipped_item); c->Message( Chat::Say, fmt::format( - "{} says, 'I can use that for my {} instead of my {}! Would you like to {} my {}?'", + "{} says, 'I can use that for my {} instead of my {}! Would you like to {}?'", Saylink::Silent( fmt::format( "^inventorygive byname {}", @@ -173,21 +290,16 @@ void bot_command_item_use(Client* c, const Seperator* sep) slot_iter, bot_iter->GetCleanName() ), - "remove" - ), - linker.GenerateLink() + "remove my item" + ) ).c_str() ); - - if (RuleB(Bots, DoResponseAnimations)) { - bot_iter->DoAnim(29); - } } - else if (!equipped_item) { + else { c->Message( Chat::Say, fmt::format( - "{} says, 'I can use that for my {}! Would you like to {} it to me?'", + "{} says, 'I can use that for my {}! Would you like to {}?'", Saylink::Silent( fmt::format( "^inventorygive byname {}", @@ -201,14 +313,14 @@ void bot_command_item_use(Client* c, const Seperator* sep) "^inventorygive byname {}", bot_iter->GetCleanName() ), - "give" + "give it to me" ) ).c_str() ); + } - if (RuleB(Bots, DoResponseAnimations)) { - bot_iter->DoAnim(29); - } + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(29); } } } From d97f691255c96f152ea6f36a9e0e6a13e5c12cbf Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:09:08 -0600 Subject: [PATCH 034/394] misc cleanup --- zone/bot.cpp | 220 +++++++++++++++++++++---------------------- zone/bot.h | 22 ++--- zone/botspellsai.cpp | 18 ++-- 3 files changed, 130 insertions(+), 130 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 03abe808e1..e5d5600052 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9406,14 +9406,14 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return true; } -bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { +bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { if (!tar) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme return false; } if (doPrechecks) { - if (spells[spellid].target_type == ST_Self && tar != this) { + if (spells[spell_id].target_type == ST_Self && tar != this) { tar = this; } @@ -9425,105 +9425,105 @@ bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrecheck LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme - if (!IsValidSpell(spellid)) { + if (!IsValidSpell(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme return false; } - if (spells[spellid].target_type == ST_Self && tar != this) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (spells[spell_id].target_type == ST_Self && tar != this) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!CheckSpellRecastTimer(spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (!CheckSpellRecastTimer(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (!BotHasEnoughMana(spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (!BotHasEnoughMana(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (zone->IsSpellBlocked(spellid, glm::vec3(GetPosition()))) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (!zone->CanLevitate() && IsEffectInSpell(spellid, SE_Levitate)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (spells[spellid].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (spells[spellid].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (spells[spellid].zone_type == 1 && !zone->CanCastOutdoor()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (!AECheck && !IsValidSpellRange(spellid, tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), tar->GetBodyType())) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (tar->IsImmuneToBotSpell(spellid, this)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (tar->IsImmuneToBotSpell(spell_id, this)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!DoResistCheckBySpellType(tar, spellid, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if ( - (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spellid) != 0)) + (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spell_id) != 0)) && - tar->CanBuffStack(spellid, GetLevel(), true) < 0 + tar->CanBuffStack(spell_id, GetLevel(), true) < 0 ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (IsBeneficialSpell(spellid) && tar->BuffCount() >= tar->GetCurrentBuffSlots() && CalcBuffDuration(this, tar, spellid) != 0) { + if (IsBeneficialSpell(spell_id) && tar->BuffCount() >= tar->GetCurrentBuffSlots() && CalcBuffDuration(this, tar, spell_id) != 0) { return false; } - LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme - if (!CanCastSpellType(spellType, spellid, tar)) { + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!CanCastSpellType(spellType, spell_id, tar)) { return false; } return true; } -bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { - if (!spellid || !tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spellid ? GetSpellName(spellid) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme +bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { + if (!spell_id || !tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme return false; } @@ -9540,44 +9540,44 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PetResistBuffs: if ( !( - spells[spellid].target_type == ST_Target || - spells[spellid].target_type == ST_Pet || - (tar == this && spells[spellid].target_type != ST_TargetsTarget) || - spells[spellid].target_type == ST_Group || - spells[spellid].target_type == ST_GroupTeleport + spells[spell_id].target_type == ST_Target || + spells[spell_id].target_type == ST_Pet || + (tar == this && spells[spell_id].target_type != ST_TargetsTarget) || + spells[spell_id].target_type == ST_Group || + spells[spell_id].target_type == ST_GroupTeleport ) ) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (tar->IsBlockedBuff(spellid)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (tar->IsBlockedBuff(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (IsEffectInSpell(spellid, SE_Teleport) || IsEffectInSpell(spellid, SE_Succor)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spellid, SE_Illusion)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spell_id, SE_Illusion)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (spells[spellid].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (spells[spell_id].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if ((IsGroupSpell(spellid) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if ((IsGroupSpell(spell_id) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9587,21 +9587,21 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { if ( tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( - IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS)) || + (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; case Archetype::Melee: if ( - IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || - IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || - IsEffectInSpell(spellid, SE_CurrentMana) + IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || IsEffectInSpell(spell_id, SE_ManaPool) || + IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || + IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9613,14 +9613,14 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { // Differences for each type if (spellType != BotSpellTypes::InCombatBuff) { - if (IsEffectInSpell(spellid, SE_AbsorbMagicAtt) || IsEffectInSpell(spellid, SE_Rune)) { + if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { for (int i = 0; i < tar->GetMaxTotalSlots(); i++) { uint32 buff_count = tar->GetMaxTotalSlots(); for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { if (IsLichSpell(tar->GetBuffs()[j].spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } } @@ -9633,8 +9633,8 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9644,21 +9644,21 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { if ( tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( - IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS)) || + (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; case Archetype::Melee: if ( - IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || - IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || - IsEffectInSpell(spellid, SE_CurrentMana) + IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || IsEffectInSpell(spell_id, SE_ManaPool) || + IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || + IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9673,7 +9673,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { break; } - LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return true; } @@ -9692,9 +9692,9 @@ bool Bot::BotHasEnoughMana(uint16 spell_id) { return true; } -bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { +bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { - if (!tar || !spellid) { + if (!tar || !spell_id) { return true; } @@ -9717,18 +9717,18 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { m->IsCasting() && m->CastToBot()->casting_spell_targetid && entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(tar->GetID()) && - m->CastingSpellID() == spellid + m->CastingSpellID() == spell_id ) { return true; } else { - if (IsGroupSpell(spellid)) { + if (IsGroupSpell(spell_id)) { if ( m->IsBot() && m->IsCasting() && m->CastToBot()->casting_spell_targetid && - m->CastingSpellID() == spellid + m->CastingSpellID() == spell_id ) { std::vector x = GatherGroupSpellTargets(); @@ -9746,13 +9746,13 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { return false; } -bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { +bool Bot::DoResistCheck(Mob* tar, uint16 spell_id, int32 resist_limit) { - if (!tar || spellid == 0) { + if (!tar || spell_id == 0) { return false; } - int32 resist_difficulty = -spells[spellid].resist_difficulty; + int32 resist_difficulty = -spells[spell_id].resist_difficulty; int32 level_mod = (tar->GetLevel() - GetLevel()) * (tar->GetLevel() - GetLevel()) / 2; if (tar->GetLevel() - GetLevel() < 0) { @@ -9761,7 +9761,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { int32 targetResist = 0; - switch (GetSpellResistType(spellid)) { + switch (GetSpellResistType(spell_id)) { case RESIST_NONE: return true; case RESIST_MAGIC: @@ -9785,7 +9785,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { default: return true; } - //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spellid), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) + //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) if ((targetResist + level_mod - resist_difficulty) > resist_limit) { return false; } @@ -9793,8 +9793,8 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { return true; } -bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType) { - if (!tar || !IsValidSpell(spellid)) { +bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType) { + if (!tar || !IsValidSpell(spell_id)) { return false; } @@ -9802,11 +9802,11 @@ bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType) { return true; } - return DoResistCheck(tar, spellid, GetSpellTypeResistLimit(spellType)); + return DoResistCheck(tar, spell_id, GetSpellTypeResistLimit(spellType)); } -bool Bot::IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType) { - if (!spellid) { +bool Bot::IsValidTargetType(uint16 spell_id, int targetType, uint8 bodyType) { + if (!spell_id) { return false; } @@ -9890,7 +9890,7 @@ bool Bot::IsMobEngagedByAnyone(Mob* tar) { return false; } -bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid) { +bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id) { if (npc->GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { return false; } @@ -9907,7 +9907,7 @@ bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid) { return false; } - if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), npc->GetBodyType())) { + if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), npc->GetBodyType())) { return false; } @@ -10952,58 +10952,58 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { return spellType; } -bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { - if (IsAEBotSpellType(spellType) && !IsAnyAESpell(spellid)) { +bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { + if (IsAEBotSpellType(spellType) && !IsAnyAESpell(spell_id)) { return false; } - if (IsGroupBotSpellType(spellType) && !IsGroupSpell(spellid)) { + if (IsGroupBotSpellType(spellType) && !IsGroupSpell(spell_id)) { return false; } switch (spellType) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: - if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { + if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { return false; } return true; case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: - if (IsResistanceOnlySpell(spellid)) { + if (IsResistanceOnlySpell(spell_id)) { return true; } return false; case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - if (IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { + if (IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { return true; } return false; case BotSpellTypes::PBAENuke: - if (IsPBAENukeSpell(spellid) && !IsStunSpell(spellid)) { + if (IsPBAENukeSpell(spell_id) && !IsStunSpell(spell_id)) { return true; } return false; case BotSpellTypes::AERains: - if (IsAERainNukeSpell(spellid) && !IsStunSpell(spellid)) { + if (IsAERainNukeSpell(spell_id) && !IsStunSpell(spell_id)) { return true; } return false; case BotSpellTypes::AEStun: case BotSpellTypes::Stun: - if (IsStunSpell(spellid)) { + if (IsStunSpell(spell_id)) { return true; } return false; case BotSpellTypes::AENukes: case BotSpellTypes::Nuke: - if (!IsStunSpell(spellid)) { + if (!IsStunSpell(spell_id)) { return true; } @@ -11199,9 +11199,9 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } -bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar) { - int spellRange = botCaster->GetActSpellRange(spellid, spells[spellid].range); - int spellAERange = botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range); +bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar) { + int spellRange = botCaster->GetActSpellRange(spell_id, spells[spell_id].range); + int spellAERange = botCaster->GetActSpellRange(spell_id, spells[spell_id].aoe_range); int targetCount = 0; for (auto& close_mob : botCaster->m_close_mobs) { @@ -11244,14 +11244,14 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob continue; } - if (SpellBreaksMez(spellid) && m->IsMezzed()) { + if (SpellBreaksMez(spell_id) && m->IsMezzed()) { continue; } - if (IsPBAESpell(spellid)) { + if (IsPBAESpell(spell_id)) { if ( spellAERange >= Distance(botCaster->GetPosition(), m->GetPosition()) && - botCaster->CastChecks(spellid, m, spellType, true, true) + botCaster->CastChecks(spell_id, m, spellType, true, true) ) { ++targetCount; } @@ -11263,7 +11263,7 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob if ( spellAERange >= Distance(tar->GetPosition(), m->GetPosition()) && - botCaster->CastChecks(spellid, m, spellType, true, true) + botCaster->CastChecks(spell_id, m, spellType, true, true) ) { ++targetCount; } diff --git a/zone/bot.h b/zone/bot.h index ff3c7c1296..b3f2ae1712 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -445,13 +445,13 @@ class Bot : public NPC { std::vector GatherSpellTargets(bool entireRaid = false, bool noClients = false, bool noBots = false, bool noPets = false); bool PrecastChecks(Mob* tar, uint16 spellType); - bool CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); - bool CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar); + bool CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); + bool CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar); bool BotHasEnoughMana(uint16 spell_id); - bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid); - bool DoResistCheck(Mob* target, uint16 spellid, int32 resist_limit); - bool DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType); - bool IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType); + bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); + bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); + bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType); + bool IsValidTargetType(uint16 spell_id, int targetType, uint8 bodyType); bool IsMobEngagedByAnyone(Mob* tar); void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); void CopySettings(Bot* to, uint8 settingType, uint16 spellType = UINT16_MAX); @@ -522,11 +522,11 @@ class Bot : public NPC { std::list GetSpellTypesPrioritized(uint8 priorityType); uint16 GetSpellListSpellType(uint16 spellType); - bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid); + bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spellType); - bool HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar); + bool HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar); void CheckBotSpells(); @@ -592,8 +592,8 @@ class Bot : public NPC { static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE = false); - bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid); + static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE = false); + bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Pet); static std::string GetBotMagicianPetType(Bot* botCaster); @@ -735,7 +735,7 @@ class Bot : public NPC { inline const InspectMessage_Struct& GetInspectMessage() const { return _botInspectMessage; } // "Quest API" Methods - bool HasBotSpellEntry(uint16 spellid); + bool HasBotSpellEntry(uint16 spell_id); void ApplySpell(int spell_id, int duration = 0, int level = -1, ApplySpellType apply_type = ApplySpellType::Solo, bool allow_pets = false, bool is_raid_group_only = true); void BreakInvis(); void Escape(); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 5adbdb3e98..0138d58ad7 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1429,11 +1429,11 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster, uint16 spellType) { return result; } -Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE) { +Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE) { Mob* result = nullptr; if (botCaster && botCaster->GetOwner()) { - int spellRange = (!AE ? botCaster->GetActSpellRange(spellid, spells[spellid].range) : botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range)); + int spellRange = (!AE ? botCaster->GetActSpellRange(spell_id, spells[spell_id].range) : botCaster->GetActSpellRange(spell_id, spells[spell_id].aoe_range)); int buff_count = 0; NPC* npc = nullptr; @@ -1445,7 +1445,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy continue; } - if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), npc, spellid)) { + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), npc, spell_id)) { continue; } @@ -1459,11 +1459,11 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy continue; } - if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), m, spellid)) { + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), m, spell_id)) { continue; } - if (IsPBAESpell(spellid)) { + if (IsPBAESpell(spell_id)) { if (spellRange < Distance(botCaster->GetPosition(), m->GetPosition())) { continue; } @@ -1474,7 +1474,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy } } - if (botCaster->CastChecks(spellid, m, spellType, true, true)) { + if (botCaster->CastChecks(spell_id, m, spellType, true, true)) { ++targetCount; } @@ -1499,7 +1499,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy continue; } - if (!botCaster->CastChecks(spellid, npc, spellType, true)) { + if (!botCaster->CastChecks(spell_id, npc, spellType, true)) { continue; } @@ -2631,7 +2631,7 @@ void Bot::AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot) { } } -bool Bot::HasBotSpellEntry(uint16 spellid) { +bool Bot::HasBotSpellEntry(uint16 spell_id) { auto* spell_list = content_db.GetBotSpells(GetBotSpellID()); if (!spell_list) { @@ -2640,7 +2640,7 @@ bool Bot::HasBotSpellEntry(uint16 spellid) { // Check if Spell ID is found in Bot Spell Entries for (auto& e : spell_list->entries) { - if (spellid == e.spellid) { + if (spell_id == e.spellid) { return true; } } From bdea548460dfd1745d4264eae10ab9de19e12285 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:16:58 -0600 Subject: [PATCH 035/394] fix resistbuffs and damageshields spell type checks --- common/spdat.cpp | 21 ++------------------- common/spdat.h | 1 - zone/bot.cpp | 4 ++-- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 8211ecfdcc..7b0e540894 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3214,14 +3214,6 @@ bool IsResistanceOnlySpell(uint16 spell_id) { } bool IsDamageShieldOnlySpell(uint16 spell_id) { - if (SpellEffectsCount(spell_id) == 1 && IsEffectInSpell(spell_id, SE_DamageShield)) { - return true; - } - - return false; -} - -bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id) { if (!IsValidSpell(spell_id)) { return false; } @@ -3234,19 +3226,10 @@ bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id) { } if ( - spell.effect_id[i] == SE_DamageShield || - spell.effect_id[i] == SE_ResistFire || - spell.effect_id[i] == SE_ResistCold || - spell.effect_id[i] == SE_ResistPoison || - spell.effect_id[i] == SE_ResistDisease || - spell.effect_id[i] == SE_ResistMagic || - spell.effect_id[i] == SE_ResistCorruption || - spell.effect_id[i] == SE_ResistAll + spell.effect_id[i] != SE_DamageShield ) { - continue; + return false; } - - return false; } return true; diff --git a/common/spdat.h b/common/spdat.h index 60257a4014..4c1b52ecaf 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1726,6 +1726,5 @@ bool IsResurrectSpell(uint16 spell_id); bool RequiresStackCheck(uint16 spellType); bool IsResistanceOnlySpell(uint16 spell_id); bool IsDamageShieldOnlySpell(uint16 spell_id); -bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id); #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index e5d5600052..988c0d4e3d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10964,7 +10964,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { switch (spellType) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: - if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { + if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id)) { return false; } @@ -10978,7 +10978,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { return false; case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - if (IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { + if (IsDamageShieldOnlySpell(spell_id)) { return true; } From 1f82e12b34c2d71770f561295cb71a12250d7e33 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:18:11 -0600 Subject: [PATCH 036/394] Command help cleanup --- common/ruletypes.h | 1 + zone/bot.h | 2 - zone/bot_command.cpp | 79 ++++++++++++-------- zone/bot_command.h | 4 + zone/bot_commands/behind_mob.cpp | 22 +++--- zone/bot_commands/cast.cpp | 13 +++- zone/bot_commands/copy_settings.cpp | 24 +++--- zone/bot_commands/default_settings.cpp | 24 +++--- zone/bot_commands/illusion_block.cpp | 22 +++--- zone/bot_commands/max_melee_range.cpp | 22 +++--- zone/bot_commands/sit_hp_percent.cpp | 22 +++--- zone/bot_commands/sit_in_combat.cpp | 22 +++--- zone/bot_commands/sit_mana_percent.cpp | 22 +++--- zone/bot_commands/spell_aggro_checks.cpp | 25 +++---- zone/bot_commands/spell_delays.cpp | 25 +++---- zone/bot_commands/spell_engaged_priority.cpp | 25 +++---- zone/bot_commands/spell_holds.cpp | 25 +++---- zone/bot_commands/spell_idle_priority.cpp | 25 +++---- zone/bot_commands/spell_max_hp_pct.cpp | 25 +++---- zone/bot_commands/spell_max_mana_pct.cpp | 25 +++---- zone/bot_commands/spell_max_thresholds.cpp | 25 +++---- zone/bot_commands/spell_min_hp_pct.cpp | 25 +++---- zone/bot_commands/spell_min_mana_pct.cpp | 25 +++---- zone/bot_commands/spell_min_thresholds.cpp | 25 +++---- zone/bot_commands/spell_pursue_priority.cpp | 25 +++---- zone/bot_commands/spell_target_count.cpp | 25 +++---- zone/bot_commands/spelltypes.cpp | 9 +++ 27 files changed, 299 insertions(+), 314 deletions(-) create mode 100644 zone/bot_commands/spelltypes.cpp diff --git a/common/ruletypes.h b/common/ruletypes.h index ec8c60513e..8096c037b2 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -872,6 +872,7 @@ RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be s RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") +RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.h b/zone/bot.h index b3f2ae1712..8e85448e08 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -517,8 +517,6 @@ class Bot : public NPC { void SetManaWhenToMed(uint8 value) { _ManaWhenToMed = value; } void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } bool HasLoS() const { return _hasLoS; } - - void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false); std::list GetSpellTypesPrioritized(uint8 priorityType); uint16 GetSpellListSpellType(uint16 spellType); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 81f7b21a12..15ea7eba8a 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1375,6 +1375,8 @@ int bot_command_init(void) bot_command_add("spellsettingstoggle", "Toggle a bot spell use", AccountStatus::Player, bot_command_spell_settings_toggle) || bot_command_add("spellsettingsupdate", "Update a bot spell setting entry", 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("spelltypeids", "Lists spelltypes by ID", AccountStatus::Player, bot_command_spelltype_ids) || + bot_command_add("spelltypenames", "Lists spelltypes by shortname", AccountStatus::Player, bot_command_spelltype_names) || 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) || bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || @@ -2115,43 +2117,54 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp return false; } -void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt) { - if (helpPrompt) { - c->Message( - Chat::Yellow, - fmt::format( - "Use {}, {}, {} for a list of spell types by ID", - Saylink::Silent( - fmt::format("{} listid 0-19", arg0) - ), - Saylink::Silent( - fmt::format("{} listid 20-39", arg0) - ), - Saylink::Silent( - fmt::format("{} listid 40+", arg0) - ) - ).c_str() - ); +void SendSpellTypePrompts(Client *c, bool commandedTypes) { + c->Message( + Chat::Yellow, + fmt::format( + "You can view spell types by ID or shortname: {}, {}, {} / {}, {}, {}", + Saylink::Silent( + fmt::format("^spelltypeids 0-19"), "ID 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypeids 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypeids 40+"), "40+" + ), + Saylink::Silent( + fmt::format("^spelltypenames 0-19"), "Shortname 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypenames 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypenames 40+"), "40+" + ) + ).c_str() + ); + if (commandedTypes) { c->Message( Chat::Yellow, fmt::format( - "Use {}, {}, {} for a list of spell types by short name", + "You can view commanded spell types by ID or shortname: {} / {}", Saylink::Silent( - fmt::format("{} listname 0-19", arg0) + fmt::format("^spelltypeids commanded"), "ID" ), Saylink::Silent( - fmt::format("{} listname 20-39", arg0) - ), - Saylink::Silent( - fmt::format("{} listname 40+", arg0) + fmt::format("^spelltypenames commanded"), "Shortname" ) ).c_str() ); - - return; } + return; +} + +void SendSpellTypeWindow(Client *c, const Seperator* sep) { + std::string arg0 = sep->arg[0]; + std::string arg1 = sep->arg[1]; + uint8 minCount = 0; uint8 maxCount = 0; @@ -2159,18 +2172,22 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st minCount = BotSpellTypes::START; maxCount = BotSpellTypes::END; } - else if (!arg2.compare("0-19")) { + else if (!arg1.compare("0-19")) { minCount = BotSpellTypes::START; maxCount = 19; } - else if (!arg2.compare("20-39")) { + else if (!arg1.compare("20-39")) { minCount = std::min(static_cast(20), static_cast(BotSpellTypes::END)); maxCount = std::min(static_cast(39), static_cast(BotSpellTypes::END)); } - else if (!arg2.compare("40+")) { + else if (!arg1.compare("40+")) { minCount = std::min(static_cast(40), static_cast(BotSpellTypes::END)); maxCount = BotSpellTypes::END; } + else if (!arg1.compare("commanded")) { + minCount = BotSpellTypes::COMMANDED_START; + maxCount = BotSpellTypes::COMMANDED_END; + } else { c->Message(Chat::Yellow, "You must choose a valid range option"); @@ -2199,7 +2216,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st DialogueWindow::TableCell( fmt::format( "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField)) + (!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField)) ) ) ); @@ -2231,7 +2248,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st DialogueWindow::TableCell( fmt::format( "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i))) + (!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i))) ) ) ); @@ -2242,7 +2259,6 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st c->SendPopupToClient("Spell Types", popup_text.c_str()); } - #include "bot_commands/actionable.cpp" #include "bot_commands/aggressive.cpp" #include "bot_commands/appearance.cpp" @@ -2310,6 +2326,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/spell_min_thresholds.cpp" #include "bot_commands/spell_pursue_priority.cpp" #include "bot_commands/spell_target_count.cpp" +#include "bot_commands/spelltypes.cpp" #include "bot_commands/summon.cpp" #include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 373fbd1e54..8e200bd6b3 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1733,6 +1733,8 @@ 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_spelltype_ids(Client* c, const Seperator* sep); +void bot_command_spelltype_names(Client* c, const Seperator* sep); void bot_spell_info_dialogue_window(Client* c, const Seperator *sep); void bot_command_enforce_spell_list(Client* c, const Seperator* sep); void bot_command_summon_corpse(Client *c, const Seperator *sep); @@ -1820,4 +1822,6 @@ void helper_send_available_subcommands(Client *bot_owner, const char* command_si void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = Class::None); bool helper_spell_check_fail(STBaseEntry* local_entry); bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type); +void SendSpellTypePrompts(Client *c, bool commandedTypes = false); +void SendSpellTypeWindow(Client *c, const Seperator* sep); #endif diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index a7710185f1..3abde3c1ac 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -64,24 +64,22 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index fd7dc5aa83..ab02eb950e 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -94,7 +94,8 @@ void bot_command_cast(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + SendSpellTypePrompts(c, true); + c->Message( Chat::Yellow, fmt::format( @@ -103,6 +104,16 @@ void bot_command_cast(Client* c, const Seperator* sep) ).c_str() ); + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + return; } diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index e2745442f7..8e41f971a7 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -97,25 +97,23 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + SendSpellTypePrompts(c); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 2; bool validOption = false; uint16 spellType = UINT16_MAX; diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index ddf809a751..8ed6733a68 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -91,25 +91,23 @@ void bot_command_default_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + SendSpellTypePrompts(c); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 2; bool validOption = false; uint16 spellType = UINT16_MAX; diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 94faafc426..47d2d6b264 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -63,13 +63,16 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; @@ -77,11 +80,6 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp index c9e32dec1b..6c4460ac2d 100644 --- a/zone/bot_commands/max_melee_range.cpp +++ b/zone/bot_commands/max_melee_range.cpp @@ -63,24 +63,22 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index fa4a183bf5..38b42ffd99 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -63,24 +63,22 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index 210372f562..eaf08e0ae0 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -63,24 +63,22 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index a5751f9f43..820be6a758 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -63,24 +63,22 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index 2dfeef4865..cbcc4706bb 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -92,25 +92,22 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 2a5de10174..9fd37b70a0 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -98,25 +98,22 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 70425ee114..f27387b590 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -96,25 +96,22 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index fd17749d18..e30911bd43 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -82,25 +82,22 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index d6f0a0c359..cdb741d44f 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -96,25 +96,22 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 7f8cd150b4..bcd3614a58 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 44cc7e8d53..84cb9abd9a 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 7ff6515144..385258dddc 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -98,25 +98,22 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 371b448201..be6fe1b9e1 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index b053c462cb..f4ffe1bade 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index d690dede25..ffcecf8b74 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -100,25 +100,22 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 872444f322..2721354226 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -96,25 +96,22 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index 2f78a74687..ad1b897b88 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -92,25 +92,22 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spelltypes.cpp b/zone/bot_commands/spelltypes.cpp new file mode 100644 index 0000000000..f91b3ed7e1 --- /dev/null +++ b/zone/bot_commands/spelltypes.cpp @@ -0,0 +1,9 @@ +void bot_command_spelltype_ids(Client* c, const Seperator* sep) +{ + SendSpellTypeWindow(c, sep); +} + +void bot_command_spelltype_names(Client* c, const Seperator* sep) +{ + SendSpellTypeWindow(c, sep); +} From b200bdd04e832e15e10c152e03089854da61a46e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:17:36 -0600 Subject: [PATCH 037/394] implement commanded cast types --- .../database_update_manifest_bots.cpp | 901 +++++++++++++++--- common/ruletypes.h | 4 + common/spdat.cpp | 57 ++ common/spdat.h | 18 + common/version.h | 2 +- zone/bot.cpp | 181 ++++ zone/bot.h | 18 +- zone/bot_command.cpp | 38 +- zone/bot_command.h | 15 - zone/bot_commands/bind_affinity.cpp | 42 - zone/bot_commands/cast.cpp | 226 ++++- zone/bot_commands/charm.cpp | 54 -- zone/bot_commands/cure.cpp | 71 -- zone/bot_commands/escape.cpp | 44 - zone/bot_commands/identify.cpp | 38 - zone/bot_commands/invisibility.cpp | 57 -- zone/bot_commands/levitation.cpp | 38 - zone/bot_commands/mesmerize.cpp | 41 - zone/bot_commands/movement_speed.cpp | 50 - zone/bot_commands/resistance.cpp | 73 -- zone/bot_commands/resurrect.cpp | 57 -- zone/bot_commands/rune.cpp | 38 - zone/bot_commands/send_home.cpp | 47 - zone/bot_commands/size.cpp | 50 - zone/bot_commands/water_breathing.cpp | 38 - zone/botspellsai.cpp | 59 +- zone/mob.cpp | 125 +++ zone/mob.h | 3 +- 28 files changed, 1393 insertions(+), 992 deletions(-) delete mode 100644 zone/bot_commands/bind_affinity.cpp delete mode 100644 zone/bot_commands/charm.cpp delete mode 100644 zone/bot_commands/cure.cpp delete mode 100644 zone/bot_commands/escape.cpp delete mode 100644 zone/bot_commands/identify.cpp delete mode 100644 zone/bot_commands/invisibility.cpp delete mode 100644 zone/bot_commands/levitation.cpp delete mode 100644 zone/bot_commands/mesmerize.cpp delete mode 100644 zone/bot_commands/movement_speed.cpp delete mode 100644 zone/bot_commands/resistance.cpp delete mode 100644 zone/bot_commands/resurrect.cpp delete mode 100644 zone/bot_commands/rune.cpp delete mode 100644 zone/bot_commands/send_home.cpp delete mode 100644 zone/bot_commands/size.cpp delete mode 100644 zone/bot_commands/water_breathing.cpp diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index a57a6f0753..96627d8140 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -312,7 +312,7 @@ UPDATE `bot_spells_entries` SET `type` = 21 WHERE `type` = 2097152; .sql = R"( UPDATE bot_spells_entries b, spells_new s SET b.`type` = 22 -WHERE b.spellid = s.id +WHERE b.spell_id = s.id AND ( s.`effectid1` = 23 OR s.`effectid2` = 23 OR @@ -332,177 +332,764 @@ AND ( ManifestEntry{ .version = 9049, .description = "2024_05_18_correct_bot_spell_entries_types.sql", - .check = "SELECT * FROM `bot_spells_entries` where `npc_spells_id` = 3002 AND `spellid` = 14312", + .check = "SELECT * FROM `bot_spells_entries` where `npc_spells_id` = 3002 AND `spell_id` = 14312", .condition = "empty", .match = "", .sql = R"( -- Class fixes -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14312; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14313; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14314; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15186; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15187; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15188; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14446; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14447; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14467; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14468; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14469; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14955; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14956; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14387; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14388; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14389; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spell_id` = 14312; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spell_id` = 14313; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spell_id` = 14314; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spell_id` = 15186; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spell_id` = 15187; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spell_id` = 15188; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14446; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14447; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14467; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14468; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14469; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spell_id` = 14955; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spell_id` = 14956; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14387; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14388; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14389; -- Minlevel fixes -UPDATE bot_spells_entries SET `minlevel` = 34 WHERE `spellid` = 1445 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 2 WHERE `spellid` = 229 AND `npc_spells_id` = 3011; -UPDATE bot_spells_entries SET `minlevel` = 13 WHERE `spellid` = 333 AND `npc_spells_id` = 3013; -UPDATE bot_spells_entries SET `minlevel` = 29 WHERE `spellid` = 106 AND `npc_spells_id` = 3013; -UPDATE bot_spells_entries SET `minlevel` = 38 WHERE `spellid` = 754 AND `npc_spells_id` = 3010; -UPDATE bot_spells_entries SET `minlevel` = 58 WHERE `spellid` = 2589 AND `npc_spells_id` = 3003; -UPDATE bot_spells_entries SET `minlevel` = 67 WHERE `spellid` = 5305 AND `npc_spells_id` = 3004; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 23 WHERE `spellid` = 738 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 51 WHERE `spellid` = 1751 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 7 WHERE `spellid` = 734 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 5 WHERE `spellid` = 717 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15186 AND `npc_spells_id` = 3005; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15187 AND `npc_spells_id` = 3005; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15188 AND `npc_spells_id` = 3005; -UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14955 AND `npc_spells_id` = 3003; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14956 AND `npc_spells_id` = 3003; -UPDATE bot_spells_entries SET `minlevel` = 78 WHERE `spellid` = 14387 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14388 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14389 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 34 WHERE `spell_id` = 1445 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 2 WHERE `spell_id` = 229 AND `npc_spells_id` = 3011; +UPDATE bot_spells_entries SET `minlevel` = 13 WHERE `spell_id` = 333 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 29 WHERE `spell_id` = 106 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 38 WHERE `spell_id` = 754 AND `npc_spells_id` = 3010; +UPDATE bot_spells_entries SET `minlevel` = 58 WHERE `spell_id` = 2589 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 67 WHERE `spell_id` = 5305 AND `npc_spells_id` = 3004; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 23 WHERE `spell_id` = 738 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 51 WHERE `spell_id` = 1751 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 7 WHERE `spell_id` = 734 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 5 WHERE `spell_id` = 717 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 15186 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 15187 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 15188 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spell_id` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spell_id` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14955 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14956 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 78 WHERE `spell_id` = 14387 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14388 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14389 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14314 AND `npc_spells_id` = 3002; -- Maxlevel fixes -UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spell_id` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spell_id` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spell_id` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spell_id` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spell_id` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spell_id` = 14314 AND `npc_spells_id` = 3002; -- Type fixes -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 201; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 752; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 2117; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2542; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2544; -UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 2115; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1403; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1405; -UPDATE bot_spells_entries SET `type` = 9 WHERE `spellid` = 289; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 294; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 302; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 521; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 185; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 450; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 186; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 4074; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 195; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1712; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1703; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 3229; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 3345; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5509; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6826; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 281; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 505; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 526; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 110; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 506; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 162; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 111; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 507; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 527; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 163; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 112; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1588; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1573; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1592; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1577; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1578; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 1576; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3386; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3387; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 4900; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3395; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5394; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5392; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6827; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 5416; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1437; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1436; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5348; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 8008; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 2571; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 370; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1741; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1296; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2634; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2942; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 3462; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6828; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14312; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14313; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14314; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18392; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18393; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18394; -UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 15186; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15187; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15188; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14446; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14447; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14467; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14468; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14469; -UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14267; -UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14268; -UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14269; -UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14955; -UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14956; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 14387; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14388; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14389; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 10436; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 201; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 752; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 2117; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2542; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2544; +UPDATE bot_spells_entries SET `type` = 6 WHERE `spell_id` = 2115; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1403; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1405; +UPDATE bot_spells_entries SET `type` = 9 WHERE `spell_id` = 289; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 294; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 302; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 521; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 185; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 450; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 4074; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 195; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 1712; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1703; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 3229; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 3345; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 5509; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 6826; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 270; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 281; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 505; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 526; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 110; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 506; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 162; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 111; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 507; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 527; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 163; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 112; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 1588; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1573; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1592; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1577; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1578; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 1576; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3386; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3387; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 4900; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3395; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 5394; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 5392; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 6827; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 5416; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1437; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1436; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 5348; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 8008; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 2571; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 370; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 1741; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 1296; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 270; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2634; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2942; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 3462; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 6828; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 14312; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 14313; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 14314; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 18392; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 18393; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 18394; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spell_id` = 15186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 15187; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 15188; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 14446; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 14447; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14467; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14468; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14469; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spell_id` = 14267; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spell_id` = 14268; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spell_id` = 14269; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spell_id` = 14955; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spell_id` = 14956; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 14387; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14388; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14389; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 10436; --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3440; -- Ro's Illumination [#3440] from DoT [#8] to Debuff [#14] [Should be 0] --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 303; -- Whirl till you hurl [#303] from Nuke [#0] to Debuff [#14] [Should be 0] --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 619; -- Dyn's Dizzying Draught [#619] from Nuke [#0] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3440; -- Ro's Illumination [#3440] from DoT [#8] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 303; -- Whirl till you hurl [#303] from Nuke [#0] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 619; -- Dyn's Dizzying Draught [#619] from Nuke [#0] to Debuff [#14] [Should be 0] --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 74; -- Mana Sieve [#74] from Nuke [#0] to Debuff [#14] --- UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 1686; -- Theft of Thought [#1686] from Nuke [#0] to Lifetap [#6] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 74; -- Mana Sieve [#74] from Nuke [#0] to Debuff [#14] +-- UPDATE bot_spells_entries SET `type` = 6 WHERE `spell_id` = 1686; -- Theft of Thought [#1686] from Nuke [#0] to Lifetap [#6] --- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 3694; -- Stoicism [#3694] from In-Combat Buff [#10] to Regular Heal [#1] --- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 4899; -- Breath of Trushar [#4899] from In-Combat Buff [#10] to Regular Heal [#1] +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 3694; -- Stoicism [#3694] from In-Combat Buff [#10] to Regular Heal [#1] +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 4899; -- Breath of Trushar [#4899] from In-Combat Buff [#10] to Regular Heal [#1] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1748; -- Angstlich's Assonance [#1748] from Slow [#13] to DoT [#8] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1748; -- Angstlich's Assonance [#1748] from Slow [#13] to DoT [#8] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +)" + }, + ManifestEntry{ + .version = 9050, + .description = "2024_11_26_add_commanded_spelltypes.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 100", + .condition = "empty", + .match = "", + .sql = R"( +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) +VALUES +(3006, 9957, 100, 20, 254), +(3006, 9956, 100, 20, 254), +(3006, 552, 100, 25, 254), +(3006, 550, 100, 25, 254), +(3006, 553, 100, 25, 254), +(3006, 2432, 100, 26, 254), +(3006, 2020, 100, 26, 254), +(3006, 551, 100, 27, 254), +(3006, 3792, 100, 28, 254), +(3006, 2419, 100, 29, 254), +(3006, 554, 100, 30, 254), +(3006, 557, 100, 31, 254), +(3006, 1434, 100, 32, 254), +(3006, 555, 100, 32, 254), +(3006, 25898, 100, 32, 254), +(3006, 25904, 100, 32, 254), +(3006, 556, 100, 32, 254), +(3006, 25698, 100, 33, 254), +(3006, 1517, 100, 33, 254), +(3006, 2424, 100, 33, 254), +(3006, 25689, 100, 33, 254), +(3006, 25899, 100, 34, 254), +(3006, 25690, 100, 35, 254), +(3006, 25903, 100, 35, 254), +(3006, 25900, 100, 35, 254), +(3006, 558, 100, 36, 254), +(3006, 2429, 100, 37, 254), +(3006, 1438, 100, 38, 254), +(3006, 3184, 100, 38, 254), +(3006, 25697, 100, 38, 254), +(3006, 25902, 100, 39, 254), +(3006, 25695, 100, 39, 254), +(3006, 25901, 100, 40, 254), +(3006, 25694, 100, 40, 254), +(3006, 1398, 100, 40, 254), +(3006, 25905, 100, 41, 254), +(3006, 25696, 100, 42, 254), +(3006, 1440, 100, 42, 254), +(3006, 25906, 100, 43, 254), +(3006, 25693, 100, 44, 254), +(3006, 25699, 100, 45, 254), +(3006, 24773, 100, 46, 254), +(3006, 8965, 100, 52, 254), +(3006, 24771, 100, 52, 254), +(3006, 8235, 100, 52, 254), +(3006, 24775, 100, 52, 254), +(3006, 4966, 100, 54, 254), +(3006, 6184, 100, 55, 254), +(3006, 5731, 100, 55, 254), +(3006, 24776, 100, 56, 254), +(3006, 25700, 100, 56, 254), +(3006, 25691, 100, 57, 254), +(3006, 24772, 100, 57, 254), +(3006, 25692, 100, 57, 254), +(3006, 11981, 100, 59, 254), +(3006, 9953, 100, 60, 254), +(3006, 9954, 100, 60, 254), +(3006, 11980, 100, 64, 254), +(3006, 6179, 100, 64, 254), +(3006, 24774, 100, 67, 254), +(3006, 9950, 100, 70, 254), +(3006, 9951, 100, 70, 254), +(3006, 15886, 100, 75, 254), +(3006, 15887, 100, 75, 254), +(3006, 21989, 100, 80, 254), +(3006, 20539, 100, 80, 254), +(3006, 21984, 100, 80, 254), +(3006, 20538, 100, 80, 254), +(3006, 17883, 100, 85, 254), +(3006, 17884, 100, 85, 254), +(3006, 28997, 100, 90, 254), +(3006, 28998, 100, 90, 254), +(3006, 29000, 100, 92, 254), +(3006, 29001, 100, 92, 254), +(3006, 34832, 100, 95, 254), +(3006, 40217, 100, 95, 254), +(3006, 34833, 100, 95, 254), +(3006, 40216, 100, 95, 254), +(3012, 10881, 100, 20, 254), +(3012, 10880, 100, 20, 254), +(3012, 562, 100, 25, 254), +(3012, 563, 100, 27, 254), +(3012, 3793, 100, 27, 254), +(3012, 561, 100, 28, 254), +(3012, 2420, 100, 29, 254), +(3012, 2944, 100, 29, 254), +(3012, 564, 100, 32, 254), +(3012, 565, 100, 33, 254), +(3012, 1418, 100, 33, 254), +(3012, 2425, 100, 33, 254), +(3012, 1516, 100, 34, 254), +(3012, 1338, 100, 35, 254), +(3012, 3833, 100, 35, 254), +(3012, 566, 100, 35, 254), +(3012, 1336, 100, 36, 254), +(3012, 2943, 100, 36, 254), +(3012, 1423, 100, 36, 254), +(3012, 567, 100, 36, 254), +(3012, 568, 100, 37, 254), +(3012, 1337, 100, 37, 254), +(3012, 3180, 100, 38, 254), +(3012, 1339, 100, 38, 254), +(3012, 2421, 100, 39, 254), +(3012, 2430, 100, 39, 254), +(3012, 1372, 100, 40, 254), +(3012, 2426, 100, 41, 254), +(3012, 1371, 100, 41, 254), +(3012, 1399, 100, 42, 254), +(3012, 1374, 100, 42, 254), +(3012, 1373, 100, 43, 254), +(3012, 1425, 100, 43, 254), +(3012, 1375, 100, 44, 254), +(3012, 3181, 100, 45, 254), +(3012, 2022, 100, 45, 254), +(3012, 666, 100, 46, 254), +(3012, 3849, 100, 46, 254), +(3012, 674, 100, 46, 254), +(3012, 2023, 100, 46, 254), +(3012, 2024, 100, 47, 254), +(3012, 2025, 100, 48, 254), +(3012, 2431, 100, 49, 254), +(3012, 8966, 100, 51, 254), +(3012, 8236, 100, 51, 254), +(3012, 4965, 100, 54, 254), +(3012, 8969, 100, 55, 254), +(3012, 8239, 100, 55, 254), +(3012, 6183, 100, 55, 254), +(3012, 5732, 100, 55, 254), +(3012, 4964, 100, 57, 254), +(3012, 6182, 100, 58, 254), +(3012, 5735, 100, 60, 254), +(3012, 10877, 100, 60, 254), +(3012, 10878, 100, 60, 254), +(3012, 6178, 100, 64, 254), +(3012, 6177, 100, 67, 254), +(3012, 11984, 100, 69, 254), +(3012, 10874, 100, 70, 254), +(3012, 10875, 100, 70, 254), +(3012, 11983, 100, 74, 254), +(3012, 15889, 100, 75, 254), +(3012, 15890, 100, 75, 254), +(3012, 21988, 100, 80, 254), +(3012, 20542, 100, 80, 254), +(3012, 21985, 100, 80, 254), +(3012, 20541, 100, 80, 254), +(3012, 17886, 100, 85, 254), +(3012, 17887, 100, 85, 254), +(3012, 29840, 100, 90, 254), +(3012, 29841, 100, 90, 254), +(3012, 29843, 100, 92, 254), +(3012, 29844, 100, 92, 254), +(3012, 40443, 100, 95, 254), +(3012, 35715, 100, 95, 254), +(3012, 40442, 100, 95, 254), +(3012, 35714, 100, 95, 254), +(3002, 208, 101, 1, 4), +(3002, 501, 101, 5, 14), +(3002, 47, 101, 15, 35), +(3002, 45, 101, 36, 64), +(3002, 1541, 101, 55, 254), +(3002, 3197, 101, 65, 254), +(3002, 5274, 101, 70, 70), +(3002, 9798, 101, 71, 75), +(3002, 9799, 101, 71, 75), +(3002, 9797, 101, 71, 75), +(3002, 14288, 101, 76, 80), +(3002, 14289, 101, 76, 80), +(3002, 14290, 101, 76, 80), +(3002, 18309, 101, 81, 85), +(3002, 18310, 101, 81, 85), +(3002, 18311, 101, 81, 85), +(3002, 25103, 101, 86, 90), +(3002, 25101, 101, 86, 90), +(3002, 25102, 101, 86, 90), +(3002, 28102, 101, 91, 95), +(3002, 28100, 101, 91, 95), +(3002, 28101, 101, 91, 95), +(3002, 34096, 101, 96, 254), +(3002, 34094, 101, 96, 254), +(3002, 34095, 101, 96, 254), +(3003, 208, 101, 10, 24), +(3003, 501, 101, 25, 42), +(3003, 47, 101, 43, 48), +(3003, 45, 101, 49, 254), +(3003, 25294, 101, 86, 90), +(3003, 25295, 101, 86, 90), +(3003, 25296, 101, 86, 90), +(3003, 28340, 101, 91, 95), +(3003, 28338, 101, 91, 95), +(3003, 28339, 101, 91, 95), +(3003, 34346, 101, 96, 254), +(3003, 34344, 101, 96, 254), +(3003, 34345, 101, 96, 254), +(3004, 240, 101, 4, 30), +(3004, 250, 101, 22, 254), +(3004, 513, 101, 31, 254), +(3004, 3601, 101, 39, 254), +(3004, 5316, 101, 68, 70), +(3004, 10112, 101, 71, 75), +(3004, 10110, 101, 71, 75), +(3004, 10111, 101, 71, 75), +(3004, 15037, 101, 76, 80), +(3004, 15035, 101, 76, 80), +(3004, 15036, 101, 76, 80), +(3004, 19168, 101, 81, 85), +(3004, 19169, 101, 81, 85), +(3004, 19167, 101, 81, 85), +(3004, 25417, 101, 86, 87), +(3004, 25418, 101, 86, 90), +(3004, 25419, 101, 86, 90), +(3004, 25466, 101, 88, 90), +(3004, 25467, 101, 88, 90), +(3004, 25465, 101, 88, 90), +(3004, 28479, 101, 91, 92), +(3004, 28480, 101, 91, 95), +(3004, 28481, 101, 91, 95), +(3004, 28542, 101, 93, 95), +(3004, 28543, 101, 93, 95), +(3004, 28544, 101, 93, 95), +(3004, 34500, 101, 96, 97), +(3004, 34502, 101, 96, 254), +(3004, 34501, 101, 96, 254), +(3004, 34565, 101, 98, 254), +(3004, 34563, 101, 98, 254), +(3004, 34564, 101, 98, 254), +(3005, 347, 101, 9, 51), +(3005, 448, 101, 52, 254), +(3006, 240, 101, 1, 14), +(3006, 250, 101, 5, 254), +(3006, 513, 101, 15, 254), +(3006, 3601, 101, 29, 254), +(3006, 5347, 101, 67, 70), +(3006, 9851, 101, 71, 75), +(3006, 9852, 101, 71, 75), +(3006, 9853, 101, 71, 75), +(3006, 14369, 101, 76, 80), +(3006, 14367, 101, 76, 80), +(3006, 14368, 101, 76, 80), +(3006, 18409, 101, 81, 85), +(3006, 18407, 101, 81, 85), +(3006, 18408, 101, 81, 85), +(3006, 25736, 101, 86, 90), +(3006, 25734, 101, 86, 90), +(3006, 25735, 101, 86, 90), +(3006, 28831, 101, 91, 95), +(3006, 28832, 101, 91, 95), +(3006, 28830, 101, 91, 95), +(3006, 34863, 101, 96, 254), +(3006, 34864, 101, 96, 254), +(3006, 34862, 101, 96, 254), +(3007, 4614, 101, 35, 49), +(3007, 4683, 101, 50, 56), +(3007, 4684, 101, 57, 63), +(3007, 4698, 101, 64, 64), +(3007, 5019, 101, 65, 254), +(3007, 5020, 101, 65, 254), +(3007, 6175, 101, 69, 70), +(3007, 10949, 101, 71, 75), +(3007, 10947, 101, 71, 75), +(3007, 10948, 101, 71, 75), +(3007, 14800, 101, 76, 80), +(3007, 14801, 101, 76, 80), +(3007, 14799, 101, 76, 80), +(3007, 18905, 101, 81, 85), +(3007, 18906, 101, 81, 85), +(3007, 18904, 101, 81, 85), +(3007, 25912, 101, 86, 90), +(3007, 25913, 101, 86, 90), +(3007, 25911, 101, 86, 90), +(3007, 29006, 101, 91, 95), +(3007, 29007, 101, 91, 95), +(3007, 29008, 101, 91, 95), +(3007, 35047, 101, 96, 254), +(3007, 35048, 101, 96, 254), +(3007, 35049, 101, 96, 254), +(3008, 728, 101, 8, 60), +(3008, 3361, 101, 61, 254), +(3008, 5370, 101, 66, 70), +(3008, 10403, 101, 71, 75), +(3008, 10401, 101, 71, 75), +(3008, 10402, 101, 71, 75), +(3008, 14002, 101, 76, 80), +(3008, 14000, 101, 76, 80), +(3008, 14001, 101, 76, 80), +(3008, 18001, 101, 81, 85), +(3008, 18002, 101, 81, 85), +(3008, 18000, 101, 81, 85), +(3008, 25978, 101, 86, 90), +(3008, 25979, 101, 86, 90), +(3008, 25977, 101, 86, 90), +(3008, 29079, 101, 91, 95), +(3008, 29080, 101, 91, 95), +(3008, 29078, 101, 91, 95), +(3008, 35131, 101, 96, 254), +(3008, 35132, 101, 96, 254), +(3008, 35133, 101, 96, 254), +(3011, 347, 101, 2, 22), +(3011, 448, 101, 23, 254), +(3014, 208, 101, 1, 5), +(3014, 501, 101, 6, 17), +(3014, 47, 101, 18, 34), +(3014, 45, 101, 35, 61), +(3014, 1541, 101, 51, 254), +(3014, 3197, 101, 62, 254), +(3014, 5506, 101, 67, 71), +(3014, 10601, 101, 72, 76), +(3014, 10599, 101, 72, 76), +(3014, 10600, 101, 72, 76), +(3014, 14510, 101, 77, 81), +(3014, 14511, 101, 77, 81), +(3014, 14509, 101, 77, 81), +(3014, 18568, 101, 82, 86), +(3014, 18569, 101, 82, 86), +(3014, 18567, 101, 82, 86), +(3014, 26921, 101, 87, 91), +(3014, 26922, 101, 87, 91), +(3014, 26920, 101, 87, 91), +(3014, 30054, 101, 92, 96), +(3014, 30055, 101, 92, 96), +(3014, 30056, 101, 92, 96), +(3014, 36116, 101, 97, 254), +(3014, 36117, 101, 97, 254), +(3014, 36118, 101, 97, 254), +(3006, 2183, 102, 18, 56), +(3006, 1567, 102, 57, 254), +(3012, 2184, 102, 18, 56), +(3012, 2558, 102, 56, 64), +(3012, 1628, 102, 57, 254), +(3012, 3244, 102, 65, 254), +(3002, 35, 103, 10, 254), +(3006, 35, 103, 12, 254), +(3010, 35, 103, 14, 254), +(3011, 35, 103, 12, 254), +(3012, 35, 103, 12, 254), +(3013, 35, 103, 12, 254), +(3014, 35, 103, 12, 254), +(3008, 737, 104, 14, 254), +(3011, 305, 104, 17, 254), +(3012, 305, 104, 14, 254), +(3013, 305, 104, 13, 254), +(3014, 305, 104, 15, 254), +(3004, 261, 105, 35, 64), +(3004, 2517, 105, 65, 254), +(3006, 261, 105, 14, 49), +(3006, 2894, 105, 50, 53), +(3006, 2517, 105, 54, 254), +(3006, 3185, 105, 62, 254), +(3008, 718, 105, 31, 50), +(3008, 1750, 105, 51, 254), +(3010, 261, 105, 10, 50), +(3010, 2894, 105, 51, 254), +(3011, 457, 105, 41, 254), +(3011, 1391, 105, 45, 254), +(3012, 261, 105, 22, 49), +(3012, 2894, 105, 50, 254), +(3014, 261, 105, 15, 50), +(3014, 2894, 105, 51, 254), +(3015, 261, 105, 32, 254), +(3003, 1743, 106, 55, 254), +(3008, 714, 106, 41, 254), +(3008, 748, 106, 47, 57), +(3008, 1450, 106, 49, 254), +(3008, 1752, 106, 52, 254), +(3008, 1763, 106, 58, 72), +(3008, 11881, 106, 73, 77), +(3008, 11879, 106, 73, 77), +(3008, 11880, 106, 73, 77), +(3008, 14055, 106, 78, 82), +(3008, 14056, 106, 78, 82), +(3008, 14054, 106, 78, 82), +(3008, 18040, 106, 83, 87), +(3008, 18041, 106, 83, 87), +(3008, 18039, 106, 83, 87), +(3008, 26026, 106, 88, 92), +(3008, 26027, 106, 88, 92), +(3008, 26025, 106, 88, 92), +(3008, 29120, 106, 93, 97), +(3008, 29121, 106, 93, 97), +(3008, 29122, 106, 93, 97), +(3008, 35170, 106, 98, 254), +(3008, 35171, 106, 98, 254), +(3008, 35172, 106, 98, 254), +(3012, 2559, 106, 58, 254), +(3014, 481, 106, 13, 21), +(3014, 21, 106, 19, 21), +(3014, 482, 106, 22, 32), +(3014, 483, 106, 33, 39), +(3014, 648, 106, 38, 39), +(3014, 484, 106, 40, 57), +(3014, 176, 106, 47, 57), +(3014, 1689, 106, 52, 57), +(3014, 1713, 106, 58, 60), +(3014, 3343, 106, 61, 66), +(3014, 6739, 106, 61, 68), +(3014, 3351, 106, 63, 66), +(3014, 5504, 106, 67, 70), +(3014, 5514, 106, 69, 70), +(3014, 6671, 106, 69, 254), +(3014, 10598, 106, 71, 75), +(3014, 10596, 106, 71, 75), +(3014, 10597, 106, 71, 75), +(3014, 10643, 106, 74, 75), +(3014, 10641, 106, 74, 75), +(3014, 10642, 106, 74, 75), +(3014, 11887, 106, 74, 78), +(3014, 11885, 106, 74, 78), +(3014, 11886, 106, 74, 78), +(3014, 14507, 106, 76, 80), +(3014, 14508, 106, 76, 80), +(3014, 14506, 106, 76, 80), +(3014, 14543, 106, 79, 80), +(3014, 14544, 106, 79, 80), +(3014, 14542, 106, 79, 80), +(3014, 14582, 106, 79, 83), +(3014, 14583, 106, 79, 83), +(3014, 14581, 106, 79, 83), +(3014, 18564, 106, 81, 85), +(3014, 18565, 106, 81, 85), +(3014, 18566, 106, 81, 85), +(3014, 18600, 106, 84, 85), +(3014, 18601, 106, 84, 85), +(3014, 18602, 106, 84, 85), +(3014, 18641, 106, 84, 88), +(3014, 18639, 106, 84, 88), +(3014, 18640, 106, 84, 88), +(3014, 26901, 106, 86, 90), +(3014, 26899, 106, 86, 90), +(3014, 26900, 106, 86, 90), +(3014, 26999, 106, 89, 90), +(3014, 26997, 106, 89, 90), +(3014, 26998, 106, 89, 90), +(3014, 27025, 106, 89, 93), +(3014, 27026, 106, 89, 93), +(3014, 27024, 106, 89, 93), +(3014, 30028, 106, 91, 95), +(3014, 30029, 106, 91, 95), +(3014, 30027, 106, 91, 95), +(3014, 30132, 106, 93, 95), +(3014, 30133, 106, 93, 95), +(3014, 30131, 106, 93, 95), +(3014, 30140, 106, 94, 95), +(3014, 30141, 106, 94, 95), +(3014, 30142, 106, 94, 95), +(3014, 30167, 106, 94, 98), +(3014, 30168, 106, 94, 98), +(3014, 30169, 106, 94, 98), +(3014, 36091, 106, 96, 254), +(3014, 36089, 106, 96, 254), +(3014, 36090, 106, 96, 254), +(3014, 36189, 106, 98, 254), +(3014, 36187, 106, 98, 254), +(3014, 36188, 106, 98, 254), +(3014, 36216, 106, 99, 254), +(3014, 36196, 106, 99, 254), +(3014, 36217, 106, 99, 254), +(3014, 36194, 106, 99, 254), +(3014, 36215, 106, 99, 254), +(3014, 36195, 106, 99, 254), +(3004, 86, 107, 20, 254), +(3006, 86, 107, 6, 49), +(3006, 2881, 107, 50, 254), +(3008, 729, 107, 16, 254), +(3010, 86, 107, 12, 50), +(3010, 2881, 107, 51, 254), +(3011, 457, 107, 41, 254), +(3011, 1391, 107, 45, 254), +(3014, 86, 107, 12, 43), +(3014, 3696, 107, 44, 50), +(3014, 2881, 107, 51, 254), +(3015, 86, 107, 25, 254), +(3010, 345, 108, 15, 254), +(3010, 2522, 108, 16, 254), +(3015, 345, 108, 23, 254), +(3002, 235, 109, 11, 254), +(3002, 1726, 109, 51, 254), +(3002, 6125, 109, 66, 254), +(3002, 14348, 109, 77, 81), +(3002, 14346, 109, 77, 81), +(3002, 14347, 109, 77, 81), +(3002, 18369, 109, 82, 254), +(3002, 18367, 109, 82, 254), +(3002, 18368, 109, 82, 254), +(3003, 235, 109, 17, 254), +(3004, 247, 109, 14, 46), +(3004, 80, 109, 32, 64), +(3004, 34, 109, 47, 254), +(3004, 2517, 109, 65, 254), +(3005, 235, 109, 4, 254), +(3006, 247, 109, 4, 17), +(3006, 255, 109, 8, 254), +(3006, 80, 109, 13, 53), +(3006, 34, 109, 18, 254), +(3006, 2516, 109, 52, 254), +(3006, 4058, 109, 52, 254), +(3006, 2517, 109, 54, 254), +(3006, 3185, 109, 62, 254), +(3006, 6123, 109, 68, 254), +(3008, 719, 109, 19, 50), +(3008, 735, 109, 24, 254), +(3008, 1750, 109, 51, 254), +(3010, 79, 109, 7, 55), +(3010, 255, 109, 10, 254), +(3010, 42, 109, 27, 254), +(3010, 1575, 109, 56, 254), +(3010, 2886, 109, 58, 254), +(3011, 235, 109, 1, 254), +(3011, 457, 109, 41, 254), +(3011, 1391, 109, 45, 254), +(3011, 6124, 109, 68, 254), +(3012, 80, 109, 4, 39), +(3012, 42, 109, 16, 254), +(3012, 3811, 109, 40, 254), +(3012, 6120, 109, 67, 254), +(3012, 15513, 109, 76, 80), +(3012, 15511, 109, 76, 80), +(3012, 15512, 109, 76, 80), +(3012, 19701, 109, 81, 254), +(3012, 19699, 109, 81, 254), +(3012, 19700, 109, 81, 254), +(3013, 42, 109, 8, 254), +(3013, 80, 109, 16, 254), +(3014, 42, 109, 4, 254), +(3014, 80, 109, 6, 43), +(3014, 235, 109, 14, 254), +(3014, 3696, 109, 44, 254), +(3014, 6122, 109, 66, 254), +(3015, 79, 109, 29, 64), +(3015, 42, 109, 43, 254), +(3015, 1575, 109, 65, 254), +(3004, 278, 110, 28, 64), +(3004, 4054, 110, 41, 64), +(3004, 4055, 110, 49, 254), +(3004, 2517, 110, 65, 254), +(3006, 278, 110, 10, 53), +(3006, 424, 110, 26, 254), +(3006, 4054, 110, 30, 53), +(3006, 169, 110, 35, 61), +(3006, 4055, 110, 35, 254), +(3006, 3579, 110, 45, 254), +(3006, 4058, 110, 52, 254), +(3006, 1554, 110, 53, 254), +(3006, 2517, 110, 54, 254), +(3006, 3185, 110, 62, 254), +(3008, 717, 110, 5, 48), +(3008, 4395, 110, 25, 48), +(3008, 2605, 110, 49, 50), +(3008, 1750, 110, 51, 254), +(3010, 278, 110, 9, 254), +(3010, 424, 110, 22, 254), +(3010, 4054, 110, 29, 254), +(3010, 4055, 110, 34, 254), +(3010, 2524, 110, 36, 254), +(3010, 1554, 110, 52, 254), +(3015, 278, 110, 24, 254), +(3015, 4054, 110, 39, 254), +(3015, 4055, 110, 44, 254), +(3012, 1422, 111, 50, 254), +(3012, 1334, 111, 52, 254), +(3005, 2213, 112, 12, 34), +(3005, 3, 112, 35, 56), +(3005, 1773, 112, 57, 70), +(3005, 10042, 112, 71, 75), +(3005, 14823, 112, 76, 80), +(3005, 18928, 112, 81, 85), +(3005, 25555, 112, 86, 90), +(3005, 28632, 112, 91, 95), +(3005, 34662, 112, 96, 254), +(3011, 2213, 112, 12, 34), +(3011, 3, 112, 35, 56), +(3011, 1773, 112, 57, 70), +(3011, 10042, 112, 71, 75), +(3011, 14823, 112, 76, 80), +(3011, 18928, 112, 81, 85), +(3011, 25555, 112, 86, 90), +(3011, 28632, 112, 91, 95), +(3011, 34662, 112, 96, 254); )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/ruletypes.h b/common/ruletypes.h index 8096c037b2..32c8378fba 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -871,6 +871,10 @@ RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follo RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") +RULE_BOOL(Bots, AllowCommandedCharm, true, "If enabled bots can be commanded to charm NPCs.") +RULE_BOOL(Bots, AllowCommandedMez, true, "If enabled bots can be commanded to mez NPCs.") +RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded to resurrect players.") +RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_CATEGORY_END() diff --git a/common/spdat.cpp b/common/spdat.cpp index 7b0e540894..a691d51972 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2842,6 +2842,7 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { case BotSpellTypes::AEDoT: case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: + case BotSpellTypes::Lull: return true; case BotSpellTypes::InCombatBuff: if (cls == Class::ShadowKnight) { @@ -2885,6 +2886,18 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { case BotSpellTypes::PetResistBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return true; case BotSpellTypes::InCombatBuff: if (cls == Class::ShadowKnight) { @@ -2922,6 +2935,18 @@ bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return true; default: return false; @@ -2953,6 +2978,7 @@ bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { case BotSpellTypes::Stun: case BotSpellTypes::AEMez: case BotSpellTypes::Mez: + case BotSpellTypes::Lull: return true; default: return false; @@ -3234,3 +3260,34 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { return true; } + +bool IsCommandedSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Lull: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: + //case BotSpellTypes::Charm: + //case BotSpellTypes::Resurrect: + //case BotSpellTypes::Cure: + //case BotSpellTypes::GroupCures: + //case BotSpellTypes::DamageShields: + //case BotSpellTypes::PetDamageShields: + //case BotSpellTypes::ResistBuffs: + //case BotSpellTypes::PetResistBuffs: + return true; + default: + return false; + } + + return false; +} diff --git a/common/spdat.h b/common/spdat.h index 4c1b52ecaf..cefac6784d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -708,9 +708,26 @@ namespace BotSpellTypes constexpr uint16 ResistBuffs = 52; constexpr uint16 PetDamageShields = 53; constexpr uint16 PetResistBuffs = 54; + + // Command Spell Types + constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic + constexpr uint16 Lull = 101; + constexpr uint16 Succor = 102; + constexpr uint16 BindAffinity = 103; + constexpr uint16 Identify = 104; + constexpr uint16 Levitate = 105; + constexpr uint16 Rune = 106; + constexpr uint16 WaterBreathing = 107; + constexpr uint16 Size = 108; + constexpr uint16 Invisibility = 109; + constexpr uint16 MovementSpeed = 110; + constexpr uint16 SendHome = 111; + constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed + constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this + constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); @@ -730,6 +747,7 @@ bool IsClientBotSpellType(uint16 spellType); bool IsHealBotSpellType(uint16 spellType); bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); +bool IsCommandedSpellType(uint16 spellType); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only diff --git a/common/version.h b/common/version.h index 7cf293612d..d9d57e7ca1 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9284 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9049 //TODO update as needed +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9050 //TODO update as needed #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 988c0d4e3d..56736942ed 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9538,6 +9538,17 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: if ( !( spells[spell_id].target_type == ST_Target || @@ -10904,6 +10915,18 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return BotSpellTypes::Buff; case BotSpellTypes::AEMez: case BotSpellTypes::Mez: @@ -10945,6 +10968,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::Resurrect: + case BotSpellTypes::Lull: default: return spellType; } @@ -11007,6 +11031,84 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { return true; } + return false; + case BotSpellTypes::Lull: + if (!IsHarmonySpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Teleport: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + return true; + } + + return false; + case BotSpellTypes::Succor: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + return true; + } + + return false; + case BotSpellTypes::BindAffinity: + if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + return true; + } + + return false; + case BotSpellTypes::Identify: + if (IsEffectInSpell(spell_id, SE_Identify)) { + return true; + } + + return false; + case BotSpellTypes::Levitate: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + return true; + } + + return false; + case BotSpellTypes::Rune: + if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + return true; + } + + return false; + case BotSpellTypes::WaterBreathing: + if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { + return true; + } + + return false; + case BotSpellTypes::Size: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + return true; + } + + return false; + case BotSpellTypes::Invisibility: + if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::MovementSpeed: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + return true; + } + + return false; + case BotSpellTypes::SendHome: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + return true; + } + + return false; + case BotSpellTypes::SummonCorpse: + if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + return true; + } + return false; default: return true; @@ -11502,3 +11604,82 @@ bool Bot::BotPassiveCheck() { return false; } + +bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell_id) { + if (subType == UINT16_MAX) { + return true; + } + + switch (subType) { + case CommandedSubTypes::SingleTarget: + if (!IsAnyAESpell(spell_id) && !IsGroupSpell(spell_id)) { + return true; + } + + break; + case CommandedSubTypes::GroupTarget: + if (IsGroupSpell(spell_id)) { + return true; + } + + break; + case CommandedSubTypes::AETarget: + if (IsAnyAESpell(spell_id)) { + return true; + } + + break; + case CommandedSubTypes::SeeInvis: + if (IsEffectInSpell(spell_id, SE_SeeInvis)) { + return true; + } + + break; + case CommandedSubTypes::Invis: + if (IsEffectInSpell(spell_id, SE_Invisibility) || IsEffectInSpell(spell_id, SE_Invisibility2)) { + return true; + } + + break; + case CommandedSubTypes::InvisUndead: + if (IsEffectInSpell(spell_id, SE_InvisVsUndead) || IsEffectInSpell(spell_id, SE_InvisVsUndead2)) { + return true; + } + + break; + case CommandedSubTypes::InvisAnimals: + if (IsEffectInSpell(spell_id, SE_InvisVsAnimals) || IsEffectInSpell(spell_id, SE_ImprovedInvisAnimals)) { + return true; + } + + break; + case CommandedSubTypes::Shrink: + if ( + (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) < 100) || + (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) < 100) + ) { + return true; + } + + break; + case CommandedSubTypes::Grow: + if ( + (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) > 100) || + (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) > 100) + ) { + return true; + } + + break; + case CommandedSubTypes::Selo: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed) && IsBardSong(spell_id)) { + return true; + } + + break; + default: + break; + } + + return false; +} diff --git a/zone/bot.h b/zone/bot.h index 8e85448e08..2360d73b4d 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -147,6 +147,19 @@ namespace BotBaseSettings { constexpr uint16 END = BotBaseSettings::ManaWhenToMed; // Increment as needed }; +namespace CommandedSubTypes { + constexpr uint16 SingleTarget = 1; + constexpr uint16 GroupTarget = 2; + constexpr uint16 AETarget = 3; + constexpr uint16 SeeInvis = 4; + constexpr uint16 Invis = 5; + constexpr uint16 InvisUndead = 6; + constexpr uint16 InvisAnimals = 7; + constexpr uint16 Shrink = 8; + constexpr uint16 Grow = 9; + constexpr uint16 Selo = 10; +}; + class Bot : public NPC { friend class Mob; public: @@ -387,7 +400,7 @@ class Bot : public NPC { void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); // AI Methods - bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType); + bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); bool AttemptAICastSpell(uint16 spellType); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; @@ -523,6 +536,7 @@ class Bot : public NPC { bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spellType); + bool IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell_id); bool HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar); @@ -577,7 +591,7 @@ class Bot : public NPC { static std::list GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect); static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType); static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); - static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false); + static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 15ea7eba8a..7af4109f5b 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1251,7 +1251,6 @@ int bot_command_init(void) bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) || bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) || bot_command_add("behindmob", "Toggles whether or not your bot tries to stay behind a mob", AccountStatus::Player, bot_command_behind_mob) || - bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", AccountStatus::Player, bot_command_bind_affinity) || bot_command_add("bot", "Lists the available bot management [subcommands]", AccountStatus::Player, bot_command_bot) || bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", AccountStatus::Player, bot_command_appearance) || bot_command_add("botbeardcolor", "Changes the beard color of a bot", AccountStatus::Player, bot_command_beard_color) || @@ -1286,16 +1285,13 @@ int bot_command_init(void) bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_command_woad) || bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || bot_command_add("distanceranged", "Controls the range casters and ranged will try to stay away from a mob", AccountStatus::Player, bot_command_distance_ranged) || - bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || - bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) || bot_command_add("defaultsettings", "Restores a bot back to default settings", AccountStatus::Player, bot_command_default_settings) || bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || - bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || + bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || //TODO bot rewrite - deleteme bot_command_add("enforcespellsettings", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || - bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", AccountStatus::Player, bot_command_escape) || bot_command_add("findaliases", "Find available aliases for a bot command", AccountStatus::Player, bot_command_find_aliases) || bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", AccountStatus::Player, bot_command_follow) || bot_command_add("guard", "Orders bots to guard their current positions", AccountStatus::Player, bot_command_guard) || @@ -1322,20 +1318,15 @@ int bot_command_init(void) bot_command_add("healrotationstop", "Stops a heal rotation", AccountStatus::Player, bot_command_heal_rotation_stop) || bot_command_add("help", "List available commands and their description - specify partial command as argument to search", AccountStatus::Player, bot_command_help) || bot_command_add("hold", "Prevents a bot from attacking until released", AccountStatus::Player, bot_command_hold) || - bot_command_add("identify", "Orders a bot to cast an item identification spell", AccountStatus::Player, bot_command_identify) || bot_command_add("illusionblock", "Control whether or not illusion effects will land on the bot if casted by another player or bot", AccountStatus::Player, bot_command_illusion_block) || bot_command_add("inventory", "Lists the available bot inventory [subcommands]", AccountStatus::Player, bot_command_inventory) || bot_command_add("inventorygive", "Gives the item on your cursor to a bot", AccountStatus::Player, bot_command_inventory_give) || bot_command_add("inventorylist", "Lists all items in a bot's inventory", AccountStatus::Player, bot_command_inventory_list) || bot_command_add("inventoryremove", "Removes an item from a bot's inventory", AccountStatus::Player, bot_command_inventory_remove) || bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", AccountStatus::Player, bot_command_inventory_window) || - bot_command_add("invisibility", "Orders a bot to cast a cloak of invisibility, or allow them to be seen", AccountStatus::Player, bot_command_invisibility) || bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", AccountStatus::Player, bot_command_item_use) || - bot_command_add("levitation", "Orders a bot to cast a levitation spell", AccountStatus::Player, bot_command_levitation) || - bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || + bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || //TODO bot rewrite - IMPLEMENT bot_command_add("maxmeleerange", "Toggles whether your bot is at max melee range or not. This will disable all special abilities, including taunt.", AccountStatus::Player, bot_command_max_melee_range) || - bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", AccountStatus::Player, bot_command_mesmerize) || - bot_command_add("movementspeed", "Orders a bot to cast a movement speed enhancement spell", AccountStatus::Player, bot_command_movement_speed) || bot_command_add("owneroption", "Sets options available to bot owners", AccountStatus::Player, bot_command_owner_option) || bot_command_add("pet", "Lists the available bot pet [subcommands]", AccountStatus::Player, bot_command_pet) || bot_command_add("petgetlost", "Orders a bot to remove its summoned pet", AccountStatus::Player, bot_command_pet_get_lost) || @@ -1346,14 +1337,9 @@ int bot_command_init(void) bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || - bot_command_add("resistance", "Orders a bot to cast a specified resistance buff", AccountStatus::Player, bot_command_resistance) || - bot_command_add("resurrect", "Orders a bot to resurrect a player's (players') corpse(s)", AccountStatus::Player, bot_command_resurrect) || - 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("sithppercent", "HP threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_hp_percent) || bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || - bot_command_add("size", "Orders a bot to change a player's size", AccountStatus::Player, bot_command_size) || bot_command_add("spellaggrochecks", "Toggles whether or not bots will cast a spell type if they think it will get them aggro", AccountStatus::Player, bot_command_spell_aggro_checks) || bot_command_add("spellengagedpriority", "Controls the order of casts by spell type when engaged in combat", AccountStatus::Player, bot_command_spell_engaged_priority) || bot_command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, bot_command_spell_delays) || @@ -1374,15 +1360,14 @@ int bot_command_init(void) bot_command_add("spellsettingsdelete", "Delete a bot spell setting entry", AccountStatus::Player, bot_command_spell_settings_delete) || bot_command_add("spellsettingstoggle", "Toggle a bot spell use", AccountStatus::Player, bot_command_spell_settings_toggle) || bot_command_add("spellsettingsupdate", "Update a bot spell setting entry", 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("spelltypeids", "Lists spelltypes by ID", AccountStatus::Player, bot_command_spelltype_ids) || bot_command_add("spelltypenames", "Lists spelltypes by shortname", AccountStatus::Player, bot_command_spelltype_names) || + bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) || //TODO bot rewrite - IMPLEMENT 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) || bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || bot_command_add("track", "Orders a capable bot to track enemies", AccountStatus::Player, bot_command_track) || - bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) || - bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing) + bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) ) { bot_command_deinit(); return -1; @@ -2266,36 +2251,27 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/apply_potion.cpp" #include "bot_commands/attack.cpp" #include "bot_commands/behind_mob.cpp" -#include "bot_commands/bind_affinity.cpp" #include "bot_commands/bot.cpp" #include "bot_commands/bot_settings.cpp" #include "bot_commands/cast.cpp" -#include "bot_commands/charm.cpp" #include "bot_commands/class_race_list.cpp" #include "bot_commands/click_item.cpp" #include "bot_commands/copy_settings.cpp" -#include "bot_commands/cure.cpp" #include "bot_commands/default_settings.cpp" #include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" #include "bot_commands/distance_ranged.cpp" -#include "bot_commands/escape.cpp" #include "bot_commands/find_aliases.cpp" #include "bot_commands/follow.cpp" #include "bot_commands/guard.cpp" #include "bot_commands/heal_rotation.cpp" #include "bot_commands/help.cpp" #include "bot_commands/hold.cpp" -#include "bot_commands/identify.cpp" #include "bot_commands/illusion_block.cpp" #include "bot_commands/inventory.cpp" -#include "bot_commands/invisibility.cpp" #include "bot_commands/item_use.cpp" -#include "bot_commands/levitation.cpp" #include "bot_commands/lull.cpp" #include "bot_commands/max_melee_range.cpp" -#include "bot_commands/mesmerize.cpp" -#include "bot_commands/movement_speed.cpp" #include "bot_commands/name.cpp" #include "bot_commands/owner_option.cpp" #include "bot_commands/pet.cpp" @@ -2304,14 +2280,9 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/precombat.cpp" #include "bot_commands/pull.cpp" #include "bot_commands/release.cpp" -#include "bot_commands/resistance.cpp" -#include "bot_commands/resurrect.cpp" -#include "bot_commands/rune.cpp" -#include "bot_commands/send_home.cpp" #include "bot_commands/sit_hp_percent.cpp" #include "bot_commands/sit_in_combat.cpp" #include "bot_commands/sit_mana_percent.cpp" -#include "bot_commands/size.cpp" #include "bot_commands/spell.cpp" #include "bot_commands/spell_aggro_checks.cpp" #include "bot_commands/spell_delays.cpp" @@ -2334,4 +2305,3 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/timer.cpp" #include "bot_commands/track.cpp" #include "bot_commands/view_combos.cpp" -#include "bot_commands/water_breathing.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 8e200bd6b3..56be996809 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1669,36 +1669,27 @@ void bot_command_apply_poison(Client *c, const Seperator *sep); void bot_command_apply_potion(Client* c, const Seperator* sep); void bot_command_attack(Client *c, const Seperator *sep); void bot_command_behind_mob(Client* c, const Seperator* sep); -void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep); void bot_command_bot_settings(Client* c, const Seperator* sep); void bot_command_cast(Client* c, const Seperator* sep); void bot_command_distance_ranged(Client* c, const Seperator* sep); -void bot_command_charm(Client *c, const Seperator *sep); void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); void bot_command_copy_settings(Client* c, const Seperator* sep); -void bot_command_cure(Client *c, const Seperator *sep); void bot_command_default_settings(Client* c, const Seperator* sep); void bot_command_defensive(Client *c, const Seperator *sep); void bot_command_depart(Client *c, const Seperator *sep); -void bot_command_escape(Client *c, const Seperator *sep); void bot_command_find_aliases(Client *c, const Seperator *sep); void bot_command_follow(Client *c, const Seperator *sep); void bot_command_guard(Client *c, const Seperator *sep); void bot_command_heal_rotation(Client *c, const Seperator *sep); void bot_command_help(Client *c, const Seperator *sep); void bot_command_hold(Client *c, const Seperator *sep); -void bot_command_identify(Client *c, const Seperator *sep); void bot_command_illusion_block(Client* c, const Seperator* sep); void bot_command_inventory(Client *c, const Seperator *sep); -void bot_command_invisibility(Client *c, const Seperator *sep); void bot_command_item_use(Client *c, const Seperator *sep); -void bot_command_levitation(Client *c, const Seperator *sep); void bot_command_lull(Client *c, const Seperator *sep); void bot_command_max_melee_range(Client* c, const Seperator* sep); -void bot_command_mesmerize(Client *c, const Seperator *sep); -void bot_command_movement_speed(Client *c, const Seperator *sep); void bot_command_owner_option(Client *c, const Seperator *sep); void bot_command_pet(Client *c, const Seperator *sep); void bot_command_pick_lock(Client *c, const Seperator *sep); @@ -1706,14 +1697,9 @@ void bot_command_pickpocket(Client* c, const Seperator* sep); void bot_command_precombat(Client* c, const Seperator* sep); void bot_command_pull(Client *c, const Seperator *sep); void bot_command_release(Client *c, const Seperator *sep); -void bot_command_resistance(Client *c, const Seperator *sep); -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_sit_hp_percent(Client* c, const Seperator* sep); void bot_command_sit_in_combat(Client* c, const Seperator* sep); void bot_command_sit_mana_percent(Client* c, const Seperator* sep); -void bot_command_size(Client *c, const Seperator *sep); void bot_command_spell_aggro_checks(Client* c, const Seperator* sep); void bot_command_spell_delays(Client* c, const Seperator* sep); void bot_command_spell_engaged_priority(Client* c, const Seperator* sep); @@ -1743,7 +1729,6 @@ void bot_command_taunt(Client *c, const Seperator *sep); void bot_command_timer(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); // Bot Subcommands void bot_command_appearance(Client *c, const Seperator *sep); diff --git a/zone/bot_commands/bind_affinity.cpp b/zone/bot_commands/bind_affinity.cpp deleted file mode 100644 index a59c5947ae..0000000000 --- a/zone/bot_commands/bind_affinity.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "../bot_command.h" - -void bot_command_bind_affinity(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_BindAffinity]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_BindAffinity) || helper_command_alias_fail(c, "bot_command_bind_affinity", sep->arg[0], "bindaffinity")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - c->Message(Chat::White, "note: Orders a bot to attempt an affinity binding", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_BindAffinity); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - // Cast effect message is not being generated - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id)) - c->Message(Chat::White, "Successfully bound %s to this location", target_mob->GetCleanName()); - else - c->Message(Chat::White, "Failed to bind %s to this location", target_mob->GetCleanName()); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index ab02eb950e..49112b8693 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -99,8 +99,8 @@ void bot_command_cast(Client* c, const Seperator* sep) c->Message( Chat::Yellow, fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") + "Use help after any command type for more subtypes to use, for example: {}.", + Saylink::Silent("^cast invisibility help") ).c_str() ); @@ -118,9 +118,58 @@ void bot_command_cast(Client* c, const Seperator* sep) } std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + + //Commanded type help prompts + if (!arg2.compare("help")) { + c->Message(Chat::Yellow, "You can also use [single], [group], [ae]. Ex: ^cast movementspeed group.", sep->arg[0]); + } + + if (!arg1.compare("invisibility") && !arg2.compare("help")) { + c->Message( + Chat::Yellow, + fmt::format( + "Available options for {} are: {}, {}, {}, {}.", + sep->arg[0], + Saylink::Silent("^cast invisibility see", "see"), + Saylink::Silent("^cast invisibility invis", "invis"), + Saylink::Silent("^cast invisibility undead", "undead"), + Saylink::Silent("^cast invisibility animals", "animals") + ).c_str() + ); - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + if (!arg1.compare("size") && !arg2.compare("help")) { + c->Message( + Chat::Yellow, + fmt::format( + "Available options for {} are: {}, {}.", + sep->arg[0], + Saylink::Silent("^cast size grow", "grow"), + Saylink::Silent("^cast size shrink", "shrink") + ).c_str() + ); + + return; + } + + if (!arg1.compare("movementspeed") && !arg2.compare("help")) { + c->Message( + Chat::Yellow, + fmt::format( + "Available options for {} are: {}, {}.", + sep->arg[0], + Saylink::Silent("^cast movementspeed selo"), "selo" + ).c_str() + ); + + return; + } + + if (!arg2.compare("help")) { + c->Message(Chat::Yellow, "There are no additional options for {}.", sep->arg[0]); return; } @@ -131,8 +180,16 @@ void bot_command_cast(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spellType = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); return; } @@ -156,6 +213,88 @@ void bot_command_cast(Client* c, const Seperator* sep) } } + switch (spellType) { //Allowed command checks + case BotSpellTypes::Charm: + if (!RuleB(Bots, AllowCommandedCharm)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + if (!RuleB(Bots, AllowCommandedMez)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + case BotSpellTypes::Resurrect: + if (!RuleB(Bots, AllowCommandedResurrect)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + case BotSpellTypes::SummonCorpse: + if (!RuleB(Bots, AllowCommandedSummonCorpse)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + default: + break; + } + + std::string argString = sep->arg[ab_arg]; + uint16 subType = UINT16_MAX; + uint16 subTargetType = UINT16_MAX; + + if (!argString.compare("shrink")) { + subType = CommandedSubTypes::Shrink; + ++ab_arg; + } + else if (!argString.compare("grow")) { + subType = CommandedSubTypes::Grow; + ++ab_arg; + } + else if (!argString.compare("see")) { + subType = CommandedSubTypes::SeeInvis; + ++ab_arg; + } + else if (!argString.compare("invis")) { + subType = CommandedSubTypes::Invis; + ++ab_arg; + } + else if (!argString.compare("undead")) { + subType = CommandedSubTypes::InvisUndead; + ++ab_arg; + } + else if (!argString.compare("animals")) { + subType = CommandedSubTypes::InvisAnimals; + ++ab_arg; + } + else if (!argString.compare("selo")) { + subType = CommandedSubTypes::Selo; + ++ab_arg; + } + + argString = sep->arg[ab_arg]; + + if (!argString.compare("single")) { + subTargetType = CommandedSubTypes::SingleTarget; + ++ab_arg; + } + else if (!argString.compare("group")) { + subTargetType = CommandedSubTypes::GroupTarget; + ++ab_arg; + } + else if (!argString.compare("ae")) { + subTargetType = CommandedSubTypes::AETarget; + ++ab_arg; + } + if ( spellType == BotSpellTypes::PetBuffs || spellType == BotSpellTypes::PetCompleteHeals || @@ -169,47 +308,63 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* tar = c->GetTarget(); - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme - if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { - if (!tar) { + LogTestDebug("{}: 'Attempting {} [{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + + if (!tar) { + if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { c->Message(Chat::Yellow, "You need a target for that."); return; } + } - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { - c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); - return; - } + switch (spellType) { //Target Checks + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + c->Message(Chat::Yellow, "[%s] is not a player's corpse.", tar->GetCleanName()); - if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { - if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { - c->Message(Chat::Yellow, "[%s] is an invalid target.", tar->GetCleanName()); - return; - } - } - } - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme - switch (spellType) { - case BotSpellTypes::Stun: - case BotSpellTypes::AEStun: - if (tar->GetSpecialAbility(SpecialAbility::StunImmunity)) { - c->Message(Chat::Yellow, "[%s] is immune to stuns.", tar->GetCleanName()); return; } - + break; - case BotSpellTypes::Resurrect: - if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { - c->Message(Chat::Yellow, "[%s] is an invalid target. I can only resurrect player corpses.", tar->GetCleanName()); + case BotSpellTypes::Identify: + case BotSpellTypes::SendHome: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::SummonCorpse: + if (!tar->IsClient() || !c->IsInGroupOrRaid(tar)) { + c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + return; } break; default: + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { + c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); + + return; + } + + if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { + if ( + (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) || + ((tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner()))) + ) { + c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + + return; + } + } + break; } const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; @@ -219,7 +374,7 @@ void bot_command_cast(Client* c, const Seperator* sep) std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -240,7 +395,8 @@ void bot_command_cast(Client* c, const Seperator* sep) /* TODO bot rewrite - - FIX: Snares, Group Cures, OOC Song, Precombat, HateRedux, Fear/AE Fear + FIX: Depart, SummonCorpse, Lull, + Group Cures, Precombat, Fear/AE Fear ICB (SK) casting hate on friendly but not hostile? NEED TO CHECK: precombat, AE Dispel, AE Lifetap DO I NEED A PBAE CHECK??? @@ -250,7 +406,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* newTar = tar; - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { newTar = bot_iter; } @@ -279,11 +435,11 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); - if (bot_iter->AICastSpell(newTar, 100, spellType)) { + if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { if (!firstFound) { firstFound = bot_iter; } diff --git a/zone/bot_commands/charm.cpp b/zone/bot_commands/charm.cpp deleted file mode 100644 index 2df4694b66..0000000000 --- a/zone/bot_commands/charm.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "../bot_command.h" - -void bot_command_charm(Client *c, const Seperator *sep) -{ - auto local_list = &bot_command_spells[BCEnum::SpT_Charm]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Charm) || helper_command_alias_fail(c, "bot_command_charm", sep->arg[0], "charm")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: dire])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Charm); - return; - } - - bool dire = false; - std::string dire_arg = sep->arg[1]; - if (!dire_arg.compare("dire")) - dire = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToCharm(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->dire != dire) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY); - if (!target_mob) - continue; - if (target_mob->IsCharmed()) { - c->Message(Chat::White, "Your is already charmed"); - return; - } - - if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob, true); - if (!my_bot) - continue; - - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/cure.cpp b/zone/bot_commands/cure.cpp deleted file mode 100644 index 4452f8088d..0000000000 --- a/zone/bot_commands/cure.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "../bot_command.h" - -void bot_command_cure(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Cure]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Cure) || helper_command_alias_fail(c, "bot_command_cure", sep->arg[0], "cure")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [ailment: blindness | disease | poison | curse | corruption]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Cure); - return; - } - - std::string ailment_arg = sep->arg[1]; - - auto ailment_type = BCEnum::AT_None; - if (!ailment_arg.compare("blindness")) - ailment_type = BCEnum::AT_Blindness; - else if (!ailment_arg.compare("disease")) - ailment_type = BCEnum::AT_Disease; - else if (!ailment_arg.compare("poison")) - ailment_type = BCEnum::AT_Poison; - else if (!ailment_arg.compare("curse")) - ailment_type = BCEnum::AT_Curse; - else if (!ailment_arg.compare("corruption")) - ailment_type = BCEnum::AT_Corruption; - - if (ailment_type == BCEnum::AT_None) { - c->Message(Chat::White, "You must specify a cure [ailment] to use this command"); - return; - } - - local_list->sort([ailment_type](STBaseEntry* l, STBaseEntry* r) { - auto _l = l->SafeCastToCure(), _r = r->SafeCastToCure(); - if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] < _r->cure_value[AILMENTIDTOINDEX(ailment_type)]) - return true; - if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] == _r->cure_value[AILMENTIDTOINDEX(ailment_type)] && spells[_l->spell_id].mana < spells[_r->spell_id].mana) - return true; - if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] == _r->cure_value[AILMENTIDTOINDEX(ailment_type)] && spells[_l->spell_id].mana == spells[_r->spell_id].mana && _l->cure_total < _r->cure_total) - return true; - - return false; - }); - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToCure(); - if (helper_spell_check_fail(local_entry)) - continue; - if (!local_entry->cure_value[AILMENTIDTOINDEX(ailment_type)]) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/escape.cpp b/zone/bot_commands/escape.cpp deleted file mode 100644 index 31fde82a6b..0000000000 --- a/zone/bot_commands/escape.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "../bot_command.h" - -void bot_command_escape(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Escape]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Escape) || helper_command_alias_fail(c, "bot_command_escape", sep->arg[0], "escape")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: lesser])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Escape); - return; - } - - bool use_lesser = false; - if (!strcasecmp(sep->arg[1], "lesser")) - use_lesser = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToEscape(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->lesser != use_lesser) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/identify.cpp b/zone/bot_commands/identify.cpp deleted file mode 100644 index 7ac90a4e4e..0000000000 --- a/zone/bot_commands/identify.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_identify(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Identify]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Identify) || helper_command_alias_fail(c, "bot_command_identify", sep->arg[0], "identify")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Identify); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/invisibility.cpp b/zone/bot_commands/invisibility.cpp deleted file mode 100644 index dd826ebc8a..0000000000 --- a/zone/bot_commands/invisibility.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "../bot_command.h" - -void bot_command_invisibility(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Invisibility]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Invisibility) || helper_command_alias_fail(c, "bot_command_invisibility", sep->arg[0], "invisibility")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [invisibility: living | undead | animal | see]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Invisibility); - return; - } - - std::string invisibility = sep->arg[1]; - - BCEnum::IType invisibility_type = BCEnum::IT_None; - if (!invisibility.compare("living")) - invisibility_type = BCEnum::IT_Living; - else if (!invisibility.compare("undead")) - invisibility_type = BCEnum::IT_Undead; - else if (!invisibility.compare("animal")) - invisibility_type = BCEnum::IT_Animal; - else if (!invisibility.compare("see")) - invisibility_type = BCEnum::IT_See; - - if (invisibility_type == BCEnum::IT_None) { - c->Message(Chat::White, "You must specify an [invisibility]"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToInvisibility(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->invis_type != invisibility_type) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/levitation.cpp b/zone/bot_commands/levitation.cpp deleted file mode 100644 index d4b5cee28e..0000000000 --- a/zone/bot_commands/levitation.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_levitation(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Levitation]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Levitation) || helper_command_alias_fail(c, "bot_command_levitation", sep->arg[0], "levitation")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Levitation); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/mesmerize.cpp b/zone/bot_commands/mesmerize.cpp deleted file mode 100644 index d86fba9f71..0000000000 --- a/zone/bot_commands/mesmerize.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "../bot_command.h" - -void bot_command_mesmerize(Client *c, const Seperator *sep) -{ - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Mesmerize); - return; - } - - bool isSuccess = false; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto bot_iter : sbl) { - std::list botSpellList = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Mez, c->GetTarget(), IsAEBotSpellType(BotSpellTypes::Mez)); - - for (const auto& s : botSpellList) { - if (!IsValidSpell(s.SpellId)) { - continue; - } - - if (!bot_iter->IsInGroupOrRaid(c)) { - continue; - } - - if (!bot_iter->CastChecks(s.SpellId, c->GetTarget(), BotSpellTypes::Mez, false, false)) { - continue; - } - - if (bot_iter->CommandedDoSpellCast(s.SpellIndex, c->GetTarget(), s.ManaCost)) { - bot_iter->BotGroupSay(bot_iter, "Casting %s [%s] on %s.", GetSpellName(s.SpellId), bot_iter->GetSpellTypeNameByID(BotSpellTypes::Mez), c->GetTarget()->GetCleanName()); - isSuccess = true; - } - } - } - - if (!isSuccess) { - helper_no_available_bots(c); - } -} diff --git a/zone/bot_commands/movement_speed.cpp b/zone/bot_commands/movement_speed.cpp deleted file mode 100644 index d1b8841546..0000000000 --- a/zone/bot_commands/movement_speed.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "../bot_command.h" - -void bot_command_movement_speed(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_MovementSpeed]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_MovementSpeed) || helper_command_alias_fail(c, "bot_command_movement_speed", sep->arg[0], "movementspeed")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([group | sow])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_MovementSpeed); - return; - } - - bool group = false; - bool sow = false; - std::string arg1 = sep->arg[1]; - if (!arg1.compare("group")) - group = true; - else if (!arg1.compare("sow")) - sow = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToMovementSpeed(); - if (helper_spell_check_fail(local_entry)) - continue; - if (!sow && (local_entry->group != group)) - continue; - if (sow && (local_entry->spell_id != 278)) // '278' = single-target "Spirit of Wolf" - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/resistance.cpp b/zone/bot_commands/resistance.cpp deleted file mode 100644 index 0b84b4b88a..0000000000 --- a/zone/bot_commands/resistance.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "../bot_command.h" - -void bot_command_resistance(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resistance]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resistance) || helper_command_alias_fail(c, "bot_command_resistance", sep->arg[0], "resistance")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [resistance: fire | cold | poison | disease | magic | corruption]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Resistance); - return; - } - - std::string resistance_arg = sep->arg[1]; - - auto resistance_type = BCEnum::RT_None; - if (!resistance_arg.compare("fire")) - resistance_type = BCEnum::RT_Fire; - else if (!resistance_arg.compare("cold")) - resistance_type = BCEnum::RT_Cold; - else if (!resistance_arg.compare("poison")) - resistance_type = BCEnum::RT_Poison; - else if (!resistance_arg.compare("disease")) - resistance_type = BCEnum::RT_Disease; - else if (!resistance_arg.compare("magic")) - resistance_type = BCEnum::RT_Magic; - else if (!resistance_arg.compare("corruption")) - resistance_type = BCEnum::RT_Corruption; - - if (resistance_type == BCEnum::RT_None) { - c->Message(Chat::White, "You must specify a [resistance]"); - return; - } - - local_list->sort([resistance_type](STBaseEntry* l, STBaseEntry* r) { - auto _l = l->SafeCastToResistance(), _r = r->SafeCastToResistance(); - if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] > _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)]) - return true; - if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana < spells[_r->spell_id].mana) - return true; - if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana == spells[_r->spell_id].mana && _l->resist_total > _r->resist_total) - return true; - - return false; - }); - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToResistance(); - if (helper_spell_check_fail(local_entry)) - continue; - if (!local_entry->resist_value[RESISTANCEIDTOINDEX(resistance_type)]) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/resurrect.cpp b/zone/bot_commands/resurrect.cpp deleted file mode 100644 index 0b15ef3e11..0000000000 --- a/zone/bot_commands/resurrect.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "../bot_command.h" - -void bot_command_resurrect(Client *c, const Seperator *sep) -{ - // Obscure bot spell code prohibits the aoe portion from working correctly... - - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resurrect]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resurrect) || helper_command_alias_fail(c, "bot_command_resurrect", sep->arg[0], "resurrect")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - //c->Message(Chat::White, "usage: %s ([option: aoe])", sep->arg[0]); - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Resurrect); - return; - } - - bool aoe = false; - //std::string aoe_arg = sep->arg[1]; - //if (!aoe_arg.compare("aoe")) - // aoe = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToResurrect(); - if (helper_spell_check_fail(local_entry)) - continue; - //if (local_entry->aoe != aoe) - // continue; - if (local_entry->aoe) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - //if (!target_mob && !local_entry->aoe) - // continue; - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - //if (local_entry->aoe) - // target_mob = my_bot; - - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/rune.cpp b/zone/bot_commands/rune.cpp deleted file mode 100644 index 71b1cf572a..0000000000 --- a/zone/bot_commands/rune.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_rune(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Rune]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Rune) || helper_command_alias_fail(c, "bot_command_rune", sep->arg[0], "rune")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Rune); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/send_home.cpp b/zone/bot_commands/send_home.cpp deleted file mode 100644 index 6950e2bee9..0000000000 --- a/zone/bot_commands/send_home.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bot_command.h" - -void bot_command_send_home(Client *c, const Seperator *sep) -{ - // Obscure bot spell code prohibits the aoe portion from working correctly... - - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_SendHome]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_SendHome) || helper_command_alias_fail(c, "bot_command_send_home", sep->arg[0], "sendhome")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: group])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_SendHome); - return; - } - - bool group = false; - std::string group_arg = sep->arg[1]; - if (!group_arg.compare("group")) - group = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToSendHome(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->group != group) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/size.cpp b/zone/bot_commands/size.cpp deleted file mode 100644 index 69e2fd1a2f..0000000000 --- a/zone/bot_commands/size.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "../bot_command.h" - -void bot_command_size(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Size]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Size) || helper_command_alias_fail(c, "bot_command_size", sep->arg[0], "size")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [grow | shrink]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Size); - return; - } - - std::string size_arg = sep->arg[1]; - auto size_type = BCEnum::SzT_Reduce; - if (!size_arg.compare("grow")) { - size_type = BCEnum::SzT_Enlarge; - } - else if (size_arg.compare("shrink")) { - c->Message(Chat::White, "This command requires a [grow | shrink] argument"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToSize(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->size_type != size_type) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/water_breathing.cpp b/zone/bot_commands/water_breathing.cpp deleted file mode 100644 index cb9b792c47..0000000000 --- a/zone/bot_commands/water_breathing.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_water_breathing(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_WaterBreathing]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_WaterBreathing) || helper_command_alias_fail(c, "bot_command_water_breathing", sep->arg[0], "waterbreathing")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_WaterBreathing); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 0138d58ad7..241854dfa5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -21,7 +21,7 @@ #include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/npc_spells_repository.h" -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType, uint16 subType) { if (!tar) { return false; } @@ -48,7 +48,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { return false; } - if (spellType != BotSpellTypes::Resurrect && tar->GetAppearance() == eaDead) { + if ((spellType != BotSpellTypes::Resurrect && spellType != BotSpellTypes::SummonCorpse) && tar->GetAppearance() == eaDead) { if (!((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot())) { return false; } @@ -83,7 +83,12 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { } break; - //SpecialAbility::PacifyImmunity -- TODO bot rewrite + case BotSpellTypes::Lull: + if (tar->GetSpecialAbility(SpecialAbility::PacifyImmunity)) { + return false; + } + + break; case BotSpellTypes::Fear: if (tar->GetSpecialAbility(SpecialAbility::FearImmunity)) { return false; @@ -128,6 +133,17 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { return false; } @@ -182,6 +198,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { return BotCastPet(tar, botClass, botSpell, spellType); case BotSpellTypes::Resurrect: + case BotSpellTypes::SummonCorpse: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { return false; } @@ -197,7 +214,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { break; } - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType), subTargetType, subType); for (const auto& s : botSpellList) { @@ -643,7 +660,7 @@ bool Bot::AI_PursueCastCheck() { continue; } - if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -706,7 +723,7 @@ bool Bot::AI_IdleCastCheck() { continue; } - if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -756,7 +773,7 @@ bool Bot::AI_EngagedCastCheck() { continue; } - if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -979,7 +996,7 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp return result; } -std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE) { +std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE, uint16 subTargetType, uint16 subType) { std::list result; if (botCaster && botCaster->AI_HasSpells()) { @@ -1001,6 +1018,16 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) ) { + if ( + botCaster->IsCommandedSpell() && + ( + !botCaster->IsValidSpellTypeSubType(spellType, subTargetType, botSpellList[i].spellid) || + !botCaster->IsValidSpellTypeSubType(spellType, subType, botSpellList[i].spellid) + ) + ) { + continue; + } + if (!AE && IsAnyAESpell(botSpellList[i].spellid) && !IsGroupSpell(botSpellList[i].spellid)) { continue; } @@ -2040,7 +2067,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) return result; } -uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adjust, move AEs to own rule? +uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: @@ -2072,6 +2099,18 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::PetResistBuffs: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return RuleI(Bots, PercentChanceToCastBuff); case BotSpellTypes::Escape: return RuleI(Bots, PercentChanceToCastEscape); @@ -2949,6 +2988,7 @@ void Bot::CheckBotSpells() { break; } break; + //TODO bot rewrite - add commanded types default: break; @@ -3044,6 +3084,7 @@ void Bot::CheckBotSpells() { else if (IsEffectInSpell(spell_id, SE_Revive)) { correctType = BotSpellTypes::Resurrect; } + //TODO bot rewrite - add commanded types if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" diff --git a/zone/mob.cpp b/zone/mob.cpp index ef6659bed8..4c16356551 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8720,6 +8720,12 @@ uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { } } + for (int i = BotSpellTypes::COMMANDED_START; i <= BotSpellTypes::COMMANDED_END; ++i) { + if (!Strings::ToLower(spellTypeString).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + return UINT16_MAX; } @@ -8892,6 +8898,45 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "Pet Resist Buff"; break; + case BotSpellTypes::Lull: + spellTypeName = "Lull"; + break; + case BotSpellTypes::Teleport: + spellTypeName = "Teleport"; + break; + case BotSpellTypes::Succor: + spellTypeName = "Succor"; + break; + case BotSpellTypes::BindAffinity: + spellTypeName = "Bind Affinity"; + break; + case BotSpellTypes::Identify: + spellTypeName = "Identify"; + break; + case BotSpellTypes::Levitate: + spellTypeName = "Levitate"; + break; + case BotSpellTypes::Rune: + spellTypeName = "Rune"; + break; + case BotSpellTypes::WaterBreathing: + spellTypeName = "Water Breathing"; + break; + case BotSpellTypes::Size: + spellTypeName = "Size"; + break; + case BotSpellTypes::Invisibility: + spellTypeName = "Invisibility"; + break; + case BotSpellTypes::MovementSpeed: + spellTypeName = "Movement Speed"; + break; + case BotSpellTypes::SendHome: + spellTypeName = "Send Home"; + break; + case BotSpellTypes::SummonCorpse: + spellTypeName = "Summon Corpse"; + break; default: break; } @@ -9068,6 +9113,45 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "petresistbuffs"; break; + case BotSpellTypes::Lull: + spellTypeName = "lull"; + break; + case BotSpellTypes::Teleport: + spellTypeName = "teleport"; + break; + case BotSpellTypes::Succor: + spellTypeName = "succor"; + break; + case BotSpellTypes::BindAffinity: + spellTypeName = "bindaffinity"; + break; + case BotSpellTypes::Identify: + spellTypeName = "identify"; + break; + case BotSpellTypes::Levitate: + spellTypeName = "levitate"; + break; + case BotSpellTypes::Rune: + spellTypeName = "rune"; + break; + case BotSpellTypes::WaterBreathing: + spellTypeName = "waterbreathing"; + break; + case BotSpellTypes::Size: + spellTypeName = "size"; + break; + case BotSpellTypes::Invisibility: + spellTypeName = "invisibility"; + break; + case BotSpellTypes::MovementSpeed: + spellTypeName = "movementspeed"; + break; + case BotSpellTypes::SendHome: + spellTypeName = "sendhome"; + break; + case BotSpellTypes::SummonCorpse: + spellTypeName = "summoncorpse"; + break; default: break; } @@ -9075,6 +9159,47 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { return spellTypeName; } +std::string Mob::GetSubTypeNameByID(uint16 subType) { + std::string subTypeName = "null"; + + switch (subType) { + case CommandedSubTypes::SingleTarget: + subTypeName = "SingleTarget"; + break; + case CommandedSubTypes::GroupTarget: + subTypeName = "GroupTarget"; + break; + case CommandedSubTypes::AETarget: + subTypeName = "AETarget"; + break; + case CommandedSubTypes::SeeInvis: + subTypeName = "SeeInvis"; + break; + case CommandedSubTypes::Invis: + subTypeName = "Invis"; + break; + case CommandedSubTypes::InvisUndead: + subTypeName = "InvisUndead"; + break; + case CommandedSubTypes::InvisAnimals: + subTypeName = "InvisAnimals"; + break; + case CommandedSubTypes::Shrink: + subTypeName = "Shrink"; + break; + case CommandedSubTypes::Grow: + subTypeName = "Grow"; + break; + case CommandedSubTypes::Selo: + subTypeName = "Selo"; + break; + default: + break; + } + + return subTypeName; +} + bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::Nuke: diff --git a/zone/mob.h b/zone/mob.h index 662798af57..865e768ba5 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -435,7 +435,8 @@ class Mob : public Entity { uint16 GetSpellTypeIDByShortName(std::string spellTypeString); std::string GetSpellTypeNameByID(uint16 spellType); - std::string GetSpellTypeShortNameByID(uint16 spellType); + std::string GetSpellTypeShortNameByID(uint16 spellType); + std::string GetSubTypeNameByID(uint16 subType); bool GetDefaultSpellHold(uint16 spellType, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellDelay(uint16 spellType, uint8 stance = Stance::Balanced); From 10effce2a6e1fbdcbeea60797859777f5c568b17 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:51:37 -0600 Subject: [PATCH 038/394] Implement more commanded types properly, move shadownight hate to hateline type... Add incapacitated checks to casting logic and checks. Add candocombat zone check, summon other's corpse for bot, in/out combat spell checks, mute checks, level restriction --- .../database_update_manifest_bots.cpp | 125 ++++++-- common/ruletypes.h | 1 + common/spdat.cpp | 67 ++-- common/spdat.h | 5 +- common/version.h | 2 +- zone/bot.cpp | 288 +++++++++++------- zone/bot_commands/cast.cpp | 26 +- zone/botspellsai.cpp | 241 +++++++++++---- zone/mob.cpp | 66 +++- zone/spells.cpp | 15 +- 10 files changed, 588 insertions(+), 248 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 96627d8140..72e26142c2 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -513,7 +513,7 @@ UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 10436; .match = "", .sql = R"( INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) -VALUES +VALUES (3006, 9957, 100, 20, 254), (3006, 9956, 100, 20, 254), (3006, 552, 100, 25, 254), @@ -764,31 +764,6 @@ VALUES (3006, 34863, 101, 96, 254), (3006, 34864, 101, 96, 254), (3006, 34862, 101, 96, 254), -(3007, 4614, 101, 35, 49), -(3007, 4683, 101, 50, 56), -(3007, 4684, 101, 57, 63), -(3007, 4698, 101, 64, 64), -(3007, 5019, 101, 65, 254), -(3007, 5020, 101, 65, 254), -(3007, 6175, 101, 69, 70), -(3007, 10949, 101, 71, 75), -(3007, 10947, 101, 71, 75), -(3007, 10948, 101, 71, 75), -(3007, 14800, 101, 76, 80), -(3007, 14801, 101, 76, 80), -(3007, 14799, 101, 76, 80), -(3007, 18905, 101, 81, 85), -(3007, 18906, 101, 81, 85), -(3007, 18904, 101, 81, 85), -(3007, 25912, 101, 86, 90), -(3007, 25913, 101, 86, 90), -(3007, 25911, 101, 86, 90), -(3007, 29006, 101, 91, 95), -(3007, 29007, 101, 91, 95), -(3007, 29008, 101, 91, 95), -(3007, 35047, 101, 96, 254), -(3007, 35048, 101, 96, 254), -(3007, 35049, 101, 96, 254), (3008, 728, 101, 8, 60), (3008, 3361, 101, 61, 254), (3008, 5370, 101, 66, 70), @@ -1090,6 +1065,104 @@ VALUES (3011, 25555, 112, 86, 90), (3011, 28632, 112, 91, 95), (3011, 34662, 112, 96, 254); +)" + }, + ManifestEntry{ + .version = 9051, + .description = "2024_11_26_remove_sk_icb.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 55", + .condition = "empty", + .match = "", + .sql = R"( +DELETE +FROM bot_spells_entries +WHERE `npc_spells_id` = 3005 +AND `type` = 10; + +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) +VALUES +(3003, 10175, 55, 72, 76), +(3003, 10173, 55, 72, 76), +(3003, 10174, 55, 72, 76), +(3003, 14956, 55, 77, 81), +(3003, 14954, 55, 77, 81), +(3003, 14955, 55, 77, 81), +(3003, 19070, 55, 82, 86), +(3003, 19068, 55, 82, 86), +(3003, 19069, 55, 82, 86), +(3003, 25298, 55, 87, 91), +(3003, 25299, 55, 87, 91), +(3003, 25297, 55, 87, 91), +(3003, 28348, 55, 92, 96), +(3003, 28349, 55, 92, 96), +(3003, 28347, 55, 92, 96), +(3003, 34351, 55, 97, 254), +(3003, 34352, 55, 97, 254), +(3003, 34350, 55, 97, 254), +(3003, 40078, 55, 98, 254), +(3003, 40079, 55, 98, 254), +(3003, 40080, 55, 98, 254), +(3005, 1221, 55, 33, 41), +(3005, 1222, 55, 42, 52), +(3005, 1223, 55, 53, 58), +(3005, 1224, 55, 59, 62), +(3005, 3405, 55, 63, 66), +(3005, 5329, 55, 67, 70), +(3005, 5336, 55, 69, 73), +(3005, 10258, 55, 71, 71), +(3005, 10259, 55, 71, 71), +(3005, 10257, 55, 71, 71), +(3005, 10261, 55, 72, 76), +(3005, 10262, 55, 72, 76), +(3005, 10260, 55, 72, 76), +(3005, 10291, 55, 74, 78), +(3005, 10292, 55, 74, 78), +(3005, 10293, 55, 74, 78), +(3005, 15160, 55, 76, 76), +(3005, 15161, 55, 76, 76), +(3005, 15162, 55, 76, 76), +(3005, 15165, 55, 77, 81), +(3005, 15163, 55, 77, 81), +(3005, 15164, 55, 77, 81), +(3005, 15186, 55, 79, 83), +(3005, 15184, 55, 79, 83), +(3005, 15185, 55, 79, 83), +(3005, 19315, 55, 81, 81), +(3005, 19313, 55, 81, 81), +(3005, 19314, 55, 81, 81), +(3005, 19317, 55, 82, 86), +(3005, 19318, 55, 82, 86), +(3005, 19316, 55, 82, 86), +(3005, 19338, 55, 84, 88), +(3005, 19339, 55, 84, 88), +(3005, 19337, 55, 84, 88), +(3005, 25581, 55, 86, 86), +(3005, 25582, 55, 86, 86), +(3005, 25580, 55, 86, 86), +(3005, 25586, 55, 87, 91), +(3005, 25587, 55, 87, 91), +(3005, 25588, 55, 87, 91), +(3005, 25641, 55, 89, 93), +(3005, 25642, 55, 89, 93), +(3005, 25643, 55, 89, 93), +(3005, 28659, 55, 91, 91), +(3005, 28657, 55, 91, 91), +(3005, 28658, 55, 91, 91), +(3005, 28665, 55, 92, 96), +(3005, 28663, 55, 92, 96), +(3005, 28664, 55, 92, 96), +(3005, 28735, 55, 94, 98), +(3005, 28733, 55, 94, 98), +(3005, 28734, 55, 94, 98), +(3005, 34688, 55, 96, 96), +(3005, 34689, 55, 96, 96), +(3005, 34687, 55, 96, 96), +(3005, 34694, 55, 97, 254), +(3005, 34695, 55, 97, 254), +(3005, 34693, 55, 97, 254), +(3005, 34752, 55, 99, 254), +(3005, 34753, 55, 99, 254), +(3005, 34751, 55, 99, 254); )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/ruletypes.h b/common/ruletypes.h index 32c8378fba..2dcdd3e97b 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -800,6 +800,7 @@ RULE_INT(Bots, PercentChanceToCastSnare, 75, "The chance for a bot to attempt to RULE_INT(Bots, PercentChanceToCastDOT, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") diff --git a/common/spdat.cpp b/common/spdat.cpp index a691d51972..c4086c0ac3 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2843,13 +2843,8 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: case BotSpellTypes::Lull: + case BotSpellTypes::HateLine: return true; - case BotSpellTypes::InCombatBuff: - if (cls == Class::ShadowKnight) { - return true; - } - - return false; default: return false; } @@ -2898,12 +2893,6 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: - return true; - case BotSpellTypes::InCombatBuff: - if (cls == Class::ShadowKnight) { - return false; - } - return true; default: return false; @@ -3134,12 +3123,7 @@ bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { case BotSpellTypes::PetFastHeals: case BotSpellTypes::PetVeryFastHeals: case BotSpellTypes::PetHoTHeals: - return false; case BotSpellTypes::InCombatBuff: - if (cls && cls == Class::ShadowKnight) { - return true; - } - return false; default: return true; @@ -3150,13 +3134,44 @@ bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls) { switch (spellType) { - case BotSpellTypes::Escape: - if (cls == Class::ShadowKnight) { - return false; - } - - return true; case BotSpellTypes::Pet: + case BotSpellTypes::Succor: + return false; + default: + return true; + } + + return true; +} + +bool SpellTypeRequiresCastChecks(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AEFear: + case BotSpellTypes::AELifetap: + case BotSpellTypes::AEMez: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AERoot: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEStun: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Mez: + case BotSpellTypes::SummonCorpse: + return false; + default: + return true; + } + + return true; +} + +bool SpellTypeRequiresAEChecks(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AEMez: return false; default: return true; @@ -3263,6 +3278,10 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { bool IsCommandedSpellType(uint16 spellType) { switch (spellType) { + case BotSpellTypes::Charm: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::Resurrect: case BotSpellTypes::Lull: case BotSpellTypes::Teleport: case BotSpellTypes::Succor: @@ -3276,8 +3295,6 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: - //case BotSpellTypes::Charm: - //case BotSpellTypes::Resurrect: //case BotSpellTypes::Cure: //case BotSpellTypes::GroupCures: //case BotSpellTypes::DamageShields: diff --git a/common/spdat.h b/common/spdat.h index cefac6784d..1c3bcf52cc 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -708,6 +708,7 @@ namespace BotSpellTypes constexpr uint16 ResistBuffs = 52; constexpr uint16 PetDamageShields = 53; constexpr uint16 PetResistBuffs = 54; + constexpr uint16 HateLine = 55; // Command Spell Types constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic @@ -725,7 +726,7 @@ namespace BotSpellTypes constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed + constexpr uint16 END = BotSpellTypes::HateLine; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } @@ -747,6 +748,8 @@ bool IsClientBotSpellType(uint16 spellType); bool IsHealBotSpellType(uint16 spellType); bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); +bool SpellTypeRequiresCastChecks(uint16 spellType); +bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); // These should not be used to determine spell category.. diff --git a/common/version.h b/common/version.h index d9d57e7ca1..800ee365d6 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9284 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9050 //TODO update as needed +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9051 //TODO update as needed #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 56736942ed..fedaa32642 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5675,9 +5675,7 @@ bool Bot::CastSpell( casting_spell_id || delaytimer || spellend_timer.Enabled() || - IsStunned() || - IsFeared() || - IsMezzed() || + ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id)) ) { @@ -9414,7 +9412,12 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec if (doPrechecks) { if (spells[spell_id].target_type == ST_Self && tar != this) { - tar = this; + if (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse)) { + //tar = this; + } + else { + tar = this; + } } if (!PrecastChecks(tar, spellType)) { @@ -9430,11 +9433,30 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (spells[spell_id].target_type == ST_Self && tar != this) { + if (IsFeared() || IsSilenced() || IsAmnesiad()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if ( + spells[spell_id].target_type == ST_Self + && tar != this && + (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } + if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if (!CheckSpellRecastTimer(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; @@ -9450,6 +9472,42 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (this == tar && IsSacrificeSpell(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + + if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + + if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { + if (IsBeneficialSpell(spell_id)) { + if (IsEngaged()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + } + } + else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { + if (IsBeneficialSpell(spell_id)) { + if (!IsEngaged()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + } + } + + if (!IsDiscipline(spell_id)) { + int chance = GetFocusEffect(focusFcMute, spell_id); + + if (chance && zone->random.Roll(chance)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return(false); + } + } + if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; @@ -9534,6 +9592,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::InCombatBuff: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: @@ -9567,6 +9626,11 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { return false; } + if (!tar->CheckSpellLevelRestriction(this, spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9679,6 +9743,13 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } } + break; + case BotSpellTypes::Lull: + if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + break; default: break; @@ -10505,50 +10576,48 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, return 20; case BotSpellTypes::Mez: return 21; - case BotSpellTypes::AEDispel: + case BotSpellTypes::HateLine: return 22; - case BotSpellTypes::Dispel: + case BotSpellTypes::AEDispel: return 23; - case BotSpellTypes::AEDebuff: + case BotSpellTypes::Dispel: return 24; - case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: return 25; - case BotSpellTypes::AESnare: + case BotSpellTypes::Debuff: return 26; - case BotSpellTypes::Snare: + case BotSpellTypes::AESnare: return 27; - case BotSpellTypes::AEFear: + case BotSpellTypes::Snare: return 28; - case BotSpellTypes::Fear: - return 29; case BotSpellTypes::AESlow: - return 30; + return 29; case BotSpellTypes::Slow: - return 31; + return 30; case BotSpellTypes::AERoot: - return 32; + return 31; case BotSpellTypes::Root: - return 33; + return 32; case BotSpellTypes::AEDoT: - return 34; + return 33; case BotSpellTypes::DOT: - return 35; + return 34; case BotSpellTypes::AEStun: - return 36; + return 35; case BotSpellTypes::PBAENuke: - return 37; + return 36; case BotSpellTypes::AENukes: - return 38; + return 37; case BotSpellTypes::AERains: - return 39; + return 38; case BotSpellTypes::Stun: - return 40; + return 39; case BotSpellTypes::Nuke: - return 41; + return 40; case BotSpellTypes::InCombatBuff: - return 42; + return 41; case BotSpellTypes::InCombatBuffSong: - return 43; + return 42; default: return 0; } @@ -10915,8 +10984,6 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: - case BotSpellTypes::Teleport: - case BotSpellTypes::Succor: case BotSpellTypes::BindAffinity: case BotSpellTypes::Identify: case BotSpellTypes::Levitate: @@ -10925,8 +10992,6 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Size: case BotSpellTypes::Invisibility: case BotSpellTypes::MovementSpeed: - case BotSpellTypes::SendHome: - case BotSpellTypes::SummonCorpse: return BotSpellTypes::Buff; case BotSpellTypes::AEMez: case BotSpellTypes::Mez: @@ -10961,6 +11026,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Charm: case BotSpellTypes::Escape: case BotSpellTypes::HateRedux: + case BotSpellTypes::HateLine: case BotSpellTypes::InCombatBuff: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: @@ -11032,84 +11098,84 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { } return false; - case BotSpellTypes::Lull: - if (!IsHarmonySpell(spell_id)) { - return true; - } - - return false; - case BotSpellTypes::Teleport: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - return true; - } - - return false; - case BotSpellTypes::Succor: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - return true; - } - - return false; - case BotSpellTypes::BindAffinity: - if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - return true; - } - - return false; - case BotSpellTypes::Identify: - if (IsEffectInSpell(spell_id, SE_Identify)) { - return true; - } - - return false; - case BotSpellTypes::Levitate: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - return true; - } - - return false; - case BotSpellTypes::Rune: - if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - return true; - } - - return false; - case BotSpellTypes::WaterBreathing: - if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { - return true; - } - - return false; - case BotSpellTypes::Size: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - return true; - } - - return false; - case BotSpellTypes::Invisibility: - if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - return true; - } - - return false; - case BotSpellTypes::MovementSpeed: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - return true; - } - - return false; - case BotSpellTypes::SendHome: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - return true; - } - - return false; - case BotSpellTypes::SummonCorpse: - if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - return true; - } - - return false; + //case BotSpellTypes::Lull: + // if (IsHarmonySpell(spell_id)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Teleport: + // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Succor: + // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::BindAffinity: + // if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Identify: + // if (IsEffectInSpell(spell_id, SE_Identify)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Levitate: + // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Rune: + // if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::WaterBreathing: + // if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Size: + // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Invisibility: + // if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::MovementSpeed: + // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::SendHome: + // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::SummonCorpse: + // if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + // return true; + // } + // + // return false; default: return true; } @@ -11342,7 +11408,7 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo break; } - if (!m->IsNPC() || !m->CastToNPC()->IsOnHatelist(botCaster->GetOwner())) { + if (!m->IsNPC() || (!IsCommandedSpell() && !m->CastToNPC()->IsOnHatelist(botCaster->GetOwner()))) { continue; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 49112b8693..10238aac20 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -308,7 +308,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* tar = c->GetTarget(); - LogTestDebug("{}: 'Attempting {} [{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + //LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (!tar) { if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { @@ -338,7 +338,17 @@ void bot_command_cast(Client* c, const Seperator* sep) break; default: - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { + if ( + (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) || + ( + spellType == BotSpellTypes::Charm && + ( + tar->IsClient() || + tar->IsCorpse() || + tar->GetOwner() + ) + ) + ) { c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); return; @@ -395,18 +405,16 @@ void bot_command_cast(Client* c, const Seperator* sep) /* TODO bot rewrite - - FIX: Depart, SummonCorpse, Lull, - Group Cures, Precombat, Fear/AE Fear - ICB (SK) casting hate on friendly but not hostile? + FIX: Depart + Group Cures, Precombat NEED TO CHECK: precombat, AE Dispel, AE Lifetap - DO I NEED A PBAE CHECK??? */ - if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { + if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; } Mob* newTar = tar; - LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { newTar = bot_iter; } @@ -435,7 +443,7 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 241854dfa5..d3123bf86d 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -116,11 +116,13 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; case BotSpellTypes::InCombatBuff: - if (GetClass() == Class::ShadowKnight && (tar->IsOfClientBot() || (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()))) { + if (!IsCommandedSpell() && GetClass() != Class::Shaman && spellType == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { return false; } - if (!IsCommandedSpell() && GetClass() != Class::Shaman && spellType == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { + break; + case BotSpellTypes::HateLine: + if (!tar->IsNPC()) { return false; } @@ -198,7 +200,6 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return BotCastPet(tar, botClass, botSpell, spellType); case BotSpellTypes::Resurrect: - case BotSpellTypes::SummonCorpse: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { return false; } @@ -267,6 +268,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return true; } + else { LogTestDebug("{} says, '{} [#{}] - [{}] FAILED AIDoSpellCast on {}.'", GetCleanName(), spells[s.SpellId].name, s.SpellId, GetSpellTypeNameByID(spellType), tar->GetCleanName()); } //deleteme } return false; @@ -631,7 +633,7 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } bool Bot::AI_PursueCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -656,11 +658,11 @@ bool Bot::AI_PursueCastCheck() { continue; } - if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. continue; } @@ -680,7 +682,7 @@ bool Bot::AI_PursueCastCheck() { } bool Bot::AI_IdleCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -719,11 +721,11 @@ bool Bot::AI_IdleCastCheck() { continue; } - if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. continue; } @@ -743,7 +745,7 @@ bool Bot::AI_IdleCastCheck() { } bool Bot::AI_EngagedCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -769,11 +771,11 @@ bool Bot::AI_EngagedCastCheck() { continue; } - if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. continue; } @@ -1045,28 +1047,19 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa } if ( - ( - !botCaster->IsCommandedSpell() || - ( - botCaster->IsCommandedSpell() && - (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez) - ) - ) - && - ( - !IsPBAESpell(botSpellList[i].spellid) && - !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType)) - ) + (!botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && SpellTypeRequiresCastChecks(spellType))) && + (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) ) { continue; } if ( - botCaster->IsCommandedSpell() || + botCaster->IsCommandedSpell() || !AE || - (spellType == BotSpellTypes::GroupCures) || - (spellType == BotSpellTypes::AEMez) || - (AE && botCaster->HasValidAETarget(botCaster, botSpellList[i].spellid, spellType, tar)) + ( + SpellTypeRequiresAEChecks(spellType) && + botCaster->HasValidAETarget(botCaster, botSpellList[i].spellid, spellType, tar) + ) ) { BotSpell_wPriority botSpell; botSpell.SpellId = botSpellList[i].spellid; @@ -2099,18 +2092,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) case BotSpellTypes::PetResistBuffs: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - case BotSpellTypes::Teleport: - case BotSpellTypes::Succor: - case BotSpellTypes::BindAffinity: - case BotSpellTypes::Identify: - case BotSpellTypes::Levitate: - case BotSpellTypes::Rune: - case BotSpellTypes::WaterBreathing: - case BotSpellTypes::Size: - case BotSpellTypes::Invisibility: - case BotSpellTypes::MovementSpeed: - case BotSpellTypes::SendHome: - case BotSpellTypes::SummonCorpse: return RuleI(Bots, PercentChanceToCastBuff); case BotSpellTypes::Escape: return RuleI(Bots, PercentChanceToCastEscape); @@ -2124,6 +2105,8 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) return RuleI(Bots, PercentChanceToCastDispel); case BotSpellTypes::InCombatBuff: return RuleI(Bots, PercentChanceToCastInCombatBuff); + case BotSpellTypes::HateLine: + return RuleI(Bots, PercentChanceToCastHateLine); case BotSpellTypes::Mez: return RuleI(Bots, PercentChanceToCastMez); case BotSpellTypes::Slow: @@ -2842,65 +2825,71 @@ void Bot::CheckBotSpells() { switch (s.type) { - case BotSpellTypes::Nuke: //DONE + case BotSpellTypes::Nuke: if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::RegularHeal: //DONE - //if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { + case BotSpellTypes::RegularHeal: if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Root: //DONE + case BotSpellTypes::Root: if (IsEffectInSpell(spell_id, SE_Root)) { valid = true; break; } break; - case BotSpellTypes::Buff: //DONE + case BotSpellTypes::Buff: if (IsAnyBuffSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Pet: //DONE + case BotSpellTypes::Pet: if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { valid = true; break; } break; - case BotSpellTypes::Lifetap: //DONE + case BotSpellTypes::Lifetap: if (IsLifetapSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Snare: //DONE + case BotSpellTypes::Snare: if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::DOT: //DONE + case BotSpellTypes::DOT: if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Dispel: //DONE + case BotSpellTypes::Dispel: if (IsDispelSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::InCombatBuff: //DONE + case BotSpellTypes::InCombatBuff: if ( IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) || + IsAnyBuffSpell(spell_id) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::HateLine: + if ( (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) ) { @@ -2908,37 +2897,37 @@ void Bot::CheckBotSpells() { break; } break; - case BotSpellTypes::Mez: //DONE + case BotSpellTypes::Mez: if (IsMesmerizeSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Charm: //DONE + case BotSpellTypes::Charm: if (IsCharmSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Slow: //DONE + case BotSpellTypes::Slow: if (IsSlowSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Debuff: //DONE + case BotSpellTypes::Debuff: if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Cure: //DONE + case BotSpellTypes::Cure: if (IsCureSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::PreCombatBuff: //DONE + case BotSpellTypes::PreCombatBuff: if ( IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id) && @@ -2950,9 +2939,9 @@ void Bot::CheckBotSpells() { break; } break; - case BotSpellTypes::InCombatBuffSong: //DONE - case BotSpellTypes::OutOfCombatBuffSong: //DONE - case BotSpellTypes::PreCombatBuffSong: //DONE + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: if ( IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id) && @@ -2964,7 +2953,7 @@ void Bot::CheckBotSpells() { break; } break; - case BotSpellTypes::Fear: //DONE + case BotSpellTypes::Fear: if (IsFearSpell(spell_id)) { valid = true; break; @@ -2988,7 +2977,84 @@ void Bot::CheckBotSpells() { break; } break; - //TODO bot rewrite - add commanded types + case BotSpellTypes::Lull: + if (IsHarmonySpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Teleport: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + valid = true; + break; + } + break; + case BotSpellTypes::Succor: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + valid = true; + break; + } + break; + case BotSpellTypes::BindAffinity: + if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + valid = true; + break; + } + break; + case BotSpellTypes::Identify: + if (IsEffectInSpell(spell_id, SE_Identify)) { + valid = true; + break; + } + break; + case BotSpellTypes::Levitate: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + valid = true; + break; + } + break; + case BotSpellTypes::Rune: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + valid = true; + break; + } + break; + case BotSpellTypes::WaterBreathing: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { + valid = true; + break; + } + break; + case BotSpellTypes::Size: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + valid = true; + break; + } + break; + case BotSpellTypes::Invisibility: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::MovementSpeed: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + valid = true; + break; + } + break; + case BotSpellTypes::SendHome: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + valid = true; + break; + } + break; + case BotSpellTypes::SummonCorpse: + if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + valid = true; + break; + } + break; default: break; @@ -2997,7 +3063,6 @@ void Bot::CheckBotSpells() { if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { correctType = BotSpellTypes::Nuke; } - //else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { correctType = BotSpellTypes::RegularHeal; } @@ -3024,11 +3089,15 @@ void Bot::CheckBotSpells() { } else if ( IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) || + IsAnyBuffSpell(spell_id) + ) { + correctType = BotSpellTypes::InCombatBuff; + } + else if ( (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) ) { - correctType = BotSpellTypes::InCombatBuff; + correctType = BotSpellTypes::HateLine; } else if (IsMesmerizeSpell(spell_id)) { correctType = BotSpellTypes::Mez; @@ -3084,7 +3153,45 @@ void Bot::CheckBotSpells() { else if (IsEffectInSpell(spell_id, SE_Revive)) { correctType = BotSpellTypes::Resurrect; } - //TODO bot rewrite - add commanded types + else if (IsHarmonySpell(spell_id)) { + correctType = BotSpellTypes::Lull; + } + else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + correctType = BotSpellTypes::Teleport; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + correctType = BotSpellTypes::Succor; + } + else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + correctType = BotSpellTypes::BindAffinity; + } + else if (IsEffectInSpell(spell_id, SE_Identify)) { + correctType = BotSpellTypes::Identify; + } + else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + correctType = BotSpellTypes::Levitate; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + correctType = BotSpellTypes::Rune; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { + correctType = BotSpellTypes::WaterBreathing; + } + else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + correctType = BotSpellTypes::Size; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + correctType = BotSpellTypes::Invisibility; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + correctType = BotSpellTypes::MovementSpeed; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + correctType = BotSpellTypes::SendHome; + } + else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + correctType = BotSpellTypes::SummonCorpse; + } if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" diff --git a/zone/mob.cpp b/zone/mob.cpp index 4c16356551..6d04c99848 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8898,6 +8898,9 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "Pet Resist Buff"; break; + case BotSpellTypes::HateLine: + spellTypeName = "Hate Line"; + break; case BotSpellTypes::Lull: spellTypeName = "Lull"; break; @@ -9113,6 +9116,9 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "petresistbuffs"; break; + case BotSpellTypes::HateLine: + spellTypeName = "hateline"; + break; case BotSpellTypes::Lull: spellTypeName = "lull"; break; @@ -9231,8 +9237,11 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case BotSpellTypes::AEFear: case BotSpellTypes::Fear: return true; + case BotSpellTypes::Mez: case BotSpellTypes::AEMez: + case BotSpellTypes::Debuff: case BotSpellTypes::AEDebuff: + case BotSpellTypes::Slow: case BotSpellTypes::AESlow: case BotSpellTypes::HateRedux: switch (stance) { @@ -9249,12 +9258,7 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case Stance::Assist: return true; default: - if (GetClass() == Class::Wizard) { - return true; - } - else { - return false; - } + return false; } case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: @@ -9265,6 +9269,55 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { else { return true; } + case BotSpellTypes::HateLine: + if (GetClass() == Class::ShadowKnight || GetClass() == Class::Paladin) { + switch (stance) { + case Stance::Aggressive: + return false; + default: + return true; + } + } + else { + return true; + } + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::RegularHeal: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::Pet: + case BotSpellTypes::Escape: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Buff: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::PreCombatBuff: default: return false; } @@ -9487,6 +9540,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::PetResistBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: + case BotSpellTypes::HateLine: return 100; case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: diff --git a/zone/spells.cpp b/zone/spells.cpp index 1f80e30210..d24b5b7e4a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -803,7 +803,12 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp if (IsGroupSpell(spell_id)) { return true; } else if (spells[spell_id].target_type == ST_Self) { - spell_target = this; + if (IsBot() && (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse))) { + //spell_target = this; + } + else { + spell_target = this; + } } } else { if (IsGroupSpell(spell_id) && spell_target != this) { @@ -1951,7 +1956,13 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // single target spells case ST_Self: { - spell_target = this; + if (IsBot() && (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse))) { + //spell_target = this; + } + else { + spell_target = this; + } + CastAction = SingleTarget; break; } From f6cb63a89bf67c1dee3bef14b3efb8cc591a6275 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:02:37 -0600 Subject: [PATCH 039/394] more command cleanup --- zone/bot_command.cpp | 60 ++---- zone/bot_command.h | 6 +- zone/bot_commands/cast.cpp | 2 +- zone/bot_commands/copy_settings.cpp | 2 +- zone/bot_commands/default_settings.cpp | 2 +- zone/bot_commands/lull.cpp | 43 ----- zone/bot_commands/spell_aggro_checks.cpp | 2 +- zone/bot_commands/spell_delays.cpp | 2 +- zone/bot_commands/spell_engaged_priority.cpp | 2 +- zone/bot_commands/spell_holds.cpp | 2 +- zone/bot_commands/spell_idle_priority.cpp | 2 +- zone/bot_commands/spell_max_hp_pct.cpp | 2 +- zone/bot_commands/spell_max_mana_pct.cpp | 2 +- zone/bot_commands/spell_max_thresholds.cpp | 2 +- zone/bot_commands/spell_min_hp_pct.cpp | 2 +- zone/bot_commands/spell_min_mana_pct.cpp | 2 +- zone/bot_commands/spell_min_thresholds.cpp | 2 +- zone/bot_commands/spell_pursue_priority.cpp | 2 +- zone/bot_commands/spell_target_count.cpp | 2 +- zone/bot_commands/summon_corpse.cpp | 47 ----- zone/client.cpp | 60 ++++++ zone/client.h | 1 + zone/command.cpp | 2 +- zone/command.h | 2 +- zone/gm_commands/spell_delays.cpp | 185 ++++++++---------- zone/gm_commands/spell_holds.cpp | 186 ++++++++----------- zone/gm_commands/spell_max_thresholds.cpp | 186 ++++++++----------- zone/gm_commands/spell_min_thresholds.cpp | 186 ++++++++----------- 28 files changed, 389 insertions(+), 607 deletions(-) delete mode 100644 zone/bot_commands/lull.cpp delete mode 100644 zone/bot_commands/summon_corpse.cpp diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 7af4109f5b..650f4a5e86 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1325,7 +1325,6 @@ int bot_command_init(void) bot_command_add("inventoryremove", "Removes an item from a bot's inventory", AccountStatus::Player, bot_command_inventory_remove) || bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", AccountStatus::Player, bot_command_inventory_window) || bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", AccountStatus::Player, bot_command_item_use) || - bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || //TODO bot rewrite - IMPLEMENT bot_command_add("maxmeleerange", "Toggles whether your bot is at max melee range or not. This will disable all special abilities, including taunt.", AccountStatus::Player, bot_command_max_melee_range) || bot_command_add("owneroption", "Sets options available to bot owners", AccountStatus::Player, bot_command_owner_option) || bot_command_add("pet", "Lists the available bot pet [subcommands]", AccountStatus::Player, bot_command_pet) || @@ -1362,7 +1361,6 @@ int bot_command_init(void) bot_command_add("spellsettingsupdate", "Update a bot spell setting entry", AccountStatus::Player, bot_command_spell_settings_update) || bot_command_add("spelltypeids", "Lists spelltypes by ID", AccountStatus::Player, bot_command_spelltype_ids) || bot_command_add("spelltypenames", "Lists spelltypes by shortname", AccountStatus::Player, bot_command_spelltype_names) || - bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) || //TODO bot rewrite - IMPLEMENT 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) || bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || @@ -2102,56 +2100,13 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp return false; } -void SendSpellTypePrompts(Client *c, bool commandedTypes) { - c->Message( - Chat::Yellow, - fmt::format( - "You can view spell types by ID or shortname: {}, {}, {} / {}, {}, {}", - Saylink::Silent( - fmt::format("^spelltypeids 0-19"), "ID 0-19" - ), - Saylink::Silent( - fmt::format("^spelltypeids 20-39"), "20-39" - ), - Saylink::Silent( - fmt::format("^spelltypeids 40+"), "40+" - ), - Saylink::Silent( - fmt::format("^spelltypenames 0-19"), "Shortname 0-19" - ), - Saylink::Silent( - fmt::format("^spelltypenames 20-39"), "20-39" - ), - Saylink::Silent( - fmt::format("^spelltypenames 40+"), "40+" - ) - ).c_str() - ); - - if (commandedTypes) { - c->Message( - Chat::Yellow, - fmt::format( - "You can view commanded spell types by ID or shortname: {} / {}", - Saylink::Silent( - fmt::format("^spelltypeids commanded"), "ID" - ), - Saylink::Silent( - fmt::format("^spelltypenames commanded"), "Shortname" - ) - ).c_str() - ); - } - - return; -} - -void SendSpellTypeWindow(Client *c, const Seperator* sep) { +void SendSpellTypeWindow(Client* c, const Seperator* sep) { std::string arg0 = sep->arg[0]; std::string arg1 = sep->arg[1]; uint8 minCount = 0; uint8 maxCount = 0; + bool clientOnly = false; if (BotSpellTypes::END <= 19) { minCount = BotSpellTypes::START; @@ -2173,6 +2128,11 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { minCount = BotSpellTypes::COMMANDED_START; maxCount = BotSpellTypes::COMMANDED_END; } + else if (!arg1.compare("client")) { + minCount = BotSpellTypes::START; + maxCount = BotSpellTypes::END; + clientOnly = true; + } else { c->Message(Chat::Yellow, "You must choose a valid range option"); @@ -2222,6 +2182,10 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { ); for (int i = minCount; i <= maxCount; ++i) { + if (clientOnly && !IsClientBotSpellType(i)) { + continue; + } + popup_text += DialogueWindow::TableRow( DialogueWindow::TableCell( fmt::format( @@ -2270,7 +2234,6 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/illusion_block.cpp" #include "bot_commands/inventory.cpp" #include "bot_commands/item_use.cpp" -#include "bot_commands/lull.cpp" #include "bot_commands/max_melee_range.cpp" #include "bot_commands/name.cpp" #include "bot_commands/owner_option.cpp" @@ -2299,7 +2262,6 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/spell_target_count.cpp" #include "bot_commands/spelltypes.cpp" #include "bot_commands/summon.cpp" -#include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" #include "bot_commands/taunt.cpp" #include "bot_commands/timer.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 56be996809..ecce9485d7 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1688,7 +1688,6 @@ void bot_command_hold(Client *c, const Seperator *sep); void bot_command_illusion_block(Client* c, const Seperator* sep); void bot_command_inventory(Client *c, const Seperator *sep); void bot_command_item_use(Client *c, const Seperator *sep); -void bot_command_lull(Client *c, const Seperator *sep); void bot_command_max_melee_range(Client* c, const Seperator* sep); void bot_command_owner_option(Client *c, const Seperator *sep); void bot_command_pet(Client *c, const Seperator *sep); @@ -1723,7 +1722,6 @@ void bot_command_spelltype_ids(Client* c, const Seperator* sep); void bot_command_spelltype_names(Client* c, const Seperator* sep); void bot_spell_info_dialogue_window(Client* c, const Seperator *sep); void bot_command_enforce_spell_list(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); void bot_command_timer(Client* c, const Seperator* sep); @@ -1807,6 +1805,6 @@ void helper_send_available_subcommands(Client *bot_owner, const char* command_si void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = Class::None); bool helper_spell_check_fail(STBaseEntry* local_entry); bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type); -void SendSpellTypePrompts(Client *c, bool commandedTypes = false); -void SendSpellTypeWindow(Client *c, const Seperator* sep); +void SendSpellTypeWindow(Client* c, const Seperator* sep); + #endif diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 10238aac20..eb1361d8d8 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -94,7 +94,7 @@ void bot_command_cast(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c, true); + c->SendSpellTypePrompts(true); c->Message( Chat::Yellow, diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 8e41f971a7..359ac8c22e 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -97,7 +97,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 8ed6733a68..b12e183b40 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -91,7 +91,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/lull.cpp b/zone/bot_commands/lull.cpp deleted file mode 100644 index d39f534d16..0000000000 --- a/zone/bot_commands/lull.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "../bot_command.h" - -void bot_command_lull(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Lull]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Lull) || helper_command_alias_fail(c, "bot_command_lull", sep->arg[0], "lull")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Lull); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY); - if (!target_mob) - continue; - - //if (spells[local_entry->spell_id].max[EFFECTIDTOINDEX(3)] && spells[local_entry->spell_id].max[EFFECTIDTOINDEX(3)] < target_mob->GetLevel()) - // continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index cbcc4706bb..0be650d696 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -92,7 +92,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 9fd37b70a0..d1fb2d6646 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -98,7 +98,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index f27387b590..2c5208d145 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -96,7 +96,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index e30911bd43..d83b5369b7 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -82,7 +82,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index cdb741d44f..9576d4a8f0 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -96,7 +96,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index bcd3614a58..42c552cdb5 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 84cb9abd9a..da8ad9d369 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 385258dddc..ed3316c012 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -98,7 +98,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index be6fe1b9e1..3c512ab3ad 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index f4ffe1bade..43c37ed8cb 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index ffcecf8b74..4c1b11a3a3 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -100,7 +100,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 2721354226..864bc2ba93 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -96,7 +96,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index ad1b897b88..066fe95cc0 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -92,7 +92,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/summon_corpse.cpp b/zone/bot_commands/summon_corpse.cpp deleted file mode 100644 index 2a7a5b80f5..0000000000 --- a/zone/bot_commands/summon_corpse.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bot_command.h" - -void bot_command_summon_corpse(Client *c, const Seperator *sep) -{ - // Same methodology as old command..but, does not appear to work... (note: didn't work there, either...) - - // temp - c->Message(Chat::White, "This command is currently unavailable..."); - return; - - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_SummonCorpse]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_SummonCorpse) || helper_command_alias_fail(c, "bot_command_summon_corpse", sep->arg[0], "summoncorpse")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_SummonCorpse); - return; - } - - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = ActionableTarget::AsSingle_ByPlayer(c); - if (!target_mob) - continue; - - if (spells[local_entry->spell_id].base_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/client.cpp b/zone/client.cpp index 9d54f0b3d7..569f4986cc 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13338,3 +13338,63 @@ std::string Client::SplitCommandHelpText(std::vector msg, std::stri return returnText; } + +void Client::SendSpellTypePrompts(bool commandedTypes, bool clientOnlyTypes) { + if (clientOnlyTypes) { + Message( + Chat::Yellow, + fmt::format( + "You can view spell types by {} or {}.", + Saylink::Silent( + fmt::format("^spelltypeids client"), "ID" + ), + Saylink::Silent( + fmt::format("^spelltypenames client"), "Shortname" + ) + ).c_str() + ); + } + else { + Message( + Chat::Yellow, + fmt::format( + "You can view spell types by {}, {}, {} or by {}, {}, {}.", + Saylink::Silent( + fmt::format("^spelltypeids 0-19"), "ID 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypeids 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypeids 40+"), "40+" + ), + Saylink::Silent( + fmt::format("^spelltypenames 0-19"), "Shortname 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypenames 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypenames 40+"), "40+" + ) + ).c_str() + ); + } + + if (commandedTypes) { + Message( + Chat::Yellow, + fmt::format( + "You can view commanded spell types by {} or {}.", + Saylink::Silent( + fmt::format("^spelltypeids commanded"), "ID" + ), + Saylink::Silent( + fmt::format("^spelltypenames commanded"), "Shortname" + ) + ).c_str() + ); + } + + return; +} diff --git a/zone/client.h b/zone/client.h index 77cf9a574c..2a090eeac4 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1263,6 +1263,7 @@ class Client : public Mob ); std::string GetCommandHelpHeader(std::string color, std::string header); std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor = false, std::string secondaryColor = ""); + void SendSpellTypePrompts(bool commandedTypes = false, bool clientOnlyTypes = false); // Task System Methods void LoadClientTaskState(); diff --git a/zone/command.cpp b/zone/command.cpp index 21f57a7099..8fea9152bb 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -219,7 +219,7 @@ int command_init(void) command_add("spawneditmass", "[Search Criteria] [Edit Option] [Edit Value] [Apply] Mass editing spawn command (Apply is optional, 0 = False, 1 = True, default is False)", AccountStatus::GMLeadAdmin, command_spawneditmass) || command_add("spawnfix", "Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, command_spell_delays) || - command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, command_spell_holds) || + //command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, command_spell_holds) || //currently unusued command_add("spellmaxthresholds", "Controls the minimum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_max_thresholds) || command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_min_thresholds) || command_add("stun", "[duration] - Stuns you or your target for duration", AccountStatus::GMAdmin, command_stun) || diff --git a/zone/command.h b/zone/command.h index aa5771da99..39fc87e170 100644 --- a/zone/command.h +++ b/zone/command.h @@ -172,7 +172,7 @@ void command_spawn(Client *c, const Seperator *sep); void command_spawneditmass(Client *c, const Seperator *sep); void command_spawnfix(Client *c, const Seperator *sep); void command_spell_delays(Client* c, const Seperator* sep); -void command_spell_holds(Client* c, const Seperator* sep); +//void command_spell_holds(Client* c, const Seperator* sep); //currently unusued void command_spell_max_thresholds(Client* c, const Seperator* sep); void command_spell_min_thresholds(Client* c, const Seperator* sep); void command_stun(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp index 09b2b0c5d5..f7fda40ee7 100644 --- a/zone/gm_commands/spell_delays.cpp +++ b/zone/gm_commands/spell_delays.cpp @@ -7,101 +7,85 @@ void command_spell_delays(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 4000] or [%s cures 4000] would allow bots to cast cures on you every 4 seconds.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message( - Chat::White, + std::vector description = + { + "Controls how often bots can cast certain spell types on you" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set Very Fast Heals to be received every 1 second:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 1000", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::VeryFastHeals) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 1000", + sep->arg[0], + BotSpellTypes::VeryFastHeals ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current Regular Heal delay:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::RegularHeal) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::RegularHeal ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -112,18 +96,8 @@ void command_spell_delays(Client* c, const Seperator* sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -133,20 +107,8 @@ void command_spell_delays(Client* c, const Seperator* sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -164,10 +126,11 @@ void command_spell_delays(Client* c, const Seperator* sep) } } + // Enable/Disable/Current checks if (sep->IsNumber(2)) { typeValue = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 1 || typeValue > 60000) { + if (typeValue < 0 || typeValue > 60000) { c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); return; @@ -195,18 +158,18 @@ void command_spell_delays(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your current {} delay is {} seconds.", + "Your [{}] delay is currently {} seconds.'", c->GetSpellTypeNameByID(spellType), c->GetSpellDelay(spellType) / 1000.00 ).c_str() ); } else { - c->SetSpellDelay(spellType, typeValue); + c->SetSpellHold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your {} delay was set to {} seconds.", + "Your [{}] delay was set to {} seconds.'", c->GetSpellTypeNameByID(spellType), c->GetSpellDelay(spellType) / 1000.00 ).c_str() diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp index 90a725dfbb..c9c3641d75 100644 --- a/zone/gm_commands/spell_holds.cpp +++ b/zone/gm_commands/spell_holds.cpp @@ -2,108 +2,94 @@ void command_spell_holds(Client *c, const Seperator *sep) { + //unused for clients + c->Message(Chat::Yellow, "Spell Holds for players is currently unused."); + return; + const int arguments = sep->argnum; if (arguments) { const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 1] or [%s cures 1] would prevent bots from casting cures on you.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message(Chat::White, "note: Set to 0 to unhold the given spell type."); - c->Message(Chat::White, "note: Set to 1 to hold the given spell type."); - c->Message( - Chat::White, + std::vector description = + { + "Toggles whether or not bots can cast certain spell types on you" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set DoTs to be held:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 1", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 1", + sep->arg[0], + BotSpellTypes::DOT ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current DoT settings:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::DOT ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -114,18 +100,8 @@ void command_spell_holds(Client *c, const Seperator *sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -135,20 +111,8 @@ void command_spell_holds(Client *c, const Seperator *sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -198,7 +162,7 @@ void command_spell_holds(Client *c, const Seperator *sep) c->Message( Chat::Green, fmt::format( - "Your current Hold {}s status is {}.", + "Your [{}] spell hold is currently [{}].'", c->GetSpellTypeNameByID(spellType), c->GetSpellHold(spellType) ? "enabled" : "disabled" ).c_str() @@ -209,7 +173,7 @@ void command_spell_holds(Client *c, const Seperator *sep) c->Message( Chat::Green, fmt::format( - "Your Hold {}s status was {}.", + "Your [{}] spell hold was [{}].'", c->GetSpellTypeNameByID(spellType), c->GetSpellHold(spellType) ? "enabled" : "disabled" ).c_str() diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp index a67660d2e8..3e0188baa9 100644 --- a/zone/gm_commands/spell_max_thresholds.cpp +++ b/zone/gm_commands/spell_max_thresholds.cpp @@ -7,101 +7,85 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 95] or [%s cures 95] would allow bots to cast cures on you when you are under 95%% health.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message( - Chat::White, + std::vector description = + { + "Threshold of your own health when bots will start casting the chosen spell type" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set Complete Heals to start at 90% health:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 90", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::CompleteHeal) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 90", + sep->arg[0], + BotSpellTypes::CompleteHeal ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current HoT Heal settings:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::HoTHeals) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::HoTHeals ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -112,18 +96,8 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -133,20 +107,8 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -168,8 +130,8 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { typeValue = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 150) { - c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); return; } @@ -196,18 +158,18 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your current max threshold for {}s is {}%%.", + "Your [{}] maximum hold is currently [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMaxThreshold(spellType) ).c_str() ); } else { - c->SetSpellMaxThreshold(spellType, typeValue); + c->SetSpellHold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your max threshold for {}s was set to {}%%.", + "Your [{}] maximum hold was set to [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMaxThreshold(spellType) ).c_str() diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp index 1b0fcfdf2a..c2b3893d7b 100644 --- a/zone/gm_commands/spell_min_thresholds.cpp +++ b/zone/gm_commands/spell_min_thresholds.cpp @@ -7,101 +7,85 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 15] or [%s cures 15] would prevent bots from casting cures on you when you are under 15%% health.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message( - Chat::White, + std::vector description = + { + "Threshold of your own health when bots will stop casting the chosen spell type" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set Fast Heals to be stopped at 65% health:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 65", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 65", + sep->arg[0], + BotSpellTypes::FastHeals ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current Cure settings:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::Cure ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -112,18 +96,8 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -133,20 +107,8 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -168,8 +130,8 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { typeValue = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 150) { - c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); return; } @@ -196,18 +158,18 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your current min threshold for {}s is {}%%.", + "Your [{}] minimum hold is currently [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMinThreshold(spellType) ).c_str() ); } else { - c->SetSpellMinThreshold(spellType, typeValue); + c->SetSpellHold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your min threshold for {}s was set to {}%%.", + "Your [{}] minimum hold was set to [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMinThreshold(spellType) ).c_str() From df6c6c3ea0ab3ecc3e3c94b7880a25b27b92fd36 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:50:21 -0600 Subject: [PATCH 040/394] add aehateline spell type --- common/spdat.cpp | 1 + common/spdat.h | 3 ++- zone/bot.cpp | 49 ++++++++++++++++++++++---------------- zone/bot_commands/cast.cpp | 2 +- zone/botspellsai.cpp | 1 + zone/mob.cpp | 8 +++++++ 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index c4086c0ac3..bef0507df5 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2844,6 +2844,7 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { case BotSpellTypes::PBAENuke: case BotSpellTypes::Lull: case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index 1c3bcf52cc..3f87672b4d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -709,6 +709,7 @@ namespace BotSpellTypes constexpr uint16 PetDamageShields = 53; constexpr uint16 PetResistBuffs = 54; constexpr uint16 HateLine = 55; + constexpr uint16 AEHateLine = 56; // Command Spell Types constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic @@ -726,7 +727,7 @@ namespace BotSpellTypes constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::HateLine; // Do not remove this, increment as needed + constexpr uint16 END = BotSpellTypes::AEHateLine; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } diff --git a/zone/bot.cpp b/zone/bot.cpp index fedaa32642..4b632b7bef 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10576,48 +10576,50 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, return 20; case BotSpellTypes::Mez: return 21; - case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return 22; - case BotSpellTypes::AEDispel: + case BotSpellTypes::HateLine: return 23; - case BotSpellTypes::Dispel: + case BotSpellTypes::AEDispel: return 24; - case BotSpellTypes::AEDebuff: + case BotSpellTypes::Dispel: return 25; - case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: return 26; - case BotSpellTypes::AESnare: + case BotSpellTypes::Debuff: return 27; - case BotSpellTypes::Snare: + case BotSpellTypes::AESnare: return 28; - case BotSpellTypes::AESlow: + case BotSpellTypes::Snare: return 29; - case BotSpellTypes::Slow: + case BotSpellTypes::AESlow: return 30; - case BotSpellTypes::AERoot: + case BotSpellTypes::Slow: return 31; - case BotSpellTypes::Root: + case BotSpellTypes::AERoot: return 32; - case BotSpellTypes::AEDoT: + case BotSpellTypes::Root: return 33; - case BotSpellTypes::DOT: + case BotSpellTypes::AEDoT: return 34; - case BotSpellTypes::AEStun: + case BotSpellTypes::DOT: return 35; - case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEStun: return 36; - case BotSpellTypes::AENukes: + case BotSpellTypes::PBAENuke: return 37; - case BotSpellTypes::AERains: + case BotSpellTypes::AENukes: return 38; - case BotSpellTypes::Stun: + case BotSpellTypes::AERains: return 39; - case BotSpellTypes::Nuke: + case BotSpellTypes::Stun: return 40; - case BotSpellTypes::InCombatBuff: + case BotSpellTypes::Nuke: return 41; - case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::InCombatBuff: return 42; + case BotSpellTypes::InCombatBuffSong: + return 43; default: return 0; } @@ -11027,6 +11029,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Escape: case BotSpellTypes::HateRedux: case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: case BotSpellTypes::InCombatBuff: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: @@ -11404,6 +11407,10 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo } break; + case BotSpellTypes::AEHateLine: + if (!m->IsNPC()) { + continue; + } default: break; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index eb1361d8d8..fb7f97209a 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -407,7 +407,7 @@ void bot_command_cast(Client* c, const Seperator* sep) TODO bot rewrite - FIX: Depart Group Cures, Precombat - NEED TO CHECK: precombat, AE Dispel, AE Lifetap + NEED TO CHECK: precombat */ if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index d3123bf86d..35cec06d1a 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2076,6 +2076,7 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) case BotSpellTypes::AELifetap: case BotSpellTypes::AERoot: case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEHateLine: return RuleI(Bots, PercentChanceToCastAEs); case BotSpellTypes::GroupHeals: case BotSpellTypes::GroupCompleteHeals: diff --git a/zone/mob.cpp b/zone/mob.cpp index 6d04c99848..a455932e61 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8901,6 +8901,9 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::HateLine: spellTypeName = "Hate Line"; break; + case BotSpellTypes::AEHateLine: + spellTypeName = "AE Hate Line"; + break; case BotSpellTypes::Lull: spellTypeName = "Lull"; break; @@ -9119,6 +9122,9 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::HateLine: spellTypeName = "hateline"; break; + case BotSpellTypes::AEHateLine: + spellTypeName = "aehateline"; + break; case BotSpellTypes::Lull: spellTypeName = "lull"; break; @@ -9236,6 +9242,7 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case BotSpellTypes::Dispel: case BotSpellTypes::AEFear: case BotSpellTypes::Fear: + case BotSpellTypes::AEHateLine: return true; case BotSpellTypes::Mez: case BotSpellTypes::AEMez: @@ -9541,6 +9548,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return 100; case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: From 156c9285216940cd5484b82c7dfa37105d096750 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:50:56 -0600 Subject: [PATCH 041/394] debug cleanup --- zone/bot.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4b632b7bef..d7ffd8b28f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2757,28 +2757,6 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo if (!GetCombatRoundForAlerts()) { SetCombatRoundForAlerts(); - LogTestDebugDetail("{} says, 'I'm {} {}. I am currently {} away {} to be between [{} - {}] away. MMR is {}.'" - , GetCleanName() - , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) - , tar->GetCleanName() - , tar_distance - , (tar_distance <= melee_distance ? "but only needed" : "but need to be") - , melee_distance_min - , melee_distance - , melee_distance_max - ); //deleteme - LogTestDebugDetail("{} says, 'My stance is {} #{}, I am {} taunting. I am set to {} {}, {} at MMR, distanceranged {}, sml {} [{}]'" - , GetCleanName() - , Stance::GetName(GetBotStance()) - , GetBotStance() - , (IsTaunting() ? "currently" : "not") - , (BehindMob() ? "stay behind" : "not stay behind") - , tar->GetCleanName() - , (GetMaxMeleeRange() ? "I stay" : "I do not stay") - , GetBotDistanceRanged() - , GetStopMeleeLevel() - , ((GetLevel() <= GetStopMeleeLevel()) ? "disabled" : "enabled") - ); //deleteme } if (tar_distance <= melee_distance) { From 6846bdc56c9e9a813d6276b1190e0c841f5fb698 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:09:51 -0600 Subject: [PATCH 042/394] commanded spell fixes. All should be working now minus depart --- zone/bot.cpp | 75 ++++++++++++++++++++++---------------- zone/bot_commands/cast.cpp | 6 --- zone/botspellsai.cpp | 9 ++--- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index d7ffd8b28f..0bc474139f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9383,12 +9383,12 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { } bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { - if (!tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme - return false; - } - if (doPrechecks) { + if (!tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + if (spells[spell_id].target_type == ST_Self && tar != this) { if (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse)) { //tar = this; @@ -9404,7 +9404,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } } - LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "nobody")); //deleteme if (!IsValidSpell(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme @@ -9412,26 +9412,17 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (IsFeared() || IsSilenced() || IsAmnesiad()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if ( - spells[spell_id].target_type == ST_Self - && tar != this && - (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) - ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } @@ -9450,11 +9441,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (this == tar && IsSacrificeSpell(spell_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme - return false; - } - if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { LogBotPreChecks("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; @@ -9506,11 +9492,39 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (SpellTypeRequiresTarget(spellType) && !tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + + if ( + spells[spell_id].target_type == ST_Self + && tar != this && + (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (this == tar && IsSacrificeSpell(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!CanCastSpellType(spellType, spell_id, tar)) { + return false; + } + + if (IsCommandedSpell()) { //stop checks here for commanded spells + return true; + } + if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9526,13 +9540,13 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9549,11 +9563,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - if (!CanCastSpellType(spellType, spell_id, tar)) { - return false; - } - return true; } @@ -11389,6 +11398,8 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo if (!m->IsNPC()) { continue; } + + break; default: break; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index fb7f97209a..ef5074e89b 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -403,12 +403,6 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - /* - TODO bot rewrite - - FIX: Depart - Group Cures, Precombat - NEED TO CHECK: precombat - */ if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 35cec06d1a..a86b8e6c81 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1042,14 +1042,11 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa IsGroupSpell(botSpellList[i].spellid) && !IsTGBCompatibleSpell(botSpellList[i].spellid) && !botCaster->IsInGroupOrRaid(tar, true) - ) { + ) { continue; } - - if ( - (!botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && SpellTypeRequiresCastChecks(spellType))) && - (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) - ) { + if (spellType == debugSpellType) { LogTestDebugDetail("{} - #{}: [{} #{}] - {} says, '{} #{} - Passed TGB checks.'", __FILE__, __LINE__, botCaster->GetSpellTypeNameByID(spellType), spellType, botCaster->GetCleanName(), spells[botSpellList[i].spellid].name, botSpellList[i].spellid); }; //deleteme + if (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } From d0033d47863d290a4ddb92b9e35ce1f286e3f345 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 00:56:55 -0600 Subject: [PATCH 043/394] add check for self on isingrouporraid --- zone/mob.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/mob.cpp b/zone/mob.cpp index a455932e61..da438671df 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9777,6 +9777,10 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { return false; } + if (this == other) { + return true; + } + auto* r = GetRaid(); auto* rO = other->GetRaid(); From ceeb08386493555860e3da86cbde30d52ba2dcf4 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 22:15:02 -0600 Subject: [PATCH 044/394] Allow bots to bypass los checks for positioning if no detrimental types allowed --- common/spdat.cpp | 39 +++++++++++++++++++++++++++++++++++++++ common/spdat.h | 1 + zone/bot.cpp | 20 +++++++------------- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index bef0507df5..32782c3c27 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3309,3 +3309,42 @@ bool IsCommandedSpellType(uint16 spellType) { return false; } + +bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls) { + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Root: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Mez: + //case BotSpellTypes::Charm: // commanded + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::HateRedux: + //case BotSpellTypes::Fear: // commanded + case BotSpellTypes::Stun: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEMez: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + //case BotSpellTypes::AEFear: // commanded + case BotSpellTypes::AEDispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + // case BotSpellTypes::Lull: // commanded + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: + return true; + default: + return false; + } + + return false; +} diff --git a/common/spdat.h b/common/spdat.h index 3f87672b4d..9d7c0910a8 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -752,6 +752,7 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); +bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only diff --git a/zone/bot.cpp b/zone/bot.cpp index 0bc474139f..28f9cb5494 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11329,14 +11329,14 @@ bool Bot::RequiresLoSForPositioning() { if (GetLevel() < GetStopMeleeLevel()) { return true; } - else if (GetClass() == Class::Bard) { - return false; - } - else if (GetClass() == Class::Cleric) { //TODO bot rewrite - add check to see if spell requires los - return false; + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (BOT_SPELL_TYPES_DETRIMENTAL(i) && !GetSpellHold(i)) { + return true; + } } - return true; + return false; } bool Bot::HasRequiredLoSForPositioning(Mob* tar) { @@ -11344,13 +11344,7 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } - if (GetClass() == Class::Cleric) { //add check to see if spell requires los - return true; - } - else if (GetClass() == Class::Bard && GetLevel() >= GetStopMeleeLevel()) { - return true; - } - if (!DoLosChecks(this, tar)) { + if (RequiresLoSForPositioning() && !DoLosChecks(this, tar)) { return false; } From 12f703678e709381bb81dfbac18d2788b79d9d21 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 22:15:34 -0600 Subject: [PATCH 045/394] add invalid spell id cleanup to bot spell list inserts --- common/database/database_update_manifest_bots.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 72e26142c2..235c160b35 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -1065,6 +1065,13 @@ VALUES (3011, 25555, 112, 86, 90), (3011, 28632, 112, 91, 95), (3011, 34662, 112, 96, 254); + +DELETE +FROM bot_spells_entries +WHERE NOT EXISTS +(SELECT * +FROM spells_new +WHERE bot_spells_entries.spell_id = spells_new.id); )" }, ManifestEntry{ @@ -1163,6 +1170,13 @@ VALUES (3005, 34752, 55, 99, 254), (3005, 34753, 55, 99, 254), (3005, 34751, 55, 99, 254); + +DELETE +FROM bot_spells_entries +WHERE NOT EXISTS +(SELECT * +FROM spells_new +WHERE bot_spells_entries.spell_id = spells_new.id); )" } // -- template; copy/paste this when you need to create a new entry From dc0819c2c60d52ce31decfdc55141046015934c9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 22:33:30 -0600 Subject: [PATCH 046/394] misc cleanup --- common/spdat.cpp | 23 ----------------------- common/version.h | 2 +- zone/bot.cpp | 8 ++++---- zone/bot.h | 1 - zone/entity.h | 2 +- 5 files changed, 6 insertions(+), 30 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 32782c3c27..50b7651f4a 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -678,29 +678,6 @@ bool IsAnyNukeOrStunSpell(uint16 spell_id) { } bool IsAnyAESpell(uint16 spell_id) { - //if ( - // spells[spell_id].target_type == ST_Target || - // spells[spell_id].target_type == ST_Self || - // spells[spell_id].target_type == ST_Animal || - // spells[spell_id].target_type == ST_Undead || - // spells[spell_id].target_type == ST_Summoned || - // spells[spell_id].target_type == ST_Tap || - // spells[spell_id].target_type == ST_Pet || - // spells[spell_id].target_type == ST_Corpse || - // spells[spell_id].target_type == ST_Plant || - // spells[spell_id].target_type == ST_Giant || - // spells[spell_id].target_type == ST_Dragon || - // spells[spell_id].target_type == ST_HateList || - // spells[spell_id].target_type == ST_LDoNChest_Cursed || - // spells[spell_id].target_type == ST_Muramite || - // spells[spell_id].target_type == ST_SummonedPet || - // spells[spell_id].target_type == ST_TargetsTarget || - // spells[spell_id].target_type == ST_PetMaster //|| - // //spells[spell_id].target_type == ST_AEBard //TODO needed? - //) { - // return false; - //} - if (IsAESpell(spell_id) || IsPBAENukeSpell(spell_id) || IsPBAESpell(spell_id) || IsAERainSpell(spell_id) || IsAERainNukeSpell(spell_id) || IsAEDurationSpell(spell_id)) { return true; } diff --git a/common/version.h b/common/version.h index 800ee365d6..92982a1faa 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9284 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9051 //TODO update as needed +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9051 //TODO bot rewrite - update as needed #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 28f9cb5494..ae915ed806 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1613,7 +1613,7 @@ bool Bot::Process() } ScanCloseMobProcess(); - CheckScanCloseMobsMovingTimer(); // TODO bot rewrite -- necessary for bots? + CheckScanCloseMobsMovingTimer(); SpellProcess(); if (tic_timer.Check()) { @@ -1661,7 +1661,7 @@ bool Bot::Process() } } - if (viral_timer.Check()) { // TODO bot rewrite -- necessary for bots? + if (viral_timer.Check()) { VirusEffectProcess(); } @@ -7160,7 +7160,7 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType) { +bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType) { if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, caster->GetClass())) { LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); @@ -10927,7 +10927,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { } if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), BotAISpellRange, spellType)) { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), spellType)) { return result; } } diff --git a/zone/bot.h b/zone/bot.h index 2360d73b4d..8d1a600472 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -46,7 +46,6 @@ constexpr uint32 MAG_EPIC_1_0 = 28034; extern WorldServer worldserver; -constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this constexpr int NegativeItemReuse = -1; // Unlinked timer for items constexpr uint8 SumWater = 1; diff --git a/zone/entity.h b/zone/entity.h index 814a5feb5c..b55c1c7a63 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -628,7 +628,7 @@ class EntityList Client* GetBotOwnerByBotID(const uint32 bot_id); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); - bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate + bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); From 08387560b31afece3dc6fe2a66008cc6ffa5b2cd Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:35:21 -0600 Subject: [PATCH 047/394] implement depart to use spell lists --- zone/bot_command.cpp | 2 +- zone/bot_commands/depart.cpp | 312 ++++++++++++++++++++++++++++------- 2 files changed, 254 insertions(+), 60 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 650f4a5e86..1b00d701a6 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1290,7 +1290,7 @@ int bot_command_init(void) bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || bot_command_add("defaultsettings", "Restores a bot back to default settings", AccountStatus::Player, bot_command_default_settings) || bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || - bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || //TODO bot rewrite - deleteme + bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || bot_command_add("enforcespellsettings", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || bot_command_add("findaliases", "Find available aliases for a bot command", AccountStatus::Player, bot_command_find_aliases) || bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", AccountStatus::Player, bot_command_follow) || diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index d9b09d2d4e..80c3c4e221 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -2,102 +2,296 @@ void bot_command_depart(Client* c, const Seperator* sep) { - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) { - return; - } - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Depart); + std::vector description = + { + "Tells bots to list their port locations or port to a specific location" + }; + + std::vector notes = + { + "- This will interrupt any spell currently being cast by bots told to use the command.", + "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells" + }; + + std::vector example_format = + { + fmt::format( + "{} [list | zone shortname] [optional: single | group | ae] [actionable, default: spawned]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To tell everyone to list their portable locations:", + fmt::format( + "{} list spawned", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To tell all bots to port to Nexus:", + fmt::format( + "{} nexus spawned", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To tell Druidbot to single target port to Butcher:", + fmt::format( + "{} butcher single byname Druidbot", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; bool single = false; + bool single = false; + bool group = true; + bool ae = false; std::string single_arg = sep->arg[2]; - + bool list = false; + std::string destination = sep->arg[1]; + int ab_arg = 2; + if (!single_arg.compare("single")) { + ++ab_arg; single = true; + group = false; + } + else if (!single_arg.compare("group")) { + ++ab_arg; + single = false; + group = true; + } + else if (!single_arg.compare("ae")) { + ++ab_arg; + single = false; + group = false; + ae = true; } - std::string destination = sep->arg[1]; + if (!destination.compare("list") || destination.empty()) { + list = true; - if (!destination.compare("list")) { - Bot* my_druid_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Druid); - Bot* my_wizard_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Wizard); - - if ( - (!my_druid_bot && !my_wizard_bot) || - (my_druid_bot && !my_druid_bot->IsInGroupOrRaid(c)) || - (my_wizard_bot && !my_wizard_bot->IsInGroupOrRaid(c)) - ) { - c->Message(Chat::Yellow, "No compatible bots found for %s.", sep->arg[0]); - return; + if (destination.empty()) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); } + } - helper_command_depart_list(c, my_druid_bot, my_wizard_bot, local_list, single); + Mob* tar = c->GetTarget(); - return; + if (!tar) { + tar = c; + } + + std::string argString = sep->arg[ab_arg]; + + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } - else if (destination.empty()) { - c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); + std::list sbl; + + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - my_bot = nullptr; - sbl.clear(); - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - bool cast_success = false; + sbl.remove(nullptr); - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToDepart(); + BotSpell botSpell; + botSpell.SpellId = 0; + botSpell.SpellIndex = 0; + botSpell.ManaCost = 0; - if (helper_spell_check_fail(local_entry)) { - continue; - } + bool isSuccess = false; + std::map> listZones; - if (local_entry->single != single) { + for (auto bot_iter : sbl) { + if (!bot_iter->IsInGroupOrRaid(tar, !single)) { continue; } - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) { + if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; } - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); + std::list botSpellListItr = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Teleport, tar); - if (!target_mob) - continue; + for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + if (!IsValidSpell(itr->SpellId)) { + continue; + } - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); + if ( + (single && spells[itr->SpellId].target_type != ST_Target) || + (group && !IsGroupSpell(itr->SpellId)) || + (ae && !IsAnyAESpell(itr->SpellId)) + ) { + continue; + } - if (!my_bot) { - continue; - } + if (list) { + auto it = listZones.find(spells[itr->SpellId].teleport_zone); - if (my_bot->BotPassiveCheck()) { - continue; - } + if (it != listZones.end()) { + const auto& [val1, val2] = it->second; - if (!my_bot->IsInGroupOrRaid(c)) { - continue; - } + if (val1 == spells[itr->SpellId].target_type && val2 == bot_iter->GetClass()) { + continue; + } + } - if (local_entry->spell_id != 0 && my_bot->GetMana() < spells[local_entry->spell_id].mana) { - continue; - } + Bot::BotGroupSay( + bot_iter, + fmt::format( + "I can port you to {}.", + Saylink::Silent( + fmt::format( + "{} {} {} byname {}", + sep->arg[0], + spells[itr->SpellId].teleport_zone, + (spells[itr->SpellId].target_type == ST_Target ? "single" : (IsGroupSpell(itr->SpellId) ? "group" : "ae")), + bot_iter->GetCleanName() + ).c_str() + , ZoneLongName(ZoneID(spells[itr->SpellId].teleport_zone))) + ).c_str() + ); + + listZones.insert({ spells[itr->SpellId].teleport_zone, {spells[itr->SpellId].target_type, bot_iter->GetClass()} }); + + continue; + } + + if (destination.compare(spells[itr->SpellId].teleport_zone)) { + continue; + } + + bot_iter->SetCommandedSpell(true); + + if (!IsValidSpellAndLoS(itr->SpellId, bot_iter->HasLoS())) { + continue; + } + + if (IsInvulnerabilitySpell(itr->SpellId)) { + tar = bot_iter; //target self for invul type spells + } - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); + if (bot_iter->IsCommandedSpell() && bot_iter->IsCasting()) { + Bot::BotGroupSay( + bot_iter, + fmt::format( + "Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.", + bot_iter->CastingSpellID() ? spells[bot_iter->CastingSpellID()].name : "my spell", + bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), + spells[itr->SpellId].name, + tar->GetCleanName() + ).c_str() + ); - break; + bot_iter->InterruptSpell(); + } + + if (bot_iter->CastSpell(itr->SpellId, tar->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1)) { + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(BotSpellTypes::Teleport)) { + bot_iter->SetCastedSpellType(UINT16_MAX); + } + else { + bot_iter->SetCastedSpellType(BotSpellTypes::Teleport); + } + + if (bot_iter->GetClass() != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + Bot::BotGroupSay( + bot_iter, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(itr->SpellId), + bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), + (tar == bot_iter ? "myself" : tar->GetCleanName()) + ).c_str() + ); + } + + isSuccess = true; + } + + bot_iter->SetCommandedSpell(false); + + if (isSuccess) { + return; + } + else { + continue; + } + } } - if (!cast_success) { - helper_no_available_bots(c, my_bot); + if ( + (list && listZones.empty()) || + (!list && !isSuccess) + ) { + c->Message( + Chat::Yellow, + fmt::format( + "No bots are capable of that on {}. Be sure they are in the same group, raid or raid group if necessary.", + tar ? tar->GetCleanName() : "you" + ).c_str() + ); } } From 867771e76bd5259b550737675d229dc639ac66f2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:35:35 -0600 Subject: [PATCH 048/394] add default spawned note to ^cast --- zone/bot_commands/cast.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index ef5074e89b..9808215419 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -17,11 +17,11 @@ void bot_command_cast(Client* c, const Seperator* sep) std::vector example_format = { fmt::format( - "{} [Type Shortname] [actionable]" + "{} [Type Shortname] [actionable, default: spawned]" , sep->arg[0] ), fmt::format( - "{} [Type ID] [actionable]" + "{} [Type ID] [actionable, default: spawned]" , sep->arg[0] ) }; From 962e2d80b9165009c3994e0863570c072fc087f1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:36:27 -0600 Subject: [PATCH 049/394] fix AA loading and expansionbitmask saving/loading --- zone/bot.cpp | 7 +++---- zone/bot.h | 1 + zone/bot_database.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ae915ed806..18e2fb0482 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -248,12 +248,12 @@ Bot::Bot( database.botdb.LoadTimers(this); - LoadAAs(); - LoadDefaultBotSettings(); database.botdb.LoadBotSettings(this); + LoadAAs(); + if (database.botdb.LoadBuffs(this)) { //reapply some buffs uint32 buff_count = GetMaxBuffSlots(); @@ -1258,7 +1258,6 @@ int32 Bot::GenerateBaseHitPoints() { } void Bot::LoadAAs() { - aa_ranks.clear(); int id = 0; @@ -10213,7 +10212,7 @@ void Bot::LoadDefaultBotSettings() { uint8 botStance = GetBotStance(); - for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, botStance)); LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); //deleteme } diff --git a/zone/bot.h b/zone/bot.h index 8d1a600472..8426a9b7fc 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -142,6 +142,7 @@ namespace BotBaseSettings { constexpr uint16 HPWhenToMed = 12; constexpr uint16 ManaWhenToMed = 13; + constexpr uint16 START_ALL = ExpansionBitmask; constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players constexpr uint16 END = BotBaseSettings::ManaWhenToMed; // Increment as needed }; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 60f389a9b0..a4d4c83858 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2294,7 +2294,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) if (m->IsBot()) { uint8 botStance = m->CastToBot()->GetBotStance(); - for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, botStance)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, From 338985634f0150d7d7c4bdc61402a828cc07786b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:46:17 -0600 Subject: [PATCH 050/394] Add taunting update on stance change when necessary --- zone/bot_commands/bot.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index f9a3ecfe91..b91d5a0a78 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1404,6 +1404,25 @@ void bot_command_stance(Client *c, const Seperator *sep) bot_iter->SetBotStance(value); bot_iter->LoadDefaultBotSettings(); database.botdb.LoadBotSettings(bot_iter); + + if ( + (bot_iter->GetClass() == Class::Warrior || bot_iter->GetClass() == Class::Paladin || bot_iter->GetClass() == Class::ShadowKnight) && + (bot_iter->GetBotStance() == Stance::Aggressive) + ) { + bot_iter->SetTaunting(true); + + if (bot_iter->HasPet() && bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) { + bot_iter->GetPet()->CastToNPC()->SetTaunting(true); + } + } + else { + bot_iter->SetTaunting(false); + + if (bot_iter->HasPet() && bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) { + bot_iter->GetPet()->CastToNPC()->SetTaunting(false); + } + } + bot_iter->Save(); ++success_count; } From ea84fd75da18e0bc594f099fdf0a35f3b0429316 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:46:50 -0600 Subject: [PATCH 051/394] Clean up and fix any melee attacks to line up with clients --- zone/bot.cpp | 149 +++++++++++++++++++++++++++------------------------ zone/bot.h | 3 +- 2 files changed, 79 insertions(+), 73 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 18e2fb0482..95067a89ea 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1779,29 +1779,43 @@ void Bot::BotMeditate(bool isSitting) { } } -void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { - if (!TargetValidation(other)) { return; } +bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { + if (!other || !IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { + return false; + } + + if ( + !GetPullingFlag() && + ( + (GetBotStance() != Stance::Aggressive && GetBotStance() != Stance::Burn && GetBotStance() != Stance::AEBurn) && + other->GetHPRatio() > 99.0f + ) + ) { + return false; + } if (!CanDoubleAttack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { LogCombatDetail("Bot ranged attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); - Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); - return; + return false; } const auto rangedItem = GetBotItem(EQ::invslot::slotRange); const EQ::ItemData* RangeWeapon = nullptr; + if (rangedItem) { RangeWeapon = rangedItem->GetItem(); } if (!RangeWeapon) { - return; + return false; } const auto ammoItem = GetBotItem(EQ::invslot::slotAmmo); const EQ::ItemData* Ammo = nullptr; - if (ammoItem) + + if (ammoItem) { Ammo = ammoItem->GetItem(); + } // Bow requires arrows if ( @@ -1827,7 +1841,7 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { } } - return; + return false; } LogCombatDetail("Ranged attacking [{}] with {} [{}] ([{}]){}{}{}", @@ -1840,10 +1854,6 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? std::to_string(Ammo->ID) : "") ); - if (!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { - return; - } - SendItemAnimation(other, Ammo, (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? EQ::skills::SkillArchery : EQ::skills::SkillThrowing)); if (RangeWeapon->ItemType == EQ::item::ItemTypeBow) { DoArcheryAttackDmg(other, rangedItem, ammoItem); // watch @@ -1861,7 +1871,7 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { ) ) { ammoItem->SetCharges((ammoItem->GetCharges() - 1)); - LogCombat("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); + LogCombatDetail("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); if (ammoItem->GetCharges() < 1) { RemoveBotItemBySlot(EQ::invslot::slotAmmo); @@ -1869,10 +1879,10 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { } } else if (!consumes_ammo) { - LogCombat("Archery Ammo Consumption is disabled."); + LogCombatDetail("Archery Ammo Consumption is disabled."); } else { - LogCombat("Endless Quiver prevented Ammo Consumption."); + LogCombatDetail("Endless Quiver prevented Ammo Consumption."); } } else { @@ -1880,7 +1890,7 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { // Consume Ammo, unless Ammo Consumption is disabled if (RuleB(Bots, BotThrowingConsumesAmmo)) { ammoItem->SetCharges((ammoItem->GetCharges() - 1)); - LogCombat("Consumed Throwing Ammo from slot {}.", EQ::invslot::slotAmmo); + LogCombatDetail("Consumed Throwing Ammo from slot {}.", EQ::invslot::slotAmmo); if (ammoItem->GetCharges() < 1) { RemoveBotItemBySlot(EQ::invslot::slotAmmo); @@ -1888,45 +1898,51 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { } } else { - LogCombat("Throwing Ammo Consumption is disabled."); + LogCombatDetail("Throwing Ammo Consumption is disabled."); } } CommonBreakInvisibleFromCombat(); + + return true; } bool Bot::CheckBotDoubleAttack(bool tripleAttack) { //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) - uint32 bonusGiveDA = (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack); - // If you don't have the double attack skill, return - if (!GetSkill(EQ::skills::SkillDoubleAttack) && !(GetClass() == Class::Bard || GetClass() == Class::Beastlord)) + uint32 bonus_give_double_attack = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; + + if (!GetSkill(EQ::skills::SkillDoubleAttack) && !bonus_give_double_attack) { return false; + } - // You start with no chance of double attacking float chance = 0.0f; uint16 skill = GetSkill(EQ::skills::SkillDoubleAttack); - int32 bonusDA = (aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance); - //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. - if (skill) - chance = ((float(skill + GetLevel()) * (float(100.0f + bonusDA + bonusGiveDA) / 100.0f)) / 500.0f); - else - chance = ((float(bonusGiveDA) * (float(100.0f + bonusDA) / 100.0f)) / 100.0f); - //Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM. - //A reasonable forumla would then be TA = 20% * chance - //AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack) - //Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect. - if (tripleAttack) { - // Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp] - int32 triple_bonus = (spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance); - chance *= 0.2f; //Baseline chance is 20% of your double attack chance. - chance *= (float(100.0f + triple_bonus) / 100.0f); //Apply modifiers. + int32 bonus_double_attack = 0; + if ((GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (!HasTwoHanderEquipped())) { + LogCombatDetail("Knight class without a 2 hand weapon equipped = No DA Bonus!"); + } + else { + bonus_double_attack = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; } - if (zone->random.Real(0, 1) < chance) - return true; + //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. + if (skill) { + chance = (float(skill + GetLevel()) * (float(100.0f + bonus_double_attack + bonus_give_double_attack) / 100.0f)) / 500.0f; + } + else { + chance = (float(bonus_give_double_attack + bonus_double_attack) / 100.0f); + } - return false; + LogCombatDetail( + "skill [{}] bonus_give_double_attack [{}] bonus_double_attack [{}] chance [{}]", + skill, + bonus_give_double_attack, + bonus_double_attack, + chance + ); + + return zone->random.Roll(chance); } bool Bot::CheckTripleAttack() @@ -2200,17 +2216,16 @@ void Bot::AI_Process() } } else { - if (atCombatRange && IsBotRanged()){ - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - - TryRangedAttack(tar); - - if (!TargetValidation(tar)) { return; } + //TODO bot rewrite - add casting AI to this + if (atCombatRange && IsBotRanged() && ranged_timer.Check(false)) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - if (CheckDoubleRangedAttack()) { + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { BotRangedAttack(tar, true); } + ranged_timer.Start(); + return; } } @@ -2255,13 +2270,11 @@ void Bot::AI_Process() } if (IsBotRanged() && ranged_timer.Check(false)) { - TryRangedAttack(tar); - - if (!TargetValidation(tar)) { return; } - - if (CheckDoubleRangedAttack()) { + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { BotRangedAttack(tar, true); } + + ranged_timer.Start(); } else if (!IsBotRanged() && GetLevel() < stopMeleeLevel) { if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) { @@ -2551,7 +2564,7 @@ void Bot::DoAttackRounds(Mob* target, int hand) { (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack) > 0; if (candouble) { - if (CastToClient()->CheckDoubleAttack()) { + if (CheckBotDoubleAttack()) { Attack(target, hand, false, false); if (hand == EQ::invslot::slotPrimary) { @@ -2593,8 +2606,8 @@ void Bot::DoAttackRounds(Mob* target, int hand) { if (hand == EQ::invslot::slotPrimary && CanThisClassTripleAttack()) { if (CheckTripleAttack()) { Attack(target, hand, false, false); - int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance + - itembonuses.FlurryChance; + + int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; if (flurry_chance && zone->random.Roll(flurry_chance)) { Attack(target, hand, false, false); @@ -2602,7 +2615,15 @@ void Bot::DoAttackRounds(Mob* target, int hand) { if (zone->random.Roll(flurry_chance)) { Attack(target, hand, false, false); } - //MessageString(Chat::NPCFlurry, YOU_FLURRY); //TODO bot rewrite - add output to others hits with flurry message + + if (GetOwner()) { + GetOwner()->MessageString( + Chat::NPCFlurry, + NPC_FLURRY, + GetCleanName(), + target->GetCleanName() + ); //TODO bot rewrite - add output to others hits with flurry message + } } } } @@ -2610,22 +2631,6 @@ void Bot::DoAttackRounds(Mob* target, int hand) { } } -bool Bot::TryRangedAttack(Mob* tar) { - - if (IsBotRanged() && ranged_timer.Check(false)) { - - if (!TargetValidation(tar)) { return false; } - - if (GetPullingFlag() || GetTarget()->GetHPRatio() <= 99.0f) { - BotRangedAttack(tar); - } - - return true; - } - - return false; -} - bool Bot::TryFacingTarget(Mob* tar) { if (!IsSitting() && !IsFacingMob(tar)) { FaceTarget(tar); @@ -11318,8 +11323,10 @@ void Bot::DoCombatPositioning( bool Bot::CheckDoubleRangedAttack() { int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; - if (chance && zone->random.Roll(chance)) + + if (chance && zone->random.Roll(chance)) { return true; + } return false; } diff --git a/zone/bot.h b/zone/bot.h index 8426a9b7fc..c8f6e4a8eb 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -979,14 +979,13 @@ class Bot : public NPC { // Try Combat Methods bool TryEvade(Mob* tar); bool TryFacingTarget(Mob* tar); - bool TryRangedAttack(Mob* tar); bool TryPursueTarget(float leash_distance, glm::vec3& Goal); bool TryMeditate(); bool TryAutoDefend(Client* bot_owner, float leash_distance); bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); bool TryBardMovementCasts(); - void BotRangedAttack(Mob* other, bool CanDoubleAttack = false); + bool BotRangedAttack(Mob* other, bool CanDoubleAttack = false); bool CheckDoubleRangedAttack(); // Public "Refactor" Methods From af5dbbe932b5af26a8a3f16bf413a2bc0fb04743 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:05:50 -0600 Subject: [PATCH 052/394] remove unnecessary messages on silence /block for bots --- zone/bot.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 95067a89ea..450315514b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5664,13 +5664,13 @@ bool Bot::CastSpell( LogSpellsDetail("Spell casting canceled: not able to cast now. Valid? [{}] casting [{}] waiting? [{}] spellend? [{}] stunned? [{}] feared? [{}] mezed? [{}] silenced? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); - if (IsSilenced() && !IsDiscipline(spell_id)) { - MessageString(Chat::White, SILENCED_STRING); - } - - if (IsAmnesiad() && IsDiscipline(spell_id)) { - MessageString(Chat::White, MELEE_SILENCE); - } + //if (IsSilenced() && !IsDiscipline(spell_id)) { + // MessageString(Chat::White, SILENCED_STRING); + //} + // + //if (IsAmnesiad() && IsDiscipline(spell_id)) { + // MessageString(Chat::White, MELEE_SILENCE); + //} if (casting_spell_id) { AI_Bot_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); @@ -5681,7 +5681,7 @@ bool Bot::CastSpell( } if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - MessageString(Chat::White, SPELL_WOULDNT_HOLD); + //MessageString(Chat::White, SPELL_WOULDNT_HOLD); if (casting_spell_id) { AI_Bot_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); } From 3034bd576218b7a9db9859a4fdd2ad6f4b694dff Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:31:39 -0600 Subject: [PATCH 053/394] fix changes made to mercs by mistake --- zone/merc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/merc.cpp b/zone/merc.cpp index d66501efad..c54585f9eb 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1500,7 +1500,7 @@ bool Merc::AI_IdleCastCheck() { bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { - if (BOT_SPELL_TYPES_DETRIMENTAL(iSpellTypes)) { + if ((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { //according to live, you can buff and heal through walls... //now with PCs, this only applies if you can TARGET the target, but // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. @@ -1569,7 +1569,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon float dist2 = 0; - if (mercSpell.type == SpellType_Escape) { + if (mercSpell.type & SpellType_Escape) { dist2 = 0; } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); From a22502a13dfff550b851a18bb77498d8a41a5f16 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:33:21 -0600 Subject: [PATCH 054/394] rename BOT_SPELL_TYPE functions --- common/spdat.cpp | 10 +++++----- common/spdat.h | 8 ++++---- zone/bot.cpp | 14 +++++++------- zone/bot_commands/cast.cpp | 8 ++++---- zone/bot_commands/depart.cpp | 2 +- zone/botspellsai.cpp | 6 +++--- zone/mob.cpp | 8 ++++---- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 50b7651f4a..5c8064f8c5 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2791,7 +2791,7 @@ bool IsLichSpell(uint16 spell_id) return false; } -bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { +bool IsBotSpellTypeDetrimental(uint16 spellType, uint8 cls) { switch (spellType) { case BotSpellTypes::Nuke: case BotSpellTypes::Root: @@ -2830,7 +2830,7 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { return false; } -bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { +bool IsBotSpellTypeBeneficial(uint16 spellType, uint8 cls) { switch (spellType) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: @@ -2879,7 +2879,7 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { return false; } -bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { +bool IsBotSpellTypeOtherBeneficial(uint16 spellType) { switch (spellType) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: @@ -2922,7 +2922,7 @@ bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { return false; } -bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { +bool IsBotSpellTypeInnate(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: case BotSpellTypes::AERains: @@ -2955,7 +2955,7 @@ bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { } bool IsBotSpellType(uint16 spellType) { - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && BOT_SPELL_TYPES_BENEFICIAL(spellType) && BOT_SPELL_TYPES_INNATE(spellType)) { + if (IsBotSpellTypeDetrimental(spellType) && IsBotSpellTypeBeneficial(spellType) && IsBotSpellTypeInnate(spellType)) { return true; } diff --git a/common/spdat.h b/common/spdat.h index 9d7c0910a8..adf9c05e47 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -736,10 +736,10 @@ const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellT const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); -bool BOT_SPELL_TYPES_DETRIMENTAL (uint16 spellType, uint8 cls = 0); -bool BOT_SPELL_TYPES_BENEFICIAL (uint16 spellType, uint8 cls = 0); -bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType); -bool BOT_SPELL_TYPES_INNATE (uint16 spellType); +bool IsBotSpellTypeDetrimental (uint16 spellType, uint8 cls = 0); +bool IsBotSpellTypeBeneficial (uint16 spellType, uint8 cls = 0); +bool IsBotSpellTypeOtherBeneficial(uint16 spellType); +bool IsBotSpellTypeInnate (uint16 spellType); bool IsBotSpellType (uint16 spellType); bool IsAEBotSpellType(uint16 spellType); bool IsGroupBotSpellType(uint16 spellType); diff --git a/zone/bot.cpp b/zone/bot.cpp index 450315514b..b6b74e74a9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7166,7 +7166,7 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType) { - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, caster->GetClass())) { + if (IsBotSpellTypeDetrimental(spellType, caster->GetClass())) { LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); return false; } @@ -10258,7 +10258,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spellType, Mob* tar, bool preCast) { return; } - if (!preCast && BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + if (!preCast && IsBotSpellTypeOtherBeneficial(spellType)) { return; } @@ -10276,7 +10276,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spellType, Mob* tar, bool preCast) { if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { return tar->GetOwner()->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); } - else if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + else if (IsBotSpellTypeOtherBeneficial(spellType)) { tar->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); } else { @@ -10407,7 +10407,7 @@ uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, ui } uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance) { - if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, botClass)) { + if (!IsBotSpellTypeBeneficial(spellType, botClass)) { return 0; } @@ -10669,7 +10669,7 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance) { - if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + if (!IsBotSpellTypeBeneficial(spellType, GetClass())) { return RuleI(Bots, SpellResistLimit); } else { @@ -10924,7 +10924,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { return result; } - if (BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + if (IsBotSpellTypeBeneficial(spellType, GetClass())) { if (!PrecastChecks(this, spellType) || !AICastSpell(this, GetChanceToCastBySpellType(spellType), spellType)) { if (GetClass() == Class::Bard) { return result; @@ -11337,7 +11337,7 @@ bool Bot::RequiresLoSForPositioning() { } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (BOT_SPELL_TYPES_DETRIMENTAL(i) && !GetSpellHold(i)) { + if (IsBotSpellTypeDetrimental(i) && !GetSpellHold(i)) { return true; } } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 9808215419..9b92636703 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -339,7 +339,7 @@ void bot_command_cast(Client* c, const Seperator* sep) break; default: if ( - (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) || + (IsBotSpellTypeDetrimental(spellType) && !c->IsAttackAllowed(tar)) || ( spellType == BotSpellTypes::Charm && ( @@ -354,7 +354,7 @@ void bot_command_cast(Client* c, const Seperator* sep) return; } - if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { + if (IsBotSpellTypeBeneficial(spellType)) { if ( (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) || ((tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner()))) @@ -418,14 +418,14 @@ void bot_command_cast(Client* c, const Seperator* sep) } if ( - BOT_SPELL_TYPES_BENEFICIAL(spellType) && + IsBotSpellTypeBeneficial(spellType) && !RuleB(Bots, CrossRaidBuffingAndHealing) && !bot_iter->IsInGroupOrRaid(newTar, true) ) { continue; } - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { + if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { bot_iter->BotGroupSay( bot_iter, fmt::format( diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 80c3c4e221..1facaef1fc 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -249,7 +249,7 @@ void bot_command_depart(Client* c, const Seperator* sep) } if (bot_iter->CastSpell(itr->SpellId, tar->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1)) { - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(BotSpellTypes::Teleport)) { + if (IsBotSpellTypeOtherBeneficial(BotSpellTypes::Teleport)) { bot_iter->SetCastedSpellType(UINT16_MAX); } else { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index a86b8e6c81..a5718a2dc7 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -243,7 +243,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + if (IsBotSpellTypeOtherBeneficial(spellType)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { @@ -293,7 +293,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellT } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + if (IsBotSpellTypeOtherBeneficial(spellType)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { @@ -2514,7 +2514,7 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id) entry.bucket_comparison = e.bucket_comparison; // some spell types don't make much since to be priority 0, so fix that - if (!BOT_SPELL_TYPES_INNATE(entry.type) && entry.priority == 0) { + if (!IsBotSpellTypeInnate(entry.type) && entry.priority == 0) { entry.priority = 1; } diff --git a/zone/mob.cpp b/zone/mob.cpp index da438671df..8e8cb8c014 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9631,7 +9631,7 @@ uint16 Mob::GetUltimateSpellDelay(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->GetSpellDelay(spellType); } @@ -9647,7 +9647,7 @@ bool Mob::GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar) { return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->SpellTypeRecastCheck(spellType); } @@ -9663,7 +9663,7 @@ uint8 Mob::GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->GetSpellMinThreshold(spellType); } @@ -9679,7 +9679,7 @@ uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->GetSpellMaxThreshold(spellType); } From 1d0ca78e2d09d2e29e7015a07070e310baa9b554 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:34:40 -0600 Subject: [PATCH 055/394] remove unused functions --- common/spdat.cpp | 8 -------- zone/bot.h | 2 -- 2 files changed, 10 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 5c8064f8c5..e9aec626ca 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2954,14 +2954,6 @@ bool IsBotSpellTypeInnate(uint16 spellType) { return false; } -bool IsBotSpellType(uint16 spellType) { - if (IsBotSpellTypeDetrimental(spellType) && IsBotSpellTypeBeneficial(spellType) && IsBotSpellTypeInnate(spellType)) { - return true; - } - - return false; -} - bool IsAEBotSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AEDebuff: diff --git a/zone/bot.h b/zone/bot.h index c8f6e4a8eb..bf785cfc56 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -991,8 +991,6 @@ class Bot : public NPC { // Public "Refactor" Methods static bool CheckCampSpawnConditions(Client* c); - inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); } - protected: void BotMeditate(bool isSitting); bool CheckBotDoubleAttack(bool Triple = false); From 51eb75523e68af475f17252c5d8b27858c5f0bd9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:35:21 -0600 Subject: [PATCH 056/394] add !commandedspell() check to aggro checks on cast --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b6b74e74a9..c0896ad604 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10919,7 +10919,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { Mob* tar = GetTarget(); - if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { + if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme return result; } From ead31d2cd40cf943230502071d5c47c8f90c751e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:37:39 -0600 Subject: [PATCH 057/394] Update cast.cpp --- zone/bot_commands/cast.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 9b92636703..a524068105 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -451,6 +451,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } bot_iter->SetCommandedSpell(false); + continue; } From 1af85092f71d0333d436e169ee03a5b58596308d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 2 Dec 2024 07:32:30 -0600 Subject: [PATCH 058/394] Implement spell AI pulling, fix throw stone --- common/ruletypes.h | 4 +++- common/spdat.cpp | 19 ++++++++++++++++++ common/spdat.h | 2 +- zone/bot.cpp | 49 ++++++++++++++++++++++++++++++++++------------ zone/bot.h | 3 +++ 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 2dcdd3e97b..7710ecc50c 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -828,8 +828,10 @@ RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summ RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.") -RULE_BOOL(Bots, AllowSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") +RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots") +RULE_BOOL(Bots, AllowRangedPulling, true, "If enabled bots will pull with their ranged items if set to ranged.") +RULE_BOOL(Bots, AllowAISpellPulling, true, "If enabled bots will rely on their detrimental AI to pull when within range.") RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") diff --git a/common/spdat.cpp b/common/spdat.cpp index e9aec626ca..8a21e23b94 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3279,6 +3279,25 @@ bool IsCommandedSpellType(uint16 spellType) { return false; } +bool IsPullingSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + case BotSpellTypes::HateLine: + return true; + default: + return false; + } + + return false; +} + bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls) { switch (spellType) { case BotSpellTypes::Nuke: diff --git a/common/spdat.h b/common/spdat.h index adf9c05e47..e7a266459b 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -740,7 +740,6 @@ bool IsBotSpellTypeDetrimental (uint16 spellType, uint8 cls = 0); bool IsBotSpellTypeBeneficial (uint16 spellType, uint8 cls = 0); bool IsBotSpellTypeOtherBeneficial(uint16 spellType); bool IsBotSpellTypeInnate (uint16 spellType); -bool IsBotSpellType (uint16 spellType); bool IsAEBotSpellType(uint16 spellType); bool IsGroupBotSpellType(uint16 spellType); bool IsGroupTargetOnlyBotSpellType(uint16 spellType); @@ -752,6 +751,7 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); +bool IsPullingSpellType(uint16 spellType); bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls); // These should not be used to determine spell category.. diff --git a/zone/bot.cpp b/zone/bot.cpp index c0896ad604..81c7de2390 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -101,6 +101,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm LoadDefaultBotSettings(); SetCastedSpellType(UINT16_MAX); SetCommandedSpell(false); + SetPullingSpell(false); //DisableBotSpellTimers(); // Do this once and only in this constructor @@ -178,6 +179,7 @@ Bot::Bot( SetBotCharmer(false); SetCastedSpellType(UINT16_MAX); SetCommandedSpell(false); + SetPullingSpell(false); bool stance_flag = false; if (!database.botdb.LoadStance(this, stance_flag) && bot_owner) { @@ -2198,36 +2200,53 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { - if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { - return; + if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells()) { + SetPullingSpell(true); + + if (AI_EngagedCastCheck()) { + SetPullingSpell(false); + return; + } + + SetPullingSpell(false); } - if (RuleB(Bots, AllowSpellPulling)) { + if (RuleB(Bots, UseSpellPulling)) { uint16 pullSpell = RuleI(Bots, PullSpellID); - if (tar_distance <= (spells[pullSpell].range * spells[pullSpell].range)) { + if (tar_distance <= spells[pullSpell].range) { StopMoving(); if (!TargetValidation(tar)) { return; } CastSpell(pullSpell, tar->GetID()); + SetPullingSpell(false); return; } } else { - //TODO bot rewrite - add casting AI to this - if (atCombatRange && IsBotRanged() && ranged_timer.Check(false)) { - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); + if (atCombatRange) { + if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { - BotRangedAttack(tar, true); - } + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } - ranged_timer.Start(); + ranged_timer.Start(); + SetPullingSpell(false); - return; + return; + } + else if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { + SetPullingSpell(false); + + return; + } } + + return; } } @@ -2622,7 +2641,7 @@ void Bot::DoAttackRounds(Mob* target, int hand) { NPC_FLURRY, GetCleanName(), target->GetCleanName() - ); //TODO bot rewrite - add output to others hits with flurry message + ); } } } @@ -9357,6 +9376,10 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return false; } + if (IsPullingSpell() && IsPullingSpellType(spellType)) { //Skip remaining checks for commanded + return true; + } + if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; diff --git a/zone/bot.h b/zone/bot.h index bf785cfc56..5a9cbf7fbf 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -410,6 +410,8 @@ class Bot : public NPC { void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; } bool IsCommandedSpell() const { return _commandedSpell; } void SetCommandedSpell(bool value) { _commandedSpell = value; } + bool IsPullingSpell() const { return _pullingSpell; } + void SetPullingSpell(bool value) { _pullingSpell = value; } void SetGuardMode(); void SetHoldMode(); @@ -1083,6 +1085,7 @@ class Bot : public NPC { uint16 _castedSpellType; bool _hasLoS; bool _commandedSpell; + bool _pullingSpell; // Private "base stats" Members int32 _baseMR; From f12e597ff3acfbbe6e7fca82c241781a2bbac391 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:16:11 -0600 Subject: [PATCH 059/394] more pull tweaks --- zone/bot.cpp | 50 +++++++++++++++----------------------- zone/bot_commands/pull.cpp | 8 +++++- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 81c7de2390..bfedc1494d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2200,15 +2200,28 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { - if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells()) { - SetPullingSpell(true); + if (!TargetValidation(tar)) { return; } + + if (atCombatRange) { + if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); + + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } + + ranged_timer.Start(); - if (AI_EngagedCastCheck()) { - SetPullingSpell(false); return; } - SetPullingSpell(false); + if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && AI_HasSpells()) { + SetPullingSpell(true); + AI_EngagedCastCheck(); + SetPullingSpell(false); + + return; + } } if (RuleB(Bots, UseSpellPulling)) { @@ -2216,38 +2229,15 @@ void Bot::AI_Process() if (tar_distance <= spells[pullSpell].range) { StopMoving(); - - if (!TargetValidation(tar)) { return; } - + SetPullingSpell(true); CastSpell(pullSpell, tar->GetID()); SetPullingSpell(false); return; } } - else { - if (atCombatRange) { - if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { - BotRangedAttack(tar, true); - } - - ranged_timer.Start(); - SetPullingSpell(false); - - return; - } - else if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { - SetPullingSpell(false); - - return; - } - } - - return; - } + return; } // ENGAGED AT COMBAT RANGE diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index fa222fcfad..5357437122 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -15,6 +15,12 @@ void bot_command_pull(Client *c, const Seperator *sep) std::string arg1 = sep->arg[1]; int ab_arg = 1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; @@ -24,7 +30,7 @@ void bot_command_pull(Client *c, const Seperator *sep) std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } From b69ac7dc2108fd200fc12af00648439fa0618637 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:25:06 -0600 Subject: [PATCH 060/394] holding check at start of ai process --- zone/bot.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index bfedc1494d..89d0f2cc25 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2087,7 +2087,7 @@ void Bot::AI_Process() return; } - if (raid && r_group == RAID_GROUPLESS) { + if (HOLDING || (raid && r_group == RAID_GROUPLESS)) { glm::vec3 Goal(0, 0, 0); TryNonCombatMovementChecks(bot_owner, follow_mob, Goal); @@ -2269,7 +2269,7 @@ void Bot::AI_Process() } } - if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { + if (!IsBotNonSpellFighter() && AI_HasSpells() && AI_EngagedCastCheck()) { return; } @@ -2330,6 +2330,7 @@ void Bot::AI_Process() SetAttackFlag(false); SetCombatRoundForAlerts(false); SetAttackingFlag(false); + if (!bot_owner->GetBotPulling()) { SetPullingFlag(false); @@ -2345,7 +2346,6 @@ void Bot::AI_Process() SetTarget(nullptr); if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { - GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -2360,10 +2360,10 @@ void Bot::AI_Process() if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { return; } - if (!HOLDING && AI_HasSpells() && TryIdleChecks(fm_distance)) { + if (AI_HasSpells() && TryIdleChecks(fm_distance)) { return; } - if (!HOLDING && AI_HasSpells() && TryBardMovementCasts()) { + if (AI_HasSpells() && TryBardMovementCasts()) { return; } } From 71bf497803a57d152f6a6696f2468cee387fdb46 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:53:24 -0600 Subject: [PATCH 061/394] Update groups.cpp --- zone/groups.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index e157b00638..512cccfc39 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -2459,7 +2459,7 @@ bool Group::AmIMainAssist(const char *mob_name) if (!mob_name) return false; - return !((bool)MainTankName.compare(mob_name)); + return !((bool)MainAssistName.compare(mob_name)); } bool Group::AmIPuller(const char *mob_name) From d6df4aae3f65d28fe05b9ef665e8a95566852bcf Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:56:09 -0600 Subject: [PATCH 062/394] fully implement ^pull logic to always return, can still be overidden by ^attack --- zone/bot.cpp | 50 +++++++++++++++++++++++++++++++++++++------------- zone/bot.h | 2 +- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 89d0f2cc25..70bc8fa79b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2117,6 +2117,7 @@ void Bot::AI_Process() //ALT COMBAT (ACQUIRE HATE) glm::vec3 Goal(0, 0, 0); + // We have aggro to choose from if (IsEngaged()) { if (rest_timer.Enabled()) { @@ -2133,16 +2134,18 @@ void Bot::AI_Process() // RETURNING FLAG - else if (GetReturningFlag()) { - if (!ReturningFlagChecks(bot_owner, fm_distance)) { - return; - } + if (GetReturningFlag()) { + LogTestDebugDetail("#{}: {} has ReturningFlag", __LINE__, GetCleanName()); //deleteme + ReturningFlagChecks(bot_owner, leash_owner, fm_distance); + + return; } // DEFAULT (ACQUIRE TARGET) // VERIFY TARGET AND STANCE auto tar = GetBotTarget(bot_owner); + if (!tar) { return; } @@ -2202,6 +2205,9 @@ void Bot::AI_Process() if (GetPullingFlag()) { if (!TargetValidation(tar)) { return; } + if (!DoLosChecks(this, tar)) { + return; + } if (atCombatRange) { if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); @@ -2237,7 +2243,9 @@ void Bot::AI_Process() } } + TryPursueTarget(leash_distance, Goal); return; + //TODO bot rewrite - need pulling checks below to prevent assist } // ENGAGED AT COMBAT RANGE @@ -2332,7 +2340,6 @@ void Bot::AI_Process() SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { - SetPullingFlag(false); SetReturningFlag(false); } @@ -3011,24 +3018,41 @@ Mob* Bot::GetBotTarget(Client* bot_owner) return t; } -bool Bot::ReturningFlagChecks(Client* bot_owner, float fm_distance) {// Need to make it back to group before clearing return flag - if (fm_distance <= GetFollowDistance()) { - - // Once we're back, clear blocking flags so everyone else can join in +bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance) { + if ( + (NOT_GUARDING && fm_distance <= GetFollowDistance()) || + (GUARDING && DistanceSquared(GetPosition(), GetGuardPoint()) <= GetFollowDistance()) + ) { // Once we're back, clear blocking flags so everyone else can join in SetReturningFlag(false); bot_owner->SetBotPulling(false); if (GetPet()) { GetPet()->SetPetOrder(m_previous_pet_order); } + + return false; } // Need to keep puller out of combat until they reach their 'return to' destination - if (HasTargetReflection()) { + WipeHateList(); - SetTarget(nullptr); - WipeHateList(); - return false; + if (!IsMoving()) { + glm::vec3 Goal(0, 0, 0); + + if (GUARDING) { + Goal = GetGuardPoint(); + } + else { + Mob* follow_mob = entity_list.GetMob(GetFollowID()); + + if (!follow_mob) { + follow_mob = leash_owner; + SetFollowID(leash_owner->GetID()); + } + + Goal = follow_mob->GetPosition(); + } + RunTo(Goal.x, Goal.y, Goal.z); } return true; diff --git a/zone/bot.h b/zone/bot.h index 5a9cbf7fbf..9e187d216e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -932,7 +932,7 @@ class Bot : public NPC { ); bool PullingFlagChecks(Client* bot_owner); - bool ReturningFlagChecks(Client* bot_owner, float fm_distance); + bool ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance); void BotPullerProcess(Client* bot_owner, Raid* raid); From 3d4474861c95d7400eab46b6fd43077de3cbb952 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 5 Dec 2024 07:21:34 -0600 Subject: [PATCH 063/394] Rewrite ^pull logic and handling. **MORE** Add ^setassistee command to set who your bots will assist. Bots will always assist you first before anyone else. If the rule Bots, AllowCrossGroupRaidAssist is enabled bots will assist the group or raid main assists. Rewrites logic in handling of pull and returning to ensure bots make it back to their location. --- common/ruletypes.h | 1 + zone/bot.cpp | 154 ++++++++++++++++++++++++----- zone/bot_command.cpp | 2 + zone/bot_command.h | 1 + zone/bot_commands/set_assistee.cpp | 59 +++++++++++ zone/client.h | 3 + 6 files changed, 197 insertions(+), 23 deletions(-) create mode 100644 zone/bot_commands/set_assistee.cpp diff --git a/common/ruletypes.h b/common/ruletypes.h index 7710ecc50c..79daedca55 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -880,6 +880,7 @@ RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") +RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index 70bc8fa79b..6f3ab2e508 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2039,6 +2039,7 @@ void Bot::AI_Process() auto raid = entity_list.GetRaidByBotName(GetName()); uint32 r_group = RAID_GROUPLESS; + if (raid) { raid->VerifyRaid(); r_group = raid->GetGroup(GetName()); @@ -2135,7 +2136,6 @@ void Bot::AI_Process() // RETURNING FLAG if (GetReturningFlag()) { - LogTestDebugDetail("#{}: {} has ReturningFlag", __LINE__, GetCleanName()); //deleteme ReturningFlagChecks(bot_owner, leash_owner, fm_distance); return; @@ -2208,6 +2208,7 @@ void Bot::AI_Process() if (!DoLosChecks(this, tar)) { return; } + if (atCombatRange) { if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); @@ -2244,8 +2245,8 @@ void Bot::AI_Process() } TryPursueTarget(leash_distance, Goal); + return; - //TODO bot rewrite - need pulling checks below to prevent assist } // ENGAGED AT COMBAT RANGE @@ -2457,42 +2458,150 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { if ( m_auto_defend_timer.Check() && - bot_owner->GetAggroCount() && NOT_HOLDING && NOT_PASSIVE ) { - auto xhaters = bot_owner->GetXTargetAutoMgr(); + XTargetAutoHaters* tempHaters; + std::vector assisteeHaters; + std::vector assisteeMembers; + bool found = false; - if (xhaters && !xhaters->empty()) { - for (auto hater_iter : xhaters->get_list()) { - if (!hater_iter.spawn_id) { - continue; + if (bot_owner->GetAggroCount()) { + tempHaters = bot_owner->GetXTargetAutoMgr(); + + if (tempHaters && !tempHaters->empty()) { + assisteeHaters.emplace_back(tempHaters); + assisteeMembers.emplace_back(bot_owner); + } + } + + if ( + (!bot_owner->GetAssistee() || !entity_list.GetClientByCharID(bot_owner->GetAssistee())) && + RuleB(Bots, AllowCrossGroupRaidAssist) + ) { + XTargetAutoHaters* temp_xhaters = bot_owner->GetXTargetAutoMgr(); + bool assisteeFound = false; + + if (IsRaidGrouped()) { + Raid* r = entity_list.GetRaidByBotName(GetName()); + if (r) { + for (const auto& m : r->members) { + if ( + m.member && + m.member->IsClient() && + m.member->GetAggroCount() && + r->IsAssister(m.member_name) + ) { + temp_xhaters = m.member->GetXTargetAutoMgr(); + + if (!temp_xhaters || temp_xhaters->empty()) { + continue; + } + + assisteeHaters.emplace_back(temp_xhaters); + assisteeMembers.emplace_back(m.member); + } + } } + } + else if (HasGroup()) { + Group* g = GetGroup(); + if (g) { + for (auto& m : g->members) { + if ( + m && + m->IsClient() && + m->CastToClient()->GetAggroCount() && + g->AmIMainAssist(m->GetName()) + ) { + temp_xhaters = m->CastToClient()->GetXTargetAutoMgr(); - if (bot_owner->GetBotPulling() && bot_owner->GetTarget() && hater_iter.spawn_id == bot_owner->GetTarget()->GetID()) { - continue; + if (!temp_xhaters || temp_xhaters->empty()) { + continue; + } + + assisteeHaters.emplace_back(temp_xhaters); + assisteeMembers.emplace_back(m->CastToClient()); + } + } } + } + else { + return false; + } + } + else { + if (bot_owner->GetAssistee()) { + Client* c = entity_list.GetClientByCharID(bot_owner->GetAssistee()); - auto hater = entity_list.GetMob(hater_iter.spawn_id); - if (hater && hater->CastToNPC()->IsOnHatelist(bot_owner) && !hater->IsMezzed() && DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) { - // This is roughly equivilent to npc attacking a client pet owner - AddToHateList(hater, 1); - SetTarget(hater); - SetAttackingFlag(); + if (bot_owner->IsInGroupOrRaid(c) && c->GetAggroCount()) { + tempHaters = bot_owner->GetXTargetAutoMgr(); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { - GetPet()->AddToHateList(hater, 1); - GetPet()->SetTarget(hater); + if (tempHaters && !tempHaters->empty()) { + assisteeHaters.emplace_back(tempHaters); + assisteeMembers.emplace_back(c); } + } + } + } + + if (!assisteeHaters.empty()) { + for (XTargetAutoHaters* xHaters : assisteeHaters) { + if (!xHaters->empty()) { + for (auto hater_iter : xHaters->get_list()) { + if (!hater_iter.spawn_id) { + continue; + } + + Mob* hater = nullptr; + + for (Client* xMember : assisteeMembers) { + if ( + xMember && + xMember->GetBotPulling() && + xMember->GetTarget() && + (hater_iter.spawn_id == xMember->GetTarget()->GetID()) + ) { + continue; + } - m_auto_defend_timer.Disable(); + hater = entity_list.GetMob(hater_iter.spawn_id); - return true; + if ( + hater && + !hater->IsMezzed() && + (DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) && + hater->CastToNPC()->IsOnHatelist(xMember) + ) { + break; + } + + hater = nullptr; + } + + if (hater) { + AddToHateList(hater, 1); + SetTarget(hater); + SetAttackingFlag(); + + if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + GetPet()->AddToHateList(hater, 1); + GetPet()->SetTarget(hater); + } + + m_auto_defend_timer.Disable(); + + return true; + } + } } } } + + return false; } } + return false; } @@ -3052,6 +3161,7 @@ bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_dist Goal = follow_mob->GetPosition(); } + RunTo(Goal.x, Goal.y, Goal.z); } @@ -11283,8 +11393,6 @@ void Bot::DoCombatPositioning( bool behindMob, bool frontMob ) { - //LogTestDebug("{} says, 'DoCombatPositioning. {} #{}", GetCleanName(), __FILE__, __LINE__); //deleteme - if (HasTargetReflection()) { if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) { if (TryEvade(tar)) { diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 1b00d701a6..a2a25a71f2 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1336,6 +1336,7 @@ int bot_command_init(void) bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || + bot_command_add("setassistee", "Sets your target to be the person your bots assist. Bots will always assist you before others", AccountStatus::Player, bot_command_set_assistee) || bot_command_add("sithppercent", "HP threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_hp_percent) || bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || @@ -2243,6 +2244,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/precombat.cpp" #include "bot_commands/pull.cpp" #include "bot_commands/release.cpp" +#include "bot_commands/set_assistee.cpp" #include "bot_commands/sit_hp_percent.cpp" #include "bot_commands/sit_in_combat.cpp" #include "bot_commands/sit_mana_percent.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index ecce9485d7..d6644bbf60 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1747,6 +1747,7 @@ void bot_command_heritage(Client *c, const Seperator *sep); void bot_command_inspect_message(Client *c, const Seperator *sep); void bot_command_list_bots(Client *c, const Seperator *sep); void bot_command_report(Client *c, const Seperator *sep); +void bot_command_set_assistee(Client* c, const Seperator* sep); void bot_command_spawn(Client *c, const Seperator *sep); void bot_command_stance(Client *c, const Seperator *sep); void bot_command_stop_melee_level(Client *c, const Seperator *sep); diff --git a/zone/bot_commands/set_assistee.cpp b/zone/bot_commands/set_assistee.cpp new file mode 100644 index 0000000000..d2be953e27 --- /dev/null +++ b/zone/bot_commands/set_assistee.cpp @@ -0,0 +1,59 @@ +#include "../bot_command.h" + +void bot_command_set_assistee(Client* c, const Seperator* sep) +{ + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets your bots to assist your target in addition to yourself" + }; + + std::vector notes = + { + "- Your target must be another player in your group or raid.", + "- This needs to be set on every zone/camp you do." + }; + + std::vector example_format = { }; + std::vector examples_one = { }; + std::vector examples_two = { }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + return; + } + + Mob* assistee = c->GetTarget(); + + if (assistee && assistee->IsClient() && c->IsInGroupOrRaid(assistee)) { + c->SetAssistee(assistee->CastToClient()->CharacterID()); + c->Message(Chat::Green, "Your bots will now assist %s.", assistee->GetCleanName()); + + return; + } + + c->Message(Chat::Yellow, "You can only set your bots to assist clients that are in your group or raid."); + + return; +} diff --git a/zone/client.h b/zone/client.h index 2a090eeac4..c4ffd54609 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2221,6 +2221,8 @@ class Client : public Mob bool GetBotPulling() { return m_bot_pulling; } void SetBotPulling(bool flag = true) { m_bot_pulling = flag; } + uint32 GetAssistee() { return _assistee; } + void SetAssistee(uint32 id = 0) { _assistee = id; } bool GetBotPrecombat() { return m_bot_precombat; } void SetBotPrecombat(bool flag = true) { m_bot_precombat = flag; } @@ -2257,6 +2259,7 @@ class Client : public Mob uint8 cure_min_threshold; uint8 cure_threshold; bool illusion_block; + uint32 _assistee; bool CanTradeFVNoDropItem(); void SendMobPositions(); From 69f724a2b9411936b567fe681a64ae0211a5f3a7 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 5 Dec 2024 23:46:37 -0600 Subject: [PATCH 064/394] Move HateLine to a better ID --- .../database_update_manifest_bots.cpp | 166 +++++++++--------- common/spdat.h | 68 +++---- zone/botspellsai.cpp | 2 +- 3 files changed, 118 insertions(+), 118 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 235c160b35..d93ec37bf2 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -1077,7 +1077,7 @@ WHERE bot_spells_entries.spell_id = spells_new.id); ManifestEntry{ .version = 9051, .description = "2024_11_26_remove_sk_icb.sql", - .check = "SELECT * FROM `bot_spells_entries` where `type` = 55", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 24", .condition = "empty", .match = "", .sql = R"( @@ -1088,88 +1088,88 @@ AND `type` = 10; INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) VALUES -(3003, 10175, 55, 72, 76), -(3003, 10173, 55, 72, 76), -(3003, 10174, 55, 72, 76), -(3003, 14956, 55, 77, 81), -(3003, 14954, 55, 77, 81), -(3003, 14955, 55, 77, 81), -(3003, 19070, 55, 82, 86), -(3003, 19068, 55, 82, 86), -(3003, 19069, 55, 82, 86), -(3003, 25298, 55, 87, 91), -(3003, 25299, 55, 87, 91), -(3003, 25297, 55, 87, 91), -(3003, 28348, 55, 92, 96), -(3003, 28349, 55, 92, 96), -(3003, 28347, 55, 92, 96), -(3003, 34351, 55, 97, 254), -(3003, 34352, 55, 97, 254), -(3003, 34350, 55, 97, 254), -(3003, 40078, 55, 98, 254), -(3003, 40079, 55, 98, 254), -(3003, 40080, 55, 98, 254), -(3005, 1221, 55, 33, 41), -(3005, 1222, 55, 42, 52), -(3005, 1223, 55, 53, 58), -(3005, 1224, 55, 59, 62), -(3005, 3405, 55, 63, 66), -(3005, 5329, 55, 67, 70), -(3005, 5336, 55, 69, 73), -(3005, 10258, 55, 71, 71), -(3005, 10259, 55, 71, 71), -(3005, 10257, 55, 71, 71), -(3005, 10261, 55, 72, 76), -(3005, 10262, 55, 72, 76), -(3005, 10260, 55, 72, 76), -(3005, 10291, 55, 74, 78), -(3005, 10292, 55, 74, 78), -(3005, 10293, 55, 74, 78), -(3005, 15160, 55, 76, 76), -(3005, 15161, 55, 76, 76), -(3005, 15162, 55, 76, 76), -(3005, 15165, 55, 77, 81), -(3005, 15163, 55, 77, 81), -(3005, 15164, 55, 77, 81), -(3005, 15186, 55, 79, 83), -(3005, 15184, 55, 79, 83), -(3005, 15185, 55, 79, 83), -(3005, 19315, 55, 81, 81), -(3005, 19313, 55, 81, 81), -(3005, 19314, 55, 81, 81), -(3005, 19317, 55, 82, 86), -(3005, 19318, 55, 82, 86), -(3005, 19316, 55, 82, 86), -(3005, 19338, 55, 84, 88), -(3005, 19339, 55, 84, 88), -(3005, 19337, 55, 84, 88), -(3005, 25581, 55, 86, 86), -(3005, 25582, 55, 86, 86), -(3005, 25580, 55, 86, 86), -(3005, 25586, 55, 87, 91), -(3005, 25587, 55, 87, 91), -(3005, 25588, 55, 87, 91), -(3005, 25641, 55, 89, 93), -(3005, 25642, 55, 89, 93), -(3005, 25643, 55, 89, 93), -(3005, 28659, 55, 91, 91), -(3005, 28657, 55, 91, 91), -(3005, 28658, 55, 91, 91), -(3005, 28665, 55, 92, 96), -(3005, 28663, 55, 92, 96), -(3005, 28664, 55, 92, 96), -(3005, 28735, 55, 94, 98), -(3005, 28733, 55, 94, 98), -(3005, 28734, 55, 94, 98), -(3005, 34688, 55, 96, 96), -(3005, 34689, 55, 96, 96), -(3005, 34687, 55, 96, 96), -(3005, 34694, 55, 97, 254), -(3005, 34695, 55, 97, 254), -(3005, 34693, 55, 97, 254), -(3005, 34752, 55, 99, 254), -(3005, 34753, 55, 99, 254), -(3005, 34751, 55, 99, 254); +(3003, 10175, 24, 72, 76), +(3003, 10173, 24, 72, 76), +(3003, 10174, 24, 72, 76), +(3003, 14956, 24, 77, 81), +(3003, 14954, 24, 77, 81), +(3003, 14955, 24, 77, 81), +(3003, 19070, 24, 82, 86), +(3003, 19068, 24, 82, 86), +(3003, 19069, 24, 82, 86), +(3003, 25298, 24, 87, 91), +(3003, 25299, 24, 87, 91), +(3003, 25297, 24, 87, 91), +(3003, 28348, 24, 92, 96), +(3003, 28349, 24, 92, 96), +(3003, 28347, 24, 92, 96), +(3003, 34351, 24, 97, 254), +(3003, 34352, 24, 97, 254), +(3003, 34350, 24, 97, 254), +(3003, 40078, 24, 98, 254), +(3003, 40079, 24, 98, 254), +(3003, 40080, 24, 98, 254), +(3005, 1221, 24, 33, 41), +(3005, 1222, 24, 42, 52), +(3005, 1223, 24, 53, 58), +(3005, 1224, 24, 59, 62), +(3005, 3405, 24, 63, 66), +(3005, 5329, 24, 67, 70), +(3005, 5336, 24, 69, 73), +(3005, 10258, 24, 71, 71), +(3005, 10259, 24, 71, 71), +(3005, 10257, 24, 71, 71), +(3005, 10261, 24, 72, 76), +(3005, 10262, 24, 72, 76), +(3005, 10260, 24, 72, 76), +(3005, 10291, 24, 74, 78), +(3005, 10292, 24, 74, 78), +(3005, 10293, 24, 74, 78), +(3005, 15160, 24, 76, 76), +(3005, 15161, 24, 76, 76), +(3005, 15162, 24, 76, 76), +(3005, 15165, 24, 77, 81), +(3005, 15163, 24, 77, 81), +(3005, 15164, 24, 77, 81), +(3005, 15186, 24, 79, 83), +(3005, 15184, 24, 79, 83), +(3005, 15185, 24, 79, 83), +(3005, 19315, 24, 81, 81), +(3005, 19313, 24, 81, 81), +(3005, 19314, 24, 81, 81), +(3005, 19317, 24, 82, 86), +(3005, 19318, 24, 82, 86), +(3005, 19316, 24, 82, 86), +(3005, 19338, 24, 84, 88), +(3005, 19339, 24, 84, 88), +(3005, 19337, 24, 84, 88), +(3005, 25581, 24, 86, 86), +(3005, 25582, 24, 86, 86), +(3005, 25580, 24, 86, 86), +(3005, 25586, 24, 87, 91), +(3005, 25587, 24, 87, 91), +(3005, 25588, 24, 87, 91), +(3005, 25641, 24, 89, 93), +(3005, 25642, 24, 89, 93), +(3005, 25643, 24, 89, 93), +(3005, 28659, 24, 91, 91), +(3005, 28657, 24, 91, 91), +(3005, 28658, 24, 91, 91), +(3005, 28665, 24, 92, 96), +(3005, 28663, 24, 92, 96), +(3005, 28664, 24, 92, 96), +(3005, 28735, 24, 94, 98), +(3005, 28733, 24, 94, 98), +(3005, 28734, 24, 94, 98), +(3005, 34688, 24, 96, 96), +(3005, 34689, 24, 96, 96), +(3005, 34687, 24, 96, 96), +(3005, 34694, 24, 97, 254), +(3005, 34695, 24, 97, 254), +(3005, 34693, 24, 97, 254), +(3005, 34752, 24, 99, 254), +(3005, 34753, 24, 99, 254), +(3005, 34751, 24, 99, 254); DELETE FROM bot_spells_entries diff --git a/common/spdat.h b/common/spdat.h index e7a266459b..fe94a82a40 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -677,39 +677,39 @@ namespace BotSpellTypes constexpr uint16 PreCombatBuffSong = 21; constexpr uint16 Fear = 22; constexpr uint16 Stun = 23; - constexpr uint16 GroupCures = 24; - constexpr uint16 CompleteHeal = 25; - constexpr uint16 FastHeals = 26; - constexpr uint16 VeryFastHeals = 27; - constexpr uint16 GroupHeals = 28; - constexpr uint16 GroupCompleteHeals = 29; - constexpr uint16 GroupHoTHeals = 30; - constexpr uint16 HoTHeals = 31; - constexpr uint16 AENukes = 32; - constexpr uint16 AERains = 33; - constexpr uint16 AEMez = 34; - constexpr uint16 AEStun = 35; - constexpr uint16 AEDebuff = 36; - constexpr uint16 AESlow = 37; - constexpr uint16 AESnare = 38; - constexpr uint16 AEFear = 39; - constexpr uint16 AEDispel = 40; - constexpr uint16 AERoot = 41; - constexpr uint16 AEDoT = 42; - constexpr uint16 AELifetap = 43; - constexpr uint16 PBAENuke = 44; - constexpr uint16 PetBuffs = 45; - constexpr uint16 PetRegularHeals = 46; - constexpr uint16 PetCompleteHeals = 47; - constexpr uint16 PetFastHeals = 48; - constexpr uint16 PetVeryFastHeals = 49; - constexpr uint16 PetHoTHeals = 50; - constexpr uint16 DamageShields = 51; - constexpr uint16 ResistBuffs = 52; - constexpr uint16 PetDamageShields = 53; - constexpr uint16 PetResistBuffs = 54; - constexpr uint16 HateLine = 55; - constexpr uint16 AEHateLine = 56; + constexpr uint16 HateLine = 24; + constexpr uint16 GroupCures = 25; + constexpr uint16 CompleteHeal = 26; + constexpr uint16 FastHeals = 27; + constexpr uint16 VeryFastHeals = 28; + constexpr uint16 GroupHeals = 29; + constexpr uint16 GroupCompleteHeals = 30; + constexpr uint16 GroupHoTHeals = 31; + constexpr uint16 HoTHeals = 32; + constexpr uint16 AENukes = 33; + constexpr uint16 AERains = 34; + constexpr uint16 AEMez = 35; + constexpr uint16 AEStun = 36; + constexpr uint16 AEDebuff = 37; + constexpr uint16 AESlow = 38; + constexpr uint16 AESnare = 39; + constexpr uint16 AEFear = 40; + constexpr uint16 AEDispel = 41; + constexpr uint16 AERoot = 42; + constexpr uint16 AEDoT = 43; + constexpr uint16 AELifetap = 44; + constexpr uint16 AEHateLine = 45; + constexpr uint16 PBAENuke = 46; + constexpr uint16 PetBuffs = 47; + constexpr uint16 PetRegularHeals = 48; + constexpr uint16 PetCompleteHeals = 49; + constexpr uint16 PetFastHeals = 50; + constexpr uint16 PetVeryFastHeals = 51; + constexpr uint16 PetHoTHeals = 52; + constexpr uint16 DamageShields = 53; + constexpr uint16 ResistBuffs = 54; + constexpr uint16 PetDamageShields = 55; + constexpr uint16 PetResistBuffs = 56; // Command Spell Types constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic @@ -727,7 +727,7 @@ namespace BotSpellTypes constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::AEHateLine; // Do not remove this, increment as needed + constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index a5718a2dc7..f83455cc83 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1045,7 +1045,7 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa ) { continue; } - if (spellType == debugSpellType) { LogTestDebugDetail("{} - #{}: [{} #{}] - {} says, '{} #{} - Passed TGB checks.'", __FILE__, __LINE__, botCaster->GetSpellTypeNameByID(spellType), spellType, botCaster->GetCleanName(), spells[botSpellList[i].spellid].name, botSpellList[i].spellid); }; //deleteme + if (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } From 67ff004d617037ee30160aa8a34e09b5c1f61489 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:38:37 -0600 Subject: [PATCH 065/394] cleanup ST_Self logic in CastChecks --- zone/bot.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 6f3ab2e508..5b858ecdae 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9541,10 +9541,10 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (spells[spell_id].target_type == ST_Self && tar != this) { - if (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse)) { - //tar = this; - } - else { + if ( + !IsEffectInSpell(spell_id, SE_SummonCorpse) || + (IsEffectInSpell(spell_id, SE_SummonCorpse) && !RuleB(Bots, AllowCommandedSummonCorpse)) + ) { tar = this; } } From db26842194ffc9b3e4ec91aa5abdbce58bbc11d4 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:38:59 -0600 Subject: [PATCH 066/394] Removed unused BotSpellTypeRequiresLoS --- common/spdat.cpp | 39 --------------------------------------- common/spdat.h | 1 - 2 files changed, 40 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 8a21e23b94..2d36d49163 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3297,42 +3297,3 @@ bool IsPullingSpellType(uint16 spellType) { return false; } - -bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls) { - switch (spellType) { - case BotSpellTypes::Nuke: - case BotSpellTypes::Root: - case BotSpellTypes::Lifetap: - case BotSpellTypes::Snare: - case BotSpellTypes::DOT: - case BotSpellTypes::Dispel: - case BotSpellTypes::Mez: - //case BotSpellTypes::Charm: // commanded - case BotSpellTypes::Slow: - case BotSpellTypes::Debuff: - case BotSpellTypes::HateRedux: - //case BotSpellTypes::Fear: // commanded - case BotSpellTypes::Stun: - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AEMez: - case BotSpellTypes::AEStun: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: - //case BotSpellTypes::AEFear: // commanded - case BotSpellTypes::AEDispel: - case BotSpellTypes::AERoot: - case BotSpellTypes::AEDoT: - case BotSpellTypes::AELifetap: - case BotSpellTypes::PBAENuke: - // case BotSpellTypes::Lull: // commanded - case BotSpellTypes::HateLine: - case BotSpellTypes::AEHateLine: - return true; - default: - return false; - } - - return false; -} diff --git a/common/spdat.h b/common/spdat.h index fe94a82a40..01cdf85af6 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -752,7 +752,6 @@ bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); bool IsPullingSpellType(uint16 spellType); -bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only From 0f3c1129725b27c934543487aa09925d7ea15d03 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:29:18 -0600 Subject: [PATCH 067/394] [Bots] Fix AA ranks to account for level Previously level requirement was only being checked on the initial rank of an AA. If passed, bots would gain all ranks for that AA regardless of level, this will now check for the level requirement for each rank before granting the AA --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5b858ecdae..a190d3315c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1285,7 +1285,7 @@ void Bot::LoadAAs() { } while(current) { - if (!CanUseAlternateAdvancementRank(current)) { + if (current->level_req > GetLevel() || !CanUseAlternateAdvancementRank(current)) { current = nullptr; } else { current = current->next; From 347a916bbb1f6570429b04638eb67e5413150b41 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:32:34 -0600 Subject: [PATCH 068/394] Move fizzle message to define --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a190d3315c..24394d5b66 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5834,7 +5834,7 @@ bool Bot::CastSpell( if (DivineAura()) { LogSpellsDetail("Spell casting canceled: cannot cast while Divine Aura is in effect"); - InterruptSpell(173, 0x121, false); + InterruptSpell(SPELL_FIZZLE, 0x121, false); return false; } From 6d6fd9ee15e2b9f17772a7c3ad4a00078f57117c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:33:32 -0600 Subject: [PATCH 069/394] add timer checks to Idle/Engaged/Pursue CastCheck to early terminate --- zone/botspellsai.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index f83455cc83..410aa7e69d 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -633,7 +633,7 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } bool Bot::AI_PursueCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { + if (GetAppearance() == eaDead || delaytimer || spellend_timer.Enabled() || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -682,7 +682,7 @@ bool Bot::AI_PursueCastCheck() { } bool Bot::AI_IdleCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { + if (GetAppearance() == eaDead || delaytimer || spellend_timer.Enabled() || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -745,7 +745,7 @@ bool Bot::AI_IdleCastCheck() { } bool Bot::AI_EngagedCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { + if (GetAppearance() == eaDead || delaytimer || spellend_timer.Enabled() || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } From 221333a7bacd6d5c5abf1d33b862b1a1ffc89b23 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:32:49 -0600 Subject: [PATCH 070/394] Add back !IsBotNonSpellFighter() check to the different CastCheck --- zone/bot.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 24394d5b66..bdd3a32e5d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2368,10 +2368,10 @@ void Bot::AI_Process() if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { return; } - if (AI_HasSpells() && TryIdleChecks(fm_distance)) { + if (!IsBotNonSpellFighter() && AI_HasSpells() && TryIdleChecks(fm_distance)) { return; } - if (AI_HasSpells() && TryBardMovementCasts()) { + if (!IsBotNonSpellFighter() && AI_HasSpells() && TryBardMovementCasts()) { return; } } @@ -2663,7 +2663,7 @@ bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { } // This is a mob that is fleeing either because it has been feared or is low on hitpoints - if (!HOLDING && AI_HasSpells()) { + if (!HOLDING && !IsBotNonSpellFighter() && AI_HasSpells()) { AI_PursueCastCheck(); } From 9917be3094c5684d50e4dd58d83bb3373977741f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:35:36 -0600 Subject: [PATCH 071/394] Correct IsValidSpellRange --- zone/bot.cpp | 128 +++++++++++++++++++++++++++++++++++++++---- zone/botspellsai.cpp | 41 ++++++++------ 2 files changed, 143 insertions(+), 26 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index bdd3a32e5d..523f6c9183 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9690,16 +9690,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - - if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } if ( (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spell_id) != 0)) @@ -9714,6 +9704,20 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (spellType == UINT16_MAX) { //AA cast checks, return here + return true; + } + + if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + return true; } @@ -11095,6 +11099,81 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { return result; } +bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { + if (!tar) { + tar = this; + } + + if (!DoLosChecks(this, tar)) { + return false; + } + + if (CheckSpellRecastTimer(spell_id)) { + if (IsBeneficialSpell(spell_id)) { + if ( + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !GetOwner()->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !GetOwner()->IsInGroupOrRaid(tar)) + ) { + GetBotOwner()->Message(Chat::Yellow, "[%s] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName()); + + return false; + } + } + + if (IsDetrimentalSpell(spell_id) && !IsAttackAllowed(tar)) { + GetBotOwner()->Message(Chat::Yellow, "%s says, 'I cannot attack [%s]'.", GetCleanName(), tar->GetCleanName()); + + return false; + } + + if (!CastChecks(spell_id, tar, UINT16_MAX)) { + GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", GetCleanName()); + + return false; + } + + + if (CastSpell(spell_id, tar->GetID())) { + BotGroupSay( + this, + fmt::format( + "Casting {} on {}.", + GetSpellName(spell_id), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); + + int timer_duration = (rank->recast_time - GetAlternateAdvancementCooldownReduction(rank)) * 1000; + + if (timer_duration < 0) { + timer_duration = 0; + } + + SetSpellRecastTimer(spell_id, timer_duration); + } + else { + GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", GetCleanName()); + + return false; + } + } + else { + GetBotOwner()->Message( + Chat::Yellow, + fmt::format( + "{} says, 'Ability recovery time not yet met. {} remaining.'", + GetCleanName(), + Strings::SecondsToTime(GetSpellRecastRemainingTime(spell_id), true) + ).c_str() + ); + + return false; + } + + return true; +} + uint16 Bot::GetSpellListSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: @@ -11890,3 +11969,32 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell return false; } + +uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) { + uint16 spell_id = 0; + std::pair aa_ability = std::make_pair(nullptr, nullptr); + AA::Ability* ability = zone->GetAlternateAdvancementAbility(id); + + if (!ability || !ability->first_rank_id) { + return spell_id; + } + + uint32 points = GetAA(ability->first_rank_id); + //if (points) { LogTestDebug("{}: {} says, '{} points for {} [#{} - {}] rank {}'", __LINE__, GetCleanName(), points, zone->GetAAName(aa_ability.first->id), aa_ability.first->id, aa_ability.second->id, points); } //deleteme + if (points > 0) { + aa_ability = zone->GetAlternateAdvancementAbilityAndRank(ability->id, points); + } + + rank = aa_ability.second; + + if (!points || !rank) { + LogTestDebug("{}: {} says, 'No {} found'", __LINE__, GetCleanName(), (!points ? "points" : "rank")); //deleteme + return spell_id; + } + + spell_id = rank->spell; + + LogTestDebug("{}: {} says, 'Found {} [#{}]'", __LINE__, GetCleanName(), spells[spell_id].name, spell_id); //deleteme + + return spell_id; +} diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 410aa7e69d..b4588811d0 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2669,31 +2669,40 @@ bool Bot::HasBotSpellEntry(uint16 spell_id) { } bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { - if (!IsValidSpell(spell_id)) { + if (!IsValidSpell(spell_id) || !tar) { return false; } - if (tar) { - float range = spells[spell_id].range; - - if (tar != this) { - range += GetRangeDistTargetSizeMod(tar); - } + float range = spells[spell_id].range + GetRangeDistTargetSizeMod(tar); + + if (IsAnyAESpell(spell_id)) { + range = GetAOERange(spell_id); + } + + if (RuleB(Bots, EnableBotTGB) && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) { + range = spells[spell_id].aoe_range; + } - range = GetActSpellRange(spell_id, range); + range = GetActSpellRange(spell_id, range); - if (range >= Distance(m_Position, tar->GetPosition())) { - return true; - } + if (IsIllusionSpell(spell_id) && (HasProjectIllusion())) { + range = 100; + } - range = GetActSpellRange(spell_id, spells[spell_id].aoe_range); + float dist2 = DistanceSquared(m_Position, tar->GetPosition()); + float range2 = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; - if (range >= Distance(m_Position, tar->GetPosition())) { - return true; - } + if (dist2 > range2) { + //target is out of range. + return false; + } + else if (dist2 < min_range2) { + //target is too close range. + return false; } - return false; + return true; } BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE, Mob* tar) { From ef36f2873c6b1e19cd6329f98f03c6c5ac8f0318 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:36:56 -0600 Subject: [PATCH 072/394] Implement AAs and harmtouch/layonhands to ^cast --- fix IsValidSpellRange --- zone/bot.cpp | 4 +- zone/bot.h | 2 + zone/bot_commands/cast.cpp | 378 ++++++++++++++++++++++--------------- zone/spells.cpp | 2 +- 4 files changed, 226 insertions(+), 160 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 523f6c9183..347689f206 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9750,7 +9750,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::Invisibility: case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: - if ( + if ( // TODO bot rewrite - fix this, missing other target types (43 for example) !( spells[spell_id].target_type == ST_Target || spells[spell_id].target_type == ST_Pet || @@ -9759,7 +9759,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { spells[spell_id].target_type == ST_GroupTeleport ) ) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme return false; } diff --git a/zone/bot.h b/zone/bot.h index 9e187d216e..3371a6b998 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -402,6 +402,7 @@ class Bot : public NPC { // AI Methods bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); bool AttemptAICastSpell(uint16 spellType); + bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; @@ -478,6 +479,7 @@ class Bot : public NPC { void LoadDefaultBotSettings(); void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); + uint16 GetSpellByAA(int id, AA::Rank* &rank); std::string GetBotSpellCategoryName(uint8 setting_type); std::string GetBotSettingCategoryName(uint8 setting_type); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index a524068105..7915a491c9 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -11,7 +11,13 @@ void bot_command_cast(Client* c, const Seperator* sep) std::vector notes = { "- This will interrupt any spell currently being cast by bots told to use the command.", - "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells" + "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells", + fmt::format( + "- You can use {} aa # to cast any clickable AA or specifically {} harmtouch / {} layonhands" + , sep->arg[0] + , sep->arg[0] + , sep->arg[0] + ) }; std::vector example_format = @@ -29,12 +35,12 @@ void bot_command_cast(Client* c, const Seperator* sep) { "To tell everyone to Nuke the target:", fmt::format( - "{} {} spawned", + "{} {}", sep->arg[0], c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( - "{} {} spawned", + "{} {}", sep->arg[0], BotSpellTypes::Nuke ) @@ -57,16 +63,14 @@ void bot_command_cast(Client* c, const Seperator* sep) }; std::vector examples_three = { - "To tell Clrbot to resurrect the targeted corpse:", + "To tell Skbot to Harm Touch the target:", fmt::format( - "{} {} byname Clrbot", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Resurrect) + "{} aa 6000 byname Skbot", + sep->arg[0] ), fmt::format( - "{} {} byname Clrbot", - sep->arg[0], - BotSpellTypes::Resurrect + "{} harmtouch byname Skbot", + sep->arg[0] ) }; @@ -120,6 +124,11 @@ void bot_command_cast(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; + //AA help + if (!arg1.compare("aa") && !arg2.compare("help")) { + c->Message(Chat::Yellow, "Enter the ID of an AA to attempt to cast.", sep->arg[0]); + } + //Commanded type help prompts if (!arg2.compare("help")) { c->Message(Chat::Yellow, "You can also use [single], [group], [ae]. Ex: ^cast movementspeed group.", sep->arg[0]); @@ -174,144 +183,168 @@ void bot_command_cast(Client* c, const Seperator* sep) } int ab_arg = 2; - uint16 spellType = 0; - - // String/Int type checks - if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); - - if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) { - c->Message( - Chat::Yellow, - fmt::format( - "You must choose a valid spell type. Use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); + uint16 spellType = UINT16_MAX; + uint16 subType = UINT16_MAX; + uint16 subTargetType = UINT16_MAX; + bool aaType = false; + int aaID = 0; - return; + if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) { + if (!arg1.compare("harmtouch")) { + aaID = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id; } - } - else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + else if (!arg1.compare("layonhands")) { + aaID = zone->GetAlternateAdvancementAbilityByRank(aaLayonHands)->id; } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - + else if (!sep->IsNumber(2) || !zone->GetAlternateAdvancementAbility(Strings::ToInt(arg2))) { + c->Message(Chat::Yellow, "You must enter an AA ID."); return; } + else { + ++ab_arg; + aaID = Strings::ToInt(arg2); + } + + aaType = true; } - switch (spellType) { //Allowed command checks - case BotSpellTypes::Charm: - if (!RuleB(Bots, AllowCommandedCharm)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); - return; - } + if (!aaType) { + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); - break; - case BotSpellTypes::AEMez: - case BotSpellTypes::Mez: - if (!RuleB(Bots, AllowCommandedMez)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); return; } - - break; - case BotSpellTypes::Resurrect: - if (!RuleB(Bots, AllowCommandedResurrect)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); - return; + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); - break; - case BotSpellTypes::SummonCorpse: - if (!RuleB(Bots, AllowCommandedSummonCorpse)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); return; } + } - break; - default: - break; - } + switch (spellType) { //Allowed command checks + case BotSpellTypes::Charm: + if (!RuleB(Bots, AllowCommandedCharm)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - std::string argString = sep->arg[ab_arg]; - uint16 subType = UINT16_MAX; - uint16 subTargetType = UINT16_MAX; - - if (!argString.compare("shrink")) { - subType = CommandedSubTypes::Shrink; - ++ab_arg; - } - else if (!argString.compare("grow")) { - subType = CommandedSubTypes::Grow; - ++ab_arg; - } - else if (!argString.compare("see")) { - subType = CommandedSubTypes::SeeInvis; - ++ab_arg; - } - else if (!argString.compare("invis")) { - subType = CommandedSubTypes::Invis; - ++ab_arg; - } - else if (!argString.compare("undead")) { - subType = CommandedSubTypes::InvisUndead; - ++ab_arg; - } - else if (!argString.compare("animals")) { - subType = CommandedSubTypes::InvisAnimals; - ++ab_arg; - } - else if (!argString.compare("selo")) { - subType = CommandedSubTypes::Selo; - ++ab_arg; - } + break; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + if (!RuleB(Bots, AllowCommandedMez)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - argString = sep->arg[ab_arg]; + break; + case BotSpellTypes::Resurrect: + if (!RuleB(Bots, AllowCommandedResurrect)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - if (!argString.compare("single")) { - subTargetType = CommandedSubTypes::SingleTarget; - ++ab_arg; - } - else if (!argString.compare("group")) { - subTargetType = CommandedSubTypes::GroupTarget; - ++ab_arg; - } - else if (!argString.compare("ae")) { - subTargetType = CommandedSubTypes::AETarget; - ++ab_arg; - } + break; + case BotSpellTypes::SummonCorpse: + if (!RuleB(Bots, AllowCommandedSummonCorpse)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - if ( - spellType == BotSpellTypes::PetBuffs || - spellType == BotSpellTypes::PetCompleteHeals || - spellType == BotSpellTypes::PetFastHeals || - spellType == BotSpellTypes::PetHoTHeals || - spellType == BotSpellTypes::PetRegularHeals || - spellType == BotSpellTypes::PetVeryFastHeals - ) { - c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); - return; + break; + default: + break; + } + + std::string argString = sep->arg[ab_arg]; + + + if (!argString.compare("shrink")) { + subType = CommandedSubTypes::Shrink; + ++ab_arg; + } + else if (!argString.compare("grow")) { + subType = CommandedSubTypes::Grow; + ++ab_arg; + } + else if (!argString.compare("see")) { + subType = CommandedSubTypes::SeeInvis; + ++ab_arg; + } + else if (!argString.compare("invis")) { + subType = CommandedSubTypes::Invis; + ++ab_arg; + } + else if (!argString.compare("undead")) { + subType = CommandedSubTypes::InvisUndead; + ++ab_arg; + } + else if (!argString.compare("animals")) { + subType = CommandedSubTypes::InvisAnimals; + ++ab_arg; + } + else if (!argString.compare("selo")) { + subType = CommandedSubTypes::Selo; + ++ab_arg; + } + + argString = sep->arg[ab_arg]; + + if (!argString.compare("single")) { + subTargetType = CommandedSubTypes::SingleTarget; + ++ab_arg; + } + else if (!argString.compare("group")) { + subTargetType = CommandedSubTypes::GroupTarget; + ++ab_arg; + } + else if (!argString.compare("ae")) { + subTargetType = CommandedSubTypes::AETarget; + ++ab_arg; + } + + if ( + spellType == BotSpellTypes::PetBuffs || + spellType == BotSpellTypes::PetCompleteHeals || + spellType == BotSpellTypes::PetFastHeals || + spellType == BotSpellTypes::PetHoTHeals || + spellType == BotSpellTypes::PetRegularHeals || + spellType == BotSpellTypes::PetVeryFastHeals + ) { + c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); + return; + } } Mob* tar = c->GetTarget(); //LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (!tar) { - if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { + if (!aaType && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { c->Message(Chat::Yellow, "You need a target for that."); return; } @@ -356,10 +389,11 @@ void bot_command_cast(Client* c, const Seperator* sep) if (IsBotSpellTypeBeneficial(spellType)) { if ( - (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) || - ((tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner()))) + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) ) { - c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + c->Message(Chat::Yellow, "[%s] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName()); return; } @@ -408,49 +442,79 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* newTar = tar; - //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme - if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { - newTar = bot_iter; - } - if (!newTar) { - continue; - } + if (!aaType) { + //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { + newTar = bot_iter; + } - if ( - IsBotSpellTypeBeneficial(spellType) && - !RuleB(Bots, CrossRaidBuffingAndHealing) && - !bot_iter->IsInGroupOrRaid(newTar, true) - ) { - continue; - } + if (!newTar) { + continue; + } - if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { - bot_iter->BotGroupSay( - bot_iter, - fmt::format( - "I cannot attack [{}].", - newTar->GetCleanName() - ).c_str() - ); + if ( + IsBotSpellTypeBeneficial(spellType) && + !RuleB(Bots, CrossRaidBuffingAndHealing) && + !bot_iter->IsInGroupOrRaid(newTar, true) + ) { + continue; + } - continue; + if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { + bot_iter->BotGroupSay( + bot_iter, + fmt::format( + "I cannot attack [{}].", + newTar->GetCleanName() + ).c_str() + ); + + continue; + } } - LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + if (aaType) { + if (!bot_iter->GetAA(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)) { + continue; + } + + LogTestDebug("{}: {} says, 'aaID is {}'", __LINE__, bot_iter->GetCleanName(), aaID); //deleteme + AA::Rank* tempRank = nullptr; + AA::Rank*& rank = tempRank; + uint16 spell_id = bot_iter->GetSpellByAA(aaID, rank); - bot_iter->SetCommandedSpell(true); + if (!IsValidSpell(spell_id)) { + continue; + } - if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { - if (!firstFound) { - firstFound = bot_iter; + if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) { + continue; } isSuccess = true; ++successCount; } + else { + LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + bot_iter->SetCommandedSpell(true); + + if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + else { + bot_iter->GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", bot_iter->GetCleanName()); - bot_iter->SetCommandedSpell(false); + continue; + } + + bot_iter->SetCommandedSpell(false); + } continue; } @@ -460,7 +524,7 @@ void bot_command_cast(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "No bots are capable of casting [{}] on {}.", - c->GetSpellTypeNameByID(spellType), + (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), tar ? tar->GetCleanName() : "your target" ).c_str() ); @@ -471,7 +535,7 @@ void bot_command_cast(Client* c, const Seperator* sep) "{} {} [{}]{}", ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), - c->GetSpellTypeNameByID(spellType), + (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." ).c_str() ); diff --git a/zone/spells.cpp b/zone/spells.cpp index d24b5b7e4a..7ca17d394b 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2580,7 +2580,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in } range = GetActSpellRange(spell_id, range); - if(IsClient() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){ + if(IsOfClientBot() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){ range = 100; } From 33386e1e66cc9b3a9197df9b12216eef3fb6a267 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 23:10:49 -0600 Subject: [PATCH 073/394] Add PetDamageShields and PetResistBuffs to IsPetBotSpellType() --- common/spdat.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/spdat.cpp b/common/spdat.cpp index 2d36d49163..42e64116ea 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3012,6 +3012,8 @@ bool IsPetBotSpellType(uint16 spellType) { case BotSpellTypes::PetFastHeals: case BotSpellTypes::PetVeryFastHeals: case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: return true; default: return false; From 51332cc0c7ed166b829f353531214c1be930005d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:58:54 -0600 Subject: [PATCH 074/394] Add priorities to HateLine inserts for db update --- .../database_update_manifest_bots.cpp | 166 +++++++++--------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index d93ec37bf2..bd35d776ff 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -1086,90 +1086,90 @@ FROM bot_spells_entries WHERE `npc_spells_id` = 3005 AND `type` = 10; -INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`, `priority`) VALUES -(3003, 10175, 24, 72, 76), -(3003, 10173, 24, 72, 76), -(3003, 10174, 24, 72, 76), -(3003, 14956, 24, 77, 81), -(3003, 14954, 24, 77, 81), -(3003, 14955, 24, 77, 81), -(3003, 19070, 24, 82, 86), -(3003, 19068, 24, 82, 86), -(3003, 19069, 24, 82, 86), -(3003, 25298, 24, 87, 91), -(3003, 25299, 24, 87, 91), -(3003, 25297, 24, 87, 91), -(3003, 28348, 24, 92, 96), -(3003, 28349, 24, 92, 96), -(3003, 28347, 24, 92, 96), -(3003, 34351, 24, 97, 254), -(3003, 34352, 24, 97, 254), -(3003, 34350, 24, 97, 254), -(3003, 40078, 24, 98, 254), -(3003, 40079, 24, 98, 254), -(3003, 40080, 24, 98, 254), -(3005, 1221, 24, 33, 41), -(3005, 1222, 24, 42, 52), -(3005, 1223, 24, 53, 58), -(3005, 1224, 24, 59, 62), -(3005, 3405, 24, 63, 66), -(3005, 5329, 24, 67, 70), -(3005, 5336, 24, 69, 73), -(3005, 10258, 24, 71, 71), -(3005, 10259, 24, 71, 71), -(3005, 10257, 24, 71, 71), -(3005, 10261, 24, 72, 76), -(3005, 10262, 24, 72, 76), -(3005, 10260, 24, 72, 76), -(3005, 10291, 24, 74, 78), -(3005, 10292, 24, 74, 78), -(3005, 10293, 24, 74, 78), -(3005, 15160, 24, 76, 76), -(3005, 15161, 24, 76, 76), -(3005, 15162, 24, 76, 76), -(3005, 15165, 24, 77, 81), -(3005, 15163, 24, 77, 81), -(3005, 15164, 24, 77, 81), -(3005, 15186, 24, 79, 83), -(3005, 15184, 24, 79, 83), -(3005, 15185, 24, 79, 83), -(3005, 19315, 24, 81, 81), -(3005, 19313, 24, 81, 81), -(3005, 19314, 24, 81, 81), -(3005, 19317, 24, 82, 86), -(3005, 19318, 24, 82, 86), -(3005, 19316, 24, 82, 86), -(3005, 19338, 24, 84, 88), -(3005, 19339, 24, 84, 88), -(3005, 19337, 24, 84, 88), -(3005, 25581, 24, 86, 86), -(3005, 25582, 24, 86, 86), -(3005, 25580, 24, 86, 86), -(3005, 25586, 24, 87, 91), -(3005, 25587, 24, 87, 91), -(3005, 25588, 24, 87, 91), -(3005, 25641, 24, 89, 93), -(3005, 25642, 24, 89, 93), -(3005, 25643, 24, 89, 93), -(3005, 28659, 24, 91, 91), -(3005, 28657, 24, 91, 91), -(3005, 28658, 24, 91, 91), -(3005, 28665, 24, 92, 96), -(3005, 28663, 24, 92, 96), -(3005, 28664, 24, 92, 96), -(3005, 28735, 24, 94, 98), -(3005, 28733, 24, 94, 98), -(3005, 28734, 24, 94, 98), -(3005, 34688, 24, 96, 96), -(3005, 34689, 24, 96, 96), -(3005, 34687, 24, 96, 96), -(3005, 34694, 24, 97, 254), -(3005, 34695, 24, 97, 254), -(3005, 34693, 24, 97, 254), -(3005, 34752, 24, 99, 254), -(3005, 34753, 24, 99, 254), -(3005, 34751, 24, 99, 254); +(3003, 10173, 24, 72, 76, 3), +(3003, 10174, 24, 72, 76, 2), +(3003, 10175, 24, 72, 76, 1), +(3003, 14954, 24, 77, 81, 3), +(3003, 14955, 24, 77, 81, 2), +(3003, 14956, 24, 77, 81, 1), +(3003, 19068, 24, 82, 86, 3), +(3003, 19069, 24, 82, 86, 2), +(3003, 19070, 24, 82, 86, 1), +(3003, 25297, 24, 87, 91, 3), +(3003, 25298, 24, 87, 91, 2), +(3003, 25299, 24, 87, 91, 1), +(3003, 28347, 24, 92, 96, 3), +(3003, 28348, 24, 92, 96, 2), +(3003, 28349, 24, 92, 96, 1), +(3003, 34350, 24, 97, 254, 3), +(3003, 34351, 24, 97, 254, 2), +(3003, 34352, 24, 97, 254, 1), +(3003, 40078, 24, 98, 254, 3), +(3003, 40079, 24, 98, 254, 2), +(3003, 40080, 24, 98, 254, 1), +(3005, 1221, 24, 33, 41, 1), +(3005, 1222, 24, 42, 52, 1), +(3005, 1223, 24, 53, 58, 1), +(3005, 1224, 24, 59, 62, 1), +(3005, 3405, 24, 63, 66, 1), +(3005, 5329, 24, 67, 70, 5), +(3005, 5336, 24, 69, 73, 4), +(3005, 10257, 24, 71, 71, 3), +(3005, 10258, 24, 71, 71, 2), +(3005, 10259, 24, 71, 71, 1), +(3005, 10260, 24, 72, 76, 3), +(3005, 10261, 24, 72, 76, 2), +(3005, 10262, 24, 72, 76, 1), +(3005, 10291, 24, 74, 78, 3), +(3005, 10292, 24, 74, 78, 2), +(3005, 10293, 24, 74, 78, 1), +(3005, 15160, 24, 76, 76, 3), +(3005, 15161, 24, 76, 76, 2), +(3005, 15162, 24, 76, 76, 1), +(3005, 15163, 24, 77, 81, 3), +(3005, 15164, 24, 77, 81, 2), +(3005, 15165, 24, 77, 81, 1), +(3005, 15184, 24, 79, 83, 3), +(3005, 15185, 24, 79, 83, 2), +(3005, 15186, 24, 79, 83, 1), +(3005, 19313, 24, 81, 81, 3), +(3005, 19314, 24, 81, 81, 2), +(3005, 19315, 24, 81, 81, 1), +(3005, 19316, 24, 82, 86, 3), +(3005, 19317, 24, 82, 86, 2), +(3005, 19318, 24, 82, 86, 1), +(3005, 19337, 24, 84, 88, 3), +(3005, 19338, 24, 84, 88, 2), +(3005, 19339, 24, 84, 88, 1), +(3005, 25580, 24, 86, 86, 3), +(3005, 25581, 24, 86, 86, 2), +(3005, 25582, 24, 86, 86, 1), +(3005, 25586, 24, 87, 91, 3), +(3005, 25587, 24, 87, 91, 2), +(3005, 25588, 24, 87, 91, 1), +(3005, 25641, 24, 89, 93, 3), +(3005, 25642, 24, 89, 93, 2), +(3005, 25643, 24, 89, 93, 1), +(3005, 28657, 24, 91, 91, 3), +(3005, 28658, 24, 91, 91, 2), +(3005, 28659, 24, 91, 91, 1), +(3005, 28663, 24, 92, 96, 3), +(3005, 28664, 24, 92, 96, 2), +(3005, 28665, 24, 92, 96, 1), +(3005, 28733, 24, 94, 98, 3), +(3005, 28734, 24, 94, 98, 2), +(3005, 28735, 24, 94, 98, 1), +(3005, 34687, 24, 96, 96, 3), +(3005, 34688, 24, 96, 96, 2), +(3005, 34689, 24, 96, 96, 1), +(3005, 34693, 24, 97, 254, 3), +(3005, 34694, 24, 97, 254, 2), +(3005, 34695, 24, 97, 254, 1), +(3005, 34751, 24, 99, 254, 3), +(3005, 34752, 24, 99, 254, 2), +(3005, 34753, 24, 99, 254, 1); DELETE FROM bot_spells_entries From ef7af5c63f6b1409a35eecf8cc90c4c4660dc281 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:59:17 -0600 Subject: [PATCH 075/394] Remove SpellTypeRequiresCastChecks --- common/spdat.cpp | 25 ------------------------- common/spdat.h | 1 - 2 files changed, 26 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 42e64116ea..ff4a3006ae 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3116,31 +3116,6 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls) { return true; } -bool SpellTypeRequiresCastChecks(uint16 spellType) { - switch (spellType) { - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AEDispel: - case BotSpellTypes::AEDoT: - case BotSpellTypes::AEFear: - case BotSpellTypes::AELifetap: - case BotSpellTypes::AEMez: - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AERoot: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: - case BotSpellTypes::AEStun: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Mez: - case BotSpellTypes::SummonCorpse: - return false; - default: - return true; - } - - return true; -} - bool SpellTypeRequiresAEChecks(uint16 spellType) { switch (spellType) { case BotSpellTypes::AEMez: diff --git a/common/spdat.h b/common/spdat.h index 01cdf85af6..7b533328b0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -748,7 +748,6 @@ bool IsClientBotSpellType(uint16 spellType); bool IsHealBotSpellType(uint16 spellType); bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); -bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); bool IsPullingSpellType(uint16 spellType); From aa996e7b31b62c03e508b3566abb9e8d0b2b8f97 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:59:48 -0600 Subject: [PATCH 076/394] Add bot check to DetermineSpellTargets for IsIllusionSpell --- zone/spells.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 7ca17d394b..be631b76f2 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1939,6 +1939,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce && spell_target != nullptr // null ptr crash safeguard && !spell_target->IsNPC() // still self only if NPC targetted && IsClient() + && IsOfClientBot() && (IsGrouped() // still self only if not grouped || IsRaidGrouped()) && (HasProjectIllusion())){ From 307b576da9e28af4b4ed8ef3215d20b52ec5228a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:59:59 -0600 Subject: [PATCH 077/394] merge with previous --- zone/spells.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index be631b76f2..32903bfcf3 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1938,7 +1938,6 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(IsIllusionSpell(spell_id) && spell_target != nullptr // null ptr crash safeguard && !spell_target->IsNPC() // still self only if NPC targetted - && IsClient() && IsOfClientBot() && (IsGrouped() // still self only if not grouped || IsRaidGrouped()) From 389515d75c49dd834294347760256fb7897ab0a5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:00:17 -0600 Subject: [PATCH 078/394] Correct bot checks for ST_GroupClientAndPet --- zone/spells.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 32903bfcf3..62b0e886bb 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2321,6 +2321,19 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(GetOwner()) group_id_caster = (GetRaid()->GetGroup(GetOwner()->CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(GetOwner()->CastToClient()) + 1); } + if (IsGrouped()) + { + if (Group* group = GetGroup()) { + group_id_caster = group->GetID(); + } + } + else if (IsRaidGrouped()) + { + if (Raid* raid = GetRaid()) { + uint32 group_id = raid->GetGroup(GetName()); + group_id_caster = (group_id == 0xFFFFFFFF) ? 0 : (group_id + 1); + } + } } if(spell_target->IsClient()) From 211908196e68f1bb1ec9b8c61055f1f25f70678f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:01:29 -0600 Subject: [PATCH 079/394] Remove misc target_type checks --- zone/bot.cpp | 10 +--------- zone/botspellsai.cpp | 43 ++++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 347689f206..107f86304e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9750,15 +9750,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::Invisibility: case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: - if ( // TODO bot rewrite - fix this, missing other target types (43 for example) - !( - spells[spell_id].target_type == ST_Target || - spells[spell_id].target_type == ST_Pet || - (tar == this && spells[spell_id].target_type != ST_TargetsTarget) || - spells[spell_id].target_type == ST_Group || - spells[spell_id].target_type == ST_GroupTeleport - ) - ) { + if (tar == this && spells[spell_id].target_type == ST_TargetsTarget) { LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme return false; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b4588811d0..aa24443a34 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -595,27 +595,28 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); - if ( - ( - ( - ( - (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || - spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || - spells[AIBot_spells[i].spellid].target_type ==ST_Group || - spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || - ( - tar == this && spells[AIBot_spells[i].spellid].target_type != ST_TargetsTarget - ) - ) && - dist2 <= spells[AIBot_spells[i].spellid].aoe_range*spells[AIBot_spells[i].spellid].aoe_range - ) || - dist2 <= GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range)*GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range) - ) && - ( - mana_cost <= GetMana() || - IsBotNonSpellFighter() - ) - ) { + //if ( + // ( + // ( + // ( + // (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || + // spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || + // spells[AIBot_spells[i].spellid].target_type ==ST_Group || + // spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || + // ( + // tar == this && spells[AIBot_spells[i].spellid].target_type != ST_TargetsTarget + // ) + // ) && + // dist2 <= spells[AIBot_spells[i].spellid].aoe_range*spells[AIBot_spells[i].spellid].aoe_range + // ) || + // dist2 <= GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range)*GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range) + // ) && + // ( + // mana_cost <= GetMana() || + // IsBotNonSpellFighter() + // ) + //) { + if (IsValidSpellRange(AIBot_spells[i].spellid, tar) && (mana_cost <= GetMana() || IsBotNonSpellFighter())) { casting_spell_AIindex = i; LogAI("spellid [{}] tar [{}] mana [{}] Name [{}]", AIBot_spells[i].spellid, tar->GetName(), mana_cost, spells[AIBot_spells[i].spellid].name); result = Mob::CastSpell(AIBot_spells[i].spellid, tar->GetID(), EQ::spells::CastingSlot::Gem2, spells[AIBot_spells[i].spellid].cast_time, AIBot_spells[i].manacost == -2 ? 0 : mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIBot_spells[i].resist_adjust)); From f3e0fdae73c51dfaa5d14ccb7b420598f9163d72 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:03:33 -0600 Subject: [PATCH 080/394] Add lull/aelull to ^cast --- common/ruletypes.h | 1 + common/spdat.cpp | 7 +++++++ common/spdat.h | 3 ++- zone/bot.cpp | 7 +++++-- zone/bot_commands/cast.cpp | 8 ++++++++ zone/botspellsai.cpp | 1 + zone/mob.cpp | 6 ++++++ 7 files changed, 30 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 79daedca55..9a7b3acbd6 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -878,6 +878,7 @@ RULE_BOOL(Bots, AllowCommandedCharm, true, "If enabled bots can be commanded to RULE_BOOL(Bots, AllowCommandedMez, true, "If enabled bots can be commanded to mez NPCs.") RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded to resurrect players.") RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.") +RULE_BOOL(Bots, AllowCommandedLull, true, "If enabled bots can be commanded to lull targets.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.") diff --git a/common/spdat.cpp b/common/spdat.cpp index ff4a3006ae..d5ec840928 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2820,6 +2820,7 @@ bool IsBotSpellTypeDetrimental(uint16 spellType, uint8 cls) { case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: case BotSpellTypes::Lull: + case BotSpellTypes::AELull: case BotSpellTypes::HateLine: case BotSpellTypes::AEHateLine: return true; @@ -2946,6 +2947,9 @@ bool IsBotSpellTypeInnate(uint16 spellType) { case BotSpellTypes::AEMez: case BotSpellTypes::Mez: case BotSpellTypes::Lull: + case BotSpellTypes::AELull: + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return true; default: return false; @@ -2969,6 +2973,8 @@ bool IsAEBotSpellType(uint16 spellType) { case BotSpellTypes::PBAENuke: case BotSpellTypes::AELifetap: case BotSpellTypes::AERoot: + case BotSpellTypes::AEHateLine: + case BotSpellTypes::AELull: return true; default: return false; @@ -3242,6 +3248,7 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: + case BotSpellTypes::AELull: //case BotSpellTypes::Cure: //case BotSpellTypes::GroupCures: //case BotSpellTypes::DamageShields: diff --git a/common/spdat.h b/common/spdat.h index 7b533328b0..e030143c7c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -725,11 +725,12 @@ namespace BotSpellTypes constexpr uint16 MovementSpeed = 110; constexpr uint16 SendHome = 111; constexpr uint16 SummonCorpse = 112; + constexpr uint16 AELull = 113; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this - constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed + constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed } const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); diff --git a/zone/bot.cpp b/zone/bot.cpp index 107f86304e..21d36b8590 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9878,6 +9878,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } break; + case BotSpellTypes::AELull: case BotSpellTypes::Lull: if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme @@ -11234,6 +11235,9 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::AELifetap: case BotSpellTypes::Lifetap: return BotSpellTypes::Lifetap; + case BotSpellTypes::AELull: + case BotSpellTypes::Lull: + return BotSpellTypes::Lull; case BotSpellTypes::Charm: case BotSpellTypes::Escape: case BotSpellTypes::HateRedux: @@ -11245,8 +11249,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Pet: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: - case BotSpellTypes::Resurrect: - case BotSpellTypes::Lull: + case BotSpellTypes::Resurrect: default: return spellType; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 7915a491c9..5866bacf41 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -268,6 +268,14 @@ void bot_command_cast(Client* c, const Seperator* sep) return; } + break; + case BotSpellTypes::AELull: + case BotSpellTypes::Lull: + if (!RuleB(Bots, AllowCommandedLull)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + break; case BotSpellTypes::SummonCorpse: if (!RuleB(Bots, AllowCommandedSummonCorpse)) { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index aa24443a34..93c38ccf68 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -83,6 +83,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge } break; + case BotSpellTypes::AELull: case BotSpellTypes::Lull: if (tar->GetSpecialAbility(SpecialAbility::PacifyImmunity)) { return false; diff --git a/zone/mob.cpp b/zone/mob.cpp index 8e8cb8c014..fdf1ed2a09 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8943,6 +8943,9 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::SummonCorpse: spellTypeName = "Summon Corpse"; break; + case BotSpellTypes::AELull: + spellTypeName = "AE Lull"; + break; default: break; } @@ -9164,6 +9167,9 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::SummonCorpse: spellTypeName = "summoncorpse"; break; + case BotSpellTypes::AELull: + spellTypeName = "aelull"; + break; default: break; } From 57a81fc31067cb69a32754da71e6fe5ebf13a7ca Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:04:07 -0600 Subject: [PATCH 081/394] Add more checks for CommandedSubTypes::AETarget --- zone/bot.cpp | 2 +- zone/botspellsai.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 21d36b8590..2163a13484 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11905,7 +11905,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell break; case CommandedSubTypes::AETarget: - if (IsAnyAESpell(spell_id)) { + if (IsAnyAESpell(spell_id) && !IsGroupSpell(spell_id)) { return true; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 93c38ccf68..c6ac703976 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -216,7 +216,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; } - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType), subTargetType, subType); + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, (IsAEBotSpellType(spellType) || subTargetType == CommandedSubTypes::AETarget), subTargetType, subType); for (const auto& s : botSpellList) { From 51711e799b82b364137714605cb8e80f712f042e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:07:21 -0600 Subject: [PATCH 082/394] remove unneeded checks on IsValidSpellTypeBySpellID --- zone/bot.cpp | 82 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 80 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2163a13484..747a30d9c2 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9761,12 +9761,12 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } if (!tar->CheckSpellLevelRestriction(this, spell_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -11313,84 +11313,6 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { } return false; - //case BotSpellTypes::Lull: - // if (IsHarmonySpell(spell_id)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Teleport: - // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Succor: - // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::BindAffinity: - // if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Identify: - // if (IsEffectInSpell(spell_id, SE_Identify)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Levitate: - // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Rune: - // if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::WaterBreathing: - // if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Size: - // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Invisibility: - // if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::MovementSpeed: - // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::SendHome: - // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::SummonCorpse: - // if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - // return true; - // } - // - // return false; default: return true; } From 229b8684ab84a1847ce2712e5c266e507b7e5900 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:16:43 -0600 Subject: [PATCH 083/394] add to aelull --- common/spdat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index d5ec840928..c912553947 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3235,6 +3235,7 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::AEFear: case BotSpellTypes::Fear: case BotSpellTypes::Resurrect: + case BotSpellTypes::AELull: case BotSpellTypes::Lull: case BotSpellTypes::Teleport: case BotSpellTypes::Succor: @@ -3248,7 +3249,6 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: - case BotSpellTypes::AELull: //case BotSpellTypes::Cure: //case BotSpellTypes::GroupCures: //case BotSpellTypes::DamageShields: From 1536e26b310fc6d047100b93c16c46730be6f112 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:18:12 -0600 Subject: [PATCH 084/394] rewrite GetCorrectSpellType --- common/ruletypes.h | 3 + common/spdat.cpp | 250 ++++++++++++++++++++++++- common/spdat.h | 3 + zone/bot.cpp | 2 +- zone/botspellsai.cpp | 421 ++++--------------------------------------- 5 files changed, 291 insertions(+), 388 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 9a7b3acbd6..4d57b4f820 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -824,6 +824,9 @@ RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid rang RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.") RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.") RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks") +RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires") +RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list") +RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA") RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel") RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") diff --git a/common/spdat.cpp b/common/spdat.cpp index c912553947..cbff317312 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1582,11 +1582,11 @@ bool IsRegularPetHealSpell(uint16 spell_id) if (spell_id) { if ( - spells[spell_id].target_type == ST_Pet && + (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id) - ) { + ) { for (int i = 0; i < EFFECT_COUNT; i++) { if ( spells[spell_id].base_value[i] > 0 && @@ -1594,8 +1594,8 @@ bool IsRegularPetHealSpell(uint16 spell_id) ( spells[spell_id].effect_id[i] == SE_CurrentHP || spells[spell_id].effect_id[i] == SE_CurrentHPOnce - ) - ) { + ) + ) { return true; } } @@ -3229,6 +3229,17 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { return true; } +bool IsHateSpell(uint16 spell_id) { + if ( + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) + ) { + return true; + } + + return false; +} + bool IsCommandedSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::Charm: @@ -3281,3 +3292,234 @@ bool IsPullingSpellType(uint16 spellType) { return false; } + +uint16 GetCorrectSpellType(uint16 spellType, uint16 spell_id) { + uint16 correctType = UINT16_MAX; + SPDat_Spell_Struct spell = spells[spell_id]; + std::string teleportZone = spell.teleport_zone; + + if (IsCharmSpell(spell_id)) { + correctType = BotSpellTypes::Charm; + } + else if (IsFearSpell(spell_id)) { + correctType = BotSpellTypes::Fear; + } + else if (IsEffectInSpell(spell_id, SE_Revive)) { + correctType = BotSpellTypes::Resurrect; + } + else if (IsHarmonySpell(spell_id)) { + correctType = BotSpellTypes::Lull; + } + else if (teleportZone.compare("") && !IsEffectInSpell(spell_id, SE_GateToHomeCity) && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + correctType = BotSpellTypes::Teleport; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + correctType = BotSpellTypes::Succor; + } + else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + correctType = BotSpellTypes::BindAffinity; + } + else if (IsEffectInSpell(spell_id, SE_Identify)) { + correctType = BotSpellTypes::Identify; + } + else if (spellType == BotSpellTypes::Levitate && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + correctType = BotSpellTypes::Levitate; + } + else if (spellType == BotSpellTypes::Rune && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))) { + correctType = BotSpellTypes::Rune; + } + else if (spellType == BotSpellTypes::WaterBreathing && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { + correctType = BotSpellTypes::WaterBreathing; + } + else if (spellType == BotSpellTypes::Size && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + correctType = BotSpellTypes::Size; + } + else if (spellType == BotSpellTypes::Invisibility && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))) { + correctType = BotSpellTypes::Invisibility; + } + else if (spellType == BotSpellTypes::MovementSpeed && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + correctType = BotSpellTypes::MovementSpeed; + } + else if (!teleportZone.compare("") && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))) { + correctType = BotSpellTypes::SendHome; + } + else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + correctType = BotSpellTypes::SummonCorpse; + } + + if (correctType == UINT16_MAX) { + if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { + correctType = BotSpellTypes::Pet; + } + else if (IsMesmerizeSpell(spell_id)) { + correctType = BotSpellTypes::Mez; + } + else if (IsEscapeSpell(spell_id)) { + correctType = BotSpellTypes::Escape; + } + else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_Root)) { + if (IsAnyAESpell(spell_id)) { + correctType = BotSpellTypes::AERoot; + } + else { + correctType = BotSpellTypes::Root; + } + } + else if (IsDetrimentalSpell(spell_id) && IsLifetapSpell(spell_id)) { + correctType = BotSpellTypes::Lifetap; + } + else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + correctType = BotSpellTypes::Snare; + } + else if (IsDetrimentalSpell(spell_id) && (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))) { + correctType = BotSpellTypes::DOT; + } + else if (IsDispelSpell(spell_id)) { + correctType = BotSpellTypes::Dispel; + } + else if (IsDetrimentalSpell(spell_id) && IsSlowSpell(spell_id)) { + correctType = BotSpellTypes::Slow; + } + else if (IsDebuffSpell(spell_id) && !IsHateReduxSpell(spell_id) && !IsHateSpell(spell_id)) { + correctType = BotSpellTypes::Debuff; + } + else if (IsHateReduxSpell(spell_id)) { + correctType = BotSpellTypes::HateRedux; + } + else if (IsDetrimentalSpell(spell_id) && IsHateSpell(spell_id)) { + correctType = BotSpellTypes::HateLine; + } + else if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) + ) { + if ( + spellType == BotSpellTypes::InCombatBuffSong || + spellType == BotSpellTypes::OutOfCombatBuffSong || + spellType == BotSpellTypes::PreCombatBuffSong + ) { + correctType = spellType; + } + else { + correctType = BotSpellTypes::OutOfCombatBuffSong; + } + } + else if ( + !IsBardSong(spell_id) && + ( + (IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) || + (spellType == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) + ) + ) { + correctType = BotSpellTypes::InCombatBuff; + } + else if ( + spellType == BotSpellTypes::PreCombatBuff && + IsAnyBuffSpell(spell_id) && + !IsBardSong(spell_id) + ) { + correctType = BotSpellTypes::PreCombatBuff; + } + else if ( + (IsCureSpell(spell_id) && spellType == BotSpellTypes::Cure) || + (IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id)) + ) { + correctType = BotSpellTypes::Cure; + } + else if (IsAnyNukeOrStunSpell(spell_id)) { + if (IsAnyAESpell(spell_id)) { + if (IsAERainSpell(spell_id)) { + correctType = BotSpellTypes::AERains; + } + else if (IsPBAENukeSpell(spell_id)) { + correctType = BotSpellTypes::PBAENuke; + } + else if (IsStunSpell(spell_id)) { + correctType = BotSpellTypes::AEStun; + } + else { + correctType = BotSpellTypes::AENukes; + } + } + else if (IsStunSpell(spell_id)) { + correctType = BotSpellTypes::Stun; + } + else { + correctType = BotSpellTypes::Nuke; + } + } + else if (IsAnyHealSpell(spell_id)) { + if (IsGroupSpell(spell_id)) { + if (IsGroupCompleteHealSpell(spell_id)) { + correctType = BotSpellTypes::GroupCompleteHeals; + } + else if (IsGroupHealOverTimeSpell(spell_id)) { + correctType = BotSpellTypes::GroupHoTHeals; + } + else if (IsRegularGroupHealSpell(spell_id)) { + correctType = BotSpellTypes::GroupHeals; + } + } + else { + if (IsVeryFastHealSpell(spell_id)) { + correctType = BotSpellTypes::VeryFastHeals; + } + else if (IsFastHealSpell(spell_id)) { + correctType = BotSpellTypes::FastHeals; + } + else if (IsCompleteHealSpell(spell_id)) { + correctType = BotSpellTypes::CompleteHeal; + } + else if (IsHealOverTimeSpell(spell_id)) { + correctType = BotSpellTypes::HoTHeals; + } + else if (IsRegularSingleTargetHealSpell(spell_id)) { + correctType = BotSpellTypes::RegularHeal; + } + else if (IsRegularPetHealSpell(spell_id)) { + correctType = BotSpellTypes::RegularHeal; + } + } + } + else if (IsAnyBuffSpell(spell_id)) { + if (IsResistanceOnlySpell(spell_id)) { + correctType = BotSpellTypes::ResistBuffs; + } + else if (IsDamageShieldOnlySpell(spell_id)) { + correctType = BotSpellTypes::DamageShields; + } + else { + correctType = BotSpellTypes::Buff; + } + } + } + + + return correctType; +} + +uint16 GetPetSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; + default: + return spellType; + } + + return spellType; +} diff --git a/common/spdat.h b/common/spdat.h index e030143c7c..ffe3525ade 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -752,6 +752,8 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); bool IsPullingSpellType(uint16 spellType); +uint16 GetCorrectSpellType(uint16 spellType, uint16 spell_id); +uint16 GetPetSpellType(uint16 spellType); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only @@ -1748,5 +1750,6 @@ bool IsResurrectSpell(uint16 spell_id); bool RequiresStackCheck(uint16 spellType); bool IsResistanceOnlySpell(uint16 spell_id); bool IsDamageShieldOnlySpell(uint16 spell_id); +bool IsHateSpell(uint16 spell_id); #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 747a30d9c2..86ea16881e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3560,7 +3560,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { } if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) { - OwnerMessage("Running SpellType checks. There may be some spells that are flagged as incorrect but actually are correct. Use this as a guideline."); + OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline."); CheckBotSpells(); //This runs through a serious of checks and outputs any spells that are set to the wrong spell type in the database } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index c6ac703976..835e990400 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2753,10 +2753,11 @@ BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, u } void Bot::CheckBotSpells() { - bool valid = false; - uint16 correctType; auto spellList = BotSpellsEntriesRepository::All(content_db); uint16 spell_id; + SPDat_Spell_Struct spell; + uint16 correctType; + uint16 parentType; for (const auto& s : spellList) { if (!IsValidSpell(s.spell_id)) { @@ -2764,64 +2765,65 @@ void Bot::CheckBotSpells() { continue; } - spell_id = s.spell_id; + spell = spells[s.spell_id]; + spell_id = spell.id; - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] >= 255) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) { LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); //deleteme } else { - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.minlevel) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) { LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}." , GetSpellName(spell_id) , spell_id - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.minlevel ); //deleteme LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , spell_id , s.npc_spells_id , GetSpellName(spell_id) , spell_id , s.minlevel - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id ); //deleteme } - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] < s.minlevel) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) { LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}." , GetSpellName(spell_id) , spell_id - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.minlevel ); //deleteme LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , spell_id , s.npc_spells_id , GetSpellName(spell_id) , spell_id , s.minlevel - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id ); //deleteme } - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.maxlevel) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) { LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}." , GetSpellName(spell_id) , spell_id - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.maxlevel @@ -2829,380 +2831,33 @@ void Bot::CheckBotSpells() { } } - correctType = UINT16_MAX; - valid = false; + correctType = GetCorrectSpellType(s.type, spell_id); + parentType = GetSpellListSpellType(correctType); - - switch (s.type) { - case BotSpellTypes::Nuke: - if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::RegularHeal: - if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Root: - if (IsEffectInSpell(spell_id, SE_Root)) { - valid = true; - break; - } - break; - case BotSpellTypes::Buff: - if (IsAnyBuffSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Pet: - if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { - valid = true; - break; - } - break; - case BotSpellTypes::Lifetap: - if (IsLifetapSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Snare: - if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::DOT: - if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Dispel: - if (IsDispelSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::InCombatBuff: - if ( - IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::HateLine: - if ( - (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || - (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::Mez: - if (IsMesmerizeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Charm: - if (IsCharmSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Slow: - if (IsSlowSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Debuff: - if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Cure: - if (IsCureSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::PreCombatBuff: - if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - !IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::PreCombatBuffSong: - if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::Fear: - if (IsFearSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Escape: - if (IsEscapeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::HateRedux: - if (IsHateReduxSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Resurrect: - if (IsEffectInSpell(spell_id, SE_Revive)) { - valid = true; - break; - } - break; - case BotSpellTypes::Lull: - if (IsHarmonySpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Teleport: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - valid = true; - break; - } - break; - case BotSpellTypes::Succor: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - valid = true; - break; - } - break; - case BotSpellTypes::BindAffinity: - if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - valid = true; - break; - } - break; - case BotSpellTypes::Identify: - if (IsEffectInSpell(spell_id, SE_Identify)) { - valid = true; - break; - } - break; - case BotSpellTypes::Levitate: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - valid = true; - break; - } - break; - case BotSpellTypes::Rune: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - valid = true; - break; - } - break; - case BotSpellTypes::WaterBreathing: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { - valid = true; - break; - } - break; - case BotSpellTypes::Size: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - valid = true; - break; - } - break; - case BotSpellTypes::Invisibility: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::MovementSpeed: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - valid = true; - break; - } - break; - case BotSpellTypes::SendHome: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - valid = true; - break; - } - break; - case BotSpellTypes::SummonCorpse: - if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - valid = true; - break; - } - break; - default: - break; - - } - - if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { - correctType = BotSpellTypes::Nuke; - } - else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { - correctType = BotSpellTypes::RegularHeal; - } - else if (IsEffectInSpell(spell_id, SE_Root)) { - correctType = BotSpellTypes::Root; - } - else if (IsAnyBuffSpell(spell_id)) { - correctType = BotSpellTypes::Buff; - } - else if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { - correctType = BotSpellTypes::Pet; - } - else if (IsLifetapSpell(spell_id)) { - correctType = BotSpellTypes::Lifetap; - } - else if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { - correctType = BotSpellTypes::Snare; - } - else if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { - correctType = BotSpellTypes::DOT; - } - else if (IsDispelSpell(spell_id)) { - correctType = BotSpellTypes::Dispel; - } - else if ( - IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) - ) { - correctType = BotSpellTypes::InCombatBuff; - } - else if ( - (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || - (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) - ) { - correctType = BotSpellTypes::HateLine; - } - else if (IsMesmerizeSpell(spell_id)) { - correctType = BotSpellTypes::Mez; - } - else if (IsCharmSpell(spell_id)) { - correctType = BotSpellTypes::Charm; - } - else if (IsSlowSpell(spell_id)) { - correctType = BotSpellTypes::Slow; - } - else if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { - correctType = BotSpellTypes::Debuff; - } - else if (IsCureSpell(spell_id)) { - correctType = BotSpellTypes::Cure; - } - else if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - if ( - s.type == BotSpellTypes::InCombatBuffSong || - s.type == BotSpellTypes::OutOfCombatBuffSong || - s.type == BotSpellTypes::PreCombatBuffSong - ) { - correctType = s.type; - } - else { - correctType = BotSpellTypes::OutOfCombatBuffSong; + if (RuleB(Bots, UseParentSpellTypeForChecks)) { + if (s.type == parentType || s.type == correctType) { + continue; } } - else if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - !IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - correctType = BotSpellTypes::PreCombatBuff; - } - else if (IsFearSpell(spell_id)) { - correctType = BotSpellTypes::Fear; - } - else if (IsEscapeSpell(spell_id)) { - correctType = BotSpellTypes::Escape; - } - else if (IsHateReduxSpell(spell_id)) { - correctType = BotSpellTypes::HateRedux; - } - else if (IsEffectInSpell(spell_id, SE_Revive)) { - correctType = BotSpellTypes::Resurrect; - } - else if (IsHarmonySpell(spell_id)) { - correctType = BotSpellTypes::Lull; - } - else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - correctType = BotSpellTypes::Teleport; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - correctType = BotSpellTypes::Succor; - } - else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - correctType = BotSpellTypes::BindAffinity; - } - else if (IsEffectInSpell(spell_id, SE_Identify)) { - correctType = BotSpellTypes::Identify; - } - else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - correctType = BotSpellTypes::Levitate; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - correctType = BotSpellTypes::Rune; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { - correctType = BotSpellTypes::WaterBreathing; - } - else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - correctType = BotSpellTypes::Size; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - correctType = BotSpellTypes::Invisibility; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - correctType = BotSpellTypes::MovementSpeed; + else { + if (IsPetBotSpellType(s.type)) { + correctType = GetPetSpellType(correctType); + } } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - correctType = BotSpellTypes::SendHome; + + if (correctType == s.type) { + continue; } - else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - correctType = BotSpellTypes::SummonCorpse; + + if (correctType == UINT16_MAX) { + LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown." + , GetSpellName(spell_id) + , spell_id + , GetSpellTypeNameByID(s.type) + , s.type + ); //deleteme } - - if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { + else { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" , GetSpellName(spell_id) , spell_id @@ -3211,7 +2866,7 @@ void Bot::CheckBotSpells() { , GetSpellTypeNameByID(correctType) , correctType ); //deleteme - LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spellid` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" , correctType , spell_id , GetSpellName(spell_id) From afbf1b74c4278ec10de41b4296480e9cb90cc070 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:18:40 -0600 Subject: [PATCH 085/394] Add IsBlockedBuff to CastChecks --- zone/bot.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 86ea16881e..5ecd824ae6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9652,7 +9652,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec spells[spell_id].target_type == ST_Self && tar != this && (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) - ) { + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9667,6 +9667,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (IsBeneficialSpell(spell_id) && tar->IsBlockedBuff(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme if (!CanCastSpellType(spellType, spell_id, tar)) { return false; From 186b06ef47fd4fabc277aab6360f9d29a3a7551c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:19:17 -0600 Subject: [PATCH 086/394] Add spellid option to ^cast to allow casting of a specific spell by ID --- zone/bot.cpp | 135 ++++++++++++++++++++- zone/bot.h | 2 +- zone/bot_commands/cast.cpp | 233 ++++++++++++++++++++++++++----------- 3 files changed, 300 insertions(+), 70 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5ecd824ae6..a823b01af8 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9709,7 +9709,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (spellType == UINT16_MAX) { //AA cast checks, return here + if (spellType == UINT16_MAX) { //AA/Forced cast checks, return here return true; } @@ -11098,7 +11098,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { } bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { - if (!tar) { + if (!tar || spells[spell_id].target_type == ST_Self) { tar = this; } @@ -11132,11 +11132,25 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { } + if (IsCasting()) { + BotGroupSay( + this, + fmt::format( + "Interrupting {}. I have been commanded to try to cast an AA - {} on {}.", + CastingSpellID() ? spells[CastingSpellID()].name : "my spell", + spells[spell_id].name, + tar->GetCleanName() + ).c_str() + ); + + InterruptSpell(); + } + if (CastSpell(spell_id, tar->GetID())) { BotGroupSay( this, fmt::format( - "Casting {} on {}.", + "Casting an AA - {} on {}.", GetSpellName(spell_id), (tar == this ? "myself" : tar->GetCleanName()) ).c_str() @@ -11172,6 +11186,121 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { return true; } +bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { + SPDat_Spell_Struct spell = spells[spell_id]; + uint16 forcedSpellID = spell.id; + + if (!tar || (spells[spell_id].target_type == ST_Self && tar != this)) { + LogTestDebug("{} set my target to myself for {} [#{}] due to !tar.", GetCleanName(), spell.name, forcedSpellID); //deleteme + tar = this; + } + + if (IsBeneficialSpell(forcedSpellID)) { + if ( + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar)) + ) { + GetBotOwner()->Message( + Chat::Yellow, + fmt::format( + "[{}] is an invalid target. Only players or their pet in your group or raid are eligible targets." + , tar->GetCleanName() + ).c_str() + ); + + return false; + } + } + + if (IsDetrimentalSpell(forcedSpellID) && (!GetBotOwner()->IsAttackAllowed(tar) || !IsAttackAllowed(tar))) { + GetBotOwner()->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I cannot attack [{}]'.", + GetCleanName(), + tar->GetCleanName() + ).c_str() + ); + + return false; + } + + if (!CheckSpellRecastTimer(forcedSpellID)) { + LogTestDebug("{} failed CheckSpellRecastTimer for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + return false; + } + + if ( + !RuleB(Bots, EnableBotTGB) && + IsGroupSpell(forcedSpellID) && + !IsTGBCompatibleSpell(forcedSpellID) && + !IsInGroupOrRaid(tar, true) + ) { + LogTestDebug("{} failed TGB for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + return false; + } + + if (!DoLosChecks(this, tar)) { + LogTestDebug("{} failed LoS for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + return false; + } + + if (!CastChecks(forcedSpellID, tar, UINT16_MAX)) { + LogTestDebug("{} failed CastChecks for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + GetBotOwner()->Message( + Chat::Red, + fmt::format( + "{} says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", + GetBotOwner()->GetCleanName() + ).c_str() + ); + + return false; + } + + if (IsCasting()) { + BotGroupSay( + this, + fmt::format( + "Interrupting {}. I have been commanded to try to cast {} on {}.", + CastingSpellID() ? spells[CastingSpellID()].name : "my spell", + spell.name, + tar->GetCleanName() + ).c_str() + ); + + InterruptSpell(); + } + + if (CastSpell(forcedSpellID, tar->GetID())) { + BotGroupSay( + this, + fmt::format( + "Casting {} on {}.", + GetSpellName(forcedSpellID), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); + + int timer_duration = CalcBuffDuration(tar, this, forcedSpellID); + + if (timer_duration) { // negatives are perma buffs + timer_duration = GetActSpellDuration(forcedSpellID, timer_duration); + } + + if (timer_duration < 0) { + timer_duration = 0; + } + + SetSpellRecastTimer(forcedSpellID, timer_duration); + + return true; + } + + return false; +} + uint16 Bot::GetSpellListSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: diff --git a/zone/bot.h b/zone/bot.h index 3371a6b998..98475e9099 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -403,6 +403,7 @@ class Bot : public NPC { bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); bool AttemptAICastSpell(uint16 spellType); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); + bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; @@ -546,7 +547,6 @@ class Bot : public NPC { void CheckBotSpells(); - [[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; } [[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; } [[nodiscard]] int GetMaxDiscSlots() const final { return EQ::spells::DISC_BUFFS; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 5866bacf41..65d03ad2b6 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -47,29 +47,21 @@ void bot_command_cast(Client* c, const Seperator* sep) }; std::vector examples_two = { - "To tell all Enchanters to slow the target:", + "To tell Skbot to Harm Touch the target:", fmt::format( - "{} {} byclass {}", - sep->arg[0], - Class::Enchanter, - c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) + "{} aa 6000 byname Skbot", + sep->arg[0] ), fmt::format( - "{} {} byclass {}", - sep->arg[0], - Class::Enchanter, - BotSpellTypes::Slow + "{} harmtouch byname Skbot", + sep->arg[0] ) }; std::vector examples_three = { - "To tell Skbot to Harm Touch the target:", - fmt::format( - "{} aa 6000 byname Skbot", - sep->arg[0] - ), + "To tell all bots to try to cast spell #93 (Burst of Flame)", fmt::format( - "{} harmtouch byname Skbot", + "{} spellid 93", sep->arg[0] ) }; @@ -188,8 +180,15 @@ void bot_command_cast(Client* c, const Seperator* sep) uint16 subTargetType = UINT16_MAX; bool aaType = false; int aaID = 0; + bool bySpellID = false; + uint16 chosenSpellID = UINT16_MAX; if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) { + if (!RuleB(Bots, AllowForcedCastsBySpellID)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + if (!arg1.compare("harmtouch")) { aaID = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id; } @@ -208,8 +207,25 @@ void bot_command_cast(Client* c, const Seperator* sep) aaType = true; } - if (!aaType) { - // String/Int type checks + if (!arg1.compare("spellid")) { + if (!RuleB(Bots, AllowCastAAs)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + if (sep->IsNumber(2) && IsValidSpell(atoi(sep->arg[2]))) { + ++ab_arg; + chosenSpellID = atoi(sep->arg[2]); + bySpellID = true; + } + else { + c->Message(Chat::Yellow, "You must enter a valid spell ID."); + + return; + } + } + + if (!aaType && !bySpellID) { if (sep->IsNumber(1)) { spellType = atoi(sep->arg[1]); @@ -342,7 +358,7 @@ void bot_command_cast(Client* c, const Seperator* sep) spellType == BotSpellTypes::PetHoTHeals || spellType == BotSpellTypes::PetRegularHeals || spellType == BotSpellTypes::PetVeryFastHeals - ) { + ) { c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); return; } @@ -352,62 +368,88 @@ void bot_command_cast(Client* c, const Seperator* sep) //LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (!tar) { - if (!aaType && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { + if ((!aaType && !bySpellID) && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { c->Message(Chat::Yellow, "You need a target for that."); return; } } - switch (spellType) { //Target Checks - case BotSpellTypes::Resurrect: - if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { - c->Message(Chat::Yellow, "[%s] is not a player's corpse.", tar->GetCleanName()); + if (!aaType && !bySpellID) { + switch (spellType) { //Target Checks + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] is not a player's corpse.", + tar->GetCleanName() + ).c_str() + ); - return; - } + return; + } - break; - case BotSpellTypes::Identify: - case BotSpellTypes::SendHome: - case BotSpellTypes::BindAffinity: - case BotSpellTypes::SummonCorpse: - if (!tar->IsClient() || !c->IsInGroupOrRaid(tar)) { - c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + break; + case BotSpellTypes::Identify: + case BotSpellTypes::SendHome: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::SummonCorpse: + if (!tar->IsClient() || !c->IsInGroupOrRaid(tar)) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] is an invalid target. Only players in your group or raid are eligible targets.", + tar->GetCleanName() + ).c_str() + ); - return; - } + return; + } - break; - default: - if ( - (IsBotSpellTypeDetrimental(spellType) && !c->IsAttackAllowed(tar)) || - ( - spellType == BotSpellTypes::Charm && + break; + default: + if ( + (IsBotSpellTypeDetrimental(spellType) && !c->IsAttackAllowed(tar)) || ( - tar->IsClient() || - tar->IsCorpse() || - tar->GetOwner() + spellType == BotSpellTypes::Charm && + ( + tar->IsClient() || + tar->IsCorpse() || + tar->GetOwner() + ) ) - ) - ) { - c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); - - return; - } - - if (IsBotSpellTypeBeneficial(spellType)) { - if ( - (tar->IsNPC() && !tar->GetOwner()) || - (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) || - (tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) ) { - c->Message(Chat::Yellow, "[%s] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName()); + c->Message( + Chat::Yellow, + fmt::format( + "You cannot attack [{}].", + tar->GetCleanName() + ).c_str() + ); return; } - } - break; + if (IsBotSpellTypeBeneficial(spellType)) { + if ( + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) + ) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] is an invalid target. Only players or their pet in your group or raid are eligible targets.", + tar->GetCleanName() + ).c_str() + ); + + return; + } + } + + break; + } } const int ab_mask = ActionableBots::ABM_Type1; @@ -451,7 +493,7 @@ void bot_command_cast(Client* c, const Seperator* sep) Mob* newTar = tar; - if (!aaType) { + if (!aaType && !bySpellID) { //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { newTar = bot_iter; @@ -502,9 +544,48 @@ void bot_command_cast(Client* c, const Seperator* sep) isSuccess = true; ++successCount; + + continue; + } + else if (bySpellID) { + SPDat_Spell_Struct spell = spells[chosenSpellID]; + + LogTestDebug("Starting bySpellID checks."); //deleteme + if (!bot_iter->HasBotSpellEntry(chosenSpellID)) { + LogTestDebug("{} does not have {} [#{}].", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme + continue; + } + + if (!tar || (spell.target_type == ST_Self && tar != bot_iter)) { + LogTestDebug("{} set my target to myself for {} [#{}] due to !tar.", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme + tar = bot_iter; + } + + if (bot_iter->AttemptForcedCastSpell(tar, chosenSpellID)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + else { + c->Message( + Chat::Red, + fmt::format( + "{} says, '{} [#{}] failed to cast on [{}]. This could be due to this to any number of things: range, mana, immune, etc.'", + bot_iter->GetCleanName(), + spell.name, + chosenSpellID, + tar->GetCleanName() + ).c_str() + ); + } + + continue; } else { - LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on [{}]'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { @@ -516,34 +597,54 @@ void bot_command_cast(Client* c, const Seperator* sep) ++successCount; } else { - bot_iter->GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", bot_iter->GetCleanName()); - - continue; + c->Message( + Chat::Red, + fmt::format( + "{} says, 'Ability failed to cast [{}]. This could be due to this to any number of things: range, mana, immune, etc.'", + bot_iter->GetCleanName(), + tar->GetCleanName() + ).c_str() + ); } bot_iter->SetCommandedSpell(false); + + continue; } continue; } + std::string type = ""; + + if (aaType) { + type = zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id); + } + else if (bySpellID) { + type = "Forced"; + } + else { + type = c->GetSpellTypeNameByID(spellType); + } + if (!isSuccess) { c->Message( Chat::Yellow, fmt::format( "No bots are capable of casting [{}] on {}.", - (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), + (bySpellID ? spells[chosenSpellID].name : type), tar ? tar->GetCleanName() : "your target" ).c_str() ); } else { - c->Message( Chat::Yellow, + c->Message( + Chat::Yellow, fmt::format( "{} {} [{}]{}", ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), - (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), + (bySpellID ? spells[chosenSpellID].name : type), tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." ).c_str() ); From 783a5f0adf4fea75cb2974d61a12b78f5b4b9a87 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:59:54 -0600 Subject: [PATCH 087/394] ^cast adjustments for spellid casts --- zone/bot.cpp | 8 ++++++++ zone/bot.h | 1 + zone/bot_commands/cast.cpp | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a823b01af8..d685a41f24 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11195,6 +11195,14 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { tar = this; } + if ((IsCharmSpell(forcedSpellID) || IsPetSpell(forcedSpellID) && HasPet())) { + return false; + } + + if (IsResurrectSpell(forcedSpellID) && (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse())) { + return false; + } + if (IsBeneficialSpell(forcedSpellID)) { if ( (tar->IsNPC() && !tar->GetOwner()) || diff --git a/zone/bot.h b/zone/bot.h index 98475e9099..287721ae94 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -752,6 +752,7 @@ class Bot : public NPC { // "Quest API" Methods bool HasBotSpellEntry(uint16 spell_id); + bool CanUseBotSpell(uint16 spell_id); void ApplySpell(int spell_id, int duration = 0, int level = -1, ApplySpellType apply_type = ApplySpellType::Solo, bool allow_pets = false, bool is_raid_group_only = true); void BreakInvis(); void Escape(); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 65d03ad2b6..afc923118b 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -551,7 +551,7 @@ void bot_command_cast(Client* c, const Seperator* sep) SPDat_Spell_Struct spell = spells[chosenSpellID]; LogTestDebug("Starting bySpellID checks."); //deleteme - if (!bot_iter->HasBotSpellEntry(chosenSpellID)) { + if (!bot_iter->CanUseBotSpell(chosenSpellID)) { LogTestDebug("{} does not have {} [#{}].", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme continue; } From 90fe8a31d751338ae0f2de489c0a8606babd34ae Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:00:12 -0600 Subject: [PATCH 088/394] Add missing alert round for ranged attacks --- zone/bot.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index d685a41f24..ad5aa206b6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1839,7 +1839,8 @@ bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { ) { if (!Ammo || ammoItem->GetCharges() < 1) { if (!GetCombatRoundForAlerts()) { - GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + SetCombatRoundForAlerts(); + BotGroupSay(this, "I do not have enough any ammo!"); } } From 4019e7da6500889aa0b6eca1d62f527e9205abe7 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:01:15 -0600 Subject: [PATCH 089/394] More castcheck improvements --- zone/bot.cpp | 25 +++++++++------ zone/botspellsai.cpp | 23 +------------- zone/spells.cpp | 76 ++++++++++++++++++++------------------------ 3 files changed, 50 insertions(+), 74 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ad5aa206b6..163bc8eb6b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9663,6 +9663,21 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (tar->GetSpecialAbility(SpecialAbility::MagicImmunity)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsImmuneToBotSpell(spell_id, this)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9686,16 +9701,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - - if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if (tar->IsImmuneToBotSpell(spell_id, this)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } if ( (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spell_id) != 0)) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 835e990400..d1eb10ecdd 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -207,7 +207,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; case BotSpellTypes::Charm: - if (tar->IsCharmed() || !tar->IsNPC() || tar->GetSpecialAbility(SpecialAbility::CharmImmunity)) { + if (HasPet() || tar->IsCharmed() || !tar->IsNPC() || tar->GetSpecialAbility(SpecialAbility::CharmImmunity)) { return false; } @@ -596,27 +596,6 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); - //if ( - // ( - // ( - // ( - // (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || - // spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || - // spells[AIBot_spells[i].spellid].target_type ==ST_Group || - // spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || - // ( - // tar == this && spells[AIBot_spells[i].spellid].target_type != ST_TargetsTarget - // ) - // ) && - // dist2 <= spells[AIBot_spells[i].spellid].aoe_range*spells[AIBot_spells[i].spellid].aoe_range - // ) || - // dist2 <= GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range)*GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range) - // ) && - // ( - // mana_cost <= GetMana() || - // IsBotNonSpellFighter() - // ) - //) { if (IsValidSpellRange(AIBot_spells[i].spellid, tar) && (mana_cost <= GetMana() || IsBotNonSpellFighter())) { casting_spell_AIindex = i; LogAI("spellid [{}] tar [{}] mana [{}] Name [{}]", AIBot_spells[i].spellid, tar->GetName(), mana_cost, spells[AIBot_spells[i].spellid].name); diff --git a/zone/spells.cpp b/zone/spells.cpp index 62b0e886bb..160a414bb6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -7579,8 +7579,9 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) { int effect_index; - if (caster == nullptr) + if (caster == nullptr) { return(false); + } //TODO: this function loops through the effect list for //this spell like 10 times, this could easily be consolidated @@ -7588,14 +7589,19 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) LogSpells("Checking to see if we are immune to spell [{}] cast by [{}]", spell_id, caster->GetName()); - if (!IsValidSpell(spell_id)) + if (!IsValidSpell(spell_id)) { return true; + } - if (IsBeneficialSpell(spell_id) && (caster->GetNPCTypeID())) //then skip the rest, stop NPCs aggroing each other with buff spells. 2013-03-05 + if (IsDispelSpell(spell_id) && GetSpecialAbility(SpecialAbility::DispellImmunity)) { return false; + } - if (IsMesmerizeSpell(spell_id)) - { + if (IsHarmonySpell(spell_id) && GetSpecialAbility(SpecialAbility::PacifyImmunity)) { + return false; + } + + if (IsMesmerizeSpell(spell_id)) { if (GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { return true; } @@ -7604,90 +7610,76 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) effect_index = GetSpellEffectIndex(spell_id, SE_Mez); assert(effect_index >= 0); // NPCs get to ignore the max level - if ((GetLevel() > spells[spell_id].max_value[effect_index]) && - (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))) - { + if ( + (GetLevel() > spells[spell_id].max_value[effect_index]) && + (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))) + ) { return true; } } // slow and haste spells - if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) - { + if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) { return true; } // client vs client fear - if (IsEffectInSpell(spell_id, SE_Fear)) - { + if (IsEffectInSpell(spell_id, SE_Fear)) { effect_index = GetSpellEffectIndex(spell_id, SE_Fear); + if (GetSpecialAbility(SpecialAbility::FearImmunity)) { return true; } - else if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) - { + else if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) { LogSpells("Clients cannot fear eachother!"); caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up return true; } - else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) - { + else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { return true; } - else if (CheckAATimer(aaTimerWarcry)) - { + else if (CheckAATimer(aaTimerWarcry)) { return true; } } - if (IsCharmSpell(spell_id)) - { - if (GetSpecialAbility(SpecialAbility::CharmImmunity)) - { + if (IsCharmSpell(spell_id)) { + if (GetSpecialAbility(SpecialAbility::CharmImmunity)) { return true; } - if (this == caster) - { + if (this == caster) { return true; } //let npcs cast whatever charm on anyone - if (!caster->IsNPC()) - { + if (!caster->IsNPC()) { // check level limit of charm spell effect_index = GetSpellEffectIndex(spell_id, SE_Charm); assert(effect_index >= 0); - if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) - { + if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { return true; } } } - if - ( - IsEffectInSpell(spell_id, SE_Root) || - IsEffectInSpell(spell_id, SE_MovementSpeed) - ) - { + if ( + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) { if (GetSpecialAbility(SpecialAbility::SnareImmunity)) { return true; } } - if (IsLifetapSpell(spell_id)) - { - if (this == caster) - { + if (IsLifetapSpell(spell_id)) { + if (this == caster) { return true; } } - if (IsSacrificeSpell(spell_id)) - { - if (this == caster) - { + if (IsSacrificeSpell(spell_id)) { + if (this == caster) { return true; } } From f67df44d99671cdff2fa04e795fa4a8f15ee1067 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:01:26 -0600 Subject: [PATCH 090/394] CanUseBotSpell for ^cast --- zone/botspellsai.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index d1eb10ecdd..e61a407d1b 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2649,6 +2649,30 @@ bool Bot::HasBotSpellEntry(uint16 spell_id) { return false; } +bool Bot::CanUseBotSpell(uint16 spell_id) { + if (AIBot_spells.empty()) { + return false; + } + + for (const auto& s : AIBot_spells) { + if (!IsValidSpell(s.spellid)) { + return false; + } + + if (s.spellid != spell_id) { + continue; + } + + if (s.minlevel > GetLevel()) { + return false; + } + + return true; + } + + return false; +} + bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { if (!IsValidSpell(spell_id) || !tar) { return false; From a142298e3413c144c3fb4c105614312d04e39206 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:58:25 -0600 Subject: [PATCH 091/394] remove ht/loh from attack ai --- zone/bot.cpp | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 163bc8eb6b..9a498bc083 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5223,7 +5223,6 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); bool ma_time = monkattack_timer.Check(false); - bool ka_time = knightattack_timer.Check(false); if (taunt_time) { @@ -5240,36 +5239,10 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if ((ca_time || ma_time || ka_time) && !IsAttackAllowed(target)) { + if ((ca_time || ma_time) && !IsAttackAllowed(target)) { return; } - if (ka_time) { - - switch (GetClass()) { - case Class::ShadowKnight: { - CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); - knightattack_timer.Start(HarmTouchReuseTime * 1000); - - break; - } - case Class::Paladin: { - if (GetHPRatio() < 20) { - CastSpell(SPELL_LAY_ON_HANDS, GetID()); - knightattack_timer.Start(LayOnHandsReuseTime * 1000); - } - else { - knightattack_timer.Start(2000); - } - - break; - } - default: { - break; - } - } - } - if (IsTaunting() && target->IsNPC() && taunt_time) { if (GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { BotGroupSay( From d9ab4a5f2786625fea969b92414f0d229f28bdbc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:35:55 -0600 Subject: [PATCH 092/394] remove SetCombatRoundForAlerts that triggered every engagement --- zone/bot.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 9a498bc083..97e0978272 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2885,10 +2885,6 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo // Calculate melee distances CalcMeleeDistances(tar, p_item, s_item, backstab_weapon, behindMob, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); - if (!GetCombatRoundForAlerts()) { - SetCombatRoundForAlerts(); - } - if (tar_distance <= melee_distance) { atCombatRange = true; } From 67ce8d44bb8430c87385b28d25242d48634b3dec Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:36:09 -0600 Subject: [PATCH 093/394] Add RangedAttackImmunity checks before trying to ranged attack --- zone/bot.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 97e0978272..56d8a39bed 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2211,7 +2211,7 @@ void Bot::AI_Process() } if (atCombatRange) { - if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + if (!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { @@ -2288,7 +2288,7 @@ void Bot::AI_Process() return; } - if (IsBotRanged() && ranged_timer.Check(false)) { + if (!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && IsBotRanged() && ranged_timer.Check(false)) { if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { BotRangedAttack(tar, true); } From 872abdc795434ee276c6798c0be97928dfb43164 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:33:11 -0600 Subject: [PATCH 094/394] move bot backstab to mob --- zone/attack.cpp | 3 +- zone/bot.cpp | 28 +++++++++---- zone/bot.h | 3 -- zone/special_attacks.cpp | 85 ++++++++++++++++++++++++++++++---------- 4 files changed, 88 insertions(+), 31 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 4a2e03bbf2..fa6e25e063 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -6461,8 +6461,9 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac } else { int ass = TryAssassinate(defender, hit.skill); - if (ass > 0) + if (ass > 0) { hit.damage_done = ass; + } } } else if (hit.skill == EQ::skills::SkillFrenzy && GetClass() == Class::Berserker && GetLevel() > 50) { diff --git a/zone/bot.cpp b/zone/bot.cpp index 56d8a39bed..25253c1830 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5383,18 +5383,32 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } if (skill_to_use == EQ::skills::SkillFrenzy) { - int AtkRounds = 3; + int AtkRounds = 1; + float HasteMod = (FrenzyReuseTime - 1) / (GetHaste() * 0.01f); + reuse = (FrenzyReuseTime * 1000); DoAnim(anim2HSlashing); - reuse = (FrenzyReuseTime * 1000); - did_attack = true; - while(AtkRounds > 0) { - if (GetTarget() && (AtkRounds == 1 || zone->random.Int(0, 100) < 75)) { - DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, reuse, true); - } + // bards can do riposte frenzy for some reason + if (!IsRiposte && GetClass() == Class::Berserker) { + int chance = GetLevel() * 2 + GetSkill(EQ::skills::SkillFrenzy); + if (zone->random.Roll0(450) < chance) + AtkRounds++; + if (zone->random.Roll0(450) < chance) + AtkRounds++; + } + + while (AtkRounds > 0) { + if (GetTarget() != this) + DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, HasteMod); AtkRounds--; } + + if (reuse > 0 && IsRiposte) { + reuse = 0; + } + + did_attack = true; } if (skill_to_use == EQ::skills::SkillKick) { diff --git a/zone/bot.h b/zone/bot.h index 287721ae94..457f7eebf1 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -249,9 +249,6 @@ class Bot : public NPC { inline uint16 MaxSkill(EQ::skills::SkillType skillid) { return MaxSkill(skillid, GetClass(), GetLevel()); } int GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target = nullptr) override; void DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance = false); - void TryBackstab(Mob *other,int ReuseTime = 10) override; - void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10) override; - void RogueAssassinate(Mob* other) override; void DoClassAttacks(Mob *target, bool IsRiposte=false); void CalcBonuses() override; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 5e9c6f7920..cd8b8ac912 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -709,54 +709,78 @@ int Mob::MonkSpecialAttack(Mob *other, uint8 unchecked_type) } void Mob::TryBackstab(Mob *other, int ReuseTime) { - if(!other) + if (!other) { return; + } bool bIsBehind = false; bool bCanFrontalBS = false; //make sure we have a proper weapon if we are a client. - if(IsClient()) { + if (IsClient()) { const EQ::ItemInstance *wpn = CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); + if (!wpn || (wpn->GetItem()->ItemType != EQ::item::ItemType1HPiercing)){ MessageString(Chat::Red, BACKSTAB_WEAPON); return; } } + else if (IsBot()) { + const EQ::ItemInstance* inst = CastToBot()->GetBotItem(EQ::invslot::slotPrimary); + const EQ::ItemData* botpiercer = nullptr; + + if (inst) { + botpiercer = inst->GetItem(); + } + + if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { + if (!CastToBot()->GetCombatRoundForAlerts()) { + CastToBot()->SetCombatRoundForAlerts(); + CastToBot()->BotGroupSay(this, "I can't backstab with this weapon!"); + } + + return; + } + } //Live AA - Triple Backstab int tripleChance = itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab; - if (BehindMob(other, GetX(), GetY())) + if (BehindMob(other, GetX(), GetY())) { bIsBehind = true; - + } else { //Live AA - Seized Opportunity int FrontalBSChance = itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance; - if (FrontalBSChance && zone->random.Roll(FrontalBSChance)) + if (FrontalBSChance && zone->random.Roll(FrontalBSChance)) { bCanFrontalBS = true; + } } if (bIsBehind || bCanFrontalBS || (IsNPC() && CanFacestab())) { // Player is behind other OR can do Frontal Backstab - if (bCanFrontalBS && IsClient()) // I don't think there is any message ... - CastToClient()->Message(Chat::White,"Your fierce attack is executed with such grace, your target did not see it coming!"); + if (bCanFrontalBS && IsClient()) { // I don't think there is any message ... + CastToClient()->Message(Chat::White, "Your fierce attack is executed with such grace, your target did not see it coming!"); + } RogueBackstab(other,false,ReuseTime); + if (level >= RuleI(Combat, DoubleBackstabLevelRequirement)) { // TODO: 55-59 doesn't appear to match just checking double attack, 60+ does though - if(IsClient() && CastToClient()->CheckDoubleAttack()) - { - if(other->GetHP() > 0) - RogueBackstab(other,false,ReuseTime); + if(IsOfClientBot() && CastToClient()->CheckDoubleAttack()) { + if (other->GetHP() > 0) { + RogueBackstab(other, false, ReuseTime); + } - if (tripleChance && other->GetHP() > 0 && zone->random.Roll(tripleChance)) - RogueBackstab(other,false,ReuseTime); + if (tripleChance && other->GetHP() > 0 && zone->random.Roll(tripleChance)) { + RogueBackstab(other, false, ReuseTime); + } } } - if(IsClient()) + if (IsClient()) { CastToClient()->CheckIncreaseSkill(EQ::skills::SkillBackstab, other, 10); + } } //Live AA - Chaotic Backstab @@ -766,14 +790,20 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { //we can stab from any angle, we do min damage though. // chaotic backstab can't double etc Seized can, but that's because it's a chance to do normal BS // Live actually added SPA 473 which grants chance to double here when they revamped chaotic/seized + RogueBackstab(other, true, ReuseTime); - if(IsClient()) + + if (IsClient()) { CastToClient()->CheckIncreaseSkill(EQ::skills::SkillBackstab, other, 10); + } + m_specialattacks = eSpecialAttacks::None; int double_bs_front = aabonuses.Double_Backstab_Front + itembonuses.Double_Backstab_Front + spellbonuses.Double_Backstab_Front; - if (double_bs_front && other->GetHP() > 0 && zone->random.Roll(double_bs_front)) + + if (double_bs_front && other->GetHP() > 0 && zone->random.Roll(double_bs_front)) { RogueBackstab(other, false, ReuseTime); + } } else { //We do a single regular attack if we attack from the front without chaotic stab Attack(other, EQ::invslot::slotPrimary); @@ -790,10 +820,25 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) // make sure we can hit (bane, magical, etc) if (IsClient()) { - const EQ::ItemInstance *wpn = CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); - if (!GetWeaponDamage(other, wpn)) + const EQ::ItemInstance* wpn = CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); + + if (!GetWeaponDamage(other, wpn)) { + return; + } + } + else if (IsBot()) { + EQ::ItemInstance* botweaponInst = CastToBot()->GetBotItem(EQ::invslot::slotPrimary); + + if (botweaponInst) { + if (!GetWeaponDamage(other, botweaponInst)) { + return; + } + } + else if (!GetWeaponDamage(other, (const EQ::ItemData*)nullptr)) { return; - } else if (!GetWeaponDamage(other, (const EQ::ItemData*)nullptr)){ + } + } + else if (!GetWeaponDamage(other, (const EQ::ItemData*)nullptr)) { return; } @@ -2454,7 +2499,7 @@ int Mob::TryAssassinate(Mob *defender, EQ::skills::SkillType skillInUse) int chance = GetDEX(); if (skillInUse == EQ::skills::SkillBackstab) { chance = 100 * chance / (chance + 3500); - if (IsClient() || IsBot()) { + if (IsOfClientBot()) { chance += GetHeroicDEX(); } chance *= 10; From 783781fe20c745daaeaa3d54e618f8617e14e084 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:40:22 -0600 Subject: [PATCH 095/394] fix MinStatusToBypassCreateLimit --- zone/client_bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index bfe4cdb58f..9d796cef10 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -20,7 +20,7 @@ uint32 Client::GetBotCreationLimit(uint8 class_id) uint32 bot_creation_limit = RuleI(Bots, CreationLimit); if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { - return RuleI(Bots, MinStatusToBypassCreateLimit); + return RuleI(Bots, StatusCreateLimit); } const auto bucket_name = fmt::format( From 372fd044defa5a95c84ba277038bb6aed72cdea6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:06:54 -0600 Subject: [PATCH 096/394] more backstab to mob cleanup --- zone/bot.cpp | 98 +++------------------------------------------------- 1 file changed, 5 insertions(+), 93 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 25253c1830..9949de8881 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5121,96 +5121,6 @@ void Bot::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 max TrySkillProc(who, skill, (ReuseTime * 1000), true); } -void Bot::TryBackstab(Mob *other, int ReuseTime) { - if (!other) - return; - - bool bIsBehind = false; - bool bCanFrontalBS = false; - const EQ::ItemInstance* inst = GetBotItem(EQ::invslot::slotPrimary); - const EQ::ItemData* botpiercer = nullptr; - if (inst) - botpiercer = inst->GetItem(); - - if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { - if (!GetCombatRoundForAlerts()) { - SetCombatRoundForAlerts(); - BotGroupSay(this, "I can't backstab with this weapon!"); - } - - return; - } - - int tripleChance = (itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab); - if (BehindMob(other, GetX(), GetY())) - bIsBehind = true; - else { - int FrontalBSChance = (itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance); - if (FrontalBSChance && (FrontalBSChance > zone->random.Int(0, 100))) - bCanFrontalBS = true; - } - - if (bIsBehind || bCanFrontalBS) { - int chance = (10 + (GetDEX() / 10) + (itembonuses.HeroicDEX / 10)); - if (level >= 60 && other->GetLevel() <= 45 && !other->CastToNPC()->IsEngaged() && other->GetHP()<= 32000 && other->IsNPC() && zone->random.Real(0, 99) < chance) { - entity_list.MessageCloseString(this, false, 200, Chat::MeleeCrit, ASSASSINATES, GetName()); - RogueAssassinate(other); - } else { - RogueBackstab(other); - if (level > 54) { - float DoubleAttackProbability = ((GetSkill(EQ::skills::SkillDoubleAttack) + GetLevel()) / 500.0f); - if (zone->random.Real(0, 1) < DoubleAttackProbability) { - if (other->GetHP() > 0) - RogueBackstab(other,false,ReuseTime); - - if (tripleChance && other->GetHP() > 0 && tripleChance > zone->random.Int(0, 100)) - RogueBackstab(other,false,ReuseTime); - } - } - } - } else if (aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) { - m_specialattacks = eSpecialAttacks::ChaoticStab; - RogueBackstab(other, true); - m_specialattacks = eSpecialAttacks::None; - } - else - Attack(other, EQ::invslot::slotPrimary); -} - -void Bot::RogueBackstab(Mob *other, bool min_damage, int ReuseTime) -{ - if (!other) - return; - - EQ::ItemInstance *botweaponInst = GetBotItem(EQ::invslot::slotPrimary); - if (botweaponInst) { - if (!GetWeaponDamage(other, botweaponInst)) - return; - } else if (!GetWeaponDamage(other, (const EQ::ItemData *)nullptr)) { - return; - } - - int64 hate = 0; - - int base_damage = GetBaseSkillDamage(EQ::skills::SkillBackstab, other); - hate = base_damage; - - DoSpecialAttackDamage(other, EQ::skills::SkillBackstab, base_damage, 0, hate, ReuseTime); - DoAnim(anim1HPiercing); -} - -void Bot::RogueAssassinate(Mob* other) { - EQ::ItemInstance* botweaponInst = GetBotItem(EQ::invslot::slotPrimary); - if (botweaponInst) { - if (GetWeaponDamage(other, botweaponInst)) - other->Damage(this, 32000, SPELL_UNKNOWN, EQ::skills::SkillBackstab); - else - other->Damage(this, -5, SPELL_UNKNOWN, EQ::skills::SkillBackstab); - } - - DoAnim(anim1HPiercing); -} - void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if (!target || GetAppearance() == eaDead || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) { return; @@ -5399,7 +5309,6 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { while (AtkRounds > 0) { if (GetTarget() != this) - DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, HasteMod); AtkRounds--; } @@ -5479,11 +5388,14 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if (skill_to_use == EQ::skills::SkillBackstab) { reuse = (BackstabReuseTime * 1000); did_attack = true; - if (IsRiposte) + + if (IsRiposte) { reuse = 0; + } - TryBackstab(target,reuse); + Mob::TryBackstab(target, reuse); } + classattack_timer.Start(reuse / HasteModifier); } From a28a123209df01521c7454fdbcbd3ddc1db6f77c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:07:54 -0600 Subject: [PATCH 097/394] add bot checks to tryheadshot / tryassassinate --- zone/attack.cpp | 1 + zone/special_attacks.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index fa6e25e063..b29eb6266b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -6461,6 +6461,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac } else { int ass = TryAssassinate(defender, hit.skill); + if (ass > 0) { hit.damage_done = ass; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index cd8b8ac912..e7c5350be5 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -2453,7 +2453,7 @@ int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) // Only works on YOUR target. if ( defender && - !defender->IsClient() && + !defender->IsOfClientBot() && skillInUse == EQ::skills::SkillArchery && GetTarget() == defender && (defender->GetBodyType() == BodyType::Humanoid || !RuleB(Combat, HeadshotOnlyHumanoids)) && @@ -2466,7 +2466,7 @@ int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) if (HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)) { int chance = GetDEX(); chance = 100 * chance / (chance + 3500); - if (IsClient() || IsBot()) { + if (IsOfClientBot()) { chance += GetHeroicDEX() / 25; } chance *= 10; @@ -2490,7 +2490,7 @@ int Mob::TryAssassinate(Mob *defender, EQ::skills::SkillType skillInUse) { if ( defender && - !defender->IsClient() && + !defender->IsOfClientBot() && GetLevel() >= RuleI(Combat, AssassinateLevelRequirement) && (skillInUse == EQ::skills::SkillBackstab || skillInUse == EQ::skills::SkillThrowing) && (defender->GetBodyType() == BodyType::Humanoid || !RuleB(Combat, AssassinateOnlyHumanoids)) && From 6574f780dbbea515567385fd89ad6cecd1c828ac Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 26 Apr 2024 22:38:56 -0500 Subject: [PATCH 098/394] Bot Rework --- common/classes.h | 2 + .../database_update_manifest_bots.cpp | 340 ++ common/database_schema.h | 1 + common/eqemu_logsys.h | 16 +- common/eqemu_logsys_log_aliases.h | 70 + .../base/base_bot_data_repository.h | 85 +- .../base/base_bot_settings_repository.h | 464 ++ common/repositories/bot_data_repository.h | 40 - common/repositories/bot_settings_repository.h | 50 + common/ruletypes.h | 113 + common/spdat.cpp | 783 ++- common/spdat.h | 100 +- zone/aggro.cpp | 119 + zone/attack.cpp | 7 +- zone/bot.cpp | 4230 +++++++++++++---- zone/bot.h | 342 +- zone/bot_command.cpp | 229 +- zone/bot_command.h | 67 +- zone/bot_commands/actionable.cpp | 69 +- zone/bot_commands/aggressive.cpp | 24 +- zone/bot_commands/appearance.cpp | 2 +- zone/bot_commands/apply_poison.cpp | 1 + zone/bot_commands/attack.cpp | 9 +- zone/bot_commands/behind_mob.cpp | 173 + zone/bot_commands/bind_affinity.cpp | 1 + zone/bot_commands/bot.cpp | 369 +- zone/bot_commands/bot_settings.cpp | 43 + zone/bot_commands/cast.cpp | 301 ++ zone/bot_commands/caster_range.cpp | 9 +- zone/bot_commands/class_race_list.cpp | 113 + zone/bot_commands/click_item.cpp | 4 +- zone/bot_commands/copy_settings.cpp | 366 ++ zone/bot_commands/default_settings.cpp | 473 ++ zone/bot_commands/defensive.cpp | 11 +- zone/bot_commands/follow.cpp | 100 +- zone/bot_commands/guard.cpp | 6 +- zone/bot_commands/hold.cpp | 6 +- zone/bot_commands/illusion_block.cpp | 172 + zone/bot_commands/inventory.cpp | 21 +- zone/bot_commands/max_melee_range.cpp | 172 + zone/bot_commands/mesmerize.cpp | 44 +- zone/bot_commands/pet.cpp | 47 +- zone/bot_commands/pull.cpp | 18 +- zone/bot_commands/release.cpp | 2 +- zone/bot_commands/sit_hp_percent.cpp | 172 + zone/bot_commands/sit_in_combat.cpp | 172 + zone/bot_commands/sit_mana_percent.cpp | 172 + zone/bot_commands/spell.cpp | 4 +- zone/bot_commands/spell_aggro_checks.cpp | 236 + zone/bot_commands/spell_delays.cpp | 242 + zone/bot_commands/spell_engaged_priority.cpp | 240 + zone/bot_commands/spell_holds.cpp | 229 + zone/bot_commands/spell_idle_priority.cpp | 240 + zone/bot_commands/spell_max_hp_pct.cpp | 236 + zone/bot_commands/spell_max_mana_pct.cpp | 236 + zone/bot_commands/spell_max_thresholds.cpp | 243 + zone/bot_commands/spell_min_hp_pct.cpp | 236 + zone/bot_commands/spell_min_mana_pct.cpp | 236 + zone/bot_commands/spell_min_thresholds.cpp | 244 + zone/bot_commands/spell_pursue_priority.cpp | 240 + zone/bot_commands/spell_target_count.cpp | 236 + zone/bot_commands/suspend.cpp | 2 +- zone/bot_commands/taunt.cpp | 15 +- zone/bot_commands/timer.cpp | 9 +- zone/bot_database.cpp | 343 +- zone/bot_database.h | 26 +- zone/bot_structs.h | 5 + zone/botspellsai.cpp | 3483 ++++++-------- zone/client.cpp | 275 +- zone/client.h | 32 + zone/client_bot.cpp | 8 + zone/client_mods.cpp | 6 +- zone/client_packet.cpp | 13 +- zone/command.cpp | 10 + zone/command.h | 5 + zone/entity.cpp | 1 + zone/entity.h | 2 +- zone/exp.cpp | 1 + zone/gm_commands/illusion_block.cpp | 31 + zone/gm_commands/spell_delays.cpp | 215 + zone/gm_commands/spell_holds.cpp | 218 + zone/gm_commands/spell_max_thresholds.cpp | 216 + zone/gm_commands/spell_min_thresholds.cpp | 216 + zone/lua_bot.cpp | 6 - zone/lua_bot.h | 1 - zone/merc.cpp | 4 +- zone/mob.cpp | 825 +++- zone/mob.h | 103 + zone/perl_bot.cpp | 6 - zone/perl_client.cpp | 4 +- zone/perl_npc.cpp | 2 +- zone/questmgr.cpp | 2 +- zone/spell_effects.cpp | 10 +- zone/spells.cpp | 203 +- 94 files changed, 15747 insertions(+), 3779 deletions(-) create mode 100644 common/repositories/base/base_bot_settings_repository.h create mode 100644 common/repositories/bot_settings_repository.h create mode 100644 zone/bot_commands/behind_mob.cpp create mode 100644 zone/bot_commands/bot_settings.cpp create mode 100644 zone/bot_commands/cast.cpp create mode 100644 zone/bot_commands/class_race_list.cpp create mode 100644 zone/bot_commands/copy_settings.cpp create mode 100644 zone/bot_commands/default_settings.cpp create mode 100644 zone/bot_commands/illusion_block.cpp create mode 100644 zone/bot_commands/max_melee_range.cpp create mode 100644 zone/bot_commands/sit_hp_percent.cpp create mode 100644 zone/bot_commands/sit_in_combat.cpp create mode 100644 zone/bot_commands/sit_mana_percent.cpp create mode 100644 zone/bot_commands/spell_aggro_checks.cpp create mode 100644 zone/bot_commands/spell_delays.cpp create mode 100644 zone/bot_commands/spell_engaged_priority.cpp create mode 100644 zone/bot_commands/spell_holds.cpp create mode 100644 zone/bot_commands/spell_idle_priority.cpp create mode 100644 zone/bot_commands/spell_max_hp_pct.cpp create mode 100644 zone/bot_commands/spell_max_mana_pct.cpp create mode 100644 zone/bot_commands/spell_max_thresholds.cpp create mode 100644 zone/bot_commands/spell_min_hp_pct.cpp create mode 100644 zone/bot_commands/spell_min_mana_pct.cpp create mode 100644 zone/bot_commands/spell_min_thresholds.cpp create mode 100644 zone/bot_commands/spell_pursue_priority.cpp create mode 100644 zone/bot_commands/spell_target_count.cpp create mode 100644 zone/gm_commands/illusion_block.cpp create mode 100644 zone/gm_commands/spell_delays.cpp create mode 100644 zone/gm_commands/spell_holds.cpp create mode 100644 zone/gm_commands/spell_max_thresholds.cpp create mode 100644 zone/gm_commands/spell_min_thresholds.cpp diff --git a/common/classes.h b/common/classes.h index 96e5cad263..dc586ceebf 100644 --- a/common/classes.h +++ b/common/classes.h @@ -131,6 +131,8 @@ static std::map class_names = { #define ARMOR_TYPE_LAST ARMOR_TYPE_PLATE #define ARMOR_TYPE_COUNT 5 +#define BOT_CLASS_BASE_ID_PREFIX 3000 + const char* GetClassIDName(uint8 class_id, uint8 level = 0); diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 48c9aa7f85..afaa9f23ea 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -161,6 +161,346 @@ ADD COLUMN `extra_haste` mediumint(8) NOT NULL DEFAULT 0 AFTER `wis`; .sql = R"( ALTER TABLE `bot_spells_entries` CHANGE COLUMN `spellid` `spell_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER `npc_spells_id`; +)" + }, + ManifestEntry{ + .version = 9046, + .description = "2024_05_18_bot_settings.sql", + .check = "SHOW TABLES LIKE 'bot_settings'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `bot_settings` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `char_id` INT UNSIGNED NOT NULL, + `bot_id` INT UNSIGNED NOT NULL, + `setting_id` INT UNSIGNED NOT NULL, + `setting_type` INT UNSIGNED NOT NULL, + `value` INT UNSIGNED NOT NULL, + `category_name` VARCHAR(64) NULL DEFAULT '', + `setting_name` VARCHAR(64) NULL DEFAULT '', + PRIMARY KEY (`id`) USING BTREE +) +COLLATE='utf8mb4_general_ci'; + +INSERT INTO bot_settings SELECT NULL, 0, bd.`bot_id`, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd +JOIN rule_values rv +WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' +AND bd.expansion_bitmask != rv.rule_value; + +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, `follow_distance`, 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; + +INSERT INTO bot_settings +SELECT NULL, 0, `bot_id`, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' +FROM ( + SELECT `bot_id`, + (CASE + WHEN (`class` IN (2, 6, 10, 11, 12, 13, 14)) THEN 13 + ELSE 255 + END) AS `sml`, + `stop_melee_level` + FROM bot_data +) AS `subquery` +WHERE `sml` != `stop_melee_level`; + +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; + +INSERT INTO bot_settings +SELECT NULL, 0, `bot_id`, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' +FROM ( + SELECT `bot_id`, + (CASE + WHEN (`class` IN (1, 7, 19, 16)) THEN 0 + WHEN `class` = 8 THEN 0 + ELSE 90 + END) AS `casterRange`, + `caster_range` + FROM bot_data +) AS `subquery` +WHERE `casterRange` != `caster_range`; + +ALTER TABLE `bot_data` + DROP COLUMN `show_helm`; +ALTER TABLE `bot_data` + DROP COLUMN `follow_distance`; +ALTER TABLE `bot_data` + DROP COLUMN `stop_melee_level`; +ALTER TABLE `bot_data` + DROP COLUMN `expansion_bitmask`; +ALTER TABLE `bot_data` + DROP COLUMN `enforce_spell_settings`; +ALTER TABLE `bot_data` + DROP COLUMN `archery_setting`; +ALTER TABLE `bot_data` + DROP COLUMN `caster_range`; + +UPDATE `bot_command_settings` SET `aliases`= 'bh' WHERE `bot_command`='behindmob'; +UPDATE `bot_command_settings` SET `aliases`= 'bs|settings' WHERE `bot_command`='botsettings'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ranged|toggleranged|btr') ELSE 'ranged|toggleranged|btr' END WHERE `bot_command`='bottoggleranged' AND `aliases` NOT LIKE '%ranged|toggleranged|btr%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|cr') ELSE 'cr' END WHERE `bot_command`='casterrange' AND `aliases` NOT LIKE '%cr%'; +UPDATE `bot_command_settings` SET `aliases`= 'copy' WHERE `bot_command`='copysettings'; +UPDATE `bot_command_settings` SET `aliases`= 'default' WHERE `bot_command`='defaultsettings'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|enforce') ELSE 'enforce' END WHERE `bot_command`='enforcespellsettings' AND `aliases` NOT LIKE '%enforce%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ib') ELSE 'ib' END WHERE `bot_command`='illusionblock' AND `aliases` NOT LIKE '%ib%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ig') ELSE 'invgive|ig' END WHERE `bot_command`='inventorygive' AND `aliases` NOT LIKE '%ig%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|il') ELSE 'invlist|il' END WHERE `bot_command`='inventorylist' AND `aliases` NOT LIKE '%il%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ir') ELSE 'invremove|ir' END WHERE `bot_command`='inventoryremove' AND `aliases` NOT LIKE '%ir%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|iw') ELSE 'invwindow|iw' END WHERE `bot_command`='inventorywindow' AND `aliases` NOT LIKE '%iw%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|iu') ELSE 'iu' END WHERE `bot_command`='itemuse' AND `aliases` NOT LIKE '%iu%'; +UPDATE `bot_command_settings` SET `aliases`= 'mmr' WHERE `bot_command`='maxmeleerange'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|pp') ELSE 'pp' END WHERE `bot_command`='pickpocket' AND `aliases` NOT LIKE '%pp%'; +UPDATE `bot_command_settings` SET `aliases`= 'sithp' WHERE `bot_command`='sithppercent'; +UPDATE `bot_command_settings` SET `aliases`= 'sitcombat' WHERE `bot_command`='sitincombat'; +UPDATE `bot_command_settings` SET `aliases`= 'sitmana' WHERE `bot_command`='sitmanapercent'; +UPDATE `bot_command_settings` SET `aliases`= 'aggrochecks' WHERE `bot_command`='spellaggrochecks'; +UPDATE `bot_command_settings` SET `aliases`= 'delays' WHERE `bot_command`='spelldelays'; +UPDATE `bot_command_settings` SET `aliases`= 'engagedpriority' WHERE `bot_command`='spellengagedpriority'; +UPDATE `bot_command_settings` SET `aliases`= 'holds' WHERE `bot_command`='spellholds'; +UPDATE `bot_command_settings` SET `aliases`= 'idlepriority' WHERE `bot_command`='spellidlepriority'; +UPDATE `bot_command_settings` SET `aliases`= 'maxhp' WHERE `bot_command`='spellmaxhppct'; +UPDATE `bot_command_settings` SET `aliases`= 'maxmana' WHERE `bot_command`='spellmaxmanapct'; +UPDATE `bot_command_settings` SET `aliases`= 'maxthresholds' WHERE `bot_command`='spellmaxthresholds'; +UPDATE `bot_command_settings` SET `aliases`= 'minhp' WHERE `bot_command`='spellminhppct'; +UPDATE `bot_command_settings` SET `aliases`= 'minmana' WHERE `bot_command`='spellminmanapct'; +UPDATE `bot_command_settings` SET `aliases`= 'minthresholds' WHERE `bot_command`='spellminthresholds'; +UPDATE `bot_command_settings` SET `aliases`= 'pursuepriority' WHERE `bot_command`='spellpursuepriority'; +UPDATE `bot_command_settings` SET `aliases`= 'targetcount' WHERE `bot_command`='spelltargetcount'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|vc') ELSE 'vc' END WHERE `bot_command`='viewcombos' AND `aliases` NOT LIKE '%vc%'; +)" + }, + ManifestEntry{ + .version = 9047, + .description = "2024_05_18_bot_update_spell_types.sql", + .check = "SELECT * FROM `bot_spells_entries` WHERE `type` > 21", + .condition = "not_empty", + .match = "", + .sql = R"( +UPDATE `bot_spells_entries` SET `type` = 0 WHERE `type` = 1; +UPDATE `bot_spells_entries` SET `type` = 1 WHERE `type` = 2; +UPDATE `bot_spells_entries` SET `type` = 2 WHERE `type` = 4; +UPDATE `bot_spells_entries` SET `type` = 3 WHERE `type` = 8; +UPDATE `bot_spells_entries` SET `type` = 4 WHERE `type` = 16; +UPDATE `bot_spells_entries` SET `type` = 5 WHERE `type` = 32; +UPDATE `bot_spells_entries` SET `type` = 6 WHERE `type` = 64; +UPDATE `bot_spells_entries` SET `type` = 7 WHERE `type` = 128; +UPDATE `bot_spells_entries` SET `type` = 8 WHERE `type` = 256; +UPDATE `bot_spells_entries` SET `type` = 9 WHERE `type` = 512; +UPDATE `bot_spells_entries` SET `type` = 10 WHERE `type` = 1024; +UPDATE `bot_spells_entries` SET `type` = 11 WHERE `type` = 2048; +UPDATE `bot_spells_entries` SET `type` = 12 WHERE `type` = 4096; +UPDATE `bot_spells_entries` SET `type` = 13 WHERE `type` = 8192; +UPDATE `bot_spells_entries` SET `type` = 14 WHERE `type` = 16384; +UPDATE `bot_spells_entries` SET `type` = 15 WHERE `type` = 32768; +UPDATE `bot_spells_entries` SET `type` = 16 WHERE `type` = 65536; +UPDATE `bot_spells_entries` SET `type` = 17 WHERE `type` = 131072; +UPDATE `bot_spells_entries` SET `type` = 18 WHERE `type` = 262144; +UPDATE `bot_spells_entries` SET `type` = 19 WHERE `type` = 524288; +UPDATE `bot_spells_entries` SET `type` = 20 WHERE `type` = 1048576; +UPDATE `bot_spells_entries` SET `type` = 21 WHERE `type` = 2097152; +)" + }, + ManifestEntry{ + .version = 9048, + .description = "2024_05_18_bot_fear_spell_type.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 22", + .condition = "empty", + .match = "", + .sql = R"( +UPDATE bot_spells_entries b, spells_new s +SET b.`type` = 22 +WHERE b.spellid = s.id +AND ( + s.`effectid1` = 23 OR + s.`effectid2` = 23 OR + s.`effectid3` = 23 OR + s.`effectid4` = 23 OR + s.`effectid5` = 23 OR + s.`effectid6` = 23 OR + s.`effectid7` = 23 OR + s.`effectid8` = 23 OR + s.`effectid9` = 23 OR + s.`effectid10` = 23 OR + s.`effectid11` = 23 OR + s.`effectid12` = 23 + ); +)" + }, + ManifestEntry{ + .version = 9049, + .description = "2024_05_18_correct_bot_spell_entries_types.sql", + .check = "SELECT * FROM `bot_spells_entries` where `npc_spells_id` = 3002 AND `spellid` = 14312", + .condition = "empty", + .match = "", + .sql = R"( +-- Class fixes +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14312; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14313; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14314; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15186; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15187; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15188; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14446; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14447; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14467; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14468; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14469; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14955; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14956; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14387; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14388; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14389; + +-- Minlevel fixes +UPDATE bot_spells_entries SET `minlevel` = 34 WHERE `spellid` = 1445 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 2 WHERE `spellid` = 229 AND `npc_spells_id` = 3011; +UPDATE bot_spells_entries SET `minlevel` = 13 WHERE `spellid` = 333 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 29 WHERE `spellid` = 106 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 38 WHERE `spellid` = 754 AND `npc_spells_id` = 3010; +UPDATE bot_spells_entries SET `minlevel` = 58 WHERE `spellid` = 2589 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 67 WHERE `spellid` = 5305 AND `npc_spells_id` = 3004; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 23 WHERE `spellid` = 738 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 51 WHERE `spellid` = 1751 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 7 WHERE `spellid` = 734 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 5 WHERE `spellid` = 717 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15186 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15187 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15188 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14955 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14956 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 78 WHERE `spellid` = 14387 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14388 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14389 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; + +-- Maxlevel fixes +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; + +-- Type fixes +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 201; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 752; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 2117; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2542; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2544; +UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 2115; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1403; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1405; +UPDATE bot_spells_entries SET `type` = 9 WHERE `spellid` = 289; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 294; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 302; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 521; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 185; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 450; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 4074; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 195; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1712; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1703; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 3229; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 3345; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5509; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6826; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 281; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 505; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 526; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 110; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 506; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 162; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 111; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 507; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 527; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 163; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 112; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1588; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1573; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1592; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1577; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1578; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 1576; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3386; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3387; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 4900; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3395; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5394; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5392; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6827; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 5416; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1437; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1436; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5348; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 8008; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 2571; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 370; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1741; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1296; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2634; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2942; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 3462; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6828; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14312; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14313; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14314; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18392; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18393; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18394; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 15186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15187; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15188; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14446; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14447; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14467; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14468; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14469; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14267; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14268; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14269; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14955; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14956; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 14387; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14388; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14389; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 10436; + +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3440; -- Ro's Illumination [#3440] from DoT [#8] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 303; -- Whirl till you hurl [#303] from Nuke [#0] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 619; -- Dyn's Dizzying Draught [#619] from Nuke [#0] to Debuff [#14] [Should be 0] + +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 74; -- Mana Sieve [#74] from Nuke [#0] to Debuff [#14] +-- UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 1686; -- Theft of Thought [#1686] from Nuke [#0] to Lifetap [#6] + +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 3694; -- Stoicism [#3694] from In-Combat Buff [#10] to Regular Heal [#1] +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 4899; -- Breath of Trushar [#4899] from In-Combat Buff [#10] to Regular Heal [#1] + +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1748; -- Angstlich's Assonance [#1748] from Slow [#13] to DoT [#8] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] + )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/database_schema.h b/common/database_schema.h index e0b87b94d3..6bf75b6d1e 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -415,6 +415,7 @@ namespace DatabaseSchema { "bot_pet_buffs", "bot_pet_inventories", "bot_pets", + "bot_settings", "bot_spell_casting_chances", "bot_spell_settings", "bot_spells_entries", diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 8bf474f349..0fa85b75ab 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -142,6 +142,13 @@ namespace Logs { EqTime, Corpses, XTargets, + BotSettings, + BotPreChecks, + BotHoldChecks, + BotDelayChecks, + BotThresholdChecks, + BotSpellTypeChecks, + TestDebug, MaxCategoryID /* Don't Remove this */ }; @@ -242,7 +249,14 @@ namespace Logs { "Zoning", "EqTime", "Corpses", - "XTargets" + "XTargets", + "Bot Settings", + "Bot Pre Checks", + "Bot Hold Checks", + "Bot Delay Checks", + "Bot Threshold Checks", + "Bot Spell Type Checks", + "Test Debug" }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 10c9cd98a2..fdd33be14d 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -844,6 +844,76 @@ OutF(LogSys, Logs::Detail, Logs::XTargets, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) +#define LogBotSettings(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotSettings))\ + OutF(LogSys, Logs::General, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotSettingsDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSettings))\ + OutF(LogSys, Logs::Detail, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotPreChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotPreChecks))\ + OutF(LogSys, Logs::General, Logs::BotPreChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotPreChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotPreChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotPreChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotHoldChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotHoldChecks))\ + OutF(LogSys, Logs::General, Logs::BotHoldChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotHoldChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotHoldChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotHoldChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotDelayChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotDelayChecks))\ + OutF(LogSys, Logs::General, Logs::BotDelayChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotDelayChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotDelayChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotDelayChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotThresholdChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotThresholdChecks))\ + OutF(LogSys, Logs::General, Logs::BotThresholdChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotThresholdChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotThresholdChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotThresholdChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotSpellTypeChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotSpellTypeChecks))\ + OutF(LogSys, Logs::General, Logs::BotSpellTypeChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogBotSpellTypeChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSpellTypeChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotSpellTypeChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTestDebug(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::TestDebug))\ + OutF(LogSys, Logs::General, Logs::TestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + +#define LogTestDebugDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::TestDebug))\ + OutF(LogSys, Logs::Detail, Logs::TestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +} while (0) + #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.IsLogEnabled(debug_level, log_category))\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ diff --git a/common/repositories/base/base_bot_data_repository.h b/common/repositories/base/base_bot_data_repository.h index 96622c2db8..eb0cdcb991 100644 --- a/common/repositories/base/base_bot_data_repository.h +++ b/common/repositories/base/base_bot_data_repository.h @@ -64,13 +64,6 @@ class BaseBotDataRepository { int16_t poison; int16_t disease; int16_t corruption; - uint32_t show_helm; - uint32_t follow_distance; - uint8_t stop_melee_level; - int32_t expansion_bitmask; - uint8_t enforce_spell_settings; - uint8_t archery_setting; - uint32_t caster_range; }; static std::string PrimaryKey() @@ -126,13 +119,6 @@ class BaseBotDataRepository { "poison", "disease", "corruption", - "show_helm", - "follow_distance", - "stop_melee_level", - "expansion_bitmask", - "enforce_spell_settings", - "archery_setting", - "caster_range", }; } @@ -184,13 +170,6 @@ class BaseBotDataRepository { "poison", "disease", "corruption", - "show_helm", - "follow_distance", - "stop_melee_level", - "expansion_bitmask", - "enforce_spell_settings", - "archery_setting", - "caster_range", }; } @@ -276,13 +255,7 @@ class BaseBotDataRepository { e.poison = 0; e.disease = 0; e.corruption = 0; - e.show_helm = 0; - e.follow_distance = 200; - e.stop_melee_level = 255; - e.expansion_bitmask = -1; - e.enforce_spell_settings = 0; - e.archery_setting = 0; - e.caster_range = 300; + return e; } @@ -364,13 +337,6 @@ class BaseBotDataRepository { e.poison = row[42] ? static_cast(atoi(row[42])) : 0; e.disease = row[43] ? static_cast(atoi(row[43])) : 0; e.corruption = row[44] ? static_cast(atoi(row[44])) : 0; - e.show_helm = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; - e.follow_distance = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 200; - e.stop_melee_level = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 255; - e.expansion_bitmask = row[48] ? static_cast(atoi(row[48])) : -1; - e.enforce_spell_settings = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; - e.archery_setting = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; - e.caster_range = row[51] ? static_cast(strtoul(row[51], nullptr, 10)) : 300; return e; } @@ -448,13 +414,6 @@ class BaseBotDataRepository { v.push_back(columns[42] + " = " + std::to_string(e.poison)); v.push_back(columns[43] + " = " + std::to_string(e.disease)); v.push_back(columns[44] + " = " + std::to_string(e.corruption)); - v.push_back(columns[45] + " = " + std::to_string(e.show_helm)); - v.push_back(columns[46] + " = " + std::to_string(e.follow_distance)); - v.push_back(columns[47] + " = " + std::to_string(e.stop_melee_level)); - v.push_back(columns[48] + " = " + std::to_string(e.expansion_bitmask)); - v.push_back(columns[49] + " = " + std::to_string(e.enforce_spell_settings)); - v.push_back(columns[50] + " = " + std::to_string(e.archery_setting)); - v.push_back(columns[51] + " = " + std::to_string(e.caster_range)); auto results = db.QueryDatabase( fmt::format( @@ -521,13 +480,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); auto results = db.QueryDatabase( fmt::format( @@ -602,13 +554,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } @@ -687,13 +632,6 @@ class BaseBotDataRepository { e.poison = row[42] ? static_cast(atoi(row[42])) : 0; e.disease = row[43] ? static_cast(atoi(row[43])) : 0; e.corruption = row[44] ? static_cast(atoi(row[44])) : 0; - e.show_helm = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; - e.follow_distance = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 200; - e.stop_melee_level = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 255; - e.expansion_bitmask = row[48] ? static_cast(atoi(row[48])) : -1; - e.enforce_spell_settings = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; - e.archery_setting = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; - e.caster_range = row[51] ? static_cast(strtoul(row[51], nullptr, 10)) : 300; all_entries.push_back(e); } @@ -763,13 +701,6 @@ class BaseBotDataRepository { e.poison = row[42] ? static_cast(atoi(row[42])) : 0; e.disease = row[43] ? static_cast(atoi(row[43])) : 0; e.corruption = row[44] ? static_cast(atoi(row[44])) : 0; - e.show_helm = row[45] ? static_cast(strtoul(row[45], nullptr, 10)) : 0; - e.follow_distance = row[46] ? static_cast(strtoul(row[46], nullptr, 10)) : 200; - e.stop_melee_level = row[47] ? static_cast(strtoul(row[47], nullptr, 10)) : 255; - e.expansion_bitmask = row[48] ? static_cast(atoi(row[48])) : -1; - e.enforce_spell_settings = row[49] ? static_cast(strtoul(row[49], nullptr, 10)) : 0; - e.archery_setting = row[50] ? static_cast(strtoul(row[50], nullptr, 10)) : 0; - e.caster_range = row[51] ? static_cast(strtoul(row[51], nullptr, 10)) : 300; all_entries.push_back(e); } @@ -889,13 +820,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); auto results = db.QueryDatabase( fmt::format( @@ -963,13 +887,6 @@ class BaseBotDataRepository { v.push_back(std::to_string(e.poison)); v.push_back(std::to_string(e.disease)); v.push_back(std::to_string(e.corruption)); - v.push_back(std::to_string(e.show_helm)); - v.push_back(std::to_string(e.follow_distance)); - v.push_back(std::to_string(e.stop_melee_level)); - v.push_back(std::to_string(e.expansion_bitmask)); - v.push_back(std::to_string(e.enforce_spell_settings)); - v.push_back(std::to_string(e.archery_setting)); - v.push_back(std::to_string(e.caster_range)); insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); } diff --git a/common/repositories/base/base_bot_settings_repository.h b/common/repositories/base/base_bot_settings_repository.h new file mode 100644 index 0000000000..2018177dc9 --- /dev/null +++ b/common/repositories/base/base_bot_settings_repository.h @@ -0,0 +1,464 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H +#define EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseBotSettingsRepository { +public: + struct BotSettings { + uint32_t id; + uint32_t char_id; + uint32_t bot_id; + uint16_t setting_id; + uint8_t setting_type; + int32_t value; + std::string category_name; + std::string setting_name; + }; + + static std::string PrimaryKey() + { + return std::string("id"); + } + + static std::vector Columns() + { + return { + "id", + "char_id", + "bot_id", + "setting_id", + "setting_type", + "value", + "category_name", + "setting_name", + }; + } + + static std::vector SelectColumns() + { + return { + "id", + "char_id", + "bot_id", + "setting_id", + "setting_type", + "value", + "category_name", + "setting_name", + }; + } + + 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_settings"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static BotSettings NewEntity() + { + BotSettings e{}; + + e.id = 0; + e.char_id = 0; + e.bot_id = 0; + e.setting_id = 0; + e.setting_type = 0; + e.value = 0; + e.category_name = ""; + e.setting_name = ""; + + return e; + } + + static BotSettings GetBotSettings( + const std::vector &bot_settingss, + int bot_settings_id + ) + { + for (auto &bot_settings : bot_settingss) { + if (bot_settings.id == bot_settings_id) { + return bot_settings; + } + } + + return NewEntity(); + } + + static BotSettings FindOne( + Database& db, + int bot_settings_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + bot_settings_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + BotSettings e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int bot_settings_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + bot_settings_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const BotSettings &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = " + std::to_string(e.id)); + v.push_back(columns[1] + " = " + std::to_string(e.char_id)); + v.push_back(columns[2] + " = " + std::to_string(e.bot_id)); + v.push_back(columns[3] + " = " + std::to_string(e.setting_id)); + v.push_back(columns[4] + " = " + std::to_string(e.setting_type)); + v.push_back(columns[5] + " = " + std::to_string(e.value)); + v.push_back(columns[6] + " = '" + Strings::Escape(e.category_name) + "'"); + v.push_back(columns[7] + " = '" + Strings::Escape(e.setting_name) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.bot_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static BotSettings InsertOne( + Database& db, + BotSettings e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + 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 &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + BotSettings e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector 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) { + BotSettings e{}; + + e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const BotSettings &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.id)); + v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.setting_id)); + v.push_back(std::to_string(e.setting_type)); + v.push_back(std::to_string(e.value)); + v.push_back("'" + Strings::Escape(e.category_name) + "'"); + v.push_back("'" + Strings::Escape(e.setting_name) + "'"); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_BOT_SETTINGS_REPOSITORY_H diff --git a/common/repositories/bot_data_repository.h b/common/repositories/bot_data_repository.h index f6508479f1..e8d0038a44 100644 --- a/common/repositories/bot_data_repository.h +++ b/common/repositories/bot_data_repository.h @@ -44,46 +44,6 @@ class BotDataRepository: public BaseBotDataRepository { */ // Custom extended repository methods here - static bool SaveAllHelmAppearances(Database& db, const uint32 owner_id, const bool show_flag) - { - auto results = db.QueryDatabase( - fmt::format( - "UPDATE `{}` SET `show_helm` = {} WHERE `owner_id` = {}", - TableName(), - show_flag ? 1 : 0, - owner_id - ) - ); - - return results.Success(); - } - - static bool ToggleAllHelmAppearances(Database& db, const uint32 owner_id) - { - auto results = db.QueryDatabase( - fmt::format( - "UPDATE `{}` SET `show_helm` = (`show_helm` XOR '1') WHERE `owner_id` = {}", - TableName(), - owner_id - ) - ); - - return results.Success(); - } - - static bool SaveAllFollowDistances(Database& db, const uint32 owner_id, const uint32 follow_distance) - { - auto results = db.QueryDatabase( - fmt::format( - "UPDATE `{}` SET `follow_distance` = {} WHERE `owner_id` = {}", - TableName(), - follow_distance, - owner_id - ) - ); - - return results.Success(); - } }; #endif //EQEMU_BOT_DATA_REPOSITORY_H diff --git a/common/repositories/bot_settings_repository.h b/common/repositories/bot_settings_repository.h new file mode 100644 index 0000000000..7cf1d0dbd1 --- /dev/null +++ b/common/repositories/bot_settings_repository.h @@ -0,0 +1,50 @@ +#ifndef EQEMU_BOT_SETTINGS_REPOSITORY_H +#define EQEMU_BOT_SETTINGS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_bot_settings_repository.h" + +class BotSettingsRepository: public BaseBotSettingsRepository { +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 + * + * BotSettingsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * BotSettingsRepository::GetWhereNeverExpires() + * BotSettingsRepository::GetWhereXAndY() + * BotSettingsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_BOT_SETTINGS_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index 60560345ee..e033ffd82a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -381,6 +381,9 @@ RULE_BOOL(Map, MobZVisualDebug, false, "Displays spell effects determining wheth RULE_BOOL(Map, MobPathingVisualDebug, false, "Displays nodes in pathing points in realtime to help with visual debugging") RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20, "At runtime in SendTo: maximum change in Z to allow the BestZ code to apply") RULE_INT(Map, FindBestZHeightAdjust, 1, "Adds this to the current Z before seeking the best Z position") +RULE_BOOL(Map, CheckForLoSCheat, false, "Runs predefined zone checks to check for LoS cheating through doors and such.") +RULE_BOOL(Map, EnableLoSCheatExemptions, false, "Enables exemptions for the LoS Cheat check.") +RULE_REAL(Map, RangeCheckForLoSCheat, 20.0, "Default 20.0. Range to check if one is within range of a door.") RULE_CATEGORY_END() RULE_CATEGORY(Pathing) @@ -753,6 +756,7 @@ RULE_INT(Bots, CommandSpellRank, 1, "Filters bot command spells by rank. 1, 2 an RULE_INT(Bots, CreationLimit, 150, "Number of bots that each account can create") RULE_BOOL(Bots, FinishBuffing, false, "Allow for buffs to complete even if the bot caster is out of mana. Only affects buffing out of combat") RULE_BOOL(Bots, GroupBuffing, false, "Bots will cast single target buffs as group buffs, default is false for single. Does not make single target buffs work for MGB") +RULE_BOOL(Bots, RaidBuffing, false, "Bots will cast single target buffs as raid buffs, default is false for single. Does not make single target buffs work for MGB") RULE_INT(Bots, HealRotationMaxMembers, 24, "Maximum number of heal rotation members") RULE_INT(Bots, HealRotationMaxTargets, 12, "Maximum number of heal rotation targets") RULE_REAL(Bots, ManaRegen, 2.0, "Adjust mana regen. Acts as a final multiplier, stacks with Rule Character:ManaRegenMultiplier.") @@ -782,6 +786,91 @@ RULE_BOOL(Bots, CanClickMageEpicV1, true, "Whether or not bots are allowed to cl RULE_BOOL(Bots, BotsIgnoreLevelBasedHasteCaps, false, "Ignores hard coded level based haste caps.") RULE_INT(Bots, BotsHasteCap, 100, "Haste cap for non-v3(over haste) haste") RULE_INT(Bots, BotsHastev3Cap, 25, "Haste cap for v3(over haste) haste") +RULE_BOOL(Bots, CrossRaidBuffingAndHealing, true, "If True, bots will be able to cast on all raid members rather than just their raid group members. Default true.") +RULE_BOOL(Bots, CanCastIllusionsOnPets, false, "If True, bots will be able to cast spells that have an illusion effect on pets. Default false.") +RULE_BOOL(Bots, CanCastPetOnlyOnOthersPets, false, "If True, bots will be able to cast pet only spells on other's pets. Default false.") +RULE_BOOL(Bots, RequirePetAffinity, true, "If True, bots will be need to have the Pet Affinity AA to allow their pets to be hit with group spells.") +RULE_INT(Bots, SpellResistLimit, 150, "150 Default. This is the resist cap where bots will refuse to cast spells on enemies due to a high resist chance.") +RULE_INT(Bots, StunCastChanceIfCasting, 50, "50 Default. Chance for non-Paladins to cast a stun spell if the target is casting.") +RULE_INT(Bots, StunCastChanceNormal, 15, "15 Default. Chance for non-Paladins to cast a stun spell on the target.") +RULE_INT(Bots, StunCastChancePaladins, 75, "75 Default. Chance for Paladins to cast a stun spell if the target is casting.") +RULE_INT(Bots, PercentChanceToCastNuke, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") +RULE_INT(Bots, PercentChanceToCastRoot, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastBuff, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") +RULE_INT(Bots, PercentChanceToCastEscape, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastLifetap, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastSnare, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastDOT, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastHateRedux, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastFear, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastOtherType, 90, "The chance for a bot to attempt to cast the remaining spell types in combat. Default 0-%.") +RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 250, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2000ms.") +RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 125, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 200ms.") +RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MezChance, 35, "35 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.") +RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.") +RULE_INT(Bots, MezSuccessDelay, 3500, "3500 (3.5 sec) Default. Delay between successful Mez attempts.") +RULE_INT(Bots, AEMezSuccessDelay, 5000, "5000 (5 sec) Default. Delay between successful AEMez attempts.") +RULE_INT(Bots, MezFailDelay, 2000, "2000 (2 sec) Default. Delay between failed Mez attempts.") +RULE_INT(Bots, MezAEFailDelay, 4000, "4000 (4 sec) Default. Delay between failed AEMez attempts.") +RULE_INT(Bots, MinGroupHealTargets, 3, "Minimum number of targets in valid range that are required for a group heal to cast. Default 3.") +RULE_INT(Bots, MinGroupCureTargets, 3, "Minimum number of targets in valid range that are required for a cure heal to cast. Default 3.") +RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid range that are required for an AE spell to cast. Default 3.") +RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.") +RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.") +RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks") +RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel") +RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") +RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") +RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.") +RULE_BOOL(Bots, AllowSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") +RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots") +RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") +RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") +RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") +RULE_INT(Bots, StackSizeMin, 100, "100 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") +RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") +RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") +RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") +RULE_REAL(Bots, PercentMinMeleeDistance, 0.60, "Multiplier of the max melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") +RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.") +RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .") +RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.25, "Multiplier of max melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") +RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.") +RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.") +RULE_BOOL(Bots, CastersStayJustOutOfMeleeRange, true, "True Default. If true, caster bots will stay just out of melee range. Otherwise they use Bots:PercentMinCasterRangeDistance.") +RULE_REAL(Bots, PercentMinCasterRangeDistance, 0.60, "Multiplier of the closest caster range at which a bot will stand while casting before trying to adjust. 0.60 Recommended.") +RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.") +RULE_REAL(Bots, DistanceTauntingBotsStickMainHate, 25.00, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") +RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, false, "False Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") +RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.") +RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.") +RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.") +RULE_BOOL(Bots, PreventBotSpawnOnFD, true, "True Default. If true, players will not be able to spawn bots while feign death.") +RULE_BOOL(Bots, PreventBotSpawnOnEngaged, true, "True Default. If true, players will not be able to spawn bots while you, your group or raid are engaged.") +RULE_BOOL(Bots, PreventBotCampOnEngaged, true, "True Default. If true, players will not be able to camp bots while you, your group or raid are engaged.") +RULE_BOOL(Bots, CopySettingsOwnBotsOnly, true, "Determines whether a bot you are copying settings from must be a bot you own or not, default true.") +RULE_BOOL(Bots, AllowCopySettingsAnon, true, "If player's are allowed to copy settings of bots owned by anonymous players.") +RULE_BOOL(Bots, AllowCharmedPetBuffs, true, "Whether or not bots are allowed to cast buff charmed pets, default true.") +RULE_BOOL(Bots, AllowCharmedPetHeals, true, "Whether or not bots are allowed to cast heal charmed pets, default true.") +RULE_BOOL(Bots, AllowCharmedPetCures, true, "Whether or not bots are allowed to cast cure charmed pets, default true.") +RULE_BOOL(Bots, ShowResistMessagesToOwner, true, "Default True. If enabled, when a bot's spell is resisted it will send a spell failure to their owner.") +RULE_BOOL(Bots, BotBuffLevelRestrictions, true, "Buffs will not land on low level bots like live players") +RULE_BOOL(Bots, BotsUseLiveBlockedMessage, false, "Setting whether detailed spell block messages should be used for bots as players do on the live servers") +RULE_BOOL(Bots, BotSoftDeletes, true, "When bots are deleted, they are only soft deleted") +RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass the anti-spam system") +RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Default 120.") +RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass the anti-spam system") +RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. Default 120.") +RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) @@ -1007,6 +1096,30 @@ RULE_CATEGORY_END() RULE_CATEGORY(Command) RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.") RULE_BOOL(Command, HideMeCommandDisablesTells, true, "Disable this to allow tells to be received when using #hideme.") +RULE_INT(Command, MaxHelpLineLength, 53, "Maximum length of a line before splitting it in to new lines for DiaWind. Default 53.") +RULE_STRING(Command, DescriptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, DescriptionHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, AltDescriptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, NoteColor, "dark_orange", "Color for command help windows") +RULE_STRING(Command, NoteHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, AltNoteColor, "dark_orange", "Color for command help windows") +RULE_STRING(Command, ExampleColor, "goldenrod", "Color for command help windows") +RULE_STRING(Command, ExampleHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, SubExampleColor, "slate_blue", "Color for command help windows") +RULE_STRING(Command, AltExampleColor, "goldenrod", "Color for command help windows") +RULE_STRING(Command, SubAltExampleColor, "goldenrod", "Color for command help windows") +RULE_STRING(Command, OptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, OptionHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, SubOptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, AltOptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, SubAltOptionColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, ActionableColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, ActionableHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, AltActionableColor, "light_grey", "Color for command help windows") +RULE_STRING(Command, HeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, SecondaryHeaderColor, "slate_blue", "Color for command help windows") +RULE_STRING(Command, AltHeaderColor, "indian_red", "Color for command help windows") +RULE_STRING(Command, FillerLineColor, "dark_grey", "Color for command help windows") RULE_CATEGORY_END() RULE_CATEGORY(Doors) diff --git a/common/spdat.cpp b/common/spdat.cpp index 3acac1c8b9..53bf03f6d3 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -125,9 +125,26 @@ bool IsMesmerizeSpell(uint16 spell_id) return IsEffectInSpell(spell_id, SE_Mez); } +bool SpellBreaksMez(uint16 spell_id) +{ + if (IsDetrimentalSpell(spell_id) && IsAnyDamageSpell(spell_id)) { + return true; + } + + return false; +} + bool IsStunSpell(uint16 spell_id) { - return IsEffectInSpell(spell_id, SE_Stun); + if (IsEffectInSpell(spell_id, SE_Stun)) { + return true; + } + + if (IsEffectInSpell(spell_id, SE_SpinTarget)) { + return true; + } + + return false; } bool IsSummonSpell(uint16 spell_id) @@ -160,6 +177,27 @@ bool IsDamageSpell(uint16 spell_id) const auto& spell = spells[spell_id]; + for (int i = 0; i < EFFECT_COUNT; i++) { + const auto effect_id = spell.effect_id[i]; + if ( + spell.base_value[i] < 0 && + (effect_id == SE_CurrentHPOnce || effect_id == SE_CurrentHP) + ) { + return true; + } + } + + return false; +} + +bool IsAnyDamageSpell(uint16 spell_id) +{ + if (IsLifetapSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + for (int i = 0; i < EFFECT_COUNT; i++) { const auto effect_id = spell.effect_id[i]; if ( @@ -169,8 +207,8 @@ bool IsDamageSpell(uint16 spell_id) ( effect_id == SE_CurrentHP && spell.buff_duration < 1 + ) ) - ) ) { return true; } @@ -179,6 +217,35 @@ bool IsDamageSpell(uint16 spell_id) return false; } +bool IsDamageOverTimeSpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + if (IsLifetapSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + if (spell.good_effect || !spell.buff_duration_formula) { + return false; + } + + for (int i = 0; i < EFFECT_COUNT; i++) { + const auto effect_id = spell.effect_id[i]; + if ( + spell.base_value[i] < 0 && + effect_id == SE_CurrentHP && + spell.buff_duration > 1 + ) { + return true; + } + } + + return false; +} bool IsFearSpell(uint16 spell_id) { @@ -409,7 +476,8 @@ bool IsSummonPetSpell(uint16 spell_id) return ( IsEffectInSpell(spell_id, SE_SummonPet) || IsEffectInSpell(spell_id, SE_SummonBSTPet) || - IsEffectInSpell(spell_id, SE_Familiar) + IsEffectInSpell(spell_id, SE_Familiar) || + IsEffectInSpell(spell_id, SE_NecPet) ); } @@ -560,12 +628,11 @@ bool IsPBAENukeSpell(uint16 spell_id) if ( IsPureNukeSpell(spell_id) && - spell.aoe_range > 0 && - spell.target_type == ST_AECaster + !IsTargetRequiredForSpell(spell_id) ) { return true; } - + return false; } @@ -588,6 +655,137 @@ bool IsAERainNukeSpell(uint16 spell_id) return false; } +bool IsAnyNukeOrStunSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if (IsSelfConversionSpell(spell_id) || IsEscapeSpell(spell_id)) { + return false; + } + + if ( + IsPBAENukeSpell(spell_id) || + IsAERainNukeSpell(spell_id) || + IsPureNukeSpell(spell_id) || + IsStunSpell(spell_id) || + (IsDamageSpell(spell_id) && !IsDamageOverTimeSpell(spell_id)) + ) { + return true; + } + + return false; +} + +bool IsAnyAESpell(uint16 spell_id) { + //if ( + // spells[spell_id].target_type == ST_Target || + // spells[spell_id].target_type == ST_Self || + // spells[spell_id].target_type == ST_Animal || + // spells[spell_id].target_type == ST_Undead || + // spells[spell_id].target_type == ST_Summoned || + // spells[spell_id].target_type == ST_Tap || + // spells[spell_id].target_type == ST_Pet || + // spells[spell_id].target_type == ST_Corpse || + // spells[spell_id].target_type == ST_Plant || + // spells[spell_id].target_type == ST_Giant || + // spells[spell_id].target_type == ST_Dragon || + // spells[spell_id].target_type == ST_HateList || + // spells[spell_id].target_type == ST_LDoNChest_Cursed || + // spells[spell_id].target_type == ST_Muramite || + // spells[spell_id].target_type == ST_SummonedPet || + // spells[spell_id].target_type == ST_TargetsTarget || + // spells[spell_id].target_type == ST_PetMaster //|| + // //spells[spell_id].target_type == ST_AEBard //TODO needed? + //) { + // return false; + //} + + if (IsAESpell(spell_id) || IsPBAENukeSpell(spell_id) || IsPBAESpell(spell_id) || IsAERainSpell(spell_id) || IsAERainNukeSpell(spell_id) || IsAEDurationSpell(spell_id)) { + return true; + } + + return false; +} + +bool IsAESpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + switch (spells[spell_id].target_type) { + case ST_TargetOptional: + case ST_GroupTeleport : + case ST_Target: + case ST_Self: + case ST_Animal: + case ST_Undead: + case ST_Summoned: + case ST_Tap: + case ST_Pet: + case ST_Corpse: + case ST_Plant: + case ST_Giant: + case ST_Dragon: + case ST_LDoNChest_Cursed: + case ST_Muramite: + case ST_SummonedPet: + case ST_GroupNoPets: + case ST_Group: + case ST_GroupClientAndPet: + case ST_TargetsTarget: + case ST_PetMaster: + return false; + default: + break; + } + + if ( + spells[spell_id].aoe_range > 0 + ) { + return true; + } + + return false; +} + +bool IsPBAESpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + if ( + spell.aoe_range > 0 && + spell.target_type == ST_AECaster + ) { + return true; + } + + return false; +} + +bool IsAERainSpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + if ( + spell.aoe_range > 0 && + spell.aoe_duration > 1000 + ) { + return true; + } + + return false; +} + bool IsPartialResistableSpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) { @@ -644,7 +842,9 @@ bool IsGroupSpell(uint16 spell_id) return ( spell.target_type == ST_AEBard || spell.target_type == ST_Group || - spell.target_type == ST_GroupTeleport + spell.target_type == ST_GroupTeleport || + spell.target_type == ST_GroupNoPets || + spell.target_type == ST_GroupClientAndPet ); } @@ -1265,6 +1465,7 @@ bool IsCompleteHealSpell(uint16 spell_id) } return false; + } bool IsFastHealSpell(uint16 spell_id) @@ -1386,20 +1587,61 @@ bool IsRegularSingleTargetHealSpell(uint16 spell_id) return false; } -bool IsRegularGroupHealSpell(uint16 spell_id) +bool IsRegularPetHealSpell(uint16 spell_id) { spell_id = ( IsEffectInSpell(spell_id, SE_CurrentHP) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHP) - ); + ); if (!spell_id) { spell_id = ( IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) + ); + } + + if (spell_id) { + if ( + spells[spell_id].target_type == ST_Pet && + !IsCompleteHealSpell(spell_id) && + !IsHealOverTimeSpell(spell_id) && + !IsGroupSpell(spell_id) + ) { + for (int i = 0; i < EFFECT_COUNT; i++) { + if ( + spells[spell_id].base_value[i] > 0 && + spells[spell_id].buff_duration == 0 && + ( + spells[spell_id].effect_id[i] == SE_CurrentHP || + spells[spell_id].effect_id[i] == SE_CurrentHPOnce + ) + ) { + return true; + } + } + } + } + + return false; +} + +bool IsRegularGroupHealSpell(uint16 spell_id) +{ + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHP) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHP) ); + + if (!spell_id) { + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) + ); } if (spell_id) { @@ -1415,8 +1657,8 @@ bool IsRegularGroupHealSpell(uint16 spell_id) ( spells[spell_id].effect_id[i] == SE_CurrentHP || spells[spell_id].effect_id[i] == SE_CurrentHPOnce - ) - ) { + ) + ) { return true; } } @@ -1429,9 +1671,14 @@ bool IsRegularGroupHealSpell(uint16 spell_id) bool IsGroupCompleteHealSpell(uint16 spell_id) { if ( - IsGroupSpell(spell_id) && - IsCompleteHealSpell(spell_id) - ) { + ( + spell_id == SPELL_COMPLETE_HEAL || + IsEffectInSpell(spell_id, SE_CompleteHeal) || + IsPercentalHealSpell(spell_id) || + GetSpellTriggerSpellID(spell_id, SE_CompleteHeal) + ) && + IsGroupSpell(spell_id) + ) { return true; } @@ -1441,9 +1688,92 @@ bool IsGroupCompleteHealSpell(uint16 spell_id) bool IsGroupHealOverTimeSpell(uint16 spell_id) { if ( - IsGroupSpell(spell_id) && - IsHealOverTimeSpell(spell_id) && - spells[spell_id].buff_duration < 10 + ( + IsEffectInSpell(spell_id, SE_HealOverTime) || + GetSpellTriggerSpellID(spell_id, SE_HealOverTime) + ) && + IsGroupSpell(spell_id) + ) { + return true; + } + + return false; +} + +bool IsAnyHealSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if (spell_id == SPELL_NATURES_RECOVERY) { + return false; + } + + //spell_id != SPELL_ADRENALINE_SWELL && + //spell_id != SPELL_ADRENALINE_SWELL_RK2 && + //spell_id != SPELL_ADRENALINE_SWELL_RK3 && + if ( + IsHealOverTimeSpell(spell_id) || + IsGroupHealOverTimeSpell(spell_id) || + IsFastHealSpell(spell_id) || + IsVeryFastHealSpell(spell_id) || + IsRegularSingleTargetHealSpell(spell_id) || + IsRegularGroupHealSpell(spell_id) || + IsCompleteHealSpell(spell_id) || + IsGroupCompleteHealSpell(spell_id) || + IsRegularPetHealSpell(spell_id) + ) { + return true; + } + + return false; +} + +bool IsAnyBuffSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + spell_id == SPELL_NATURES_RECOVERY || + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + !IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + return true; + } + + return false; +} +bool IsDispelSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + IsEffectInSpell(spell_id, SE_CancelMagic) || + IsEffectInSpell(spell_id, SE_DispelBeneficial) || + IsEffectInSpell(spell_id, SE_DispelBeneficial) + ) { + return true; + } + + return false; +} + +bool IsEscapeSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + IsInvulnerabilitySpell(spell_id) || + IsEffectInSpell(spell_id, SE_FeignDeath) || + IsEffectInSpell(spell_id, SE_DeathSave) || + IsEffectInSpell(spell_id, SE_Destroy) || + (IsEffectInSpell(spell_id, SE_WipeHateList) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_WipeHateList)] > 0) ) { return true; } @@ -1464,7 +1794,8 @@ bool IsDebuffSpell(uint16 spell_id) IsEffectInSpell(spell_id, SE_CancelMagic) || IsEffectInSpell(spell_id, SE_MovementSpeed) || IsFearSpell(spell_id) || - IsEffectInSpell(spell_id, SE_InstantHate) + IsEffectInSpell(spell_id, SE_InstantHate) || + IsEffectInSpell(spell_id, SE_TossUp) ) { return false; } @@ -1472,6 +1803,22 @@ bool IsDebuffSpell(uint16 spell_id) return true; } +bool IsHateReduxSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] < 0) || + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] < 0) || + (IsEffectInSpell(spell_id, SE_ReduceHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_ReduceHate)] < 0) + ) { + return true; + } + + return false; +} + bool IsResistDebuffSpell(uint16 spell_id) { if ( @@ -2383,7 +2730,7 @@ bool AegolismStackingIsSymbolSpell(uint16 spell_id) { if ((i < 2 && spells[spell_id].effect_id[i] != SE_CHA) || i > 3 && spells[spell_id].effect_id[i] != SE_Blank) { - return 0;; + return 0; } if (i == 2 && spells[spell_id].effect_id[i] == SE_TotalHP) { @@ -2430,3 +2777,401 @@ bool AegolismStackingIsArmorClassSpell(uint16 spell_id) { return 0; } + +int8 SpellEffectsCount(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + int8 i = 0; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (!IsBlankSpellEffect(spell_id, i)) { + ++i; + } + } + + return i; +} + +bool IsLichSpell(uint16 spell_id) +{ + if (!IsValidSpell(spell_id)) { + return false; + } + + if ( + GetSpellTargetType(spell_id) == ST_Self && + IsEffectInSpell(spell_id, SE_CurrentMana) && + IsEffectInSpell(spell_id, SE_CurrentHP) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0 && + spells[spell_id].buff_duration > 0 + ) { + return true; + } + + return false; +} + +bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Root: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Mez: + case BotSpellTypes::Charm: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::HateRedux: + case BotSpellTypes::Fear: + case BotSpellTypes::Stun: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEMez: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + return true; + case BotSpellTypes::InCombatBuff: + if (cls == Class::ShadowKnight) { + return true; + } + + return false; + default: + return false; + } + + return false; +} + +bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::Resurrect: + return true; + case BotSpellTypes::InCombatBuff: + if (cls == Class::ShadowKnight) { + return false; + } + + return true; + default: + return false; + } + + return false; +} + +bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + return true; + default: + return false; + } + + return false; +} + +bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::Charm: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return true; + default: + return false; + } + + return false; +} + +bool IsBotSpellType(uint16 spellType) { + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && BOT_SPELL_TYPES_BENEFICIAL(spellType) && BOT_SPELL_TYPES_INNATE(spellType)) { + return true; + } + + return false; +} + +bool IsAEBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEMez: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AEDoT: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AELifetap: + case BotSpellTypes::AERoot: + return true; + default: + return false; + } + + return false; +} + +bool IsGroupBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::GroupCures: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsGroupTargetOnlyBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::GroupCures: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsPetBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsClientBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + return true; + default: + return false; + } + + return false; +} + +bool IsHealBotSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { + if (IsAEBotSpellType(spellType)) { // These gather their own targets later + return false; + } + + switch (spellType) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return false; + case BotSpellTypes::InCombatBuff: + if (cls && cls == Class::ShadowKnight) { + return true; + } + + return false; + default: + return true; + } + + return true; +} + +bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls) { + switch (spellType) { + case BotSpellTypes::Escape: + if (cls == Class::ShadowKnight) { + return false; + } + + return true; + case BotSpellTypes::Pet: + return false; + default: + return true; + } + + return true; +} + +bool IsValidSpellAndLoS(uint32 spell_id, bool hasLoS) { + if (!IsValidSpell(spell_id)) { + return false; + } + + if (!hasLoS && IsTargetRequiredForSpell(spell_id)) { + return false; + } + + return true; +} + +bool IsInstantHealSpell(uint32 spell_id) { + if (IsRegularSingleTargetHealSpell(spell_id) || IsRegularGroupHealSpell(spell_id) || IsRegularPetHealSpell(spell_id) || IsRegularGroupHealSpell(spell_id) || spell_id == SPELL_COMPLETE_HEAL) { + return true; + } + + return false; +} + +bool IsResurrectSpell(uint16 spell_id) +{ + return IsEffectInSpell(spell_id, SE_Revive); +} + +bool RequiresStackCheck(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::GroupCompleteHeals: + return false; + default: + return true; + } + + return true; +} diff --git a/common/spdat.h b/common/spdat.h index d597c79faf..432774b95b 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -213,6 +213,10 @@ #define SPELL_BLOODTHIRST 8476 #define SPELL_AMPLIFICATION 2603 #define SPELL_DIVINE_REZ 2738 +#define SPELL_NATURES_RECOVERY 2520 +#define SPELL_ADRENALINE_SWELL 14445 +#define SPELL_ADRENALINE_SWELL_RK2 14446 +#define SPELL_ADRENALINE_SWELL_RK3 14447 // discipline IDs. #define DISC_UNHOLY_AURA 4520 @@ -647,14 +651,84 @@ enum SpellTypes : uint32 SpellType_PreCombatBuffSong = (1 << 21) }; -const uint32 SPELL_TYPE_MIN = (SpellType_Nuke << 1) - 1; -const uint32 SPELL_TYPE_MAX = (SpellType_PreCombatBuffSong << 1) - 1; -const uint32 SPELL_TYPE_ANY = 0xFFFFFFFF; +namespace BotSpellTypes +{ + constexpr uint16 Nuke = 0; + constexpr uint16 RegularHeal = 1; + constexpr uint16 Root = 2; + constexpr uint16 Buff = 3; + constexpr uint16 Escape = 4; + constexpr uint16 Pet = 5; + constexpr uint16 Lifetap = 6; + constexpr uint16 Snare = 7; + constexpr uint16 DOT = 8; + constexpr uint16 Dispel = 9; + constexpr uint16 InCombatBuff = 10; + constexpr uint16 Mez = 11; + constexpr uint16 Charm = 12; + constexpr uint16 Slow = 13; + constexpr uint16 Debuff = 14; + constexpr uint16 Cure = 15; + constexpr uint16 Resurrect = 16; + constexpr uint16 HateRedux = 17; + constexpr uint16 InCombatBuffSong = 18; + constexpr uint16 OutOfCombatBuffSong = 19; + constexpr uint16 PreCombatBuff = 20; + constexpr uint16 PreCombatBuffSong = 21; + constexpr uint16 Fear = 22; + constexpr uint16 Stun = 23; + constexpr uint16 GroupCures = 24; + constexpr uint16 CompleteHeal = 25; + constexpr uint16 FastHeals = 26; + constexpr uint16 VeryFastHeals = 27; + constexpr uint16 GroupHeals = 28; + constexpr uint16 GroupCompleteHeals = 29; + constexpr uint16 GroupHoTHeals = 30; + constexpr uint16 HoTHeals = 31; + constexpr uint16 AENukes = 32; + constexpr uint16 AERains = 33; + constexpr uint16 AEMez = 34; + constexpr uint16 AEStun = 35; + constexpr uint16 AEDebuff = 36; + constexpr uint16 AESlow = 37; + constexpr uint16 AESnare = 38; + constexpr uint16 AEFear = 39; + constexpr uint16 AEDispel = 40; + constexpr uint16 AERoot = 41; + constexpr uint16 AEDoT = 42; + constexpr uint16 AELifetap = 43; + constexpr uint16 PBAENuke = 44; + constexpr uint16 PetBuffs = 45; + constexpr uint16 PetRegularHeals = 46; + constexpr uint16 PetCompleteHeals = 47; + constexpr uint16 PetFastHeals = 48; + constexpr uint16 PetVeryFastHeals = 49; + constexpr uint16 PetHoTHeals = 50; + constexpr uint16 DamageShields = 51; + constexpr uint16 ResistBuffs = 52; + + constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this + constexpr uint16 END = BotSpellTypes::ResistBuffs; // Do not remove this, increment as needed +} const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); +bool BOT_SPELL_TYPES_DETRIMENTAL (uint16 spellType, uint8 cls = 0); +bool BOT_SPELL_TYPES_BENEFICIAL (uint16 spellType, uint8 cls = 0); +bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType); +bool BOT_SPELL_TYPES_INNATE (uint16 spellType); +bool IsBotSpellType (uint16 spellType); +bool IsAEBotSpellType(uint16 spellType); +bool IsGroupBotSpellType(uint16 spellType); +bool IsGroupTargetOnlyBotSpellType(uint16 spellType); +bool IsPetBotSpellType(uint16 spellType); +bool IsClientBotSpellType(uint16 spellType); +bool IsHealBotSpellType(uint16 spellType); +bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); +bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); + // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only // TODO: import sai list @@ -1503,6 +1577,7 @@ bool IsTargetableAESpell(uint16 spell_id); bool IsSacrificeSpell(uint16 spell_id); bool IsLifetapSpell(uint16 spell_id); bool IsMesmerizeSpell(uint16 spell_id); +bool SpellBreaksMez(uint16 spell_id); bool IsStunSpell(uint16 spell_id); bool IsSlowSpell(uint16 spell_id); bool IsHasteSpell(uint16 spell_id); @@ -1536,6 +1611,11 @@ bool IsPureNukeSpell(uint16 spell_id); bool IsAENukeSpell(uint16 spell_id); bool IsPBAENukeSpell(uint16 spell_id); bool IsAERainNukeSpell(uint16 spell_id); +bool IsAnyNukeOrStunSpell(uint16 spell_id); +bool IsAnyAESpell(uint16 spell_id); +bool IsAESpell(uint16 spell_id); +bool IsPBAESpell(uint16 spell_id); +bool IsAERainSpell(uint16 spell_id); bool IsPartialResistableSpell(uint16 spell_id); bool IsResistableSpell(uint16 spell_id); bool IsGroupSpell(uint16 spell_id); @@ -1545,8 +1625,11 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id); uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id); bool IsBlankSpellEffect(uint16 spell_id, int effect_index); bool IsValidSpell(uint32 spell_id); +bool IsValidSpellAndLoS(uint32 spell_id, bool hasLoS = true); bool IsSummonSpell(uint16 spell_id); bool IsDamageSpell(uint16 spell_id); +bool IsAnyDamageSpell(uint16 spell_id); +bool IsDamageOverTimeSpell(uint16 spell_i); bool IsFearSpell(uint16 spell_id); bool IsCureSpell(uint16 spell_id); bool IsHarmTouchSpell(uint16 spell_id); @@ -1585,10 +1668,16 @@ bool IsCompleteHealSpell(uint16 spell_id); bool IsFastHealSpell(uint16 spell_id); bool IsVeryFastHealSpell(uint16 spell_id); bool IsRegularSingleTargetHealSpell(uint16 spell_id); +bool IsRegularPetHealSpell(uint16 spell_id); bool IsRegularGroupHealSpell(uint16 spell_id); bool IsGroupCompleteHealSpell(uint16 spell_id); bool IsGroupHealOverTimeSpell(uint16 spell_id); +bool IsAnyHealSpell(uint16 spell_id); +bool IsAnyBuffSpell(uint16 spell_id); +bool IsDispelSpell(uint16 spell_id); +bool IsEscapeSpell(uint16 spell_id); bool IsDebuffSpell(uint16 spell_id); +bool IsHateReduxSpell(uint16 spell_id); bool IsResistDebuffSpell(uint16 spell_id); bool IsSelfConversionSpell(uint16 spell_id); bool IsBuffSpell(uint16 spell_id); @@ -1628,5 +1717,10 @@ bool IsCastRestrictedSpell(uint16 spell_id); bool IsAegolismSpell(uint16 spell_id); bool AegolismStackingIsSymbolSpell(uint16 spell_id); bool AegolismStackingIsArmorClassSpell(uint16 spell_id); +int8 SpellEffectsCount(uint16 spell_id); +bool IsLichSpell(uint16 spell_id); +bool IsInstantHealSpell(uint32 spell_id); +bool IsResurrectSpell(uint16 spell_id); +bool RequiresStackCheck(uint16 spellType); #endif diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 1bc7efa768..1a8c7b9290 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1278,6 +1278,39 @@ bool Mob::CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarge return zone->zonemap->CheckLoS(posWatcher, posTarget); } +bool Mob::CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ) { + if (zone->zonemap == nullptr) { + //not sure what the best return is on error + //should make this a database variable, but im lazy today +#ifdef LOS_DEFAULT_CAN_SEE + return(true); +#else + return(false); +#endif + } + + if (!other) { + return(true); + } + glm::vec3 myloc; + glm::vec3 oloc; + +#define LOS_DEFAULT_HEIGHT 6.0f + + oloc.x = other->GetX(); + oloc.y = other->GetY(); + oloc.z = other->GetZ() + (other->GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : other->GetSize()) / 2 * SEE_POSITION; + + myloc.x = posX; + myloc.y = posY; + myloc.z = posZ + (GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : GetSize()) / 2 * HEAD_POSITION; + +#if LOSDEBUG>=5 + LogDebug("LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}])", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize); +#endif + return zone->zonemap->CheckLoS(myloc, oloc); +} + //offensive spell aggro int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool is_proc) { @@ -1659,3 +1692,89 @@ void Mob::RogueEvade(Mob *other) return; } +bool Mob::DoLosChecks(Mob* who, Mob* other) { + if (!who->CheckLosFN(other) || !who->CheckWaterLoS(other)) { + if (who->CheckLosCheatExempt(who, other)) { + return true; + } + + if (CheckLosCheat(who, other)) { + return true; + } + + return false; + } + + return true; +} + +bool Mob::CheckLosCheat(Mob* who, Mob* other) { + if (RuleB(Map, CheckForLoSCheat)) { + auto& door_list = entity_list.GetDoorsList(); + for (auto itr : door_list) { + Doors* door = itr.second; + if (door && !door->IsDoorOpen() && (door->GetTriggerType() == 255 || door->GetLockpick() != 0 || door->GetKeyItem() != 0 || door->GetNoKeyring() != 0)) { + if (DistanceNoZ(who->GetPosition(), door->GetPosition()) <= 50) { + auto who_to_door = DistanceNoZ(who->GetPosition(), door->GetPosition()); + auto other_to_door = DistanceNoZ(other->GetPosition(), door->GetPosition()); + auto who_to_other = DistanceNoZ(who->GetPosition(), other->GetPosition()); + auto distance_difference = who_to_other - (who_to_door + other_to_door); + if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) { + LogTestDebug("CheckLosCheat failed at Door [{}], TriggerType [{}], GetLockpick [{}], GetKeyItem [{}], GetNoKeyring [{}]", door->GetDoorID(), door->GetTriggerType(), door->GetLockpick(), door->GetKeyItem(), door->GetNoKeyring()); //deleteme + return false; + } + } + } + } + } + + //if (RuleB(Map, CheckForLoSCheat)) { + // uint8 zone_id = zone->GetZoneID(); + // // ZoneID, target XYZ, my range from target + // //float zone_basic_checks[] = { 6, 36 }; + // //float zone_basic_x_coord[] = { -295, -179.908 }; + // //float zone_basic_y_coord[] = { -18, -630.708 }; + // //float zone_basic_y_coord[] = { 50.97, -69.971 }; + // //float zone_basic_range_check[] = { 21, 10 }; + // //if door and target infront, fail + // //if door and target behind, fail + // + // if (zone_id == 103) { + // Doors* door_to_check = entity_list.FindDoor(8); + // TestDebug("Entered LoSCheat for ZoneID: [{}]", zone_id); //deleteme + // glm::vec4 who_check; who_check.x = 1202; who_check.y = 559; who_check.z = -158.94; + // glm::vec4 other_check; other_check.x = 1291; other_check.y = 559; other_check.z = -158.19; + // float my_distance = DistanceNoZ(who->GetPosition(), who_check); + // float tar_distance = DistanceNoZ(other->GetPosition(), other_check); + // float my_range = 16; + // float tar_range = 75; + // if (my_distance <= my_range && tar_distance <= tar_range && !quest_manager.isdooropen(8)) { + // TestDebug("Door is NOT open"); //deleteme + // TestDebug("LoSCheat failed"); //deleteme + // return false; + // } + // TestDebug("LoS Check for ZoneID: [{}] was [{}] units for [{}], [{}] units for [{}]", zone_id, my_distance, who->GetCleanName(), tar_distance, other->GetCleanName()); //deleteme + // } + //} + return true; +} + +bool Mob::CheckLosCheatExempt(Mob* who, Mob* other) { + if (RuleB(Map, EnableLoSCheatExemptions)) { + glm::vec4 exempt_check_who; + glm::vec4 exempt_check_other; + /* This is an exmaple of how to configure exemptions for LoS checks. + if (zone->GetZoneID() == 222) { //PoEarthB + exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns + //check to be sure the player and the target are in the pit to PoEarthB + //if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) + //otherwise they can pass LoS checks even if they don't have true LoS + if (who->GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(who->GetPosition(), exempt_check_who) <= 800) { + return true; + } + } + */ + } + + return false; +} diff --git a/zone/attack.cpp b/zone/attack.cpp index e2d7cdf176..0d8e420ca1 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1246,7 +1246,12 @@ int64 Mob::GetWeaponDamage(Mob *against, const EQ::ItemInstance *weapon_item, in return 0; } - if (!weapon_item->IsClassEquipable(GetClass())) { + if (!weapon_item->IsClassEquipable(GetClass()) && + ( + !IsBot() || + (IsBot() && !RuleB(Bots, AllowBotEquipAnyClassGear)) + ) + ) { return 0; } diff --git a/zone/bot.cpp b/zone/bot.cpp index aa746e7677..0b7fd0cc82 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -69,23 +69,20 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm RestRegenHP = 0; RestRegenMana = 0; RestRegenEndurance = 0; - m_enforce_spell_settings = false; - m_bot_archery_setting = false; - m_expansion_bitmask = -1; - m_bot_caster_range = 0; + SetBotID(0); SetBotSpellID(0); SetSpawnStatus(false); - SetBotCharmer(false); - SetPetChooser(false); - SetRangerAutoWeaponSelect(false); - SetTaunting(GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight); + SetBotCharmer(false); SetDefaultBotStance(); + SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == Stance::Aggressive)); - SetAltOutOfCombatBehavior(GetClass() == Class::Bard); // will need to be updated if more classes make use of this flag - SetShowHelm(true); SetPauseAI(false); + m_combat_jitter_timer.Disable(); + auto_save_timer.Disable(); + m_rogue_evade_timer.Disable(); + m_monk_evade_timer.Disable(); m_auto_defend_timer.Disable(); SetGuardFlag(false); SetHoldFlag(false); @@ -98,12 +95,12 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm m_previous_pet_order = SPO_Guard; rest_timer.Disable(); - ping_timer.Disable(); - SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT); - if (IsCasterClass(GetClass())) - SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel)); - else - SetStopMeleeLevel(255); + ping_timer.Disable(); + + LoadDefaultBotSettings(); + SetCastedSpellType(UINT16_MAX); + SetCommandedSpell(false); + //DisableBotSpellTimers(); // Do this once and only in this constructor GenerateAppearance(); @@ -131,8 +128,7 @@ Bot::Bot( uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, - NPCType *npcTypeData, - int32 expansion_bitmask + NPCType *npcTypeData ) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) { @@ -175,13 +171,12 @@ Bot::Bot( RestRegenHP = 0; RestRegenMana = 0; RestRegenEndurance = 0; - m_expansion_bitmask = expansion_bitmask; SetBotID(botID); SetBotSpellID(botSpellsID); SetSpawnStatus(false); SetBotCharmer(false); - SetPetChooser(false); - SetRangerAutoWeaponSelect(false); + SetCastedSpellType(UINT16_MAX); + SetCommandedSpell(false); bool stance_flag = false; if (!database.botdb.LoadStance(this, stance_flag) && bot_owner) { @@ -207,6 +202,10 @@ Bot::Bot( SetTaunting((GetClass() == Class::Warrior || GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (GetBotStance() == Stance::Aggressive)); SetPauseAI(false); + m_combat_jitter_timer.Disable(); + auto_save_timer.Disable(); + m_rogue_evade_timer.Disable(); + m_monk_evade_timer.Disable(); m_auto_defend_timer.Disable(); SetGuardFlag(false); SetHoldFlag(false); @@ -220,11 +219,6 @@ Bot::Bot( rest_timer.Disable(); ping_timer.Disable(); - SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT); - if (IsCasterClass(GetClass())) - SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel)); - else - SetStopMeleeLevel(255); strcpy(name, GetCleanName()); @@ -235,7 +229,10 @@ Bot::Bot( EquipBot(); if (GetClass() == Class::Rogue) { - m_evade_timer.Start(); + m_rogue_evade_timer.Start(); + } + if (GetClass() == Class::Monk) { + m_monk_evade_timer.Start(); } m_CastingRoles.GroupHealer = false; @@ -251,6 +248,10 @@ Bot::Bot( LoadAAs(); + LoadDefaultBotSettings(); + + database.botdb.LoadBotSettings(this); + if (database.botdb.LoadBuffs(this)) { //reapply some buffs uint32 buff_count = GetMaxBuffSlots(); @@ -269,6 +270,10 @@ Bot::Bot( switch (spell.effect_id[x1]) { case SE_IllusionCopy: case SE_Illusion: { + if (GetIllusionBlock()) { + break; + } + if (spell.base_value[x1] == -1) { if (gender == Gender::Female) { gender = Gender::Male; @@ -538,39 +543,51 @@ void Bot::SetSuffix(std::string_view bot_suffix) { } } -uint32 Bot::GetBotArcheryRange() { +uint32 Bot::GetBotRangedValue() { const EQ::ItemInstance *range_inst = GetBotItem(EQ::invslot::slotRange); const EQ::ItemInstance *ammo_inst = GetBotItem(EQ::invslot::slotAmmo); - if (!range_inst || !ammo_inst) + if (!range_inst) return 0; const EQ::ItemData *range_item = range_inst->GetItem(); - const EQ::ItemData *ammo_item = ammo_inst->GetItem(); - if (!range_item || !ammo_item || range_item->ItemType != EQ::item::ItemTypeBow || ammo_item->ItemType != EQ::item::ItemTypeArrow) + const EQ::ItemData *ammo_item = nullptr; + + if (ammo_inst) { + ammo_item = ammo_inst->GetItem(); + } + + // Bow requires arrows + if (range_item && range_item->ItemType == EQ::item::ItemTypeBow && (!ammo_item || ammo_item->ItemType != EQ::item::ItemTypeArrow)) { return 0; + } + + // Throwing items + if (range_item && (range_item->ItemType == EQ::item::ItemTypeSmallThrowing || range_item->ItemType == EQ::item::ItemTypeLargeThrowing)) { + return range_item->Range; + } - // everything is good! - return (range_item->Range + ammo_item->Range); + // Bows and arrows + if (range_item && ammo_item && range_item->ItemType == EQ::item::ItemTypeBow && ammo_item->ItemType == EQ::item::ItemTypeArrow) { + return (range_item->Range + ammo_item->Range); + } + + return 0; } -void Bot::ChangeBotArcherWeapons(bool isArcher) { - if ((GetClass()==Class::Warrior) || (GetClass()==Class::Paladin) || (GetClass()==Class::Ranger) || (GetClass()==Class::ShadowKnight) || (GetClass()==Class::Rogue)) { - if (!isArcher) { - BotAddEquipItem(EQ::invslot::slotPrimary, GetBotItemBySlot(EQ::invslot::slotPrimary)); - BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotSecondary)); - SetAttackTimer(); - BotGroupSay(this, "My blade is ready"); - } else { - BotRemoveEquipItem(EQ::invslot::slotPrimary); - BotRemoveEquipItem(EQ::invslot::slotSecondary); - BotAddEquipItem(EQ::invslot::slotAmmo, GetBotItemBySlot(EQ::invslot::slotAmmo)); - BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotRange)); - SetAttackTimer(); - BotGroupSay(this, "My bow is true and ready"); - } +void Bot::ChangeBotRangedWeapons(bool isRanged) { + if (!isRanged) { + BotAddEquipItem(EQ::invslot::slotPrimary, GetBotItemBySlot(EQ::invslot::slotPrimary)); + BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotSecondary)); + SetAttackTimer(); + BotGroupSay(this, "My blade is ready"); + } else { + BotRemoveEquipItem(EQ::invslot::slotPrimary); + BotRemoveEquipItem(EQ::invslot::slotSecondary); + BotAddEquipItem(EQ::invslot::slotAmmo, GetBotItemBySlot(EQ::invslot::slotAmmo)); + BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotRange)); + SetAttackTimer(); + BotGroupSay(this, "My bow is true and ready"); //TODO bot rewrite - make this say throwing or bow } - else - BotGroupSay(this, "I don't know how to use a bow"); } void Bot::Sit() { @@ -1304,10 +1321,13 @@ bool Bot::IsValidName() bool Bot::IsValidName(std::string& name) { - if (name.length() < 4) + if (name.length() < 4 || name.length() > 15) { return false; - if (!isupper(name[0])) + } + + if (!isupper(name[0])) { return false; + } for (char c : name.substr(1)) { if (!RuleB(Bots, AllowCamelCaseNames) && !islower(c)) { @@ -1344,6 +1364,7 @@ bool Bot::Save() database.botdb.SaveBuffs(this); database.botdb.SaveTimers(this); database.botdb.SaveStance(this); + database.botdb.SaveBotSettings(this); if (!SavePet()) bot_owner->Message(Chat::White, "Failed to save pet for '%s'", GetCleanName()); @@ -1389,24 +1410,30 @@ bool Bot::DeleteBot() RemoveBotFromRaid(this); } - if (!database.botdb.DeleteItems(GetBotID())) { - return false; - } + if (!RuleB(Bots, BotSoftDeletes)) { + if (!database.botdb.DeleteItems(GetBotID())) { + return false; + } - if (!database.botdb.DeleteTimers(GetBotID())) { - return false; - } + if (!database.botdb.DeleteTimers(GetBotID())) { + return false; + } - if (!database.botdb.DeleteBuffs(GetBotID())) { - return false; - } + if (!database.botdb.DeleteBuffs(GetBotID())) { + return false; + } - if (!database.botdb.DeleteStance(GetBotID())) { - return false; - } + if (!database.botdb.DeleteStance(GetBotID())) { + return false; + } - if (!database.botdb.DeleteBot(GetBotID())) { - return false; + if (!database.botdb.DeleteBotSettings(GetBotID())) { + return false; + } + + if (!database.botdb.DeleteBot(GetBotID())) { + return false; + } } return true; @@ -1708,94 +1735,137 @@ void Bot::AI_Bot_Init() } void Bot::SpellProcess() { - if (spellend_timer.Check(false)) { + if (spellend_timer.Check(false)) { NPC::SpellProcess(); if (GetClass() == Class::Bard && casting_spell_id != 0) casting_spell_id = 0; } } void Bot::BotMeditate(bool isSitting) { - if (isSitting) { - if (GetManaRatio() < 99.0f || GetHPRatio() < 99.0f) { - if (!IsEngaged() && !IsSitting()) { - Sit(); - } - } else { - if (IsSitting()) { - Stand(); - } + if (GetManaRatio() < GetManaWhenToMed() || (GetHPRatio() < GetHPWhenToMed() && GetLevel() < GetStopMeleeLevel())) { + if ((!IsEngaged() || (IsEngaged() && GetMedInCombat() && !HasTargetReflection())) && !isSitting) { + Sit(); } - } else { - if (IsSitting()) { + } + else { + if (isSitting) { Stand(); } } } -void Bot::BotRangedAttack(Mob* other) { - //make sure the attack and ranged timers are up - //if the ranged timer is disabled, then they have no ranged weapon and shouldent be attacking anyhow - if ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check())) { - LogCombatDetail("Bot Archery attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); +void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { + if (!TargetValidation(other)) { return; } + + if (!CanDoubleAttack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { + LogCombatDetail("Bot ranged attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return; } const auto rangedItem = GetBotItem(EQ::invslot::slotRange); const EQ::ItemData* RangeWeapon = nullptr; - if (rangedItem) + if (rangedItem) { RangeWeapon = rangedItem->GetItem(); + } + + if (!RangeWeapon) { + return; + } const auto ammoItem = GetBotItem(EQ::invslot::slotAmmo); const EQ::ItemData* Ammo = nullptr; if (ammoItem) Ammo = ammoItem->GetItem(); - if (!RangeWeapon || !Ammo) - return; + // Bow requires arrows + if ( + !Ammo || + (RangeWeapon && + ( + (RangeWeapon->ItemType != EQ::item::ItemTypeBow && RangeWeapon->ItemType != EQ::item::ItemTypeSmallThrowing && RangeWeapon->ItemType != EQ::item::ItemTypeLargeThrowing) || + (RangeWeapon->ItemType == EQ::item::ItemTypeBow && (Ammo->ItemType != EQ::item::ItemTypeArrow)) || + ( + (RangeWeapon->ItemType == EQ::item::ItemTypeSmallThrowing || RangeWeapon->ItemType == EQ::item::ItemTypeLargeThrowing) && + ammoItem->GetCharges() < 1 || + ( + (RuleI(Bots, StackSizeMin) != -1 && rangedItem->GetCharges() != RangeWeapon->StackSize) || + rangedItem->GetCharges() < RuleI(Bots, StackSizeMin) + ) + ) + ) + ) + ) { + if (!Ammo || ammoItem->GetCharges() < 1) { + GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + } - LogCombatDetail("Shooting [{}] with bow [{}] ([{}]) and arrow [{}] ([{}])", other->GetCleanName(), RangeWeapon->Name, RangeWeapon->ID, Ammo->Name, Ammo->ID); - if (!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) return; + } - SendItemAnimation(other, Ammo, EQ::skills::SkillArchery); - DoArcheryAttackDmg(other, rangedItem, ammoItem); // watch + LogCombatDetail("Ranged attacking [{}] with {} [{}] ([{}]){}{}{}", + other->GetCleanName(), + (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? "bow" : "throwing"), + RangeWeapon->Name, + RangeWeapon->ID, + (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? " and arrow " : ""), + (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? Ammo->Name : ""), + (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? std::to_string(Ammo->ID) : "") + ); - //break invis when you attack - if (invisible) { - LogCombatDetail("Removing invisibility due to melee attack"); - BuffFadeByEffect(SE_Invisibility); - BuffFadeByEffect(SE_Invisibility2); - invisible = false; + if (!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { + return; } - if (invisible_undead) { - LogCombatDetail("Removing invisibility vs. undead due to melee attack"); - BuffFadeByEffect(SE_InvisVsUndead); - BuffFadeByEffect(SE_InvisVsUndead2); - invisible_undead = false; - } + SendItemAnimation(other, Ammo, (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? EQ::skills::SkillArchery : EQ::skills::SkillThrowing)); + if (RangeWeapon->ItemType == EQ::item::ItemTypeBow) { + DoArcheryAttackDmg(other, rangedItem, ammoItem); // watch + //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. + int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; - if (invisible_animals) { - LogCombatDetail("Removing invisibility vs. animals due to melee attack"); - BuffFadeByEffect(SE_InvisVsAnimals); - invisible_animals = false; - } + // Consume Ammo, unless Ammo Consumption is disabled or player has Endless Quiver + bool consumes_ammo = RuleB(Bots, BotArcheryConsumesAmmo); + if ( + consumes_ammo && + ( + RangeWeapon->ExpendableArrow || + !ChanceAvoidConsume || + (ChanceAvoidConsume < 100 && zone->random.Int(0, 99) > ChanceAvoidConsume) + ) + ) { + ammoItem->SetCharges((ammoItem->GetCharges() - 1)); + LogCombat("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); - if (spellbonuses.NegateIfCombat) - BuffFadeByEffect(SE_NegateIfCombat); + if (ammoItem->GetCharges() < 1) { + RemoveBotItemBySlot(EQ::invslot::slotAmmo); + BotRemoveEquipItem(EQ::invslot::slotAmmo); + } + } + else if (!consumes_ammo) { + LogCombat("Archery Ammo Consumption is disabled."); + } + else { + LogCombat("Endless Quiver prevented Ammo Consumption."); + } + } + else { + DoThrowingAttackDmg(other, rangedItem); // watch + // Consume Ammo, unless Ammo Consumption is disabled + if (RuleB(Bots, BotThrowingConsumesAmmo)) { + ammoItem->SetCharges((ammoItem->GetCharges() - 1)); + LogCombat("Consumed Throwing Ammo from slot {}.", EQ::invslot::slotAmmo); - if (hidden || improved_hidden) { - hidden = false; - improved_hidden = false; - auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - auto sa_out = (SpawnAppearance_Struct*)outapp->pBuffer; - sa_out->spawn_id = GetID(); - sa_out->type = 0x03; - sa_out->parameter = 0; - entity_list.QueueClients(this, outapp, true); - safe_delete(outapp); + if (ammoItem->GetCharges() < 1) { + RemoveBotItemBySlot(EQ::invslot::slotAmmo); + BotRemoveEquipItem(EQ::invslot::slotAmmo); + } + } + else { + LogCombat("Throwing Ammo Consumption is disabled."); + } } + + CommonBreakInvisibleFromCombat(); } bool Bot::CheckBotDoubleAttack(bool tripleAttack) { @@ -1832,6 +1902,53 @@ bool Bot::CheckBotDoubleAttack(bool tripleAttack) { return false; } +bool Bot::CheckTripleAttack() +{ + int chance; + + if (RuleB(Combat, ClassicTripleAttack)) { + if ( + GetLevel() >= 60 && + ( + GetClass() == Class::Warrior || + GetClass() == Class::Ranger || + GetClass() == Class::Monk || + GetClass() == Class::Berserker + ) + ) { + switch (GetClass()) { + case Class::Warrior: + chance = RuleI(Combat, ClassicTripleAttackChanceWarrior); + break; + case Class::Ranger: + chance = RuleI(Combat, ClassicTripleAttackChanceRanger); + break; + case Class::Monk: + chance = RuleI(Combat, ClassicTripleAttackChanceMonk); + break; + case Class::Berserker: + chance = RuleI(Combat, ClassicTripleAttackChanceBerserker); + break; + default: + break; + } + } + } + else { + chance = GetSkill(EQ::skills::SkillTripleAttack); + } + + if (chance < 1) { + return false; + } + + int inc = aabonuses.TripleAttackChance + spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance; + chance = static_cast(chance * (1 + inc / 100.0f)); + chance = (chance * 100) / (chance + 800); + + return zone->random.Int(1, 100) <= chance; +} + void Bot::SetTarget(Mob *mob) { if (mob != this) { @@ -1839,13 +1956,6 @@ void Bot::SetTarget(Mob *mob) } } -void Bot::SetStopMeleeLevel(uint8 level) { - if (IsCasterClass(GetClass()) || IsHybridClass(GetClass())) - _stopMeleeLevel = level; - else - _stopMeleeLevel = 255; -} - void Bot::SetGuardMode() { StopMoving(); @@ -1864,25 +1974,6 @@ void Bot::SetHoldMode() { // AI Processing for the Bot object -constexpr float MAX_CASTER_DISTANCE[Class::PLAYER_CLASS_COUNT] = { - 0, - (34 * 34), - (24 * 24), - (28 * 28), - (26 * 26), - (42 * 42), - 0, - (30 * 30), - 0, - (38 * 38), - (54 * 54), - (48 * 48), - (52 * 52), - (50 * 50), - (32 * 32), - 0 -}; - void Bot::AI_Process() { @@ -1940,6 +2031,13 @@ void Bot::AI_Process() return; } + if (raid && r_group == RAID_GROUPLESS) { + glm::vec3 Goal(0, 0, 0); + TryNonCombatMovementChecks(bot_owner, follow_mob, Goal); + + return; + } + float fm_distance = DistanceSquared(m_Position, follow_mob->GetPosition()); float lo_distance = DistanceSquared(m_Position, leash_owner->GetPosition()); float leash_distance = RuleR(Bots, LeashDistance); @@ -1951,7 +2049,6 @@ void Bot::AI_Process() } // HEAL ROTATION CASTING CHECKS - HealRotationChecks(); if (GetAttackFlag()) { // Push owner's target onto our hate list @@ -1962,12 +2059,10 @@ void Bot::AI_Process() } //ALT COMBAT (ACQUIRE HATE) - glm::vec3 Goal(0, 0, 0); // We have aggro to choose from if (IsEngaged()) { - if (rest_timer.Enabled()) { rest_timer.Disable(); } @@ -1991,7 +2086,6 @@ void Bot::AI_Process() // DEFAULT (ACQUIRE TARGET) // VERIFY TARGET AND STANCE - auto tar = GetBotTarget(bot_owner); if (!tar) { return; @@ -2006,7 +2100,6 @@ void Bot::AI_Process() float tar_distance = DistanceSquared(m_Position, tar->GetPosition()); // TARGET VALIDATION - if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) { return; } @@ -2026,76 +2119,136 @@ void Bot::AI_Process() SendAddPlayerState(PlayerState::Aggressive); } +// COMBAT RANGE CALCS + + bool atCombatRange = false; + bool behindMob = false; + uint8 stopMeleeLevel = GetStopMeleeLevel(); + const EQ::ItemInstance* p_item; + const EQ::ItemInstance* s_item; + float melee_distance_min = 0.0f; + float melee_distance_max = 0.0f; + float melee_distance = 0.0f; + + CheckCombatRange(tar, sqrt(tar_distance), atCombatRange, behindMob, p_item, s_item, melee_distance_min, melee_distance_max, melee_distance, stopMeleeLevel); + // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { + //TODO bot rewrite - add ways to here to determine if throw stone is allowed, then check for ranged/spell/melee + if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { + return; + } - constexpr size_t PULL_AGGRO = 5225; // spells[5225]: 'Throw Stone' - 0 cast time + if (RuleB(Bots, AllowSpellPulling)) { + uint16 pullSpell = RuleI(Bots, PullSpellID); - if (tar_distance <= (spells[PULL_AGGRO].range * spells[PULL_AGGRO].range)) { + if (tar_distance <= (spells[pullSpell].range * spells[pullSpell].range)) { + StopMoving(); - StopMoving(); - CastSpell(PULL_AGGRO, tar->GetID()); - return; + if (!TargetValidation(tar)) { return; } + + CastSpell(pullSpell, tar->GetID()); + + return; + } } - } + else { + if (atCombatRange && IsBotRanged()){ + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); -// COMBAT RANGE CALCS + TryRangedAttack(tar); - bool atCombatRange; - const EQ::ItemInstance* p_item; - const EQ::ItemInstance* s_item; - CheckCombatRange(tar, tar_distance, atCombatRange, p_item, s_item); + if (!TargetValidation(tar)) { return; } + + if (CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } + + return; + } + } + } // ENGAGED AT COMBAT RANGE // We can fight if (atCombatRange) { + bool jitterCooldown = false; - if (IsMoving()) { - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - return; + if (m_combat_jitter_timer.GetRemainingTime() > 1 && m_combat_jitter_timer.Enabled()) { + jitterCooldown = true; } - if (AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { - - if (TryEvade(tar)) { - return; + if (IsMoving() || GetCombatJitterFlag() || GetCombatOutOfRangeJitterFlag()) { + if (!GetCombatJitterFlag() || !IsMoving() || GetCombatOutOfRangeJitterFlag()) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); } - if (TryFacingTarget(tar)) { + return; + } + + if (!jitterCooldown && AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { + DoCombatPositioning(tar, Goal, stopMeleeLevel, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behindMob); + return; + } + else { + if (!IsSitting() && !IsFacingMob(tar)) { + FaceTarget(tar); return; } } - if (!IsBotNonSpellFighter() && AI_EngagedCastCheck()) { + if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { return; } - // Up to this point, GetTarget() has been safe to dereference since the initial - // if (!GetTarget() || GetAppearance() == eaDead) { return false; } call. Due to the chance of the target dying and our pointer - // being nullified, we need to test it before dereferencing to avoid crashes - - if (IsBotArcher() && TryRangedAttack(tar)) { + if (IsMoving()) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); return; } - if (!IsBotArcher() && GetLevel() < GetStopMeleeLevel()) { - if (!TryClassAttacks(tar)) { + if (IsBotRanged() && ranged_timer.Check(false)) { // Can shoot mezzed, stunned and dead!? + TryRangedAttack(tar); + + if (!TargetValidation(tar)) { return; } + + if (CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } + } + else if (!IsBotRanged() && GetLevel() < stopMeleeLevel) { + if (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())) { return; } - if (!TryPrimaryWeaponAttacks(tar, p_item)) { - return; + if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) { + DoClassAttacks(tar); } - if (!TrySecondaryWeaponAttacks(tar, s_item)) { - return; + if (!TargetValidation(tar)) { return; } + + if (attack_timer.Check()) { + TryCombatProcs(p_item, tar, EQ::invslot::slotPrimary); + TriggerDefensiveProcs(tar, EQ::invslot::slotPrimary, false); + DoAttackRounds(tar, EQ::invslot::slotPrimary); + + if (TryDoubleMeleeRoundEffect()) { + DoAttackRounds(tar, EQ::invslot::slotPrimary); + } } - } - if (GetAppearance() == eaDead) { - return; + if (!TargetValidation(tar)) { return; } + + if (attack_dw_timer.Check()) { + if (CanThisClassDualWield() && CastToClient()->CheckDualWield()) { + TryCombatProcs(s_item, tar, EQ::invslot::slotSecondary); + DoAttackRounds(tar, EQ::invslot::slotSecondary); + } + } + + if (!TargetValidation(tar)) { return; } + } } @@ -2107,12 +2260,9 @@ void Bot::AI_Process() // End not in combat range - if (TryMeditate()) { - return; - } + TryMeditate(); } else { // Out-of-combat behavior - SetAttackFlag(false); SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { @@ -2145,16 +2295,19 @@ void Bot::AI_Process() if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { return; } - if (TryIdleChecks(fm_distance)) { + if (!HOLDING && AI_HasSpells() && TryIdleChecks(fm_distance)) { return; } - if (TryBardMovementCasts()) { + if (!HOLDING && AI_HasSpells() && TryBardMovementCasts()) { return; } } } bool Bot::TryBardMovementCasts() {// Basically, bard bots get a chance to cast idle spells while moving + if (HOLDING) { + return false; + } if (GetClass() == Class::Bard && IsMoving() && NOT_PASSIVE && !spellend_timer.Enabled() && AI_think_timer->Check()) { @@ -2165,7 +2318,6 @@ bool Bot::TryBardMovementCasts() {// Basically, bard bots get a chance to cast i } bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal) {// Non-engaged movement checks - if (AI_movement_timer->Check() && (!IsCasting() || GetClass() == Class::Bard)) { if (GUARDING) { Goal = GetGuardPoint(); @@ -2196,6 +2348,7 @@ bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, g } } } + return false; } @@ -2210,18 +2363,10 @@ bool Bot::TryIdleChecks(float fm_distance) { !spellend_timer.Enabled() ) { - if (NOT_PASSIVE) { - - if (!AI_IdleCastCheck() && !IsCasting() && GetClass() != Class::Bard) { - BotMeditate(true); - } - - } else { - if (GetClass() != Class::Bard) { - BotMeditate(true); - } - + if (!AI_IdleCastCheck() && !IsCasting()) { + BotMeditate(IsSitting()); } + return true; } return false; @@ -2283,30 +2428,35 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { bool Bot::TryMeditate() { if (!IsMoving() && !spellend_timer.Enabled()) { - if (GetTarget() && AI_EngagedCastCheck()) { - BotMeditate(false); - } else if (GetArchetype() == Archetype::Caster) { - BotMeditate(true); + + if (IsEngaged() && HasOrMayGetAggro(IsSitting())) { + if (IsSitting()) { + Stand(); + return false; + } } + BotMeditate(IsSitting()); + if (!(GetPlayerState() & static_cast(PlayerState::Aggressive))) { SendAddPlayerState(PlayerState::Aggressive); } return true; } + return false; } // This code actually gets processed when we are too far away from target and have not engaged yet bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { - if (AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { if (GetTarget() && !IsRooted()) { LogAIDetail("Pursuing [{}] while engaged", GetTarget()->GetCleanName()); Goal = GetTarget()->GetPosition(); + if (DistanceSquared(m_Position, Goal) <= leash_distance) { RunTo(Goal.x, Goal.y, Goal.z); - + SetCombatOutOfRangeJitter(); } else { WipeHateList(); SetTarget(nullptr); @@ -2316,8 +2466,8 @@ bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { GetPet()->SetTarget(nullptr); } } - return true; + return true; } else { if (IsMoving()) { StopMoving(); @@ -2333,252 +2483,206 @@ bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { } // This is a mob that is fleeing either because it has been feared or is low on hitpoints - AI_PursueCastCheck(); + if (!HOLDING && AI_HasSpells()) { + AI_PursueCastCheck(); + } return true; } + return false; } -bool Bot::TrySecondaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* s_item) { +void Bot::DoAttackRounds(Mob* target, int hand) { + if (!target || (target && target->IsCorpse())) { + return; + } - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (attack_dw_timer.Check() && CanThisClassDualWield()) { - const EQ::ItemData* s_itemdata = nullptr; + Attack(target, hand, false, false); - // Can only dual wield without a weapon if you're a monk - if (s_item || (GetClass() == Class::Monk)) { + bool candouble = CanThisClassDoubleAttack(); + // extra off hand non-sense, can only double with skill of 150 or above + // or you have any amount of GiveDoubleAttack + if (candouble && hand == EQ::invslot::slotSecondary) + candouble = + GetSkill(EQ::skills::SkillDoubleAttack) > 149 || + (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack) > 0; - if (s_item) { - s_itemdata = s_item->GetItem(); - } + if (candouble) { + if (CastToClient()->CheckDoubleAttack()) { + Attack(target, hand, false, false); - if (!s_itemdata) { - return false; - } + if (hand == EQ::invslot::slotPrimary) { - bool use_fist = true; - if (s_itemdata) { - use_fist = false; + if (HasTwoHanderEquipped()) { + auto extraattackchance = aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE]; + if (extraattackchance && zone->random.Roll(extraattackchance)) { + auto extraattackamt = std::max({ aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); + for (int i = 0; i < extraattackamt; i++) { + Attack(target, hand, false, false); + } + } + } + else { + auto extraattackchance_primary = aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE]; + if (extraattackchance_primary && zone->random.Roll(extraattackchance_primary)) { + auto extraattackamt_primary = std::max({ aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); + for (int i = 0; i < extraattackamt_primary; i++) { + Attack(target, hand, false, false); + } + } + } } - if (use_fist || !s_itemdata->IsType2HWeapon()) { - - float DualWieldProbability = 0.0f; - - int32 Ambidexterity = (aabonuses.Ambidexterity + spellbonuses.Ambidexterity + itembonuses.Ambidexterity); - DualWieldProbability = ((GetSkill(EQ::skills::SkillDualWield) + GetLevel() + Ambidexterity) / 400.0f); // 78.0 max chance - - int32 DWBonus = (spellbonuses.DualWieldChance + itembonuses.DualWieldChance); - DualWieldProbability += (DualWieldProbability * float(DWBonus) / 100.0f); + if (hand == EQ::invslot::slotSecondary) { + auto extraattackchance_secondary = aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE]; + if (extraattackchance_secondary && zone->random.Roll(extraattackchance_secondary)) { + auto extraattackamt_secondary = std::max({ aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); + for (int i = 0; i < extraattackamt_secondary; i++) { + Attack(target, hand, false, false); + } + } + } - float random = zone->random.Real(0, 1); - if (random < DualWieldProbability) { // Max 78% for DW chance - Attack(tar, EQ::invslot::slotSecondary); // Single attack with offhand + // you can only triple from the main hand + if (hand == EQ::invslot::slotPrimary && CanThisClassTripleAttack()) { + if (CheckTripleAttack()) { + Attack(target, hand, false, false); + int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance + + itembonuses.FlurryChance; - if (GetAppearance() == eaDead) { return false; } - TryCombatProcs(s_item, tar, EQ::invslot::slotSecondary); + if (flurry_chance && zone->random.Roll(flurry_chance)) { + Attack(target, hand, false, false); - if (GetAppearance() == eaDead) { return false; } - if (CanThisClassDoubleAttack() && CheckBotDoubleAttack() && tar->GetHP() > -10) { - Attack(tar, EQ::invslot::slotSecondary); // Single attack with offhand + if (zone->random.Roll(flurry_chance)) { + Attack(target, hand, false, false); + } + //MessageString(Chat::NPCFlurry, YOU_FLURRY); //TODO bot rewrite - add output to others hits with flurry message } } } } } - return true; } -bool Bot::TryPrimaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* p_item) { - - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (attack_timer.Check()) { // Process primary weapon attacks +bool Bot::TryRangedAttack(Mob* tar) { - Attack(tar, EQ::invslot::slotPrimary); + if (IsBotRanged() && ranged_timer.Check(false)) { - if (GetAppearance() == eaDead) { return false; } - TriggerDefensiveProcs(tar, EQ::invslot::slotPrimary, false); + if (!TargetValidation(tar)) { return false; } - if (GetAppearance() == eaDead) { return false; } - TryCombatProcs(p_item, tar, EQ::invslot::slotPrimary); + if (GetPullingFlag() || GetTarget()->GetHPRatio() <= 99.0f) { + BotRangedAttack(tar); + } - if (GetAppearance() == eaDead) { return false; } - if (CanThisClassDoubleAttack()) { + return true; + } - if (CheckBotDoubleAttack()) { - Attack(tar, EQ::invslot::slotPrimary, true); - } + return false; +} - if (GetAppearance() == eaDead) { return false; } - if (GetSpecialAbility(SpecialAbility::TripleAttack) && CheckBotDoubleAttack(true)) { +bool Bot::TryFacingTarget(Mob* tar) { + if (!IsSitting() && !IsFacingMob(tar)) { + FaceTarget(tar); + return true; + } + return false; +} - Attack(tar, EQ::invslot::slotPrimary, true); - } - if (GetAppearance() == eaDead) { return false; } - // quad attack, does this belong here?? - if (GetSpecialAbility(SpecialAbility::QuadrupleAttack) && CheckBotDoubleAttack(true)) { - Attack(tar, EQ::invslot::slotPrimary, true); - } - } +bool Bot::TryEvade(Mob* tar) { + if (HasTargetReflection() && !tar->IsFeared() && !tar->IsStunned()) { + if (GetClass() == Class::Rogue && !GetSpellHold(BotSpellTypes::Escape)) { + if (m_rogue_evade_timer.Check(false)) { + int timer_duration = (HideReuseTime - GetSkillReuseTime(EQ::skills::SkillHide)) * 1000; - if (GetAppearance() == eaDead) { return false; } - // Live AA - Flurry, Rapid Strikes ect (Flurry does not require Triple Attack). - if (int32 flurrychance = (aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance)) { + if (timer_duration < 0) { + timer_duration = 0; + } - if (zone->random.Int(0, 100) < flurrychance) { + m_rogue_evade_timer.Start(timer_duration); + BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); - MessageString(Chat::NPCFlurry, YOU_FLURRY); - Attack(tar, EQ::invslot::slotPrimary, false); + if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) { + //SendAppearancePacket(AT_Invis, Invisibility::Invisible); + RogueEvade(tar); + } - if (GetAppearance() == eaDead) { return false; } - Attack(tar, EQ::invslot::slotPrimary, false); + //SendAppearancePacket(AT_Invis, Invisibility::Visible); + return true; } } + else if (GetClass() == Class::Monk && GetLevel() >= 17 && !GetSpellHold(BotSpellTypes::Escape)) { + if (m_monk_evade_timer.Check(false)) { + int timer_duration = (FeignDeathReuseTime - GetSkillReuseTime(EQ::skills::SkillFeignDeath)) * 1000; - if (GetAppearance() == eaDead) { return false; } - auto ExtraAttackChanceBonus = - (spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] + - aabonuses.ExtraAttackChance[0]); - - if ( - ExtraAttackChanceBonus && - p_item && - p_item->GetItem()->IsType2HWeapon() && - zone->random.Int(0, 100) < ExtraAttackChanceBonus - ) { - Attack(tar, EQ::invslot::slotPrimary, false); - } - } - return true; -} - -// We can't fight if we don't have a target, are stun/mezzed or dead.. -bool Bot::TryClassAttacks(Mob* tar) { - -// Stop attacking if the target is enraged - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())) { - return false; - } - - // First, special attack per class (kick, backstab etc..) - DoClassAttacks(tar); - return true; -} + if (timer_duration < 0) { + timer_duration = 0; + } -bool Bot::TryRangedAttack(Mob* tar) { + m_monk_evade_timer.Start(timer_duration); + BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); - if (IsBotArcher() && ranged_timer.Check(false)) { + if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillFeignDeath)) { + //SendAppearancePacket(AT_Anim, ANIM_DEATH); + entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); + } + else { + SetFeigned(true); + //SendAppearancePacket(AT_Anim, ANIM_DEATH); + } - if (!GetTarget() || GetAppearance() == eaDead) { return false; } - if (GetTarget()->GetHPRatio() <= 99.0f) { - BotRangedAttack(tar); + //SendAppearancePacket(AT_Anim, ANIM_STAND); + SetFeigned(false); + return true; + } } - return true; } - return false; -} - -bool Bot::TryFacingTarget(Mob* tar) { - if (!IsSitting() && !IsFacingMob(tar)) { - FaceTarget(tar); - return true; - } return false; } +void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance_max, float& melee_distance, uint8 stopMeleeLevel) { + atCombatRange= false; -bool Bot::TryEvade(Mob* tar) { - - if ( - !IsRooted() && - HasTargetReflection() && - !tar->IsFeared() && - !tar->IsStunned() && - GetClass() == Class::Rogue && - m_evade_timer.Check(false) - ) { - int timer_duration = (HideReuseTime - GetSkillReuseTime(EQ::skills::SkillHide)) * 1000; - - if (timer_duration < 0) { - timer_duration = 0; - } - - m_evade_timer.Start(timer_duration); - if (zone->random.Int(0, 260) < (int) GetSkill(EQ::skills::SkillHide)) { - RogueEvade(tar); - } - return true; - } - - return false; -} - -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item) { + p_item = GetBotItem(EQ::invslot::slotPrimary); + s_item = GetBotItem(EQ::invslot::slotSecondary); - atCombatRange= false; - p_item= GetBotItem(EQ::invslot::slotPrimary); - s_item= GetBotItem(EQ::invslot::slotSecondary); - bool behind_mob = false; bool backstab_weapon = false; - if (GetClass() == Class::Rogue) { - behind_mob = BehindMob(tar, GetX(), GetY()); // Can be separated for other future use - backstab_weapon = p_item && p_item->GetItemBackstabDamage(); + if (GetBehindMob()) { + behindMob = BehindMob(tar, GetX(), GetY()); // Can be separated for other future use + if (GetClass() == Class::Rogue) { + backstab_weapon = p_item && p_item->GetItemBackstabDamage(); + } } // Calculate melee distances - float melee_distance_max = 0.0f; - float melee_distance = 0.0f; - - CalcMeleeDistances(tar, p_item, s_item, behind_mob, backstab_weapon, melee_distance_max, melee_distance); - - float caster_distance_max = GetBotCasterMaxRange(melee_distance_max); - - bool atArcheryRange = IsArcheryRange(tar); - - SetRangerCombatWeapon(atArcheryRange); - - bool stop_melee_level = GetLevel() >= GetStopMeleeLevel(); - if (IsBotArcher() && atArcheryRange) { - atCombatRange = true; - } - else if (caster_distance_max && tar_distance <= caster_distance_max && stop_melee_level) { - atCombatRange = true; - } - else if (tar_distance <= melee_distance) { + CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_max, melee_distance, melee_distance_min, stopMeleeLevel); + + //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be {} [{} - {}] away." + // , GetCleanName() + // , (tar_distance <= melee_distance ? "within range of" : "too far away from") + // , tar->GetCleanName() + // , tar_distance + // , (tar_distance <= melee_distance ? "but only needed" : "but need to be") + // , melee_distance + // , melee_distance_min + // , melee_distance_max + //); //deleteme + + if (tar_distance <= melee_distance) { atCombatRange = true; } } -void Bot::SetRangerCombatWeapon(bool atArcheryRange) { - - if (GetRangerAutoWeaponSelect()) { - bool changeWeapons = false; - - if (atArcheryRange && !IsBotArcher()) { - SetBotArcherySetting(true); - changeWeapons = true; - } - - else if (!atArcheryRange && IsBotArcher()) { - SetBotArcherySetting(false); - changeWeapons = true; - } - - if (changeWeapons) { - ChangeBotArcherWeapons(IsBotArcher()); - } - } -} - -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behind_mob, bool backstab_weapon, float& melee_distance_max, float& melee_distance) const { - +void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_max, float& melee_distance, float& melee_distance_min, uint8 stopMeleeLevel) { float size_mod = GetSize(); float other_size_mod = tar->GetSize(); + bool resquareDistance = false; // For races with a fixed size if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) { @@ -2614,58 +2718,115 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it size_mod *= (size_mod * 4.0f); } + if (tar->GetRace() == Race::VeliousDragon) // Lord Vyemm and other velious dragons + { + size_mod *= 1.75; + } + if (tar->GetRace() == Race::DragonSkeleton) // Dracoliche in Fear. Skeletal Dragon + { + size_mod *= 2.25; + } + + size_mod *= RuleR(Combat, HitBoxMod); // used for testing sizemods on different races. + // Prevention of ridiculously sized hit boxes if (size_mod > 10000.0f) { size_mod = (size_mod / 7.0f); } melee_distance_max = size_mod; + if (!RuleB(Bots, UseFlatNormalMeleeRange)) { + switch (GetClass()) { + case Class::Warrior: + case Class::Paladin: + case Class::ShadowKnight: + if (p_item && p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.45f; + } + else if ((s_item && s_item->GetItem()->IsTypeShield()) || (!p_item && !s_item)) { + melee_distance = melee_distance_max * 0.35f; + } + else { + melee_distance = melee_distance_max * 0.40f; + } + break; + case Class::Necromancer: + case Class::Wizard: + case Class::Magician: + case Class::Enchanter: + if (p_item && p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.95f; + } + else { + melee_distance = melee_distance_max * 0.75f; + } + break; + case Class::Rogue: + if (behindMob && backstab_weapon) { + if (p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.30f; + } + else { + melee_distance = melee_distance_max * 0.25f; + } + break; + } + // Fall-through + default: + if (p_item && p_item->GetItem()->IsType2HWeapon()) { + melee_distance = melee_distance_max * 0.70f; + } + else { + melee_distance = melee_distance_max * 0.50f; + } - switch (GetClass()) { - case Class::Warrior: - case Class::Paladin: - case Class::ShadowKnight: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.45f; - } - else if ((s_item && s_item->GetItem()->IsTypeShield()) || (!p_item && !s_item)) { - melee_distance = melee_distance_max * 0.35f; - } - else { - melee_distance = melee_distance_max * 0.40f; - } - break; - case Class::Necromancer: - case Class::Wizard: - case Class::Magician: - case Class::Enchanter: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.95f; - } - else { - melee_distance = melee_distance_max * 0.75f; - } - break; - case Class::Rogue: - if (behind_mob && backstab_weapon) { - if (p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.30f; - } - else { - melee_distance = melee_distance_max * 0.25f; - } - break; + break; } - // Fall-through - default: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.70f; + + melee_distance = sqrt(melee_distance); + melee_distance_max = sqrt(melee_distance_max); + } + else { + melee_distance_max = sqrt(melee_distance_max); + melee_distance = melee_distance_max * RuleR(Bots, NormalMeleeRangeDistance); + } + + if (melee_distance > RuleR(Bots, MaxDistanceForMelee)) { + melee_distance = RuleR(Bots, MaxDistanceForMelee); + } + + melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMeleeDistance); + + if (taunting) { + melee_distance_min = melee_distance_max * RuleR(Bots, PercentTauntMinMeleeDistance); + melee_distance = melee_distance_max * RuleR(Bots, TauntNormalMeleeRangeDistance); + } + + if (!taunting && !IsBotRanged() && GetMaxMeleeRange()) { + melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); + melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); + } + + + /* Caster Range Checks */ + bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; + if (isStopMeleeLevel) { + melee_distance = GetBotCasterMaxRange(melee_distance_max); + if (RuleB(Bots, CastersStayJustOutOfMeleeRange)) { + melee_distance_min = melee_distance_max + 1; } else { - melee_distance = melee_distance_max * 0.50f; + melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinCasterRangeDistance); } + } - break; + /* Archer Checks*/ + if (IsBotRanged()) { + float archeryRange = GetBotRangedValue(); + float casterRange = GetBotCasterRange(); + float minArcheryRange = RuleI(Combat, MinRangedAttackDist); + melee_distance = std::min(archeryRange, (casterRange * 2)); + melee_distance_min = std::max(std::max(minArcheryRange, (melee_distance_max + 1)), std::min(casterRange, archeryRange)); } } @@ -2685,10 +2846,11 @@ bool Bot::IsValidTarget( bool invalid_target_state = false; if (HOLDING || !tar->IsNPC() || - tar->IsMezzed() || + (tar->IsMezzed() && !HasBotAttackFlag(tar)) || + (!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) || lo_distance > leash_distance || tar_distance > leash_distance || - (!GetAttackingFlag() && !CheckLosFN(tar) && !leash_owner->CheckLosFN(tar)) || + (!GetAttackingFlag() && !CheckLosCheat(this, tar) && !CheckLosCheat(leash_owner, tar)) || !IsAttackAllowed(tar) ) { invalid_target_state = true; @@ -2753,9 +2915,7 @@ Mob* Bot::GetBotTarget(Client* bot_owner) } } - if (GetArchetype() == Archetype::Caster) { - BotMeditate(true); - } + BotMeditate(IsSitting()); } return t; @@ -2915,13 +3075,11 @@ bool Bot::CheckIfIncapacitated() { void Bot::SetBerserkState() {// Berserk updates should occur if primary AI criteria are met if (GetClass() == Class::Warrior || GetClass() == Class::Berserker) { - - if (!berserk && GetHP() > 0 && GetHPRatio() < 30.0f) { + if (!berserk && GetHPRatio() < RuleI(Combat, BerserkerFrenzyStart)) { entity_list.MessageCloseString(this, false, 200, 0, BERSERK_START, GetName()); berserk = true; } - - if (berserk && GetHPRatio() >= 30.0f) { + if (berserk && GetHPRatio() > RuleI(Combat, BerserkerFrenzyEnd)) { entity_list.MessageCloseString(this, false, 200, 0, BERSERK_END, GetName()); berserk = false; } @@ -2969,10 +3127,9 @@ void Bot::SetOwnerTarget(Client* bot_owner) { bot_owner->SetBotPulling(false); if (NOT_HOLDING && NOT_PASSIVE) { - auto attack_target = bot_owner->GetTarget(); - if (attack_target && HasBotAttackFlag(attack_target)) { + if (attack_target && HasBotAttackFlag(attack_target)) { InterruptSpell(); WipeHateList(); AddToHateList(attack_target, 1); @@ -2999,7 +3156,7 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { auto pull_target = bot_owner->GetTarget(); if (pull_target) { if (raid) { - const auto msg = fmt::format("Pulling {} to the group..", pull_target->GetCleanName()); + const auto msg = fmt::format("Pulling {}.", pull_target->GetCleanName()); raid->RaidSay(msg.c_str(), GetCleanName(), 0, 100); } else { BotGroupSay( @@ -3020,8 +3177,9 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { GetPet()->WipeHateList(); - GetPet()->SetTarget(nullptr); + GetPet()->SetTarget(nullptr); m_previous_pet_order = GetPet()->GetPetOrder(); + GetPet()->CastToNPC()->SaveGuardSpot(GetPosition()); GetPet()->SetPetOrder(SPO_Guard); } } @@ -3108,6 +3266,22 @@ bool Bot::Spawn(Client* botCharacterOwner) { SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); ping_timer.Start(8000); auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); + + pDontHealMeBefore = 0; + pDontGroupHealMeBefore = 0; + pDontGroupHoTHealMeBefore = 0; + pDontBuffMeBefore = Timer::GetCurrentTime() + 400; + pDontDotMeBefore = 0; + pDontRootMeBefore = 0; + pDontSnareMeBefore = 0; + pDontCureMeBefore = 0; + pDontRegularHealMeBefore = 0; + pDontVeryFastHealMeBefore = 0; + pDontFastHealMeBefore = 0; + pDontCompleteHealMeBefore = 0; + pDontGroupCompleteHealMeBefore = 0; + pDontHotHealMeBefore = 0; + // there is something askew with spawn struct appearance fields... // I re-enabled this until I can sort it out const auto& m = GetBotItemSlots(); @@ -3144,6 +3318,11 @@ bool Bot::Spawn(Client* botCharacterOwner) { } } + if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) { + OwnerMessage("Running SpellType checks. There may be some spells that are flagged as incorrect but actually are correct. Use this as a guideline."); + CheckBotSpells(); //This runs through a serious of checks and outputs any spells that are set to the wrong spell type in the database + } + return true; } @@ -3427,7 +3606,6 @@ void Bot::LevelBotWithClient(Client* c, uint8 new_level, bool send_appearance) { parse->EventBot(EVENT_LEVEL_UP, e, nullptr, std::to_string(levels_change), 0); } - e->SetPetChooser(false); // not sure what this does, but was in bot 'update' code e->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); if (send_appearance) { @@ -3824,23 +4002,37 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* return; } - if (trade_instance->IsStackable() && trade_instance->GetCharges() < trade_instance->GetItem()->StackSize) { // temp until partial stacks are implemented - if (trade_event_exists) { - event_trade.push_back(ClientTrade(trade_instance, trade_index)); - continue; - } - else { - client->Message( - Chat::Yellow, - fmt::format( - "{} is only a partially stacked item, the trade has been cancelled!", - item_link - ).c_str() - ); - client->ResetTrade(); - return; + if (RuleI(Bots, StackSizeMin) != -1) { + if (trade_instance->IsStackable() && trade_instance->GetCharges() < RuleI(Bots, StackSizeMin)) { // temp until partial stacks are implemented + if (trade_event_exists) { + event_trade.push_back(ClientTrade(trade_instance, trade_index)); + continue; + } + else { + client->Message( + Chat::Yellow, + fmt::format( + "{} is too small of a stack, you need atleast {}, the trade has been cancelled!", + item_link, + RuleI(Bots, StackSizeMin) + ).c_str() + ); + client->ResetTrade(); + return; + } } } + else if (trade_instance->IsStackable() && trade_instance->GetCharges() < trade_instance->GetItem()->StackSize) { + client->Message( + Chat::Yellow, + fmt::format( + "{} is only a partially stacked item, the trade has been cancelled!", + item_link + ).c_str() + ); + client->ResetTrade(); + return; + } for (int m = EQ::invaug::SOCKET_BEGIN; m <= EQ::invaug::SOCKET_END; ++m) { const auto augment = trade_instance->GetAugment(m); @@ -4765,8 +4957,9 @@ void Bot::RogueAssassinate(Mob* other) { } void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { - if (!target || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) + if (!target || GetAppearance() == eaDead || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) { return; + } bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); @@ -4834,58 +5027,58 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if (ma_time) { switch (GetClass()) { - case Class::Monk: { - int reuse = (MonkSpecialAttack(target, EQ::skills::SkillTigerClaw) - 1); - - // Live AA - Technique of Master Wu - int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; - - if (wuchance) { - const int MonkSPA[5] = { - EQ::skills::SkillFlyingKick, - EQ::skills::SkillDragonPunch, - EQ::skills::SkillEagleStrike, - EQ::skills::SkillTigerClaw, - EQ::skills::SkillRoundKick - }; - int extra = 0; - // always 1/4 of the double attack chance, 25% at rank 5 (100/4) - while (wuchance > 0) { - if (zone->random.Roll(wuchance)) { - ++extra; - } - else { - break; + case Class::Monk: { + int reuse = (MonkSpecialAttack(target, EQ::skills::SkillTigerClaw) - 1); + + // Live AA - Technique of Master Wu + int wuchance = itembonuses.DoubleSpecialAttack + spellbonuses.DoubleSpecialAttack + aabonuses.DoubleSpecialAttack; + + if (wuchance) { + const int MonkSPA[5] = { + EQ::skills::SkillFlyingKick, + EQ::skills::SkillDragonPunch, + EQ::skills::SkillEagleStrike, + EQ::skills::SkillTigerClaw, + EQ::skills::SkillRoundKick + }; + int extra = 0; + // always 1/4 of the double attack chance, 25% at rank 5 (100/4) + while (wuchance > 0) { + if (zone->random.Roll(wuchance)) { + ++extra; + } + else { + break; + } + wuchance /= 4; } - wuchance /= 4; - } - Mob* bo = GetBotOwner(); - if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { + Mob* bo = GetBotOwner(); + if (bo && bo->IsClient() && bo->CastToClient()->GetBotOption(Client::booMonkWuMessage)) { - bo->Message( - GENERIC_EMOTE, - "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", - GetCleanName(), - GetCleanName(), - extra - ); - } + bo->Message( + GENERIC_EMOTE, + "The spirit of Master Wu fills %s! %s gains %d additional attack(s).", + GetCleanName(), + GetCleanName(), + extra + ); + } - auto classic = RuleB(Combat, ClassicMasterWu); - while (extra) { - MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQ::skills::SkillTigerClaw)); - --extra; + auto classic = RuleB(Combat, ClassicMasterWu); + while (extra) { + MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQ::skills::SkillTigerClaw)); + --extra; + } } - } - float HasteModifier = (GetHaste() * 0.01f); - monkattack_timer.Start((reuse * 1000) / HasteModifier); + float HasteModifier = (GetHaste() * 0.01f); + monkattack_timer.Start((reuse * 1000) / HasteModifier); - break; - } - default: - break; + break; + } + default: + break; } } @@ -5337,7 +5530,7 @@ bool Bot::CastSpell( ) { bool Result = false; if (zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - // LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", spells[spell_id].name, spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); + // LogSpells("CastSpell called for spell [{}] ([{}]) on entity [{}], slot [{}], time [{}], mana [{}], from item slot [{}]", GetSpellName(spell_id), spell_id, target_id, slot, cast_time, mana_cost, (item_slot==0xFFFFFFFF)?999:item_slot); if (casting_spell_id == spell_id) { ZeroCastingVars(); @@ -5622,12 +5815,10 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe if ( spellTarget && - IsGrouped() && - ( - spellTarget->IsBot() || - spellTarget->IsClient() - ) && - RuleB(Bots, GroupBuffing) + GetClass() != Class::Bard && + (IsGrouped() || (IsRaidGrouped() && GetRaid()->GetGroup(GetCleanName()) != RAID_GROUPLESS)) && + (spellTarget->IsBot() || spellTarget->IsClient()) && + (RuleB(Bots, GroupBuffing) || RuleB(Bots, CrossRaidBuffingAndHealing)) ) { bool noGroupSpell = false; uint16 thespell = spell_id; @@ -5635,9 +5826,9 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe int j = BotGetSpells(i); int spelltype = BotGetSpellType(i); bool spellequal = (j == thespell); - bool spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32)); - bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].target_type == ST_Self)); - bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == Class::Shaman)); + bool spelltypeequal = ((spelltype == BotSpellTypes::RegularHeal) || (spelltype == BotSpellTypes::Escape) || (spelltype == BotSpellTypes::Pet)); + bool spelltypetargetequal = ((spelltype == BotSpellTypes::Buff) && (spells[thespell].target_type == ST_Self)); + bool spelltypeclassequal = ((spelltype == BotSpellTypes::InCombatBuff) && (GetClass() == Class::Shaman)); bool slotequal = (slot == EQ::spells::CastingSlot::Item); if (spellequal || slotequal) { if ((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { @@ -5655,24 +5846,42 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe } if (!noGroupSpell) { - Group *g = GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if ((g->members[i]->GetClass() == Class::Necromancer) && (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune))) { - } - else - SpellOnTarget(thespell, g->members[i]); + std::vector v; + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = GatherSpellTargets(true); + } + else { + v = GatherGroupSpellTargets(); + } - if (g->members[i] && g->members[i]->GetPetID()) - SpellOnTarget(thespell, g->members[i]->GetPet()); + for (Mob* m : v) { + if (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune)) { + for (int i = 0; i < m->GetMaxTotalSlots(); i++) { + uint32 buff_count = m->GetMaxTotalSlots(); + + for (unsigned int j = 0; j < buff_count; j++) { + if (IsValidSpell(m->GetBuffs()[j].spellid)) { + if (IsLichSpell(m->GetBuffs()[j].spellid)) { + continue; + } + } + } } } - SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (g->GroupCount() - 1))); + + SpellOnTarget(thespell, m); + + if (m->GetPetID() && (!RuleB(Bots, RequirePetAffinity) || m->HasPetAffinity())) { + SpellOnTarget(thespell, m->GetPet()); + } + + SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (v.size() - 1))); } } + stopLogic = true; } + return true; } @@ -5691,30 +5900,26 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel SpellOnTarget(spell_id, this); entity_list.AESpell(this, this, spell_id, true); } - else if (raid) - { - std::vector raid_group_members = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); - for (auto iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member) { - SpellOnTarget(spell_id, iter->member); - if (iter->member && iter->member->GetPetID()) - SpellOnTarget(spell_id, iter->member ->GetPet()); - } + else { + if (spellTarget != this) { + SpellOnTarget(spell_id, this); } - } - else - { - Group *g = GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; ++i) { - if (g->members[i]) { - SpellOnTarget(spell_id, g->members[i]); - if (g->members[i] && g->members[i]->GetPetID()) - SpellOnTarget(spell_id, g->members[i]->GetPet()); + + if (spellTarget->IsOfClientBotMerc()) { + const std::vector v = GatherGroupSpellTargets(spellTarget); + for (Mob* m : v) { + SpellOnTarget(spell_id, m); + + if (m->GetPetID() && (!RuleB(Bots, RequirePetAffinity) || m->HasPetAffinity())) { + SpellOnTarget(spell_id, m->GetPet()); } } } + else if (spellTarget->IsPet() && !spellTarget->IsFamiliar() && spellTarget->GetOwner() && (!RuleB(Bots, RequirePetAffinity) || spellTarget->GetOwner()->HasPetAffinity())) { + SpellOnTarget(spell_id, spellTarget); + } } + stopLogic = true; return true; } @@ -5783,9 +5988,9 @@ int32 Bot::GetMaxStat() { int32 Bot::GetMaxResist() { int level = GetLevel(); int32 base = 500; - if (level > 60) - base += ((level - 60) * 5); - + if (level > 65) { + base += ((level - 65) * 5); + } return base; } @@ -6162,24 +6367,52 @@ int64 Bot::CalcManaRegen() { uint8 level = GetLevel(); uint8 botclass = GetClass(); int32 regen = 0; - if (IsSitting()) { - BuffFadeBySitModifier(); - if (botclass != Class::Warrior && botclass != Class::Monk && botclass != Class::Rogue && botclass != Class::Berserker) { - regen = ((((GetSkill(EQ::skills::SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4); - regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen); - } else + + if (GetClass() == Class::Bard) { + if (IsSitting()) { + BuffFadeBySitModifier(); + regen = 2; + regen += (itembonuses.ManaRegen + aabonuses.ManaRegen); + } + else { + regen = 1; + regen += (itembonuses.ManaRegen + aabonuses.ManaRegen); + } + } + else { + if (IsSitting()) { + BuffFadeBySitModifier(); + if (GetArchetype() != Archetype::Melee) { + regen = ((((GetSkill(EQ::skills::SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4); + regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen); + } + else + regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); + } + else regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); - } else - regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); - regen += aabonuses.ManaRegen + itembonuses.heroic_mana_regen; + if (IsHeroicINTCasterClass(GetClass())) { + regen += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25; + } + else if (IsHeroicWISCasterClass(GetClass())) { + regen += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25; + } + else { + regen = 0; + } + + regen += aabonuses.ManaRegen; + regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100); + float mana_regen_rate = RuleR(Bots, ManaRegen); - regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100); - float mana_regen_rate = RuleR(Bots, ManaRegen); - if (mana_regen_rate < 0.0f) - mana_regen_rate = 0.0f; + if (mana_regen_rate < 0.0f) { + mana_regen_rate = 0.0f; + } + + regen = (regen * mana_regen_rate); + } - regen = (regen * mana_regen_rate); return regen; } @@ -6363,29 +6596,52 @@ void Bot::DoEnduranceUpkeep() { void Bot::Camp(bool save_to_database) { - if (IsEngaged() || GetBotOwner()->IsEngaged()) { - GetBotOwner()->Message( - Chat::White, - fmt::format( - "You cannot camp your bots while in combat" - ).c_str() - ); + if (RuleB(Bots, PreventBotCampOnFD) && GetBotOwner()->GetFeigned()) { + GetBotOwner()->Message(Chat::White, "You cannot camp bots while feigned."); return; } - Sit(); - - LeaveHealRotationMemberPool(); + if (RuleB(Bots, PreventBotCampOnEngaged)) { + Raid* raid = entity_list.GetRaidByClient(GetBotOwner()->CastToClient()); + if (raid && raid->IsEngaged()) { + GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); + return; + } - if (save_to_database) { - Save(); - } + auto* owner_group = GetBotOwner()->GetGroup(); + if (owner_group) { + std::list member_list; + owner_group->GetClientList(member_list); + member_list.remove(nullptr); - if (HasGroup() || HasRaid()) { - Zone(); - } else { - Depop(); - } + for (auto member_iter : member_list) { + if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { + GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); + return; + } + } + } + else { + if (GetBotOwner()->CastToClient()->GetAggroCount() > 0) { + GetBotOwner()->Message(Chat::White, "You cannot spawn bots while you are engaged,"); + return; + } + } + } + + Sit(); + + LeaveHealRotationMemberPool(); + + if (save_to_database) { + Save(); + } + + if (HasGroup() || HasRaid()) { + Zone(); + } else { + Depop(); + } } void Bot::Zone() { @@ -6400,10 +6656,10 @@ void Bot::Zone() { Depop(); } -bool Bot::IsArcheryRange(Mob *target) { +bool Bot::IsAtRange(Mob *target) { bool result = false; if (target) { - float range = (GetBotArcheryRange() + 5.0); + float range = (GetBotRangedValue() + 5.0); range *= range; float targetDistance = DistanceSquaredNoZ(m_Position, target->GetPosition()); float minRuleDistance = (RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist)); @@ -6799,349 +7055,64 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { +bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType) { - if ((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, caster->GetClass())) { LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); return false; } - if (!caster || !caster->AI_HasSpells()) { - return false; - } + std::vector v; - if (iChance < 100) { - uint8 tmp = zone->random.Int(1, 100); - if (tmp > iChance) - return false; + if (IsGroupTargetOnlyBotSpellType(spellType)) { + v = caster->GatherGroupSpellTargets(); } - - uint8 botCasterClass = caster->GetClass(); - - if (iSpellTypes == SpellType_Heal) { - if (botCasterClass == Class::Cleric || botCasterClass == Class::Druid || botCasterClass == Class::Shaman) { - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && !iter->member->qglobal) { - if (iter->member->IsClient() && iter->member->GetHPRatio() < 90) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - else if ((iter->member->GetClass() == Class::Warrior || iter->member->GetClass() == Class::Paladin || iter->member->GetClass() == Class::ShadowKnight) && iter->member->GetHPRatio() < 95) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - else if (iter->member->GetClass() == Class::Enchanter && iter->member->GetHPRatio() < 80) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - else if (iter->member->GetHPRatio() < 70) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - - } - - if (iter->member && !iter->member->qglobal && iter->member->HasPet() && iter->member->GetPet()->GetHPRatio() < 50) { - if (iter->member->GetPet()->GetOwner() != caster && caster->IsEngaged() && iter->member->IsCasting() && iter->member->GetClass() != Class::Enchanter) - continue; - - if (caster->AICastSpell(iter->member->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } - - else if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && !g->members[i]->qglobal) { - if (g->members[i]->IsClient() && g->members[i]->GetHPRatio() < 90) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if ((g->members[i]->GetClass() == Class::Warrior || g->members[i]->GetClass() == Class::Paladin || g->members[i]->GetClass() == Class::ShadowKnight) && g->members[i]->GetHPRatio() < 95) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetClass() == Class::Enchanter && g->members[i]->GetHPRatio() < 80) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetHPRatio() < 70) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } - } - - if (g->members[i] && !g->members[i]->qglobal && g->members[i]->HasPet() && g->members[i]->GetPet()->GetHPRatio() < 50) { - if (g->members[i]->GetPet()->GetOwner() != caster && caster->IsEngaged() && g->members[i]->IsCasting() && g->members[i]->GetClass() != Class::Enchanter ) - continue; - - if (caster->AICastSpell(g->members[i]->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } + else { + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = caster->GatherSpellTargets(true); } - - if ((botCasterClass == Class::Paladin || botCasterClass == Class::Beastlord || botCasterClass == Class::Ranger) && (caster->HasGroup() || caster->IsRaidGrouped())) { - float hpRatioToHeal = 25.0f; - switch(caster->GetBotStance()) { - case Stance::Reactive: - case Stance::Balanced: - hpRatioToHeal = 50.0f; - break; - case Stance::Burn: - case Stance::AEBurn: - hpRatioToHeal = 20.0f; - break; - case Stance::Aggressive: - case Stance::Efficient: - default: - hpRatioToHeal = 25.0f; - break; - } - if (caster->IsRaidGrouped()) { - if (auto raid = entity_list.GetRaidByBotName(caster->GetName())) { - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); - iter != raid_group_members.end(); ++iter) { - if (iter->member && !iter->member->qglobal) { - if (iter->member->IsClient() && iter->member->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } else if ( - (iter->member->GetClass() == Class::Warrior || iter->member->GetClass() == Class::Paladin || - iter->member->GetClass() == Class::ShadowKnight) && - iter->member->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } else if (iter->member->GetClass() == Class::Enchanter && - iter->member->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } else if (iter->member->GetHPRatio() < hpRatioToHeal / 2) { - if (caster->AICastSpell(iter->member, 100, SpellType_Heal)) - return true; - } - } - - if (iter->member && !iter->member->qglobal && iter->member->HasPet() && - iter->member->GetPet()->GetHPRatio() < 25) { - if (iter->member->GetPet()->GetOwner() != caster && caster->IsEngaged() && - iter->member->IsCasting() && iter->member->GetClass() != Class::Enchanter) - continue; - - if (caster->AICastSpell(iter->member->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } - } - else if (caster->HasGroup()) { - if (auto g = caster->GetGroup()) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && !g->members[i]->qglobal) { - if (g->members[i]->IsClient() && g->members[i]->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if ((g->members[i]->GetClass() == Class::Warrior || g->members[i]->GetClass() == Class::Paladin || - g->members[i]->GetClass() == Class::ShadowKnight) && - g->members[i]->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetClass() == Class::Enchanter && - g->members[i]->GetHPRatio() < hpRatioToHeal) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } else if (g->members[i]->GetHPRatio() < hpRatioToHeal / 2) { - if (caster->AICastSpell(g->members[i], 100, SpellType_Heal)) - return true; - } - } - - if (g->members[i] && !g->members[i]->qglobal && g->members[i]->HasPet() && - g->members[i]->GetPet()->GetHPRatio() < 25) { - if (g->members[i]->GetPet()->GetOwner() != caster && caster->IsEngaged() && - g->members[i]->IsCasting() && g->members[i]->GetClass() != Class::Enchanter) - continue; - - if (caster->AICastSpell(g->members[i]->GetPet(), 100, SpellType_Heal)) - return true; - } - } - } - } + else { + v = caster->GatherGroupSpellTargets(); } + v = caster->GatherSpellTargets(); } - if (iSpellTypes == SpellType_Buff) { - uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(SpellType_Buff) : 100; - if (botCasterClass == Class::Bard) { - if (caster->AICastSpell(caster, chanceToCast, SpellType_Buff)) { - return true; - } else - return false; - } + Mob* tar = nullptr; - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 g = raid->GetGroup(caster->GetName()); - if (g < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(g); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member) { - if (caster->AICastSpell(iter->member, chanceToCast, SpellType_Buff) || caster->AICastSpell(iter->member->GetPet(), chanceToCast, SpellType_Buff)) - return true; - } - } - } - } - if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if (caster->AICastSpell(g->members[i], chanceToCast, SpellType_Buff) || caster->AICastSpell(g->members[i]->GetPet(), chanceToCast, SpellType_Buff)) - return true; - } - } - } + for (Mob* m : v) { + tar = m; + + if (!tar) { + continue; } - } - if (iSpellTypes == SpellType_Cure) { - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && caster->GetNeedsCured(iter->member)) { - if (caster->AICastSpell(iter->member, caster->GetChanceToCastBySpellType(SpellType_Cure), SpellType_Cure)) - return true; - else if (botCasterClass == Class::Bard) { - return false; - } - } + uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(spellType) : 100; - if (iter->member && iter->member->GetPet() && caster->GetNeedsCured(iter->member->GetPet())) { - if (caster->AICastSpell(iter->member->GetPet(), (int)caster->GetChanceToCastBySpellType(SpellType_Cure) / 4, SpellType_Cure)) - return true; - } - } - } + if (!caster->PrecastChecks(tar, spellType)) { + continue; } - else if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && caster->GetNeedsCured(g->members[i])) { - if (caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_Cure), SpellType_Cure)) - return true; - else if (botCasterClass == Class::Bard) - return false; - } - if (g->members[i] && g->members[i]->GetPet() && caster->GetNeedsCured(g->members[i]->GetPet())) { - if (caster->AICastSpell(g->members[i]->GetPet(), (int)caster->GetChanceToCastBySpellType(SpellType_Cure)/4, SpellType_Cure)) - return true; - } - } - } + if (caster->AICastSpell(tar, chanceToCast, spellType)) { + return true; } - } - if (iSpellTypes == SpellType_HateRedux) { - if (!caster->IsEngaged()) - return false; + if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { + tar = m->GetPet(); - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 gid = raid->GetGroup(caster->GetName()); - if (gid < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(gid); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && caster->GetNeedsHateRedux(iter->member)) { - if (caster->AICastSpell(iter->member, caster->GetChanceToCastBySpellType(SpellType_HateRedux), SpellType_HateRedux)) - return true; - } - } - } - } - else if (caster->HasGroup()) { - Group *g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i] && caster->GetNeedsHateRedux(g->members[i])) { - if (caster->AICastSpell(g->members[i], caster->GetChanceToCastBySpellType(SpellType_HateRedux), SpellType_HateRedux)) - return true; - } - } + if (!tar) { + continue; } - } - } - if (iSpellTypes == SpellType_PreCombatBuff) { - if (botCasterClass == Class::Bard || caster->IsEngaged()) - return false; - - //added raid check - if (caster->IsRaidGrouped()) { - Raid* raid = entity_list.GetRaidByBotName(caster->GetName()); - uint32 g = raid->GetGroup(caster->GetName()); - if (g < MAX_RAID_GROUPS) { - std::vector raid_group_members = raid->GetRaidGroupMembers(g); - for (std::vector::iterator iter = raid_group_members.begin(); iter != raid_group_members.end(); ++iter) { - if (iter->member && - (caster->AICastSpell(iter->member, iChance, SpellType_PreCombatBuff) || - caster->AICastSpell(iter->member->GetPet(), iChance, SpellType_PreCombatBuff)) - ) { - return true; - } - } - } - } else if (caster->HasGroup()) { - const auto g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if (caster->AICastSpell(g->members[i], iChance, SpellType_PreCombatBuff) || caster->AICastSpell(g->members[i]->GetPet(), iChance, SpellType_PreCombatBuff)) - return true; - } - } + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + continue; } - } - } - if (iSpellTypes == SpellType_InCombatBuff) { - if (botCasterClass == Class::Bard) { - if (caster->AICastSpell(caster, iChance, SpellType_InCombatBuff)) { - return true; - } - else { - return false; + if (!caster->PrecastChecks(tar, spellType)) { + continue; } - } - if (caster->HasGroup()) { - Group* g = caster->GetGroup(); - if (g) { - for (int i = 0; i < MAX_GROUP_MEMBERS; i++) { - if (g->members[i]) { - if (caster->AICastSpell(g->members[i], iChance, SpellType_InCombatBuff) || caster->AICastSpell(g->members[i]->GetPet(), iChance, SpellType_InCombatBuff)) { - return true; - } - } - } + if (caster->AICastSpell(tar, chanceToCast, spellType)) { + return true; } } } @@ -7438,7 +7409,7 @@ uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid need_healed++; } - if (includePets && member->GetPet() && member->GetPet()->GetHPRatio() <= hpr) { + if (includePets && member->GetPet() && !member->GetPet()->IsFamiliar() && member->GetPet()->GetHPRatio() <= hpr) { need_healed++; } } @@ -7568,69 +7539,74 @@ int Bot::GroupLeadershipAAOffenseEnhancement() { bool Bot::GetNeedsCured(Mob *tar) { bool needCured = false; + if (tar) { if (tar->FindType(SE_PoisonCounter) || tar->FindType(SE_DiseaseCounter) || tar->FindType(SE_CurseCounter) || tar->FindType(SE_CorruptionCounter)) { uint32 buff_count = tar->GetMaxTotalSlots(); - int buffsWithCounters = 0; - needCured = true; + for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { if (CalculateCounters(tar->GetBuffs()[j].spellid) > 0) { - buffsWithCounters++; - if (buffsWithCounters == 1 && (tar->GetBuffs()[j].ticsremaining < 2 || (int32)((tar->GetBuffs()[j].ticsremaining * 6) / tar->GetBuffs()[j].counters) < 2)) { - needCured = false; - break; + if (tar->GetBuffs()[j].ticsremaining < 1) { + continue; } + + needCured = true; } } } } } + return needCured; } bool Bot::GetNeedsHateRedux(Mob *tar) { - // This really should be a scalar function based in class Mob that returns 'this' state..but, is inline with current Bot coding... - // TODO: Good starting point..but, can be refined.. - // TODO: Still awaiting bot spell rework.. - if (!tar || !tar->IsEngaged() || !tar->HasTargetReflection() || !tar->GetTarget()->IsNPC()) + if (!tar || !tar->IsEngaged() || !tar->HasTargetReflection() || !tar->GetTarget()->IsNPC() || (tar->IsBot() && tar->CastToBot()->IsTaunting())) { return false; + } if (tar->IsBot()) { - switch (tar->GetClass()) { - case Class::Rogue: - if (tar->CanFacestab() || tar->CastToBot()->m_evade_timer.Check(false)) - return false; - case Class::Cleric: - case Class::Druid: - case Class::Shaman: - case Class::Necromancer: - case Class::Wizard: - case Class::Magician: - case Class::Enchanter: + if (tar->GetHPRatio() > GetUltimateSpellMinThreshold(BotSpellTypes::HateRedux, tar) && tar->GetHPRatio() < GetUltimateSpellMaxThreshold(BotSpellTypes::HateRedux, tar)) { return true; - default: - return false; } } return false; } -bool Bot::HasOrMayGetAggro() { +bool Bot::HasOrMayGetAggro(bool SitAggro, uint32 spell_id) { bool mayGetAggro = false; + if (GetTarget() && GetTarget()->GetHateTop()) { - Mob *topHate = GetTarget()->GetHateTop(); - if (topHate == this) + Mob* topHate = GetTarget()->GetHateTop(); + if (topHate == this) { mayGetAggro = true; + } else { uint32 myHateAmt = GetTarget()->GetHateAmount(this); uint32 topHateAmt = GetTarget()->GetHateAmount(topHate); - if (myHateAmt > 0 && topHateAmt > 0 && (uint8)((myHateAmt / topHateAmt) * 100) > 90) + if (SitAggro && !spell_id) { + myHateAmt *= (1 + (RuleI(Aggro, SittingAggroMod) / 100)); + } + + if (spell_id && IsValidSpell(spell_id) && GetTarget()) { + myHateAmt += CheckAggroAmount(spell_id, GetTarget()); + } + + if ( + topHateAmt < 1 || + ( + myHateAmt > 0 && + (uint8)((myHateAmt / topHateAmt) * 100) > RuleI(Bots, HasOrMayGetAggroThreshold) + ) + ) { mayGetAggro = true; + } } } + return mayGetAggro; } @@ -7990,51 +7966,24 @@ bool Bot::CheckDataBucket(std::string bucket_name, const std::string& bucket_val int Bot::GetExpansionBitmask() { - if (m_expansion_bitmask >= 0) { - return m_expansion_bitmask; + if (_expansionBitmask >= 0) { + return _expansionBitmask; } return RuleI(Bots, BotExpansionSettings); } -void Bot::SetExpansionBitmask(int expansion_bitmask, bool save) +void Bot::SetExpansionBitmask(int expansionBitmask) { - m_expansion_bitmask = expansion_bitmask; - - if (save) { - if (!database.botdb.SaveExpansionBitmask(GetBotID(), expansion_bitmask)) { - if (GetBotOwner() && GetBotOwner()->IsClient()) { - GetBotOwner()->CastToClient()->Message( - Chat::White, - fmt::format( - "Failed to save expansion bitmask for {}.", - GetCleanName() - ).c_str() - ); - } - } - } + _expansionBitmask = expansionBitmask; LoadAAs(); } -void Bot::SetBotEnforceSpellSetting(bool enforce_spell_settings, bool save) +void Bot::SetBotEnforceSpellSetting(bool enforceSpellSettings) { - m_enforce_spell_settings = enforce_spell_settings; + _enforceSpellSettings = enforceSpellSettings; - if (save) { - if (!database.botdb.SaveEnforceSpellSetting(GetBotID(), enforce_spell_settings)) { - if (GetBotOwner() && GetBotOwner()->IsClient()) { - GetBotOwner()->CastToClient()->Message( - Chat::White, - fmt::format( - "Failed to save enforce spell settings for {}.", - GetCleanName() - ).c_str() - ); - } - } - } LoadBotSpellSettings(); AI_AddBotSpells(GetBotSpellID()); } @@ -8284,24 +8233,6 @@ std::string Bot::GetHPString(int8 min_hp, int8 max_hp) return hp_string; } -void Bot::SetBotArcherySetting(bool bot_archer_setting, bool save) -{ - m_bot_archery_setting = bot_archer_setting; - if (save) { - if (!database.botdb.SaveBotArcherSetting(GetBotID(), bot_archer_setting)) { - if (GetBotOwner() && GetBotOwner()->IsClient()) { - GetBotOwner()->CastToClient()->Message( - Chat::White, - fmt::format( - "Failed to save archery settings for {}.", - GetCleanName() - ).c_str() - ); - } - } - } -} - std::vector Bot::GetApplySpellList( ApplySpellType apply_type, bool allow_pets, @@ -8479,16 +8410,20 @@ float Bot::GetBotCasterMaxRange(float melee_distance_max) {// Calculate caster d float caster_distance_min = 0.0f; float caster_distance = 0.0f; - caster_distance_max = GetBotCasterRange() * GetBotCasterRange(); + caster_distance_max = GetBotCasterRange(); + if (!GetBotCasterRange() && GetLevel() >= GetStopMeleeLevel() && GetClass() >= Class::Warrior && GetClass() <= Class::Berserker) { - caster_distance_max = MAX_CASTER_DISTANCE[GetClass() - 1]; + caster_distance_max = GetDefaultBotBaseSetting(BotBaseSettings::CasterRange); } + if (caster_distance_max) { caster_distance_min = melee_distance_max; + if (caster_distance_max <= caster_distance_min) { caster_distance_max = caster_distance_min * 1.25f; } } + return caster_distance_max; } @@ -8500,33 +8435,36 @@ int32 Bot::CalcItemATKCap() bool Bot::CheckSpawnConditions(Client* c) { - if (c->GetFeigned()) { - c->Message(Chat::White, "You cannot spawn a bot-group while feigned."); + if (RuleB(Bots, PreventBotSpawnOnFD) && c->GetFeigned()) { + c->Message(Chat::White, "You cannot spawn bots while feigned."); return false; } - Raid* raid = entity_list.GetRaidByClient(c); - if (raid && raid->IsEngaged()) { - c->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); - return false; - } + if (RuleB(Bots, PreventBotSpawnOnEngaged)) { + Raid* raid = entity_list.GetRaidByClient(c); + if (raid && raid->IsEngaged()) { + c->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); + return false; + } - auto* owner_group = c->GetGroup(); - if (owner_group) { - std::list member_list; - owner_group->GetClientList(member_list); - member_list.remove(nullptr); + auto* owner_group = c->GetGroup(); + if (owner_group) { + std::list member_list; + owner_group->GetClientList(member_list); + member_list.remove(nullptr); - for (auto member_iter : member_list) { - if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); - return false; + for (auto member_iter : member_list) { + if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { + c->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); + return false; + } } } - } else { - if (c->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while you are engaged,"); - return false; + else { + if (c->GetAggroCount() > 0) { + c->Message(Chat::White, "You cannot spawn bots while you are engaged,"); + return false; + } } } @@ -8599,12 +8537,12 @@ void Bot::SetSpellRecastTimer(uint16 spell_id, int32 recast_delay) { if (CheckSpellRecastTimer(spell_id)) { BotTimer_Struct t; - t.timer_id = spells[ spell_id ].timer_id; + t.timer_id = spells[spell_id].timer_id; t.timer_value = (Timer::GetCurrentTime() + recast_delay); t.recast_time = recast_delay; t.is_spell = true; t.is_disc = false; - t.spell_id = spells[ spell_id ].id; + t.spell_id = spells[spell_id].id; t.is_item = false; t.item_id = 0; @@ -8617,7 +8555,7 @@ void Bot::SetSpellRecastTimer(uint16 spell_id, int32 recast_delay) { ( ( spells[spell_id].timer_id != 0 && - spells[spell_id].timer_id == bot_timers[ i ].timer_id + spells[spell_id].timer_id == bot_timers[i].timer_id ) || bot_timers[i].spell_id == spell_id ) @@ -8705,12 +8643,12 @@ void Bot::SetDisciplineReuseTimer(uint16 spell_id, int32 reuse_timer) if (CheckDisciplineReuseTimer(spell_id)) { BotTimer_Struct t; - t.timer_id = spells[ spell_id ].timer_id; + t.timer_id = spells[spell_id].timer_id; t.timer_value = (Timer::GetCurrentTime() + reuse_timer); t.recast_time = reuse_timer; t.is_spell = false; t.is_disc = true; - t.spell_id = spells[ spell_id ].id; + t.spell_id = spells[spell_id].id; t.is_item = false; t.item_id = 0; @@ -9210,3 +9148,2275 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) } uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 }; + +std::vector Bot::GatherGroupSpellTargets(Mob* target, bool noClients, bool noBots) { + std::vector valid_spell_targets; + + if (IsRaidGrouped()) { + if (auto raid = entity_list.GetRaidByBotName(GetName())) { + std::vector raidGroupMembers; + if (target) { + auto raidGroup = raid->GetGroup(target->GetName()); + + if (raidGroup != RAID_GROUPLESS) { + raidGroupMembers = raid->GetRaidGroupMembers(raidGroup); + } + else { + return valid_spell_targets; + } + } + else { + auto raidGroup = raid->GetGroup(GetName()); + + if (raidGroup != RAID_GROUPLESS) { + raidGroupMembers = raid->GetRaidGroupMembers(raidGroup); + } + else { + return valid_spell_targets; + } + } + + for (const auto& m : raidGroupMembers) { + if ( + m.member && m.group_number != RAID_GROUPLESS && + ( + (m.member->IsClient() && !noClients) || + (m.member->IsBot() && !noBots) + ) + ) { + valid_spell_targets.emplace_back(m.member); + } + } + } + } + else if (IsGrouped()) { + Group* group = GetGroup(); + if (group) { + for (const auto& m : group->members) { + if ( + m && + ( + (m->IsClient() && !noClients) || + (m->IsBot() && !noBots) + ) + ) { + valid_spell_targets.emplace_back(m); + } + } + } + } + else { + valid_spell_targets.emplace_back(this); + } + + return valid_spell_targets; +} + +std::vector Bot::GatherSpellTargets(bool entireRaid, bool noClients, bool noBots, bool noPets) { + std::vector valid_spell_targets; + + if (IsRaidGrouped()) { + if (auto raid = entity_list.GetRaidByBotName(GetName())) { + if (entireRaid) { + for (const auto& m : raid->members) { + if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m.member); + } + } + } + else { + std::vector raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); + + for (const auto& m : raidGroup) { + if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m.member); + } + } + } + } + } + else if (IsGrouped()) { + Group* group = GetGroup(); + if (group) { + for (const auto& m : group->members) { + if (m && ((m->IsClient() && !noClients) || (m->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m); + } + } + } + } + else { + valid_spell_targets.emplace_back(this); + } + + return valid_spell_targets; +} + +bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { + if (!tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + + LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + + if (GetUltimateSpellHold(spellType, tar)) { + LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + if (GetHPRatio() < GetSpellTypeMinHPLimit(spellType) || GetHPRatio() > GetSpellTypeMaxHPLimit(spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + if (!GetUltimateSpellDelayCheck(spellType, tar)) { + LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + + switch (spellType) { //This will skip Threshold Checks during Precast for specific SpellTypes that are checked when acquiring new targets + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + return true; + default: + if (GetHPRatioForSpellType(spellType, tar) < GetUltimateSpellMinThreshold(spellType, tar) || GetHPRatioForSpellType(spellType, tar) > GetUltimateSpellMaxThreshold(spellType, tar)) { + LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + return false; + } + } + + return true; +} + +bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { + if (!tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + + if (doPrechecks) { + if (spells[spellid].target_type == ST_Self && tar != this) { + tar = this; + } + + if (!PrecastChecks(tar, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); //deleteme + return false; + } + } + + LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + + if (!IsValidSpell(spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme + return false; + } + + if (spells[spellid].target_type == ST_Self && tar != this) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!CheckSpellRecastTimer(spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (!BotHasEnoughMana(spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (zone->IsSpellBlocked(spellid, glm::vec3(GetPosition()))) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (!zone->CanLevitate() && IsEffectInSpell(spellid, SE_Levitate)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (spells[spellid].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (spells[spellid].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (spells[spellid].zone_type == 1 && !zone->CanCastOutdoor()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spellid)); //deleteme + return false; + } + + if (!AECheck && !IsValidSpellRange(spellid, tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), tar->GetBodyType())) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsImmuneToBotSpell(spellid, this)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!DoResistCheckBySpellType(tar, spellid, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell() && !taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if ( + (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spellid) != 0)) + && + tar->CanBuffStack(spellid, GetLevel(), true) < 0 + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!CanCastSpellType(spellType, spellid, tar)) { + return false; + } + + return true; +} + +bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { + if (!spellid || !tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spellid ? GetSpellName(spellid) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme + return false; + } + + uint8 botClass = GetClass(); + //uint8 botLevel = GetLevel(); + + switch (spellType) { + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + if ( + !( + spells[spellid].target_type == ST_Target || + spells[spellid].target_type == ST_Pet || + (tar == this && spells[spellid].target_type != ST_TargetsTarget) || + spells[spellid].target_type == ST_Group || + spells[spellid].target_type == ST_GroupTeleport //|| + //(botClass == Class::Bard && spells[spellid].target_type == ST_AEBard) //TODO bot rewrite - is this needed? + ) + ) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsBlockedBuff(spellid)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (IsEffectInSpell(spellid, SE_Teleport) || IsEffectInSpell(spellid, SE_Succor)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spellid, SE_Illusion)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (spells[spellid].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if ((IsGroupSpell(spellid) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell()) { + switch (tar->GetArchetype()) { + case Archetype::Caster: + if ( + tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && + ( + IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || + (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) + ) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Melee: + if ( + IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || + IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || + IsEffectInSpell(spellid, SE_CurrentMana) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Hybrid: //Hybrids get all buffs + default: + break; + } + } + + // Differences for each type + if (spellType != BotSpellTypes::InCombatBuff) { + if (IsEffectInSpell(spellid, SE_AbsorbMagicAtt) || IsEffectInSpell(spellid, SE_Rune)) { + for (int i = 0; i < tar->GetMaxTotalSlots(); i++) { + uint32 buff_count = tar->GetMaxTotalSlots(); + + for (unsigned int j = 0; j < buff_count; j++) { + if (IsValidSpell(tar->GetBuffs()[j].spellid)) { + if (IsLichSpell(tar->GetBuffs()[j].spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + } + } + } + } + } + + break; + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + //switch (spells[spellid].target_type) { //TODO bot rewrite - is this needed? + // case ST_AEBard: + // case ST_AECaster: + // case ST_GroupTeleport: + // case ST_Group: + // case ST_Self: + // break; + // default: + // LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + // return false; + //} + + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + + if (!IsCommandedSpell()) { + switch (tar->GetArchetype()) { + case Archetype::Caster: + if ( + tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && + ( + IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || + (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) + ) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Melee: + if ( + IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || + IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || + IsEffectInSpell(spellid, SE_CurrentMana) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return false; + } + break; + case Archetype::Hybrid: //Hybrids get all buffs + default: + break; + } + } + + break; + default: + break; + } + + LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + return true; +} + +bool Bot::BotHasEnoughMana(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + //int32 manaCost = GetActSpellCost(spell_id, spells[spell_id].mana); + int32 manaCost = spells[spell_id].mana; + + if (GetMana() < manaCost) { + return false; + } + + return true; +} + +bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { + + if (!tar || !spellid) { + return true; + } + + if (IsNPC() && CastToNPC()->GetSwarmOwner()) { + return true; + } + + const std::vector v = GatherSpellTargets(); + for (Mob* m : v) { + if ( + m->IsBot() && + m->IsCasting() && + m->CastToBot()->casting_spell_targetid && + entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(tar->GetID()) && + m->CastingSpellID() == spellid + ) { + + return true; + } + else { + if (IsGroupSpell(spellid)) { + if ( + m->IsBot() && + m->IsCasting() && + m->CastToBot()->casting_spell_targetid && + m->CastingSpellID() == spellid + ) { + + const std::vector v = GatherGroupSpellTargets(); + for (Mob* m : v) { + if (entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(m->GetID())) { + return true; + } + } + } + } + } + } + + return false; +} + +bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { + + if (!tar || spellid == 0) { + return false; + } + + int32 resist_difficulty = -spells[spellid].resist_difficulty; + int32 level_mod = (tar->GetLevel() - GetLevel()) * (tar->GetLevel() - GetLevel()) / 2; + + if (tar->GetLevel() - GetLevel() < 0) { + level_mod = -level_mod; + } + + int32 targetResist = 0; + + switch (GetSpellResistType(spellid)) { + case RESIST_NONE: + return true; + case RESIST_MAGIC: + targetResist = tar->GetMR(); + break; + case RESIST_COLD: + targetResist = tar->GetCR(); + break; + case RESIST_FIRE: + targetResist = tar->GetFR(); + break; + case RESIST_POISON: + targetResist = tar->GetPR(); + break; + case RESIST_DISEASE: + targetResist = tar->GetDR(); + break; + case RESIST_CORRUPTION: + targetResist = tar->GetCorrup(); + break; + default: + return true; + } + //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spellid), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) + if ((targetResist + level_mod - resist_difficulty) > resist_limit) { + return false; + } + + return true; +} + +bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType) { + if (!tar || !IsValidSpell(spellid)) { + return false; + } + + if (GetSpellTypeResistLimit(spellType) == 0) { + return true; + } + + return DoResistCheck(tar, spellid, GetSpellTypeResistLimit(spellType)); +} + +bool Bot::IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType) { + if (!spellid) { + return false; + } + + switch (targetType) { + case ST_Undead: + if (bodyType == BodyType::Undead || bodyType == BodyType::SummonedUndead || bodyType == BodyType::Vampire) { + return true; + } + + break; + case ST_Summoned: + if (bodyType == BodyType::Summoned || bodyType == BodyType::Summoned2 || bodyType == BodyType::Summoned3) { + return true; + } + + break; + case ST_Animal: + if (bodyType == BodyType::Animal) { + return true; + } + + break; + case ST_Plant: + if (bodyType == BodyType::Plant) { + return true; + } + + break; + case ST_Giant: + if (bodyType == BodyType::Giant || bodyType == BodyType::RaidGiant) { + return true; + } + + break; + case ST_Dragon: + if (bodyType == BodyType::Dragon || bodyType == BodyType::VeliousDragon) { + return true; + } + + break; + default: + return true; + } + + return false; +} + +bool Bot::IsMobEngagedByAnyone(Mob* tar) { + if (!tar) { + return false; + } + + const std::vector v = GatherSpellTargets(true); + + for (Mob* m : v) { + if (m->GetTarget() == tar) { + if ( + m->IsBot() && + !m->CastToBot()->GetHoldFlag() && + m->IsEngaged() && + ( + !m->CastToBot()->IsBotNonSpellFighter() || + ( + m->GetLevel() >= m->CastToBot()->GetStopMeleeLevel() && + !m->IsCasting() + ) + ) + ) { + return true; + } + + if (m->IsCasting() && SpellBreaksMez(m->CastingSpellID())) { + return true; + } + + if (m->IsClient() && (m->CastToClient()->AutoAttackEnabled() || m->CastToClient()->AutoFireEnabled())) { + return true; + } + } + } + + return false; +} + +bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid) { + if (npc->GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { + return false; + } + + if (!npc->CastToNPC()->IsOnHatelist(owner)) { + return false; + } + + if (npc->IsMezzed() || HasBotAttackFlag(npc)) { + return false; + } + + if (npc->HasOwner() && npc->GetOwner() && npc->GetOwner()->IsOfClientBotMerc()) { + return false; + } + + if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), npc->GetBodyType())) { + return false; + } + + if (!IsAttackAllowed(GetTarget())) { + return false; + } + + if (!DoLosChecks(this, npc)) { + return false; + } + + if (IsMobEngagedByAnyone(npc)) { + return false; + } + + int buff_count = npc->GetMaxTotalSlots(); + auto npc_buffs = npc->GetBuffs(); + + for (int i = 0; i < buff_count; i++) { + if (IsDetrimentalSpell(npc_buffs[i].spellid) && IsEffectInSpell(npc_buffs[i].spellid, SE_CurrentHP)) { + return false; + } + } + + return true; +} + +void Bot::SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + SetBotBaseSetting(botSetting, settingValue); + break; + case BotSettingCategories::SpellHold: + SetSpellHold(botSetting, settingValue); + break; + case BotSettingCategories::SpellDelay: + SetSpellDelay(botSetting, settingValue); + break; + case BotSettingCategories::SpellMinThreshold: + SetSpellMinThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellMaxThreshold: + SetSpellMaxThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeAggroCheck: + SetSpellTypeAggroCheck(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMinManaPct: + SetSpellTypeMinManaLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMaxManaPct: + SetSpellTypeMaxManaLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMinHPPct: + SetSpellTypeMinHPLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeMaxHPPct: + SetSpellTypeMaxHPLimit(botSetting, settingValue); + break; + case BotSettingCategories::SpellTypeIdlePriority: + SetSpellTypePriority(botSetting, BotPriorityCategories::Idle, settingValue); + break; + case BotSettingCategories::SpellTypeEngagedPriority: + SetSpellTypePriority(botSetting, BotPriorityCategories::Engaged, settingValue); + break; + case BotSettingCategories::SpellTypePursuePriority: + SetSpellTypePriority(botSetting, BotPriorityCategories::Pursue, settingValue); + break; + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + SetSpellTypeAEOrGroupTargetCount(botSetting, settingValue); + break; + } +} + +void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { + switch (botSetting) { + case BotBaseSettings::ExpansionBitmask: + SetExpansionBitmask(settingValue); + break; + case BotBaseSettings::ShowHelm: + SetShowHelm(settingValue); + break; + case BotBaseSettings::FollowDistance: + SetFollowDistance(EQ::Clamp(static_cast(settingValue), static_cast(1), BOT_FOLLOW_DISTANCE_DEFAULT_MAX)); + break; + case BotBaseSettings::StopMeleeLevel: + SetStopMeleeLevel(settingValue); + break; + case BotBaseSettings::EnforceSpellSettings: + SetBotEnforceSpellSetting(settingValue); + break; + case BotBaseSettings::RangedSetting: + SetBotRangedSetting(settingValue); + break; + case BotBaseSettings::PetSetTypeSetting: + SetPetChooserID(settingValue); + break; + case BotBaseSettings::BehindMob: + SetBehindMob(settingValue); + break; + case BotBaseSettings::CasterRange: + SetBotCasterRange(settingValue); + break; + case BotBaseSettings::IllusionBlock: + SetIllusionBlock(settingValue); + break; + case BotBaseSettings::MaxMeleeRange: + SetMaxMeleeRange(settingValue); + break; + case BotBaseSettings::MedInCombat: + SetMedInCombat(settingValue); + break; + case BotBaseSettings::HPWhenToMed: + SetHPWhenToMed(settingValue); + break; + case BotBaseSettings::ManaWhenToMed: + SetManaWhenToMed(settingValue); + break; + default: + break; + } +} + +int Bot::GetBotBaseSetting(uint16 botSetting) { + switch (botSetting) { + case BotBaseSettings::ExpansionBitmask: + //LogBotSettingsDetail("Returning current GetExpansionBitmask of [{}] for [{}]", GetExpansionBitmask(), GetCleanName()); //deleteme + return GetExpansionBitmask(); + case BotBaseSettings::ShowHelm: + //LogBotSettingsDetail("Returning current GetShowHelm of [{}] for [{}]", GetShowHelm(), GetCleanName()); //deleteme + return GetShowHelm(); + case BotBaseSettings::FollowDistance: + //LogBotSettingsDetail("Returning current GetFollowDistance of [{}] for [{}]", GetFollowDistance(), GetCleanName()); //deleteme + return GetFollowDistance(); + case BotBaseSettings::StopMeleeLevel: + //LogBotSettingsDetail("Returning current GetStopMeleeLevel of [{}] for [{}]", GetStopMeleeLevel(), GetCleanName()); //deleteme + return GetStopMeleeLevel(); + case BotBaseSettings::EnforceSpellSettings: + //LogBotSettingsDetail("Returning current GetBotEnforceSpellSetting of [{}] for [{}]", GetBotEnforceSpellSetting(), GetCleanName()); //deleteme + return GetBotEnforceSpellSetting(); + case BotBaseSettings::RangedSetting: + //LogBotSettingsDetail("Returning current IsBotRanged of [{}] for [{}]", IsBotRanged(), GetCleanName()); //deleteme + return IsBotRanged(); + case BotBaseSettings::PetSetTypeSetting: + //LogBotSettingsDetail("Returning current GetPetChooserID of [{}] for [{}]", GetPetChooserID(), GetCleanName()); //deleteme + return GetPetChooserID(); + case BotBaseSettings::BehindMob: + //LogBotSettingsDetail("Returning current GetBehindMob of [{}] for [{}]", GetBehindMob(), GetCleanName()); //deleteme + return GetBehindMob(); + case BotBaseSettings::CasterRange: + //LogBotSettingsDetail("Returning current GetBotCasterRange of [{}] for [{}]", GetBotCasterRange(), GetCleanName()); //deleteme + return GetBotCasterRange(); + case BotBaseSettings::IllusionBlock: + //LogBotSettingsDetail("Returning current GetIllusionBlock of [{}] for [{}]", GetIllusionBlock(), GetCleanName()); //deleteme + return GetIllusionBlock(); + case BotBaseSettings::MaxMeleeRange: + //LogBotSettingsDetail("Returning current MaxMeleeRange of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetMaxMeleeRange(); + case BotBaseSettings::MedInCombat: + //LogBotSettingsDetail("Returning current GetMedInCombate of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetMedInCombat(); + case BotBaseSettings::HPWhenToMed: + //LogBotSettingsDetail("Returning current GetHPWhenToMed of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetHPWhenToMed(); + case BotBaseSettings::ManaWhenToMed: + //LogBotSettingsDetail("Returning current GetManaWhenToMed of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetManaWhenToMed(); + default: + return true; + } + + return true; +} + +int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { + switch (botSetting) { + case BotBaseSettings::ExpansionBitmask: + return RuleI(Bots, BotExpansionSettings); + case BotBaseSettings::ShowHelm: + return true; + case BotBaseSettings::FollowDistance: + return BOT_FOLLOW_DISTANCE_DEFAULT; + case BotBaseSettings::StopMeleeLevel: + if (IsCasterClass(GetClass())) { + return RuleI(Bots, CasterStopMeleeLevel); + } + else { + return 255; + } + case BotBaseSettings::PetSetTypeSetting: + return 0; + case BotBaseSettings::BehindMob: + if (GetClass() == Class::Rogue) { + return true; + } + else { + return false; + } + case BotBaseSettings::CasterRange: + switch (GetClass()) { + case Class::Warrior: + case Class::Monk: + case Class::Rogue: + case Class::Berserker: + return 0; + case Class::Bard: + return 30; + default: + return 90; + } + case BotBaseSettings::MedInCombat: + if (IsCasterClass(GetClass())) { + return true; + } + + return false; + case BotBaseSettings::HPWhenToMed: + case BotBaseSettings::ManaWhenToMed: + return 80; + case BotBaseSettings::EnforceSpellSettings: + case BotBaseSettings::RangedSetting: + case BotBaseSettings::IllusionBlock: + case BotBaseSettings::MaxMeleeRange: + default: + return false; + } + + return true; +} + + +void Bot::LoadDefaultBotSettings() { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i)); //deleteme + } + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + BotSpellSettings_Struct t; + + t.spellType = i; + t.shortName = GetSpellTypeShortNameByID(i); + t.name = GetSpellTypeNameByID(i); + t.hold = GetDefaultSpellHold(i); + t.delay = GetDefaultSpellDelay(i); + t.minThreshold = GetDefaultSpellMinThreshold(i); + t.maxThreshold = GetDefaultSpellMaxThreshold(i); + t.resistLimit = GetDefaultSpellTypeResistLimit(i); + t.aggroCheck = GetDefaultSpellTypeAggroCheck(i); + t.minManaPct = GetDefaultSpellTypeMinManaLimit(i); + t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i); + t.minHPPct = GetDefaultSpellTypeMinHPLimit(i); + t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i); + t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass()); + t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass()); + t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass()); + t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i); + t.recastTimer.Start(); + + _spellSettings.push_back(t); + + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); //deleteme + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); //deleteme + LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i), GetDefaultSpellTypeMinManaLimit(i), GetDefaultSpellTypeMaxManaLimit(i), GetDefaultSpellTypeMinHPLimit(i), GetDefaultSpellTypeMaxHPLimit(i)); //deleteme + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}] | recastTimer = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass()), GetDefaultSpellTypeEngagedPriority(i, GetClass()), GetDefaultSpellTypePursuePriority(i, GetClass()), GetDefaultSpellTypeAEOrGroupTargetCount(i), t.recastTimer.GetRemainingTime()); //deleteme + } +} + +void Bot::SetBotSpellRecastTimer(uint16 spellType, Mob* tar, bool preCast) { + if (!tar) { + return; + } + + if (!preCast && BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + return; + } + + uint32 addedDelay = 0; + + switch (spellType) { //Additional delays + case BotSpellTypes::Mez: + addedDelay = RuleI(Bots, MezSuccessDelay); + break; + case BotSpellTypes::AEMez: + addedDelay = RuleI(Bots, AEMezSuccessDelay); + break; + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + } + else if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + tar->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + } + else { + SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + } +} + +BotSpell Bot::GetSpellByHealType(uint16 spellType, Mob* tar) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return GetBestBotSpellForVeryFastHeal(this, tar, spellType); + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return GetBestBotSpellForFastHeal(this, tar, spellType); + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return GetBestBotSpellForRegularSingleTargetHeal(this, tar, spellType); + case BotSpellTypes::GroupHeals: + return GetBestBotSpellForGroupHeal(this, tar, spellType); + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetCompleteHeals: + return GetBestBotSpellForPercentageHeal(this, tar, spellType); + case BotSpellTypes::GroupCompleteHeals: + return GetBestBotSpellForGroupCompleteHeal(this, tar, spellType); + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return GetBestBotSpellForHealOverTime(this, tar, spellType); + case BotSpellTypes::GroupHoTHeals: + return GetBestBotSpellForGroupHealOverTime(this, tar, spellType); + } +} + +uint16 Bot::GetSpellTypePriority(uint16 spellType, uint8 priorityType) { + switch (priorityType) { + case BotPriorityCategories::Idle: + return _spellSettings[spellType].idlePriority; + case BotPriorityCategories::Engaged: + return _spellSettings[spellType].engagedPriority; + case BotPriorityCategories::Pursue: + return _spellSettings[spellType].pursuePriority; + default: + return 0; + } +} + +int Bot::GetDefaultSetting(uint16 settingCategory, uint16 settingType) { + switch (settingCategory) { + case BotSettingCategories::BaseSetting: + return GetDefaultBotBaseSetting(settingType); + case BotSettingCategories::SpellHold: + return GetDefaultSpellHold(settingType); + case BotSettingCategories::SpellDelay: + return GetDefaultSpellDelay(settingType); + case BotSettingCategories::SpellMinThreshold: + return GetDefaultSpellMinThreshold(settingType); + case BotSettingCategories::SpellMaxThreshold: + return GetDefaultSpellMinThreshold(settingType); + case BotSettingCategories::SpellTypeAggroCheck: + return GetDefaultSpellTypeAggroCheck(settingType); + case BotSettingCategories::SpellTypeMinManaPct: + return GetDefaultSpellTypeMinManaLimit(settingType); + case BotSettingCategories::SpellTypeMaxManaPct: + return GetDefaultSpellTypeMaxManaLimit(settingType); + case BotSettingCategories::SpellTypeMinHPPct: + return GetDefaultSpellTypeMinHPLimit(settingType); + case BotSettingCategories::SpellTypeMaxHPPct: + return GetDefaultSpellTypeMaxHPLimit(settingType); + case BotSettingCategories::SpellTypeIdlePriority: + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Idle, GetClass()); + case BotSettingCategories::SpellTypeEngagedPriority: + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Engaged, GetClass()); + case BotSettingCategories::SpellTypePursuePriority: + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Pursue, GetClass()); + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + return GetDefaultSpellTypeAEOrGroupTargetCount(settingType); + default: + break; + } +} + +int Bot::GetSetting(uint16 settingCategory, uint16 settingType) { + switch (settingCategory) { + case BotSettingCategories::BaseSetting: + return GetBotBaseSetting(settingType); + case BotSettingCategories::SpellHold: + return GetSpellHold(settingType); + case BotSettingCategories::SpellDelay: + return GetSpellDelay(settingType); + case BotSettingCategories::SpellMinThreshold: + return GetSpellMinThreshold(settingType); + case BotSettingCategories::SpellMaxThreshold: + return GetSpellMinThreshold(settingType); + case BotSettingCategories::SpellTypeAggroCheck: + return GetSpellTypeAggroCheck(settingType); + case BotSettingCategories::SpellTypeMinManaPct: + return GetSpellTypeMinManaLimit(settingType); + case BotSettingCategories::SpellTypeMaxManaPct: + return GetSpellTypeMaxManaLimit(settingType); + case BotSettingCategories::SpellTypeMinHPPct: + return GetSpellTypeMinHPLimit(settingType); + case BotSettingCategories::SpellTypeMaxHPPct: + return GetSpellTypeMaxHPLimit(settingType); + case BotSettingCategories::SpellTypeIdlePriority: + return GetSpellTypePriority(settingType, BotPriorityCategories::Idle); + case BotSettingCategories::SpellTypeEngagedPriority: + return GetSpellTypePriority(settingType, BotPriorityCategories::Engaged); + case BotSettingCategories::SpellTypePursuePriority: + return GetSpellTypePriority(settingType, BotPriorityCategories::Pursue); + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + return GetSpellTypeAEOrGroupTargetCount(settingType); + default: + break; + } +} + +uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass) { + switch (priorityType) { + case BotPriorityCategories::Idle: + return GetDefaultSpellTypeIdlePriority(spellType, botClass); + case BotPriorityCategories::Engaged: + return GetDefaultSpellTypeEngagedPriority(spellType, botClass); + case BotPriorityCategories::Pursue: + return GetDefaultSpellTypePursuePriority(spellType, botClass); + default: + return 0; + } +} + +uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { + if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, botClass)) { + return 0; + } + + uint16 priority = 0; + + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + priority = 1; + + break; + case BotSpellTypes::FastHeals: + priority = 2; + + break; + case BotSpellTypes::GroupHeals: + priority = 3; + + break; + case BotSpellTypes::RegularHeal: + priority = 4; + + break; + case BotSpellTypes::GroupCompleteHeals: + priority = 5; + + break; + case BotSpellTypes::CompleteHeal: + priority = 6; + + break; + case BotSpellTypes::GroupHoTHeals: + priority = 7; + + break; + case BotSpellTypes::HoTHeals: + priority = 8; + + break; + case BotSpellTypes::GroupCures: + priority = 9; + + break; + case BotSpellTypes::Cure: + priority = 10; + + break; + case BotSpellTypes::InCombatBuff: // this has a check at the end to decrement everything below if it's not a SK (SK use InCombatBuffs as it is their hate line so it's not use when Idle) + priority = 11; + + break; + case BotSpellTypes::PetVeryFastHeals: + priority = 12; + + break; + case BotSpellTypes::PetFastHeals: + priority = 13; + + break; + case BotSpellTypes::PetRegularHeals: + priority = 14; + + break; + case BotSpellTypes::PetCompleteHeals: + priority = 15; + + break; + case BotSpellTypes::PetHoTHeals: + priority = 16; + + break; + case BotSpellTypes::Pet: + priority = 17; + + break; + case BotSpellTypes::Buff: + priority = 18; + + break; + case BotSpellTypes::OutOfCombatBuffSong: + priority = 19; + + break; + case BotSpellTypes::ResistBuffs: + priority = 20; + + break; + case BotSpellTypes::DamageShields: + priority = 21; + + break; + case BotSpellTypes::PetBuffs: + priority = 22; + + break; + case BotSpellTypes::PreCombatBuff: + priority = 23; + + break; + case BotSpellTypes::PreCombatBuffSong: + priority = 24; + + break; + default: + priority = 0; //unused + + break; + } + + if ( + priority >= 11 && + botClass && botClass == Class::ShadowKnight && + spellType != BotSpellTypes::InCombatBuff + ) { + --priority; + } + + return priority; +} + +uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass) { + switch (spellType) { + case BotSpellTypes::Escape: + return 1; + case BotSpellTypes::VeryFastHeals: + return 2; + case BotSpellTypes::FastHeals: + return 3; + case BotSpellTypes::GroupHeals: + return 4; + case BotSpellTypes::RegularHeal: + return 5; + case BotSpellTypes::GroupCompleteHeals: + return 6; + case BotSpellTypes::CompleteHeal: + return 7; + case BotSpellTypes::GroupHoTHeals: + return 8; + case BotSpellTypes::HoTHeals: + return 9; + case BotSpellTypes::GroupCures: + return 10; + case BotSpellTypes::Cure: + return 11; + case BotSpellTypes::PetVeryFastHeals: + return 12; + case BotSpellTypes::PetFastHeals: + return 13; + case BotSpellTypes::PetRegularHeals: + return 14; + case BotSpellTypes::PetCompleteHeals: + return 15; + case BotSpellTypes::PetHoTHeals: + return 16; + case BotSpellTypes::AELifetap: + return 17; + case BotSpellTypes::Lifetap: + return 18; + case BotSpellTypes::HateRedux: + return 19; + case BotSpellTypes::AEMez: + return 20; + case BotSpellTypes::Mez: + return 21; + case BotSpellTypes::AEDispel: + return 22; + case BotSpellTypes::Dispel: + return 23; + case BotSpellTypes::AEDebuff: + return 24; + case BotSpellTypes::Debuff: + return 25; + case BotSpellTypes::AESnare: + return 26; + case BotSpellTypes::Snare: + return 27; + case BotSpellTypes::AEFear: + return 28; + case BotSpellTypes::Fear: + return 29; + case BotSpellTypes::AESlow: + return 30; + case BotSpellTypes::Slow: + return 31; + case BotSpellTypes::AERoot: + return 32; + case BotSpellTypes::Root: + return 33; + case BotSpellTypes::AEDoT: + return 34; + case BotSpellTypes::DOT: + return 35; + case BotSpellTypes::AEStun: + return 36; + case BotSpellTypes::PBAENuke: + return 37; + case BotSpellTypes::AENukes: + return 38; + case BotSpellTypes::AERains: + return 39; + case BotSpellTypes::Stun: + return 40; + case BotSpellTypes::Nuke: + return 41; + case BotSpellTypes::InCombatBuff: + return 42; + case BotSpellTypes::InCombatBuffSong: + return 43; + case BotSpellTypes::Pet: + return 44; + default: + return 0; + } +} + +uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass) { + switch (spellType) { + case BotSpellTypes::Escape: + return 1; + case BotSpellTypes::VeryFastHeals: + return 2; + case BotSpellTypes::FastHeals: + return 3; + case BotSpellTypes::GroupHeals: + return 4; + case BotSpellTypes::RegularHeal: + return 5; + case BotSpellTypes::GroupCompleteHeals: + return 6; + case BotSpellTypes::CompleteHeal: + return 7; + case BotSpellTypes::GroupHoTHeals: + return 8; + case BotSpellTypes::HoTHeals: + return 9; + case BotSpellTypes::GroupCures: + return 10; + case BotSpellTypes::Cure: + return 11; + case BotSpellTypes::Snare: + return 12; + case BotSpellTypes::Lifetap: + return 13; + case BotSpellTypes::Dispel: + return 14; + case BotSpellTypes::Stun: + return 15; + case BotSpellTypes::Nuke: + return 16; + case BotSpellTypes::DOT: + return 17; + case BotSpellTypes::PetVeryFastHeals: + return 18; + case BotSpellTypes::PetFastHeals: + return 19; + case BotSpellTypes::PetRegularHeals: + return 20; + case BotSpellTypes::PetCompleteHeals: + return 21; + case BotSpellTypes::PetHoTHeals: + return 22; + default: + return 0; + } +} + +uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType) { + + if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + return RuleI(Bots, SpellResistLimit); + } + else { + return 0; + } +} + +bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + return true; + default: + return false; + } +} + +uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spellType) { + return 0; +} + +uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::InCombatBuff: + if (GetClass() == Class::Shaman) { + return 75; + } + + break; + default: + break; + } + + return 100; +} + +uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::InCombatBuff: + if (GetClass() == Class::Shaman) { + return 40; + } + + break; + default: + break; + } + + return 0; +} + +uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spellType) { + return 100; +} + +uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType) { + if (IsAEBotSpellType(spellType)) { + return RuleI(Bots, MinTargetsForAESpell); + } + + if (IsGroupBotSpellType(spellType)) { + return RuleI(Bots, MinTargetsForGroupSpell); + } + + return 0; +} + +void Bot::SetSpellTypePriority(uint16 spellType, uint8 priorityType, uint16 priority) { + switch (priorityType) { + case BotPriorityCategories::Idle: + _spellSettings[spellType].idlePriority = priority; + break; + case BotPriorityCategories::Engaged: + _spellSettings[spellType].engagedPriority = priority; + break; + case BotPriorityCategories::Pursue: + _spellSettings[spellType].pursuePriority = priority; + break; + default: + return; + } +} + +void Bot::SetSpellTypeResistLimit(uint16 spellType, uint16 resistLimit) { + _spellSettings[spellType].resistLimit = resistLimit; +} + +void Bot::SetSpellTypeAggroCheck(uint16 spellType, bool aggroCheck) { + _spellSettings[spellType].aggroCheck = aggroCheck; +} + +void Bot::SetSpellTypeMinManaLimit(uint16 spellType, uint8 manaLimit) { + _spellSettings[spellType].minManaPct = manaLimit; +} + +void Bot::SetSpellTypeMaxManaLimit(uint16 spellType, uint8 manaLimit) { + _spellSettings[spellType].maxManaPct = manaLimit; +} + +void Bot::SetSpellTypeMinHPLimit(uint16 spellType, uint8 hpLimit) { + _spellSettings[spellType].minHPPct = hpLimit; +} + +void Bot::SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit) { + _spellSettings[spellType].maxHPPct = hpLimit; +} + +void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount) { + _spellSettings[spellType].AEOrGroupTargetCount = targetCount; +} + +std::string Bot::GetBotSettingCategoryName(uint8 setting_type) { + switch (setting_type) { + case BotBaseSettings::ExpansionBitmask: + return "ExpansionBitmask"; + case BotBaseSettings::ShowHelm: + return "ShowHelm"; + case BotBaseSettings::FollowDistance: + return "FollowDistance"; + case BotBaseSettings::StopMeleeLevel: + return "StopMeleeLevel"; + case BotBaseSettings::EnforceSpellSettings: + return "EnforceSpellSettings"; + case BotBaseSettings::RangedSetting: + return "RangedSetting"; + case BotBaseSettings::PetSetTypeSetting: + return "PetSetTypeSetting"; + case BotBaseSettings::BehindMob: + return "BehindMob"; + case BotBaseSettings::CasterRange: + return "CasterRange"; + case BotBaseSettings::IllusionBlock: + return "IllusionBlock"; + case BotBaseSettings::MaxMeleeRange: + return "MaxMeleeRange"; + case BotBaseSettings::MedInCombat: + return "MedInCombat"; + case BotBaseSettings::HPWhenToMed: + return "HPWhenToMed"; + case BotBaseSettings::ManaWhenToMed: + return "ManaWhenToMed"; + default: + return "Null"; + } + + return "Null"; +} + +std::string Bot::GetBotSpellCategoryName(uint8 setting_type) { + switch (setting_type) { + case BotSettingCategories::BaseSetting: + return "BaseSetting"; + case BotSettingCategories::SpellHold: + return "SpellHold"; + case BotSettingCategories::SpellDelay: + return "SpellDelay"; + case BotSettingCategories::SpellMinThreshold: + return "SpellMinThreshold"; + case BotSettingCategories::SpellMaxThreshold: + return "SpellMaxThreshold"; + case BotSettingCategories::SpellTypeAggroCheck: + return "SpellTypeAggroCheck"; + case BotSettingCategories::SpellTypeMinManaPct: + return "SpellTypeMinManaPct"; + case BotSettingCategories::SpellTypeMaxManaPct: + return "SpellTypeMaxManaPct"; + case BotSettingCategories::SpellTypeMinHPPct: + return "SpellTypeMinHPPct"; + case BotSettingCategories::SpellTypeMaxHPPct: + return "SpellTypeMaxHPPct"; + case BotSettingCategories::SpellTypeIdlePriority: + return "SpellTypeIdlePriority"; + case BotSettingCategories::SpellTypeEngagedPriority: + return "SpellTypeEngagedPriority"; + case BotSettingCategories::SpellTypePursuePriority: + return "SpellTypePursuePriority"; + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + return "SpellTypeAEOrGroupTargetCount"; + case BotSettingCategories::SpellTypeRecastDelay: + return "SpellTypeRecastDelay"; + default: + return "Null"; + } + + return "Null"; +} + +std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { + std::list castOrder; + std::list tempCastOrder; + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; i++) { + BotSpellTypeOrder typeSettings = { + .spellType = i, + .priority = GetSpellTypePriority(i, priorityType) + }; + + castOrder.emplace_back(typeSettings); + } + + for (auto& currentType : castOrder) { + if (currentType.priority != 0) { + tempCastOrder.emplace_back(currentType); + } + } + + castOrder = tempCastOrder; + + if (castOrder.size() > 1) { + castOrder.sort( + [](BotSpellTypeOrder const& l, BotSpellTypeOrder const& r) { + return l.priority < r.priority; + } + ); + } + + return castOrder; +} + +bool Bot::AttemptAICastSpell(uint16 spellType) { + bool result = false; + + Mob* tar = GetTarget(); + + if (!taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme + return result; + } + + if (BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + if (!PrecastChecks(this, spellType) || !AICastSpell(this, GetChanceToCastBySpellType(spellType), spellType)) { + if (GetClass() == Class::Bard) { + return result; + } + + if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), BotAISpellRange, spellType)) { + return result; + } + } + } + } + else { + if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + return result; + } + } + + result = true; + + return result; +} + +uint16 Bot::GetSpellListSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEStun: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::Stun: + return BotSpellTypes::Nuke; + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return BotSpellTypes::RegularHeal; + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::Buff; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return BotSpellTypes::Mez; + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + return BotSpellTypes::Debuff; + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + return BotSpellTypes::Slow; + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + return BotSpellTypes::Snare; + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + return BotSpellTypes::Fear; + case BotSpellTypes::GroupCures: + case BotSpellTypes::Cure: + return BotSpellTypes::Cure; + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + return BotSpellTypes::Root; + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + return BotSpellTypes::Dispel; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + return BotSpellTypes::DOT; + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + return BotSpellTypes::Lifetap; + case BotSpellTypes::Charm: + case BotSpellTypes::Escape: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::Resurrect: + default: + return spellType; + } + + return spellType; +} + +bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { + if (IsAEBotSpellType(spellType) && !IsAnyAESpell(spellid)) { + return false; + } + + if (IsGroupBotSpellType(spellType) && !IsGroupSpell(spellid)) { + return false; + } + + switch (spellType) { //TODO bot rewrite - fix Buff/ResistBuff + case BotSpellTypes::Buff: + if (IsEffectInSpell(spellid, SE_DamageShield)) { + return false; + } + + if ( + IsEffectInSpell(spellid, SE_ResistMagic) || + IsEffectInSpell(spellid, SE_ResistFire) || + IsEffectInSpell(spellid, SE_ResistCold) || + IsEffectInSpell(spellid, SE_ResistPoison) || + IsEffectInSpell(spellid, SE_ResistDisease) || + IsEffectInSpell(spellid, SE_ResistCorruption) || + IsEffectInSpell(spellid, SE_ResistAll) + ) { + return false; + } + + return true; + case BotSpellTypes::ResistBuffs: + if ( + IsEffectInSpell(spellid, SE_ResistMagic) || + IsEffectInSpell(spellid, SE_ResistFire) || + IsEffectInSpell(spellid, SE_ResistCold) || + IsEffectInSpell(spellid, SE_ResistPoison) || + IsEffectInSpell(spellid, SE_ResistDisease) || + IsEffectInSpell(spellid, SE_ResistCorruption) || + IsEffectInSpell(spellid, SE_ResistAll) + ) { + return true; + } + + return false; + case BotSpellTypes::DamageShields: + if (IsEffectInSpell(spellid, SE_DamageShield)) { + return true; + } + + return false; + case BotSpellTypes::PBAENuke: + if (IsPBAENukeSpell(spellid) && !IsStunSpell(spellid)) { + return true; + } + + return false; + case BotSpellTypes::AERains: + if (IsAERainNukeSpell(spellid) && !IsStunSpell(spellid)) { + return true; + } + return false; + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + if (IsStunSpell(spellid)) { + return true; + } + + return false; + case BotSpellTypes::AENukes: + case BotSpellTypes::Nuke: + if (!IsStunSpell(spellid)) { + return true; + } + + return false; + default: + return true; + } + + return true; +} + +void Bot::SetCastedSpellType(uint16 spellType) { + _castedSpellType = spellType; +} + +void Bot::DoFaceCheckWithJitter(Mob* tar) { + if (!tar) { + return; + } + + if (IsMoving()) { + return; + } + + SetCombatJitter(); + if (!IsFacingMob(tar)) { + FaceTarget(tar); + return; + } + return; +} + +void Bot::DoFaceCheckNoJitter(Mob* tar) { + if (!tar) { + return; + } + + if (IsMoving()) { + return; + } + + if (!IsFacingMob(tar)) { + FaceTarget(tar); + return; + } + return; +} + +void Bot::RunToGoalWithJitter(glm::vec3 Goal) { + RunTo(Goal.x, Goal.y, Goal.z); + SetCombatJitter(); +} + +void Bot::SetCombatOutOfRangeJitter() { + SetCombatOutOfRangeJitterFlag(); + + if (RuleI(Bots, MaxJitterTimer) > 0) { + m_combat_jitter_timer.Start(zone->random.Int(RuleI(Bots, MinJitterTimer), RuleI(Bots, MaxJitterTimer)), true); + } +} + +void Bot::SetCombatJitter() { + SetCombatJitterFlag(); + + if (RuleI(Bots, MaxJitterTimer) > 0) { + m_combat_jitter_timer.Start(zone->random.Int(RuleI(Bots, MinJitterTimer), RuleI(Bots, MaxJitterTimer)), true); + } +} + +void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob) { + if (HasTargetReflection()) { + if (!tar->IsFeared() && !tar->IsStunned()) { + if (TryEvade(tar)) { + return; + } + } + + if (tar->IsRooted() && !taunting) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay + if (tar_distance <= melee_distance_max) { + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, false, true)) { + RunToGoalWithJitter(Goal); + return; + } + } + } + + if (taunting && tar_distance < melee_distance_min) { // Back up any taunting bots that are too close + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, taunting)) { + RunToGoalWithJitter(Goal); + return; + } + } + } + else { + if (!tar->IsFeared()) { + if (taunting) { // Taunting adjustments + Mob* mobTar = tar->GetTarget(); + if (!mobTar || mobTar == nullptr) { + DoFaceCheckNoJitter(tar); + return; + } + + if (RuleB(Bots, TauntingBotsFollowTopHate)) { // If enabled, taunting bots will stick to top hate + if ((DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + Goal = mobTar->GetPosition(); + RunToGoalWithJitter(Goal); + return; + } + } + else { // Otherwise, stick to any other bots that are taunting + if (mobTar->IsBot() && mobTar->CastToBot()->taunting && (DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + Goal = mobTar->GetPosition(); + RunToGoalWithJitter(Goal); + return; + } + } + } + else if (tar_distance < melee_distance_min || (GetBehindMob() && !behindMob) || !HasRequiredLoSForPositioning(tar)) { // Regular adjustment + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), taunting)) { + RunToGoalWithJitter(Goal); + return; + } + //else { + // if (stopMeleeLevel || IsBotArcher()) { + // if (IsBotArcher()) { + // float minArcheryRange = RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist); + // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, minArcheryRange, melee_distance, false, taunting)) { + // RunToGoalWithJitter(Goal); + // return; + // } + // } + // else { + // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_max + 1, melee_distance, false, taunting)) { + // RunToGoalWithJitter(Goal); + // return; + // } + // } + // } + // DoFaceCheckWithJitter(tar); + // return; + //} + } + } + } + + DoFaceCheckNoJitter(tar); + return; +} + +bool Bot::CheckDoubleRangedAttack() { + int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; + if (chance && zone->random.Roll(chance)) + return true; + + return false; +} + +bool Bot::RequiresLoSForPositioning() { + if (GetLevel() < GetStopMeleeLevel()) { + return true; + } + else if (GetClass() == Class::Bard) { + return false; + } + else if (GetClass() == Class::Cleric) { //TODO bot rewrite - add check to see if spell requires los + return false; + } + + return true; +} + +bool Bot::HasRequiredLoSForPositioning(Mob* tar) { + if (!tar) { + return true; + } + + if (GetClass() == Class::Cleric) { //add check to see if spell requires los + return true; + } + else if (GetClass() == Class::Bard && GetLevel() >= GetStopMeleeLevel()) { + return true; + } + if (!DoLosChecks(this, tar)) { + return false; + } + + return true; +} + +bool Bot::IsInGroupOrRaid(bool announce) { + if (!GetOwner()) { + return false; + } + + Mob* c = GetOwner(); + + if ( + (!GetRaid() && !GetGroup()) || + (!c->GetRaid() && !c->GetGroup()) + ) { + return false; + } + + if ( + c->GetRaid() && + ( + !GetRaid() || + c->GetRaid() != GetRaid() || + GetRaid()->GetGroup(GetCleanName()) == RAID_GROUPLESS + ) + ) { + return false; + } + + if ( + c->GetGroup() && + ( + !GetGroup() || + c->GetGroup() != GetGroup() + ) + ) { + return false; + } + + + if (announce) { + c->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I am not currently in your group or raid.", + GetCleanName() + ).c_str() + ); + } + + return true; +} + +bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar) { + int spellRange = botCaster->GetActSpellRange(spellid, spells[spellid].range); + int spellAERange = botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range); + int targetCount = 0; + + for (auto& close_mob : botCaster->m_close_mobs) { + Mob* m = close_mob.second; + + if (tar == m) { + continue; + } + + switch (spellType) { + case BotSpellTypes::AEDispel: + if (m->GetSpecialAbility(SpecialAbility::DispellImmunity)) { + continue; + } + + break; + case BotSpellTypes::AEFear: + if (m->GetSpecialAbility(SpecialAbility::FearImmunity)) { + continue; + } + + break; + case BotSpellTypes::AESnare: + if (m->GetSpecialAbility(SpecialAbility::SnareImmunity)) { + continue; + } + + break; + case BotSpellTypes::AESlow: + if (m->GetSpecialAbility(SpecialAbility::SlowImmunity)) { + continue; + } + + break; + default: + break; + } + + if (!m->IsNPC() || !m->CastToNPC()->IsOnHatelist(botCaster->GetOwner())) { + continue; + } + + if (SpellBreaksMez(spellid) && m->IsMezzed()) { + continue; + } + + if (IsPBAESpell(spellid)) { + if ( + spellAERange >= Distance(botCaster->GetPosition(), m->GetPosition()) && + botCaster->CastChecks(spellid, m, spellType, true, true) + ) { + ++targetCount; + } + } + else { + if (!tar || spellRange < Distance(botCaster->GetPosition(), tar->GetPosition()) || !DoLosChecks(this, m)) { + continue; + } + + if ( + spellAERange >= Distance(tar->GetPosition(), m->GetPosition()) && + botCaster->CastChecks(spellid, m, spellType, true, true) + ) { + ++targetCount; + } + } + } + + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + return false; + } + + SetHasLoS(true); + + return true; +} + +void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + to->SetBotBaseSetting(i, GetBotBaseSetting(i)); + } + + break; + case BotSettingCategories::SpellHold: + if (spellType != UINT16_MAX) { + to->SetSpellHold(spellType, GetSpellHold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellHold(i, GetSpellHold(i)); + } + } + + break; + case BotSettingCategories::SpellDelay: + if (spellType != UINT16_MAX) { + to->SetSpellDelay(spellType, GetSpellDelay(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellDelay(i, GetSpellDelay(i)); + } + } + + break; + case BotSettingCategories::SpellMinThreshold: + if (spellType != UINT16_MAX) { + to->SetSpellMinThreshold(spellType, GetSpellMinThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellMinThreshold(i, GetSpellMinThreshold(i)); + } + } + + break; + case BotSettingCategories::SpellMaxThreshold: + if (spellType != UINT16_MAX) { + to->SetSpellMaxThreshold(spellType, GetSpellMaxThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellMaxThreshold(i, GetSpellMaxThreshold(i)); + } + } + + break; + case BotSettingCategories::SpellTypeAggroCheck: + if (spellType != UINT16_MAX) { + to->SetSpellTypeAggroCheck(spellType, GetSpellTypeAggroCheck(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeAggroCheck(i, GetSpellTypeAggroCheck(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMinManaPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMinManaLimit(spellType, GetSpellTypeMinManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMinManaLimit(i, GetSpellTypeMinManaLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMaxManaPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMaxManaLimit(spellType, GetSpellTypeMaxManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMaxManaLimit(i, GetSpellTypeMaxManaLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMinHPPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMinHPLimit(spellType, GetSpellTypeMinHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMinHPLimit(i, GetSpellTypeMinHPLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeMaxHPPct: + if (spellType != UINT16_MAX) { + to->SetSpellTypeMaxHPLimit(spellType, GetSpellTypeMaxHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeMaxHPLimit(i, GetSpellTypeMaxHPLimit(i)); + } + } + + break; + case BotSettingCategories::SpellTypeIdlePriority: + if (spellType != UINT16_MAX) { + to->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, GetSpellTypePriority(spellType, BotPriorityCategories::Idle)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypePriority(i, BotPriorityCategories::Idle, GetSpellTypePriority(i, BotPriorityCategories::Idle)); + } + } + + break; + case BotSettingCategories::SpellTypeEngagedPriority: + if (spellType != UINT16_MAX) { + to->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, GetSpellTypePriority(spellType, BotPriorityCategories::Engaged)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypePriority(i, BotPriorityCategories::Engaged, GetSpellTypePriority(i, BotPriorityCategories::Engaged)); + } + } + + break; + case BotSettingCategories::SpellTypePursuePriority: + if (spellType != UINT16_MAX) { + to->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, GetSpellTypePriority(spellType, BotPriorityCategories::Pursue)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypePriority(i, BotPriorityCategories::Pursue, GetSpellTypePriority(i, BotPriorityCategories::Pursue)); + } + } + + break; + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + if (spellType != UINT16_MAX) { + to->SetSpellTypeAEOrGroupTargetCount(spellType, GetSpellTypeAEOrGroupTargetCount(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeAEOrGroupTargetCount(i, GetSpellTypeAEOrGroupTargetCount(i)); + } + } + + break; + } +} + +void Bot::CopyBotSpellSettings(Bot* to) +{ + to->ResetBotSpellSettings(); + to->bot_spell_settings.clear(); + + auto s = BotSpellSettingsRepository::GetWhere(content_db, fmt::format("bot_id = {}", GetBotID())); + if (s.empty()) { + return; + } + + auto* spell_list = content_db.GetBotSpells(to->GetBotSpellID()); + + for (const auto& e : s) { + BotSpellSetting b; + + b.priority = e.priority; + b.min_hp = e.min_hp; + b.max_hp = e.max_hp; + b.is_enabled = e.is_enabled; + + if (IsSpellInBotList(spell_list, e.spell_id)) { + for (auto& se : spell_list->entries) { + if (se.spellid == e.spell_id) { + if (EQ::ValueWithin(to->GetLevel(), se.minlevel, se.maxlevel) && se.spellid) { + to->AddBotSpellSetting(e.spell_id, &b); + } + } + } + } + } + + to->LoadBotSpellSettings(); + to->AI_AddBotSpells(to->GetBotSpellID()); + to->SetBotEnforceSpellSetting(GetBotEnforceSpellSetting()); +} + +void Bot::ResetBotSpellSettings() +{ + auto s = BotSpellSettingsRepository::GetWhere(content_db, fmt::format("bot_id = {}", GetBotID())); + if (s.empty()) { + return; + } + + for (const auto& e : s) { + DeleteBotSpellSetting(e.spell_id); + } + + LoadBotSpellSettings(); + AI_AddBotSpells(GetBotSpellID()); + SetBotEnforceSpellSetting(false); +} diff --git a/zone/bot.h b/zone/bot.h index 68b027f052..97d987e567 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -42,6 +42,9 @@ constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500; // as DSq value (50 uni constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds +constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 1500; // 1.5 seconds +constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MAX = 3000; // 3 seconds + constexpr uint32 MAG_EPIC_1_0 = 28034; extern WorldServer worldserver; @@ -49,6 +52,13 @@ extern WorldServer worldserver; constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this constexpr int NegativeItemReuse = -1; // Unlinked timer for items +constexpr uint8 SumWater = 1; +constexpr uint8 SumFire = 2; +constexpr uint8 SumAir = 3; +constexpr uint8 SumEarth = 4; +constexpr uint8 MonsterSum = 5; +constexpr uint8 SumMageMultiElement = 6; + // nHSND negative Healer/Slower/Nuker/Doter // pH positive Healer // pS positive Slower @@ -87,6 +97,58 @@ enum BotCastingChanceConditional : uint8 cntHSND = 16 }; +namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed + constexpr uint8 BaseSetting = 0; + constexpr uint8 SpellHold = 1; + constexpr uint8 SpellDelay = 2; + constexpr uint8 SpellMinThreshold = 3; + constexpr uint8 SpellMaxThreshold = 4; + constexpr uint8 SpellTypeAggroCheck = 5; + constexpr uint8 SpellTypeMinManaPct = 6; + constexpr uint8 SpellTypeMaxManaPct = 7; + constexpr uint8 SpellTypeMinHPPct = 8; + constexpr uint8 SpellTypeMaxHPPct = 9; + constexpr uint8 SpellTypeIdlePriority = 10; + constexpr uint8 SpellTypeEngagedPriority = 11; + constexpr uint8 SpellTypePursuePriority = 12; + constexpr uint8 SpellTypeAEOrGroupTargetCount = 13; + constexpr uint8 SpellTypeRecastDelay = 14; + + constexpr uint16 START = BotSettingCategories::BaseSetting; + constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; + constexpr uint16 START_CLIENT = BotSettingCategories::SpellHold; + constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; + constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; // Increment as needed +}; + +namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed + constexpr uint8 Idle = 0; + constexpr uint8 Engaged = 1; + constexpr uint8 Pursue = 2; + + constexpr uint16 START = BotPriorityCategories::Idle; + constexpr uint16 END = BotPriorityCategories::Pursue; // Increment as needed +}; + +namespace BotBaseSettings { + constexpr uint16 ExpansionBitmask = 0; + constexpr uint16 ShowHelm = 1; + constexpr uint16 FollowDistance = 2; + constexpr uint16 StopMeleeLevel = 3; + constexpr uint16 EnforceSpellSettings = 4; + constexpr uint16 RangedSetting = 5; + constexpr uint16 PetSetTypeSetting = 6; + constexpr uint16 BehindMob = 7; + constexpr uint16 CasterRange = 8; + constexpr uint16 IllusionBlock = 9; + constexpr uint16 MaxMeleeRange = 10; + constexpr uint16 MedInCombat = 11; + constexpr uint16 HPWhenToMed = 12; + constexpr uint16 ManaWhenToMed = 13; + + constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players + constexpr uint16 END = BotBaseSettings::ManaWhenToMed; // Increment as needed +}; class Bot : public NPC { friend class Mob; @@ -128,7 +190,7 @@ class Bot : public NPC { // Class Constructors Bot(NPCType *npcTypeData, Client* botOwner); - Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData, int32 expansion_bitmask); + Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData); //abstract virtual override function implementations requird by base abstract class bool Death(Mob* killer_mob, int64 damage, uint16 spell_id, EQ::skills::SkillType attack_skill, KilledByTypes killed_by = KilledByTypes::Killed_NPC, bool is_buff_tic = false) override; @@ -199,8 +261,8 @@ class Bot : public NPC { void Camp(bool save_to_database = true); void SetTarget(Mob* mob) override; void Zone(); - bool IsArcheryRange(Mob* target); - void ChangeBotArcherWeapons(bool isArcher); + bool IsAtRange(Mob* target); + void ChangeBotRangedWeapons(bool isRanged); void Sit(); void Stand(); bool IsSitting() const override; @@ -228,7 +290,7 @@ class Bot : public NPC { uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid); bool GetNeedsCured(Mob *tar); bool GetNeedsHateRedux(Mob *tar); - bool HasOrMayGetAggro(); + bool HasOrMayGetAggro(bool SitAggro, uint32 spell_id = 0); void SetDefaultBotStance(); void SetSurname(std::string_view bot_surname); void SetTitle(std::string_view bot_title); @@ -327,18 +389,20 @@ class Bot : public NPC { void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); // AI Methods - bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes); + bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType); + bool AttemptAICastSpell(uint16 spellType); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; bool AIHealRotation(Mob* tar, bool useFastHeals); bool GetPauseAI() const { return _pauseAI; } void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; } - uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } - void SetStopMeleeLevel(uint8 level); + bool IsCommandedSpell() const { return _commandedSpell; } + void SetCommandedSpell(bool value) { _commandedSpell = value; } + void SetGuardMode(); void SetHoldMode(); - uint32 GetBotCasterRange() const { return m_bot_caster_range; } + bool IsValidSpellRange(uint16 spell_id, Mob const* tar); // Bot AI Methods @@ -377,6 +441,97 @@ class Bot : public NPC { inline bool Attack(Mob* other, int Hand = EQ::invslot::slotPrimary, bool FromRiposte = false, bool IsStrikethrough = false, bool IsFromSpell = false, ExtraAttackOptions *opts = nullptr) override { return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); } + void DoAttackRounds(Mob* target, int hand); + + std::vector GatherGroupSpellTargets(Mob* target = nullptr, bool noClients = false, bool noBots = false); + std::vector GatherSpellTargets(bool entireRaid = false, bool noClients = false, bool noBots = false, bool noPets = false); + + bool PrecastChecks(Mob* tar, uint16 spellType); + bool CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); + bool CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar); + bool BotHasEnoughMana(uint16 spell_id); + bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid); + bool DoResistCheck(Mob* target, uint16 spellid, int32 resist_limit); + bool DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType); + bool IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType); + bool IsMobEngagedByAnyone(Mob* tar); + void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); + void CopySettings(Bot* to, uint8 settingType, uint16 spellType = UINT16_MAX); + void CopyBotSpellSettings(Bot* to); + void ResetBotSpellSettings(); + int GetBotBaseSetting(uint16 botSetting); + int GetDefaultBotBaseSetting(uint16 botSetting); + void SetBotBaseSetting(uint16 botSetting, int settingValue); + void LoadDefaultBotSettings(); + void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); + BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); + + std::string GetBotSpellCategoryName(uint8 setting_type); + std::string GetBotSettingCategoryName(uint8 setting_type); + + int GetDefaultSetting(uint16 settingCategory, uint16 settingType); + uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass); + uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass); + uint16 GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass); + uint16 GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass); + uint16 GetDefaultSpellTypeResistLimit(uint16 spellType); + bool GetDefaultSpellTypeAggroCheck(uint16 spellType); + uint8 GetDefaultSpellTypeMinManaLimit(uint16 spellType); + uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spellType); + uint8 GetDefaultSpellTypeMinHPLimit(uint16 spellType); + uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spellType); + uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType); + + int GetSetting(uint16 settingCategory, uint16 settingType); + uint16 GetSpellTypePriority(uint16 spellType, uint8 priorityType); + void SetSpellTypePriority(uint16 spellType, uint8 priorityType, uint16 priority); + inline uint16 GetSpellTypeResistLimit(uint16 spellType) const { return _spellSettings[spellType].resistLimit; } + void SetSpellTypeResistLimit(uint16 spellType, uint16 resistLimit); + inline bool GetSpellTypeAggroCheck(uint16 spellType) const { return _spellSettings[spellType].aggroCheck; } + void SetSpellTypeAggroCheck(uint16 spellType, bool AggroCheck); + inline uint8 GetSpellTypeMinManaLimit(uint16 spellType) const { return _spellSettings[spellType].minManaPct; } + inline uint8 GetSpellTypeMaxManaLimit(uint16 spellType) const { return _spellSettings[spellType].maxManaPct; } + void SetSpellTypeMinManaLimit(uint16 spellType, uint8 manaLimit); + void SetSpellTypeMaxManaLimit(uint16 spellType, uint8 manaLimit); + inline uint8 GetSpellTypeMinHPLimit(uint16 spellType) const { return _spellSettings[spellType].minHPPct; } + inline uint8 GetSpellTypeMaxHPLimit(uint16 spellType) const { return _spellSettings[spellType].maxHPPct; } + void SetSpellTypeMinHPLimit(uint16 spellType, uint8 hpLimit); + void SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit); + inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spellType) const { return _spellSettings[spellType].AEOrGroupTargetCount; } + void SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount); + + bool GetShowHelm() const { return _showHelm; } + void SetShowHelm(bool showHelm) { _showHelm = showHelm; } + bool GetBehindMob() const { return _behindMobStatus; } + void SetBehindMob(bool value) { _behindMobStatus = value; } + bool GetMaxMeleeRange() const { return _maxMeleeRangeStatus; } + void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; } + uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } + void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; } + uint32 GetBotCasterRange() const { return _casterRange; } + void SetBotCasterRange(uint32 casterRange) { _casterRange = casterRange; } + bool GetMedInCombat() const { return _medInCombat; } + void SetMedInCombat(bool value) { _medInCombat = value; } + uint8 GetHPWhenToMed() const { return _HPWhenToMed; } + void SetHPWhenToMed(uint8 value) { _HPWhenToMed = value; } + uint8 GetManaWhenToMed() const { return _ManaWhenToMed; } + void SetManaWhenToMed(uint8 value) { _ManaWhenToMed = value; } + void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } + bool HasLoS() const { return _hasLoS; } + + bool IsInGroupOrRaid(bool announce = false); + void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false); + + std::list GetSpellTypesPrioritized(uint8 priorityType); + uint16 GetSpellListSpellType(uint16 spellType); + bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid); + inline uint16 GetCastedSpellType() const { return _castedSpellType; } + void SetCastedSpellType(uint16 spellType); + + bool HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar); + + void CheckBotSpells(); + [[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; } [[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; } @@ -423,33 +578,36 @@ class Bot : public NPC { ProcessBotGroupAdd(Group* group, Raid* raid, Client* client = nullptr, bool new_raid = false, bool initial = false); - static std::list GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect); - static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType); - static std::list GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType); - static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType); - - static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType); - static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster); - static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster); - static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* botCaster); - static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster); - static BotSpell GetBestBotSpellForMagicBasedSlow(Bot* botCaster); - static BotSpell GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster); - - static Mob* GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell); - static BotSpell GetBestBotSpellForMez(Bot* botCaster); - static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster); + static std::list GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect); + static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType); + static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); + static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false); + + static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); + static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); + + static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE = false); + bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid); + static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); + static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); static std::string GetBotMagicianPetType(Bot* botCaster); - static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType); - static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType); - static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target, uint16 spellType); static BotSpell GetDebuffBotSpell(Bot* botCaster, Mob* target); - static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target, uint16 spellType); static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellType); + static BotSpell GetBestBotSpellForCharm(Bot* botCaster, Mob* target, uint16 spellType); static NPCType *CreateDefaultNPCTypeStructForBot( const std::string& botName, @@ -472,12 +630,11 @@ class Bot : public NPC { uint32 GetBotOwnerCharacterID() const { return _botOwnerCharacterID; } uint32 GetBotSpellID() const { return npc_spells_id; } Mob* GetBotOwner() { return this->_botOwner; } - uint32 GetBotArcheryRange(); + uint32 GetBotRangedValue(); EQ::ItemInstance* GetBotItem(uint16 slot_id); bool GetSpawnStatus() { return _spawnStatus; } uint8 GetPetChooserID() { return _petChooserID; } - bool IsPetChooser() { return _petChooser; } - bool IsBotArcher() { return m_bot_archery_setting; } + bool IsBotRanged() { return _botRangedSetting; } bool IsBotCharmer() { return _botCharmer; } bool IsBot() const override { return true; } bool IsOfClientBot() const override { return true; } @@ -485,8 +642,7 @@ class Bot : public NPC { bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; } uint8 GetBotStance() { return _botStance; } - uint8 GetChanceToCastBySpellType(uint32 spellType); - bool GetBotEnforceSpellSetting() { return m_enforce_spell_settings; } + uint8 GetChanceToCastBySpellType(uint16 spellType); float GetBotCasterMaxRange(float melee_distance_max); bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; } bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; } @@ -527,8 +683,6 @@ class Bot : public NPC { std::shared_ptr* MemberOfHealRotation() { return &m_member_of_heal_rotation; } - bool GetAltOutOfCombatBehavior() const { return _altoutofcombatbehavior;} - bool GetShowHelm() const { return _showhelm; } inline int32 GetSTR() const override { return STR; } inline int32 GetSTA() const override { return STA; } inline int32 GetDEX() const override { return DEX; } @@ -600,13 +754,11 @@ class Bot : public NPC { void SetBotSpellID(uint32 newSpellID); void SetSpawnStatus(bool spawnStatus) { _spawnStatus = spawnStatus; } void SetPetChooserID(uint8 id) { _petChooserID = id; } - void SetBotArcherySetting(bool bot_archer_setting, bool save = false); + void SetBotRangedSetting(bool botRangedSetting) { _botRangedSetting = botRangedSetting; } void SetBotCharmer(bool c) { _botCharmer = c; } - void SetPetChooser(bool p) { _petChooser = p; } void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; } void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; } void SetBotStance(uint8 stance_id) { _botStance = Stance::IsValid(stance_id) ? stance_id : Stance::Passive; } - void SetBotCasterRange(uint32 bot_caster_range) { m_bot_caster_range = bot_caster_range; } uint32 GetSpellRecastTimer(uint16 spell_id = 0); bool CheckSpellRecastTimer(uint16 spell_id = 0); uint32 GetSpellRecastRemainingTime(uint16 spell_id = 0); @@ -624,8 +776,6 @@ class Bot : public NPC { void ClearSpellRecastTimer(uint16 spell_id = 0); uint32 GetItemReuseRemainingTime(uint32 item_id = 0); void ClearExpiredTimers(); - void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;} - void SetShowHelm(bool showhelm) { _showhelm = showhelm; } void SetBeardColor(uint8 value) { beardcolor = value; } void SetBeard(uint8 value) { beard = value; } void SetEyeColor1(uint8 value) { eyecolor1 = value; } @@ -639,7 +789,7 @@ class Bot : public NPC { bool DyeArmor(int16 slot_id, uint32 rgb, bool all_flag = false, bool save_flag = true); int GetExpansionBitmask(); - void SetExpansionBitmask(int expansion_bitmask, bool save = true); + void SetExpansionBitmask(int expansionBitmask); void ListBotSpells(uint8 min_level); @@ -651,15 +801,12 @@ class Bot : public NPC { void ListBotSpellSettings(); void LoadBotSpellSettings(); bool UpdateBotSpellSetting(uint16 spell_id, BotSpellSetting* bs); - void SetBotEnforceSpellSetting(bool enforcespellsettings, bool save = false); - bool GetBotEnforceSpellSetting() const { return m_enforce_spell_settings; } + void SetBotEnforceSpellSetting(bool enforceSpellSettings); + bool GetBotEnforceSpellSetting() { return _enforceSpellSettings; } // Class Destructors ~Bot() override; - // Publicized protected functions - void BotRangedAttack(Mob* other); - // Publicized private functions static NPCType *FillNPCTypeStruct( uint32 botSpellsID, @@ -750,24 +897,11 @@ class Bot : public NPC { static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND]; - bool BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid); - bool BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid); - bool BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los); - bool BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass); - bool BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iSpellTypes); - bool BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los); - bool BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const bool& checked_los); - bool BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell); - bool BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass); - bool BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes); - bool BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes); - bool BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const bool& checked_los); - bool BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los, Raid* raid); - bool BotCastDebuff(Mob* tar, uint8 botLevel, BotSpell& botSpell, bool checked_los); - bool BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, Raid* raid); - bool BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpell); - bool BotCastCombatSong(Mob* tar, uint8 botLevel); - bool BotCastSong(Mob* tar, uint8 botLevel); + bool BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); bool CheckIfIncapacitated(); bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid); @@ -796,39 +930,65 @@ class Bot : public NPC { const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, - bool behind_mob, + bool behindMob, bool backstab_weapon, float& melee_distance_max, - float& melee_distance - ) const; + float& melee_distance, + float& melee_distance_min, + uint8 stopMeleeLevel + ); // Combat Checks void SetBerserkState(); bool CheckIfCasting(float fm_distance); void HealRotationChecks(); - void CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item); + void CheckCombatRange( + Mob* tar, + float tar_distance, + bool& atCombatRange, + bool& behindMob, + const EQ::ItemInstance*& p_item, + const EQ::ItemInstance*& s_item, + float& melee_distance_min, + float& melee_distance_max, + float& melee_distance, + uint8 stopMeleeLevel + ); + bool GetCombatJitterFlag() { return m_combat_jitter_flag; } + void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; } + bool GetCombatOutOfRangeJitterFlag() { return m_combat_out_of_range_jitter_flag; } + void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; } + void SetCombatJitter(); + void SetCombatOutOfRangeJitter(); + void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob); + void DoFaceCheckWithJitter(Mob* tar); + void DoFaceCheckNoJitter(Mob* tar); + void RunToGoalWithJitter(glm::vec3 Goal); + bool RequiresLoSForPositioning(); + bool HasRequiredLoSForPositioning(Mob* tar); // Try Combat Methods bool TryEvade(Mob* tar); bool TryFacingTarget(Mob* tar); bool TryRangedAttack(Mob* tar); - bool TryClassAttacks(Mob* tar); - bool TryPrimaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* p_item); - bool TrySecondaryWeaponAttacks(Mob* tar, const EQ::ItemInstance* s_item); bool TryPursueTarget(float leash_distance, glm::vec3& Goal); bool TryMeditate(); bool TryAutoDefend(Client* bot_owner, float leash_distance); bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); bool TryBardMovementCasts(); - void SetRangerCombatWeapon(bool atArcheryRange); + void BotRangedAttack(Mob* other, bool CanDoubleAttack = false); + bool CheckDoubleRangedAttack(); // Public "Refactor" Methods static bool CheckSpawnConditions(Client* c); + inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); } + protected: void BotMeditate(bool isSitting); bool CheckBotDoubleAttack(bool Triple = false); + bool CheckTripleAttack(); void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client); bool AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = nullptr) override; @@ -849,9 +1009,7 @@ class Bot : public NPC { uint32 _botOwnerCharacterID; bool _spawnStatus; Mob* _botOwner; - bool m_bot_archery_setting; bool _botCharmer; - bool _petChooser; uint8 _petChooserID; bool berserk; EQ::InventoryProfile m_inv; @@ -876,9 +1034,15 @@ class Bot : public NPC { int32 max_end; int32 end_regen; - Timer m_evade_timer; // can be moved to pTimers at some point + Timer m_rogue_evade_timer; // Rogue evade timer + Timer m_monk_evade_timer; // Monk evade FD timer Timer m_auto_defend_timer; Timer auto_save_timer; + + Timer m_combat_jitter_timer; + bool m_combat_jitter_flag; + bool m_combat_out_of_range_jitter_flag; + bool m_dirtyautohaters; bool m_guard_flag; bool m_hold_flag; @@ -888,7 +1052,7 @@ class Bot : public NPC { bool m_pulling_flag; bool m_returning_flag; bool is_using_item_click; - uint32 m_bot_caster_range; + BotCastingRoles m_CastingRoles; std::map bot_spell_settings; @@ -896,12 +1060,22 @@ class Bot : public NPC { std::shared_ptr m_member_of_heal_rotation; InspectMessage_Struct _botInspectMessage; - bool _altoutofcombatbehavior; - bool _showhelm; bool _pauseAI; + + int _expansionBitmask; + bool _enforceSpellSettings; + bool _showHelm; + bool _botRangedSetting; uint8 _stopMeleeLevel; - int m_expansion_bitmask; - bool m_enforce_spell_settings; + uint32 _casterRange; + bool _behindMobStatus; + bool _maxMeleeRangeStatus; + bool _medInCombat; + uint8 _HPWhenToMed; + uint8 _ManaWhenToMed; + uint16 _castedSpellType; + bool _hasLoS; + bool _commandedSpell; // Private "base stats" Members int32 _baseMR; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index b15531dd75..3f6dfef5b1 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1249,8 +1249,8 @@ int bot_command_init(void) bot_command_add("actionable", "Lists actionable command arguments and use descriptions", AccountStatus::Player, bot_command_actionable) || bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", AccountStatus::Player, bot_command_aggressive) || bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) || - bot_command_add("applypotion", "Applies cursor-held potion to a bot's effects", AccountStatus::Player, bot_command_apply_potion) || bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) || + bot_command_add("behindmob", "Toggles whether or not your bot tries to stay behind a mob", AccountStatus::Player, bot_command_behind_mob) || bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", AccountStatus::Player, bot_command_bind_affinity) || bot_command_add("bot", "Lists the available bot management [subcommands]", AccountStatus::Player, bot_command_bot) || bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", AccountStatus::Player, bot_command_appearance) || @@ -1270,8 +1270,8 @@ int bot_command_init(void) bot_command_add("botheritage", "Changes the Drakkin heritage of a bot", AccountStatus::Player, bot_command_heritage) || bot_command_add("botinspectmessage", "Changes the inspect message of a bot", AccountStatus::Player, bot_command_inspect_message) || bot_command_add("botlist", "Lists the bots that you own", AccountStatus::Player, bot_command_list_bots) || - bot_command_add("botoutofcombat", "Toggles your bot between standard and out-of-combat spell/skill use - if any specialized behaviors exist", AccountStatus::Player, bot_command_out_of_combat) || bot_command_add("botreport", "Orders a bot to report its readiness", AccountStatus::Player, bot_command_report) || + bot_command_add("botsettings", "Lists settings related to spell types and bot combat", AccountStatus::Player, bot_command_bot_settings) || bot_command_add("botspawn", "Spawns a created bot", AccountStatus::Player, bot_command_spawn) || bot_command_add("botstance", "Changes the stance of a bot", AccountStatus::Player, bot_command_stance) || bot_command_add("botstopmeleelevel", "Sets the level a caster or spell-casting fighter bot will stop melee combat", AccountStatus::Player, bot_command_stop_melee_level) || @@ -1279,16 +1279,20 @@ int bot_command_init(void) bot_command_add("botsummon", "Summons bot(s) to your location", AccountStatus::Player, bot_command_summon) || bot_command_add("botsurname", "Sets a bots surname (last name)", AccountStatus::Player, bot_command_surname) || bot_command_add("bottattoo", "Changes the Drakkin tattoo of a bot", AccountStatus::Player, bot_command_tattoo) || - bot_command_add("bottogglearcher", "Toggles a archer bot between melee and ranged weapon use", AccountStatus::Player, bot_command_toggle_archer) || bot_command_add("bottogglehelm", "Toggles the helm visibility of a bot between shown and hidden", AccountStatus::Player, bot_command_toggle_helm) || + bot_command_add("bottoggleranged", "Toggles a ranged bot between melee and ranged weapon use", AccountStatus::Player, bot_command_toggle_ranged) || bot_command_add("bottitle", "Sets a bots title", AccountStatus::Player, bot_command_title) || bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_command_update) || bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_command_woad) || + bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || bot_command_add("casterrange", "Controls the range casters will try to stay away from a mob (if too far, they will skip spells that are out-of-range)", AccountStatus::Player, bot_command_caster_range) || bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_circle) || + bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || + bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) || + bot_command_add("defaultsettings", "Restores a bot back to default settings", AccountStatus::Player, bot_command_default_settings) || bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || bot_command_add("enforcespellsettings", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || @@ -1320,6 +1324,7 @@ int bot_command_init(void) bot_command_add("help", "List available commands and their description - specify partial command as argument to search", AccountStatus::Player, bot_command_help) || bot_command_add("hold", "Prevents a bot from attacking until released", AccountStatus::Player, bot_command_hold) || bot_command_add("identify", "Orders a bot to cast an item identification spell", AccountStatus::Player, bot_command_identify) || + bot_command_add("illusionblock", "Control whether or not illusion effects will land on the bot if casted by another player or bot", AccountStatus::Player, bot_command_illusion_block) || bot_command_add("inventory", "Lists the available bot inventory [subcommands]", AccountStatus::Player, bot_command_inventory) || bot_command_add("inventorygive", "Gives the item on your cursor to a bot", AccountStatus::Player, bot_command_inventory_give) || bot_command_add("inventorylist", "Lists all items in a bot's inventory", AccountStatus::Player, bot_command_inventory_list) || @@ -1329,6 +1334,7 @@ int bot_command_init(void) bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", AccountStatus::Player, bot_command_item_use) || bot_command_add("levitation", "Orders a bot to cast a levitation spell", AccountStatus::Player, bot_command_levitation) || bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || + bot_command_add("maxmeleerange", "Toggles whether your bot is at max melee range or not. This will disable all special abilities, including taunt.", AccountStatus::Player, bot_command_max_melee_range) || bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", AccountStatus::Player, bot_command_mesmerize) || bot_command_add("movementspeed", "Orders a bot to cast a movement speed enhancement spell", AccountStatus::Player, bot_command_movement_speed) || bot_command_add("owneroption", "Sets options available to bot owners", AccountStatus::Player, bot_command_owner_option) || @@ -1346,7 +1352,23 @@ int bot_command_init(void) bot_command_add("resurrect", "Orders a bot to resurrect a player's (players') corpse(s)", AccountStatus::Player, bot_command_resurrect) || 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("sithppercent", "HP threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_hp_percent) || + bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || + bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || + bot_command_add("size", "Orders a bot to change a player's size", AccountStatus::Player, bot_command_size) || + bot_command_add("spellaggrochecks", "Toggles whether or not bots will cast a spell type if they think it will get them aggro", AccountStatus::Player, bot_command_spell_aggro_checks) || + bot_command_add("spellengagedpriority", "Controls the order of casts by spell type when engaged in combat", AccountStatus::Player, bot_command_spell_engaged_priority) || + bot_command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, bot_command_spell_delays) || + bot_command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, bot_command_spell_holds) || + bot_command_add("spellidlepriority", "Controls the order of casts by spell type when out of combat", AccountStatus::Player, bot_command_spell_idle_priority) || + bot_command_add("spellmaxhppct", "Controls at what HP percent a bot will stop casting different spell types", AccountStatus::Player, bot_command_spell_max_hp_pct) || + bot_command_add("spellmaxmanapct", "Controls at what mana percent a bot will stop casting different spell types", AccountStatus::Player, bot_command_spell_max_mana_pct) || + bot_command_add("spellmaxthresholds", "Controls the minimum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, bot_command_spell_max_thresholds) || + bot_command_add("spellminhppct", "Controls at what HP percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_hp_pct) || + bot_command_add("spellminmanapct", "Controls at what mana percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_mana_pct) || + bot_command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, bot_command_spell_min_thresholds) || + bot_command_add("spellpursuepriority", "Controls the order of casts by spell type when pursuing in combat", AccountStatus::Player, bot_command_spell_pursue_priority) || + bot_command_add("spelltargetcount", "Sets the required target amount for group/AE spells by spell type", AccountStatus::Player, bot_command_spell_target_count) || bot_command_add("spellinfo", "Opens a dialogue window with spell info", AccountStatus::Player, bot_spell_info_dialogue_window) || bot_command_add("spells", "Lists all Spells learned by the Bot.", AccountStatus::Player, bot_command_spell_list) || bot_command_add("spellsettings", "Lists a bot's spell setting entries", AccountStatus::Player, bot_command_spell_settings_list) || @@ -1360,7 +1382,7 @@ int bot_command_init(void) bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || bot_command_add("track", "Orders a capable bot to track enemies", AccountStatus::Player, bot_command_track) || bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) || - bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing) + bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing) ) { bot_command_deinit(); return -1; @@ -1613,7 +1635,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->Message( Chat::White, fmt::format( - "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z'. Mixed case {} allowed.", + "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z' and it must be between 4 and 15 characters. Mixed case {} allowed.", bot_name, RuleB(Bots, AllowCamelCaseNames) ? "is" : "is not" ).c_str() ); @@ -1625,7 +1647,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->Message( Chat::White, fmt::format( - "Failed to query name availability for '{}'.", + "'{}' is already in use or an invalid name.", bot_name ).c_str() ); @@ -1806,40 +1828,6 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas return bot_id; } -void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot) -{ - if (!bot_owner || !my_bot) - return; - - switch (my_bot->GetClass()) { - case Class::Warrior: - case Class::Cleric: - case Class::Paladin: - case Class::Ranger: - case Class::ShadowKnight: - case Class::Druid: - case Class::Monk: - bot_owner->Message(Chat::White, "%s has no out-of-combat behavior defined", my_bot->GetCleanName()); - break; - case Class::Bard: - bot_owner->Message(Chat::White, "%s will %s use out-of-combat behavior for bard songs", my_bot->GetCleanName(), ((my_bot->GetAltOutOfCombatBehavior()) ? ("now") : ("no longer"))); - break; - case Class::Rogue: - case Class::Shaman: - case Class::Necromancer: - case Class::Wizard: - case Class::Magician: - case Class::Enchanter: - case Class::Beastlord: - case Class::Berserker: - bot_owner->Message(Chat::White, "%s has no out-of-combat behavior defined", my_bot->GetCleanName()); - break; - default: - break; - bot_owner->Message(Chat::White, "Undefined bot class for %s", my_bot->GetCleanName()); - } -} - int helper_bot_follow_option_chain(Client* bot_owner) { if (!bot_owner) { @@ -2085,8 +2073,11 @@ void helper_send_available_subcommands(Client *bot_owner, const char* command_si bot_owner->Message( Chat::White, fmt::format( - "^{} - {}", - subcommand_iter, + "{} - {}", + Saylink::Silent( + fmt::format("^{} help", subcommand_iter), + fmt::format("^{}", subcommand_iter) + ), find_iter != bot_command_list.end() ? find_iter->second->desc : "No Description" ).c_str() ); @@ -2126,18 +2117,152 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp return false; } +void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt) { + if (helpPrompt) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {}, {}, {} for a list of spell types by ID", + Saylink::Silent( + fmt::format("{} listid 0-19", arg0) + ), + Saylink::Silent( + fmt::format("{} listid 20-39", arg0) + ), + Saylink::Silent( + fmt::format("{} listid 40+", arg0) + ) + ).c_str() + ); + + c->Message( + Chat::Yellow, + fmt::format( + "Use {}, {}, {} for a list of spell types by short name", + Saylink::Silent( + fmt::format("{} listname 0-19", arg0) + ), + Saylink::Silent( + fmt::format("{} listname 20-39", arg0) + ), + Saylink::Silent( + fmt::format("{} listname 40+", arg0) + ) + ).c_str() + ); + + return; + } + + uint8 minCount = 0; + uint8 maxCount = 0; + + if (BotSpellTypes::END <= 19) { + minCount = BotSpellTypes::START; + maxCount = BotSpellTypes::END; + } + else if (!arg2.compare("0-19")) { + minCount = BotSpellTypes::START; + maxCount = 19; + } + else if (!arg2.compare("20-39")) { + minCount = std::min(static_cast(20), static_cast(BotSpellTypes::END)); + maxCount = std::min(static_cast(39), static_cast(BotSpellTypes::END)); + } + else if (!arg2.compare("40+")) { + minCount = std::min(static_cast(40), static_cast(BotSpellTypes::END)); + maxCount = BotSpellTypes::END; + } + else { + c->Message(Chat::Yellow, "You must choose a valid range option"); + + return; + } + + const std::string& indian_red = "indian_red"; + const std::string& gold = "gold"; + const std::string& slate_blue = "slate_blue"; + const std::string& forest_green = "forest_green"; + const std::string& goldenrod = "goldenrod"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(goldenrod, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(gold, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(gold, fillerLine) + ) + ) + ); + + for (int i = minCount; i <= maxCount; ++i) { + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(forest_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(forest_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); +} + + #include "bot_commands/actionable.cpp" #include "bot_commands/aggressive.cpp" #include "bot_commands/appearance.cpp" #include "bot_commands/apply_poison.cpp" #include "bot_commands/apply_potion.cpp" #include "bot_commands/attack.cpp" +#include "bot_commands/behind_mob.cpp" #include "bot_commands/bind_affinity.cpp" #include "bot_commands/bot.cpp" +#include "bot_commands/bot_settings.cpp" +#include "bot_commands/cast.cpp" #include "bot_commands/caster_range.cpp" #include "bot_commands/charm.cpp" +#include "bot_commands/class_race_list.cpp" #include "bot_commands/click_item.cpp" +#include "bot_commands/copy_settings.cpp" #include "bot_commands/cure.cpp" +#include "bot_commands/default_settings.cpp" #include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" #include "bot_commands/escape.cpp" @@ -2148,11 +2273,13 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp #include "bot_commands/help.cpp" #include "bot_commands/hold.cpp" #include "bot_commands/identify.cpp" +#include "bot_commands/illusion_block.cpp" #include "bot_commands/inventory.cpp" #include "bot_commands/invisibility.cpp" #include "bot_commands/item_use.cpp" #include "bot_commands/levitation.cpp" #include "bot_commands/lull.cpp" +#include "bot_commands/max_melee_range.cpp" #include "bot_commands/mesmerize.cpp" #include "bot_commands/movement_speed.cpp" #include "bot_commands/name.cpp" @@ -2167,8 +2294,24 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp #include "bot_commands/resurrect.cpp" #include "bot_commands/rune.cpp" #include "bot_commands/send_home.cpp" +#include "bot_commands/sit_hp_percent.cpp" +#include "bot_commands/sit_in_combat.cpp" +#include "bot_commands/sit_mana_percent.cpp" #include "bot_commands/size.cpp" #include "bot_commands/spell.cpp" +#include "bot_commands/spell_aggro_checks.cpp" +#include "bot_commands/spell_delays.cpp" +#include "bot_commands/spell_engaged_priority.cpp" +#include "bot_commands/spell_holds.cpp" +#include "bot_commands/spell_idle_priority.cpp" +#include "bot_commands/spell_max_hp_pct.cpp" +#include "bot_commands/spell_max_mana_pct.cpp" +#include "bot_commands/spell_max_thresholds.cpp" +#include "bot_commands/spell_min_hp_pct.cpp" +#include "bot_commands/spell_min_mana_pct.cpp" +#include "bot_commands/spell_min_thresholds.cpp" +#include "bot_commands/spell_pursue_priority.cpp" +#include "bot_commands/spell_target_count.cpp" #include "bot_commands/summon.cpp" #include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 74dfa14017..53cb2776bc 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -666,6 +666,27 @@ namespace MyBots UniquifySBL(sbl); } } + + static void PopulateSBL_ByAtMMR(Client* bot_owner, std::list& sbl, bool clear_list = true) { + if (clear_list) { + sbl.clear(); + } + + if (!bot_owner) { + return; + } + + auto selectable_bot_list = entity_list.GetBotsByBotOwnerCharacterID(bot_owner->CharacterID()); + for (auto bot_iter : selectable_bot_list) { + if (!bot_iter->GetMaxMeleeRange()) { + continue; + } + sbl.push_back(bot_iter); + } + if (!clear_list) { + UniquifySBL(sbl); + } + } }; namespace ActionableTarget @@ -875,6 +896,7 @@ namespace ActionableBots ABT_HealRotation, ABT_HealRotationMembers, ABT_HealRotationTargets, + ABT_MMR, ABT_Class, ABT_Race, ABT_Spawned, @@ -892,6 +914,7 @@ namespace ActionableBots ABM_HealRotation = (1 << (ABT_HealRotation - 1)), ABM_HealRotationMembers = (1 << (ABT_HealRotationMembers - 1)), ABM_HealRotationTargets = (1 << (ABT_HealRotationTargets - 1)), + ABM_MMR = (1 << (ABT_MMR - 1)), ABM_Class = (1 << (ABT_Class - 1)), ABM_Race = (1 << (ABT_Race - 1)), ABM_Spawned = (1 << (ABT_Spawned - 1)), @@ -899,8 +922,8 @@ namespace ActionableBots ABM_Spawned_All = (3 << (ABT_Spawned - 1)), ABM_NoFilter = ~0, // grouped values - ABM_Type1 = (ABM_Target | ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_TargetGroup | ABM_NamesGroup | ABM_HealRotationTargets | ABM_Spawned | ABM_Class | ABM_Race), - ABM_Type2 = (ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_NamesGroup | ABM_HealRotation | ABM_Spawned | ABM_Class | ABM_Race) + ABM_Type1 = (ABM_Target | ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_TargetGroup | ABM_NamesGroup | ABM_HealRotationTargets | ABM_Spawned | ABM_MMR | ABM_Class | ABM_Race), + ABM_Type2 = (ABM_ByName | ABM_OwnerGroup | ABM_OwnerRaid | ABM_NamesGroup | ABM_HealRotation | ABM_Spawned | ABM_MMR | ABM_Class | ABM_Race) }; // Populates 'sbl' @@ -941,6 +964,9 @@ namespace ActionableBots else if (!ab_type_arg.compare("healrotationtargets")) { ab_type = ABT_HealRotationTargets; } + else if (!ab_type_arg.compare("mmr")) { + ab_type = ABT_MMR; + } else if (!ab_type_arg.compare("byclass")) { ab_type = ABT_Class; } @@ -1004,6 +1030,11 @@ namespace ActionableBots MyBots::PopulateSBL_ByHealRotationTargets(bot_owner, sbl, name, clear_list); } break; + case ABT_MMR: + if (ab_mask & ABM_MMR) { + MyBots::PopulateSBL_ByAtMMR(bot_owner, sbl, clear_list); + } + break; case ABT_Class: if (ab_mask & ABM_Class) { MyBots::PopulateSBL_BySpawnedBotsClass(bot_owner, sbl, classrace, clear_list); @@ -1256,9 +1287,9 @@ namespace ActionableBots sbl.remove_if([min_level](const Bot* l) { return (l->GetLevel() < min_level); }); } - static void Filter_ByArcher(Client* bot_owner, std::list& sbl) { + static void Filter_ByRanged(Client* bot_owner, std::list& sbl) { sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); - sbl.remove_if([bot_owner](Bot* l) { return (!l->IsBotArcher()); }); + sbl.remove_if([bot_owner](Bot* l) { return (!l->IsBotRanged()); }); } static void Filter_ByHighestSkill(Client* bot_owner, std::list& sbl, EQ::skills::SkillType skill_type, float& skill_value) { @@ -1637,12 +1668,18 @@ void bot_command_aggressive(Client *c, const Seperator *sep); void bot_command_apply_poison(Client *c, const Seperator *sep); void bot_command_apply_potion(Client* c, const Seperator* sep); void bot_command_attack(Client *c, const Seperator *sep); +void bot_command_behind_mob(Client* c, const Seperator* sep); void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep); +void bot_command_bot_settings(Client* c, const Seperator* sep); +void bot_command_cast(Client* c, const Seperator* sep); void bot_command_caster_range(Client* c, const Seperator* sep); void bot_command_charm(Client *c, const Seperator *sep); +void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); +void bot_command_copy_settings(Client* c, const Seperator* sep); void bot_command_cure(Client *c, const Seperator *sep); +void bot_command_default_settings(Client* c, const Seperator* sep); void bot_command_defensive(Client *c, const Seperator *sep); void bot_command_depart(Client *c, const Seperator *sep); void bot_command_escape(Client *c, const Seperator *sep); @@ -1653,11 +1690,13 @@ void bot_command_heal_rotation(Client *c, const Seperator *sep); void bot_command_help(Client *c, const Seperator *sep); void bot_command_hold(Client *c, const Seperator *sep); void bot_command_identify(Client *c, const Seperator *sep); +void bot_command_illusion_block(Client* c, const Seperator* sep); void bot_command_inventory(Client *c, const Seperator *sep); void bot_command_invisibility(Client *c, const Seperator *sep); void bot_command_item_use(Client *c, const Seperator *sep); void bot_command_levitation(Client *c, const Seperator *sep); void bot_command_lull(Client *c, const Seperator *sep); +void bot_command_max_melee_range(Client* c, const Seperator* sep); void bot_command_mesmerize(Client *c, const Seperator *sep); void bot_command_movement_speed(Client *c, const Seperator *sep); void bot_command_owner_option(Client *c, const Seperator *sep); @@ -1671,7 +1710,23 @@ void bot_command_resistance(Client *c, const Seperator *sep); 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_sit_hp_percent(Client* c, const Seperator* sep); +void bot_command_sit_in_combat(Client* c, const Seperator* sep); +void bot_command_sit_mana_percent(Client* c, const Seperator* sep); void bot_command_size(Client *c, const Seperator *sep); +void bot_command_spell_aggro_checks(Client* c, const Seperator* sep); +void bot_command_spell_delays(Client* c, const Seperator* sep); +void bot_command_spell_engaged_priority(Client* c, const Seperator* sep); +void bot_command_spell_holds(Client* c, const Seperator* sep); +void bot_command_spell_idle_priority(Client* c, const Seperator* sep); +void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep); +void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep); +void bot_command_spell_max_thresholds(Client* c, const Seperator* sep); +void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep); +void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep); +void bot_command_spell_min_thresholds(Client* c, const Seperator* sep); +void bot_command_spell_pursue_priority(Client* c, const Seperator* sep); +void bot_command_spell_target_count(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); @@ -1706,7 +1761,6 @@ void bot_command_hairstyle(Client *c, const Seperator *sep); void bot_command_heritage(Client *c, const Seperator *sep); void bot_command_inspect_message(Client *c, const Seperator *sep); void bot_command_list_bots(Client *c, const Seperator *sep); -void bot_command_out_of_combat(Client *c, const Seperator *sep); void bot_command_report(Client *c, const Seperator *sep); void bot_command_spawn(Client *c, const Seperator *sep); void bot_command_stance(Client *c, const Seperator *sep); @@ -1716,8 +1770,8 @@ void bot_command_summon(Client *c, const Seperator *sep); void bot_command_surname(Client *c, const Seperator *sep); void bot_command_tattoo(Client *c, const Seperator *sep); void bot_command_title(Client *c, const Seperator *sep); -void bot_command_toggle_archer(Client *c, const Seperator *sep); void bot_command_toggle_helm(Client *c, const Seperator *sep); +void bot_command_toggle_ranged(Client* c, const Seperator* sep); void bot_command_update(Client *c, const Seperator *sep); void bot_command_woad(Client *c, const Seperator *sep); @@ -1757,7 +1811,6 @@ bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, BCEnum::AFType f void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot); void helper_bot_appearance_form_update(Bot *my_bot); uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender); -void helper_bot_out_of_combat(Client *bot_owner, Bot *my_bot); int helper_bot_follow_option_chain(Client *bot_owner); bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr); bool helper_command_disabled(Client *bot_owner, bool rule_value, const char *command); diff --git a/zone/bot_commands/actionable.cpp b/zone/bot_commands/actionable.cpp index 84965b57d1..73ec9ef6c6 100644 --- a/zone/bot_commands/actionable.cpp +++ b/zone/bot_commands/actionable.cpp @@ -3,22 +3,61 @@ void bot_command_actionable(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_actionable", sep->arg[0], "actionable")) { + c->Message(Chat::White, "note: Lists actionable command arguments and use descriptions"); return; } - c->Message(Chat::White, "Actionable command arguments:"); - c->Message(Chat::White, "target - selects target as single bot .. use ^command [target] or imply by empty actionable argument"); - c->Message(Chat::White, "byname [name] - selects single bot by name"); - c->Message(Chat::White, "ownergroup - selects all bots in the owner's group"); - c->Message(Chat::White, "ownerraid - selects all bots in the owner's raid"); - c->Message(Chat::White, "targetgroup - selects all bots in target's group"); - c->Message(Chat::White, "namesgroup [name] - selects all bots in name's group"); - c->Message(Chat::White, "healrotation [name] - selects all member and target bots of a heal rotation where name is a member"); - c->Message(Chat::White, "healrotationmembers [name] - selects all member bots of a heal rotation where name is a member"); - c->Message(Chat::White, "healrotationtargets [name] - selects all target bots of a heal rotation where name is a member"); - c->Message(Chat::White, "byclass - selects all bots of the chosen class"); - c->Message(Chat::White, "byrace - selects all bots of the chosen rsce"); - c->Message(Chat::White, "spawned - selects all spawned bots"); - c->Message(Chat::White, "all - selects all spawned bots .. argument use indicates en masse database updating"); - c->Message(Chat::White, "You may only select your bots as actionable"); + std::vector description = + { + "Lists actionable command arguments and use descriptions." + }; + + std::vector notes = + { + "[target] - uses the command on the target. Some commands will default to target if no actionable is selected.", + "[byname] [name] - selects a bot by name their name.", + "[ownergroup] - selects all bots in the owner's group.", + "[ownerraid] - selects all bots in the owner's raid.", + "[targetgroup] - selects all bots in the target's group.", + "[namesgroup] [name] - selects all bots in [name]'s group.", + "[healrotation] [name] - selects all member and target bots of a heal rotation where [name] is a member.", + "[healrotationmembers] [name] - selects all member bots of a heal rotation where [name] is a member.", + "[healrotationtargets] [name] - selects all target bots of a heal rotation where [name] is a member.", + "[mmr] - selects all bots that are currently at max melee range.", + "[byclass] - selects all bots of the chosen class.", + "[byrace] - selects all bots of the chosen race.", + "[spawned] - selects all spawned bots.", + "[all] - selects all spawned bots.", + "
", + "You may only select your bots as actionable" + }; + + std::vector example_format = { }; + std::vector examples_one = { }; + std::vector examples_two = { }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + return; } diff --git a/zone/bot_commands/aggressive.cpp b/zone/bot_commands/aggressive.cpp index 6a3881d98c..b40bf71232 100644 --- a/zone/bot_commands/aggressive.cpp +++ b/zone/bot_commands/aggressive.cpp @@ -7,32 +7,26 @@ void bot_command_aggressive(Client* c, const Seperator* sep) helper_command_alias_fail(c, "bot_command_aggressive", sep->arg[0], "aggressive")) { return; } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message( - Chat::White, - "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", - sep->arg[0] - ); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "note: Orders a bot to use a aggressive discipline"); helper_send_usage_required_bots(c, BCEnum::SpT_Stance); return; } + const int ab_mask = ActionableBots::ABM_Type1; + int ab_arg = 1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; - std::string class_race_arg = sep->arg[1]; - bool class_race_check = false; if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL( - c, - sep->arg[1], - sbl, - ab_mask, - !class_race_check ? sep->arg[2] : nullptr, - class_race_check ? atoi(sep->arg[2]) : 0 - ) == ActionableBots::ABT_None) { + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } diff --git a/zone/bot_commands/appearance.cpp b/zone/bot_commands/appearance.cpp index fcaec025cd..c6aed9e08f 100644 --- a/zone/bot_commands/appearance.cpp +++ b/zone/bot_commands/appearance.cpp @@ -158,7 +158,7 @@ void bot_command_dye_armor(Client *c, const Seperator *sep) c->Message( Chat::White, fmt::format( - "Usage: {} [Material Slot] [Red: 0-255] [Green: 0-255] [Blue: 0-255] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", + "Usage: {} [Material Slot] [Red: 0-255] [Green: 0-255] [Blue: 0-255] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0] ).c_str() ); diff --git a/zone/bot_commands/apply_poison.cpp b/zone/bot_commands/apply_poison.cpp index cfffff2cb6..444e370ed9 100644 --- a/zone/bot_commands/apply_poison.cpp +++ b/zone/bot_commands/apply_poison.cpp @@ -11,6 +11,7 @@ void bot_command_apply_poison(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s", sep->arg[0]); + c->Message(Chat::White, "note: Applies cursor-held poison to a rogue bot's weapon"); return; } diff --git a/zone/bot_commands/attack.cpp b/zone/bot_commands/attack.cpp index 60cad8cd8b..8cce85db1b 100644 --- a/zone/bot_commands/attack.cpp +++ b/zone/bot_commands/attack.cpp @@ -5,14 +5,17 @@ void bot_command_attack(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_attack", sep->arg[0], "attack")) { return; } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: %s [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "note: Orders bots to attack a designated target"); return; } - const int ab_mask = ActionableBots::ABM_Type2; + const int ab_mask = ActionableBots::ABM_Type2; Mob* target_mob = ActionableTarget::AsSingle_ByAttackable(c); + if (!target_mob) { c->Message(Chat::White, "You must an enemy to use this command"); @@ -26,11 +29,13 @@ void bot_command_attack(Client *c, const Seperator *sep) std::string class_race_arg(sep->arg[1]); bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; + if (ActionableBots::PopulateSBL(c, ab_arg.c_str(), sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { return; } diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp new file mode 100644 index 0000000000..5761c4c319 --- /dev/null +++ b/zone/bot_commands/behind_mob.cpp @@ -0,0 +1,173 @@ +#include "../bot_command.h" + +void bot_command_behind_mob(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_behind_mob", sep->arg[0], "behindmob")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will stay behind the mob during combat." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + + std::vector examples_one = + { + "To set Monks to stay behind the mob:", + fmt::format( + "{} 1 byclass 7", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the behind mob status for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay behind mobs.'", + my_bot->GetCleanName(), + my_bot->GetBehindMob() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetBehindMob(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay behind mobs.'", + first_found->GetCleanName(), + first_found->GetBehindMob() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} stay behind mobs.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/bind_affinity.cpp b/zone/bot_commands/bind_affinity.cpp index e6d01fe4c3..a59c5947ae 100644 --- a/zone/bot_commands/bind_affinity.cpp +++ b/zone/bot_commands/bind_affinity.cpp @@ -7,6 +7,7 @@ void bot_command_bind_affinity(Client *c, const Seperator *sep) return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: () %s", sep->arg[0]); + c->Message(Chat::White, "note: Orders a bot to attempt an affinity binding", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_BindAffinity); return; } diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 5d2bfe18e5..7da91504d4 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -18,7 +18,7 @@ void bot_command_bot(Client *c, const Seperator *sep) subcommand_list.push_back("botstance"); subcommand_list.push_back("botstopmeleelevel"); subcommand_list.push_back("botsummon"); - subcommand_list.push_back("bottogglearcher"); + subcommand_list.push_back("bottoggleranged"); subcommand_list.push_back("bottogglehelm"); subcommand_list.push_back("botupdate"); @@ -33,7 +33,7 @@ void bot_command_camp(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_Type1; @@ -452,8 +452,8 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_follow_distance", sep->arg[0], "botfollowdistance")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [set] [distance] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "usage: %s [clear] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [set [1 to %i]] [distance] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0], BOT_FOLLOW_DISTANCE_DEFAULT_MAX); + c->Message(Chat::White, "usage: %s [clear] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; @@ -464,7 +464,7 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) if (!strcasecmp(sep->arg[1], "set")) { if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A numeric [distance] is required to use this command"); + c->Message(Chat::White, "A numeric [distance] is required to use this command. [1 to %i]", BOT_FOLLOW_DISTANCE_DEFAULT_MAX); return; } @@ -492,22 +492,15 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) continue; bot_iter->SetFollowDistance(bfd); - if (ab_type != ActionableBots::ABT_All && !database.botdb.SaveFollowDistance(bot_iter->GetBotID(), bfd)) { - return; - } ++bot_count; } if (ab_type == ActionableBots::ABT_All) { - if (!database.botdb.SaveAllFollowDistances(c->CharacterID(), bfd)) { - return; - } - - c->Message(Chat::White, "%s all of your bot follow distances", set_flag ? "Set" : "Cleared"); + c->Message(Chat::White, "%s all of your bot follow distances to %i", set_flag ? "Set" : "Cleared", bfd); } else { - c->Message(Chat::White, "%s %i of your spawned bot follow distances", (set_flag ? "Set" : "Cleared"), bot_count); + c->Message(Chat::White, "%s %i of your spawned bot follow distances to %i", (set_flag ? "Set" : "Cleared"), bot_count, bfd); } } @@ -516,7 +509,7 @@ void bot_command_inspect_message(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_inspect_message", sep->arg[0], "botinspectmessage")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [set | clear] ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [set | clear] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "Notes:"); if (c->ClientVersion() >= EQ::versions::ClientVersion::SoF) { c->Message(Chat::White, "- Self-inspect and type your bot's inspect message"); @@ -764,75 +757,31 @@ void bot_command_list_bots(Client *c, const Seperator *sep) } } -void bot_command_out_of_combat(Client *c, const Seperator *sep) +void bot_command_report(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_out_of_combat", sep->arg[0], "botoutofcombat")) + if (helper_command_alias_fail(c, "bot_command_report", sep->arg[0], "botreport")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } - const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); + + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; - - bool behavior_state = false; - bool toggle_behavior = true; int ab_arg = 1; - if (!arg1.compare("on")) { - behavior_state = true; - toggle_behavior = false; - ab_arg = 2; - } - else if (!arg1.compare("off")) { - toggle_behavior = false; - ab_arg = 2; - } - std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]) == ActionableBots::ABT_None) - return; - - for (auto bot_iter : sbl) { - if (!bot_iter) - continue; - - if (toggle_behavior) - bot_iter->SetAltOutOfCombatBehavior(!bot_iter->GetAltOutOfCombatBehavior()); - else - bot_iter->SetAltOutOfCombatBehavior(behavior_state); - - helper_bot_out_of_combat(c, bot_iter); - } -} - -void bot_command_report(Client *c, const Seperator *sep) -{ - if (helper_command_alias_fail(c, "bot_command_report", sep->arg[0], "botreport")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); - return; - } - const int ab_mask = ActionableBots::ABM_NoFilter; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; - std::string ab_type_arg = sep->arg[1]; - if (ab_type_arg.empty()) { - auto t = c->GetTarget(); - if (t && t->IsClient()) { - if (t->CastToClient() == c) { - ab_type_arg = "ownergroup"; - } else { - ab_type_arg = "targetgroup"; - } - } else { - ab_type_arg = "spawned"; - } + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, ab_type_arg, sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; + } for (auto bot_iter : sbl) { if (!bot_iter) @@ -1062,65 +1011,74 @@ void bot_command_spawn(Client *c, const Seperator *sep) void bot_command_stance(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_stance", sep->arg[0], "botstance")) + if (helper_command_alias_fail(c, "bot_command_stance", sep->arg[0], "botstance")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message( Chat::White, fmt::format( - "Value: {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({}), {} ({})", + "Value: {} ({}), {} ({}), {} ({})", Stance::Passive, Stance::GetName(Stance::Passive), Stance::Balanced, Stance::GetName(Stance::Balanced), - Stance::Efficient, - Stance::GetName(Stance::Efficient), - Stance::Reactive, - Stance::GetName(Stance::Reactive), Stance::Aggressive, - Stance::GetName(Stance::Aggressive), - Stance::Assist, - Stance::GetName(Stance::Assist), - Stance::Burn, - Stance::GetName(Stance::Burn), - Stance::Efficient2, - Stance::GetName(Stance::Efficient2), - Stance::AEBurn, - Stance::GetName(Stance::AEBurn) + Stance::GetName(Stance::Aggressive) ).c_str() ); return; } - int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); - bool current_flag = false; - uint8 bst = Stance::Unknown; + const int ab_mask = ActionableBots::ABM_Type1; - if (!strcasecmp(sep->arg[1], "current")) - current_flag = true; - else if (sep->IsNumber(1)) { - bst = static_cast(Strings::ToUnsignedInt(sep->arg[1])); - if (!Stance::IsValid(bst)) { - bst = Stance::Unknown; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + bool current_check = false; + uint32 value = 0; + + if (sep->IsNumber(1)) { + ++ab_arg; + value = atoi(sep->arg[1]); + if (value < 0 || value > 300) { + c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); + return; } } - - if (!current_flag && bst == Stance::Unknown) { - c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command"); + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); return; } + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None) + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; + } + + if (!current_check && (value == Stance::Unknown || (value != Stance::Passive && value != Stance::Balanced && value != Stance::Aggressive))) { + c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command"); + return; + } for (auto bot_iter : sbl) { if (!bot_iter) continue; - if (!current_flag) { - bot_iter->SetBotStance(bst); + if (!current_check) { + bot_iter->SetBotStance(value); bot_iter->Save(); } @@ -1135,50 +1093,138 @@ void bot_command_stance(Client *c, const Seperator *sep) } } -void bot_command_stop_melee_level(Client *c, const Seperator *sep) +void bot_command_stop_melee_level(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_stop_melee_level", sep->arg[0], "botstopmeleelevel")) + if (helper_command_alias_fail(c, "bot_command_stop_melee_level", sep->arg[0], "botstopmeleelevel")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | reset | sync | value: 0-255]", sep->arg[0]); + c->Message(Chat::White, "usage: %s [current | reset | sync | value: 0-255] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "note: Only caster or hybrid class bots may be modified"); c->Message(Chat::White, "note: Use [reset] to set stop melee level to server rule"); c->Message(Chat::White, "note: Use [sync] to set stop melee level to current bot level"); return; } - auto my_bot = ActionableBots::AsTarget_ByBot(c); - if (!my_bot) { - c->Message(Chat::White, "You must a bot that you own to use this command"); - return; - } - if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { - c->Message(Chat::White, "You must a caster or hybrid class bot to use this command"); - return; - } + const int ab_mask = ActionableBots::ABM_Type1; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; uint8 sml = RuleI(Bots, CasterStopMeleeLevel); + bool sync_sml = false; + bool reset_sml = false; + bool current_check = false; if (sep->IsNumber(1)) { + ab_arg = 2; sml = Strings::ToInt(sep->arg[1]); + if (sml <= 0 || sml > 255) { + c->Message(Chat::White, "You must provide a value between 0-255."); + return; + } } else if (!strcasecmp(sep->arg[1], "sync")) { - sml = my_bot->GetLevel(); + ab_arg = 2; + sync_sml = true; } - else if (!strcasecmp(sep->arg[1], "current")) { - c->Message(Chat::White, "My current melee stop level is %u", my_bot->GetStopMeleeLevel()); + else if (!arg1.compare("current")) { + ab_arg = 2; + current_check = true; + } + else if (!strcasecmp(sep->arg[1], "reset")) { + ab_arg = 2; + reset_sml = true; + } + else { + c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); return; } - else if (strcasecmp(sep->arg[1], "reset")) { - c->Message(Chat::White, "A [current] or [reset] argument, or numeric [value] is required to use this command"); + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - // [reset] falls through with initialization value - my_bot->SetStopMeleeLevel(sml); - database.botdb.SaveStopMeleeLevel(my_bot->GetBotID(), sml); + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + + for (auto my_bot : sbl) { + if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { + c->Message( + Chat::White, + fmt::format( + "{} says, 'This command only works on caster or hybrid classes.'", + my_bot->GetCleanName() + ).c_str() + ); + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (sync_sml) { + sml = my_bot->GetLevel(); + } - c->Message(Chat::White, "Successfully set stop melee level for %s to %u", my_bot->GetCleanName(), sml); + if (reset_sml) { + sml = my_bot->GetDefaultBotBaseSetting(BotBaseSettings::StopMeleeLevel); + } + + if (current_check) { + c->Message( + Chat::White, + fmt::format( + "{} says, 'My current stop melee level is {}.'", + my_bot->GetCleanName(), + my_bot->GetStopMeleeLevel() + ).c_str() + ); + continue; + } + else { + my_bot->SetStopMeleeLevel(sml); + ++success_count; + } + } + + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::White, + fmt::format( + "{} says, 'My stop melee level was {} to {}.'", + first_found->GetCleanName(), + reset_sml ? "reset" : "set", + sml + ).c_str() + ); + } + else { + c->Message( + Chat::White, + fmt::format( + "{} of your bots {} their stop melee{}'", // level to {}. + success_count, + reset_sml ? "reset" : "set", + fmt::format("{}", reset_sml ? "." : fmt::format(" level to {}.", sml).c_str()).c_str(), + sml + ).c_str() + ); + } + } } void bot_command_summon(Client *c, const Seperator *sep) @@ -1188,19 +1234,24 @@ void bot_command_summon(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } + const int ab_mask = ActionableBots::ABM_Type1; - std::string class_race_arg = sep->arg[1]; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -1245,34 +1296,44 @@ void bot_command_summon(Client *c, const Seperator *sep) } } -void bot_command_toggle_archer(Client *c, const Seperator *sep) +void bot_command_toggle_ranged(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_toggle_archer", sep->arg[0], "bottogglearcher")) { + if (helper_command_alias_fail(c, "bot_command_toggle_ranged", sep->arg[0], "bottoggleranged")) { return; } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "note: Toggles a ranged bot between melee and ranged weapon use"); return; } - const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); + + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; - bool archer_state = false; - bool toggle_archer = true; + bool ranged_state = false; + bool toggle_ranged = true; int ab_arg = 1; if (!arg1.compare("on")) { - archer_state = true; - toggle_archer = false; - ab_arg = 2; + ranged_state = true; + toggle_ranged = false; + ++ab_arg; } else if (!arg1.compare("off")) { - toggle_archer = false; - ab_arg = 2; + toggle_ranged = false; + ++ab_arg; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]) == ActionableBots::ABT_None) { + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -1281,17 +1342,18 @@ void bot_command_toggle_archer(Client *c, const Seperator *sep) continue; } - if (toggle_archer) { - bot_iter->SetBotArcherySetting(!bot_iter->IsBotArcher(), true); - } - else { - bot_iter->SetBotArcherySetting(archer_state, true); + if (bot_iter->GetBotRangedValue() < RuleI(Combat, MinRangedAttackDist)) { + c->Message(Chat::Yellow, "%s does not have proper weapons or ammo to be at range.", bot_iter->GetCleanName()); + continue; } - bot_iter->ChangeBotArcherWeapons(bot_iter->IsBotArcher()); - if (bot_iter->GetClass() == Class::Ranger && bot_iter->GetLevel() >= 61) { - bot_iter->SetRangerAutoWeaponSelect(bot_iter->IsBotArcher()); + if (toggle_ranged) { + bot_iter->SetBotRangedSetting(!bot_iter->IsBotRanged()); } + else { + bot_iter->SetBotRangedSetting(ranged_state); + } + bot_iter->ChangeBotRangedWeapons(bot_iter->IsBotRanged()); } } @@ -1300,7 +1362,7 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_toggle_helm", sep->arg[0], "bottogglehelm")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; @@ -1313,11 +1375,11 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) if (!arg1.compare("on")) { helm_state = true; toggle_helm = false; - ab_arg = 2; + ++ab_arg; } else if (!arg1.compare("off")) { toggle_helm = false; - ab_arg = 2; + ++ab_arg; } std::list sbl; @@ -1336,9 +1398,6 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) bot_iter->SetShowHelm(helm_state); if (ab_type != ActionableBots::ABT_All) { - if (!database.botdb.SaveHelmAppearance(bot_iter->GetBotID(), bot_iter->GetShowHelm())) { - return; - } EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); SpawnAppearance_Struct* saptr = (SpawnAppearance_Struct*)outapp->pBuffer; @@ -1355,13 +1414,6 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) } if (ab_type == ActionableBots::ABT_All) { - if (toggle_helm) { - database.botdb.ToggleAllHelmAppearances(c->CharacterID()); - } - else { - database.botdb.SaveAllHelmAppearances(c->CharacterID(), helm_state); - } - c->Message(Chat::White, "%s all of your bot show helm flags", toggle_helm ? "Toggled" : (helm_state ? "Set" : "Cleared")); } else { @@ -1439,7 +1491,6 @@ void bot_command_update(Client *c, const Seperator *sep) if (!bot_iter || bot_iter->IsEngaged() || bot_iter->GetLevel() == c->GetLevel()) continue; - bot_iter->SetPetChooser(false); bot_iter->CalcBotStats(c->GetBotOption(Client::booStatsUpdate)); bot_iter->SendAppearancePacket(AppearanceType::WhoLevel, bot_iter->GetLevel(), true, true); ++bot_count; diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp new file mode 100644 index 0000000000..e94bea5f1a --- /dev/null +++ b/zone/bot_commands/bot_settings.cpp @@ -0,0 +1,43 @@ +#include "../bot_command.h" + +void bot_command_bot_settings(Client* c, const Seperator* sep) +{ + std::list subcommand_list; + subcommand_list.push_back("behindmob"); + subcommand_list.push_back("casterrange"); + subcommand_list.push_back("copysettings"); + subcommand_list.push_back("defaultsettings"); + subcommand_list.push_back("enforcespelllist"); + subcommand_list.push_back("follow"); + subcommand_list.push_back("followdistance"); + subcommand_list.push_back("illusionblock"); + subcommand_list.push_back("maxmeleerange"); + subcommand_list.push_back("owneroption"); + subcommand_list.push_back("petsettype"); + subcommand_list.push_back("sithppercent"); + subcommand_list.push_back("sitincombat"); + subcommand_list.push_back("sitmanapercent"); + subcommand_list.push_back("sithppercent"); + subcommand_list.push_back("spellaggrocheck"); + subcommand_list.push_back("spelldelays"); + subcommand_list.push_back("spellengagedpriority"); + subcommand_list.push_back("spellholds"); + subcommand_list.push_back("spellidlepriority"); + subcommand_list.push_back("spellmaxhppct"); + subcommand_list.push_back("spellmaxmanapct"); + subcommand_list.push_back("spellmaxthresholds"); + subcommand_list.push_back("spellminhppct"); + subcommand_list.push_back("spellminmanapct"); + subcommand_list.push_back("spellminthresholds"); + subcommand_list.push_back("spellpursuepriority"); + subcommand_list.push_back("spelltargetcount"); + subcommand_list.push_back("spelllist"); + subcommand_list.push_back("stance"); + subcommand_list.push_back("togglehelm"); + subcommand_list.push_back("bottoggleranged"); + + if (helper_command_alias_fail(c, "bot_command_bot_settings", sep->arg[0], "botsettings")) + return; + + helper_send_available_subcommands(c, "botsettings", subcommand_list); +} diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp new file mode 100644 index 0000000000..77d3ab23bd --- /dev/null +++ b/zone/bot_commands/cast.cpp @@ -0,0 +1,301 @@ +#include "../bot_command.h" + +void bot_command_cast(Client* c, const Seperator* sep) +{ + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc)" + }; + + std::vector notes = + { + "- This will interrupt any spell currently being cast by bots told to use the command.", + "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells" + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To tell everyone to Nuke the target:", + fmt::format( + "{} {} spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + std::vector examples_two = + { + "To tell all Enchanters to slow the target:", + fmt::format( + "{} {} byclass {}", + sep->arg[0], + Class::Enchanter, + c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) + ), + fmt::format( + "{} {} byclass {}", + sep->arg[0], + Class::Enchanter, + BotSpellTypes::Slow + ) + }; + std::vector examples_three = + { + "To tell Clrbot to resurrect the targeted corpse:", + fmt::format( + "{} {} byname Clrbot", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Resurrect) + ), + fmt::format( + "{} {} byname Clrbot", + sep->arg[0], + BotSpellTypes::Resurrect + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 2; + uint16 spellType = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if ( + spellType == BotSpellTypes::PetBuffs || + spellType == BotSpellTypes::PetCompleteHeals || + spellType == BotSpellTypes::PetFastHeals || + spellType == BotSpellTypes::PetHoTHeals || + spellType == BotSpellTypes::PetRegularHeals || + spellType == BotSpellTypes::PetVeryFastHeals + ) { + c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); + return; + } + + Mob* tar = c->GetTarget(); + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { + if (!tar) { + c->Message(Chat::Yellow, "You need a target for that."); + return; + } + + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { + c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); + return; + } + + if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + c->Message(Chat::Yellow, "[%s] is an invalid target.", tar->GetCleanName()); + return; + } + } + } + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + switch (spellType) { + case BotSpellTypes::Stun: + case BotSpellTypes::AEStun: + if (tar->GetSpecialAbility(SpecialAbility::StunImmunity)) { + c->Message(Chat::Yellow, "[%s] is immune to stuns.", tar->GetCleanName()); + return; + } + + break; + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + c->Message(Chat::Yellow, "[%s] is an invalid target. I can only resurrect player corpses.", tar->GetCleanName()); + return; + } + + break; + default: + break; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + BotSpell botSpell; + botSpell.SpellId = 0; + botSpell.SpellIndex = 0; + botSpell.ManaCost = 0; + bool isSuccess = false; + uint16 successCount = 0; + Bot* firstFound = nullptr; + + for (auto bot_iter : sbl) { + if (!bot_iter->IsInGroupOrRaid()) { + continue; + } + + /* + TODO bot rewrite - + FIX: Snares, Group Cures, OOC Song, Precombat, HateRedux, Fear/AE Fear + ICB (SK) casting hate on friendly but not hostile? + NEED TO CHECK: precombat, AE Dispel, AE Lifetap + DO I NEED A PBAE CHECK??? + */ + if (bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { + continue; + } + + Mob* newTar = tar; + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { + newTar = bot_iter; + } + + if (!newTar) { + continue; + } + + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { + bot_iter->BotGroupSay( + bot_iter, + fmt::format( + "I cannot attack [{}].", + newTar->GetCleanName() + ).c_str() + ); + + continue; + } + + LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + + bot_iter->SetCommandedSpell(true); + + if (bot_iter->AICastSpell(newTar, 100, spellType)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + + bot_iter->SetCommandedSpell(false); + continue; + } + + if (!isSuccess) { + c->Message( + Chat::Yellow, + fmt::format( + "No bots are capable of casting [{}] on {}.", + c->GetSpellTypeNameByID(spellType), + tar ? tar->GetCleanName() : "your target" + ).c_str() + ); + } + else { + c->Message( Chat::Yellow, + fmt::format( + "{} {} [{}]{}", + ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), + ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), + c->GetSpellTypeNameByID(spellType), + tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." + ).c_str() + ); + } +} diff --git a/zone/bot_commands/caster_range.cpp b/zone/bot_commands/caster_range.cpp index 2c1faf639a..447c739db7 100644 --- a/zone/bot_commands/caster_range.cpp +++ b/zone/bot_commands/caster_range.cpp @@ -7,7 +7,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "note: Can only be used for Casters or Hybrids."); c->Message(Chat::White, "note: Use [current] to check the current setting."); c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target."); @@ -15,6 +15,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) c->Message(Chat::White, "note: This is set to (90) units by default."); return; } + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; @@ -23,7 +24,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) uint32 crange = 0; if (sep->IsNumber(1)) { - ab_arg = 2; + ++ab_arg; crange = atoi(sep->arg[1]); if (crange < 0 || crange > 300) { c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); @@ -31,7 +32,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) } } else if (!arg1.compare("current")) { - ab_arg = 2; + ++ab_arg; current_check = true; } else { @@ -78,8 +79,6 @@ void bot_command_caster_range(Client* c, const Seperator* sep) else { my_bot->SetBotCasterRange(crange); ++success_count; - - database.botdb.SaveBotCasterRange(my_bot->GetBotID(), crange); } } if (!current_check) { diff --git a/zone/bot_commands/class_race_list.cpp b/zone/bot_commands/class_race_list.cpp new file mode 100644 index 0000000000..84c611ab38 --- /dev/null +++ b/zone/bot_commands/class_race_list.cpp @@ -0,0 +1,113 @@ +#include "../bot_command.h" + +void bot_command_class_race_list(Client* c, const Seperator* sep) +{ + const std::string class_substrs[17] = { + "", + "WAR", "CLR", "PAL", "RNG", + "SHD", "DRU", "MNK", "BRD", + "ROG", "SHM", "NEC", "WIZ", + "MAG", "ENC", "BST", "BER" + }; + + const std::string race_substrs[17] = { + "", + "HUM", "BAR", "ERU", "ELF", + "HIE", "DEF", "HEF", "DWF", + "TRL", "OGR", "HFL", "GNM", + "IKS", "VAH", "FRG", "DRK" + }; + + const uint16 race_values[17] = { + Race::Doug, + Race::Human, Race::Barbarian, Race::Erudite, Race::WoodElf, + Race::HighElf, Race::DarkElf, Race::HalfElf, Race::Dwarf, + Race::Troll, Race::Ogre, Race::Halfling, Race::Gnome, + Race::Iksar, Race::VahShir, Race::Froglok2, Race::Drakkin + }; + + std::string window_text; + std::string message_separator; + int object_count = 0; + const int object_max = 4; + + window_text.append( + fmt::format( + "Classes{}", + DialogueWindow::Break() + ) + ); + + window_text.append( + fmt::format( + "--------------------------------------------------------------------", + DialogueWindow::Break() + ) + ); + + window_text.append(DialogueWindow::Break()); + + message_separator = " "; + object_count = 0; + for (int i = 0; i <= 15; ++i) { + window_text.append(message_separator); + + if (object_count >= object_max) { + window_text.append(DialogueWindow::Break()); + object_count = 0; + } + + window_text.append( + fmt::format("{} ({})", + class_substrs[i + 1], + (i + 1) + ) + ); + + ++object_count; + message_separator = ", "; + } + + window_text.append(DialogueWindow::Break(2)); + + window_text.append( + fmt::format( + "Races{}", + DialogueWindow::Break() + ) + ); + + window_text.append( + fmt::format( + "--------------------------------------------------------------------", + DialogueWindow::Break() + ) + ); + + window_text.append(DialogueWindow::Break()); + + message_separator = " "; + object_count = 0; + for (int i = 0; i <= 15; ++i) { + window_text.append(message_separator); + + if (object_count >= object_max) { + window_text.append(DialogueWindow::Break()); + object_count = 0; + } + + window_text.append( + fmt::format("{} ({})", + race_substrs[i + 1], + race_values[i + 1] + ) + ); + + ++object_count; + message_separator = ", "; + } + + c->SendPopupToClient("Bot Creation Options", window_text.c_str()); + + return; +} diff --git a/zone/bot_commands/click_item.cpp b/zone/bot_commands/click_item.cpp index b236815d8a..80375b52c0 100644 --- a/zone/bot_commands/click_item.cpp +++ b/zone/bot_commands/click_item.cpp @@ -8,7 +8,7 @@ void bot_command_click_item(Client* c, const Seperator* sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "This will cause the selected bots to click the item in the given slot ID."); c->Message(Chat::White, "Use ^invlist to see their items along with slot IDs."); return; @@ -25,7 +25,7 @@ void bot_command_click_item(Client* c, const Seperator* sep) uint32 slot_id = 0; if (sep->IsNumber(1)) { - ab_arg = 2; + ++ab_arg; slot_id = atoi(sep->arg[1]); if (slot_id < EQ::invslot::EQUIPMENT_BEGIN || slot_id > EQ::invslot::EQUIPMENT_END) { c->Message(Chat::Yellow, "You must specify a valid inventory slot from 0 to 22. Use %s help for more information", sep->arg[0]); diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp new file mode 100644 index 0000000000..fafead14c1 --- /dev/null +++ b/zone/bot_commands/copy_settings.cpp @@ -0,0 +1,366 @@ +#include "../bot_command.h" + +void bot_command_copy_settings(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_copy_settings", sep->arg[0], "copysettings")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Copies settings from one bot to another bot" + }; + + std::vector notes = + { + "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" + }; + + std::vector example_format = + { + fmt::format( + "{} [from] [to] [option]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To copy all settings from BotA to BotB:", + fmt::format( + "{} BotA BotB all", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To copy only Nuke spelltypesettings from BotA to BotB:", + fmt::format( + "{} BotA BotB spelltypesettings {}", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} BotA BotB spelltypesettings {}", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + }; + std::vector examples_three = { }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid", + "targetgroup, namesgroup, healrotationtargets", + "mmr, byclass, byrace, spawned" + }; + + std::vector options = + { + "all, misc, spellsettings, spelltypesettings", + "holds, delays, minthresholds, maxthresholds", + "minmanapct, maxmanapct, minhppct, maxhppct", + "idlepriority, engagedpriority, pursuepriority", + "aggrochecks, targetcounts" + }; + std::vector options_one = + { + "[spellsettings] will copy ^spellsettings options", + "[spelltypesettings] copies all spell type settings", + "[all] copies all settings" + }; + std::vector options_two = + { + "[misc] copies all miscellaneous options such as:", + "- ^showhelm, ^followd, ^stopmeleelevel", + "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", + "- ^behindmob, ^casterrange, ^illusionblock", + "- ^sitincombat, ^sithppercent and ^sitmanapercent", + + }; + std::vector options_three = + { + "The remaining options copy that specific type" + }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 2; + bool validOption = false; + uint16 spellType = UINT16_MAX; + std::vector options = + { + "all", + "misc" + "spellsettings", + "spelltypesettings", + "holds", + "delays", + "minthresholds", + "maxthresholds", + "aggrochecks", + "minmanapct", + "maxmanapct", + "minhppct", + "maxhppct", + "idlepriority", + "engagedpriority", + "pursuepriority", + "targetcounts" + }; + + for (int i = 0; i < options.size(); i++) { + if (sep->arg[3] == options[i]) { + if (options[i] != "all" && options[i] != "misc" && options[i] != "spellsettings") { + + if (sep->IsNumber(4) || c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX) { + if (sep->IsNumber(4)) { + spellType = atoi(sep->arg[4]); + } + + if (c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(sep->arg[4]); + } + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + } + else if ( + (options[i] == "all" || options[i] == "misc" || options[i] == "spellsettings") && + ((sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX)) + ) { + break; + } + + validOption = true; + break; + } + } + + if (!validOption) { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + auto from = entity_list.GetBotByBotName(sep->arg[1]); + + if (!from) { + c->Message(Chat::Yellow, "Could not find %s.", sep->arg[1]); + return; + } + + if (!from->IsBot()) { + c->Message(Chat::Yellow, "%s is not a bot.", from->GetCleanName()); + return; + } + + if (!from->GetOwner()) { + c->Message(Chat::Yellow, "Could not find %s's owner.", from->GetCleanName()); + } + + if (RuleB(Bots, CopySettingsOwnBotsOnly) && from->GetOwner() != c) { + c->Message(Chat::Yellow, "You name a bot you own to use this command."); + return; + } + + if (!RuleB(Bots, AllowCopySettingsAnon) && from->GetOwner() != c && from->GetOwner()->CastToClient()->GetAnon()) { + c->Message(Chat::Yellow, "You name a bot you own to use this command."); + return; + } + + auto to = ActionableBots::AsNamed_ByBot(c, sep->arg[2]); + + if (!to) { + c->Message(Chat::Yellow, "Could not find %s.", sep->arg[1]); + return; + } + + if (!to->IsBot()) { + c->Message(Chat::Yellow, "%s is not a bot.", to->GetCleanName()); + return; + } + + if (!to->GetOwner()) { + c->Message(Chat::Yellow, "Could not find %s's owner.", to->GetCleanName()); + } + + if (to->GetOwner() != c) { + c->Message(Chat::Yellow, "You must name a spawned bot that you own to use this command."); + return; + } + + if (to == from) { + c->Message(Chat::Yellow, "You cannot copy to the same bot that you're copying from."); + return; + } + + std::string output = ""; + + if (!strcasecmp(sep->arg[3], "misc")) { + from->CopySettings(to, BotSettingCategories::BaseSetting); + output = "Miscellaneous"; + } + else if (!strcasecmp(sep->arg[3], "holds")) { + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellHold); + } + else if (!strcasecmp(sep->arg[3], "delays")) { + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellDelay); + } + else if (!strcasecmp(sep->arg[3], "minthresholds")) { + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellMinThreshold); + } + else if (!strcasecmp(sep->arg[3], "maxthresholds")) { + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellMaxThreshold); + } + else if (!strcasecmp(sep->arg[3], "aggrochecks")) { + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeAggroCheck); + } + else if (!strcasecmp(sep->arg[3], "minmanapct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMinManaPct); + } + else if (!strcasecmp(sep->arg[3], "maxmanapct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMaxManaPct); + } + else if (!strcasecmp(sep->arg[3], "minhppct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMinHPPct); + } + else if (!strcasecmp(sep->arg[3], "maxhppct")) { + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMaxHPPct); + } + else if (!strcasecmp(sep->arg[3], "idlepriority")) { + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeIdlePriority); + } + else if (!strcasecmp(sep->arg[3], "engagedpriority")) { + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeEngagedPriority); + } + else if (!strcasecmp(sep->arg[3], "pursuepriority")) { + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypePursuePriority); + } + else if (!strcasecmp(sep->arg[3], "targetcounts")) { + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeAEOrGroupTargetCount); + } + else if (!strcasecmp(sep->arg[3], "spellsettings")) { + from->CopyBotSpellSettings(to); + output = "^spellsettings"; + } + else if (!strcasecmp(sep->arg[3], "spelltypesettings")) { + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + output = "spell type"; + } + else if (!strcasecmp(sep->arg[3], "all")) { + from->CopySettings(to, BotSettingCategories::BaseSetting); + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + from->CopyBotSpellSettings(to); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + c->Message( + Chat::Green, + fmt::format( + "{}'s{}{} settings were copied to {}.", + from->GetCleanName(), + ( + spellType != UINT16_MAX ? + fmt::format(" [{}] ", + c->GetSpellTypeNameByID(spellType) + ) + : " " + ), + output, + to->GetCleanName() + ).c_str() + ); +} diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp new file mode 100644 index 0000000000..1f7c854cad --- /dev/null +++ b/zone/bot_commands/default_settings.cpp @@ -0,0 +1,473 @@ +#include "../bot_command.h" + +void bot_command_default_settings(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_default_settings", sep->arg[0], "defaultsettings")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Restores a bot's setting(s) to defaults" + }; + + std::vector notes = + { + "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" + }; + + std::vector example_format = + { + fmt::format( + "{} [option] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To restore delays for Clerics:", + fmt::format( + "{} delays byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To restore only Snare delays for BotA:", + fmt::format( + "{} delays {} byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} delays {} byname BotA", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_three = { }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = + { + "all, misc, spellsettings, spelltypesettings, holds, delays, minthresholds, maxthresholds minmanapct, maxmanapct, minhppct, maxhppct, idlepriority, engagedpriority, pursuepriority, aggrocheck, targetcounts" + }; + std::vector options_one = + { + "[spellsettings] will restore ^spellsettings options", + "[spelltypesettings] restores all spell type settings", + "[all] restores all settings" + }; + std::vector options_two = + { + "[misc] restores all miscellaneous options such as:", + "- ^showhelm, ^followd, ^stopmeleelevel", + "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", + "- ^behindmob, ^casterrange, ^illusionblock", + "- ^sitincombat, ^sithppercent and ^sitmanapercent", + + }; + std::vector options_three = + { + "The remaining options restore that specific type" + }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 2; + bool validOption = false; + uint16 spellType = UINT16_MAX; + std::vector options = + { + "all", + "misc" + "spellsettings", + "spelltypesettings", + "holds", + "delays", + "minthresholds", + "maxthresholds", + "aggrocheck", + "minmanapct", + "maxmanapct", + "minhppct", + "maxhppct", + "idlepriority", + "engagedpriority", + "pursuepriority", + "targetcounts" + }; + + for (int i = 0; i < options.size(); i++) { + if (sep->arg[1] == options[i]) { + if (options[i] != "all" && options[i] != "misc" && options[i] != "spellsettings") { + + if (sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { + if (sep->IsNumber(2)) { + spellType = atoi(sep->arg[2]); + } + + if (c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(sep->arg[2]); + } + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + + ++ab_arg; + } + } + else if ( + (options[i] == "all" || options[i] == "misc" || options[i] == "spellsettings") && + ((sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX)) + ) { + break; + } + + validOption = true; + break; + } + } + + if (!validOption) { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + std::string output = ""; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + + if (!strcasecmp(sep->arg[1], "misc")) { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + output = "miscellanous settings"; + } + } + else if (!strcasecmp(sep->arg[1], "holds")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + } + } + + output = "hold settings"; + } + else if (!strcasecmp(sep->arg[1], "delays")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + } + } + + output = "delay settings"; + } + else if (!strcasecmp(sep->arg[1], "minthresholds")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + } + } + + output = "minimum threshold settings"; + } + else if (!strcasecmp(sep->arg[1], "maxthresholds")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + } + } + + output = "maximum threshold settings"; + } + else if (!strcasecmp(sep->arg[1], "aggrochecks")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + } + } + + output = "aggro check settings"; + } + else if (!strcasecmp(sep->arg[1], "minmanapct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + } + } + + output = "min mana settings"; + } + else if (!strcasecmp(sep->arg[1], "maxmanapct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + } + } + + output = "max mana settings"; + } + else if (!strcasecmp(sep->arg[1], "minhppct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + } + } + + output = "min hp settings"; + } + else if (!strcasecmp(sep->arg[1], "maxhppct")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + } + } + + output = "max hp settings"; + } + else if (!strcasecmp(sep->arg[1], "idlepriority")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + } + } + + output = "idle priority settings"; + } + else if (!strcasecmp(sep->arg[1], "engagedpriority")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + } + } + + output = "engaged priority settings"; + } + else if (!strcasecmp(sep->arg[1], "pursuepriority")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + } + } + + output = "pursue priority settings"; + } + else if (!strcasecmp(sep->arg[1], "targetcounts")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + } + } + + output = "ae/group count settings"; + } + else if (!strcasecmp(sep->arg[1], "spellsettings")) { + my_bot->ResetBotSpellSettings(); + output = "^spellsettings"; + } + else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { + if (spellType != UINT16_MAX) { + my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); + my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); + my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); + my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); + my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); + my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); + my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); + my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); + my_bot->SetSpellTypeAEOrGroupTargetCount(spellType, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + } + } + + output = "spell type settings"; + } + else if (!strcasecmp(sep->arg[1], "all")) { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + } + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + }; + + my_bot->ResetBotSpellSettings(); + + output = "settings"; + + } + + ++success_count; + } + + if (success_count == 1) { + c->Message( + Chat::Green, + fmt::format( + "{} says, '{}{} were restored.'", + first_found->GetCleanName(), + ( + spellType != UINT16_MAX ? + fmt::format("My [{}] ", + c->GetSpellTypeNameByID(spellType) + ) + : "My " + ), + output + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bot's{}{} were restored.", + success_count, + ( + spellType != UINT16_MAX ? + fmt::format(" [{}] ", + c->GetSpellTypeNameByID(spellType) + ) + : " " + ), + output + ).c_str() + ); + } +} diff --git a/zone/bot_commands/defensive.cpp b/zone/bot_commands/defensive.cpp index a2edc3e6e9..081e07f342 100644 --- a/zone/bot_commands/defensive.cpp +++ b/zone/bot_commands/defensive.cpp @@ -6,22 +6,27 @@ void bot_command_defensive(Client *c, const Seperator *sep) if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Stance) || helper_command_alias_fail(c, "bot_command_defensive", sep->arg[0], "defensive")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_Stance); return; } const int ab_mask = ActionableBots::ABM_Type1; - std::string class_race_arg = sep->arg[1]; + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); int success_count = 0; diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 65ec73f8b0..3ff0cb44ba 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -1,11 +1,11 @@ #include "../bot_command.h" -void bot_command_follow(Client *c, const Seperator *sep) +void bot_command_follow(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | mmr | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); c->Message(Chat::White, "usage: %s chain", sep->arg[0]); return; } @@ -26,13 +26,22 @@ void bot_command_follow(Client *c, const Seperator *sep) } else if (!optional_arg.compare("reset")) { reset = true; - ab_arg = 2; - name_arg = 3; + ++ab_arg; + ++name_arg ; } else { - target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single); + //target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single); + target_mob = c->GetTarget(); if (!target_mob) { - c->Message(Chat::White, "You must a friendly mob to use this command"); + c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); + return; + } + else if (!target_mob->IsBot() && !target_mob->IsClient()) { + c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); + return; + } + else if ((target_mob->GetGroup() && target_mob->GetGroup() != c->GetGroup()) || (target_mob->GetRaid() && target_mob->GetRaid() != c->GetRaid())) { + c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); return; } } @@ -51,29 +60,54 @@ void bot_command_follow(Client *c, const Seperator *sep) sbl.remove(nullptr); for (auto bot_iter : sbl) { bot_iter->WipeHateList(); - auto my_group = bot_iter->GetGroup(); - if (my_group) { - if (reset) { - if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter) - bot_iter->SetFollowID(c->GetID()); - else - bot_iter->SetFollowID(my_group->GetLeader()->GetID()); + if (!bot_iter->GetGroup() && !bot_iter->GetRaid()) { + bot_iter->SetFollowID(0); + bot_iter->SetManualFollow(false); + } + else { + if (reset) { + bot_iter->SetFollowID(c->GetID()); bot_iter->SetManualFollow(false); } else { - if (bot_iter == target_mob) - bot_iter->SetFollowID(c->GetID()); - else + if (target_mob->IsGrouped() || target_mob->IsRaidGrouped()) { bot_iter->SetFollowID(target_mob->GetID()); - - bot_iter->SetManualFollow(true); + bot_iter->SetManualFollow(true); + } + else if (bot_iter == target_mob) { + bot_iter->SetFollowID(c->GetID()); + bot_iter->SetManualFollow(true); + } + else { + bot_iter->SetFollowID(0); + bot_iter->SetManualFollow(false); + } } } - else { - bot_iter->SetFollowID(0); - bot_iter->SetManualFollow(false); - } + //auto my_group = bot_iter->GetGroup(); + //if (my_group) { + // if (reset) { + // if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter) + // bot_iter->SetFollowID(c->GetID()); + // else + // bot_iter->SetFollowID(my_group->GetLeader()->GetID()); + // + // bot_iter->SetManualFollow(false); + // } + // else { + // if (bot_iter == target_mob) + // bot_iter->SetFollowID(c->GetID()); + // else + // bot_iter->SetFollowID(target_mob->GetID()); + // + // bot_iter->SetManualFollow(true); + // } + //} + //else { + // bot_iter->SetFollowID(0); + // bot_iter->SetManualFollow(false); + //} if (!bot_iter->GetPet()) continue; @@ -81,31 +115,37 @@ void bot_command_follow(Client *c, const Seperator *sep) bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); } + Mob* follow_mob = nullptr; if (sbl.size() == 1) { - Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); - Bot::BotGroupSay( - sbl.front(), + follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + c->Message( + Chat::White, fmt::format( "Following {}.", - follow_mob ? follow_mob->GetCleanName() : "no one" + follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); - } else { + } + else { if (reset) { c->Message( Chat::White, fmt::format( - "{} of your bots are following their default assignments.", + "{} of your bots are following you.", sbl.size() ).c_str() ); - } else { + } + else { + if (!sbl.empty()) { + follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + } c->Message( Chat::White, fmt::format( "{} of your bots are following {}.", sbl.size(), - target_mob->GetCleanName() + follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); } diff --git a/zone/bot_commands/guard.cpp b/zone/bot_commands/guard.cpp index 27a9ed3430..1cacd27426 100644 --- a/zone/bot_commands/guard.cpp +++ b/zone/bot_commands/guard.cpp @@ -7,7 +7,7 @@ void bot_command_guard(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); return; } const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2); @@ -20,8 +20,8 @@ void bot_command_guard(Client *c, const Seperator *sep) if (!clear_arg.compare("clear")) { clear = true; - ab_arg = 2; - name_arg = 3; + ++ab_arg; + ++name_arg; } std::string class_race_arg = sep->arg[ab_arg]; diff --git a/zone/bot_commands/hold.cpp b/zone/bot_commands/hold.cpp index 49f0f516fb..0fad872dc7 100644 --- a/zone/bot_commands/hold.cpp +++ b/zone/bot_commands/hold.cpp @@ -7,7 +7,7 @@ void bot_command_hold(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: clear]) [actionable: byname | ownergroup | ownerraid | namesgroup | healrotation | mmr | byclass | byrace | default: spawned] ([actionable_name])", sep->arg[0]); return; } const int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_Type2); @@ -20,8 +20,8 @@ void bot_command_hold(Client *c, const Seperator *sep) if (!clear_arg.compare("clear")) { clear = true; - ab_arg = 2; - name_arg = 3; + ++ab_arg; + ++name_arg; } std::string class_race_arg = sep->arg[ab_arg]; diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp new file mode 100644 index 0000000000..0a3625046a --- /dev/null +++ b/zone/bot_commands/illusion_block.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_illusion_block(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_illusion_block", sep->arg[0], "illusionblock")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will block the illusion effects of spells cast by players or bots." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set BotA to block illusions:", + fmt::format( + "{} 1 byname BotA", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the illusion block status for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} block illusions.'", + my_bot->GetCleanName(), + my_bot->GetIllusionBlock() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetIllusionBlock(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} block illusions.'", + first_found->GetCleanName(), + first_found->GetIllusionBlock() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} block illusions.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/inventory.cpp b/zone/bot_commands/inventory.cpp index b934f96a25..7371475ed3 100644 --- a/zone/bot_commands/inventory.cpp +++ b/zone/bot_commands/inventory.cpp @@ -217,9 +217,18 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep) } const auto* itm = inst->GetItem(); + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemInst); + linker.SetItemInst(inst); if (inst && itm && c->CheckLoreConflict(itm)) { - c->MessageString(Chat::White, PICK_LORE); + c->Message( + Chat::White, + fmt::format( + "You cannot pick up {} because it is a lore item you already possess.", + linker.GenerateLink() + ).c_str() + ); return; } @@ -233,7 +242,13 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep) continue; } - c->MessageString(Chat::White, PICK_LORE); + c->Message( + Chat::White, + fmt::format( + "You cannot pick up {} because it is a lore item you already possess.", + linker.GenerateLink() + ).c_str() + ); return; } @@ -247,7 +262,7 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep) slot_id == EQ::invslot::slotRange || slot_id == EQ::invslot::slotAmmo ) { - my_bot->SetBotArcherySetting(false, true); + my_bot->SetBotRangedSetting(false); } my_bot->RemoveBotItemBySlot(slot_id); diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp new file mode 100644 index 0000000000..e876a97591 --- /dev/null +++ b/zone/bot_commands/max_melee_range.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_max_melee_range(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_max_melee_range", sep->arg[0], "maxmeleerange")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will stay at max melee range during combat." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set BotA to stay at max melee range:", + fmt::format( + "{} 1 byname BotA", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the max melee range status for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay at max melee range.'", + my_bot->GetCleanName(), + my_bot->GetMaxMeleeRange() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetMaxMeleeRange(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} stay at max melee range.'", + first_found->GetCleanName(), + first_found->GetMaxMeleeRange() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} stay at max melee range.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/mesmerize.cpp b/zone/bot_commands/mesmerize.cpp index 532a3c4049..1f51a70d39 100644 --- a/zone/bot_commands/mesmerize.cpp +++ b/zone/bot_commands/mesmerize.cpp @@ -2,42 +2,40 @@ void bot_command_mesmerize(Client *c, const Seperator *sep) { - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Mesmerize]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Mesmerize) || helper_command_alias_fail(c, "bot_command_mesmerize", sep->arg[0], "mesmerize")) - return; if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_Mesmerize); return; } - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; + bool isSuccess = false; std::list sbl; MyBots::PopulateSBL_BySpawnedBots(c, sbl); - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY); - if (!target_mob) - continue; + for (auto bot_iter : sbl) { + std::list botSpellList = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Mez, c->GetTarget(), IsAEBotSpellType(BotSpellTypes::Mez)); - if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) - continue; + for (const auto& s : botSpellList) { + if (!IsValidSpell(s.SpellId)) { + continue; + } - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; + if (!bot_iter->IsInGroupOrRaid()) { + continue; + } - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); + if (!bot_iter->CastChecks(s.SpellId, c->GetTarget(), BotSpellTypes::Mez, false, false)) { + continue; + } - break; + if (bot_iter->CommandedDoSpellCast(s.SpellIndex, c->GetTarget(), s.ManaCost)) { + bot_iter->BotGroupSay(bot_iter, "Casting %s [%s] on %s.", GetSpellName(s.SpellId), bot_iter->GetSpellTypeNameByID(BotSpellTypes::Mez), c->GetTarget()->GetCleanName()); + isSuccess = true; + } + } } - helper_no_available_bots(c, my_bot); + if (!isSuccess) { + helper_no_available_bots(c); + } } diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index f6c6ae21dd..c8b48368c4 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -19,7 +19,7 @@ void bot_command_pet_get_lost(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_pet_get_lost", sep->arg[0], "petgetlost")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | targetgroup | namesgroup | healrotation | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } int ab_mask = ActionableBots::ABM_NoFilter; @@ -98,7 +98,8 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_pet_set_type", sep->arg[0], "petsettype")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [type: water | fire | air | earth | monster] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s [type: auto | water | fire | air | earth | monster | epic] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "if set to 'auto', bots will choose their own pet type"); c->Message(Chat::White, "requires one of the following bot classes:"); c->Message(Chat::White, "Magician(1)"); return; @@ -109,29 +110,41 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) uint8 pet_type = 255; uint8 level_req = 255; - if (!pet_arg.compare("water")) { + if (!pet_arg.compare("auto")) { pet_type = 0; level_req = 1; } - else if (!pet_arg.compare("fire")) { + else if (!pet_arg.compare("water")) { pet_type = 1; + level_req = 1; + } + else if (!pet_arg.compare("fire")) { + pet_type = 2; level_req = 3; } else if (!pet_arg.compare("air")) { - pet_type = 2; + pet_type = 3; level_req = 4; } else if (!pet_arg.compare("earth")) { - pet_type = 3; + pet_type = 4; level_req = 5; } else if (!pet_arg.compare("monster")) { - pet_type = 4; + pet_type = 5; level_req = 30; } + else if (!pet_arg.compare("epic")) { + pet_type = 6; + if (!RuleB(Bots, AllowMagicianEpicPet)) { + c->Message(Chat::Yellow, "Epic pets are currently disabled for bots."); + return; + } + level_req = RuleI(Bots,AllowMagicianEpicPetLevel); + } if (pet_type == 255) { - c->Message(Chat::White, "You must specify a pet [type: water | fire | air | earth | monster]"); + c->Message(Chat::White, "You must specify a pet [type: auto | water | fire | air | earth | monster | epic]"); return; } @@ -157,7 +170,23 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) if (!bot_iter) continue; - bot_iter->SetPetChooser(true); + if (RuleI(Bots, RequiredMagicianEpicPetItemID) > 0) { + bool has_item = bot_iter->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX; + + if (!has_item) { + c->Message( + Chat::Say, + fmt::format( + "{} says, 'I require {} to cast an epic pet which I do not currently possess.'", + bot_iter->GetCleanName(), + (database.GetItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) ? database.CreateItemLink(RuleI(Bots, RequiredMagicianEpicPetItemID)) : "an item") + ).c_str() + ); + + continue; + } + } + bot_iter->SetPetChooserID(pet_type); if (bot_iter->GetPet()) { auto pet_id = bot_iter->GetPetID(); diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index 6265d141e7..fa222fcfad 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -7,15 +7,27 @@ void bot_command_pull(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } - int ab_mask = ActionableBots::ABM_OwnerGroup; // existing behavior - need to add c->IsGrouped() check and modify code if different behavior is desired + + const int ab_mask = ActionableBots::ABM_Type1; + + std::string arg1 = sep->arg[1]; + int ab_arg = 1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } std::list sbl; - if (ActionableBots::PopulateSBL(c, "ownergroup", sbl, ab_mask) == ActionableBots::ABT_None) { + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single); diff --git a/zone/bot_commands/release.cpp b/zone/bot_commands/release.cpp index 1872d43d61..bf8741d33d 100644 --- a/zone/bot_commands/release.cpp +++ b/zone/bot_commands/release.cpp @@ -5,7 +5,7 @@ void bot_command_release(Client *c, const Seperator *sep) if (helper_command_alias_fail(c, "bot_command_release", sep->arg[0], "release")) return; if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: ] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp new file mode 100644 index 0000000000..ac94a13fd8 --- /dev/null +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_sit_hp_percent(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_sit_hp_percent", sep->arg[0], "sithppercent")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "HP % threshold when bots will sit in combat if allowed." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set Clerics to sit at 45% HP:", + fmt::format( + "{} 45 byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the HP threshold for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I sit in combat whem at or below [{}%%] HP.'", + my_bot->GetCleanName(), + my_bot->GetHPWhenToMed() + ).c_str() + ); + } + else { + my_bot->SetHPWhenToMed(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I will now sit in combat whem at or below [{}%%] HP.'", + first_found->GetCleanName(), + first_found->GetHPWhenToMed() + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots will now sit in combat whem at or below [{}%%] HP.'", + success_count, + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp new file mode 100644 index 0000000000..ffcf4d8878 --- /dev/null +++ b/zone/bot_commands/sit_in_combat.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_sit_in_combat(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_sit_in_combat", sep->arg[0], "sitincombat")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will sit in combat to heal or med." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set Clerics to sit in combat:", + fmt::format( + "{} 1 byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the sit in combat state for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} sit in combat.'", + my_bot->GetCleanName(), + my_bot->GetMedInCombat() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetMedInCombat(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} sit in combat.'", + first_found->GetCleanName(), + first_found->GetMedInCombat() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} sit in combat.", + success_count, + typeValue ? "will now" : "will no longer" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp new file mode 100644 index 0000000000..85afc8047e --- /dev/null +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -0,0 +1,172 @@ +#include "../bot_command.h" + +void bot_command_sit_mana_percent(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_sit_mana_percent", sep->arg[0], "sitmanapercent")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Mana % threshold when bots will sit in combat if allowed." + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set Clerics to sit at 45% mana:", + fmt::format( + "{} 45 byclass 2", + sep->arg[0] + ) + }; + std::vector examples_two = { }; + std::vector examples_three = + { + "To check the mana threshold for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + int ab_arg = 1; + bool current_check = false; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + typeValue = atoi(sep->arg[1]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg1.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I sit in combat whem at or below [{}%%] mana.'", + my_bot->GetCleanName(), + my_bot->GetManaWhenToMed() + ).c_str() + ); + } + else { + my_bot->SetManaWhenToMed(typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I will now sit in combat whem at or below [{}%%] mana.'", + first_found->GetCleanName(), + first_found->GetManaWhenToMed() + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots will now sit in combat whem at or below [{}%%] mana.'", + success_count, + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell.cpp b/zone/bot_commands/spell.cpp index 45d9ec6ebc..833c8ef7ab 100644 --- a/zone/bot_commands/spell.cpp +++ b/zone/bot_commands/spell.cpp @@ -503,7 +503,7 @@ void bot_spell_info_dialogue_window(Client* c, const Seperator *sep) auto results = database.QueryDatabase( fmt::format( "SELECT value FROM db_str WHERE id = {} and type = 6 LIMIT 1", - spells[spell_id].effect_description_id + spells[spell_id].description_id ) ); @@ -557,7 +557,7 @@ void bot_command_enforce_spell_list(Client* c, const Seperator *sep) } bool enforce_state = (sep->argnum > 0) ? Strings::ToBool(sep->arg[1]) : !my_bot->GetBotEnforceSpellSetting(); - my_bot->SetBotEnforceSpellSetting(enforce_state, true); + my_bot->SetBotEnforceSpellSetting(enforce_state); c->Message( Chat::White, diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp new file mode 100644 index 0000000000..e9ae709eee --- /dev/null +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_aggro_checks", sep->arg[0], "spellaggrochecks")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots will cast a spell type if they think it will get them aggro" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to check aggro on nukes:", + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + std::vector examples_two = + { + "To set Shadowknights to ignore aggro checks on snares:", + fmt::format( + "{} {} 0 byclass 5", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 0 byclass 5", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_three = + { + "To check the current DoT aggro check on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] aggro check is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeAggroCheck(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + my_bot->SetSpellTypeAggroCheck(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] aggro check was [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeAggroCheck(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots [{}] their [{}] aggro check.", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue ? "enabled" : "disabled" + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp new file mode 100644 index 0000000000..3c3b153ca8 --- /dev/null +++ b/zone/bot_commands/spell_delays.cpp @@ -0,0 +1,242 @@ +#include "../bot_command.h" + +void bot_command_spell_delays(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_delays", sep->arg[0], "spelldelays")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls how long a bot will wait between casts of different spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Necromancers to an 8s DoT delay:", + fmt::format( + "{} {} 8000 byclass 11", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} 8000 byclass 11", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_two = + { + "To set all Warriors to receive Fast Heals every 2.5s:", + fmt::format( + "{} {} 2500 byclass 1", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 2500 byclass 1", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Nuke delay on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 1 || typeValue > 60000) { + c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell delay is currently [{}] seconds.'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } + else { + my_bot->SetSpellDelay(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell delay was set to [{}] seconds.'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] spell delay to [{}] seconds.", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue / 1000.00 + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp new file mode 100644 index 0000000000..03c151c3ae --- /dev/null +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -0,0 +1,240 @@ +#include "../bot_command.h" + +void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_engaged_priority", sep->arg[0], "spellengagedpriority")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets the order of spell casts when engaged in combat by spell type" + }; + + std::vector notes = + { + "-Setting a spell type to 0 will prevent that type from being cast.", + "-If 2 or more are set to the same priority they will sort by spell type ID." + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Shaman to cast slows first:", + fmt::format( + "{} {} 1 byclass 10", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) + ), + fmt::format( + "{} {} 1 byclass 10", + sep->arg[0], + BotSpellTypes::Slow + ) + }; + std::vector examples_two = + { + "To set all bots to not cast snares:", + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_three = + { + "To check the current engaged priority of dispels on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Dispel) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Dispel + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] engaged cast priority is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Engaged) + ).c_str() + ); + } + else { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] engaged cast priority was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Engaged) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] engaged cast priority to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp new file mode 100644 index 0000000000..effd97a7ae --- /dev/null +++ b/zone/bot_commands/spell_holds.cpp @@ -0,0 +1,229 @@ +#include "../bot_command.h" + +void bot_command_spell_holds(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_holds", sep->arg[0], "spellholds")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Toggles whether or not bots can cast or receive certain spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to hold DoTs:", + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_two = + { + "To check the current DoT settings on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_three = { }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell hold is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + my_bot->SetSpellHold(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell hold was [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots [{}] their [{}] spell hold.", + success_count, + typeValue ? "enabled" : "disabled", + c->GetSpellTypeNameByID(spellType) + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp new file mode 100644 index 0000000000..9bb95cf467 --- /dev/null +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -0,0 +1,240 @@ +#include "../bot_command.h" + +void bot_command_spell_idle_priority(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_idle_priority", sep->arg[0], "spellidlepriority")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets the order of spell casts when not in combat by spell type" + }; + + std::vector notes = + { + "-Setting a spell type to 0 will prevent that type from being cast.", + "-If 2 or more are set to the same priority they will sort by spell type ID." + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Clerics to cast fast heals third:", + fmt::format( + "{} {} 3 byclass 2", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 3 byclass 2", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_two = + { + "To set all bots to not cast cures:", + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + ), + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + BotSpellTypes::Cure + ) + }; + std::vector examples_three = + { + "To check the current idle priority of buffs on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Buff) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Buff + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] idle cast priority is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Idle) + ).c_str() + ); + } + else { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] idle cast priority was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Idle) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] idle cast priority to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp new file mode 100644 index 0000000000..9d94fd722c --- /dev/null +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_max_hp_pct", sep->arg[0], "spellmaxhppct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what health percentage a bot will start casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to start snaring when their health is at or below 100% HP:", + fmt::format( + "{} {} 100 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to start casting fast heals at 30% HP:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun max HP percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum HP is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMaxHPLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMaxHPLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum HP was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMaxHPLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] maximum HP to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp new file mode 100644 index 0000000000..9e22617b04 --- /dev/null +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_max_mana_pct", sep->arg[0], "spellmaxmanapct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what mana percentage a bot will stop casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to start snaring when their mana is at or below 100% HP:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to start casting fast heals at 30% mana:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun max mana percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum mana is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMaxManaLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMaxManaLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum mana was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMaxManaLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] maximum mana to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp new file mode 100644 index 0000000000..8ded61c1f1 --- /dev/null +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -0,0 +1,243 @@ +#include "../bot_command.h" + +void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_max_thresholds", sep->arg[0], "spellmaxthresholds")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what target HP % the bot will start casting different spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included)", + "are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to start snaring at 99%:", + fmt::format( + "{} {} 99 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 99 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set bot Enchbot to start casting Debuffs at 99%:", + fmt::format( + "{} {} 99 byname Enchbot", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + ), + fmt::format( + "{} {} 99 byname Enchbot", + sep->arg[0], + BotSpellTypes::Debuff + ) + }; + std::vector examples_three = + { + "To check the current Nuke max threshold on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Nuke + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum threshold is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellMaxThreshold(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum threshold was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] maximum threshold to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp new file mode 100644 index 0000000000..739ab0d1cd --- /dev/null +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_min_hp_pct", sep->arg[0], "spellminhppct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what health percentage a bot will stop casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to stop snaring when their health is below 10% HP:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to stop casting fast heals at 30% HP:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun min HP percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum HP is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMinHPLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMinHPLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum HP was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMinHPLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] minimum HP to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp new file mode 100644 index 0000000000..e83362f762 --- /dev/null +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_min_mana_pct", sep->arg[0], "spellminmanapct")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what mana percentage a bot will stop casting different spell types" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to stop snaring when their mana is below 10% HP:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Snare + ) + }; + std::vector examples_two = + { + "To set BotA to stop casting fast heals at 30% mana:", + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} 30 byname BotA", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_three = + { + "To check the current Stun min mana percent on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Stun + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum mana is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeMinManaLimit(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeMinManaLimit(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum mana was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeMinManaLimit(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] minimum mana to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp new file mode 100644 index 0000000000..6df8fc9993 --- /dev/null +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -0,0 +1,244 @@ +#include "../bot_command.h" + +void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_min_thresholds", sep->arg[0], "spellminthresholds")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Controls at what target HP % the bot will stop casting different spell types" + }; + + std::vector notes = + { + "- All pet types are based off the pet's owner's setting", + "- Any remaining types use the owner's setting when a pet is the target", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", + "- e.g., BotA is healing BotB using BotB's settings", + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to stop debuffing at 10%:", + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + ), + fmt::format( + "{} {} 10 spawned", + sep->arg[0], + BotSpellTypes::Debuff + ) + }; + std::vector examples_two = + { + "To set all Druids to stop casting DoTs at 15%:", + fmt::format( + "{} {} 15 byclass 6", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} 15 byclass 6", + sep->arg[0], + BotSpellTypes::DOT + ) + }; + std::vector examples_three = + { + "To check the current Fast Heal min threshold on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid", + "targetgroup, namesgroup, healrotationtargets", + "mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum threshold is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellMinThreshold(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellMinThreshold(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum threshold was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellMinThreshold(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] minimum threshold to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp new file mode 100644 index 0000000000..593606437c --- /dev/null +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -0,0 +1,240 @@ +#include "../bot_command.h" + +void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_pursue_priority", sep->arg[0], "spellpursuepriority")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets the order of spell casts when the mob is fleeing in combat by spell type" + }; + + std::vector notes = + { + "- Setting a spell type to 0 will prevent that type from being cast.", + "- If 2 or more are set to the same priority they will sort by spell type ID." + }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to cast nukes first:", + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ), + fmt::format( + "{} {} 1 spawned", + sep->arg[0], + BotSpellTypes::FastHeals + ) + }; + std::vector examples_two = + { + "To set all Shaman to not cast cures:", + fmt::format( + "{} {} 0 byclass 10", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + ), + fmt::format( + "{} {} 0 byclass 10", + sep->arg[0], + BotSpellTypes::Cure + ) + }; + std::vector examples_three = + { + "To check the current pursue priority of buffs on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Buff) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Buff + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] pursue cast priority is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Pursue) + ).c_str() + ); + } + else { + my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] pursue cast priority was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Pursue) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] pursue cast priority to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp new file mode 100644 index 0000000000..f87e232eeb --- /dev/null +++ b/zone/bot_commands/spell_target_count.cpp @@ -0,0 +1,236 @@ +#include "../bot_command.h" + +void bot_command_spell_target_count(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_spell_target_count", sep->arg[0], "spelltargetcount")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Decides how many eligible targets are required for an AE or group spell to cast by spell type" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [Type Shortname] [value] [actionable]" + , sep->arg[0] + ), + fmt::format( + "{} [Type ID] [value] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to AEMez with 5 or more targets:", + fmt::format( + "{} {} 5 spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::AEMez) + ), + fmt::format( + "{} {} 5 spawned", + sep->arg[0], + BotSpellTypes::AEMez + ) + }; + std::vector examples_two = + { + "To set Wizards to require 5 targets for AENukes:", + fmt::format( + "{} {} 3 byname BotA", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::AENukes) + ), + fmt::format( + "{} {} 3 byname BotA", + sep->arg[0], + BotSpellTypes::AENukes + ) + }; + std::vector examples_three = + { + "To check the current AESlow count on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::AESlow) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::AESlow + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + + return; + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 1 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 1-100."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (!first_found) { + first_found = my_bot; + } + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] target count is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + my_bot->GetSpellTypeAEOrGroupTargetCount(spellType) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeAEOrGroupTargetCount(spellType, typeValue); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] target count was set to [{}].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spellType), + first_found->GetSpellTypeAEOrGroupTargetCount(spellType) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] target count to [{}].", + success_count, + c->GetSpellTypeNameByID(spellType), + typeValue + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/suspend.cpp b/zone/bot_commands/suspend.cpp index e3198e79e9..287db739cd 100644 --- a/zone/bot_commands/suspend.cpp +++ b/zone/bot_commands/suspend.cpp @@ -6,7 +6,7 @@ void bot_command_suspend(Client *c, const Seperator *sep) return; } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: ] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } const int ab_mask = ActionableBots::ABM_NoFilter; diff --git a/zone/bot_commands/taunt.cpp b/zone/bot_commands/taunt.cpp index a15d29a5bd..866e9899c4 100644 --- a/zone/bot_commands/taunt.cpp +++ b/zone/bot_commands/taunt.cpp @@ -2,12 +2,15 @@ void bot_command_taunt(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) + if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } + const int ab_mask = ActionableBots::ABM_Type1; std::string arg1 = sep->arg[1]; @@ -15,26 +18,30 @@ void bot_command_taunt(Client *c, const Seperator *sep) bool taunt_state = false; bool toggle_taunt = true; int ab_arg = 1; + if (!arg1.compare("on")) { taunt_state = true; toggle_taunt = false; - ab_arg = 2; + ++ab_arg; } else if (!arg1.compare("off")) { toggle_taunt = false; - ab_arg = 2; + ++ab_arg; } std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[(ab_arg + 1)] : nullptr, class_race_check ? atoi(sep->arg[(ab_arg + 1)]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); int taunting_count = 0; diff --git a/zone/bot_commands/timer.cpp b/zone/bot_commands/timer.cpp index 93eab687d6..30b495d3ab 100644 --- a/zone/bot_commands/timer.cpp +++ b/zone/bot_commands/timer.cpp @@ -2,10 +2,12 @@ void bot_command_timer(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer")) + if (helper_command_alias_fail(c, "bot_command_timer", sep->arg[0], "timer")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] [actionable].", sep->arg[0]); + c->Message(Chat::White, "usage: %s [clear | has | set] [disc | item | spell] [timer ID | item ID | spell ID | all] [optional ms for set] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name])).", sep->arg[0]); c->Message(Chat::White, "When setting, you can leave the value blank to use the default for the item or specify a value in ms to set the timer to."); c->Message(Chat::White, "Returns or sets the provided timer(s) for the selected bot(s) or clears the selected timer(s) for the selected bot(s)."); return; @@ -88,14 +90,17 @@ void bot_command_timer(Client* c, const Seperator* sep) std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); for (auto my_bot : sbl) { diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index a24cd0c308..20715d0d7f 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -35,6 +35,7 @@ #include "../common/repositories/bot_pet_buffs_repository.h" #include "../common/repositories/bot_pet_inventories_repository.h" #include "../common/repositories/bot_spell_casting_chances_repository.h" +#include "../common/repositories/bot_settings_repository.h" #include "../common/repositories/bot_stances_repository.h" #include "../common/repositories/bot_timers_repository.h" #include "../common/repositories/character_data_repository.h" @@ -400,28 +401,13 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot) e.spells_id, e.time_spawned, e.zone_id, - t, - e.expansion_bitmask + t ); if (loaded_bot) { loaded_bot->SetSurname(e.last_name); loaded_bot->SetTitle(e.title); loaded_bot->SetSuffix(e.suffix); - - loaded_bot->SetShowHelm(e.show_helm); - - auto bfd = EQ::Clamp(e.follow_distance, static_cast(1), BOT_FOLLOW_DISTANCE_DEFAULT_MAX); - - loaded_bot->SetFollowDistance(bfd); - - loaded_bot->SetStopMeleeLevel(e.stop_melee_level); - - loaded_bot->SetBotEnforceSpellSetting(e.enforce_spell_settings); - - loaded_bot->SetBotArcherySetting(e.archery_setting); - - loaded_bot->SetBotCasterRange(e.caster_range); } return true; @@ -476,13 +462,6 @@ bool BotDatabase::SaveNewBot(Bot* b, uint32& bot_id) e.poison = b->GetBasePR(); e.disease = b->GetBaseDR(); e.corruption = b->GetBaseCorrup(); - e.show_helm = b->GetShowHelm() ? 1 : 0; - e.follow_distance = b->GetFollowDistance(); - e.stop_melee_level = b->GetStopMeleeLevel(); - e.expansion_bitmask = b->GetExpansionBitmask(); - e.enforce_spell_settings = b->GetBotEnforceSpellSetting(); - e.archery_setting = b->IsBotArcher() ? 1 : 0; - e.caster_range = b->GetBotCasterRange(); e = BotDataRepository::InsertOne(database, e); @@ -547,12 +526,6 @@ bool BotDatabase::SaveBot(Bot* b) e.poison = b->GetBasePR(); e.disease = b->GetBaseDR(); e.corruption = b->GetBaseCorrup(); - e.show_helm = b->GetShowHelm() ? 1 : 0; - e.follow_distance = b->GetFollowDistance(); - e.stop_melee_level = b->GetStopMeleeLevel(); - e.expansion_bitmask = b->GetExpansionBitmask(); - e.enforce_spell_settings = b->GetBotEnforceSpellSetting(); - e.archery_setting = b->IsBotArcher() ? 1 : 0; return BotDataRepository::UpdateOne(database, e); } @@ -868,7 +841,7 @@ bool BotDatabase::SaveTimers(Bot* b) std::vector l; if (!v.empty()) { - for (auto & bot_timer : v) { + for (auto& bot_timer : v) { if (bot_timer.timer_value <= Timer::GetCurrentTime()) { continue; } @@ -1670,59 +1643,6 @@ bool BotDatabase::SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_val return BotInventoriesRepository::SaveAllArmorColors(database, owner_id, rgb_value); } -bool BotDatabase::SaveHelmAppearance(const uint32 bot_id, const bool show_flag) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - e.show_helm = show_flag ? 1 : 0; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag) -{ - if (!owner_id) { - return false; - } - - return BotDataRepository::SaveAllHelmAppearances(database, owner_id, show_flag); -} - -bool BotDatabase::ToggleAllHelmAppearances(const uint32 owner_id) -{ - if (!owner_id) { - return false; - } - - return BotDataRepository::ToggleAllHelmAppearances(database, owner_id); -} - -bool BotDatabase::SaveFollowDistance(const uint32 bot_id, const uint32 follow_distance) -{ - if (!bot_id || !follow_distance) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - e.follow_distance = follow_distance; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance) -{ - if (!owner_id || !follow_distance) { - return false; - } - - return BotDataRepository::SaveAllFollowDistances(database, owner_id, follow_distance); -} - bool BotDatabase::CreateCloneBot(const uint32 bot_id, const std::string& clone_name, uint32& clone_id) { if (!bot_id || clone_name.empty()) { @@ -1771,19 +1691,6 @@ bool BotDatabase::CreateCloneBotInventory(const uint32 bot_id, const uint32 clon return BotInventoriesRepository::InsertMany(database, l); } -bool BotDatabase::SaveStopMeleeLevel(const uint32 bot_id, const uint8 sml_value) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - e.stop_melee_level = sml_value; - - return BotDataRepository::UpdateOne(database, e); -} - bool BotDatabase::LoadOwnerOptions(Client* c) { if (!c || !c->CharacterID()) { @@ -2224,74 +2131,6 @@ uint32 BotDatabase::GetRaceClassBitmask(uint32 bot_race) return e.race ? e.classes : 0; } -bool BotDatabase::SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.expansion_bitmask = expansion_bitmask; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.enforce_spell_settings = enforce_spell_setting ? 1 : 0; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveBotArcherSetting(const uint32 bot_id, const bool bot_archer_setting) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.archery_setting = bot_archer_setting ? 1 : 0; - - return BotDataRepository::UpdateOne(database, e); -} - -bool BotDatabase::SaveBotCasterRange(const uint32 bot_id, const uint32 bot_caster_range_value) -{ - if (!bot_id) { - return false; - } - - auto e = BotDataRepository::FindOne(database, bot_id); - - if (!e.bot_id) { - return false; - } - - e.caster_range = bot_caster_range_value; - - return BotDataRepository::UpdateOne(database, e); -} - const uint8 BotDatabase::GetBotClassByID(const uint32 bot_id) { const auto& e = BotDataRepository::FindOne(database, bot_id); @@ -2322,7 +2161,7 @@ std::vector BotDatabase::GetBotIDsByCharacterID(const uint32 character_i class_id ) : "" - ) + ) ) ); @@ -2360,3 +2199,177 @@ const int BotDatabase::GetBotExtraHasteByID(const uint32 bot_id) return e.bot_id ? e.extra_haste : 0; } + +bool BotDatabase::LoadBotSettings(Mob* m) +{ + if (!m) { + return false; + } + + if (!m->IsOfClientBot()) { + return false; + } + + uint32 mobID = (m->IsClient() ? m->CastToClient()->CharacterID() : m->CastToBot()->GetBotID()); + + std::string query = ""; + + if (m->IsClient()) { + query = fmt::format("`char_id` = {}", mobID); + } + else { + query = fmt::format("`bot_id` = {}", mobID); + } + + const auto& l = BotSettingsRepository::GetWhere(database, query); + + if (l.empty()) { + return true; + } + + for (const auto& e : l) { + LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." + , m->GetCleanName() + , (e.setting_type == BotSettingCategories::BaseSetting ? m->CastToBot()->GetBotSettingCategoryName(e.setting_id) : m->CastToBot()->GetBotSpellCategoryName(e.setting_id)) + , e.setting_id + , e.value + ); //deleteme + + m->SetBotSetting(e.setting_type, e.setting_id, e.value); + } + + return true; +} + +bool BotDatabase::SaveBotSettings(Mob* m) +{ + if (!m) { + return false; + } + + if (!m->IsOfClientBot()) { + return false; + } + + uint32 botID = (m->IsClient() ? 0 : m->CastToBot()->GetBotID()); + uint32 charID = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); + + std::string query = ""; + + if (m->IsClient()) { + query = fmt::format("`char_id` = {}", charID); + } + else { + query = fmt::format("`bot_id` = {}", botID); + } + + BotSettingsRepository::DeleteWhere(database, query); + + std::vector v; + + if (m->IsBot()) { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i)) { + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(i), + .setting_type = static_cast(BotSettingCategories::BaseSetting), + .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), + .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->CastToBot()->GetBotSettingCategoryName(i) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); //deleteme + } + } + + for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) { + for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { + if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x)) { + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(x), + .setting_type = static_cast(i), + .value = m->CastToBot()->GetSetting(i, x), + .category_name = m->CastToBot()->GetBotSpellCategoryName(i), + .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x)); //deleteme + } + } + } + } + + if (m->IsClient()) { + if (m->CastToClient()->GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock) != m->GetIllusionBlock()) { // Only illusion block supported + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(BotBaseSettings::IllusionBlock), + .setting_type = static_cast(BotSettingCategories::BaseSetting), + .value = m->GetIllusionBlock(), + .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->GetIllusionBlock()); //deleteme + } + + for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) { + for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { + LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + if (IsClientBotSpellType(x) && m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { + auto e = BotSettingsRepository::BotSettings{ + .char_id = charID, + .bot_id = botID, + .setting_id = static_cast(x), + .setting_type = static_cast(i), + .value = m->CastToClient()->GetBotSetting(i, x), + .category_name = m->CastToBot()->GetBotSpellCategoryName(i), + .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) + }; + + v.emplace_back(e); + + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + } + } + } + } + + if (!v.empty()) { + const int inserted = BotSettingsRepository::ReplaceMany(database, v); + + if (!inserted) { + return false; + } + } + + return true; +} + +bool BotDatabase::DeleteBotSettings(const uint32 bot_id) +{ + if (!bot_id) { + return false; + } + + BotSettingsRepository::DeleteWhere( + database, + fmt::format( + "`bot_id` = {}", + bot_id + ) + ); + + return true; +} diff --git a/zone/bot_database.h b/zone/bot_database.h index 139ebc643f..3539db5c6a 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -88,10 +88,6 @@ class BotDatabase bool SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, const uint32 color); - bool SaveExpansionBitmask(const uint32 bot_id, const int expansion_bitmask); - bool SaveEnforceSpellSetting(const uint32 bot_id, const bool enforce_spell_setting); - - /* Bot pet functions */ bool LoadPetIndex(const uint32 bot_id, uint32& pet_index); bool LoadPetSpellID(const uint32 bot_id, uint32& pet_spell_id); @@ -120,26 +116,16 @@ class BotDatabase bool SaveAllArmorColorBySlot(const uint32 owner_id, const int16 slot_id, const uint32 rgb_value); bool SaveAllArmorColors(const uint32 owner_id, const uint32 rgb_value); - bool SaveHelmAppearance(const uint32 bot_id, const bool show_flag = true); - bool SaveAllHelmAppearances(const uint32 owner_id, const bool show_flag = true); - - bool ToggleAllHelmAppearances(const uint32 owner_id); - - bool SaveFollowDistance(const uint32 bot_id, const uint32 follow_distance); - bool SaveAllFollowDistances(const uint32 owner_id, const uint32 follow_distance); - bool CreateCloneBot(const uint32 bot_id, const std::string& clone_name, uint32& clone_id); bool CreateCloneBotInventory(const uint32 bot_id, const uint32 clone_id); - bool SaveStopMeleeLevel(const uint32 bot_id, const uint8 sml_value); - - bool SaveBotArcherSetting(const uint32 bot_id, const bool bot_archer_setting); - bool LoadOwnerOptions(Client *owner); bool SaveOwnerOption(const uint32 owner_id, size_t type, const bool flag); bool SaveOwnerOption(const uint32 owner_id, const std::pair type, const std::pair flag); - bool SaveBotCasterRange(const uint32 bot_id, const uint32 bot_caster_range_value); + bool LoadBotSettings(Mob* m); + bool SaveBotSettings(Mob* m); + bool DeleteBotSettings(const uint32 bot_id); /* Bot group functions */ bool LoadGroupedBotsByGroupID(const uint32 owner_id, const uint32 group_id, std::list& group_list); @@ -210,12 +196,6 @@ class BotDatabase static const char* SaveAllInspectMessages(); static const char* SaveAllArmorColorBySlot(); static const char* SaveAllArmorColors(); - static const char* SaveAllHelmAppearances(); - static const char* ToggleAllHelmAppearances(); - static const char* SaveFollowDistance(); - static const char* SaveAllFollowDistances(); - static const char* SaveStopMeleeLevel(); - static const char* SaveBotCasterRange(); /* fail::Bot heal rotation functions */ static const char* LoadHealRotation(); diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 50e319e9e9..0cfc5913ea 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -92,4 +92,9 @@ struct BotTimer_Struct { uint32 item_id; }; +struct BotSpellTypeOrder { + uint16 spellType; + uint16 priority; +}; + #endif // BOT_STRUCTS diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 0050a58b20..ee3aaf3a37 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -21,1226 +21,528 @@ #include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/npc_spells_repository.h" -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) { - - // Bot AI - Raid* raid = entity_list.GetRaidByBotName(GetName()); - +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { if (!tar) { return false; } - if (!AI_HasSpells()) { + LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + + if ( + !AI_HasSpells() || + (spellType == BotSpellTypes::Pet && tar != this) || + (IsPetBotSpellType(spellType) && !tar->IsPet()) || + (spellType == BotSpellTypes::Buff && tar->IsPet()) || + (spellType == BotSpellTypes::InCombatBuffSong && tar->IsPet()) || + (spellType == BotSpellTypes::OutOfCombatBuffSong && tar->IsPet()) || + (spellType == BotSpellTypes::PreCombatBuffSong && tar->IsPet()) || + (!RuleB(Bots, AllowBuffingHealingFamiliars) && tar->IsFamiliar()) || + (tar->IsPet() && tar->IsCharmed() && spellType == BotSpellTypes::PetBuffs && !RuleB(Bots, AllowCharmedPetBuffs)) || + (tar->IsPet() && tar->IsCharmed() && (spellType == BotSpellTypes::Cure || spellType == BotSpellTypes::GroupCures) && !RuleB(Bots, AllowCharmedPetCures)) || + (tar->IsPet() && tar->IsCharmed() && IsHealBotSpellType(spellType) && !RuleB(Bots, AllowCharmedPetHeals)) + ) { return false; } - + if (iChance < 100 && zone->random.Int(0, 100) > iChance) { return false; } - if (tar->GetAppearance() == eaDead && ((tar->IsClient() && !tar->CastToClient()->GetFeigned()) || tar->IsBot())) { - return false; + if (spellType != BotSpellTypes::Resurrect && tar->GetAppearance() == eaDead) { + if (!((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot())) { + return false; + } } uint8 botClass = GetClass(); - uint8 botLevel = GetLevel(); - bool checked_los = false; //we do not check LOS until we are absolutely sure we need to, and we only do it once. + SetCastedSpellType(UINT16_MAX); BotSpell botSpell; botSpell.SpellId = 0; botSpell.SpellIndex = 0; botSpell.ManaCost = 0; - switch (iSpellTypes) { - case SpellType_Mez: - return BotCastMez(tar, botLevel, checked_los, botSpell, raid); - case SpellType_Heal: - return BotCastHeal(tar, botLevel, botClass, botSpell, raid); - case SpellType_Root: - return BotCastRoot(tar, botLevel, iSpellTypes, botSpell, checked_los); - case SpellType_Buff: - return BotCastBuff(tar, botLevel, botClass); - case SpellType_Escape: - return BotCastEscape(tar, botClass, botSpell, iSpellTypes); - case SpellType_Nuke: - return BotCastNuke(tar, botLevel, botClass, botSpell, checked_los); - case SpellType_Dispel: - return BotCastDispel(tar, botSpell, iSpellTypes, checked_los); - case SpellType_Pet: - return BotCastPet(tar, botClass, botSpell); - case SpellType_InCombatBuff: - return BotCastCombatBuff(tar, botLevel, botClass); - case SpellType_Lifetap: - return BotCastLifetap(tar, botLevel, botSpell, checked_los, iSpellTypes); - case SpellType_Snare: - return BotCastSnare(tar, botLevel, botSpell, checked_los, iSpellTypes); - case SpellType_DOT: - return BotCastDOT(tar, botLevel, botSpell, checked_los); - case SpellType_Slow: - return BotCastSlow(tar, botLevel, botClass, botSpell, checked_los, raid); - case SpellType_Debuff: - return BotCastDebuff(tar, botLevel, botSpell, checked_los); - case SpellType_Cure: - return BotCastCure(tar, botClass, botSpell, raid); - case SpellType_HateRedux: - return BotCastHateReduction(tar, botLevel, botSpell); - case SpellType_InCombatBuffSong: - return BotCastCombatSong(tar, botLevel); - case SpellType_OutOfCombatBuffSong: - return BotCastSong(tar, botLevel); - case SpellType_Resurrect: - case SpellType_PreCombatBuff: - case SpellType_PreCombatBuffSong: - default: - return false; + if (SpellTypeRequiresLoS(spellType, botClass) && tar != this) { + SetHasLoS(DoLosChecks(this, tar)); + } + else { + SetHasLoS(true); } - return false; -} - -bool Bot::BotCastSong(Mob* tar, uint8 botLevel) { - bool casted_spell = false; - if (GetClass() != Class::Bard || tar != this || IsEngaged()) // Out-of-Combat songs can not be cast in combat - return casted_spell; + switch (spellType) { + case BotSpellTypes::Slow: + if (tar->GetSpecialAbility(SpecialAbility::SlowImmunity)) { + return false; + } - for (auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_OutOfCombatBuffSong); - auto iter : botSongList) { - if (!iter.SpellId) - continue; - if (!CheckSpellRecastTimer(iter.SpellId)) - continue; - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) - continue; - switch (spells[iter.SpellId].target_type) { - case ST_AEBard: - case ST_AECaster: - case ST_GroupTeleport: - case ST_Group: - case ST_Self: break; - default: - continue; - } - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) - continue; + case BotSpellTypes::Snare: + if (tar->GetSpecialAbility(SpecialAbility::SnareImmunity)) { + return false; + } - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); - if (casted_spell) break; - } - - return casted_spell; -} + //SpecialAbility::PacifyImmunity -- TODO bot rewrite + case BotSpellTypes::Fear: + if (tar->GetSpecialAbility(SpecialAbility::FearImmunity)) { + return false; + } -bool Bot::BotCastCombatSong(Mob* tar, uint8 botLevel) { - bool casted_spell = false; - if (tar != this) { // In-Combat songs can be cast Out-of-Combat in preparation for battle - return casted_spell; - } - for (auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_InCombatBuffSong); - auto iter : botSongList) { - if (!iter.SpellId) - continue; - if (!CheckSpellRecastTimer(iter.SpellId)) - continue; - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) - continue; - switch (spells[iter.SpellId].target_type) { - case ST_AEBard: - case ST_AECaster: - case ST_GroupTeleport: - case ST_Group: - case ST_Self: - break; - default: - continue; - } - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) - continue; + if (!IsCommandedSpell() && (tar->IsRooted() || tar->GetSnaredAmount() == -1)) { + return false; + } - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); - if (casted_spell) break; - } + case BotSpellTypes::Dispel: + if (tar->GetSpecialAbility(SpecialAbility::DispellImmunity)) { + return false; + } - return casted_spell; -} + if (!IsCommandedSpell() && tar->CountDispellableBuffs() <= 0) { + return false; + } -bool Bot::BotCastHateReduction(Mob* tar, uint8 botLevel, const BotSpell& botSpell) { - bool casted_spell = false; - if (GetClass() == Class::Bard) { - std::list botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_HateRedux); - for (auto iter : botSongList) { - if (!iter.SpellId) - continue; - if (!CheckSpellRecastTimer(iter.SpellId)) - continue; - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) - continue; - if (spells[iter.SpellId].target_type != ST_Target) - continue; - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) - continue; + break; + case BotSpellTypes::HateRedux: + if (!IsCommandedSpell() && !GetNeedsHateRedux(tar)) { + return false; + } - if (IsValidSpellRange(iter.SpellId, tar)) { - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); + break; + case BotSpellTypes::InCombatBuff: + if (GetClass() == Class::ShadowKnight && (tar->IsOfClientBot() || (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()))) { + return false; } - if (casted_spell) { - BotGroupSay( - this, - fmt::format( - "Attempting to reduce hate on {} with {}.", - tar->GetCleanName(), - spells[iter.SpellId].name - ).c_str() - ); + + if (!IsCommandedSpell() && GetClass() != Class::Shaman && spellType == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { + return false; } - } - } - return casted_spell; -} + break; + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + return false; + } -bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, Raid* raid) { - bool casted_spell = false; - if ( - GetNeedsCured(tar) && - (tar->DontCureMeBefore() < Timer::GetCurrentTime()) && - GetNumberNeedingHealedInGroup(25, false, raid) <= 0 && - GetNumberNeedingHealedInGroup(40, false, raid) <= 2 - ) { - botSpell = GetBestBotSpellForCure(this, tar); + break; + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + if (!IsCommandedSpell() && (IsEngaged() || tar->IsEngaged())) { // Out-of-Combat songs can not be cast in combat + return false; + } - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } + break; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return BotCastMez(tar, botClass, botSpell, spellType); + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + return BotCastNuke(tar, botClass, botSpell, spellType); + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + return false; + } - uint32 TempDontCureMeBeforeTime = tar->DontCureMeBefore(); + return BotCastHeal(tar, botClass, botSpell, spellType); + case BotSpellTypes::GroupCures: + case BotSpellTypes::Cure: + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + return false; + } - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontCureMeBeforeTime); + return BotCastCure(tar, botClass, botSpell, spellType); + case BotSpellTypes::Pet: + if (HasPet() || IsBotCharmer()) { + return false; + } - if (casted_spell && botClass != Class::Bard) { - if (IsGroupSpell(botSpell.SpellId)) { - if (HasGroup()) { - Group const* group = GetGroup(); - if (group) { - for (auto member : group->members) { - if ( - member && - !member->qglobal && - TempDontCureMeBeforeTime != tar->DontCureMeBefore() - ) { - member->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - } else if (IsRaidGrouped()) { - uint32 r_group = raid->GetGroup(GetName()); - if (r_group) { - std::vector raid_group_members = raid->GetRaidGroupMembers(r_group); - for (auto& iter: raid_group_members) { - if ( - iter.member && - !iter.member->qglobal && - TempDontCureMeBeforeTime != tar->DontCureMeBefore() - ) { - iter.member->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); - } - } - } - } - } else if (TempDontCureMeBeforeTime != tar->DontCureMeBefore()) { - tar->SetDontCureMeBefore(Timer::GetCurrentTime() + 4000); + return BotCastPet(tar, botClass, botSpell, spellType); + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + return false; } - } - } - return casted_spell; -} -bool Bot::BotCastDebuff(Mob* tar, uint8 botLevel, BotSpell& botSpell, bool checked_los) { - bool casted_spell = false; - if ((tar->GetHPRatio() <= 99.0f) && (tar->GetHPRatio() > 20.0f)) - { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } + break; + case BotSpellTypes::Charm: + if (tar->IsCharmed() || !tar->IsNPC() || tar->GetSpecialAbility(SpecialAbility::CharmImmunity)) { + return false; + } - botSpell = GetBestBotSpellForResistDebuff(this, tar); + break; + default: + break; + } - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetDebuffBotSpell(this, tar); - } + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } + for (const auto& s : botSpellList) { - if (! - ( - !tar->IsImmuneToSpell(botSpell.SpellId, this) && - (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0) - ) - ) { - return casted_spell; + if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { + continue; } - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + if (IsInvulnerabilitySpell(s.SpellId)) { + tar = this; //target self for invul type spells } - } - return casted_spell; -} -bool Bot::BotCastSlow(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los, Raid* raid) { - bool casted_spell = false; - if (tar->GetHPRatio() <= 99.0f) { - - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; + if (IsCommandedSpell() && IsCasting()) { + BotGroupSay( + this, + fmt::format( + "Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.", + CastingSpellID() ? spells[CastingSpellID()].name : "my spell", + GetSpellTypeNameByID(spellType), + spells[s.SpellId].name, + tar->GetCleanName() + ).c_str() + ); + + InterruptSpell(); } - switch (botClass) { - case Class::Bard: { - // probably needs attackable check - for ( - auto botSongList = GetPrioritizedBotSpellsBySpellType(this, SpellType_Slow); - auto iter : botSongList - ) { - - if (!iter.SpellId) { - continue; - } - - if (!CheckSpellRecastTimer(iter.SpellId)) { - continue; - } - - if (!IsSpellUsableInThisZoneType(iter.SpellId, zone->GetZoneType())) { - continue; - } - - if (spells[iter.SpellId].target_type != ST_Target) { - continue; - } - - if (tar->CanBuffStack(iter.SpellId, botLevel, true) < 0) { - continue; - } - - if (IsValidSpellRange(iter.SpellId, tar)) { - casted_spell = AIDoSpellCast(iter.SpellIndex, tar, iter.ManaCost); - } + if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + SetCastedSpellType(UINT16_MAX); - if (casted_spell) { - return casted_spell; - } + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); } - - break; - } - case Class::Enchanter: { - botSpell = GetBestBotSpellForMagicBasedSlow(this); - break; } - case Class::Shaman: - case Class::Beastlord: { - botSpell = GetBestBotSpellForDiseaseBasedSlow(this); - - if (botSpell.SpellId == 0 || ((tar->GetMR() - 50) < (tar->GetDR() + spells[botSpell.SpellId].resist_difficulty))) - botSpell = GetBestBotSpellForMagicBasedSlow(this); - break; + else { + SetCastedSpellType(spellType); } - } - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) { - return casted_spell; - } - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); - } - - if (casted_spell && GetClass() != Class::Bard) { - if (raid) { - const auto msg = fmt::format("Attempting to slow {}.", tar->GetCleanName()); - raid->RaidSay(msg.c_str(), GetCleanName(), Language::CommonTongue, Language::MaxValue); - } else { + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { BotGroupSay( this, fmt::format( - "Attempting to slow {} with {}.", - tar->GetCleanName(), - spells[botSpell.SpellId].name + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spellType), + (tar == this ? "myself" : tar->GetCleanName()) ).c_str() ); } + + return true; } } - return casted_spell; + + return false; } -bool Bot::BotCastDOT(Mob* tar, uint8 botLevel, const BotSpell& botSpell, const bool& checked_los) { - bool casted_spell = false; +bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); - if ((tar->GetHPRatio() <= 98.0f) && (tar->DontDotMeBefore() < Timer::GetCurrentTime()) && (tar->GetHPRatio() > 15.0f)) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; + for (const auto& s : botSpellList) { + if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { + continue; } - if (GetClass() == Class::Bard) { - std::list dotList = GetPrioritizedBotSpellsBySpellType(this, SpellType_DOT); + if (!IsCommandedSpell()) { + Mob* addMob = GetFirstIncomingMobToMez(this, s.SpellId, spellType, IsAEBotSpellType(spellType)); - const int maxDotSelect = 5; - int dotSelectCounter = 0; + if (!addMob) { + return false; + } - for (const auto& s : dotList) { + tar = addMob; + } - if (!IsValidSpell(s.SpellId)) { - continue; + if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + SetCastedSpellType(UINT16_MAX); + + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); } + } + else { + SetCastedSpellType(spellType); + } - if (CheckSpellRecastTimer(s.SpellId)) - { - - if (!(!tar->IsImmuneToSpell(s.SpellId, this) && tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) { - continue; - } - - uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + BotGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spellType), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontDotMeBefore); + return true; + } + } - if (TempDontDotMeBefore != tar->DontDotMeBefore()) { - tar->SetDontDotMeBefore(TempDontDotMeBefore); - } - } + return false; +} - dotSelectCounter++; +bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + uint32 currentTime = Timer::GetCurrentTime(); + uint32 nextCureTime = tar->DontCureMeBefore(); - if ((dotSelectCounter == maxDotSelect) || casted_spell) { - break; - } - } + if (!IsCommandedSpell()) { + if ((nextCureTime > currentTime) || !GetNeedsCured(tar)) { + return false; } - else { - std::list dotList = GetBotSpellsBySpellType(this, SpellType_DOT); - - const int maxDotSelect = 5; - int dotSelectCounter = 0; - - for (const auto& s : dotList) { + } - if (!IsValidSpell(s.SpellId)) { - continue; - } + botSpell = GetBestBotSpellForCure(this, tar, spellType); - if (CheckSpellRecastTimer(s.SpellId)) { + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + return false; + } - if (!(!tar->IsImmuneToSpell(s.SpellId, this) && - tar->CanBuffStack(s.SpellId, botLevel, true) >= 0)) { - continue; - } + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (IsGroupSpell(botSpell.SpellId)) { + BotGroupSay( + this, + fmt::format( + "Curing the group with {}.", + GetSpellName(botSpell.SpellId) + ).c_str() + ); - uint32 TempDontDotMeBefore = tar->DontDotMeBefore(); + const std::vector v = GatherGroupSpellTargets(tar); - if (IsValidSpellRange(s.SpellId, tar)) { - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontDotMeBefore); - } - if (TempDontDotMeBefore != tar->DontDotMeBefore()) { - tar->SetDontDotMeBefore(TempDontDotMeBefore); + if (!IsCommandedSpell()) { + for (Mob* m : v) { + SetBotSpellRecastTimer(spellType, m, true); } } + } + else { + BotGroupSay( + this, + fmt::format( + "Curing {} with {}.", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(botSpell.SpellId) + ).c_str() + ); - dotSelectCounter++; - - if ((dotSelectCounter == maxDotSelect) || casted_spell) { - return casted_spell; + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); } + + return true; } } } - return casted_spell; + + return false; } -bool Bot::BotCastSnare(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) { - bool casted_spell = false; - if (tar->DontSnareMeBefore() < Timer::GetCurrentTime()) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; +bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + if (botClass == Class::Wizard) { + auto buffs_max = GetMaxBuffSlots(); + auto my_buffs = GetBuffs(); + int familiar_buff_slot = -1; + if (buffs_max && my_buffs) { + for (int index = 0; index < buffs_max; ++index) { + if (IsEffectInSpell(my_buffs[index].spellid, SE_Familiar)) { + MakePet(my_buffs[index].spellid, spells[my_buffs[index].spellid].teleport_zone); + familiar_buff_slot = index; + break; + } + } } - - - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; + if (GetPetID()) { + return false; } - - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) { - return casted_spell; + if (familiar_buff_slot >= 0) { + BuffFadeBySlot(familiar_buff_slot); + return false; } - uint32 TempDontSnareMeBefore = tar->DontSnareMeBefore(); - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontSnareMeBefore); - } + botSpell = GetFirstBotSpellBySpellType(this, spellType); + } + else if (botClass == Class::Magician) { + botSpell = GetBestBotMagicianPetSpell(this, spellType); + } + else { + botSpell = GetFirstBotSpellBySpellType(this, spellType); + } - if (TempDontSnareMeBefore != tar->DontSnareMeBefore()) { - tar->SetDontSnareMeBefore(TempDontSnareMeBefore); - } + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + return false; } - return casted_spell; -} -bool Bot::BotCastLifetap(Mob* tar, uint8 botLevel, BotSpell& botSpell, const bool& checked_los, uint32 iSpellTypes) { - bool casted_spell = false; - if (GetHPRatio() < 90.0f) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + SetCastedSpellType(spellType); + BotGroupSay( + this, + fmt::format( + "Summoning a pet [{}].", + GetSpellName(botSpell.SpellId) + ).c_str() + ); + + return true; + } + return false; +} - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); +bool Bot::BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + if (spellType == BotSpellTypes::Stun || spellType == BotSpellTypes::AEStun) { + uint8 stunChance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal)); - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; + if (botClass == Class::Paladin) { + stunChance = RuleI(Bots, StunCastChancePaladins); } - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))) { - return casted_spell; + if ( + !tar->GetSpecialAbility(SpecialAbility::StunImmunity) && + ( + IsCommandedSpell() || + (!tar->IsStunned() && (zone->random.Int(1, 100) <= stunChance)) + ) + ) { + botSpell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spellType, IsAEBotSpellType(spellType), tar); } - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + return false; } } - return casted_spell; -} -bool Bot::BotCastCombatBuff(Mob* tar, uint8 botLevel, uint8 botClass) { + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + botSpell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spellType, IsAEBotSpellType(spellType), tar); + } - bool casted_spell = false; - if (tar->DontBuffMeBefore() < Timer::GetCurrentTime()) { - std::list buffSpellList = GetBotSpellsBySpellType(this, SpellType_InCombatBuff); + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS()) && spellType == BotSpellTypes::Nuke && botClass == Class::Wizard) { + botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spellType); + } - for (const auto& s : buffSpellList) { + if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); - if (!IsValidSpell(s.SpellId)) { - continue; - } - // no buffs with illusions.. use #bot command to cast illusions - if (IsEffectInSpell(s.SpellId, SE_Illusion) && tar != this) { - continue; - } - //no teleport spells use #bot command to cast teleports - if (IsEffectInSpell(s.SpellId, SE_Teleport) || IsEffectInSpell(s.SpellId, SE_Succor)) { - continue; - } - // can not cast buffs for your own pet only on another pet that isn't yours - if ((spells[s.SpellId].target_type == ST_Pet) && (tar != GetPet())) { + for (const auto& s : botSpellList) { + if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { continue; } - //Conversion Spells - if ( - IsSelfConversionSpell(s.SpellId) && - ( - GetManaRatio() > 90.0f || - GetHPRatio() < 50.0f || - GetHPRatio() < (GetManaRatio() + 10.0f) - ) - ) { - break; //don't cast if low hp, lots of mana, or if mana is higher than hps - } + if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { + SetCastedSpellType(spellType); - // Validate target - // TODO: Add ST_TargetsTarget support for Buffing. - if ( - !( - ( - spells[s.SpellId].target_type == ST_Target || - spells[s.SpellId].target_type == ST_Pet || - (tar == this && spells[s.SpellId].target_type != ST_TargetsTarget) || - spells[s.SpellId].target_type == ST_Group || - spells[s.SpellId].target_type == ST_GroupTeleport || - (botClass == Class::Bard && spells[s.SpellId].target_type == ST_AEBard) - ) && - !tar->IsImmuneToSpell(s.SpellId, this) && - tar->CanBuffStack(s.SpellId, botLevel, true) >= 0 - ) - ) { - continue; - } - - // Put the zone levitate and movement check here since bots are able to bypass the client casting check - if ( - ((IsEffectInSpell(s.SpellId, SE_Levitate) && !zone->CanLevitate()) || - (IsEffectInSpell(s.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor())) && - (botClass != Class::Bard || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType())) - ) { - continue; - } - - if (!IsGroupSpell(s.SpellId)) { - //Only check archetype if spell is not a group spell - //Hybrids get all buffs - switch (tar->GetArchetype()) { - case Archetype::Caster: - //TODO: probably more caster specific spell effects in here - if ( - ( - IsEffectInSpell(s.SpellId, SE_AttackSpeed) || - IsEffectInSpell(s.SpellId, SE_ATK) || - IsEffectInSpell(s.SpellId, SE_STR) || - IsEffectInSpell(s.SpellId, SE_ReverseDS) || - IsEffectInSpell(s.SpellId, SE_DamageShield) - ) && - spells[s.SpellId].target_type != ST_Self - ) { - continue; - } - break; - case Archetype::Melee: - if ( - ( - IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) || - IsEffectInSpell(s.SpellId, SE_ManaPool) || - IsEffectInSpell(s.SpellId, SE_CastingLevel) || - IsEffectInSpell(s.SpellId, SE_ManaRegen_v2) || - IsEffectInSpell(s.SpellId, SE_CurrentMana) - ) && - spells[s.SpellId].target_type != ST_Self - ) { - continue; - } - break; - default: - break; - } - } - // TODO: Add TriggerSpell Support for Exchanter Runes - if (botClass == Class::Enchanter && IsEffectInSpell(s.SpellId, SE_Rune)) { - float manaRatioToCast = 75.0f; - - switch(GetBotStance()) { - case Stance::Efficient: - manaRatioToCast = 90.0f; - break; - case Stance::Balanced: - case Stance::Aggressive: - manaRatioToCast = 75.0f; - break; - case Stance::Reactive: - case Stance::Burn: - case Stance::AEBurn: - manaRatioToCast = 50.0f; - break; - default: - manaRatioToCast = 75.0f; - break; - } - - //If we're at specified mana % or below, don't rune as enchanter - if (GetManaRatio() <= manaRatioToCast) { - break; - } - } - - if (CheckSpellRecastTimer(s.SpellId)) { - uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore(); - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontBuffMeBefore); - if (TempDontBuffMeBefore != tar->DontBuffMeBefore()) { - tar->SetDontBuffMeBefore(TempDontBuffMeBefore); + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + BotGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spellType), + tar->GetCleanName() + ).c_str() + ); } - } - - if (casted_spell) { - return casted_spell; - } - } - } - return casted_spell; -} -bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell) { - bool casted_spell = false; - if (!IsPet() && !GetPetID() && !IsBotCharmer()) { - if (botClass == Class::Wizard) { - auto buffs_max = GetMaxBuffSlots(); - auto my_buffs = GetBuffs(); - int familiar_buff_slot = -1; - if (buffs_max && my_buffs) { - for (int index = 0; index < buffs_max; ++index) { - if (IsEffectInSpell(my_buffs[index].spellid, SE_Familiar)) { - MakePet(my_buffs[index].spellid, spells[my_buffs[index].spellid].teleport_zone); - familiar_buff_slot = index; - break; - } - } - } - if (GetPetID()) { - return casted_spell; - } - if (familiar_buff_slot >= 0) { - BuffFadeBySlot(familiar_buff_slot); - return casted_spell; + return true; } - - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet); - } - else if (botClass == Class::Magician) { - botSpell = GetBestBotMagicianPetSpell(this); - } - else { - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Pet); - } - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); - } - return casted_spell; -} - -bool Bot::BotCastDispel(Mob* tar, BotSpell& botSpell, uint32 iSpellTypes, const bool& checked_los) { - - bool casted_spell = false; - if (tar->GetHPRatio() > 95.0f) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } - - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - // TODO: Check target to see if there is anything to dispel - - if (tar->CountDispellableBuffs() > 0 && IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); } } - return casted_spell; -} - -bool Bot::BotCastNuke(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, const bool& checked_los) { - - bool casted_spell = false; - if ((tar->GetHPRatio() <= 95.0f) || ((botClass == Class::Bard) || (botClass == Class::Shaman) || (botClass == Class::Enchanter) || (botClass == Class::Paladin) || (botClass == Class::ShadowKnight) || (botClass == Class::Warrior))) - { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } - - if (botClass == Class::Cleric || botClass == Class::Enchanter) - { - float manaRatioToCast = 75.0f; - - switch(GetBotStance()) { - case Stance::Efficient: - manaRatioToCast = 90.0f; - break; - case Stance::Balanced: - manaRatioToCast = 75.0f; - break; - case Stance::Reactive: - case Stance::Aggressive: - manaRatioToCast = 50.0f; - break; - case Stance::Burn: - case Stance::AEBurn: - manaRatioToCast = 25.0f; - break; - default: - manaRatioToCast = 50.0f; - break; - } - - //If we're at specified mana % or below, don't nuke as cleric or enchanter - if (GetManaRatio() <= manaRatioToCast) - return casted_spell; - } - - if (botClass == Class::Magician || botClass == Class::ShadowKnight || botClass == Class::Necromancer || botClass == Class::Paladin || botClass == Class::Ranger || botClass == Class::Druid || botClass == Class::Cleric) { - if (tar->GetBodyType() == BodyType::Undead || tar->GetBodyType() == BodyType::SummonedUndead || tar->GetBodyType() == BodyType::Vampire) - botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Undead); - else if (tar->GetBodyType() == BodyType::Summoned || tar->GetBodyType() == BodyType::Summoned2 || tar->GetBodyType() == BodyType::Summoned3) - botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Summoned); - } - - if ((botClass == Class::Paladin || botClass == Class::Druid || botClass == Class::Cleric || botClass == Class::Enchanter || botClass == Class::Wizard) && !IsValidSpell(botSpell.SpellId)) { - uint8 stunChance = (tar->IsCasting() ? 30: 15); - - if (botClass == Class::Paladin) { - stunChance = 50; - } + else { + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + SetCastedSpellType(spellType); - if (!tar->GetSpecialAbility(SpecialAbility::StunImmunity) && !tar->IsStunned() && (zone->random.Int(1, 100) <= stunChance)) { - botSpell = GetBestBotSpellForStunByTargetType(this, ST_Target); + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + BotGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(botSpell.SpellId), + GetSpellTypeNameByID(spellType), + tar->GetCleanName() + ).c_str() + ); } - } - if (botClass == Class::Wizard && botSpell.SpellId == 0) { - botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar); - } - - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForNukeByTargetType(this, ST_Target); - } - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - if (!(!tar->IsImmuneToSpell(botSpell.SpellId, this) && (tar->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0))) { - return casted_spell; - } - if (IsFearSpell(botSpell.SpellId) && tar->GetSnaredAmount() == -1 && !tar->IsRooted()) { - return casted_spell; - } - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); - } - } - return casted_spell; -} - -bool Bot::BotCastEscape(Mob*& tar, uint8 botClass, BotSpell& botSpell, uint32 iSpellTypes) { - - bool casted_spell = false; - auto hpr = (uint8) GetHPRatio(); - bool mayGetAggro = false; - - if (hpr > 15 && ((botClass == Class::Wizard) || (botClass == Class::Enchanter) || (botClass == Class::Ranger))) { - mayGetAggro = HasOrMayGetAggro(); - } - - if (hpr <= 15 || mayGetAggro) - { - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - - if (IsInvulnerabilitySpell(botSpell.SpellId)) { - tar = this; //target self for invul type spells - } - - if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == Class::Bard) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost); + return true; } } - return casted_spell; -} - -bool Bot::BotCastBuff(Mob* tar, uint8 botLevel, uint8 botClass) { - bool casted_spell = false; - if (tar->DontBuffMeBefore() < Timer::GetCurrentTime()) { - std::list buffSpellList = GetBotSpellsBySpellType(this, SpellType_Buff); - - for(const auto& s : buffSpellList) { - - if (!IsValidSpell(s.SpellId)) { - continue; - } - - // no buffs with illusions.. use #bot command to cast illusions - if (IsEffectInSpell(s.SpellId, SE_Illusion) && tar != this) { - continue; - } - - //no teleport spells use #bot command to cast teleports - if (IsEffectInSpell(s.SpellId, SE_Teleport) || IsEffectInSpell(s.SpellId, SE_Succor)) { - continue; - } - // can not cast buffs for your own pet only on another pet that isn't yours - if ((spells[s.SpellId].target_type == ST_Pet) && (tar != GetPet())) { - continue; - } - - // Validate target - // TODO: Add ST_TargetsTarget support for Buffing. - if ( - !( - ( - spells[s.SpellId].target_type == ST_Target || - spells[s.SpellId].target_type == ST_Pet || - (tar == this && spells[s.SpellId].target_type != ST_TargetsTarget) || - spells[s.SpellId].target_type == ST_Group || - spells[s.SpellId].target_type == ST_GroupTeleport || - (botClass == Class::Bard && spells[s.SpellId].target_type == ST_AEBard) - ) && - !tar->IsImmuneToSpell(s.SpellId, this) && - tar->CanBuffStack(s.SpellId, botLevel, true) >= 0 - ) - ) { - continue; - } - - // Put the zone levitate and movement check here since bots are able to bypass the client casting check - if ( - ( - (IsEffectInSpell(s.SpellId, SE_Levitate) && !zone->CanLevitate()) || - (IsEffectInSpell(s.SpellId, SE_MovementSpeed) && !zone->CanCastOutdoor()) - ) && - (botClass != Class::Bard || !IsSpellUsableInThisZoneType(s.SpellId, zone->GetZoneType())) - ) { - continue; - } - - switch (tar->GetArchetype()) - { - case Archetype::Caster: - //TODO: probably more caster specific spell effects in here - if (IsEffectInSpell(s.SpellId, SE_AttackSpeed) || IsEffectInSpell(s.SpellId, SE_ATK) || - IsEffectInSpell(s.SpellId, SE_STR) || IsEffectInSpell(s.SpellId, SE_ReverseDS)) - { - continue; - } - break; - case Archetype::Melee: - if (IsEffectInSpell(s.SpellId, SE_IncreaseSpellHaste) || IsEffectInSpell(s.SpellId, SE_ManaPool) || - IsEffectInSpell(s.SpellId, SE_CastingLevel) || IsEffectInSpell(s.SpellId, SE_ManaRegen_v2) || - IsEffectInSpell(s.SpellId, SE_CurrentMana)) - { - continue; - } - break; - default: //Hybrids get all buffs - break; - } - if (botClass == Class::Enchanter && IsEffectInSpell(s.SpellId, SE_Rune)) - { - float manaRatioToCast = 75.0f; - - switch (GetBotStance()) { - case Stance::Efficient: - manaRatioToCast = 90.0f; - break; - case Stance::Balanced: - case Stance::Aggressive: - manaRatioToCast = 75.0f; - break; - case Stance::Reactive: - case Stance::Burn: - case Stance::AEBurn: - manaRatioToCast = 50.0f; - break; - default: - manaRatioToCast = 75.0f; - break; - } - - //If we're at specified mana % or below, don't rune as enchanter - if (GetManaRatio() <= manaRatioToCast) { - return casted_spell; - } - } - - if (CheckSpellRecastTimer(s.SpellId)) - { - uint32 TempDontBuffMeBefore = tar->DontBuffMeBefore(); - - casted_spell = AIDoSpellCast(s.SpellIndex, tar, s.ManaCost, &TempDontBuffMeBefore); - - if (TempDontBuffMeBefore != tar->DontBuffMeBefore()) { - tar->SetDontBuffMeBefore(TempDontBuffMeBefore); - } - } - } - } - return casted_spell; + return false; } -bool Bot::BotCastRoot(Mob* tar, uint8 botLevel, uint32 iSpellTypes, BotSpell& botSpell, const bool& checked_los) { - bool casted_spell = false; - if (!tar->IsRooted() && tar->DontRootMeBefore() < Timer::GetCurrentTime()) { - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return casted_spell; - } +bool Bot::BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { + botSpell = GetSpellByHealType(spellType, tar); - // TODO: If there is a ranger in the group then don't allow root spells - - botSpell = GetFirstBotSpellBySpellType(this, iSpellTypes); - - if (!IsValidSpell(botSpell.SpellId)) { - return casted_spell; - } - if (tar->CanBuffStack(botSpell.SpellId, botLevel, true) == 0) { - return casted_spell; - } - uint32 TempDontRootMeBefore = tar->DontRootMeBefore(); - - if (IsValidSpellRange(botSpell.SpellId, tar)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontRootMeBefore); - } - - if (TempDontRootMeBefore != tar->DontRootMeBefore()) { - tar->SetDontRootMeBefore(TempDontRootMeBefore); - } + if (!IsValidSpell(botSpell.SpellId)) { + return false; } - return casted_spell; -} - -bool Bot::BotCastHeal(Mob* tar, uint8 botLevel, uint8 botClass, BotSpell& botSpell, Raid* raid) { - bool casted_spell = false; - if (tar->DontHealMeBefore() < Timer::GetCurrentTime()) { - auto hpr = (uint8)tar->GetHPRatio(); - bool hasAggro = false; - bool isPrimaryHealer = false; - - if (HasGroup() || IsRaidGrouped()) { - isPrimaryHealer = IsGroupHealer(); - } - - if (hpr < 95 || tar->IsClient() || botClass == Class::Bard) { - if (tar->GetClass() == Class::Necromancer && hpr >= 40) { - return false; - } - - if (tar->GetClass() == Class::Shaman && hpr >= 80) { - return false; - } - - // Evaluate the situation - if ((IsEngaged()) && ((botClass == Class::Cleric) || (botClass == Class::Druid) || (botClass == Class::Shaman) || (botClass == Class::Paladin))) { - if (tar->GetTarget() && tar->GetTarget()->GetHateTop() && tar->GetTarget()->GetHateTop() == tar) { - hasAggro = true; - } - - if (hpr < 35) { - botSpell = GetBestBotSpellForFastHeal(this); - } - else if (hpr < 70) { - if (GetNumberNeedingHealedInGroup(60, false, raid) >= 3) { - botSpell = GetBestBotSpellForGroupHeal(this); - } - - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - } - else if (hpr < 95) { - if (GetNumberNeedingHealedInGroup(80, false, raid) >= 3) { - botSpell = GetBestBotSpellForGroupHealOverTime(this); - } - - if (hasAggro) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - } - else { - if (!tar->FindType(SE_HealOverTime)) { - botSpell = GetBestBotSpellForHealOverTime(this); - } - } - } - else if ((botClass == Class::Cleric) || (botClass == Class::Druid) || (botClass == Class::Shaman) || (botClass == Class::Paladin)) { - if (GetNumberNeedingHealedInGroup(40, true, raid) >= 2) { - botSpell = GetBestBotSpellForGroupCompleteHeal(this); - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForGroupHeal(this); - } + if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { + if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (IsGroupSpell(botSpell.SpellId)) { + BotGroupSay( + this, + fmt::format( + "Healing the group with {} [{}].", + GetSpellName(botSpell.SpellId), + GetSpellTypeNameByID(spellType) + ).c_str() + ); - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForGroupHealOverTime(this); - } + if (botClass != Class::Bard) { + const std::vector v = GatherGroupSpellTargets(tar); - if (hpr < 40 && !IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForPercentageHeal(this); + if (!IsCommandedSpell()) { + for (Mob* m : v) { + SetBotSpellRecastTimer(spellType, m, true); + } } } - else if (GetNumberNeedingHealedInGroup(60, true, raid) >= 2) { - botSpell = GetBestBotSpellForGroupHeal(this); - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForGroupHealOverTime(this); - } - - if (hpr < 40 && !IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - } - else if (hpr < 40) { - botSpell = GetBestBotSpellForPercentageHeal(this); - } - else if (hpr < 75) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); - } - else { - if (hpr < 90 && !tar->FindType(SE_HealOverTime)) { - botSpell = GetBestBotSpellForHealOverTime(this); - } - } } else { - float hpRatioToCast = 0.0f; - - switch (GetBotStance()) { - case Stance::Efficient: - case Stance::Aggressive: - hpRatioToCast = isPrimaryHealer ? 90.0f : 50.0f; - break; - case Stance::Balanced: - hpRatioToCast = isPrimaryHealer ? 95.0f : 75.0f; - break; - case Stance::Reactive: - hpRatioToCast = isPrimaryHealer ? 100.0f : 90.0f; - break; - case Stance::Burn: - case Stance::AEBurn: - hpRatioToCast = isPrimaryHealer ? 75.0f : 25.0f; - break; - default: - hpRatioToCast = isPrimaryHealer ? 100.0f : 0.0f; - break; - } - - //If we're at specified mana % or below, don't heal as hybrid - if (tar->GetHPRatio() <= hpRatioToCast) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); - } - } - - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); - } - if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetFirstBotSpellForSingleTargetHeal(this); - } - if (botSpell.SpellId == 0 && botClass == Class::Bard) { - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); - } - - if (!IsValidSpell(botSpell.SpellId)) { - return false; - } - // Can we cast this spell on this target? - if (!(spells[botSpell.SpellId].target_type==ST_GroupTeleport || spells[botSpell.SpellId].target_type == ST_Target || tar == this) - && tar->CanBuffStack(botSpell.SpellId, botLevel, true) < 0) { - return false; - } - - uint32 TempDontHealMeBeforeTime = tar->DontHealMeBefore(); - - if (IsValidSpellRange(botSpell.SpellId, tar) || botClass == Class::Bard) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost, &TempDontHealMeBeforeTime); - } - - if (casted_spell && botClass != Class::Bard) { - if (IsGroupSpell(botSpell.SpellId)) { - if (HasGroup()) { - Group *group = GetGroup(); - if (group) { - BotGroupSay( - this, - fmt::format( - "Casting {}.", - spells[botSpell.SpellId].name - ).c_str() - ); - - for (const auto& member : group->members) { - if (member && !member->qglobal) { - member->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000); - } - } - } else if (IsRaidGrouped()) { - uint32 r_group = raid->GetGroup(GetName()); - const auto msg = fmt::format("Casting {}.", spells[botSpell.SpellId].name); - raid->RaidGroupSay(msg.c_str(), GetCleanName(), Language::CommonTongue, Language::MaxValue); - std::vector raid_group_members = raid->GetRaidGroupMembers(r_group); - for (const auto& rgm : raid_group_members) { - if (rgm.member && !rgm.member->qglobal) { - rgm.member->SetDontHealMeBefore(Timer::GetCurrentTime() + 1000); - } - } - } - } else { - if (tar != this) { //we don't need spam of bots healing themselves - BotGroupSay( - this, - fmt::format( - "Casting {} on {}.", - spells[botSpell.SpellId].name, - tar->GetCleanName() - ).c_str() - ); - } - } - } - tar->SetDontHealMeBefore(Timer::GetCurrentTime() + 2000); - } - } - } - return casted_spell; -} - -bool Bot::BotCastMez(Mob* tar, uint8 botLevel, bool checked_los, BotSpell& botSpell, Raid* raid) { - bool casted_spell = false; - if (!checked_los && (!CheckLosFN(tar) || !CheckWaterLoS(tar))) { - return false; - } - - //TODO - //Check if single target or AoE mez is best - //if (TARGETS ON MT IS => 3 THEN botSpell = AoEMez) - //if (TARGETS ON MT IS <= 2 THEN botSpell = BestMez) - - botSpell = GetBestBotSpellForMez(this); - - if (!IsValidSpell(botSpell.SpellId)) { - return false; - } - - Mob* addMob = GetFirstIncomingMobToMez(this, botSpell); - - if (!addMob) { - return false; - } - - if (!(!addMob->IsImmuneToSpell(botSpell.SpellId, this) && addMob->CanBuffStack(botSpell.SpellId, botLevel, true) >= 0)) { - return false; - } + BotGroupSay( + this, + fmt::format( + "Healing {} with {} [{}].", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(botSpell.SpellId), + GetSpellTypeNameByID(spellType) + ).c_str() + ); - if (IsValidSpellRange(botSpell.SpellId, addMob)) { - casted_spell = AIDoSpellCast(botSpell.SpellIndex, addMob, botSpell.ManaCost); - } - if (casted_spell) { - if (raid) { - raid->RaidSay( - GetCleanName(), - fmt::format( - "Attempting to mesmerize {} with {}.", - addMob->GetCleanName(), - spells[botSpell.SpellId].name - ).c_str(), - 0, - 100 - ); - } else { - BotGroupSay( - this, - fmt::format( - "Attempting to mesmerize {} with {}.", - addMob->GetCleanName(), - spells[botSpell.SpellId].name - ).c_str() - ); + if (botClass != Class::Bard) { + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spellType, tar, true); + } + } + } } + + return true; } - return casted_spell; + + return false; } bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore) { @@ -1260,14 +562,14 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain // Allow bots to cast buff spells even if they are out of mana if ( RuleB(Bots, FinishBuffing) && - manaCost > hasMana && AIBot_spells[i].type & SpellType_Buff + manaCost > hasMana && AIBot_spells[i].type == BotSpellTypes::Buff ) { SetMana(manaCost); } float dist2 = 0; - if (AIBot_spells[i].type & SpellType_Escape) { + if (AIBot_spells[i].type == BotSpellTypes::Escape) { dist2 = 0; } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); @@ -1276,7 +578,7 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain ( ( ( - (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == SpellType_Heal) || + (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || spells[AIBot_spells[i].spellid].target_type ==ST_Group || spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || @@ -1305,32 +607,49 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain if (!result) { SetMana(hasMana); } - else { - if (CalcSpellRecastTimer(AIBot_spells[i].spellid) > 0) { - SetSpellRecastTimer(AIBot_spells[i].spellid); - } - } return result; } bool Bot::AI_PursueCastCheck() { + if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + return false; + } + bool result = false; - if (AIautocastspell_timer->Check(false)) { + if (GetTarget() && AIautocastspell_timer->Check(false)) { + + LogAIDetail("Bot Pursue autocast check triggered: [{}]", GetCleanName()); + LogBotPreChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); //deleteme AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - LogAIDetail("Bot Engaged (pursuing) autocast check triggered. Trying to cast offensive spells"); + if (!IsAttackAllowed(GetTarget())) { + return false; + } + + auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Pursue); + + for (auto& currentCast : castOrder) { + if (currentCast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + continue; + } + + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + continue; + } + + result = AttemptAICastSpell(currentCast.spellType); - if (!AICastSpell(GetTarget(), 100, SpellType_Snare)) { - if (!AICastSpell(GetTarget(), 100, SpellType_Lifetap) && !AICastSpell(GetTarget(), 100, SpellType_Nuke)) { + if (result) { + break; } - result = true; } if (!AIautocastspell_timer->Enabled()) { - AIautocastspell_timer->Start(RandomTimer(100, 250), false); + AIautocastspell_timer->Start(RandomTimer(RuleI(Bots, MinDelayBetweenInCombatCastAttempts), RuleI(Bots, MaxDelayBetweenInCombatCastAttempts)), false); } } @@ -1338,453 +657,104 @@ bool Bot::AI_PursueCastCheck() { } bool Bot::AI_IdleCastCheck() { + if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + return false; + } + bool result = false; if (AIautocastspell_timer->Check(false)) { + LogAIDetail("Bot Non-Engaged autocast check triggered: [{}]", GetCleanName()); + LogBotPreChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); //deleteme + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - bool pre_combat = false; + bool preCombat = false; Client* test_against = nullptr; if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient()) { test_against = GetGroup()->GetLeader()->CastToClient(); - } else if (GetOwner() && GetOwner()->IsClient()) { + } + else if (GetOwner() && GetOwner()->IsClient()) { test_against = GetOwner()->CastToClient(); } if (test_against) { - pre_combat = test_against->GetBotPrecombat(); - } - - //Ok, IdleCastCheck depends of class. - switch (GetClass()) { - // Healers WITHOUT pets will check if a heal is needed before buffing. - case Class::Cleric: - case Class::Paladin: - case Class::Ranger: { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - - result = true; - break; + preCombat = test_against->GetBotPrecombat(); } - case Class::Monk: - case Class::Rogue: - case Class::Warrior: - case Class::Berserker: { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - result = true; - break; - } - // Pets class will first cast their pet, then buffs - - case Class::Magician: - case Class::ShadowKnight: - case Class::Necromancer: - case Class::Enchanter: { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(GetPet(), 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(GetPet(), 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - } + auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Idle); - result = true; - break; - } - case Class::Druid: - case Class::Shaman: - case Class::Beastlord: { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(GetPet(), 100, SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - } + for (auto& currentCast : castOrder) { + if (currentCast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + continue; } - result = true; - break; - } - case Class::Wizard: { // This can eventually be move into the Class::Beastlord case handler once pre-combat is fully implemented - if (pre_combat) { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_PreCombatBuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } - } - } - else { - if (!AICastSpell(this, 100, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Pet)) { - if (!AICastSpell(this, 100, SpellType_Heal)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) { - } - } - } - } - } + if (!preCombat && (currentCast.spellType == BotSpellTypes::PreCombatBuff || currentCast.spellType == BotSpellTypes::PreCombatBuffSong)) { + continue; } - result = true; - break; - } - case Class::Bard: { - if (pre_combat) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(this, 100, SpellType_PreCombatBuffSong)) { - if (!AICastSpell(this, 100, SpellType_InCombatBuffSong)) { - } - } - } - } - } - else { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) { - if (!AICastSpell(this, 100, SpellType_Buff)) { - if (!AICastSpell(this, 100, SpellType_OutOfCombatBuffSong)) { - if (!AICastSpell(this, 100, SpellType_InCombatBuffSong)) { - } - } - } - } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + continue; } - result = true; - break; - } - default: - break; + result = AttemptAICastSpell(currentCast.spellType); + + if (result) { + break; + } } - if (!AIautocastspell_timer->Enabled()) - AIautocastspell_timer->Start(RandomTimer(500, 2000), false); // avg human response is much less than 5 seconds..even for non-combat situations... + if (!AIautocastspell_timer->Enabled()) { + AIautocastspell_timer->Start(RandomTimer(RuleI(Bots, MinDelayBetweenOutCombatCastAttempts), RuleI(Bots, MaxDelayBetweenOutCombatCastAttempts)), false); + } } return result; } bool Bot::AI_EngagedCastCheck() { + if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + return false; + } + bool result = false; bool failedToCast = false; if (GetTarget() && AIautocastspell_timer->Check(false)) { + LogAIDetail("Bot Engaged autocast check triggered: [{}]", GetCleanName()); + LogBotPreChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); //deleteme + AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - uint8 botClass = GetClass(); - bool mayGetAggro = HasOrMayGetAggro(); - - LogAIDetail("Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells"); - - if (botClass == Class::Cleric) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - //AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die - failedToCast = true; - } - } - } - } - } - } - } - else if (botClass == Class::Druid) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - //AIautocastspell_timer->Start(RandomTimer(100, 250), false); // Do not give healer classes a lot of time off or your tank's die - failedToCast = true; - } - } - } - } - } - } - } - } - else if (botClass == Class::Shaman) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - failedToCast = true; - } - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Ranger) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - failedToCast = true; - } - } - } - } - } - } - } - else if (botClass == Class::Beastlord) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - failedToCast = true; - } - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Wizard) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - else if (botClass == Class::Paladin) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_Heal), BotAISpellRange, SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - else if (botClass == Class::ShadowKnight) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - else if (botClass == Class::Magician) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - else if (botClass == Class::Necromancer) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Pet), SpellType_Pet)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Lifetap), SpellType_Lifetap)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetPet(), GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Enchanter) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Mez), SpellType_Mez)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), mayGetAggro?0:GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - } - } - else if (botClass == Class::Bard) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) {// Bards will use their escape songs - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_HateRedux), BotAISpellRange, SpellType_HateRedux)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Slow), SpellType_Slow)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), mayGetAggro ? 0 : GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) {// Bards will use their dot songs - if (!AICastSpell(GetTarget(), mayGetAggro ? 0 : GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) {// Bards will use their nuke songs - failedToCast = true; - } - } - } - } - } - } - } - } - } - else if (botClass == Class::Berserker) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } - } + if (!IsAttackAllowed(GetTarget())) { + return false; } - else if (botClass == Class::Monk) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } + + auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Engaged); + + for (auto& currentCast : castOrder) { + if (currentCast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + continue; } - } - else if (botClass == Class::Rogue) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Escape), SpellType_Escape)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } - } + + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + continue; } - } - else if (botClass == Class::Warrior) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_Heal), SpellType_Heal)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(SpellType_InCombatBuff), BotAISpellRange, SpellType_InCombatBuff)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Debuff), SpellType_Debuff)) { - if (!AICastSpell(this, GetChanceToCastBySpellType(SpellType_InCombatBuffSong), SpellType_InCombatBuffSong)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_DOT), SpellType_DOT)) { - if (!AICastSpell(GetTarget(), GetChanceToCastBySpellType(SpellType_Nuke), SpellType_Nuke)) { - failedToCast = true; - } - } - } - } - } + + result = AttemptAICastSpell(currentCast.spellType); + + if (result) { + break; } } if (!AIautocastspell_timer->Enabled()) { - AIautocastspell_timer->Start(RandomTimer(150, 300), false); - } - - if (!failedToCast) { - result = true; + AIautocastspell_timer->Start(RandomTimer(RuleI(Bots, MinDelayBetweenInCombatCastAttempts), RuleI(Bots, MaxDelayBetweenInCombatCastAttempts)), false); } } @@ -1819,22 +789,22 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { botSpell.ManaCost = 0; if (useFastHeals) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this, tar); if (!IsValidSpell(botSpell.SpellId)) - botSpell = GetBestBotSpellForFastHeal(this); + botSpell = GetBestBotSpellForFastHeal(this, tar); } else { - botSpell = GetBestBotSpellForPercentageHeal(this); + botSpell = GetBestBotSpellForPercentageHeal(this, tar); if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetBestBotSpellForRegularSingleTargetHeal(this); + botSpell = GetBestBotSpellForRegularSingleTargetHeal(this, tar); } if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetFirstBotSpellForSingleTargetHeal(this); + botSpell = GetFirstBotSpellForSingleTargetHeal(this, tar); } if (!IsValidSpell(botSpell.SpellId)) { - botSpell = GetFirstBotSpellBySpellType(this, SpellType_Heal); + botSpell = GetFirstBotSpellBySpellType(this, BotSpellTypes::RegularHeal); } } @@ -1879,7 +849,7 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { return castedSpell; } -std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect) { +std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect) { std::list result; if (!botCaster) { @@ -1894,13 +864,16 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEff std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect)) { + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && + (IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect)) + ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; botSpell.SpellIndex = i; @@ -1914,7 +887,7 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEff return result; } -std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType) { +std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType) { std::list result; if (!botCaster) { @@ -1929,18 +902,19 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && ( IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect) ) && - spells[botSpellList[i].spellid].target_type == targetType + (targetType == ST_TargetOptional || spells[botSpellList[i].spellid].target_type == targetType) ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; @@ -1954,7 +928,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, return result; } -std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType) { +std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType) { std::list result; if (!botCaster) { @@ -1969,13 +943,15 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellTyp std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (botSpellList[i].type & spellType) { + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; botSpell.SpellIndex = i; @@ -1989,27 +965,59 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellTyp return result; } -std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint32 spellType) { +std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE) { std::list result; if (botCaster && botCaster->AI_HasSpells()) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (botSpellList[i].type & spellType) { - BotSpell_wPriority botSpell; - botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = i; - botSpell.ManaCost = botSpellList[i].manacost; - botSpell.Priority = botSpellList[i].priority; + if (spellType == BotSpellTypes::HateRedux && botCaster->GetClass() == Class::Bard) { + if (spells[botSpellList[i].spellid].target_type != ST_Target) { + continue; + } + } - result.push_back(botSpell); + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + ) { + if (!AE && IsAnyAESpell(botSpellList[i].spellid) && !IsGroupSpell(botSpellList[i].spellid)) { + continue; + } + else if (AE && !IsAnyAESpell(botSpellList[i].spellid)) { + continue; + } + + if ( + ( + !botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez)) + ) + && (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsGroupBotSpellType(spellType))) // TODO bot rewrite - needed for ae spells? + ) { + continue; + } + + if ( + botCaster->IsCommandedSpell() || + !AE || + (spellType == BotSpellTypes::GroupCures) || + (spellType == BotSpellTypes::AEMez) || + (AE && botCaster->HasValidAETarget(botCaster, botSpellList[i].spellid, spellType, tar)) + ) { + BotSpell_wPriority botSpell; + botSpell.SpellId = botSpellList[i].spellid; + botSpell.SpellIndex = i; + botSpell.ManaCost = botSpellList[i].manacost; + botSpell.Priority = botSpellList[i].priority; + + result.push_back(botSpell); + } } } @@ -2025,7 +1033,7 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa return result; } -BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { +BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2036,16 +1044,44 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if ((botSpellList[i].type & spellType) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { - result.SpellId = botSpellList[i].spellid; - result.SpellIndex = i; - result.ManaCost = botSpellList[i].manacost; + if ( + botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + ) { + result.SpellId = botSpellList[i].spellid; + result.SpellIndex = i; + result.ManaCost = botSpellList[i].manacost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + + for (auto botSpellListItr : botSpellList) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsVeryFastHealSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { + result.SpellId = botSpellListItr.SpellId; + result.SpellIndex = botSpellListItr.SpellIndex; + result.ManaCost = botSpellListItr.ManaCost; break; } @@ -2055,7 +1091,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) { return result; } -BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) { +BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2063,11 +1099,11 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); for (auto botSpellListItr : botSpellList) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsFastHealSpell(botSpellListItr.SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId)) { + if (IsFastHealSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { result.SpellId = botSpellListItr.SpellId; result.SpellIndex = botSpellListItr.SpellIndex; result.ManaCost = botSpellListItr.ManaCost; @@ -2080,7 +1116,7 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2088,29 +1124,14 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime); - std::vector botSpellList = botCaster->AIBot_spells; + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); - for (auto botSpellListItr : botHoTSpellList) { + for (auto botSpellListItr : botSpellList) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsHealOverTimeSpell(botSpellListItr.SpellId)) { - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; - } - - if ( - botSpellList[i].spellid == botSpellListItr.SpellId && - (botSpellList[i].type & SpellType_Heal) && - botCaster->CheckSpellRecastTimer(botSpellListItr.SpellId) - ) { - result.SpellId = botSpellListItr.SpellId; - result.SpellIndex = botSpellListItr.SpellIndex; - result.ManaCost = botSpellListItr.ManaCost; - } - } + if (IsHealOverTimeSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { + result.SpellId = botSpellListItr.SpellId; + result.SpellIndex = botSpellListItr.SpellIndex; + result.ManaCost = botSpellListItr.ManaCost; break; } @@ -2120,7 +1141,7 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) { +BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2129,18 +1150,21 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) { if (botCaster && botCaster->AI_HasSpells()) { std::vector botSpellList = botCaster->AIBot_spells; - for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here continue; } - if (IsCompleteHealSpell(botSpellList[i].spellid) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { + if ( + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && + IsCompleteHealSpell(botSpellList[i].spellid) && + botCaster->CastChecks(botSpellList[i].spellid, tar, spellType) + ) { result.SpellId = botSpellList[i].spellid; result.SpellIndex = i; result.ManaCost = botSpellList[i].manacost; + break; } } @@ -2149,7 +1173,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2157,14 +1181,15 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { + if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType)) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2173,7 +1198,7 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) { +BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2181,20 +1206,15 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ( - ( - IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) || - IsFastHealSpell(botSpellListItr->SpellId) - ) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { + if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType)) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2203,7 +1223,7 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2211,17 +1231,31 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + const std::vector v = botCaster->GatherSpellTargets(); + int targetCount = 0; for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsRegularGroupHealSpell(botSpellListItr->SpellId) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { + if (IsRegularGroupHealSpell(botSpellListItr->SpellId)) { + if (!botCaster->IsCommandedSpell()) { + targetCount = 0; + + for (Mob* m : v) { + if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + ++targetCount; + } + } + + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; + } + } + result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2230,7 +1264,7 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2238,31 +1272,31 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime); - std::vector botSpellList = botCaster->AIBot_spells; + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); + const std::vector v = botCaster->GatherSpellTargets(); + int targetCount = 0; - for (std::list::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); ++botSpellListItr) { + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order if (IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) { + if (!botCaster->IsCommandedSpell()) { + targetCount = 0; - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here - continue; + for (Mob* m : v) { + if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + ++targetCount; + } } - if ( - botSpellList[i].spellid == botSpellListItr->SpellId && - (botSpellList[i].type & SpellType_Heal) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; } } + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2271,7 +1305,7 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2279,17 +1313,31 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CompleteHeal); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); + const std::vector v = botCaster->GatherSpellTargets(); + int targetCount = 0; for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsGroupCompleteHealSpell(botSpellListItr->SpellId) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { + if (IsGroupCompleteHealSpell(botSpellListItr->SpellId)) { + if (!botCaster->IsCommandedSpell()) { + targetCount = 0; + + for (Mob* m : v) { + if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + ++targetCount; + } + } + + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; + } + } + result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; + break; } } @@ -2298,7 +1346,7 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) { +BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2306,7 +1354,7 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_Mez); + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Mez); for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order @@ -2326,102 +1374,101 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) { - BotSpell result; +Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE) { + Mob* result = nullptr; - result.SpellId = 0; - result.SpellIndex = 0; - result.ManaCost = 0; + if (botCaster && botCaster->GetOwner()) { + int spellRange = (!AE ? botCaster->GetActSpellRange(spellid, spells[spellid].range) : botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range)); + int buff_count = 0; + NPC* npc = nullptr; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed); + for (auto& close_mob : botCaster->m_close_mobs) { + buff_count = 0; + npc = close_mob.second->CastToNPC(); - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsSlowSpell(botSpellListItr->SpellId) && - spells[botSpellListItr->SpellId].resist_type == RESIST_MAGIC && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; - break; + if (!npc) { + continue; } - } - } - return result; -} + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), npc, spellid)) { + continue; + } -BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) { - BotSpell result; + if (AE) { + int targetCount = 0; - result.SpellId = 0; - result.SpellIndex = 0; - result.ManaCost = 0; + for (auto& close_mob : botCaster->m_close_mobs) { + Mob* m = close_mob.second; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed); + if (npc == m) { + continue; + } - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsSlowSpell(botSpellListItr->SpellId) && - spells[botSpellListItr->SpellId].resist_type == RESIST_DISEASE && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) - ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), m, spellid)) { + continue; + } - break; - } - } - } + if (IsPBAESpell(spellid)) { + if (spellRange < Distance(botCaster->GetPosition(), m->GetPosition())) { + continue; + } + } + else { + if (spellRange < Distance(m->GetPosition(), npc->GetPosition())) { + continue; + } + } - return result; -} + if (botCaster->CastChecks(spellid, m, spellType, true, true)) { + ++targetCount; + } + + if (targetCount >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + break; + } + } -Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell) { - Mob* result = 0; + if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + continue; + } - if (botCaster && IsMesmerizeSpell(botSpell.SpellId)) { + if (zone->random.Int(1, 100) < RuleI(Bots, AEMezChance)) { + botCaster->SetSpellTypeRecastTimer(spellType, RuleI(Bots, MezFailDelay)); + return result; + } - std::list npc_list; - entity_list.GetNPCList(npc_list); + result = npc; + } + else { + if (spellRange < Distance(botCaster->GetPosition(), npc->GetPosition())) { + continue; + } - for(std::list::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) { - NPC* npc = *itr; + if (!botCaster->CastChecks(spellid, npc, spellType, true)) { + continue; + } - if (DistanceSquaredNoZ(npc->GetPosition(), botCaster->GetPosition()) <= botCaster->GetActSpellRange(botSpell.SpellId, spells[botSpell.SpellId].range)) { - if (!npc->IsMezzed()) { - if (botCaster->HasGroup()) { - Group* g = botCaster->GetGroup(); + if (zone->random.Int(1, 100) < RuleI(Bots, MezChance)) { + botCaster->SetSpellTypeRecastTimer(spellType, RuleI(Bots, MezAEFailDelay)); - if (g) { - for (int counter = 0; counter < g->GroupCount(); counter++) { - if ( - npc->IsOnHatelist(g->members[counter]) && - g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) { - result = npc; - break; - } - } - } - } + return result; } + + result = npc; } - if (result) - break; + if (result) { + botCaster->SetHasLoS(true); + + return result; + } } } return result; } -BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) { +BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2429,20 +1476,21 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) { result.ManaCost = 0; if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_SummonPet); - + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_SummonPet); std::string petType = GetBotMagicianPetType(botCaster); for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsSummonPetSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { - if (!strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length())) { + if ( + IsSummonPetSpell(botSpellListItr->SpellId) && + botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) && + !strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length()) + ) { result.SpellId = botSpellListItr->SpellId; result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; break; - } } } } @@ -2454,76 +1502,101 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { std::string result; if (botCaster) { - if (botCaster->IsPetChooser()) { - switch(botCaster->GetPetChooserID()) { - case 0: + bool epicAllowed = false; + if (RuleB(Bots, AllowMagicianEpicPet)) { + if (botCaster->GetLevel() >= RuleI(Bots, AllowMagicianEpicPetLevel)) { + if (!RuleI(Bots, RequiredMagicianEpicPetItemID)) { + epicAllowed = true; + } + else { + bool has_item = botCaster->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX; + + if (has_item) { + epicAllowed = true; + } + } + } + } + + if (botCaster->GetPetChooserID() > 0) { + switch (botCaster->GetPetChooserID()) { + case SumWater: result = std::string("SumWater"); break; - case 1: + case SumFire: result = std::string("SumFire"); break; - case 2: + case SumAir: result = std::string("SumAir"); break; - case 3: + case SumEarth: result = std::string("SumEarth"); break; - default: + case MonsterSum: result = std::string("MonsterSum"); break; + case SumMageMultiElement: + if (epicAllowed) { + result = std::string(RuleS(Bots, EpicPetSpellName)); + + if (result.empty()) { + result = "SumMageMultiElement"; + } + } + else { + result = std::string("MonsterSum"); + } + break; } } else { - if (botCaster->GetLevel() == 2) - result = std::string("SumWater"); - else if (botCaster->GetLevel() == 3) - result = std::string("SumFire"); - else if (botCaster->GetLevel() == 4) - result = std::string("SumAir"); - else if (botCaster->GetLevel() == 5) - result = std::string("SumEarth"); - else if (botCaster->GetLevel() < 30) { - // Under level 30 - int counter = zone->random.Int(0, 3); - - switch(counter) { - case 0: - result = std::string("SumWater"); - break; - case 1: - result = std::string("SumFire"); - break; - case 2: - result = std::string("SumAir"); - break; - case 3: - result = std::string("SumEarth"); - break; - default: - result = std::string("MonsterSum"); - break; + if (epicAllowed) { + result = std::string(RuleS(Bots, EpicPetSpellName)); + + if (result.empty()) { + result = "SumMageMultiElement"; } } else { - // Over level 30 - int counter = zone->random.Int(0, 4); - - switch(counter) { - case 0: + if (botCaster->GetLevel() == 2) { result = std::string("SumWater"); - break; - case 1: + } + else if (botCaster->GetLevel() == 3) { result = std::string("SumFire"); - break; - case 2: + } + else if (botCaster->GetLevel() == 4) { result = std::string("SumAir"); - break; - case 3: + } + else if (botCaster->GetLevel() == 5) { result = std::string("SumEarth"); - break; - default: - result = std::string("MonsterSum"); - break; + } + else { + int counter; + + if (botCaster->GetLevel() < 30) { + counter = zone->random.Int(1, 4); + } + else { + counter = zone->random.Int(1, 5); + } + + switch (counter) { + case 1: + result = std::string("SumWater"); + break; + case 2: + result = std::string("SumFire"); + break; + case 3: + result = std::string("SumAir"); + break; + case 4: + result = std::string("SumEarth"); + break; + default: + result = std::string("MonsterSum"); + break; + } } } } @@ -2532,24 +1605,46 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType) { +BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE, Mob* tar) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; + if (tar == nullptr) { + tar = botCaster->GetTarget(); + } + if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, targetType); + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_CurrentHP, targetType); for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if ((IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) { + if (!AE && IsAnyAESpell(botSpellListItr->SpellId) && !IsGroupSpell(botSpellListItr->SpellId)) { + continue; + } + else if (AE && !IsAnyAESpell(botSpellListItr->SpellId)) { + continue; + } - break; + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + continue; + } + + + if ( + botCaster->IsCommandedSpell() || + !AE || + (AE && botCaster->HasValidAETarget(botCaster, botSpellListItr->SpellId, spellType, tar)) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } } } } @@ -2557,7 +1652,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType return result; } -BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType) +BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE, Mob* tar) { BotSpell result; @@ -2565,19 +1660,40 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType result.SpellIndex = 0; result.ManaCost = 0; + if (tar == nullptr) { + tar = botCaster->GetTarget(); + } + if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType); + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_Stun, targetType); for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsStunSpell(botSpellListItr->SpellId) && botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) - { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; - break; + if (IsStunSpell(botSpellListItr->SpellId)) { + if (!AE && IsAnyAESpell(botSpellListItr->SpellId) && !IsGroupSpell(botSpellListItr->SpellId)) { + continue; + } + else if (AE && !IsAnyAESpell(botSpellListItr->SpellId)) { + continue; + } + + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + continue; + } + + if ( + botCaster->IsCommandedSpell() || + !AE || + (AE && botCaster->HasValidAETarget(botCaster, botSpellListItr->SpellId, spellType, tar)) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } } } } @@ -2585,7 +1701,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType return result; } -BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target) { +BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -2593,50 +1709,74 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ result.ManaCost = 0; if (botCaster && target) { + const int lureResisValue = -100; - const int maxTargetResistValue = 300; + + int32 level_mod = (target->GetLevel() - botCaster->GetLevel()) * (target->GetLevel() - botCaster->GetLevel()) / 2; + + if (target->GetLevel() - botCaster->GetLevel() < 0) { + level_mod = -level_mod; + } + const int maxTargetResistValue = botCaster->GetSpellTypeResistLimit(spellType); bool selectLureNuke = false; - if ((target->GetMR() > maxTargetResistValue) && (target->GetCR() > maxTargetResistValue) && (target->GetFR() > maxTargetResistValue)) + if (((target->GetMR() + level_mod) > maxTargetResistValue) && ((target->GetCR() + level_mod) > maxTargetResistValue) && ((target->GetFR() + level_mod) > maxTargetResistValue)) { selectLureNuke = true; + } - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, ST_Target); + std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_CurrentHP, ST_Target); BotSpell firstWizardMagicNukeSpellFound; firstWizardMagicNukeSpellFound.SpellId = 0; firstWizardMagicNukeSpellFound.SpellIndex = 0; firstWizardMagicNukeSpellFound.ManaCost = 0; + bool spellSelected = false; - for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { // Assuming all the spells have been loaded into this list by level and in descending order - bool spellSelected = false; + if (!botCaster->IsValidSpellRange(botSpellListItr->SpellId, target)) { + continue; + } - if (botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { - if (selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) { + if (selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) { + if (botCaster->CastChecks(botSpellListItr->SpellId, target, spellType)) { spellSelected = true; } - else if (IsPureNukeSpell(botSpellListItr->SpellId)) { - if (((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) - && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) - { - spellSelected = true; - } - else if (((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD) - && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) - { - spellSelected = true; - } - else if (((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE) - && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue)) - { - spellSelected = true; - } - else if ((GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && !IsStunSpell(botSpellListItr->SpellId)) { - firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId; - firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex; - firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost; - } + } + else if (!selectLureNuke && IsPureNukeSpell(botSpellListItr->SpellId)) { + if ( + ((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + spellSelected = true; + } + else if ( + ((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + spellSelected = true; + } + else if ( + ((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + spellSelected = true; + } + else if ( + (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && + (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId; + firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex; + firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost; } } @@ -2645,7 +1785,26 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ result.SpellIndex = botSpellListItr->SpellIndex; result.ManaCost = botSpellListItr->ManaCost; - break; + break; + } + } + + if (!spellSelected) { + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + // Assuming all the spells have been loaded into this list by level and in descending order + + if (botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { + if (botCaster->CastChecks(botSpellListItr->SpellId, target, spellType)) { + spellSelected = true; + } + } + if (spellSelected) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } } } @@ -2671,13 +1830,11 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (((botSpellList[i].type & SpellType_Debuff) || IsDebuffSpell(botSpellList[i].spellid)) + if (((botSpellList[i].type == BotSpellTypes::Debuff) || IsDebuffSpell(botSpellList[i].spellid)) && (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster) && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { @@ -2719,13 +1876,11 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { std::vector botSpellList = botCaster->AIBot_spells; for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { - // this is both to quit early to save cpu and to avoid casting bad spells - // Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here + if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { continue; } - if (((botSpellList[i].type & SpellType_Debuff) || IsResistDebuffSpell(botSpellList[i].spellid)) + if (((botSpellList[i].type == BotSpellTypes::Debuff) || IsResistDebuffSpell(botSpellList[i].spellid)) && ((needsMagicResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistMagic)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) || (needsColdResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistCold)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) || (needsFireResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistFire)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) @@ -2746,109 +1901,82 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { return result; } -BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { +BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell_wPriority result; - bool spellSelected = false; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (!tar) + if (!tar) { return result; + } - int countNeedsCured = 0; - bool isPoisoned = tar->FindType(SE_PoisonCounter); - bool isDiseased = tar->FindType(SE_DiseaseCounter); - bool isCursed = tar->FindType(SE_CurseCounter); - bool isCorrupted = tar->FindType(SE_CorruptionCounter); - - if (botCaster && botCaster->AI_HasSpells()) { - std::list cureList = GetPrioritizedBotSpellsBySpellType(botCaster, SpellType_Cure); - - if (tar->HasGroup()) { - Group *g = tar->GetGroup(); - - if (g) { - for( int i = 0; imembers[i] && !g->members[i]->qglobal) { - if (botCaster->GetNeedsCured(g->members[i])) - countNeedsCured++; - } + if (botCaster) { + std::list botSpellListItr = GetPrioritizedBotSpellsBySpellType(botCaster, spellType, tar); + + if (IsGroupBotSpellType(spellType)) { + const std::vector v = botCaster->GatherGroupSpellTargets(tar); + int countNeedsCured = 0; + uint16 countPoisoned = 0; + uint16 countDiseased = 0; + uint16 countCursed = 0; + uint16 countCorrupted = 0; + + for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + if (!IsValidSpell(itr->SpellId) || !IsGroupSpell(itr->SpellId)) { + continue; } - } - } - //Check for group cure first - if (countNeedsCured > 2) { - for (std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { - BotSpell selectedBotSpell = *itr; - - if (IsGroupSpell(itr->SpellId) && botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) { - if (selectedBotSpell.SpellId == 0) - continue; - - if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) { - spellSelected = true; - } - else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) { - spellSelected = true; - } - else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) { - spellSelected = true; - } - else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) { - spellSelected = true; - } - else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) { - spellSelected = true; + for (Mob* m : v) { + if (botCaster->IsCommandedSpell() || botCaster->GetNeedsCured(m)) { + if (botCaster->CastChecks(itr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + if (m->FindType(SE_PoisonCounter)) { + ++countPoisoned; + } + if (m->FindType(SE_DiseaseCounter)) { + ++countDiseased; + } + if (m->FindType(SE_CurseCounter)) { + ++countCursed; + } + if (m->FindType(SE_CorruptionCounter)) { + ++countCorrupted; + } + } } + } - if (spellSelected) - { - result.SpellId = selectedBotSpell.SpellId; - result.SpellIndex = selectedBotSpell.SpellIndex; - result.ManaCost = selectedBotSpell.ManaCost; + if ( + (countPoisoned >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) || + (countDiseased >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) || + (countCursed >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) || + (countCorrupted >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) + ) { + result.SpellId = itr->SpellId; + result.SpellIndex = itr->SpellIndex; + result.ManaCost = itr->ManaCost; - break; - } + break; } } } + else { + for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + if (!IsValidSpell(itr->SpellId) || IsGroupSpell(itr->SpellId)) { + continue; + } - //no group cure for target- try to find single target spell - if (!spellSelected) { - for(std::list::iterator itr = cureList.begin(); itr != cureList.end(); ++itr) { - BotSpell selectedBotSpell = *itr; - - if (botCaster->CheckSpellRecastTimer(selectedBotSpell.SpellId)) { - if (selectedBotSpell.SpellId == 0) - continue; - - if (isPoisoned && IsEffectInSpell(selectedBotSpell.SpellId, SE_PoisonCounter)) { - spellSelected = true; - } - else if (isDiseased && IsEffectInSpell(selectedBotSpell.SpellId, SE_DiseaseCounter)) { - spellSelected = true; - } - else if (isCursed && IsEffectInSpell(selectedBotSpell.SpellId, SE_CurseCounter)) { - spellSelected = true; - } - else if (isCorrupted && IsEffectInSpell(selectedBotSpell.SpellId, SE_CorruptionCounter)) { - spellSelected = true; - } - else if (IsEffectInSpell(selectedBotSpell.SpellId, SE_DispelDetrimental)) { - spellSelected = true; - } - - if (spellSelected) - { - result.SpellId = selectedBotSpell.SpellId; - result.SpellIndex = selectedBotSpell.SpellIndex; - result.ManaCost = selectedBotSpell.ManaCost; - - break; - } + if ( + tar->FindType(SE_PoisonCounter) && IsEffectInSpell(itr->SpellId, SE_PoisonCounter) || + tar->FindType(SE_DiseaseCounter) && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter) || + tar->FindType(SE_CurseCounter) && IsEffectInSpell(itr->SpellId, SE_CurseCounter) || + tar->FindType(SE_CorruptionCounter) && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter) + ) { + result.SpellId = itr->SpellId; + result.SpellIndex = itr->SpellIndex; + result.ManaCost = itr->ManaCost; + break; } } } @@ -2857,108 +1985,69 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) { return result; } -uint8 Bot::GetChanceToCastBySpellType(uint32 spellType) +uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adjust, move AEs to own rule? { - uint8 spell_type_index = SPELL_TYPE_COUNT; switch (spellType) { - case SpellType_Nuke: - spell_type_index = spellTypeIndexNuke; - break; - case SpellType_Heal: - spell_type_index = spellTypeIndexHeal; - break; - case SpellType_Root: - spell_type_index = spellTypeIndexRoot; - break; - case SpellType_Buff: - spell_type_index = spellTypeIndexBuff; - break; - case SpellType_Escape: - spell_type_index = spellTypeIndexEscape; - break; - case SpellType_Pet: - spell_type_index = spellTypeIndexPet; - break; - case SpellType_Lifetap: - spell_type_index = spellTypeIndexLifetap; - break; - case SpellType_Snare: - spell_type_index = spellTypeIndexSnare; - break; - case SpellType_DOT: - spell_type_index = spellTypeIndexDot; - break; - case SpellType_Dispel: - spell_type_index = spellTypeIndexDispel; - break; - case SpellType_InCombatBuff: - spell_type_index = spellTypeIndexInCombatBuff; - break; - case SpellType_Mez: - spell_type_index = spellTypeIndexMez; - break; - case SpellType_Charm: - spell_type_index = spellTypeIndexCharm; - break; - case SpellType_Slow: - spell_type_index = spellTypeIndexSlow; - break; - case SpellType_Debuff: - spell_type_index = spellTypeIndexDebuff; - break; - case SpellType_Cure: - spell_type_index = spellTypeIndexCure; - break; - case SpellType_Resurrect: - spell_type_index = spellTypeIndexResurrect; - break; - case SpellType_HateRedux: - spell_type_index = spellTypeIndexHateRedux; - break; - case SpellType_InCombatBuffSong: - spell_type_index = spellTypeIndexInCombatBuffSong; - break; - case SpellType_OutOfCombatBuffSong: - spell_type_index = spellTypeIndexOutOfCombatBuffSong; - break; - case SpellType_PreCombatBuff: - spell_type_index = spellTypeIndexPreCombatBuff; - break; - case SpellType_PreCombatBuffSong: - spell_type_index = spellTypeIndexPreCombatBuffSong; - break; - default: - break; - } - - if (spell_type_index >= SPELL_TYPE_COUNT) - return 0; - - uint8 class_index = GetClass(); - if (class_index > Class::Berserker || class_index < Class::Warrior) - return 0; - --class_index; - - uint32 stance_id = GetBotStance(); - if (!Stance::IsValid(stance_id)) { - return 0; - } - - uint8 stance_index = Stance::GetIndex(stance_id); - uint8 type_index = nHSND; - - if (HasGroup()) { - if (IsGroupHealer()/* || IsRaidHealer()*/) - type_index |= pH; - if (IsGroupSlower()/* || IsRaidSlower()*/) - type_index |= pS; - if (IsGroupNuker()/* || IsRaidNuker()*/) - type_index |= pN; - if (IsGroupDoter()/* || IsRaidDoter()*/) - type_index |= pD; - } - - return database.botdb.GetSpellCastingChance(spell_type_index, class_index, stance_index, type_index); + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEStun: + case BotSpellTypes::Nuke: + return RuleI(Bots, PercentChanceToCastNuke); + case BotSpellTypes::Root: + return RuleI(Bots, PercentChanceToCastRoot); + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::DamageShields: + return RuleI(Bots, PercentChanceToCastBuff); + case BotSpellTypes::Escape: + return RuleI(Bots, PercentChanceToCastEscape); + case BotSpellTypes::Lifetap: + return RuleI(Bots, PercentChanceToCastLifetap); + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + return RuleI(Bots, PercentChanceToCastSnare); + case BotSpellTypes::DOT: + return RuleI(Bots, PercentChanceToCastDOT); + case BotSpellTypes::Dispel: + return RuleI(Bots, PercentChanceToCastDispel); + case BotSpellTypes::InCombatBuff: + return RuleI(Bots, PercentChanceToCastInCombatBuff); + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + return RuleI(Bots, PercentChanceToCastMez); + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + return RuleI(Bots, PercentChanceToCastSlow); + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + return RuleI(Bots, PercentChanceToCastDebuff); + case BotSpellTypes::Cure: + return RuleI(Bots, PercentChanceToCastCure); + case BotSpellTypes::HateRedux: + return RuleI(Bots, PercentChanceToCastHateRedux); + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + return RuleI(Bots, PercentChanceToCastFear); + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return RuleI(Bots, PercentChanceToCastHeal); + default: + return RuleI(Bots, PercentChanceToCastOtherType); + } + + return RuleI(Bots, PercentChanceToCastOtherType); } bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { @@ -3340,7 +2429,7 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id) 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) { + if (!BOT_SPELL_TYPES_INNATE(entry.type) && entry.priority == 0) { entry.priority = 1; } @@ -3504,6 +2593,460 @@ bool Bot::IsValidSpellRange(uint16 spell_id, Mob const* tar) { if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { return true; } + + spellrange = (GetActSpellRange(spell_id, spells[spell_id].aoe_range) * GetActSpellRange(spell_id, spells[spell_id].aoe_range)); + if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { + return true; + } } + return false; } + +BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE, Mob* tar) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (!botCaster || !bodyType) { + return result; + } + + if (tar == nullptr) { + tar = botCaster->GetTarget(); + } + + switch (bodyType) { + case BodyType::Undead: + case BodyType::SummonedUndead: + case BodyType::Vampire: + result = GetBestBotSpellForNukeByTargetType(botCaster, (!AE ? ST_Undead : ST_UndeadAE), spellType, AE, tar); + break; + case BodyType::Summoned: + case BodyType::Summoned2: + case BodyType::Summoned3: + result = GetBestBotSpellForNukeByTargetType(botCaster, (!AE ? ST_Summoned : ST_SummonedAE), spellType, AE, tar); + break; + case BodyType::Animal: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Animal, spellType, AE, tar); + break; + case BodyType::Plant: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Plant, spellType, AE, tar); + break; + case BodyType::Giant: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Giant, spellType, AE, tar); + break; + case BodyType::Dragon: + result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Dragon, spellType, AE, tar); + break; + default: + break; + } + + return result; +} + +void Bot::CheckBotSpells() { + bool valid = false; + uint16 correctType; + auto spellList = BotSpellsEntriesRepository::All(content_db); + uint16 spell_id; + + for (const auto& s : spellList) { + if (!IsValidSpell(s.spell_id)) { + LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); //deleteme + continue; + } + + spell_id = s.spell_id; + + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] >= 255) { + LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); //deleteme + } + else { + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.minlevel) { + LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}." + , GetSpellName(spell_id) + , spell_id + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + , s.minlevel + ); //deleteme + + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell_id + , s.npc_spells_id + , GetSpellName(spell_id) + , spell_id + , s.minlevel + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + ); //deleteme + } + + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] < s.minlevel) { + LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}." + , GetSpellName(spell_id) + , spell_id + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + , s.minlevel + ); //deleteme + + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell_id + , s.npc_spells_id + , GetSpellName(spell_id) + , spell_id + , s.minlevel + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + ); //deleteme + } + + + if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.maxlevel) { + LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}." + , GetSpellName(spell_id) + , spell_id + , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) + , s.npc_spells_id + , s.maxlevel + ); //deleteme + } + } + + correctType = UINT16_MAX; + valid = false; + + + switch (s.type) { + case BotSpellTypes::Nuke: //DONE + if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::RegularHeal: //DONE + //if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { + if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Root: //DONE + if (IsEffectInSpell(spell_id, SE_Root)) { + valid = true; + break; + } + break; + case BotSpellTypes::Buff: //DONE + if (IsAnyBuffSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Pet: //DONE + if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { + valid = true; + break; + } + break; + case BotSpellTypes::Lifetap: //DONE + if (IsLifetapSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Snare: //DONE + if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::DOT: //DONE + if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Dispel: //DONE + if (IsDispelSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::InCombatBuff: //DONE + if ( + IsSelfConversionSpell(spell_id) || + IsAnyBuffSpell(spell_id) || + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::Mez: //DONE + if (IsMesmerizeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Charm: //DONE + if (IsCharmSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Slow: //DONE + if (IsSlowSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Debuff: //DONE + if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Cure: //DONE + if (IsCureSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::PreCombatBuff: //DONE + if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + !IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::InCombatBuffSong: //DONE + case BotSpellTypes::OutOfCombatBuffSong: //DONE + case BotSpellTypes::PreCombatBuffSong: //DONE + if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::Fear: //DONE + if (IsFearSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Escape: + if (IsEscapeSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::HateRedux: + if (IsHateReduxSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Resurrect: + if (IsEffectInSpell(spell_id, SE_Revive)) { + valid = true; + break; + } + break; + default: + break; + + } + + if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { + correctType = BotSpellTypes::Nuke; + } + //else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { + else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { + correctType = BotSpellTypes::RegularHeal; + } + else if (IsEffectInSpell(spell_id, SE_Root)) { + correctType = BotSpellTypes::Root; + } + else if (IsAnyBuffSpell(spell_id)) { + correctType = BotSpellTypes::Buff; + } + else if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { + correctType = BotSpellTypes::Pet; + } + else if (IsLifetapSpell(spell_id)) { + correctType = BotSpellTypes::Lifetap; + } + else if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { + correctType = BotSpellTypes::Snare; + } + else if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { + correctType = BotSpellTypes::DOT; + } + else if (IsDispelSpell(spell_id)) { + correctType = BotSpellTypes::Dispel; + } + else if ( + IsSelfConversionSpell(spell_id) || + IsAnyBuffSpell(spell_id) || + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) + ) { + correctType = BotSpellTypes::InCombatBuff; + } + else if (IsMesmerizeSpell(spell_id)) { + correctType = BotSpellTypes::Mez; + } + else if (IsCharmSpell(spell_id)) { + correctType = BotSpellTypes::Charm; + } + else if (IsSlowSpell(spell_id)) { + correctType = BotSpellTypes::Slow; + } + else if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { + correctType = BotSpellTypes::Debuff; + } + else if (IsCureSpell(spell_id)) { + correctType = BotSpellTypes::Cure; + } + else if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + if ( + s.type == BotSpellTypes::InCombatBuffSong || + s.type == BotSpellTypes::OutOfCombatBuffSong || + s.type == BotSpellTypes::PreCombatBuffSong + ) { + correctType = s.type; + } + else { + correctType = BotSpellTypes::OutOfCombatBuffSong; + } + } + else if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + !IsBardSong(spell_id) && + !IsEscapeSpell(spell_id) && + (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) + ) { + correctType = BotSpellTypes::PreCombatBuff; + } + else if (IsFearSpell(spell_id)) { + correctType = BotSpellTypes::Fear; + } + else if (IsEscapeSpell(spell_id)) { + correctType = BotSpellTypes::Escape; + } + else if (IsHateReduxSpell(spell_id)) { + correctType = BotSpellTypes::HateRedux; + } + else if (IsEffectInSpell(spell_id, SE_Revive)) { + correctType = BotSpellTypes::Resurrect; + } + + if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { + LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" + , GetSpellName(spell_id) + , spell_id + , GetSpellTypeNameByID(s.type) + , s.type + , GetSpellTypeNameByID(correctType) + , correctType + ); //deleteme + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spellid` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" + , correctType + , spell_id + , GetSpellName(spell_id) + , spell_id + , GetSpellTypeNameByID(s.type) + , s.type + , GetSpellTypeNameByID(correctType) + , correctType + ); //deleteme + } + } +} + +BotSpell Bot::GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Revive); + + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsResurrectSpell(botSpellListItr->SpellId) && + botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForCharm(Bot* botCaster, Mob* target, uint16 spellType) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (botCaster) { + std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Charm); + + for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsCharmSpell(botSpellListItr->SpellId) && + botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + ) { + result.SpellId = botSpellListItr->SpellId; + result.SpellIndex = botSpellListItr->SpellIndex; + result.ManaCost = botSpellListItr->ManaCost; + + break; + } + } + } + + return result; +} diff --git a/zone/client.cpp b/zone/client.cpp index 920553f5ca..fe36a60567 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -51,6 +51,7 @@ extern volatile bool RunLoops; #include "water_map.h" #include "bot_command.h" #include "string_ids.h" +#include "dialogue_window.h" #include "guild_mgr.h" #include "quest_parser_collection.h" @@ -775,6 +776,8 @@ bool Client::Save(uint8 iCommitNow) { database.SaveCharacterEXPModifier(this); + database.botdb.SaveBotSettings(this); + return true; } @@ -5871,7 +5874,7 @@ void Client::SuspendMinion(int value) { m_suspendedminion.SpellID = SpellID; - m_suspendedminion.HP = CurrentPet->GetHP();; + m_suspendedminion.HP = CurrentPet->GetHP(); m_suspendedminion.Mana = CurrentPet->GetMana(); m_suspendedminion.petpower = CurrentPet->GetPetPower(); @@ -12896,11 +12899,11 @@ void Client::AddMoneyToPPWithOverflow(uint64 copper, bool update_client) SaveCurrency(); LogDebug("Client::AddMoneyToPPWithOverflow() [{}] should have: plat:[{}] gold:[{}] silver:[{}] copper:[{}]", - GetName(), - m_pp.platinum, - m_pp.gold, - m_pp.silver, - m_pp.copper + GetName(), + m_pp.platinum, + m_pp.gold, + m_pp.silver, + m_pp.copper ); } @@ -13056,8 +13059,8 @@ void Client::ClientToNpcAggroProcess() { if (zone->CanDoCombat() && !GetFeigned() && m_client_npc_aggro_scan_timer.Check()) { int npc_scan_count = 0; - for (auto &close_mob: GetCloseMobList()) { - Mob *mob = close_mob.second; + for (auto& close_mob : GetCloseMobList()) { + Mob* mob = close_mob.second; if (!mob) { continue; } @@ -13075,3 +13078,259 @@ void Client::ClientToNpcAggroProcess() LogAggro("Checking Reverse Aggro (client->npc) scanned_npcs ([{}])", npc_scan_count); } } + +void Client::LoadDefaultBotSettings() { + // Only illusion block supported currently + SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + BotSpellSettings_Struct t; + + t.spellType = i; + t.shortName = GetSpellTypeShortNameByID(i); + t.name = GetSpellTypeNameByID(i); + t.hold = GetDefaultSpellHold(i); + t.delay = GetDefaultSpellDelay(i); + t.minThreshold = GetDefaultSpellMinThreshold(i); + t.maxThreshold = GetDefaultSpellMaxThreshold(i); + + _spellSettings.push_back(t); + + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); //deleteme + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); //deleteme + } +} + +int Client::GetDefaultBotSettings(uint8 settingType, uint16 botSetting) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + return false; + case BotSettingCategories::SpellHold: + return GetDefaultSpellHold(botSetting); + case BotSettingCategories::SpellDelay: + return GetDefaultSpellDelay(botSetting); + case BotSettingCategories::SpellMinThreshold: + return GetDefaultSpellMinThreshold(botSetting); + case BotSettingCategories::SpellMaxThreshold: + return GetDefaultSpellMaxThreshold(botSetting); + } +} + +int Client::GetBotSetting(uint8 settingType, uint16 botSetting) { + switch (settingType) { + case BotSettingCategories::SpellHold: + return GetSpellHold(botSetting); + case BotSettingCategories::SpellDelay: + return GetSpellDelay(botSetting); + case BotSettingCategories::SpellMinThreshold: + return GetSpellMinThreshold(botSetting); + case BotSettingCategories::SpellMaxThreshold: + return GetSpellMaxThreshold(botSetting); + } +} + +void Client::SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue) { + switch (settingType) { + case BotSettingCategories::BaseSetting: + SetBaseSetting(botSetting, settingValue); + break; + case BotSettingCategories::SpellHold: + SetSpellHold(botSetting, settingValue); + break; + case BotSettingCategories::SpellDelay: + SetSpellDelay(botSetting, settingValue); + break; + case BotSettingCategories::SpellMinThreshold: + SetSpellMinThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellMaxThreshold: + SetSpellMaxThreshold(botSetting, settingValue); + break; + } +} + +std::string Client::SendCommandHelpWindow( + Client* c, + std::vector description, + std::vector notes, + std::vector example_format, + std::vector examples_one, std::vector examples_two, std::vector examples_three, + std::vector actionables, + std::vector options, + std::vector options_one, std::vector options_two, std::vector options_three +) { + + unsigned stringLength = 0; + unsigned currentPlace = 0; + uint16 maxLength = RuleI(Command, MaxHelpLineLength); //character length of a line before splitting in to multiple lines + const std::string& description_color = RuleS(Command, DescriptionColor); + const std::string& description_header_color = RuleS(Command, DescriptionHeaderColor); + const std::string& alt_description_color = RuleS(Command, AltDescriptionColor); + const std::string& note_color = RuleS(Command, NoteColor); + const std::string& note_header_color = RuleS(Command, NoteHeaderColor); + const std::string& alt_note_color = RuleS(Command, AltNoteColor); + const std::string& example_color = RuleS(Command, ExampleColor); + const std::string& example_header_color = RuleS(Command, ExampleHeaderColor); + const std::string& sub_example_color = RuleS(Command, SubExampleColor); + const std::string& alt_example_color = RuleS(Command, AltExampleColor); + const std::string& sub_alt_example_color = RuleS(Command, SubAltExampleColor); + const std::string& option_color = RuleS(Command, OptionColor); + const std::string& option_header_color = RuleS(Command, OptionHeaderColor); + const std::string& sub_option_color = RuleS(Command, SubOptionColor); + const std::string& alt_option_color = RuleS(Command, AltOptionColor); + const std::string& sub_alt_option_color = RuleS(Command, SubAltOptionColor); + const std::string& actionable_color = RuleS(Command, ActionableColor); + const std::string& actionable_header_color = RuleS(Command, ActionableHeaderColor); + const std::string& alt_actionable_color = RuleS(Command, AltActionableColor); + const std::string& header_color = RuleS(Command, HeaderColor); + const std::string& secondary_header_color = RuleS(Command, SecondaryHeaderColor); + const std::string& alt_header_color = RuleS(Command, AltHeaderColor); + const std::string& filler_line_color = RuleS(Command, FillerLineColor); + + + + std::string fillerLine = "--------------------------------------------------------------------"; + std::string fillerDia = DialogueWindow::TableRow(DialogueWindow::TableCell(fmt::format("{}", DialogueWindow::ColorMessage(filler_line_color, fillerLine)))); + std::string breakLine = DialogueWindow::Break(); + std::string indent = "        "; + std::string bullet = "- "; + std::string popup_text = ""; + + /* + maxLength is how long you want lines to be before splitting them. This will look for the last space before the count and split there so words are not split mid sentence + Any SplitCommandHelpText can be be have the first string from a vector differ in color from the next strings by setting secondColor to true and assigning a color. + ex: SplitCommandHelpText(examples_one, example_color, maxLength, true, alt_example_color) + - This will make the first string from examples_one vector be the color of example_color and all following strings the color of alt_example_color + ex: SplitCommandHelpText(examples_one, example_color, maxLength) + - This will apply the color example_color to everything in examples_one vector + */ + + if (!description.empty()) { + popup_text += GetCommandHelpHeader(description_header_color, "[Description]"); + popup_text += SplitCommandHelpText(description, description_color, maxLength, true, alt_description_color); + } + + if (!notes.empty()) { + popup_text += breakLine; + popup_text += breakLine; + popup_text += GetCommandHelpHeader(note_header_color, "[Notes]"); + popup_text += SplitCommandHelpText(notes, note_color, maxLength, true, alt_note_color); + } + + if (!example_format.empty()) { + popup_text += fillerDia; + popup_text += GetCommandHelpHeader(example_header_color, "[Examples]"); + popup_text += SplitCommandHelpText(example_format, example_color, maxLength, true, alt_example_color); + } + + if (!examples_one.empty()) { + popup_text += breakLine; + popup_text += breakLine; + popup_text += SplitCommandHelpText(examples_one, sub_example_color, maxLength, true, sub_alt_example_color); + } + + if (!examples_two.empty()) { + popup_text += SplitCommandHelpText(examples_two, sub_example_color, maxLength, true, sub_alt_example_color); + } + + if (!examples_three.empty()) { + popup_text += SplitCommandHelpText(examples_three, sub_example_color, maxLength, true, sub_alt_example_color); + } + + if (!options.empty()) { + popup_text += fillerDia; + popup_text += GetCommandHelpHeader(option_header_color, "[Options]"); + popup_text += SplitCommandHelpText(options, option_color, maxLength, true, alt_option_color); + } + + if (!options_one.empty()) { + popup_text += breakLine; + popup_text += breakLine; + popup_text += SplitCommandHelpText(options_one, sub_option_color, maxLength, true, sub_alt_option_color); + } + + if (!options_two.empty()) { + popup_text += SplitCommandHelpText(options_two, sub_option_color, maxLength, true, sub_alt_option_color); + } + + if (!options_three.empty()) { + popup_text += SplitCommandHelpText(options_three, secondary_header_color, maxLength, true, sub_alt_option_color); + } + + if (!actionables.empty()) { + popup_text += fillerDia; + popup_text += GetCommandHelpHeader(actionable_header_color, "[Actionables]"); + popup_text += SplitCommandHelpText(actionables, actionable_color, maxLength, true, alt_actionable_color); + } + + popup_text = DialogueWindow::Table(popup_text); + + return popup_text; +} + +std::string Client::GetCommandHelpHeader(std::string color, std::string header) { + std::string returnText = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(color, header) + ) + ) + ); + + return returnText; +} + +std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor, std::string secondaryColor) { + std::string returnText; + + for (int i = 0; i < msg.size(); i++) { + std::vector msg_split; + int stringLength = msg[i].length() + 1; + int endCount = 0; + int newCount = 0; + int splitCount = 0; + + for (int x = 0; x < stringLength; x = endCount) { + endCount = std::min(int(stringLength), (int(x) + std::min(int(stringLength), int(maxLength)))); + + if ((stringLength - (x + 1)) > maxLength) { + for (int y = endCount; y >= x; --y) { + if (msg[i][y] == ' ') { + splitCount = y - x; + msg_split.emplace_back(msg[i].substr(x, splitCount)); + endCount = y + 1; + + break; + } + + if (y == x) { + msg_split.emplace_back(msg[i].substr(x, maxLength)); + + break; + } + } + } + else { + msg_split.emplace_back(msg[i].substr(x, (stringLength - 1) - x)); + + break; + } + } + + for (const auto& s : msg_split) { + returnText += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(((secondColor && i == 0) ? color : secondaryColor), s) + ) + ) + ); + + } + } + + return returnText; +} diff --git a/zone/client.h b/zone/client.h index 5acc388b3e..71310dd84f 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1250,6 +1250,20 @@ class Client : public Mob PendingTranslocate_Struct PendingTranslocateData; void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID); + // Help Window + std::string SendCommandHelpWindow( + Client* c, + std::vector description, + std::vector notes, + std::vector example_format, + std::vector examples_one, std::vector examples_two, std::vector examples_three, + std::vector actionables, + std::vector options, + std::vector options_one, std::vector options_two, std::vector options_three + ); + std::string GetCommandHelpHeader(std::string color, std::string header); + std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor = false, std::string secondaryColor = ""); + // Task System Methods void LoadClientTaskState(); void RemoveClientTaskState(); @@ -2219,11 +2233,29 @@ class Client : public Mob void CampAllBots(uint8 class_id = Class::None); void SpawnRaidBotsOnConnect(Raid* raid); + void LoadDefaultBotSettings(); + int GetDefaultBotSettings(uint8 settingType, uint16 botSetting); + int GetBotSetting(uint8 settingType, uint16 botSetting); + void SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue); + private: bool bot_owner_options[_booCount]; bool m_bot_pulling; bool m_bot_precombat; + uint8 fast_heal_threshold; + uint8 heal_threshold; + uint8 complete_heal_threshold; + uint8 hot_heal_threshold; + uint32 fast_heal_delay; + uint32 heal_delay; + uint32 complete_heal_delay; + uint32 hot_heal_delay; + uint32 cure_delay; + uint8 cure_min_threshold; + uint8 cure_threshold; + bool illusion_block; + bool CanTradeFVNoDropItem(); void SendMobPositions(); void PlayerTradeEventLog(Trade *t, Trade *t2); diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 1f3f809dd0..bfe4cdb58f 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -19,6 +19,10 @@ uint32 Client::GetBotCreationLimit(uint8 class_id) { uint32 bot_creation_limit = RuleI(Bots, CreationLimit); + if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { + return RuleI(Bots, MinStatusToBypassCreateLimit); + } + const auto bucket_name = fmt::format( "bot_creation_limit{}", ( @@ -67,6 +71,10 @@ int Client::GetBotSpawnLimit(uint8 class_id) { int bot_spawn_limit = RuleI(Bots, SpawnLimit); + if (Admin() >= RuleI(Bots, MinStatusToBypassSpawnLimit)) { + return RuleI(Bots, MinStatusToBypassSpawnLimit); + } + const auto bucket_name = fmt::format( "bot_spawn_limit{}", ( diff --git a/zone/client_mods.cpp b/zone/client_mods.cpp index 0387b795b5..23ef134541 100644 --- a/zone/client_mods.cpp +++ b/zone/client_mods.cpp @@ -812,7 +812,7 @@ int32 Client::CalcSTR() int32 Client::CalcSTA() { - int32 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect();; + int32 val = m_pp.STA + itembonuses.STA + spellbonuses.STA + CalcAlcoholPhysicalEffect(); int32 mod = aabonuses.STA; STA = val + mod; if (STA < 1) { @@ -827,7 +827,7 @@ int32 Client::CalcSTA() int32 Client::CalcAGI() { - int32 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect();; + int32 val = m_pp.AGI + itembonuses.AGI + spellbonuses.AGI - CalcAlcoholPhysicalEffect(); int32 mod = aabonuses.AGI; int32 str = GetSTR(); //Encumbered penalty @@ -852,7 +852,7 @@ int32 Client::CalcAGI() int32 Client::CalcDEX() { - int32 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect();; + int32 val = m_pp.DEX + itembonuses.DEX + spellbonuses.DEX - CalcAlcoholPhysicalEffect(); int32 mod = aabonuses.DEX; DEX = val + mod; if (DEX < 1) { diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 4afe679356..06c7e7c60d 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -412,7 +412,7 @@ void MapOpcodes() ConnectedOpcodes[OP_TributeUpdate] = &Client::Handle_OP_TributeUpdate; ConnectedOpcodes[OP_VetClaimRequest] = &Client::Handle_OP_VetClaimRequest; ConnectedOpcodes[OP_VoiceMacroIn] = &Client::Handle_OP_VoiceMacroIn; - ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura;; + ConnectedOpcodes[OP_UpdateAura] = &Client::Handle_OP_UpdateAura; ConnectedOpcodes[OP_WearChange] = &Client::Handle_OP_WearChange; ConnectedOpcodes[OP_WhoAllRequest] = &Client::Handle_OP_WhoAllRequest; ConnectedOpcodes[OP_WorldUnknown001] = &Client::Handle_OP_Ignore; @@ -666,6 +666,10 @@ void Client::CompleteConnect() for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effect_id[x1]) { case SE_Illusion: { + if (GetIllusionBlock()) { + break; + } + if (buffs[j1].persistant_buff) { Mob *caster = entity_list.GetMobID(buffs[j1].casterid); ApplySpellEffectIllusion(spell.id, caster, j1, spell.base_value[x1], spell.limit_value[x1], spell.max_value[x1]); @@ -1483,6 +1487,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) LogError("Error loading AA points for [{}]", GetName()); } + /* Load Bots */ + + LoadDefaultBotSettings(); + + database.botdb.LoadBotSettings(this); + if (SPDAT_RECORDS > 0) { for (uint32 z = 0; z < EQ::spells::SPELL_GEM_COUNT; z++) { if (m_pp.mem_spells[z] >= (uint32)SPDAT_RECORDS) @@ -1579,7 +1589,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) LFG = false; } - /* Load Bots */ if (RuleB(Bots, Enabled)) { database.botdb.LoadOwnerOptions(this); // TODO: mod below function for loading spawned botgroups diff --git a/zone/command.cpp b/zone/command.cpp index 513cd02180..21f57a7099 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -147,6 +147,7 @@ int command_init(void) command_add("help", "[Search Criteria] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", AccountStatus::GMImpossible, command_hotfix) || command_add("hp", "Refresh your HP bar from the server.", AccountStatus::Player, command_hp) || + command_add("illusionblock", "Controls whether or not illusion effects will land on you when cast by other players or bots", AccountStatus::Player, command_illusion_block) || command_add("instance", "Modify Instances", AccountStatus::GMMgmt, command_instance) || command_add("interrogateinv", "use [help] argument for available options", AccountStatus::Player, command_interrogateinv) || command_add("interrupt", "[Message ID] [Color] - Interrupt your casting. Arguments are optional.", AccountStatus::Guide, command_interrupt) || @@ -217,6 +218,10 @@ int command_init(void) command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", AccountStatus::Steward, command_spawn) || command_add("spawneditmass", "[Search Criteria] [Edit Option] [Edit Value] [Apply] Mass editing spawn command (Apply is optional, 0 = False, 1 = True, default is False)", AccountStatus::GMLeadAdmin, command_spawneditmass) || command_add("spawnfix", "Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || + command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, command_spell_delays) || + command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, command_spell_holds) || + command_add("spellmaxthresholds", "Controls the minimum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_max_thresholds) || + command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_min_thresholds) || command_add("stun", "[duration] - Stuns you or your target for duration", AccountStatus::GMAdmin, command_stun) || command_add("summon", "[Character Name] - Summons your corpse, NPC, or player target, or by character name if specified", AccountStatus::QuestTroupe, command_summon) || command_add("summonburiedplayercorpse", "Summons the target's oldest buried corpse, if any exist.", AccountStatus::GMAdmin, command_summonburiedplayercorpse) || @@ -841,6 +846,7 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/grid.cpp" #include "gm_commands/guild.cpp" #include "gm_commands/hp.cpp" +#include "gm_commands/illusion_block.cpp" #include "gm_commands/instance.cpp" #include "gm_commands/interrogateinv.cpp" #include "gm_commands/interrupt.cpp" @@ -909,6 +915,10 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/spawn.cpp" #include "gm_commands/spawneditmass.cpp" #include "gm_commands/spawnfix.cpp" +#include "gm_commands/spell_delays.cpp" +#include "gm_commands/spell_holds.cpp" +#include "gm_commands/spell_max_thresholds.cpp" +#include "gm_commands/spell_min_thresholds.cpp" #include "gm_commands/faction_association.cpp" #include "gm_commands/stun.cpp" #include "gm_commands/summon.cpp" diff --git a/zone/command.h b/zone/command.h index d59fdf9a5e..aa5771da99 100644 --- a/zone/command.h +++ b/zone/command.h @@ -100,6 +100,7 @@ void command_guild(Client *c, const Seperator *sep); void command_help(Client *c, const Seperator *sep); void command_hotfix(Client *c, const Seperator *sep); void command_hp(Client *c, const Seperator *sep); +void command_illusion_block(Client* c, const Seperator* sep); void command_instance(Client *c, const Seperator *sep); void command_interrogateinv(Client *c, const Seperator *sep); void command_interrupt(Client *c, const Seperator *sep); @@ -170,6 +171,10 @@ void command_shutdown(Client *c, const Seperator *sep); void command_spawn(Client *c, const Seperator *sep); void command_spawneditmass(Client *c, const Seperator *sep); void command_spawnfix(Client *c, const Seperator *sep); +void command_spell_delays(Client* c, const Seperator* sep); +void command_spell_holds(Client* c, const Seperator* sep); +void command_spell_max_thresholds(Client* c, const Seperator* sep); +void command_spell_min_thresholds(Client* c, const Seperator* sep); void command_stun(Client *c, const Seperator *sep); void command_summon(Client *c, const Seperator *sep); void command_summonburiedplayercorpse(Client *c, const Seperator *sep); diff --git a/zone/entity.cpp b/zone/entity.cpp index fab1be99f7..b34f62d3d2 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2953,6 +2953,7 @@ void EntityList::ScanCloseMobs(Mob *scanning_mob) for (auto &e : mob_list) { auto mob = e.second; + if (mob->GetID() <= 0) { continue; } diff --git a/zone/entity.h b/zone/entity.h index 33313f14ee..814a5feb5c 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -628,7 +628,7 @@ class EntityList Client* GetBotOwnerByBotID(const uint32 bot_id); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); - bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes); // TODO: Evaluate this closesly in hopes to eliminate + bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); diff --git a/zone/exp.cpp b/zone/exp.cpp index be6b8af2b8..70f72cacb5 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -1004,6 +1004,7 @@ void Client::SetLevel(uint8 set_level, bool command) } } else { SetHP(CalcMaxHP()); // Why not, lets give them a free heal + SetMana(CalcMaxMana()); } if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) { diff --git a/zone/gm_commands/illusion_block.cpp b/zone/gm_commands/illusion_block.cpp new file mode 100644 index 0000000000..cd05b965ad --- /dev/null +++ b/zone/gm_commands/illusion_block.cpp @@ -0,0 +1,31 @@ +#include "../client.h" + +void command_illusion_block(Client* c, const Seperator* sep) +{ + int arguments = sep->argnum; + if (!arguments || !strcasecmp(sep->arg[1], "help")) { + c->Message(Chat::White, "usage: #illusionblock [help | current | value]."); + c->Message(Chat::White, "note: Used to control whether or not illusion effects will land on you."); + c->Message(Chat::White, "note: A value of 0 is disabled (Allow Illusions), 1 is enabled (Block Illusions)."); + c->Message(Chat::White, "note: Use [current] to check the current setting."); + return; + } + + if (sep->IsNumber(1)) { + int setStatus = atoi(sep->arg[1]); + if (setStatus == 0 || setStatus == 1) { + c->SetIllusionBlock(setStatus); + c->Message(Chat::White, "Your Illusion Block has been %s.", (setStatus ? "enabled" : "disabled")); + } + else { + c->Message(Chat::White, "You must enter 0 for disabled or 1 for enabled."); + return; + } + } + else if (!strcasecmp(sep->arg[1], "current")) { + c->Message(Chat::White, "You're currently %s illusions.", (c->GetIllusionBlock() ? "blocking" : "allowing")); + } + else { + c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + } +} diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp new file mode 100644 index 0000000000..09b2b0c5d5 --- /dev/null +++ b/zone/gm_commands/spell_delays.cpp @@ -0,0 +1,215 @@ +#include "../command.h" + +void command_spell_delays(Client* c, const Seperator* sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 4000] or [%s cures 4000] would allow bots to cast cures on you every 4 seconds.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 1 || typeValue > 60000) { + c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current {} delay is {} seconds.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } + else { + c->SetSpellDelay(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your {} delay was set to {} seconds.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellDelay(spellType) / 1000.00 + ).c_str() + ); + } +} diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp new file mode 100644 index 0000000000..90a725dfbb --- /dev/null +++ b/zone/gm_commands/spell_holds.cpp @@ -0,0 +1,218 @@ +#include "../command.h" + +void command_spell_holds(Client *c, const Seperator *sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 1] or [%s cures 1] would prevent bots from casting cures on you.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message(Chat::White, "note: Set to 0 to unhold the given spell type."); + c->Message(Chat::White, "note: Set to 1 to hold the given spell type."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + // Enable/Disable/Current checks + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current Hold {}s status is {}.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } + else { + c->SetSpellHold(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your Hold {}s status was {}.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellHold(spellType) ? "enabled" : "disabled" + ).c_str() + ); + } +} diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp new file mode 100644 index 0000000000..a67660d2e8 --- /dev/null +++ b/zone/gm_commands/spell_max_thresholds.cpp @@ -0,0 +1,216 @@ +#include "../command.h" + +void command_spell_max_thresholds(Client* c, const Seperator* sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 95] or [%s cures 95] would allow bots to cast cures on you when you are under 95%% health.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + // Enable/Disable/Current checks + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 150) { + c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current max threshold for {}s is {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } + else { + c->SetSpellMaxThreshold(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your max threshold for {}s was set to {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMaxThreshold(spellType) + ).c_str() + ); + } +} diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp new file mode 100644 index 0000000000..1b0fcfdf2a --- /dev/null +++ b/zone/gm_commands/spell_min_thresholds.cpp @@ -0,0 +1,216 @@ +#include "../command.h" + +void command_spell_min_thresholds(Client* c, const Seperator* sep) +{ + const int arguments = sep->argnum; + if (arguments) { + const bool is_help = !strcasecmp(sep->arg[1], "help"); + + if (is_help) { + c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); + c->Message(Chat::White, "example: [%s 15 15] or [%s cures 15] would prevent bots from casting cures on you when you are under 15%% health.", sep->arg[0], sep->arg[0]); + c->Message(Chat::White, "note: Use [current] to check your current setting."); + c->Message( + Chat::White, + fmt::format( + "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("listid") || !arg1.compare("listname")) { + const std::string& color_red = "red_1"; + const std::string& color_blue = "royal_blue"; + const std::string& color_green = "forest_green"; + const std::string& bright_green = "green"; + const std::string& bright_red = "red"; + const std::string& heroic_color = "gold"; + + std::string fillerLine = "-----------"; + std::string spellTypeField = "Spell Type"; + std::string pluralS = "s"; + std::string idField = "ID"; + std::string shortnameField = "Short Name"; + + std::string popup_text = DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(bright_green, spellTypeField) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) + ) + ) + ); + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + DialogueWindow::ColorMessage(heroic_color, fillerLine) + ) + ) + ); + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + + popup_text += DialogueWindow::TableRow( + DialogueWindow::TableCell( + fmt::format( + "{}{}", + DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), + DialogueWindow::ColorMessage(color_green, pluralS) + ) + ) + + DialogueWindow::TableCell( + fmt::format( + "{}", + (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) + ) + ) + ); + } + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient("Spell Types", popup_text.c_str()); + + return; + } + + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spellType = 0; + uint32 typeValue = 0; + + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); + + if (!IsClientBotSpellType(spellType)) { + c->Message( + Chat::White, + fmt::format( + "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", + Saylink::Silent( + fmt::format("{} listid", sep->arg[0]) + ), + Saylink::Silent( + fmt::format("{} listname", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + // Enable/Disable/Current checks + if (sep->IsNumber(2)) { + typeValue = atoi(sep->arg[2]); + ++ab_arg; + if (typeValue < 0 || typeValue > 150) { + c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "Your current min threshold for {}s is {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMinThreshold(spellType) + ).c_str() + ); + } + else { + c->SetSpellMinThreshold(spellType, typeValue); + c->Message( + Chat::Green, + fmt::format( + "Your min threshold for {}s was set to {}%%.", + c->GetSpellTypeNameByID(spellType), + c->GetSpellMinThreshold(spellType) + ).c_str() + ); + } +} diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index 2cf0e7db3f..8003e13881 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -105,11 +105,6 @@ void Lua_Bot::SetExpansionBitmask(int expansion_bitmask) { self->SetExpansionBitmask(expansion_bitmask); } -void Lua_Bot::SetExpansionBitmask(int expansion_bitmask, bool save) { - Lua_Safe_Call_Void(); - self->SetExpansionBitmask(expansion_bitmask, save); -} - bool Lua_Bot::ReloadBotDataBuckets() { Lua_Safe_Call_Bool(); return DataBucket::GetDataBuckets(self); @@ -762,7 +757,6 @@ luabind::scope lua_register_bot() { .def("SetBucket", (void(Lua_Bot::*)(std::string,std::string))&Lua_Bot::SetBucket) .def("SetBucket", (void(Lua_Bot::*)(std::string,std::string,std::string))&Lua_Bot::SetBucket) .def("SetExpansionBitmask", (void(Lua_Bot::*)(int))&Lua_Bot::SetExpansionBitmask) - .def("SetExpansionBitmask", (void(Lua_Bot::*)(int,bool))&Lua_Bot::SetExpansionBitmask) .def("SetDisciplineReuseTimer", (void(Lua_Bot::*)(uint16))&Lua_Bot::SetDisciplineReuseTimer) .def("SetDisciplineReuseTimer", (void(Lua_Bot::*)(uint16, uint32))&Lua_Bot::SetDisciplineReuseTimer) .def("SetItemReuseTimer", (void(Lua_Bot::*)(uint32))&Lua_Bot::SetItemReuseTimer) diff --git a/zone/lua_bot.h b/zone/lua_bot.h index b07d14abce..7a038b7dc3 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -52,7 +52,6 @@ class Lua_Bot : public Lua_Mob void ReloadBotSpellSettings(); void RemoveBotItem(uint32 item_id); void SetExpansionBitmask(int expansion_bitmask); - void SetExpansionBitmask(int expansion_bitmask, bool save); void Signal(int signal_id); bool HasBotSpellEntry(uint16 spellid); void SendPayload(int payload_id); diff --git a/zone/merc.cpp b/zone/merc.cpp index 6200e5b895..d66501efad 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1500,7 +1500,7 @@ bool Merc::AI_IdleCastCheck() { bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { - if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { + if (BOT_SPELL_TYPES_DETRIMENTAL(iSpellTypes)) { //according to live, you can buff and heal through walls... //now with PCs, this only applies if you can TARGET the target, but // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. @@ -1569,7 +1569,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon float dist2 = 0; - if (mercSpell.type & SpellType_Escape) { + if (mercSpell.type == SpellType_Escape) { dist2 = 0; } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); diff --git a/zone/mob.cpp b/zone/mob.cpp index dd62d93291..65374ce65c 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1692,6 +1692,11 @@ void Mob::StopMoving() void Mob::StopMoving(float new_heading) { + if (IsBot()) { + CastToBot()->SetCombatJitterFlag(false); + CastToBot()->SetCombatOutOfRangeJitterFlag(false); + } + StopNavigation(); RotateTo(new_heading); @@ -4664,7 +4669,7 @@ bool Mob::CanThisClassDoubleAttack(void) const bool Mob::CanThisClassTripleAttack() const { - if (!IsClient()) { + if (!IsOfClientBot()) { return false; // When they added the real triple attack skill, mobs lost the ability to triple } else { if (RuleB(Combat, ClassicTripleAttack)) { @@ -4678,7 +4683,12 @@ bool Mob::CanThisClassTripleAttack() const ) ); } else { - return CastToClient()->HasSkill(EQ::skills::SkillTripleAttack); + if (IsClient()) { + return CastToClient()->HasSkill(EQ::skills::SkillTripleAttack); + } + else { + return GetSkill(EQ::skills::SkillTripleAttack) > 0; + } } } } @@ -4812,6 +4822,88 @@ bool Mob::PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, fl return Result; } +bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behindOnly, bool frontOnly, bool bypassLoS) { + bool Result = false; + + if (target) { + float look_heading = 0; + + min_distance = min_distance; + max_distance = max_distance; + float tempX = 0; + float tempY = 0; + float tempZ = target->GetZ(); + float bestZ = 0; + auto offset = GetZOffset(); + const float tarX = target->GetX(); + const float tarY = target->GetY(); + float tar_distance = 0; + + glm::vec3 temp_z_Position; + glm::vec4 temp_m_Position; + + const uint16 maxIterationsAllowed = 50; + uint16 counter = 0; + //LogTestDebug("Plotting for {} - Min: [{}] - Max: [{}] - BehindMob: [{}] - Taunt [{}] -- LosReq [{}]", GetCleanName(), min_distance, max_distance, behindOnly, frontOnly, bypassLoS ? "bypassed" : CastToBot()->RequiresLoSForPositioning() ? "true" : "false"); //deleteme + while (counter < maxIterationsAllowed) { + tempX = tarX + zone->random.Real(-max_distance, max_distance); + tempY = tarY + zone->random.Real(-max_distance, max_distance); + + temp_z_Position.x = tempX; + temp_z_Position.y = tempY; + temp_z_Position.z = tempZ; + bestZ = GetFixedZ(temp_z_Position); + + if (bestZ != BEST_Z_INVALID) { + tempZ = bestZ; + } + else { + //LogTestDebug("{} - Plot Failed GetFixedZ - Try #[{}].", GetCleanName(), (counter + 1)); //deleteme + counter++; + continue; + } + + temp_m_Position.x = tempX; + temp_m_Position.y = tempY; + temp_m_Position.z = tempZ; + //tar_distance = DistanceNoZ(target->GetPosition(), temp_m_Position); + tar_distance = Distance(target->GetPosition(), temp_m_Position); + + if (tar_distance > max_distance || tar_distance < min_distance) { + //LogTestDebug("{} - Plot Failed Distance - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + if (frontOnly && !InFrontMob(target, tempX, tempY)) { + //LogTestDebug("{} - Plot Failed frontOnly - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + else if (behindOnly && !BehindMob(target, tempX, tempY)) { + //LogTestDebug("{} - Plot Failed BehindMob - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + if (!bypassLoS && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, tempX, tempY, tempZ)) { + //LogTestDebug("{} - Plot Failed LoS - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + counter++; + continue; + } + //LogTestDebug("{} - Plot PASSED! - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + Result = true; + break; + } + + if (Result) { + x_dest = tempX; + y_dest = tempY; + z_dest = tempZ; + } + } + + return Result; +} + bool Mob::HateSummon() { // check if mob has ability to summon // 97% is the offical % that summoning starts on live, not 94 @@ -8574,8 +8666,9 @@ bool Mob::HasBotAttackFlag(Mob* tar) { return false; } + const uint16 scan_close_mobs_timer_moving = 6000; // 6 seconds -const uint16 scan_close_mobs_timer_idle = 60000; // 60 seconds +const uint16 scan_close_mobs_timer_idle = 60000; // 60 seconds // If the moving timer triggers, lets see if we are moving or idle to restart the appropriate dynamic timer void Mob::CheckScanCloseMobsMovingTimer() @@ -8604,6 +8697,732 @@ void Mob::CheckScanCloseMobsMovingTimer() } } +uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!Strings::ToLower(spellTypeString).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + + return UINT16_MAX; +} + +std::string Mob::GetSpellTypeNameByID(uint16 spellType) { + std::string spellTypeName = "null"; + + switch (spellType) { + case BotSpellTypes::Nuke: + spellTypeName = "Nuke"; + break; + case BotSpellTypes::RegularHeal: + spellTypeName = "Regular Heal"; + break; + case BotSpellTypes::Root: + spellTypeName = "Root"; + break; + case BotSpellTypes::Buff: + spellTypeName = "Buff"; + break; + case BotSpellTypes::Escape: + spellTypeName = "Escape"; + break; + case BotSpellTypes::Pet: + spellTypeName = "Pet"; + break; + case BotSpellTypes::Lifetap: + spellTypeName = "Lifetap"; + break; + case BotSpellTypes::Snare: + spellTypeName = "Snare"; + break; + case BotSpellTypes::DOT: + spellTypeName = "DoT"; + break; + case BotSpellTypes::Dispel: + spellTypeName = "Dispel"; + break; + case BotSpellTypes::InCombatBuff: + spellTypeName = "In-Combat Buff"; + break; + case BotSpellTypes::Mez: + spellTypeName = "Mez"; + break; + case BotSpellTypes::Charm: + spellTypeName = "Charm"; + break; + case BotSpellTypes::Slow: + spellTypeName = "Slow"; + break; + case BotSpellTypes::Debuff: + spellTypeName = "Debuff"; + break; + case BotSpellTypes::Cure: + spellTypeName = "Cure"; + break; + case BotSpellTypes::GroupCures: + spellTypeName = "Group Cure"; + break; + case BotSpellTypes::Resurrect: + spellTypeName = "Resurrect"; + break; + case BotSpellTypes::HateRedux: + spellTypeName = "Hate Reduction"; + break; + case BotSpellTypes::InCombatBuffSong: + spellTypeName = "In-Combat Buff Song"; + break; + case BotSpellTypes::OutOfCombatBuffSong: + spellTypeName = "Out-of-Combat Buff Song"; + break; + case BotSpellTypes::PreCombatBuff: + spellTypeName = "Pre-Combat Buff"; + break; + case BotSpellTypes::PreCombatBuffSong: + spellTypeName = "Pre-Combat Buff Song"; + break; + case BotSpellTypes::Fear: + spellTypeName = "Fear"; + break; + case BotSpellTypes::Stun: + spellTypeName = "Stun"; + break; + case BotSpellTypes::CompleteHeal: + spellTypeName = "Complete Heal"; + break; + case BotSpellTypes::FastHeals: + spellTypeName = "Fast Heal"; + break; + case BotSpellTypes::VeryFastHeals: + spellTypeName = "Very Fast Heal"; + break; + case BotSpellTypes::GroupHeals: + spellTypeName = "Group Heal"; + break; + case BotSpellTypes::GroupCompleteHeals: + spellTypeName = "Group Complete Heal"; + break; + case BotSpellTypes::GroupHoTHeals: + spellTypeName = "Group HoT Heal"; + break; + case BotSpellTypes::HoTHeals: + spellTypeName = "HoT Heal"; + break; + case BotSpellTypes::AENukes: + spellTypeName = "AE Nuke"; + break; + case BotSpellTypes::AERains: + spellTypeName = "AE Rain"; + break; + case BotSpellTypes::AEMez: + spellTypeName = "AE Mez"; + break; + case BotSpellTypes::AEStun: + spellTypeName = "AE Stun"; + break; + case BotSpellTypes::AEDebuff: + spellTypeName = "AE Debuff"; + break; + case BotSpellTypes::AESlow: + spellTypeName = "AE Slow"; + break; + case BotSpellTypes::AESnare: + spellTypeName = "AE Snare"; + break; + case BotSpellTypes::AEFear: + spellTypeName = "AE Fear"; + break; + case BotSpellTypes::AEDispel: + spellTypeName = "AE Dispel"; + break; + case BotSpellTypes::AERoot: + spellTypeName = "AE Root"; + break; + case BotSpellTypes::AEDoT: + spellTypeName = "AE DoT"; + break; + case BotSpellTypes::AELifetap: + spellTypeName = "AE Lifetap"; + break; + case BotSpellTypes::PBAENuke: + spellTypeName = "PBAE Nuke"; + break; + case BotSpellTypes::PetBuffs: + spellTypeName = "Pet Buff"; + break; + case BotSpellTypes::PetRegularHeals: + spellTypeName = "Pet Regular Heal"; + break; + case BotSpellTypes::PetCompleteHeals: + spellTypeName = "Pet Complete Heal"; + break; + case BotSpellTypes::PetFastHeals: + spellTypeName = "Pet Fast Heal"; + break; + case BotSpellTypes::PetVeryFastHeals: + spellTypeName = "Pet Very Fast Heal"; + break; + case BotSpellTypes::PetHoTHeals: + spellTypeName = "Pet HoT Heal"; + break; + case BotSpellTypes::DamageShields: + spellTypeName = "Damage Shield"; + break; + case BotSpellTypes::ResistBuffs: + spellTypeName = "Resist Buff"; + break; + default: + break; + } + + return spellTypeName; +} + +std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { + std::string spellTypeName = "null"; + + switch (spellType) { + case BotSpellTypes::Nuke: + spellTypeName = "nukes"; + break; + case BotSpellTypes::RegularHeal: + spellTypeName = "regularheals"; + break; + case BotSpellTypes::Root: + spellTypeName = "roots"; + break; + case BotSpellTypes::Buff: + spellTypeName = "buffs"; + break; + case BotSpellTypes::Escape: + spellTypeName = "escapes"; + break; + case BotSpellTypes::Pet: + spellTypeName = "pets"; + break; + case BotSpellTypes::Lifetap: + spellTypeName = "lifetaps"; + break; + case BotSpellTypes::Snare: + spellTypeName = "snares"; + break; + case BotSpellTypes::DOT: + spellTypeName = "dots"; + break; + case BotSpellTypes::Dispel: + spellTypeName = "dispels"; + break; + case BotSpellTypes::InCombatBuff: + spellTypeName = "incombatbuffs"; + break; + case BotSpellTypes::Mez: + spellTypeName = "mez"; + break; + case BotSpellTypes::Charm: + spellTypeName = "charms"; + break; + case BotSpellTypes::Slow: + spellTypeName = "slows"; + break; + case BotSpellTypes::Debuff: + spellTypeName = "debuffs"; + break; + case BotSpellTypes::Cure: + spellTypeName = "cures"; + break; + case BotSpellTypes::GroupCures: + spellTypeName = "groupcures"; + break; + case BotSpellTypes::Resurrect: + spellTypeName = "resurrect"; + break; + case BotSpellTypes::HateRedux: + spellTypeName = "hateredux"; + break; + case BotSpellTypes::InCombatBuffSong: + spellTypeName = "incombatbuffsongs"; + break; + case BotSpellTypes::OutOfCombatBuffSong: + spellTypeName = "outofcombatbuffsongs"; + break; + case BotSpellTypes::PreCombatBuff: + spellTypeName = "precombatbuffs"; + break; + case BotSpellTypes::PreCombatBuffSong: + spellTypeName = "precombatbuffsongs"; + break; + case BotSpellTypes::Fear: + spellTypeName = "fears"; + break; + case BotSpellTypes::Stun: + spellTypeName = "stuns"; + break; + case BotSpellTypes::CompleteHeal: + spellTypeName = "completeheals"; + break; + case BotSpellTypes::FastHeals: + spellTypeName = "fastheals"; + break; + case BotSpellTypes::VeryFastHeals: + spellTypeName = "veryfastheals"; + break; + case BotSpellTypes::GroupHeals: + spellTypeName = "groupheals"; + break; + case BotSpellTypes::GroupCompleteHeals: + spellTypeName = "groupcompleteheals"; + break; + case BotSpellTypes::GroupHoTHeals: + spellTypeName = "grouphotheals"; + break; + case BotSpellTypes::HoTHeals: + spellTypeName = "hotheals"; + break; + case BotSpellTypes::AENukes: + spellTypeName = "aenukes"; + break; + case BotSpellTypes::AERains: + spellTypeName = "aerains"; + break; + case BotSpellTypes::AEMez: + spellTypeName = "aemez"; + break; + case BotSpellTypes::AEStun: + spellTypeName = "aestuns"; + break; + case BotSpellTypes::AEDebuff: + spellTypeName = "aedebuffs"; + break; + case BotSpellTypes::AESlow: + spellTypeName = "aeslows"; + break; + case BotSpellTypes::AESnare: + spellTypeName = "aesnares"; + break; + case BotSpellTypes::AEFear: + spellTypeName = "aefears"; + break; + case BotSpellTypes::AEDispel: + spellTypeName = "aedispels"; + break; + case BotSpellTypes::AERoot: + spellTypeName = "aeroots"; + break; + case BotSpellTypes::AEDoT: + spellTypeName = "aedots"; + break; + case BotSpellTypes::AELifetap: + spellTypeName = "aelifetaps"; + break; + case BotSpellTypes::PBAENuke: + spellTypeName = "pbaenukes"; + break; + case BotSpellTypes::PetBuffs: + spellTypeName = "petbuffs"; + break; + case BotSpellTypes::PetRegularHeals: + spellTypeName = "petregularheals"; + break; + case BotSpellTypes::PetCompleteHeals: + spellTypeName = "petcompleteheals"; + break; + case BotSpellTypes::PetFastHeals: + spellTypeName = "petfastheals"; + break; + case BotSpellTypes::PetVeryFastHeals: + spellTypeName = "petveryfastheals"; + break; + case BotSpellTypes::PetHoTHeals: + spellTypeName = "pethotheals"; + break; + case BotSpellTypes::DamageShields: + spellTypeName = "damageshields"; + break; + case BotSpellTypes::ResistBuffs: + spellTypeName = "resistbuffs"; + break; + default: + break; + } + + return spellTypeName; +} + +bool Mob::GetDefaultSpellHold(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEMez: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + return true; + case BotSpellTypes::Snare: + if (GetClass() == Class::Wizard) { + return true; + } + else { + return false; + } + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: + if (GetClass() == Class::Bard) { + return true; + } + else { + return false; + } + default: + return false; + } +} + +uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return 1500; + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return 2500; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return 4000; + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + return 6000; + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 8000; + case BotSpellTypes::Fear: + case BotSpellTypes::AEFear: + return 15000; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return 22000; + default: + return 1; + } +} + +uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + return 20; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + return 35; + case BotSpellTypes::Charm: + return 50; + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + return 85; + default: + return 0; + } +} + +uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Escape: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return 25; + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return 40; + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return 60; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 80; + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + return 99; + case BotSpellTypes::Buff: + case BotSpellTypes::Charm: + case BotSpellTypes::Cure: + case BotSpellTypes::DamageShields: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::Resurrect: + return 100; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + if (GetClass() == Class::Necromancer || GetClass() == Class::Shaman) { + return 60; + } + else { + return 90; + } + default: + return 100; + } +} + +void Mob::SetSpellHold(uint16 spellType, bool holdStatus) { + _spellSettings[spellType].hold = holdStatus; +} + +void Mob::SetSpellDelay(uint16 spellType, uint16 delayValue) { + _spellSettings[spellType].delay = delayValue; +} + +void Mob::SetSpellMinThreshold(uint16 spellType, uint8 thresholdValue) { + _spellSettings[spellType].minThreshold = thresholdValue; +} + +void Mob::SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue) { + _spellSettings[spellType].maxThreshold = thresholdValue; +} + +void Mob::SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime) { + _spellSettings[spellType].recastTimer.Start(recastTime); + LogBotDelayChecksDetail("{} says, 'My {} Delay was to {} seconds.'" + , GetCleanName() + , GetSpellTypeNameByID(spellType) + , (recastTime / 1000.00) + ); //deleteme +} + +void Mob::StartBotSpellTimers() { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + _spellSettings[i].recastTimer.Start(); + } +} + +void Mob::DisableBotSpellTimers() { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + _spellSettings[i].recastTimer.Disable(); + } +} + +bool Mob::GetUltimateSpellHold(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellHold(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellHold(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellHold(spellType); + } + + return GetSpellHold(spellType); +} + +uint16 Mob::GetUltimateSpellDelay(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellDelay(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellDelay(spellType); + } + + return GetSpellDelay(spellType); +} + +bool Mob::GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar) { + if (!tar) { + return SpellTypeRecastCheck(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->SpellTypeRecastCheck(spellType); + } + + return SpellTypeRecastCheck(spellType); +} + +uint8 Mob::GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellMinThreshold(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellMinThreshold(spellType); + } + + return GetSpellMinThreshold(spellType); +} + +uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar) { + if (!tar) { + return GetSpellMaxThreshold(spellType); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spellType)); + } + + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + return tar->GetSpellMaxThreshold(spellType); + } + + return GetSpellMaxThreshold(spellType); +} + +uint16 Mob::GetPetSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + default: + break; + } + + return spellType; +} + +uint8 Mob::GetHPRatioForSpellType(uint16 spellType, Mob* tar) { + switch (spellType) { + case BotSpellTypes::Escape: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + return GetHPRatio(); + default: + return tar->GetHPRatio(); + } + + return tar->GetHPRatio(); +} + +void Mob::SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue) { + if (!IsOfClientBot()) { + return; + } + + if (IsClient()) { + CastToClient()->SetBotSetting(settingType, botSetting, settingValue); + return; + } + + if (IsBot()) { + CastToBot()->SetBotSetting(settingType, botSetting, settingValue); + return; + } + + return; +} + +void Mob::SetBaseSetting(uint16 baseSetting, int settingValue) { + switch (baseSetting) { + case BotBaseSettings::IllusionBlock: + SetIllusionBlock(settingValue); + break; + default: + break; + } +} + +bool Mob::TargetValidation(Mob* other) { + if (!other || GetAppearance() == eaDead) { + return false; + } + + return true; +} + std::unordered_map &Mob::GetCloseMobList(float distance) { return entity_list.GetCloseMobList(this, distance); diff --git a/zone/mob.h b/zone/mob.h index 297aa80cba..a9d4e58577 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -93,6 +93,28 @@ struct AppearanceStruct { uint8 texture = UINT8_MAX; }; +struct BotSpellSettings_Struct +{ + uint16 spellType; // type ID of bot category + std::string shortName; // type short name of bot category + std::string name; // type name of bot category + bool hold; // 0 = allow spell type, 1 = hold spell type + uint16 delay; // delay between casts of spell type, 1ms-60,000ms + uint8 minThreshold; // minimum target health threshold to allow casting of spell type + uint8 maxThreshold; // maximum target health threshold to allow casting of spell type + uint16 resistLimit; // resist limit to skip spell type + bool aggroCheck; // whether or not to check for possible aggro before casting + uint8 minManaPct; // lower mana percentage limit to allow spell cast + uint8 maxManaPct; // upper mana percentage limit to allow spell cast + uint8 minHPPct; // lower HP percentage limit to allow spell cast + uint8 maxHPPct; // upper HP percentage limit to allow spell cast + uint16 idlePriority; // idle priority of the spell type + uint16 engagedPriority; // engaged priority of the spell type + uint16 pursuePriority; // pursue priority of the spell type + uint16 AEOrGroupTargetCount; // require target count to cast an AE or Group spell type + Timer recastTimer; // recast timer based off delay +}; + class DataBucketKey; class Mob : public Entity { public: @@ -208,6 +230,8 @@ class Mob : public Entity { // Bot attack flag Timer bot_attack_flag_timer; + std::vector _spellSettings; + //Somewhat sorted: needs documenting! //Attack @@ -403,6 +427,51 @@ class Mob : public Entity { virtual bool CheckFizzle(uint16 spell_id); virtual bool CheckSpellLevelRestriction(Mob *caster, uint16 spell_id); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); + + virtual bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); + + inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient() ? true : _spellSettings[spellType].recastTimer.GetRemainingTime() > 0 ? false : true); } + + uint16 GetSpellTypeIDByShortName(std::string spellTypeString); + + std::string GetSpellTypeNameByID(uint16 spellType); + std::string GetSpellTypeShortNameByID(uint16 spellType); + + bool GetDefaultSpellHold(uint16 spellType); + uint16 GetDefaultSpellDelay(uint16 spellType); + uint8 GetDefaultSpellMinThreshold(uint16 spellType); + uint8 GetDefaultSpellMaxThreshold(uint16 spellType); + + inline bool GetSpellHold(uint16 spellType) const { return _spellSettings[spellType].hold; } + void SetSpellHold(uint16 spellType, bool holdStatus); + inline uint16 GetSpellDelay(uint16 spellType) const { return _spellSettings[spellType].delay; } + void SetSpellDelay(uint16 spellType, uint16 delayValue); + inline uint8 GetSpellMinThreshold(uint16 spellType) const { return _spellSettings[spellType].minThreshold; } + void SetSpellMinThreshold(uint16 spellType, uint8 thresholdValue); + inline uint8 GetSpellMaxThreshold(uint16 spellType) const { return _spellSettings[spellType].maxThreshold; } + void SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue); + + inline uint16 GetSpellTypeRecastTimer(uint16 spellType) { return _spellSettings[spellType].recastTimer.GetRemainingTime(); } + void SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime); + + uint8 GetHPRatioForSpellType(uint16 spellType, Mob* tar); + bool GetUltimateSpellHold(uint16 spellType, Mob* tar); + uint16 GetUltimateSpellDelay(uint16 spellType, Mob* tar); + bool GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar); + uint8 GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar); + uint8 GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar); + + uint16 GetPetSpellType(uint16 spellType); + + void DisableBotSpellTimers(); + void StartBotSpellTimers(); + + void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); + void SetBaseSetting(uint16 baseSetting, int settingValue); + + void SetIllusionBlock(bool value) { _illusionBlock = value; } + bool GetIllusionBlock() const { return _illusionBlock; } + virtual float GetAOERange(uint16 spell_id); void InterruptSpell(uint16 spellid = SPELL_UNKNOWN); void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN); @@ -791,6 +860,11 @@ class Mob : public Entity { bool CheckLosFN(float posX, float posY, float posZ, float mobSize); static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget); virtual bool CheckWaterLoS(Mob* m); + bool CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ); + bool CheckLosCheat(Mob* who, Mob* other); + bool CheckLosCheatExempt(Mob* who, Mob* other); + bool DoLosChecks(Mob* who, Mob* other); + bool TargetValidation(Mob* other); inline void SetLastLosState(bool value) { last_los_check = value; } inline bool CheckLastLosState() const { return last_los_check; } std::string GetMobDescription(); @@ -854,6 +928,7 @@ class Mob : public Entity { void ShowStats(Client* client); void ShowBuffs(Client* c); bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true); + bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behindOnly = false, bool frontOnly = false, bool bypassLoS = false); virtual int GetKillExpMod() const { return 100; } // aura functions @@ -1252,13 +1327,30 @@ class Mob : public Entity { void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; } + inline uint32 DontGroupHealMeBefore() const { return pDontGroupHealMeBefore; } + inline uint32 DontGroupHoTHealMeBefore() const { return pDontGroupHoTHealMeBefore; } + inline uint32 DontRegularHealMeBefore() const { return pDontRegularHealMeBefore; } + inline uint32 DontVeryFastHealMeBefore() const { return pDontVeryFastHealMeBefore; } + inline uint32 DontFastHealMeBefore() const { return pDontFastHealMeBefore; } + inline uint32 DontCompleteHealMeBefore() const { return pDontCompleteHealMeBefore; } + inline uint32 DontGroupCompleteHealMeBefore() const { return pDontGroupCompleteHealMeBefore; } + inline uint32 DontHotHealMeBefore() const { return pDontHotHealMeBefore; } inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; } inline uint32 DontDotMeBefore() const { return pDontDotMeBefore; } inline uint32 DontRootMeBefore() const { return pDontRootMeBefore; } inline uint32 DontSnareMeBefore() const { return pDontSnareMeBefore; } inline uint32 DontCureMeBefore() const { return pDontCureMeBefore; } + void SetDontRootMeBefore(uint32 time) { pDontRootMeBefore = time; } void SetDontHealMeBefore(uint32 time) { pDontHealMeBefore = time; } + void SetDontGroupHealMeBefore(uint32 time) { pDontGroupHealMeBefore = time; } + void SetDontGroupHoTHealMeBefore(uint32 time) { pDontGroupHoTHealMeBefore = time; } + void SetDontRegularHealMeBefore(uint32 time) { pDontRegularHealMeBefore = time; } + void SetDontVeryFastHealMeBefore(uint32 time) { pDontVeryFastHealMeBefore = time; } + void SetDontFastHealMeBefore(uint32 time) { pDontFastHealMeBefore = time; } + void SetDontCompleteHealMeBefore(uint32 time) { pDontCompleteHealMeBefore = time; } + void SetDontGroupCompleteHealMeBefore(uint32 time) { pDontGroupCompleteHealMeBefore = time; } + void SetDontHotHealMeBefore(uint32 time) { pDontHotHealMeBefore = time; } void SetDontBuffMeBefore(uint32 time) { pDontBuffMeBefore = time; } void SetDontDotMeBefore(uint32 time) { pDontDotMeBefore = time; } void SetDontSnareMeBefore(uint32 time) { pDontSnareMeBefore = time; } @@ -1858,6 +1950,14 @@ class Mob : public Entity { bool pause_timer_complete; bool DistractedFromGrid; uint32 pDontHealMeBefore; + uint32 pDontGroupHealMeBefore; + uint32 pDontGroupHoTHealMeBefore; + uint32 pDontRegularHealMeBefore; + uint32 pDontVeryFastHealMeBefore; + uint32 pDontFastHealMeBefore; + uint32 pDontCompleteHealMeBefore; + uint32 pDontGroupCompleteHealMeBefore; + uint32 pDontHotHealMeBefore; uint32 pDontBuffMeBefore; uint32 pDontDotMeBefore; uint32 pDontRootMeBefore; @@ -1880,6 +1980,9 @@ class Mob : public Entity { //bot attack flags std::vector bot_attack_flags; + //bot related settings + bool _illusionBlock; + glm::vec3 m_TargetRing; GravityBehavior flymode; diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index 86f1045653..f24134c6a9 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -425,11 +425,6 @@ void Perl_Bot_SetExpansionBitmask(Bot* self, int expansion_bitmask) self->SetExpansionBitmask(expansion_bitmask); } -void Perl_Bot_SetExpansionBitmask(Bot* self, int expansion_bitmask, bool save) -{ - self->SetExpansionBitmask(expansion_bitmask, save); -} - void Perl_Bot_SetSpellDuration(Bot* self, int spell_id) { self->SetSpellDuration(spell_id); @@ -716,7 +711,6 @@ void perl_register_bot() package.add("SendPayload", (void(*)(Bot*, int, std::string))&Perl_Bot_SendPayload); package.add("SendSpellAnim", &Perl_Bot_SendSpellAnim); package.add("SetExpansionBitmask", (void(*)(Bot*, int))&Perl_Bot_SetExpansionBitmask); - package.add("SetExpansionBitmask", (void(*)(Bot*, int, bool))&Perl_Bot_SetExpansionBitmask); package.add("SetDisciplineReuseTimer", (void(*)(Bot*, uint16))&Perl_Bot_SetDisciplineReuseTimer); package.add("SetDisciplineReuseTimer", (void(*)(Bot*, uint16, uint32))&Perl_Bot_SetDisciplineReuseTimer); package.add("SetItemReuseTimer", (void(*)(Bot*, uint32))&Perl_Bot_SetItemReuseTimer); diff --git a/zone/perl_client.cpp b/zone/perl_client.cpp index 8bb39afda2..528445d419 100644 --- a/zone/perl_client.cpp +++ b/zone/perl_client.cpp @@ -1144,7 +1144,7 @@ void Perl_Client_SetStartZone(Client* self, uint32 zone_id, float x, float y, fl void Perl_Client_KeyRingAdd(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items { - self->KeyRingAdd(item_id);; + self->KeyRingAdd(item_id); } bool Perl_Client_KeyRingCheck(Client* self, uint32 item_id) // @categories Account and Character, Inventory and Items @@ -1154,7 +1154,7 @@ bool Perl_Client_KeyRingCheck(Client* self, uint32 item_id) // @categories Accou void Perl_Client_AddPVPPoints(Client* self, uint32 points) // @categories Currency and Points { - self->AddPVPPoints(points);; + self->AddPVPPoints(points); } void Perl_Client_AddCrystals(Client* self, uint32 radiant_count, uint32 ebon_count) // @categories Currency and Points diff --git a/zone/perl_npc.cpp b/zone/perl_npc.cpp index b47b8c0e8d..4f0999376c 100644 --- a/zone/perl_npc.cpp +++ b/zone/perl_npc.cpp @@ -892,7 +892,7 @@ void perl_register_npc() package.add("IsGuarding", &Perl_NPC_IsGuarding); package.add("IsLDoNLocked", &Perl_NPC_IsLDoNLocked); package.add("IsLDoNTrapped", &Perl_NPC_IsLDoNTrapped); - package.add("IsLDoNTrapDetected", &Perl_NPC_IsLDoNTrapDetected);; + package.add("IsLDoNTrapDetected", &Perl_NPC_IsLDoNTrapDetected); package.add("IsOnHatelist", &Perl_NPC_IsOnHatelist); package.add("IsRaidTarget", &Perl_NPC_IsRaidTarget); package.add("IsRareSpawn", &Perl_NPC_IsRareSpawn); diff --git a/zone/questmgr.cpp b/zone/questmgr.cpp index ad29a76a94..6be160943d 100644 --- a/zone/questmgr.cpp +++ b/zone/questmgr.cpp @@ -2857,7 +2857,7 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level initiator->Message( Chat::White, fmt::format( - "{} has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name.", + "{} has invalid characters. You can use only the A-Z, a-z and _ characters in a bot name and it must be between 4 and 15 characters long.", new_bot->GetCleanName() ).c_str() ); diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 7940a7c8ec..df0f745428 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1483,6 +1483,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); #endif + if (caster && caster->IsOfClientBot() && GetIllusionBlock()) { + break; + } + ApplySpellEffectIllusion(spell_id, caster, buffslot, spells[spell_id].base_value[i], spells[spell_id].limit_value[i], spells[spell_id].max_value[i]); break; } @@ -1492,6 +1496,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Illusion Copy"); #endif + if (caster && caster->IsOfClientBot() && GetIllusionBlock()) { + break; + } + if(caster && caster->GetTarget()){ SendIllusionPacket ( @@ -10660,7 +10668,7 @@ int Mob::GetBuffStatValueBySlot(uint8 slot, const char* stat_identifier) if (id == "caster_level") { return buffs[slot].casterlevel; } else if (id == "spell_id") { return buffs[slot].spellid; } - else if (id == "caster_id") { return buffs[slot].spellid;; } + else if (id == "caster_id") { return buffs[slot].spellid; } else if (id == "ticsremaining") { return buffs[slot].ticsremaining; } else if (id == "counters") { return buffs[slot].counters; } else if (id == "hit_number") { return buffs[slot].hit_number; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 15d1e55745..292106f77a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -466,7 +466,6 @@ bool Mob::DoCastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, CastedSpellFinished(spell_id, target_id, slot, mana_cost, item_slot, resist_adjust); // return true; } - // ok we know it has a cast time so we can start the timer now spellend_timer.Start(cast_time); @@ -1273,6 +1272,17 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) EQApplicationPacket *outapp = nullptr; uint16 message_other; bool bard_song_mode = false; //has the bard song gone to auto repeat mode + + if (IsBot()) { + CastToBot()->SetCastedSpellType(UINT16_MAX); + } + + if (IsBot() && IsValidSpell(spellid)) { + if (CastToBot()->CheckSpellRecastTimer(spellid)) { + CastToBot()->ClearSpellRecastTimer(spellid); + } + } + if (!IsValidSpell(spellid)) { if (bardsong) { spellid = bardsong; @@ -2074,7 +2084,17 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce } case ST_Pet: { - spell_target = GetPet(); + if ( + !( + IsBot() && + spell_target && + spell_target->GetOwner() != this && + RuleB(Bots, CanCastPetOnlyOnOthersPets) + ) + ) { + + spell_target = GetPet(); + } if(!spell_target) { LogSpells("Spell [{}] canceled: invalid target (no pet)", spell_id); @@ -2821,6 +2841,13 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in } } } + + if (IsBot() && !isproc && !IsFromTriggeredSpell(slot, inventory_slot) && IsValidSpell(spell_id)) { + if (spells[spell_id].recast_time > 1000 && !spells[spell_id].is_discipline) { + CastToBot()->SetSpellRecastTimer(spell_id); + } + } + /* Set Recast Timer on item clicks, including augmenets. */ @@ -3412,12 +3439,17 @@ bool Mob::CheckSpellLevelRestriction(Mob *caster, uint16 spell_id) bool can_cast = true; // NON GM clients might be restricted by rule setting - if (caster->IsClient()) { + if (caster->IsOfClientBot()) { if (IsClient()) { // Only restrict client on client for this rule if (RuleB(Spells, BuffLevelRestrictions)) { check_for_restrictions = true; } } + else if (IsBot()) { + if (RuleB(Bots, BotBuffLevelRestrictions)) { + check_for_restrictions = true; + } + } } // NPCS might be restricted by rule setting else if (RuleB(Spells, NPCBuffLevelRestrictions)) { @@ -3564,6 +3596,19 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid ); } + if (caster->IsBot() && RuleB(Bots, BotsUseLiveBlockedMessage) && caster->GetClass() != Class::Bard) { + caster->GetOwner()->Message( + Chat::Red, + fmt::format( + "{}'s {} did not take hold on {}. (Blocked by {}.)", + caster->GetCleanName(), + spells[spell_id].name, + GetName(), + spells[curbuf.spellid].name + ).c_str() + ); + } + std::function f = [&]() { return fmt::format( "{} {}", @@ -3758,9 +3803,15 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) continue; } - if(curbuf.spellid == spellid) - return(-1); //do not recast a buff we already have on, we recast fast enough that we dont need to refresh our buffs - + if (IsBot() && (GetClass() == Class::Bard) && curbuf.spellid == spellid && curbuf.ticsremaining == 0 && curbuf.casterid == GetID()) { + LogAI("Bard check for song, spell [{}] has [{}] ticks remaining.", spellid, curbuf.ticsremaining); + firstfree = i; + return firstfree; + } + else { + if (curbuf.spellid == spellid) + return(-1); //do not recast a buff we already have on, we recast fast enough that we dont need to refresh our buffs + } // there's a buff in this slot ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spellid, caster_level, nullptr, nullptr, i); if(ret == 1) { @@ -4406,6 +4457,18 @@ bool Mob::SpellOnTarget( } else { MessageString(Chat::SpellFailure, TARGET_RESISTED, spells[spell_id].name); spelltar->MessageString(Chat::SpellFailure, YOU_RESIST, spells[spell_id].name); + + if (IsBot() && RuleB(Bots, ShowResistMessagesToOwner)) { + CastToBot()->GetBotOwner()->Message + (Chat::SpellFailure, + fmt::format( + "{} resisted {}'s spell: {}.", + spelltar->GetCleanName(), + GetCleanName(), + spells[spell_id].name + ).c_str() + ); + } } if (spelltar->IsAIControlled()) { @@ -4624,6 +4687,14 @@ bool Mob::SpellOnTarget( LogSpells("Cast of [{}] by [{}] on [{}] complete successfully", spell_id, GetName(), spelltar->GetName()); + if (IsBot() && (CastToBot()->GetCastedSpellType() != UINT16_MAX)) { + if (!CastToBot()->IsCommandedSpell()) { + CastToBot()->SetBotSpellRecastTimer(CastToBot()->GetCastedSpellType(), spelltar); + } + + CastToBot()->SetCastedSpellType(UINT16_MAX); + } + return true; } @@ -7426,3 +7497,123 @@ bool Mob::CheckWaterLoS(Mob* m) zone->watermap->InLiquid(m->GetPosition()) ); } + +bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) +{ + int effect_index; + + if (caster == nullptr) + return(false); + + //TODO: this function loops through the effect list for + //this spell like 10 times, this could easily be consolidated + //into one loop through with a switch statement. + + LogSpells("Checking to see if we are immune to spell [{}] cast by [{}]", spell_id, caster->GetName()); + + if (!IsValidSpell(spell_id)) + return true; + + if (IsBeneficialSpell(spell_id) && (caster->GetNPCTypeID())) //then skip the rest, stop NPCs aggroing each other with buff spells. 2013-03-05 + return false; + + if (IsMesmerizeSpell(spell_id)) + { + if (GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { + return true; + } + + // check max level for spell + effect_index = GetSpellEffectIndex(spell_id, SE_Mez); + assert(effect_index >= 0); + // NPCs get to ignore the max level + if ((GetLevel() > spells[spell_id].max_value[effect_index]) && + (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))) + { + return true; + } + } + + // slow and haste spells + if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) + { + return true; + } + + // client vs client fear + if (IsEffectInSpell(spell_id, SE_Fear)) + { + effect_index = GetSpellEffectIndex(spell_id, SE_Fear); + if (GetSpecialAbility(SpecialAbility::FearImmunity)) { + return true; + } + else if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) + { + LogSpells("Clients cannot fear eachother!"); + caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up + return true; + } + else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) + { + return true; + } + else if (CheckAATimer(aaTimerWarcry)) + { + return true; + } + } + + if (IsCharmSpell(spell_id)) + { + if (GetSpecialAbility(SpecialAbility::CharmImmunity)) + { + return true; + } + + if (this == caster) + { + return true; + } + + //let npcs cast whatever charm on anyone + if (!caster->IsNPC()) + { + // check level limit of charm spell + effect_index = GetSpellEffectIndex(spell_id, SE_Charm); + assert(effect_index >= 0); + if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) + { + return true; + } + } + } + + if + ( + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) + { + if (GetSpecialAbility(SpecialAbility::SnareImmunity)) { + return true; + } + } + + if (IsLifetapSpell(spell_id)) + { + if (this == caster) + { + return true; + } + } + + if (IsSacrificeSpell(spell_id)) + { + if (this == caster) + { + return true; + } + } + + return false; +} From 9b87aaf39b1ce32e175c5915eb864554055accc6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 31 Oct 2024 07:32:16 -0500 Subject: [PATCH 099/394] More fixes TGB, ^cast, group/ae checks, in group/raid checks, inviting others bots to group, group disband fix, prevent rogue bs spam, ^follow fixes and cleanup, follow owner only by default when joining raid/group, group buff fixes for bots, range fixes for group buffs --- common/ruletypes.h | 4 +- common/spdat.cpp | 76 ++- common/spdat.h | 3 + zone/bot.cpp | 891 +++++++++++++++----------------- zone/bot.h | 8 +- zone/bot_commands/bot.cpp | 23 +- zone/bot_commands/cast.cpp | 12 +- zone/bot_commands/follow.cpp | 250 ++++++--- zone/bot_commands/item_use.cpp | 8 +- zone/bot_commands/mesmerize.cpp | 2 +- zone/bot_raid.cpp | 12 +- zone/botspellsai.cpp | 73 ++- zone/groups.cpp | 138 ++++- zone/groups.h | 2 + zone/mob.cpp | 158 +++--- zone/mob.h | 1 + zone/spells.cpp | 48 +- 17 files changed, 1046 insertions(+), 663 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index e033ffd82a..278701b78f 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -836,7 +836,7 @@ RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will b RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") -RULE_INT(Bots, StackSizeMin, 100, "100 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") +RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") @@ -871,6 +871,8 @@ RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Def RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass the anti-spam system") RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. Default 120.") RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") +RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.") +RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/common/spdat.cpp b/common/spdat.cpp index 53bf03f6d3..48b6351fb2 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2782,15 +2782,16 @@ int8 SpellEffectsCount(uint16 spell_id) { if (!IsValidSpell(spell_id)) { return false; } - int8 i = 0; + + int8 x = 0; for (int i = 0; i < EFFECT_COUNT; i++) { if (!IsBlankSpellEffect(spell_id, i)) { - ++i; + ++x; } } - return i; + return x; } bool IsLichSpell(uint16 spell_id) @@ -3175,3 +3176,72 @@ bool RequiresStackCheck(uint16 spellType) { return true; } + +bool IsResistanceOnlySpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (IsBlankSpellEffect(spell_id, i)) { + continue; + } + + if ( + spell.effect_id[i] == SE_ResistFire || + spell.effect_id[i] == SE_ResistCold || + spell.effect_id[i] == SE_ResistPoison || + spell.effect_id[i] == SE_ResistDisease || + spell.effect_id[i] == SE_ResistMagic || + spell.effect_id[i] == SE_ResistCorruption || + spell.effect_id[i] == SE_ResistAll + ) { + continue; + } + + return false; + } + + return true; +} + +bool IsDamageShieldOnlySpell(uint16 spell_id) { + if (SpellEffectsCount(spell_id) == 1 && IsEffectInSpell(spell_id, SE_DamageShield)) { + return true; + } + + return false; +} + +bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (IsBlankSpellEffect(spell_id, i)) { + continue; + } + + if ( + spell.effect_id[i] == SE_DamageShield || + spell.effect_id[i] == SE_ResistFire || + spell.effect_id[i] == SE_ResistCold || + spell.effect_id[i] == SE_ResistPoison || + spell.effect_id[i] == SE_ResistDisease || + spell.effect_id[i] == SE_ResistMagic || + spell.effect_id[i] == SE_ResistCorruption || + spell.effect_id[i] == SE_ResistAll + ) { + continue; + } + + return false; + } + + return true; +} diff --git a/common/spdat.h b/common/spdat.h index 432774b95b..b07bf7d966 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1722,5 +1722,8 @@ bool IsLichSpell(uint16 spell_id); bool IsInstantHealSpell(uint32 spell_id); bool IsResurrectSpell(uint16 spell_id); bool RequiresStackCheck(uint16 spellType); +bool IsResistanceOnlySpell(uint16 spell_id); +bool IsDamageShieldOnlySpell(uint16 spell_id); +bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id); #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 0b7fd0cc82..8052aa8d1e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -87,6 +87,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -210,6 +211,7 @@ Bot::Bot( SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -268,146 +270,146 @@ Bot::Bot( for (int x1 = 0; x1 < EFFECT_COUNT; x1++) { switch (spell.effect_id[x1]) { - case SE_IllusionCopy: - case SE_Illusion: { - if (GetIllusionBlock()) { - break; - } - - if (spell.base_value[x1] == -1) { - if (gender == Gender::Female) { - gender = Gender::Male; - } else if (gender == Gender::Male) { - gender = Gender::Female; + case SE_IllusionCopy: + case SE_Illusion: { + if (GetIllusionBlock()) { + break; } - SendIllusionPacket( - AppearanceStruct{ - .gender_id = gender, - .race_id = GetRace(), + if (spell.base_value[x1] == -1) { + if (gender == Gender::Female) { + gender = Gender::Male; + } else if (gender == Gender::Male) { + gender = Gender::Female; } - ); - } else if (spell.base_value[x1] == -2) // WTF IS THIS - { - if (GetRace() == IKSAR || GetRace() == VAHSHIR || GetRace() <= GNOME) { + SendIllusionPacket( AppearanceStruct{ - .gender_id = GetGender(), - .helmet_texture = static_cast(spell.max_value[x1]), + .gender_id = gender, .race_id = GetRace(), + } + ); + } else if (spell.base_value[x1] == -2) // WTF IS THIS + { + if (GetRace() == IKSAR || GetRace() == VAHSHIR || GetRace() <= GNOME) { + SendIllusionPacket( + AppearanceStruct{ + .gender_id = GetGender(), + .helmet_texture = static_cast(spell.max_value[x1]), + .race_id = GetRace(), + .texture = static_cast(spell.limit_value[x1]), + } + ); + } + } else if (spell.max_value[x1] > 0) { + SendIllusionPacket( + AppearanceStruct{ + .helmet_texture = static_cast(spell.max_value[x1]), + .race_id = static_cast(spell.base_value[x1]), + .texture = static_cast(spell.limit_value[x1]), + } + ); + } else { + SendIllusionPacket( + AppearanceStruct{ + .helmet_texture = static_cast(spell.max_value[x1]), + .race_id = static_cast(spell.base_value[x1]), .texture = static_cast(spell.limit_value[x1]), } ); } - } else if (spell.max_value[x1] > 0) { - SendIllusionPacket( - AppearanceStruct{ - .helmet_texture = static_cast(spell.max_value[x1]), - .race_id = static_cast(spell.base_value[x1]), - .texture = static_cast(spell.limit_value[x1]), - } - ); - } else { - SendIllusionPacket( - AppearanceStruct{ - .helmet_texture = static_cast(spell.max_value[x1]), - .race_id = static_cast(spell.base_value[x1]), - .texture = static_cast(spell.limit_value[x1]), - } - ); - } - switch (spell.base_value[x1]) { - case OGRE: - SendAppearancePacket(AppearanceType::Size, 9); + switch (spell.base_value[x1]) { + case OGRE: + SendAppearancePacket(AppearanceType::Size, 9); + break; + case TROLL: + SendAppearancePacket(AppearanceType::Size, 8); + break; + case VAHSHIR: + case BARBARIAN: + SendAppearancePacket(AppearanceType::Size, 7); + break; + case HALF_ELF: + case WOOD_ELF: + case DARK_ELF: + case FROGLOK: + SendAppearancePacket(AppearanceType::Size, 5); + break; + case DWARF: + SendAppearancePacket(AppearanceType::Size, 4); + break; + case HALFLING: + case GNOME: + SendAppearancePacket(AppearanceType::Size, 3); + break; + default: + SendAppearancePacket(AppearanceType::Size, 6); + break; + } break; - case TROLL: - SendAppearancePacket(AppearanceType::Size, 8); + } + case SE_Silence: + { + Silence(true); break; - case VAHSHIR: - case BARBARIAN: - SendAppearancePacket(AppearanceType::Size, 7); + } + case SE_Amnesia: + { + Amnesia(true); break; - case HALF_ELF: - case WOOD_ELF: - case DARK_ELF: - case FROGLOK: - SendAppearancePacket(AppearanceType::Size, 5); + } + case SE_DivineAura: + { + invulnerable = true; break; - case DWARF: - SendAppearancePacket(AppearanceType::Size, 4); + } + case SE_Invisibility2: + case SE_Invisibility: + { + invisible = true; + SendAppearancePacket(AppearanceType::Invisibility, 1); break; - case HALFLING: - case GNOME: - SendAppearancePacket(AppearanceType::Size, 3); + } + case SE_Levitate: + { + if (!zone->CanLevitate()) + { + SendAppearancePacket(AppearanceType::FlyMode, 0); + BuffFadeByEffect(SE_Levitate); + } + else { + SendAppearancePacket(AppearanceType::FlyMode, 2); + } break; - default: - SendAppearancePacket(AppearanceType::Size, 6); + } + case SE_InvisVsUndead2: + case SE_InvisVsUndead: + { + invisible_undead = true; break; } - break; - } - case SE_Silence: - { - Silence(true); - break; - } - case SE_Amnesia: - { - Amnesia(true); - break; - } - case SE_DivineAura: - { - invulnerable = true; - break; - } - case SE_Invisibility2: - case SE_Invisibility: - { - invisible = true; - SendAppearancePacket(AppearanceType::Invisibility, 1); - break; - } - case SE_Levitate: - { - if (!zone->CanLevitate()) + case SE_InvisVsAnimals: { - SendAppearancePacket(AppearanceType::FlyMode, 0); - BuffFadeByEffect(SE_Levitate); + invisible_animals = true; + break; } - else { - SendAppearancePacket(AppearanceType::FlyMode, 2); + case SE_AddMeleeProc: + case SE_WeaponProc: + { + AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel); + break; + } + case SE_DefensiveProc: + { + AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); + break; + } + case SE_RangedProc: + { + AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); + break; } - break; - } - case SE_InvisVsUndead2: - case SE_InvisVsUndead: - { - invisible_undead = true; - break; - } - case SE_InvisVsAnimals: - { - invisible_animals = true; - break; - } - case SE_AddMeleeProc: - case SE_WeaponProc: - { - AddProcToWeapon(GetProcID(buffs[j1].spellid, x1), false, 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid, buffs[j1].casterlevel); - break; - } - case SE_DefensiveProc: - { - AddDefensiveProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); - break; - } - case SE_RangedProc: - { - AddRangedProc(GetProcID(buffs[j1].spellid, x1), 100 + spells[buffs[j1].spellid].limit_value[x1], buffs[j1].spellid); - break; - } } } } @@ -1797,7 +1799,9 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { ) ) { if (!Ammo || ammoItem->GetCharges() < 1) { - GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + if (!GetCombatRoundForAlerts()) { + GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + } } return; @@ -1917,20 +1921,20 @@ bool Bot::CheckTripleAttack() ) ) { switch (GetClass()) { - case Class::Warrior: - chance = RuleI(Combat, ClassicTripleAttackChanceWarrior); - break; - case Class::Ranger: - chance = RuleI(Combat, ClassicTripleAttackChanceRanger); - break; - case Class::Monk: - chance = RuleI(Combat, ClassicTripleAttackChanceMonk); - break; - case Class::Berserker: - chance = RuleI(Combat, ClassicTripleAttackChanceBerserker); - break; - default: - break; + case Class::Warrior: + chance = RuleI(Combat, ClassicTripleAttackChanceWarrior); + break; + case Class::Ranger: + chance = RuleI(Combat, ClassicTripleAttackChanceRanger); + break; + case Class::Monk: + chance = RuleI(Combat, ClassicTripleAttackChanceMonk); + break; + case Class::Berserker: + chance = RuleI(Combat, ClassicTripleAttackChanceBerserker); + break; + default: + break; } } } @@ -2014,15 +2018,26 @@ void Bot::AI_Process() return; } - auto leash_owner = SetLeashOwner(bot_owner, bot_group, raid, r_group); + Client* leash_owner = bot_owner; if (!leash_owner) { return; } - SetFollowID(leash_owner->GetID()); + Mob* follow_mob = nullptr; - auto follow_mob = SetFollowMob(leash_owner); + if (!GetFollowID()) { + follow_mob = leash_owner; + } + else { + follow_mob = entity_list.GetMob(GetFollowID()); + + if (!follow_mob || !IsInGroupOrRaid(follow_mob)) { + follow_mob = leash_owner; + } + } + + SetFollowID(follow_mob->GetID()); SetBerserkState(); @@ -2094,6 +2109,7 @@ void Bot::AI_Process() // ATTACKING FLAG (HATE VALIDATION) if (GetAttackingFlag() && tar->CheckAggro(this)) { + SetCombatRoundForAlerts(true); SetAttackingFlag(false); } @@ -2264,6 +2280,7 @@ void Bot::AI_Process() } else { // Out-of-combat behavior SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { @@ -2408,6 +2425,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { AddToHateList(hater, 1); SetTarget(hater); SetAttackingFlag(); + SetCombatRoundForAlerts(); if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->AddToHateList(hater, 1); @@ -2869,6 +2887,7 @@ bool Bot::IsValidTarget( SetTarget(nullptr); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (PULLING_BOT) { @@ -2902,6 +2921,7 @@ Mob* Bot::GetBotTarget(Client* bot_owner) WipeHateList(); SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (PULLING_BOT) { @@ -3120,6 +3140,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { } SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -3134,6 +3155,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { WipeHateList(); AddToHateList(attack_target, 1); SetTarget(attack_target); + SetCombatRoundForAlerts(); SetAttackingFlag(); if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->WipeHateList(); @@ -3146,6 +3168,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetAttackFlag(false); + SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -3838,7 +3861,6 @@ bool Bot::AddBotToGroup(Bot* bot, Group* group) { if (bot && group->AddMember(bot)) { if (group->GetLeader()) { - bot->SetFollowID(group->GetLeader()->GetID()); // Need to send this only once when a group is formed with a bot so the client knows it is also the group leader if (group->GroupCount() == 2 && group->GetLeader()->IsClient()) { group->UpdateGroupAAs(); @@ -4621,15 +4643,15 @@ float Bot::GetProcChances(float ProcBonus, uint16 hand) { float ProcChance = 0.0f; uint32 weapon_speed = 0; switch (hand) { - case EQ::invslot::slotPrimary: - weapon_speed = attack_timer.GetDuration(); - break; - case EQ::invslot::slotSecondary: - weapon_speed = attack_dw_timer.GetDuration(); - break; - case EQ::invslot::slotRange: - weapon_speed = ranged_timer.GetDuration(); - break; + case EQ::invslot::slotPrimary: + weapon_speed = attack_timer.GetDuration(); + break; + case EQ::invslot::slotSecondary: + weapon_speed = attack_dw_timer.GetDuration(); + break; + case EQ::invslot::slotRange: + weapon_speed = ranged_timer.GetDuration(); + break; } if (weapon_speed < RuleI(Combat, MinHastedDelay)) @@ -4738,84 +4760,84 @@ int Bot::GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target) int base = EQ::skills::GetBaseDamage(skill); auto skill_level = GetSkill(skill); switch (skill) { - case EQ::skills::SkillDragonPunch: - case EQ::skills::SkillEagleStrike: - case EQ::skills::SkillTigerClaw: - if (skill_level >= 25) - base++; - if (skill_level >= 75) - base++; - if (skill_level >= 125) - base++; - if (skill_level >= 175) - base++; - return base; - case EQ::skills::SkillFrenzy: - if (GetBotItem(EQ::invslot::slotPrimary)) { - if (GetLevel() > 15) - base += GetLevel() - 15; - if (base > 23) - base = 23; - if (GetLevel() > 50) - base += 2; - if (GetLevel() > 54) + case EQ::skills::SkillDragonPunch: + case EQ::skills::SkillEagleStrike: + case EQ::skills::SkillTigerClaw: + if (skill_level >= 25) base++; - if (GetLevel() > 59) + if (skill_level >= 75) base++; + if (skill_level >= 125) + base++; + if (skill_level >= 175) + base++; + return base; + case EQ::skills::SkillFrenzy: + if (GetBotItem(EQ::invslot::slotPrimary)) { + if (GetLevel() > 15) + base += GetLevel() - 15; + if (base > 23) + base = 23; + if (GetLevel() > 50) + base += 2; + if (GetLevel() > 54) + base++; + if (GetLevel() > 59) + base++; + } + return base; + case EQ::skills::SkillFlyingKick: { + float skill_bonus = skill_level / 9.0f; + float ac_bonus = 0.0f; + auto inst = GetBotItem(EQ::invslot::slotFeet); + if (inst) + ac_bonus = inst->GetItemArmorClass(true) / 25.0f; + if (ac_bonus > skill_bonus) + ac_bonus = skill_bonus; + return static_cast(ac_bonus + skill_bonus); + } + case EQ::skills::SkillKick: { + float skill_bonus = skill_level / 10.0f; + float ac_bonus = 0.0f; + auto inst = GetBotItem(EQ::invslot::slotFeet); + if (inst) + ac_bonus = inst->GetItemArmorClass(true) / 25.0f; + if (ac_bonus > skill_bonus) + ac_bonus = skill_bonus; + return static_cast(ac_bonus + skill_bonus); + } + case EQ::skills::SkillBash: { + float skill_bonus = skill_level / 10.0f; + float ac_bonus = 0.0f; + const EQ::ItemInstance *inst = nullptr; + if (HasShieldEquipped()) + inst = GetBotItem(EQ::invslot::slotSecondary); + else if (HasTwoHanderEquipped()) + inst = GetBotItem(EQ::invslot::slotPrimary); + if (inst) + ac_bonus = inst->GetItemArmorClass(true) / 25.0f; + if (ac_bonus > skill_bonus) + ac_bonus = skill_bonus; + return static_cast(ac_bonus + skill_bonus); + } + case EQ::skills::SkillBackstab: { + float skill_bonus = static_cast(skill_level) * 0.02f; + auto inst = GetBotItem(EQ::invslot::slotPrimary); + if (inst && inst->GetItem() && inst->GetItem()->ItemType == EQ::item::ItemType1HPiercing) { + base = inst->GetItemBackstabDamage(true); + if (!inst->GetItemBackstabDamage()) + base += inst->GetItemWeaponDamage(true); + if (target) { + if (inst->GetItemElementalFlag(true) && inst->GetItemElementalDamage(true)) + base += target->ResistElementalWeaponDmg(inst); + if (inst->GetItemBaneDamageBody(true) || inst->GetItemBaneDamageRace(true)) + base += target->CheckBaneDamage(inst); + } + } + return static_cast(static_cast(base) * (skill_bonus + 2.0f)); } - return base; - case EQ::skills::SkillFlyingKick: { - float skill_bonus = skill_level / 9.0f; - float ac_bonus = 0.0f; - auto inst = GetBotItem(EQ::invslot::slotFeet); - if (inst) - ac_bonus = inst->GetItemArmorClass(true) / 25.0f; - if (ac_bonus > skill_bonus) - ac_bonus = skill_bonus; - return static_cast(ac_bonus + skill_bonus); - } - case EQ::skills::SkillKick: { - float skill_bonus = skill_level / 10.0f; - float ac_bonus = 0.0f; - auto inst = GetBotItem(EQ::invslot::slotFeet); - if (inst) - ac_bonus = inst->GetItemArmorClass(true) / 25.0f; - if (ac_bonus > skill_bonus) - ac_bonus = skill_bonus; - return static_cast(ac_bonus + skill_bonus); - } - case EQ::skills::SkillBash: { - float skill_bonus = skill_level / 10.0f; - float ac_bonus = 0.0f; - const EQ::ItemInstance *inst = nullptr; - if (HasShieldEquipped()) - inst = GetBotItem(EQ::invslot::slotSecondary); - else if (HasTwoHanderEquipped()) - inst = GetBotItem(EQ::invslot::slotPrimary); - if (inst) - ac_bonus = inst->GetItemArmorClass(true) / 25.0f; - if (ac_bonus > skill_bonus) - ac_bonus = skill_bonus; - return static_cast(ac_bonus + skill_bonus); - } - case EQ::skills::SkillBackstab: { - float skill_bonus = static_cast(skill_level) * 0.02f; - auto inst = GetBotItem(EQ::invslot::slotPrimary); - if (inst && inst->GetItem() && inst->GetItem()->ItemType == EQ::item::ItemType1HPiercing) { - base = inst->GetItemBackstabDamage(true); - if (!inst->GetItemBackstabDamage()) - base += inst->GetItemWeaponDamage(true); - if (target) { - if (inst->GetItemElementalFlag(true) && inst->GetItemElementalDamage(true)) - base += target->ResistElementalWeaponDmg(inst); - if (inst->GetItemBaneDamageBody(true) || inst->GetItemBaneDamageRace(true)) - base += target->CheckBaneDamage(inst); - } - } - return static_cast(static_cast(base) * (skill_bonus + 2.0f)); - } - default: - return 0; + default: + return 0; } } @@ -4882,7 +4904,10 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) { botpiercer = inst->GetItem(); if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { - BotGroupSay(this, "I can't backstab with this weapon!"); + if (!GetCombatRoundForAlerts()) { + BotGroupSay(this, "I can't backstab with this weapon!"); + } + return; } @@ -5818,7 +5843,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe GetClass() != Class::Bard && (IsGrouped() || (IsRaidGrouped() && GetRaid()->GetGroup(GetCleanName()) != RAID_GROUPLESS)) && (spellTarget->IsBot() || spellTarget->IsClient()) && - (RuleB(Bots, GroupBuffing) || RuleB(Bots, CrossRaidBuffingAndHealing)) + (RuleB(Bots, GroupBuffing) || RuleB(Bots, RaidBuffing)) ) { bool noGroupSpell = false; uint16 thespell = spell_id; @@ -5847,7 +5872,8 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe if (!noGroupSpell) { std::vector v; - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + + if (RuleB(Bots, RaidBuffing)) { v = GatherSpellTargets(true); } else { @@ -5906,8 +5932,20 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel } if (spellTarget->IsOfClientBotMerc()) { - const std::vector v = GatherGroupSpellTargets(spellTarget); + std::vector v; + + if (RuleB(Bots, RaidBuffing)) { + v = GatherSpellTargets(true); + } + else { + v = GatherGroupSpellTargets(spellTarget); + } + for (Mob* m : v) { + if (m == this && spellTarget != this) { + continue; + } + SpellOnTarget(spell_id, m); if (m->GetPetID() && (!RuleB(Bots, RequirePetAffinity) || m->HasPetAffinity())) { @@ -6595,40 +6633,6 @@ void Bot::DoEnduranceUpkeep() { } void Bot::Camp(bool save_to_database) { - - if (RuleB(Bots, PreventBotCampOnFD) && GetBotOwner()->GetFeigned()) { - GetBotOwner()->Message(Chat::White, "You cannot camp bots while feigned."); - return; - } - - if (RuleB(Bots, PreventBotCampOnEngaged)) { - Raid* raid = entity_list.GetRaidByClient(GetBotOwner()->CastToClient()); - if (raid && raid->IsEngaged()) { - GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); - return; - } - - auto* owner_group = GetBotOwner()->GetGroup(); - if (owner_group) { - std::list member_list; - owner_group->GetClientList(member_list); - member_list.remove(nullptr); - - for (auto member_iter : member_list) { - if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { - GetBotOwner()->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); - return; - } - } - } - else { - if (GetBotOwner()->CastToClient()->GetAggroCount() > 0) { - GetBotOwner()->Message(Chat::White, "You cannot spawn bots while you are engaged,"); - return; - } - } - } - Sit(); LeaveHealRotationMemberPool(); @@ -6700,107 +6704,107 @@ void Bot::UpdateGroupCastingRoles(const Group* group, bool disband) // GroupHealer switch (iter->GetClass()) { - case Class::Cleric: - if (!healer) - healer = iter; - else - switch (healer->GetClass()) { - case Class::Cleric: - break; - default: + case Class::Cleric: + if (!healer) healer = iter; - } + else + switch (healer->GetClass()) { + case Class::Cleric: + break; + default: + healer = iter; + } - break; - case Class::Druid: - if (!healer) - healer = iter; - else - switch (healer->GetClass()) { - case Class::Cleric: - case Class::Druid: - break; - default: + break; + case Class::Druid: + if (!healer) healer = iter; - } - break; - case Class::Shaman: - if (!healer) - healer = iter; - else - switch (healer->GetClass()) { - case Class::Cleric: - case Class::Druid: - case Class::Shaman: - break; - default: + else + switch (healer->GetClass()) { + case Class::Cleric: + case Class::Druid: + break; + default: + healer = iter; + } + break; + case Class::Shaman: + if (!healer) healer = iter; - } - break; - case Class::Paladin: - case Class::Ranger: - case Class::Beastlord: - if (!healer) - healer = iter; - break; - default: - break; - } + else + switch (healer->GetClass()) { + case Class::Cleric: + case Class::Druid: + case Class::Shaman: + break; + default: + healer = iter; + } + break; + case Class::Paladin: + case Class::Ranger: + case Class::Beastlord: + if (!healer) + healer = iter; + break; + default: + break; + } - // GroupSlower - switch (iter->GetClass()) { - case Class::Shaman: - if (!slower) - slower = iter; - else - switch (slower->GetClass()) { + // GroupSlower + switch (iter->GetClass()) { case Class::Shaman: + if (!slower) + slower = iter; + else + switch (slower->GetClass()) { + case Class::Shaman: + break; + default: + slower = iter; + } break; - default: - slower = iter; - } - break; - case Class::Enchanter: - if (!slower) - slower = iter; - else - switch (slower->GetClass()) { - case Class::Shaman: case Class::Enchanter: + if (!slower) + slower = iter; + else + switch (slower->GetClass()) { + case Class::Shaman: + case Class::Enchanter: + break; + default: + slower = iter; + } + break; + case Class::Beastlord: + if (!slower) + slower = iter; break; default: - slower = iter; - } - break; - case Class::Beastlord: - if (!slower) - slower = iter; - break; - default: - break; - } + break; + } - // GroupNuker - switch (iter->GetClass()) { - // wizard - // magician - // necromancer - // enchanter - // druid - // cleric - // shaman - // shadowknight - // paladin - // ranger - // beastlord - default: - break; - } + // GroupNuker + switch (iter->GetClass()) { + // wizard + // magician + // necromancer + // enchanter + // druid + // cleric + // shaman + // shadowknight + // paladin + // ranger + // beastlord + default: + break; + } - // GroupDoter - switch (iter->GetClass()) { - default: - break; + // GroupDoter + switch (iter->GetClass()) { + default: + break; } } @@ -6828,11 +6832,26 @@ Bot* Bot::GetBotByBotClientOwnerAndBotName(Client* c, const std::string& botName void Bot::ProcessBotGroupInvite(Client* c, std::string const& botName) { if (c && !c->HasRaid()) { - Bot* invitedBot = GetBotByBotClientOwnerAndBotName(c, botName); + Bot* invitedBot = entity_list.GetBotByBotName(botName); + if (!invitedBot) { return; } + if ( + invitedBot->GetBotOwnerCharacterID() != c->CharacterID() && + !( + c->GetGroup() && + !invitedBot->HasGroup() && + invitedBot->GetOwner()->HasGroup() && + c->GetGroup() == invitedBot->GetOwner()->GetGroup() + ) + ) { + c->Message(Chat::Red, "%s's owner needs to be in your group to be able to invite them.", invitedBot->GetCleanName()); + + return; + } + if (!invitedBot->HasGroup() && !invitedBot->HasRaid()) { if (!c->IsGrouped()) { auto g = new Group(c); @@ -7074,7 +7093,6 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl else { v = caster->GatherGroupSpellTargets(); } - v = caster->GatherSpellTargets(); } Mob* tar = nullptr; @@ -8433,17 +8451,19 @@ int32 Bot::CalcItemATKCap() return RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; } -bool Bot::CheckSpawnConditions(Client* c) { +bool Bot::CheckCampSpawnConditions(Client* c) { if (RuleB(Bots, PreventBotSpawnOnFD) && c->GetFeigned()) { - c->Message(Chat::White, "You cannot spawn bots while feigned."); + c->Message(Chat::White, "You cannot camp or spawn bots while feigned."); + return false; } if (RuleB(Bots, PreventBotSpawnOnEngaged)) { Raid* raid = entity_list.GetRaidByClient(c); if (raid && raid->IsEngaged()) { - c->Message(Chat::White, "You cannot spawn bots while your raid is engaged."); + c->Message(Chat::White, "You cannot camp or spawn bots while your raid is engaged."); + return false; } @@ -8455,14 +8475,14 @@ bool Bot::CheckSpawnConditions(Client* c) { for (auto member_iter : member_list) { if (member_iter->IsEngaged() || member_iter->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while your group is engaged,"); + c->Message(Chat::White, "You cannot camp or spawn bots while your group is engaged,"); return false; } } } else { if (c->GetAggroCount() > 0) { - c->Message(Chat::White, "You cannot spawn bots while you are engaged,"); + c->Message(Chat::White, "You cannot camp or spawn bots while you are engaged,"); return false; } } @@ -9397,6 +9417,10 @@ bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrecheck return false; } + if (IsBeneficialSpell(spellid) && tar->BuffCount() >= tar->GetCurrentBuffSlots() && CalcBuffDuration(this, tar, spellid) != 0) { + return false; + } + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme if (!CanCastSpellType(spellType, spellid, tar)) { return false; @@ -9426,8 +9450,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { spells[spellid].target_type == ST_Pet || (tar == this && spells[spellid].target_type != ST_TargetsTarget) || spells[spellid].target_type == ST_Group || - spells[spellid].target_type == ST_GroupTeleport //|| - //(botClass == Class::Bard && spells[spellid].target_type == ST_AEBard) //TODO bot rewrite - is this needed? + spells[spellid].target_type == ST_GroupTeleport ) ) { LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme @@ -9471,9 +9494,9 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) - ) - ) { + (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + ) + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; } @@ -9516,18 +9539,6 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: - //switch (spells[spellid].target_type) { //TODO bot rewrite - is this needed? - // case ST_AEBard: - // case ST_AECaster: - // case ST_GroupTeleport: - // case ST_Group: - // case ST_Self: - // break; - // default: - // LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme - // return false; - //} - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; @@ -9540,9 +9551,9 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR) - ) - ) { + (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + ) + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; } @@ -9597,7 +9608,15 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { return true; } - const std::vector v = GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = GatherSpellTargets(true); + } + else { + v = GatherGroupSpellTargets(); + } + for (Mob* m : v) { if ( m->IsBot() && @@ -9618,9 +9637,10 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { m->CastingSpellID() == spellid ) { - const std::vector v = GatherGroupSpellTargets(); - for (Mob* m : v) { - if (entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(m->GetID())) { + std::vector x = GatherGroupSpellTargets(); + + for (Mob* t : x) { + if (entity_list.GetMobID(t->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(t->GetID())) { return true; } } @@ -10263,61 +10283,57 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { case BotSpellTypes::Cure: priority = 10; - break; - case BotSpellTypes::InCombatBuff: // this has a check at the end to decrement everything below if it's not a SK (SK use InCombatBuffs as it is their hate line so it's not use when Idle) - priority = 11; - break; case BotSpellTypes::PetVeryFastHeals: - priority = 12; + priority = 11; break; case BotSpellTypes::PetFastHeals: - priority = 13; + priority = 12; break; case BotSpellTypes::PetRegularHeals: - priority = 14; + priority = 13; break; case BotSpellTypes::PetCompleteHeals: - priority = 15; + priority = 14; break; case BotSpellTypes::PetHoTHeals: - priority = 16; + priority = 15; break; case BotSpellTypes::Pet: - priority = 17; + priority = 16; break; case BotSpellTypes::Buff: - priority = 18; + priority = 17; break; case BotSpellTypes::OutOfCombatBuffSong: - priority = 19; + priority = 18; break; case BotSpellTypes::ResistBuffs: - priority = 20; + priority = 19; break; case BotSpellTypes::DamageShields: - priority = 21; + priority = 20; break; case BotSpellTypes::PetBuffs: - priority = 22; + priority = 21; break; case BotSpellTypes::PreCombatBuff: - priority = 23; + priority = 22; break; case BotSpellTypes::PreCombatBuffSong: - priority = 24; + priority = 23; break; default: @@ -10326,14 +10342,6 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { break; } - if ( - priority >= 11 && - botClass && botClass == Class::ShadowKnight && - spellType != BotSpellTypes::InCombatBuff - ) { - --priority; - } - return priority; } @@ -10839,39 +10847,19 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { switch (spellType) { //TODO bot rewrite - fix Buff/ResistBuff case BotSpellTypes::Buff: - if (IsEffectInSpell(spellid, SE_DamageShield)) { - return false; - } - - if ( - IsEffectInSpell(spellid, SE_ResistMagic) || - IsEffectInSpell(spellid, SE_ResistFire) || - IsEffectInSpell(spellid, SE_ResistCold) || - IsEffectInSpell(spellid, SE_ResistPoison) || - IsEffectInSpell(spellid, SE_ResistDisease) || - IsEffectInSpell(spellid, SE_ResistCorruption) || - IsEffectInSpell(spellid, SE_ResistAll) - ) { + if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return false; } return true; case BotSpellTypes::ResistBuffs: - if ( - IsEffectInSpell(spellid, SE_ResistMagic) || - IsEffectInSpell(spellid, SE_ResistFire) || - IsEffectInSpell(spellid, SE_ResistCold) || - IsEffectInSpell(spellid, SE_ResistPoison) || - IsEffectInSpell(spellid, SE_ResistDisease) || - IsEffectInSpell(spellid, SE_ResistCorruption) || - IsEffectInSpell(spellid, SE_ResistAll) - ) { + if (IsResistanceOnlySpell(spellid)) { return true; } return false; case BotSpellTypes::DamageShields: - if (IsEffectInSpell(spellid, SE_DamageShield)) { + if (IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return true; } @@ -11086,55 +11074,6 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } -bool Bot::IsInGroupOrRaid(bool announce) { - if (!GetOwner()) { - return false; - } - - Mob* c = GetOwner(); - - if ( - (!GetRaid() && !GetGroup()) || - (!c->GetRaid() && !c->GetGroup()) - ) { - return false; - } - - if ( - c->GetRaid() && - ( - !GetRaid() || - c->GetRaid() != GetRaid() || - GetRaid()->GetGroup(GetCleanName()) == RAID_GROUPLESS - ) - ) { - return false; - } - - if ( - c->GetGroup() && - ( - !GetGroup() || - c->GetGroup() != GetGroup() - ) - ) { - return false; - } - - - if (announce) { - c->Message( - Chat::Yellow, - fmt::format( - "{} says, 'I am not currently in your group or raid.", - GetCleanName() - ).c_str() - ); - } - - return true; -} - bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar) { int spellRange = botCaster->GetActSpellRange(spellid, spells[spellid].range); int spellAERange = botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range); diff --git a/zone/bot.h b/zone/bot.h index 97d987e567..5836af1335 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -278,6 +278,7 @@ class Bot : public NPC { void SetHoldFlag(bool flag = true) { m_hold_flag = flag; } bool GetAttackFlag() const { return m_attack_flag; } void SetAttackFlag(bool flag = true) { m_attack_flag = flag; } + bool GetCombatRoundForAlerts() const { return m_combat_round_alert_flag; } bool GetAttackingFlag() const { return m_attacking_flag; } bool GetPullFlag() const { return m_pull_flag; } void SetPullFlag(bool flag = true) { m_pull_flag = flag; } @@ -403,7 +404,7 @@ class Bot : public NPC { void SetGuardMode(); void SetHoldMode(); - bool IsValidSpellRange(uint16 spell_id, Mob const* tar); + bool IsValidSpellRange(uint16 spell_id, Mob* tar); // Bot AI Methods void AI_Bot_Init(); @@ -519,7 +520,6 @@ class Bot : public NPC { void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } bool HasLoS() const { return _hasLoS; } - bool IsInGroupOrRaid(bool announce = false); void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false); std::list GetSpellTypesPrioritized(uint8 priorityType); @@ -981,7 +981,7 @@ class Bot : public NPC { bool CheckDoubleRangedAttack(); // Public "Refactor" Methods - static bool CheckSpawnConditions(Client* c); + static bool CheckCampSpawnConditions(Client* c); inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); } @@ -1047,6 +1047,7 @@ class Bot : public NPC { bool m_guard_flag; bool m_hold_flag; bool m_attack_flag; + bool m_combat_round_alert_flag; bool m_attacking_flag; bool m_pull_flag; bool m_pulling_flag; @@ -1104,6 +1105,7 @@ class Bot : public NPC { int32 GenerateBaseManaPoints(); void GenerateSpecialAttacks(); void SetBotID(uint32 botID); + void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag; } void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; } void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; } void SetReturningFlag(bool flag = true) { m_returning_flag = flag; } diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 7da91504d4..ccf1e51ffc 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -30,12 +30,19 @@ void bot_command_bot(Client *c, const Seperator *sep) void bot_command_camp(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) + if (helper_command_alias_fail(c, "bot_command_camp", sep->arg[0], "botcamp")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); return; } + + if (!Bot::CheckCampSpawnConditions(c)) { + return; + } + const int ab_mask = ActionableBots::ABM_Type1; std::string class_race_arg = sep->arg[1]; @@ -49,8 +56,14 @@ void bot_command_camp(Client *c, const Seperator *sep) return; } - for (auto bot_iter : sbl) + uint16 campCount; + + for (auto bot_iter : sbl) { bot_iter->Camp(); + ++campCount; + } + + c->Message(Chat::White, "%i of your bots have been camped.", campCount); } void bot_command_clone(Client *c, const Seperator *sep) @@ -428,6 +441,10 @@ void bot_command_delete(Client *c, const Seperator *sep) return; } + if (!Bot::CheckCampSpawnConditions(c)) { + return; + } + auto my_bot = ActionableBots::AsTarget_ByBot(c); if (!my_bot) { c->Message(Chat::White, "You must a bot that you own to use this command"); @@ -829,7 +846,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) return; } - if (!Bot::CheckSpawnConditions(c)) { + if (!Bot::CheckCampSpawnConditions(c)) { return; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 77d3ab23bd..70b6e659cc 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -174,7 +174,7 @@ void bot_command_cast(Client* c, const Seperator* sep) if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { c->Message(Chat::Yellow, "[%s] is an invalid target.", tar->GetCleanName()); return; - } + } } } LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme @@ -223,7 +223,7 @@ void bot_command_cast(Client* c, const Seperator* sep) Bot* firstFound = nullptr; for (auto bot_iter : sbl) { - if (!bot_iter->IsInGroupOrRaid()) { + if (!bot_iter->IsInGroupOrRaid(c)) { continue; } @@ -248,6 +248,14 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } + if ( + BOT_SPELL_TYPES_BENEFICIAL(spellType) && + !RuleB(Bots, CrossRaidBuffingAndHealing) && + !bot_iter->IsInGroupOrRaid(newTar, true) + ) { + continue; + } + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { bot_iter->BotGroupSay( bot_iter, diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 3ff0cb44ba..9e7c33eafd 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -2,47 +2,112 @@ void bot_command_follow(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) + if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: reset]) [actionable: byname | ownergroup | ownerraid | namesgroup | mmr | byclass | byrace | spawned]] ([actionable_name])", sep->arg[0]); - c->Message(Chat::White, "usage: %s chain", sep->arg[0]); + std::vector description = + { + "Sets bots of your choosing to follow your target, view their current following state or reset their following state." + }; + + std::vector notes = + { + "- You can only follow players, bots or mercenaries belonging to your group or raid." + }; + + std::vector example_format = + { + fmt::format( + "{} [optional] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all Clerics to follow your target:", + fmt::format( + "{} byclass {}", + sep->arg[0], + Class::Cleric + ) + }; + std::vector examples_two = + { + "To check the current state of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To reset all bots:", + fmt::format( + "{} reset spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + return; } + const int ab_mask = ActionableBots::ABM_Type2; + bool chain = false; bool reset = false; + bool currentCheck = false; int ab_arg = 1; - int name_arg = 2; Mob* target_mob = nullptr; std::string optional_arg = sep->arg[1]; - if (!optional_arg.compare("chain")) { - - auto chain_count = helper_bot_follow_option_chain(c); - c->Message(Chat::White, "%i of your bots %s now chain following you", chain_count, (chain_count == 1 ? "is" : "are")); - - return; - } - else if (!optional_arg.compare("reset")) { + + if (!optional_arg.compare("reset")) { + target_mob = c; reset = true; ++ab_arg; - ++name_arg ; + } + else if (!optional_arg.compare("current")) { + currentCheck = true; + ++ab_arg; } else { - //target_mob = ActionableTarget::VerifyFriendly(c, BCEnum::TT_Single); target_mob = c->GetTarget(); - if (!target_mob) { - c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); - return; - } - else if (!target_mob->IsBot() && !target_mob->IsClient()) { - c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); + + if (!target_mob || !target_mob->IsOfClientBotMerc() || !c->IsInGroupOrRaid(target_mob)) { + c->Message(Chat::Yellow, "You must a friendly player, bot or merc within your group or raid to use this command"); return; } - else if ((target_mob->GetGroup() && target_mob->GetGroup() != c->GetGroup()) || (target_mob->GetRaid() && target_mob->GetRaid() != c->GetRaid())) { - c->Message(Chat::White, "You must a friendly player or bot within your group or raid to use this command"); - return; + + if (!optional_arg.compare("chain")) { + chain = true; + ++ab_arg; } } @@ -53,12 +118,66 @@ void bot_command_follow(Client* c, const Seperator* sep) } std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) { + //if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg] : nullptr, class_race_check ? atoi(sep->arg[ab_arg]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.remove(nullptr); + + auto botCount = sbl.size(); + Mob* follow_mob = nullptr; + std::list chainList; + std::list::const_iterator it = chainList.begin(); + uint16 count = 0; for (auto bot_iter : sbl) { + if (currentCheck) { + follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I am currently following {}.'", + bot_iter->GetCleanName(), + follow_mob ? follow_mob->GetCleanName() : "no one" + ).c_str() + ); + + if (!follow_mob && RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(28); + } + + continue; + } + + if (bot_iter == target_mob) { + if (botCount == 1) { + c->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I cannot follow myself, you want me to run circles?", + bot_iter->GetCleanName() + ).c_str() + ); + + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(60); + } + + return; + } + + bot_iter->WipeHateList(); + --botCount; + + continue; + } + + if (!bot_iter->IsInGroupOrRaid(target_mob)) { + --botCount; + + continue; + } + bot_iter->WipeHateList(); if (!bot_iter->GetGroup() && !bot_iter->GetRaid()) { @@ -71,57 +190,52 @@ void bot_command_follow(Client* c, const Seperator* sep) bot_iter->SetManualFollow(false); } else { - if (target_mob->IsGrouped() || target_mob->IsRaidGrouped()) { - bot_iter->SetFollowID(target_mob->GetID()); - bot_iter->SetManualFollow(true); - } - else if (bot_iter == target_mob) { - bot_iter->SetFollowID(c->GetID()); - bot_iter->SetManualFollow(true); + if (chain) { + Mob* nextTar = target_mob; + + if (count > 0) { + nextTar = *it; + + if (!nextTar) { + nextTar = target_mob; + } + } + LogTestDebug("{} is now following {}.", bot_iter->GetCleanName(), nextTar->GetCleanName()); //deleteme + chainList.push_back(bot_iter); + ++it; + ++count; + bot_iter->SetFollowID(nextTar->GetID()); } else { - bot_iter->SetFollowID(0); - bot_iter->SetManualFollow(false); + bot_iter->SetFollowID(target_mob->GetID()); } + + bot_iter->SetManualFollow(true); } } - //auto my_group = bot_iter->GetGroup(); - //if (my_group) { - // if (reset) { - // if (!my_group->GetLeader() || my_group->GetLeader() == bot_iter) - // bot_iter->SetFollowID(c->GetID()); - // else - // bot_iter->SetFollowID(my_group->GetLeader()->GetID()); - // - // bot_iter->SetManualFollow(false); - // } - // else { - // if (bot_iter == target_mob) - // bot_iter->SetFollowID(c->GetID()); - // else - // bot_iter->SetFollowID(target_mob->GetID()); - // - // bot_iter->SetManualFollow(true); - // } - //} - //else { - // bot_iter->SetFollowID(0); - // bot_iter->SetManualFollow(false); - //} - if (!bot_iter->GetPet()) + + if (!bot_iter->GetPet()) { continue; + } bot_iter->GetPet()->WipeHateList(); bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); } - Mob* follow_mob = nullptr; - if (sbl.size() == 1) { + if (currentCheck || !botCount) { + return; + } + + follow_mob = target_mob; + + if (botCount == 1) { follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + c->Message( - Chat::White, + Chat::Green, fmt::format( - "Following {}.", + "{} says, 'Following {}.'", + sbl.front()->GetCleanName(), follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); @@ -129,22 +243,20 @@ void bot_command_follow(Client* c, const Seperator* sep) else { if (reset) { c->Message( - Chat::White, + Chat::Green, fmt::format( "{} of your bots are following you.", - sbl.size() + botCount ).c_str() ); } else { - if (!sbl.empty()) { - follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); - } c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} of your bots are following {}.", - sbl.size(), + "{} of your bots are {} {}.", + botCount, + chain ? "chain following" : "following", follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() ); diff --git a/zone/bot_commands/item_use.cpp b/zone/bot_commands/item_use.cpp index 1640ff0e33..975d30b90d 100644 --- a/zone/bot_commands/item_use.cpp +++ b/zone/bot_commands/item_use.cpp @@ -179,7 +179,9 @@ void bot_command_item_use(Client* c, const Seperator* sep) ).c_str() ); - bot_iter->DoAnim(29); + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(29); + } } else if (!equipped_item) { c->Message( @@ -204,7 +206,9 @@ void bot_command_item_use(Client* c, const Seperator* sep) ).c_str() ); - bot_iter->DoAnim(29); + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(29); + } } } } diff --git a/zone/bot_commands/mesmerize.cpp b/zone/bot_commands/mesmerize.cpp index 1f51a70d39..d86fba9f71 100644 --- a/zone/bot_commands/mesmerize.cpp +++ b/zone/bot_commands/mesmerize.cpp @@ -20,7 +20,7 @@ void bot_command_mesmerize(Client *c, const Seperator *sep) continue; } - if (!bot_iter->IsInGroupOrRaid()) { + if (!bot_iter->IsInGroupOrRaid(c)) { continue; } diff --git a/zone/bot_raid.cpp b/zone/bot_raid.cpp index 25d98ef867..458ae03c41 100644 --- a/zone/bot_raid.cpp +++ b/zone/bot_raid.cpp @@ -176,13 +176,7 @@ void Bot::ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite) { // If the Bot Owner is in our raid we need to be able to invite their Bots } else if (invitee->IsBot() && (invitee->CastToBot()->GetBotOwnerCharacterID() != invitor->CharacterID())) { - invitor->Message( - Chat::Red, - fmt::format( - "{} is not your Bot. You can only invite your own Bots, or Bots that belong to a Raid member.", - invitee->GetCleanName() - ).c_str() - ); + invitor->Message(Chat::Red, "%s's owner needs to be in your raid to be able to invite them.", invitee->GetCleanName()); return; } @@ -257,10 +251,6 @@ void Bot::CreateBotRaid(Mob* invitee, Client* invitor, bool group_invite, Raid* } else { raid->AddBot(b); } - - if (new_raid) { - invitee->SetFollowID(invitor->GetID()); - } } } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index ee3aaf3a37..d7e6338223 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -995,10 +995,27 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa } if ( + !RuleB(Bots, EnableBotTGB) && + IsGroupSpell(botSpellList[i].spellid) && + !IsTGBCompatibleSpell(botSpellList[i].spellid) && + !botCaster->IsInGroupOrRaid(tar, true) + ) { + continue; + } + + if ( + ( + !botCaster->IsCommandedSpell() || + ( + botCaster->IsCommandedSpell() && + (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez) + ) + ) + && ( - !botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez)) + !IsPBAESpell(botSpellList[i].spellid) && + !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType)) ) - && (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsGroupBotSpellType(spellType))) // TODO bot rewrite - needed for ae spells? ) { continue; } @@ -1232,7 +1249,15 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spell if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); - const std::vector v = botCaster->GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = botCaster->GatherSpellTargets(true); + } + else { + v = botCaster->GatherGroupSpellTargets(); + } + int targetCount = 0; for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1273,7 +1298,15 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); - const std::vector v = botCaster->GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = botCaster->GatherSpellTargets(true); + } + else { + v = botCaster->GatherGroupSpellTargets(); + } + int targetCount = 0; for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1314,7 +1347,15 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); - const std::vector v = botCaster->GatherSpellTargets(); + std::vector v; + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = botCaster->GatherSpellTargets(true); + } + else { + v = botCaster->GatherGroupSpellTargets(); + } + int targetCount = 0; for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1629,7 +1670,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType continue; } - if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } @@ -1679,7 +1720,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType continue; } - if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsGroupBotSpellType(spellType))) { + if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } @@ -2583,19 +2624,27 @@ bool Bot::HasBotSpellEntry(uint16 spellid) { return false; } -bool Bot::IsValidSpellRange(uint16 spell_id, Mob const* tar) { +bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { if (!IsValidSpell(spell_id)) { return false; } if (tar) { - int spellrange = (GetActSpellRange(spell_id, spells[spell_id].range) * GetActSpellRange(spell_id, spells[spell_id].range)); - if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { + float range = spells[spell_id].range; + + if (tar != this) { + range += GetRangeDistTargetSizeMod(tar); + } + + range = GetActSpellRange(spell_id, range); + + if (range >= Distance(m_Position, tar->GetPosition())) { return true; } - spellrange = (GetActSpellRange(spell_id, spells[spell_id].aoe_range) * GetActSpellRange(spell_id, spells[spell_id].aoe_range)); - if (spellrange >= DistanceSquared(m_Position, tar->GetPosition())) { + range = GetActSpellRange(spell_id, spells[spell_id].aoe_range); + + if (range >= Distance(m_Position, tar->GetPosition())) { return true; } } diff --git a/zone/groups.cpp b/zone/groups.cpp index e61bbade79..512cccfc39 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1017,6 +1017,27 @@ void Group::GetBotList(std::list& bot_list, bool clear_list) } } +void Group::GetRawBotList(std::list& bot_list, bool clear_list) +{ + if (clear_list) { + bot_list.clear(); + } + + const auto& l = GroupIdRepository::GetWhere( + database, + fmt::format( + "`group_id` = {}", + GetID() + ) + ); + + for (const auto& e : l) { + if (e.bot_id) { + bot_list.push_back(e.bot_id); + } + } +} + bool Group::Process() { if(disbandcheck && !GroupCount()) return false; @@ -1234,30 +1255,49 @@ void Group::GroupMessageString(Mob* sender, uint32 type, uint32 string_id, const void Client::LeaveGroup() { Group *g = GetGroup(); - if(g) - { + if (g) { int32 MemberCount = g->GroupCount(); // Account for both client and merc leaving the group - if (GetMerc() && g == GetMerc()->GetGroup()) - { + if (GetMerc() && g == GetMerc()->GetGroup()) { MemberCount -= 1; } - if(MemberCount < 3) - { + if (RuleB(Bots, Enabled)) { + std::list sbl; + g->GetRawBotList(sbl); + + for (auto botID : sbl) { + auto b = entity_list.GetBotByBotID(botID); + + if (b) { + if (b->GetBotOwnerCharacterID() == CharacterID()) { + MemberCount -= 1; + } + } + else { + if (database.botdb.GetOwnerID(botID) == CharacterID()) { + MemberCount -= 1; + } + } + } + } + + if (MemberCount < 3) { g->DisbandGroup(); } - else - { + else { g->DelMember(this); - if (GetMerc() != nullptr && g == GetMerc()->GetGroup() ) - { + + if (GetMerc() != nullptr && g == GetMerc()->GetGroup()) { GetMerc()->RemoveMercFromGroup(GetMerc(), GetMerc()->GetGroup()); } + + if (RuleB(Bots, Enabled)) { + g->RemoveClientsBots(this); + } } } - else - { + else { //force things a little Group::RemoveFromGroup(this); @@ -2576,3 +2616,77 @@ void Group::AddToGroup(AddToGroupRequest r) } ); } + +void Group::RemoveClientsBots(Client* c) { + std::list sbl; + GetRawBotList(sbl); + + for (auto botID : sbl) { + auto b = entity_list.GetBotByBotID(botID); + + if (b) { + if (b->GetBotOwnerCharacterID() == c->CharacterID()) { + b->RemoveBotFromGroup(b, this); + } + } + else { + if (database.botdb.GetOwnerID(botID) == c->CharacterID()) { + auto botName = database.botdb.GetBotNameByID(botID); + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (membername[i] == botName) { + members[i] = nullptr; + membername[i][0] = '\0'; + memset(membername[i], 0, 64); + MemberRoles[i] = 0; + break; + } + } + + auto pack = new ServerPacket(ServerOP_GroupLeave, sizeof(ServerGroupLeave_Struct)); + ServerGroupLeave_Struct* gl = (ServerGroupLeave_Struct*)pack->pBuffer; + gl->gid = GetID(); + gl->zoneid = zone->GetZoneID(); + gl->instance_id = zone->GetInstanceID(); + strcpy(gl->member_name, botName.c_str()); + worldserver.SendPacket(pack); + safe_delete(pack); + + auto outapp = new EQApplicationPacket(OP_GroupUpdate, sizeof(GroupJoin_Struct)); + GroupJoin_Struct* gu = (GroupJoin_Struct*)outapp->pBuffer; + gu->action = groupActLeave; + strcpy(gu->membername, botName.c_str()); + strcpy(gu->yourname, botName.c_str()); + + gu->leader_aas = LeaderAbilities; + + for (uint32 i = 0; i < MAX_GROUP_MEMBERS; i++) { + if (members[i] == nullptr) { + //if (DEBUG>=5) LogFile->write(EQEMuLog::Debug, "Group::DelMember() null member at slot %i", i); + continue; + } + + if (membername[i] != botName.c_str()) { + strcpy(gu->yourname, members[i]->GetCleanName()); + + if (members[i]->IsClient()) { + members[i]->CastToClient()->QueuePacket(outapp); + } + } + } + + safe_delete(outapp); + + DelMemberOOZ(botName.c_str()); + + GroupIdRepository::DeleteWhere( + database, + fmt::format( + "`bot_id` = {}", + botID + ) + ); + } + } + } +} diff --git a/zone/groups.h b/zone/groups.h index 9cd1a594f7..f5e2b2baaf 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -69,6 +69,7 @@ class Group : public GroupIDConsumer { void GetMemberList(std::list& member_list, bool clear_list = true); void GetClientList(std::list& client_list, bool clear_list = true); void GetBotList(std::list& bot_list, bool clear_list = true); + void GetRawBotList(std::list& bot_list, bool clear_list = true); bool IsGroupMember(Mob* c); bool IsGroupMember(const char* name); bool Process(); @@ -153,6 +154,7 @@ class Group : public GroupIDConsumer { void AddToGroup(AddToGroupRequest r); void AddToGroup(Mob* m); static void RemoveFromGroup(Mob* m); + void RemoveClientsBots(Client* c); void SetGroupMentor(int percent, char *name); void ClearGroupMentor(); diff --git a/zone/mob.cpp b/zone/mob.cpp index 65374ce65c..926ea33822 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -6112,18 +6112,17 @@ int32 Mob::GetVulnerability(Mob *caster, uint32 spell_id, uint32 ticsremaining, bool Mob::IsTargetedFocusEffect(int focus_type) { switch (focus_type) { - case focusSpellVulnerability: - case focusFcSpellDamagePctIncomingPC: - case focusFcDamageAmtIncoming: - case focusFcSpellDamageAmtIncomingPC: - case focusFcCastSpellOnLand: - case focusFcHealAmtIncoming: - case focusFcHealPctCritIncoming: - case focusFcHealPctIncoming: - return true; - default: - return false; - + case focusSpellVulnerability: + case focusFcSpellDamagePctIncomingPC: + case focusFcDamageAmtIncoming: + case focusFcSpellDamageAmtIncomingPC: + case focusFcCastSpellOnLand: + case focusFcHealAmtIncoming: + case focusFcHealPctCritIncoming: + case focusFcHealPctIncoming: + return true; + default: + return false; } } @@ -7254,56 +7253,56 @@ void Mob::SlowMitigation(Mob* caster) EQ::skills::SkillType Mob::GetSkillByItemType(int ItemType) { switch (ItemType) { - case EQ::item::ItemType1HSlash: - return EQ::skills::Skill1HSlashing; - case EQ::item::ItemType2HSlash: - return EQ::skills::Skill2HSlashing; - case EQ::item::ItemType1HPiercing: - return EQ::skills::Skill1HPiercing; - case EQ::item::ItemType1HBlunt: - return EQ::skills::Skill1HBlunt; - case EQ::item::ItemType2HBlunt: - return EQ::skills::Skill2HBlunt; - case EQ::item::ItemType2HPiercing: - if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2) + case EQ::item::ItemType1HSlash: + return EQ::skills::Skill1HSlashing; + case EQ::item::ItemType2HSlash: + return EQ::skills::Skill2HSlashing; + case EQ::item::ItemType1HPiercing: return EQ::skills::Skill1HPiercing; - else - return EQ::skills::Skill2HPiercing; - case EQ::item::ItemTypeBow: - return EQ::skills::SkillArchery; - case EQ::item::ItemTypeLargeThrowing: - case EQ::item::ItemTypeSmallThrowing: - return EQ::skills::SkillThrowing; - case EQ::item::ItemTypeMartial: - return EQ::skills::SkillHandtoHand; - default: - return EQ::skills::SkillHandtoHand; + case EQ::item::ItemType1HBlunt: + return EQ::skills::Skill1HBlunt; + case EQ::item::ItemType2HBlunt: + return EQ::skills::Skill2HBlunt; + case EQ::item::ItemType2HPiercing: + if (IsClient() && CastToClient()->ClientVersion() < EQ::versions::ClientVersion::RoF2) + return EQ::skills::Skill1HPiercing; + else + return EQ::skills::Skill2HPiercing; + case EQ::item::ItemTypeBow: + return EQ::skills::SkillArchery; + case EQ::item::ItemTypeLargeThrowing: + case EQ::item::ItemTypeSmallThrowing: + return EQ::skills::SkillThrowing; + case EQ::item::ItemTypeMartial: + return EQ::skills::SkillHandtoHand; + default: + return EQ::skills::SkillHandtoHand; } } uint8 Mob::GetItemTypeBySkill(EQ::skills::SkillType skill) { switch (skill) { - case EQ::skills::SkillThrowing: - return EQ::item::ItemTypeSmallThrowing; - case EQ::skills::SkillArchery: - return EQ::item::ItemTypeArrow; - case EQ::skills::Skill1HSlashing: - return EQ::item::ItemType1HSlash; - case EQ::skills::Skill2HSlashing: - return EQ::item::ItemType2HSlash; - case EQ::skills::Skill1HPiercing: - return EQ::item::ItemType1HPiercing; - case EQ::skills::Skill2HPiercing: // watch for undesired client behavior - return EQ::item::ItemType2HPiercing; - case EQ::skills::Skill1HBlunt: - return EQ::item::ItemType1HBlunt; - case EQ::skills::Skill2HBlunt: - return EQ::item::ItemType2HBlunt; - case EQ::skills::SkillHandtoHand: - return EQ::item::ItemTypeMartial; - default: - return EQ::item::ItemTypeMartial; + case EQ::skills::SkillThrowing: + return EQ::item::ItemTypeSmallThrowing; + case EQ::skills::SkillArchery: + return EQ::item::ItemTypeArrow; + case EQ::skills::Skill1HSlashing: + return EQ::item::ItemType1HSlash; + case EQ::skills::Skill2HSlashing: + return EQ::item::ItemType2HSlash; + case EQ::skills::Skill1HPiercing: + return EQ::item::ItemType1HPiercing; + case EQ::skills::Skill2HPiercing: // watch for undesired client behavior + return EQ::item::ItemType2HPiercing; + case EQ::skills::Skill1HBlunt: + return EQ::item::ItemType1HBlunt; + case EQ::skills::Skill2HBlunt: + return EQ::item::ItemType2HBlunt; + case EQ::skills::SkillHandtoHand: + return EQ::item::ItemTypeMartial; + default: + return EQ::item::ItemTypeMartial; } } @@ -7311,7 +7310,6 @@ uint16 Mob::GetWeaponSpeedbyHand(uint16 hand) { uint16 weapon_speed = 0; switch (hand) { - case 13: weapon_speed = attack_timer.GetDuration(); break; @@ -9416,14 +9414,14 @@ void Mob::SetBaseSetting(uint16 baseSetting, int settingValue) { } bool Mob::TargetValidation(Mob* other) { - if (!other || GetAppearance() == eaDead) { - return false; - } + if (!other || GetAppearance() == eaDead) { + return false; + } - return true; + return true; } -std::unordered_map &Mob::GetCloseMobList(float distance) +std::unordered_map& Mob::GetCloseMobList(float distance) { return entity_list.GetCloseMobList(this, distance); } @@ -9445,3 +9443,41 @@ void Mob::ClearDataBucketCache() DataBucket::DeleteFromCache(id, t); } } + +bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { + if (!other || !IsOfClientBotMerc() || !other->IsOfClientBotMerc()) { + return false; + } + + auto* r = GetRaid(); + auto* rO = other->GetRaid(); + + if (r) { + if (!rO || r != rO) { + return false; + } + + auto rG = r->GetGroup(GetCleanName()); + auto rOG = rO->GetGroup(other->GetCleanName()); + + if (rG == RAID_GROUPLESS || rOG == RAID_GROUPLESS || (sameRaidGroup && rG != rOG)) { + return false; + } + + return true; + } + else { + auto* g = GetGroup(); + auto* gO = other->GetGroup(); + + if (g) { + if (!gO || g != gO) { + return false; + } + + return true; + } + } + + return false; +} diff --git a/zone/mob.h b/zone/mob.h index a9d4e58577..f2e8e9abdc 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -774,6 +774,7 @@ class Mob : public Entity { virtual bool HasGroup() = 0; virtual Raid* GetRaid() = 0; virtual Group* GetGroup() = 0; + bool IsInGroupOrRaid(Mob* other, bool sameRaidGroup = false); //Faction virtual inline int32 GetPrimaryFaction() const { return 0; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 292106f77a..4f54b38c53 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2182,14 +2182,42 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_Group: case ST_GroupNoPets: { - if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB))) { - if( (!target) || - (target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient())) || - (target->IsCorpse()) ) + if ( + IsClient() && CastToClient()->TGB() && + IsTGBCompatibleSpell(spell_id) && + (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)) + ) { + if ( + !target || + target->IsCorpse() || + ( + target->IsNPC() && + !(target->GetOwner() && target->GetOwner()->IsClient()) + ) + ) { spell_target = this; - else + } + else { spell_target = target; - } else { + } + } + else if ( + IsBot() && RuleB(Bots, EnableBotTGB) && + IsTGBCompatibleSpell(spell_id) && + (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)) + ) { + if ( + !spell_target || + spell_target->IsCorpse() || + ( + spell_target->IsNPC() && + !(spell_target->GetOwner() && spell_target->GetOwner()->IsOfClientBot()) + ) + ) { + spell_target = this; + } + } + else { spell_target = this; } @@ -2516,8 +2544,14 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in //range check our target, if we have one and it is not us float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target); - if(IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) + if ( + ( + (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB) + ) && + IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) + ) { range = spells[spell_id].aoe_range; + } range = GetActSpellRange(spell_id, range); if(IsClient() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){ From 4dcaef65cd43d82fb194d34ed009ee582533af78 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 31 Oct 2024 23:21:42 -0500 Subject: [PATCH 100/394] oopsies --- zone/bot.cpp | 16 ++++++---------- zone/bot.h | 2 +- zone/spells.cpp | 4 ++-- 3 files changed, 9 insertions(+), 13 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8052aa8d1e..34315e5236 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -87,7 +87,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -211,7 +211,7 @@ Bot::Bot( SetGuardFlag(false); SetHoldFlag(false); SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -2109,7 +2109,6 @@ void Bot::AI_Process() // ATTACKING FLAG (HATE VALIDATION) if (GetAttackingFlag() && tar->CheckAggro(this)) { - SetCombatRoundForAlerts(true); SetAttackingFlag(false); } @@ -2280,7 +2279,7 @@ void Bot::AI_Process() } else { // Out-of-combat behavior SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { @@ -2425,7 +2424,6 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { AddToHateList(hater, 1); SetTarget(hater); SetAttackingFlag(); - SetCombatRoundForAlerts(); if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->AddToHateList(hater, 1); @@ -2887,7 +2885,7 @@ bool Bot::IsValidTarget( SetTarget(nullptr); SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); if (PULLING_BOT) { @@ -2921,7 +2919,6 @@ Mob* Bot::GetBotTarget(Client* bot_owner) WipeHateList(); SetAttackFlag(false); - SetCombatRoundForAlerts(true); SetAttackingFlag(false); if (PULLING_BOT) { @@ -3140,7 +3137,6 @@ void Bot::SetOwnerTarget(Client* bot_owner) { } SetAttackFlag(false); - SetCombatRoundForAlerts(true); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -3155,7 +3151,6 @@ void Bot::SetOwnerTarget(Client* bot_owner) { WipeHateList(); AddToHateList(attack_target, 1); SetTarget(attack_target); - SetCombatRoundForAlerts(); SetAttackingFlag(); if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->WipeHateList(); @@ -3168,7 +3163,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetAttackFlag(false); - SetCombatRoundForAlerts(true); + SetCombatRoundForAlerts(false); SetAttackingFlag(false); SetPullFlag(false); SetPullingFlag(false); @@ -4905,6 +4900,7 @@ void Bot::TryBackstab(Mob *other, int ReuseTime) { if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { if (!GetCombatRoundForAlerts()) { + SetCombatRoundForAlerts(); BotGroupSay(this, "I can't backstab with this weapon!"); } diff --git a/zone/bot.h b/zone/bot.h index 5836af1335..0ee67c5ce3 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -1105,7 +1105,7 @@ class Bot : public NPC { int32 GenerateBaseManaPoints(); void GenerateSpecialAttacks(); void SetBotID(uint32 botID); - void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag; } + void SetCombatRoundForAlerts(bool flag = true) { m_combat_round_alert_flag = flag; } void SetAttackingFlag(bool flag = true) { m_attacking_flag = flag; } void SetPullingFlag(bool flag = true) { m_pulling_flag = flag; } void SetReturningFlag(bool flag = true) { m_returning_flag = flag; } diff --git a/zone/spells.cpp b/zone/spells.cpp index 4f54b38c53..5391f03da8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2546,9 +2546,9 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target); if ( ( - (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB) + (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB)) ) && - IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) + IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id) ) { range = spells[spell_id].aoe_range; } From 9a135ba32a8785ec8fd81a11b040feaf06d1798f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:26:51 -0500 Subject: [PATCH 101/394] remove commented lines for ^follow --- zone/bot_commands/follow.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 9e7c33eafd..57e97a6075 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -118,7 +118,6 @@ void bot_command_follow(Client* c, const Seperator* sep) } std::list sbl; - //if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg] : nullptr, class_race_check ? atoi(sep->arg[ab_arg]) : 0) == ActionableBots::ABT_None) { if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } From 088f13130a7ee6fb251b0f9dcfec5af3ad116cfc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:56:49 -0500 Subject: [PATCH 102/394] command cleanup --- zone/bot_commands/actionable.cpp | 2 +- zone/bot_commands/behind_mob.cpp | 2 +- zone/bot_commands/spell_engaged_priority.cpp | 4 ++-- zone/bot_commands/spell_idle_priority.cpp | 4 ++-- zone/bot_commands/spell_max_thresholds.cpp | 3 +-- 5 files changed, 7 insertions(+), 8 deletions(-) diff --git a/zone/bot_commands/actionable.cpp b/zone/bot_commands/actionable.cpp index 73ec9ef6c6..f66a5b60a2 100644 --- a/zone/bot_commands/actionable.cpp +++ b/zone/bot_commands/actionable.cpp @@ -29,7 +29,7 @@ void bot_command_actionable(Client* c, const Seperator* sep) "[spawned] - selects all spawned bots.", "[all] - selects all spawned bots.", "
", - "You may only select your bots as actionable" + "You may only select your own bots." }; std::vector example_format = { }; diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 5761c4c319..8e69158607 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -9,7 +9,7 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { std::vector description = { - "Toggles whether or not bots will stay behind the mob during combat." + "-Toggles whether or not bots will stay behind the mob during combat." }; std::vector notes = { }; diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 03c151c3ae..89d76f2b5b 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -14,8 +14,8 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) std::vector notes = { - "-Setting a spell type to 0 will prevent that type from being cast.", - "-If 2 or more are set to the same priority they will sort by spell type ID." + "- Setting a spell type to 0 will prevent that type from being cast.", + "- If 2 or more are set to the same priority they will sort by spell type ID." }; std::vector example_format = diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 9bb95cf467..566a3f46a6 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -14,8 +14,8 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) std::vector notes = { - "-Setting a spell type to 0 will prevent that type from being cast.", - "-If 2 or more are set to the same priority they will sort by spell type ID." + "- Setting a spell type to 0 will prevent that type from being cast.", + "- If 2 or more are set to the same priority they will sort by spell type ID." }; std::vector example_format = diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 8ded61c1f1..3b4e0e85ff 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -16,8 +16,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", - "- All Heals, Cures, Buffs (DS and resists included)", - "are based off the target's setting, not the caster", + "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", "- e.g., BotA is healing BotB using BotB's settings", }; From 0f16601e5d7fb4cf376272fcf10281c4fc9ee7fd Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 1 Nov 2024 14:58:37 -0500 Subject: [PATCH 103/394] Rewrite ^followd command and remove squared values from command --- .../database_update_manifest_bots.cpp | 3 +- common/ruletypes.h | 2 + zone/bot.cpp | 8 +- zone/bot.h | 3 - zone/bot_commands/bot.cpp | 193 +++++++++++++++--- 5 files changed, 178 insertions(+), 31 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index afaa9f23ea..44083475b5 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -189,7 +189,7 @@ WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' AND bd.expansion_bitmask != rv.rule_value; INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, `follow_distance`, 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' @@ -238,6 +238,7 @@ ALTER TABLE `bot_data` UPDATE `bot_command_settings` SET `aliases`= 'bh' WHERE `bot_command`='behindmob'; UPDATE `bot_command_settings` SET `aliases`= 'bs|settings' WHERE `bot_command`='botsettings'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|followdistance') ELSE 'followd||followdistance' END WHERE `bot_command`='botfollowdistance' AND `aliases` NOT LIKE '%followdistance%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ranged|toggleranged|btr') ELSE 'ranged|toggleranged|btr' END WHERE `bot_command`='bottoggleranged' AND `aliases` NOT LIKE '%ranged|toggleranged|btr%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|cr') ELSE 'cr' END WHERE `bot_command`='casterrange' AND `aliases` NOT LIKE '%cr%'; UPDATE `bot_command_settings` SET `aliases`= 'copy' WHERE `bot_command`='copysettings'; diff --git a/common/ruletypes.h b/common/ruletypes.h index 278701b78f..435c04e8e6 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -873,6 +873,8 @@ RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. De RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.") RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") +RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") +RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index 34315e5236..956692b033 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9894,7 +9894,7 @@ void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { SetShowHelm(settingValue); break; case BotBaseSettings::FollowDistance: - SetFollowDistance(EQ::Clamp(static_cast(settingValue), static_cast(1), BOT_FOLLOW_DISTANCE_DEFAULT_MAX)); + SetFollowDistance(EQ::Clamp(static_cast(settingValue * settingValue), static_cast(1), static_cast((RuleI(Bots, MaxFollowDistance) * RuleI(Bots, MaxFollowDistance))))); break; case BotBaseSettings::StopMeleeLevel: SetStopMeleeLevel(settingValue); @@ -9943,8 +9943,8 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { //LogBotSettingsDetail("Returning current GetShowHelm of [{}] for [{}]", GetShowHelm(), GetCleanName()); //deleteme return GetShowHelm(); case BotBaseSettings::FollowDistance: - //LogBotSettingsDetail("Returning current GetFollowDistance of [{}] for [{}]", GetFollowDistance(), GetCleanName()); //deleteme - return GetFollowDistance(); + //LogBotSettingsDetail("Returning current GetFollowDistance of [{}] for [{}]", sqrt(GetFollowDistance()), GetCleanName()); //deleteme + return sqrt(GetFollowDistance()); case BotBaseSettings::StopMeleeLevel: //LogBotSettingsDetail("Returning current GetStopMeleeLevel of [{}] for [{}]", GetStopMeleeLevel(), GetCleanName()); //deleteme return GetStopMeleeLevel(); @@ -9992,7 +9992,7 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { case BotBaseSettings::ShowHelm: return true; case BotBaseSettings::FollowDistance: - return BOT_FOLLOW_DISTANCE_DEFAULT; + return (RuleI(Bots, DefaultFollowDistance) * RuleI(Bots, DefaultFollowDistance)); case BotBaseSettings::StopMeleeLevel: if (IsCasterClass(GetClass())) { return RuleI(Bots, CasterStopMeleeLevel); diff --git a/zone/bot.h b/zone/bot.h index 0ee67c5ce3..4e40f3939e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -37,9 +37,6 @@ #include -constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT = 184; // as DSq value (~13.565 units) -constexpr uint32 BOT_FOLLOW_DISTANCE_DEFAULT_MAX = 2500; // as DSq value (50 units) - constexpr uint32 BOT_KEEP_ALIVE_INTERVAL = 5000; // 5 seconds constexpr uint32 BOT_COMBAT_JITTER_INTERVAL_MIN = 1500; // 1.5 seconds diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index ccf1e51ffc..e360d49ed2 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -466,58 +466,205 @@ void bot_command_delete(Client *c, const Seperator *sep) void bot_command_follow_distance(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_follow_distance", sep->arg[0], "botfollowdistance")) + if (helper_command_alias_fail(c, "bot_command_follow_distance", sep->arg[0], "botfollowdistance")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [set [1 to %i]] [distance] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0], BOT_FOLLOW_DISTANCE_DEFAULT_MAX); - c->Message(Chat::White, "usage: %s [clear] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + std::vector description = + { + "Sets or resets the follow distance of the selected bots." + }; + + std::vector notes = + { + fmt::format( + "[Default]: {}", + RuleI(Bots, MaxFollowDistance) + ), + + fmt::format( + "- You must use a value between 1 and {}.", + RuleI(Bots, MaxFollowDistance) + ) + }; + + std::vector example_format = + { + fmt::format( + "{} [reset]/[set [value]] [actionable]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all bots to follow at a distance of 25:", + fmt::format( + "{} set 25 spawned", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To check the curret following distance of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To reset the following distance of all Wizards:", + fmt::format( + "{} reset byclass {}", + sep->arg[0], + Class::Wizard + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + return; } - const int ab_mask = ActionableBots::ABM_NoFilter; - uint32 bfd = BOT_FOLLOW_DISTANCE_DEFAULT; + const int ab_mask = ActionableBots::ABM_Type2; + + uint32 bfd = RuleI(Bots, DefaultFollowDistance); bool set_flag = false; + bool currentCheck = false; int ab_arg = 2; - if (!strcasecmp(sep->arg[1], "set")) { + std::string arg1 = sep->arg[1]; + + if (!arg1.compare("set")) { if (!sep->IsNumber(2)) { - c->Message(Chat::White, "A numeric [distance] is required to use this command. [1 to %i]", BOT_FOLLOW_DISTANCE_DEFAULT_MAX); + c->Message(Chat::Yellow, "You must enter a value between 1 and %i.", RuleI(Bots, MaxFollowDistance)); + return; } bfd = Strings::ToInt(sep->arg[2]); - if (bfd < 1) - bfd = 1; - if (bfd > BOT_FOLLOW_DISTANCE_DEFAULT_MAX) - bfd = BOT_FOLLOW_DISTANCE_DEFAULT_MAX; + + if (bfd < 1) { + c->Message(Chat::Yellow, "You must enter a value between 1 and %i.", RuleI(Bots, MaxFollowDistance)); + + return; + } + + if (bfd > RuleI(Bots, MaxFollowDistance)) { + c->Message(Chat::Yellow, "You must enter a value between 1 and %i.", RuleI(Bots, MaxFollowDistance)); + + return; + } + set_flag = true; - ab_arg = 3; + ++ab_arg; } - else if (strcasecmp(sep->arg[1], "clear")) { - c->Message(Chat::White, "This command requires a [set | clear] argument"); + else if (!arg1.compare("current")) { + currentCheck = true; + } + else if (arg1.compare("reset")) { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + std::list sbl; - auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]); - if (ab_type == ActionableBots::ABT_None) + + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; + } - int bot_count = 0; + sbl.remove(nullptr); + + int botCount = 0; for (auto bot_iter : sbl) { - if (!bot_iter) + if (!bot_iter) { continue; + } - bot_iter->SetFollowDistance(bfd); + if (currentCheck) { + Mob* follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); - ++bot_count; + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I am currently following {} at a distance of {}.'", + bot_iter->GetCleanName(), + follow_mob ? follow_mob->GetCleanName() : "no one", + sqrt(bot_iter->GetFollowDistance()) + ).c_str() + ); + } + else { + bot_iter->SetFollowDistance(bfd * bfd); + ++botCount; + } } - if (ab_type == ActionableBots::ABT_All) { - c->Message(Chat::White, "%s all of your bot follow distances to %i", set_flag ? "Set" : "Cleared", bfd); + if (currentCheck) { + return; + } + + if (botCount == 1) { + Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); + + c->Message( + Chat::Green, + fmt::format( + "{} says, 'Following {} at a distance of {}.'", + sbl.front()->GetCleanName(), + follow_mob ? follow_mob->GetCleanName() : "you", + bfd + ).c_str() + ); } else { - c->Message(Chat::White, "%s %i of your spawned bot follow distances to %i", (set_flag ? "Set" : "Cleared"), bot_count, bfd); + c->Message( + Chat::Green, + fmt::format( + "{} of your bots are now following at a distance of {}.", + botCount, + bfd + ).c_str() + ); } } From 85c0a114bce0cbe7fe455966fe3ec29a6f0f3e4e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 2 Nov 2024 22:05:55 -0500 Subject: [PATCH 104/394] misc cleanup --- zone/bot_commands/caster_range.cpp | 2 +- zone/bot_commands/follow.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/bot_commands/caster_range.cpp b/zone/bot_commands/caster_range.cpp index 447c739db7..87676c7d46 100644 --- a/zone/bot_commands/caster_range.cpp +++ b/zone/bot_commands/caster_range.cpp @@ -70,7 +70,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) c->Message( Chat::White, fmt::format( - "{} says, 'My current Caster Range is {}.'", + "{} says, 'My current caster range is {}.'", my_bot->GetCleanName(), my_bot->GetBotCasterRange() ).c_str() diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 57e97a6075..c5b7f8f780 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -132,6 +132,7 @@ void bot_command_follow(Client* c, const Seperator* sep) for (auto bot_iter : sbl) { if (currentCheck) { follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); + c->Message( Chat::Green, fmt::format( From d06ae5d4789fecf2573a71ef93c0f8fc3188b31e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 00:20:41 -0600 Subject: [PATCH 105/394] fix formatting in cazictouch --- zone/spells.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 5391f03da8..a57093ee22 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2457,8 +2457,9 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in } } - if ((RuleB(Bots, CazicTouchBotsOwner) && spell_target && spell_target->IsBot()) && spell_id == (SPELL_CAZIC_TOUCH || spell_id == SPELL_TOUCH_OF_VINITRAS)) { + if ((RuleB(Bots, CazicTouchBotsOwner) && spell_target && spell_target->IsBot()) && (spell_id == SPELL_CAZIC_TOUCH || spell_id == SPELL_TOUCH_OF_VINITRAS)) { auto bot_owner = spell_target->GetOwner(); + if (bot_owner) { spell_target = bot_owner; } From 63233d11e898558435b5d83f13d0ee705ab1fa8a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 00:30:40 -0600 Subject: [PATCH 106/394] Implement and rewrite stances --- .../database_update_manifest_bots.cpp | 15 +- .../base/base_bot_settings_repository.h | 52 ++-- zone/bot.cpp | 145 +++++----- zone/bot.h | 26 +- zone/bot_commands/bot.cpp | 252 +++++++++++++++--- zone/bot_commands/default_settings.cpp | 146 +++++----- zone/bot_database.cpp | 62 +++-- zone/client.cpp | 32 +-- zone/mob.cpp | 208 ++++++++++++--- zone/mob.h | 8 +- 10 files changed, 655 insertions(+), 291 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 44083475b5..639d173b06 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -174,6 +174,7 @@ CREATE TABLE `bot_settings` ( `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, `char_id` INT UNSIGNED NOT NULL, `bot_id` INT UNSIGNED NOT NULL, + `stance` INT UNSIGNED NOT NULL, `setting_id` INT UNSIGNED NOT NULL, `setting_type` INT UNSIGNED NOT NULL, `value` INT UNSIGNED NOT NULL, @@ -183,16 +184,16 @@ CREATE TABLE `bot_settings` ( ) COLLATE='utf8mb4_general_ci'; -INSERT INTO bot_settings SELECT NULL, 0, bd.`bot_id`, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd +INSERT INTO bot_settings SELECT NULL, 0, bd.`bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd JOIN rule_values rv WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' AND bd.expansion_bitmask != rv.rule_value; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' +SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' FROM ( SELECT `bot_id`, (CASE @@ -204,11 +205,11 @@ FROM ( ) AS `subquery` WHERE `sml` != `stop_melee_level`; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; +INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' +SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' FROM ( SELECT `bot_id`, (CASE diff --git a/common/repositories/base/base_bot_settings_repository.h b/common/repositories/base/base_bot_settings_repository.h index 2018177dc9..92d4aba15f 100644 --- a/common/repositories/base/base_bot_settings_repository.h +++ b/common/repositories/base/base_bot_settings_repository.h @@ -22,6 +22,7 @@ class BaseBotSettingsRepository { uint32_t id; uint32_t char_id; uint32_t bot_id; + uint8_t stance; uint16_t setting_id; uint8_t setting_type; int32_t value; @@ -40,6 +41,7 @@ class BaseBotSettingsRepository { "id", "char_id", "bot_id", + "stance", "setting_id", "setting_type", "value", @@ -54,6 +56,7 @@ class BaseBotSettingsRepository { "id", "char_id", "bot_id", + "stance", "setting_id", "setting_type", "value", @@ -102,6 +105,7 @@ class BaseBotSettingsRepository { e.id = 0; e.char_id = 0; e.bot_id = 0; + e.stance = 0; e.setting_id = 0; e.setting_type = 0; e.value = 0; @@ -146,11 +150,12 @@ class BaseBotSettingsRepository { e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.category_name = row[6] ? row[6] : ""; - e.setting_name = row[7] ? row[7] : ""; + e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.category_name = row[7] ? row[7] : ""; + e.setting_name = row[8] ? row[8] : ""; return e; } @@ -187,11 +192,12 @@ class BaseBotSettingsRepository { v.push_back(columns[0] + " = " + std::to_string(e.id)); v.push_back(columns[1] + " = " + std::to_string(e.char_id)); v.push_back(columns[2] + " = " + std::to_string(e.bot_id)); - v.push_back(columns[3] + " = " + std::to_string(e.setting_id)); - v.push_back(columns[4] + " = " + std::to_string(e.setting_type)); - v.push_back(columns[5] + " = " + std::to_string(e.value)); - v.push_back(columns[6] + " = '" + Strings::Escape(e.category_name) + "'"); - v.push_back(columns[7] + " = '" + Strings::Escape(e.setting_name) + "'"); + v.push_back(columns[3] + " = " + std::to_string(e.stance)); + v.push_back(columns[4] + " = " + std::to_string(e.setting_id)); + v.push_back(columns[5] + " = " + std::to_string(e.setting_type)); + v.push_back(columns[6] + " = " + std::to_string(e.value)); + v.push_back(columns[7] + " = '" + Strings::Escape(e.category_name) + "'"); + v.push_back(columns[8] + " = '" + Strings::Escape(e.setting_name) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -216,6 +222,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); @@ -253,6 +260,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); @@ -294,11 +302,12 @@ class BaseBotSettingsRepository { e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.category_name = row[6] ? row[6] : ""; - e.setting_name = row[7] ? row[7] : ""; + e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.category_name = row[7] ? row[7] : ""; + e.setting_name = row[8] ? row[8] : ""; all_entries.push_back(e); } @@ -326,11 +335,12 @@ class BaseBotSettingsRepository { e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.value = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.category_name = row[6] ? row[6] : ""; - e.setting_name = row[7] ? row[7] : ""; + e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; + e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; + e.category_name = row[7] ? row[7] : ""; + e.setting_name = row[8] ? row[8] : ""; all_entries.push_back(e); } @@ -408,6 +418,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); @@ -438,6 +449,7 @@ class BaseBotSettingsRepository { v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); v.push_back(std::to_string(e.setting_type)); v.push_back(std::to_string(e.value)); diff --git a/zone/bot.cpp b/zone/bot.cpp index 956692b033..6bf9e5e250 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9985,14 +9985,14 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { return true; } -int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { +int Bot::GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance) { switch (botSetting) { case BotBaseSettings::ExpansionBitmask: return RuleI(Bots, BotExpansionSettings); case BotBaseSettings::ShowHelm: return true; case BotBaseSettings::FollowDistance: - return (RuleI(Bots, DefaultFollowDistance) * RuleI(Bots, DefaultFollowDistance)); + return RuleI(Bots, DefaultFollowDistance); case BotBaseSettings::StopMeleeLevel: if (IsCasterClass(GetClass())) { return RuleI(Bots, CasterStopMeleeLevel); @@ -10003,7 +10003,7 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { case BotBaseSettings::PetSetTypeSetting: return 0; case BotBaseSettings::BehindMob: - if (GetClass() == Class::Rogue) { + if (GetClass() == Class::Rogue || (IsPureMeleeClass() && GetClass() != Class::Warrior)) { return true; } else { @@ -10043,10 +10043,14 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting) { void Bot::LoadDefaultBotSettings() { + _spellSettings.clear(); + + uint8 botStance = GetBotStance(); + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i)); - LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i)); //deleteme - } + SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, botStance)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); //deleteme + } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { BotSpellSettings_Struct t; @@ -10054,28 +10058,28 @@ void Bot::LoadDefaultBotSettings() { t.spellType = i; t.shortName = GetSpellTypeShortNameByID(i); t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i); - t.delay = GetDefaultSpellDelay(i); - t.minThreshold = GetDefaultSpellMinThreshold(i); - t.maxThreshold = GetDefaultSpellMaxThreshold(i); - t.resistLimit = GetDefaultSpellTypeResistLimit(i); - t.aggroCheck = GetDefaultSpellTypeAggroCheck(i); - t.minManaPct = GetDefaultSpellTypeMinManaLimit(i); - t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i); - t.minHPPct = GetDefaultSpellTypeMinHPLimit(i); - t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i); - t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass()); - t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass()); - t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass()); - t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i); + t.hold = GetDefaultSpellHold(i, botStance); + t.delay = GetDefaultSpellDelay(i, botStance); + t.minThreshold = GetDefaultSpellMinThreshold(i, botStance); + t.maxThreshold = GetDefaultSpellMaxThreshold(i, botStance); + t.resistLimit = GetDefaultSpellTypeResistLimit(i, botStance); + t.aggroCheck = GetDefaultSpellTypeAggroCheck(i, botStance); + t.minManaPct = GetDefaultSpellTypeMinManaLimit(i, botStance); + t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i, botStance); + t.minHPPct = GetDefaultSpellTypeMinHPLimit(i, botStance); + t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i, botStance); + t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass(), botStance); + t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass(), botStance); + t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass(), botStance); + t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance); t.recastTimer.Start(); _spellSettings.push_back(t); - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); //deleteme - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); //deleteme - LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i), GetDefaultSpellTypeMinManaLimit(i), GetDefaultSpellTypeMaxManaLimit(i), GetDefaultSpellTypeMinHPLimit(i), GetDefaultSpellTypeMaxHPLimit(i)); //deleteme - LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}] | recastTimer = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass()), GetDefaultSpellTypeEngagedPriority(i, GetClass()), GetDefaultSpellTypePursuePriority(i, GetClass()), GetDefaultSpellTypeAEOrGroupTargetCount(i), t.recastTimer.GetRemainingTime()); //deleteme + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(botStance), botStance); //deleteme + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, botStance), GetDefaultSpellDelay(i, botStance), GetDefaultSpellMinThreshold(i, botStance), GetDefaultSpellMaxThreshold(i, botStance)); //deleteme + LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, botStance), GetDefaultSpellTypeMinManaLimit(i, botStance), GetDefaultSpellTypeMaxManaLimit(i, botStance), GetDefaultSpellTypeMinHPLimit(i, botStance), GetDefaultSpellTypeMaxHPLimit(i, botStance)); //deleteme + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), botStance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), botStance), GetDefaultSpellTypePursuePriority(i, GetClass(), botStance), GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); //deleteme } } @@ -10149,36 +10153,36 @@ uint16 Bot::GetSpellTypePriority(uint16 spellType, uint8 priorityType) { } } -int Bot::GetDefaultSetting(uint16 settingCategory, uint16 settingType) { +int Bot::GetDefaultSetting(uint16 settingCategory, uint16 settingType, uint8 stance) { switch (settingCategory) { case BotSettingCategories::BaseSetting: - return GetDefaultBotBaseSetting(settingType); + return GetDefaultBotBaseSetting(settingType, stance); case BotSettingCategories::SpellHold: - return GetDefaultSpellHold(settingType); + return GetDefaultSpellHold(settingType, stance); case BotSettingCategories::SpellDelay: - return GetDefaultSpellDelay(settingType); + return GetDefaultSpellDelay(settingType, stance); case BotSettingCategories::SpellMinThreshold: - return GetDefaultSpellMinThreshold(settingType); + return GetDefaultSpellMinThreshold(settingType, stance); case BotSettingCategories::SpellMaxThreshold: - return GetDefaultSpellMinThreshold(settingType); + return GetDefaultSpellMaxThreshold(settingType, stance); case BotSettingCategories::SpellTypeAggroCheck: - return GetDefaultSpellTypeAggroCheck(settingType); + return GetDefaultSpellTypeAggroCheck(settingType, stance); case BotSettingCategories::SpellTypeMinManaPct: - return GetDefaultSpellTypeMinManaLimit(settingType); + return GetDefaultSpellTypeMinManaLimit(settingType, stance); case BotSettingCategories::SpellTypeMaxManaPct: - return GetDefaultSpellTypeMaxManaLimit(settingType); + return GetDefaultSpellTypeMaxManaLimit(settingType, stance); case BotSettingCategories::SpellTypeMinHPPct: - return GetDefaultSpellTypeMinHPLimit(settingType); + return GetDefaultSpellTypeMinHPLimit(settingType, stance); case BotSettingCategories::SpellTypeMaxHPPct: - return GetDefaultSpellTypeMaxHPLimit(settingType); + return GetDefaultSpellTypeMaxHPLimit(settingType, stance); case BotSettingCategories::SpellTypeIdlePriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Idle, GetClass()); + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Idle, GetClass(), stance); case BotSettingCategories::SpellTypeEngagedPriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Engaged, GetClass()); + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Engaged, GetClass(), stance); case BotSettingCategories::SpellTypePursuePriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Pursue, GetClass()); + return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Pursue, GetClass(), stance); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - return GetDefaultSpellTypeAEOrGroupTargetCount(settingType); + return GetDefaultSpellTypeAEOrGroupTargetCount(settingType, stance); default: break; } @@ -10195,7 +10199,7 @@ int Bot::GetSetting(uint16 settingCategory, uint16 settingType) { case BotSettingCategories::SpellMinThreshold: return GetSpellMinThreshold(settingType); case BotSettingCategories::SpellMaxThreshold: - return GetSpellMinThreshold(settingType); + return GetSpellMaxThreshold(settingType); case BotSettingCategories::SpellTypeAggroCheck: return GetSpellTypeAggroCheck(settingType); case BotSettingCategories::SpellTypeMinManaPct: @@ -10219,20 +10223,20 @@ int Bot::GetSetting(uint16 settingCategory, uint16 settingType) { } } -uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass, uint8 stance) { switch (priorityType) { case BotPriorityCategories::Idle: - return GetDefaultSpellTypeIdlePriority(spellType, botClass); + return GetDefaultSpellTypeIdlePriority(spellType, botClass, stance); case BotPriorityCategories::Engaged: - return GetDefaultSpellTypeEngagedPriority(spellType, botClass); + return GetDefaultSpellTypeEngagedPriority(spellType, botClass, stance); case BotPriorityCategories::Pursue: - return GetDefaultSpellTypePursuePriority(spellType, botClass); + return GetDefaultSpellTypePursuePriority(spellType, botClass, stance); default: return 0; } } -uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance) { if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, botClass)) { return 0; } @@ -10341,7 +10345,7 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass) { return priority; } -uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, uint8 stance) { switch (spellType) { case BotSpellTypes::Escape: return 1; @@ -10429,14 +10433,12 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass) return 42; case BotSpellTypes::InCombatBuffSong: return 43; - case BotSpellTypes::Pet: - return 44; default: return 0; } } -uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass) { +uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint8 stance) { switch (spellType) { case BotSpellTypes::Escape: return 1; @@ -10487,7 +10489,7 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass) } } -uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType) { +uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance) { if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { return RuleI(Bots, SpellResistLimit); @@ -10497,35 +10499,46 @@ uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType) { } } -bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType) { +bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance) { + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return false; + default: + break; + } + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Root: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::Fear: + case BotSpellTypes::Stun: case BotSpellTypes::AENukes: case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: case BotSpellTypes::AESnare: - case BotSpellTypes::Snare: + case BotSpellTypes::AEFear: case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: + case BotSpellTypes::AERoot: case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - case BotSpellTypes::AEStun: - case BotSpellTypes::Stun: + case BotSpellTypes::PBAENuke: return true; default: return false; } } -uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spellType, uint8 stance) { return 0; } -uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::InCombatBuff: if (GetClass() == Class::Shaman) { @@ -10540,7 +10553,7 @@ uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType) { return 100; } -uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::InCombatBuff: if (GetClass() == Class::Shaman) { @@ -10555,11 +10568,11 @@ uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType) { return 0; } -uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spellType) { +uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spellType, uint8 stance) { return 100; } -uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType) { +uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType, uint8 stance) { if (IsAEBotSpellType(spellType)) { return RuleI(Bots, MinTargetsForAESpell); } diff --git a/zone/bot.h b/zone/bot.h index 4e40f3939e..553148ee51 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -458,7 +458,7 @@ class Bot : public NPC { void CopyBotSpellSettings(Bot* to); void ResetBotSpellSettings(); int GetBotBaseSetting(uint16 botSetting); - int GetDefaultBotBaseSetting(uint16 botSetting); + int GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance = Stance::Balanced); void SetBotBaseSetting(uint16 botSetting, int settingValue); void LoadDefaultBotSettings(); void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); @@ -467,18 +467,18 @@ class Bot : public NPC { std::string GetBotSpellCategoryName(uint8 setting_type); std::string GetBotSettingCategoryName(uint8 setting_type); - int GetDefaultSetting(uint16 settingCategory, uint16 settingType); - uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass); - uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass); - uint16 GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass); - uint16 GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass); - uint16 GetDefaultSpellTypeResistLimit(uint16 spellType); - bool GetDefaultSpellTypeAggroCheck(uint16 spellType); - uint8 GetDefaultSpellTypeMinManaLimit(uint16 spellType); - uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spellType); - uint8 GetDefaultSpellTypeMinHPLimit(uint16 spellType); - uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spellType); - uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType); + int GetDefaultSetting(uint16 settingCategory, uint16 settingType, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance = Stance::Balanced); + bool GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMinManaLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMinHPLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spellType, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType, uint8 stance = Stance::Balanced); int GetSetting(uint16 settingCategory, uint16 settingType); uint16 GetSpellTypePriority(uint16 spellType, uint8 priorityType); diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index e360d49ed2..d867b4e67a 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1180,43 +1180,188 @@ void bot_command_stance(Client *c, const Seperator *sep) } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [current | value: 1-9] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message( - Chat::White, + std::vector description = + { + "Change a bot's stance to control the way it behaves." + }; + + std::vector notes = + { + "- Changing a stance will reset all settings to match that stance type.", + "- Any changes made will only save to that stance for future use.", fmt::format( - "Value: {} ({}), {} ({}), {} ({})", - Stance::Passive, + "- {} (#{}) will tell Non-Warrior classes to taunt automatically.", + Stance::GetName(Stance::Aggressive), + Stance::Aggressive + ), + "
", + "Available stances:", + fmt::format( + "{} (#{}), {} (#{}), {} (#{}), {} (#{}), {} (#{}), {} (#{}), {} (#{})", Stance::GetName(Stance::Passive), - Stance::Balanced, + Stance::Passive, Stance::GetName(Stance::Balanced), + Stance::Balanced, + Stance::GetName(Stance::Efficient), + Stance::Efficient, + Stance::GetName(Stance::Aggressive), Stance::Aggressive, - Stance::GetName(Stance::Aggressive) - ).c_str() + Stance::GetName(Stance::Assist), + Stance::Assist, + Stance::GetName(Stance::Burn), + Stance::Burn, + Stance::GetName(Stance::AEBurn), + Stance::AEBurn + ), + "
", + fmt::format( + "- {} (#{}) [Default] - Overall balance and casts most spell types by default.", + Stance::GetName(Stance::Balanced), + Stance::Balanced + ), + fmt::format( + "- {} (#{}) - Idle. Does not cast or engage in combat.", + Stance::GetName(Stance::Passive), + Stance::Passive + ), + fmt::format( + "- {} (#{}) - More mana and aggro efficient (SKs will still cast hate line). Longer delays between detrimental spells, thresholds adjusted to cast less often.", + Stance::GetName(Stance::Efficient), + Stance::Efficient + ), + fmt::format( + "- {} (#{}) - Much more aggressive in their cast times and thresholds. More DPS, debuffs and slow but a higher risk of snagging aggro.", + Stance::GetName(Stance::Aggressive), + Stance::Aggressive + ), + fmt::format( + "- {} (#{}) - Support role. Most offensive spell types are disabled. Focused on heals, cures, CC, debuffs and slows.", + Stance::GetName(Stance::Assist), + Stance::Assist + ), + fmt::format( + "- {} (#{}) - Murder. Doesn't care about aggro, just wants to kill. DPS Machine.", + Stance::GetName(Stance::Burn), + Stance::Burn + ), + fmt::format( + "- {} (#{}) - Murder EVERYTHING. Doesn't care about aggro, casts AEs. Everything must die ASAP.", + Stance::GetName(Stance::AEBurn), + Stance::AEBurn + ) + }; + + std::vector example_format = + { + fmt::format( + "{} [current | value: {}-{}]", + sep->arg[0], + Stance::Passive, + Stance::AEBurn + ) + }; + std::vector examples_one = + { + "To set all bots to BurnAE:", + fmt::format( + "{} {} spawned {}", + sep->arg[0], + Stance::Aggressive, + Class::ShadowKnight + ) + }; + std::vector examples_two = + { + "To set all Shadowknights to Aggressive:", + fmt::format( + "{} {} byclass {}", + sep->arg[0], + Stance::Aggressive, + Class::ShadowKnight + ) + }; + std::vector examples_three = + { + "To check the current stances of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + return; } const int ab_mask = ActionableBots::ABM_Type1; - std::string arg1 = sep->arg[1]; + bool currentCheck = false; int ab_arg = 1; - bool current_check = false; uint32 value = 0; + std::string arg1 = sep->arg[1]; + if (sep->IsNumber(1)) { ++ab_arg; value = atoi(sep->arg[1]); - if (value < 0 || value > 300) { - c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); + if ( + value < Stance::Passive || + value > Stance::AEBurn || + value == Stance::Reactive || + value == Stance::Assist + ) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid stance ID, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } } else if (!arg1.compare("current")) { ++ab_arg; - current_check = true; + currentCheck = true; } else { - c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } @@ -1232,26 +1377,56 @@ void bot_command_stance(Client *c, const Seperator *sep) return; } - if (!current_check && (value == Stance::Unknown || (value != Stance::Passive && value != Stance::Balanced && value != Stance::Aggressive))) { - c->Message(Chat::White, "A [current] argument or valid numeric [value] is required to use this command"); - return; - } - + Bot* first_found = nullptr; + int success_count = 0; for (auto bot_iter : sbl) { - if (!bot_iter) - continue; + if (!first_found) { + first_found = bot_iter; + } + + if (currentCheck) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My current stance is {} ({}).'", + bot_iter->GetCleanName(), + Stance::GetName(bot_iter->GetBotStance()), + bot_iter->GetBotStance() + ).c_str() + ); - if (!current_check) { - bot_iter->SetBotStance(value); - bot_iter->Save(); + continue; } - Bot::BotGroupSay( - bot_iter, + bot_iter->Save(); + bot_iter->SetBotStance(value); + bot_iter->LoadDefaultBotSettings(); + database.botdb.LoadBotSettings(bot_iter); + bot_iter->Save(); + ++success_count; + } + + if (currentCheck) { + return; + } + + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I am now set to the stance [{}].'", + first_found->GetCleanName(), + Stance::GetName(value) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, fmt::format( - "My current stance is {} ({}).", - Stance::GetName(bot_iter->GetBotStance()), - bot_iter->GetBotStance() + "{} of your bots are now set to the stance [{}].", + success_count, + Stance::GetName(value) ).c_str() ); } @@ -1278,7 +1453,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) uint8 sml = RuleI(Bots, CasterStopMeleeLevel); bool sync_sml = false; bool reset_sml = false; - bool current_check = false; + bool currentCheck = false; if (sep->IsNumber(1)) { ab_arg = 2; @@ -1294,14 +1469,23 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) } else if (!arg1.compare("current")) { ab_arg = 2; - current_check = true; + currentCheck = true; } else if (!strcasecmp(sep->arg[1], "reset")) { ab_arg = 2; reset_sml = true; } else { - c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + return; } @@ -1347,7 +1531,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) sml = my_bot->GetDefaultBotBaseSetting(BotBaseSettings::StopMeleeLevel); } - if (current_check) { + if (currentCheck) { c->Message( Chat::White, fmt::format( @@ -1364,7 +1548,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) } } - if (!current_check) { + if (!currentCheck) { if (success_count == 1 && first_found) { c->Message( Chat::White, diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 1f7c854cad..87bb31b0f0 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -199,24 +199,28 @@ void bot_command_default_settings(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; std::string output = ""; - for (auto my_bot : sbl) { + uint8 botStance = 2; + + for (auto myBot : sbl) { if (!first_found) { - first_found = my_bot; + first_found = myBot; } + botStance = myBot->GetBotStance(); + if (!strcasecmp(sep->arg[1], "misc")) { for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + myBot->SetBotBaseSetting(i, myBot->GetDefaultBotBaseSetting(i, botStance)); output = "miscellanous settings"; } } else if (!strcasecmp(sep->arg[1], "holds")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); + myBot->SetSpellHold(spellType, myBot->GetDefaultSpellHold(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); + myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); } } @@ -224,11 +228,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "delays")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); + myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); } } @@ -236,11 +240,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "minthresholds")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); + myBot->SetSpellMinThreshold(spellType, myBot->GetDefaultSpellMinThreshold(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); + myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); } } @@ -248,11 +252,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "maxthresholds")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); + myBot->SetSpellMaxThreshold(spellType, myBot->GetDefaultSpellMaxThreshold(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); + myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); } } @@ -260,11 +264,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "aggrochecks")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); + myBot->SetSpellTypeAggroCheck(spellType, myBot->GetDefaultSpellTypeAggroCheck(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); + myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); } } @@ -272,11 +276,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "minmanapct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); + myBot->SetSpellTypeMinManaLimit(spellType, myBot->GetDefaultSpellTypeMinManaLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); + myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); } } @@ -284,11 +288,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "maxmanapct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); + myBot->SetSpellTypeMaxManaLimit(spellType, myBot->GetDefaultSpellTypeMaxManaLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); + myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); } } @@ -296,11 +300,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "minhppct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); + myBot->SetSpellTypeMinHPLimit(spellType, myBot->GetDefaultSpellTypeMinHPLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); + myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); } } @@ -308,11 +312,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "maxhppct")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); + myBot->SetSpellTypeMaxHPLimit(spellType, myBot->GetDefaultSpellTypeMaxHPLimit(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); + myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); } } @@ -320,11 +324,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "idlepriority")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); } } @@ -332,11 +336,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "engagedpriority")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); } } @@ -344,11 +348,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "pursuepriority")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); } } @@ -356,51 +360,51 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "targetcounts")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); + myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); } } output = "ae/group count settings"; } else if (!strcasecmp(sep->arg[1], "spellsettings")) { - my_bot->ResetBotSpellSettings(); + myBot->ResetBotSpellSettings(); output = "^spellsettings"; } else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { if (spellType != UINT16_MAX) { - my_bot->SetSpellHold(spellType, my_bot->GetDefaultSpellHold(spellType)); - my_bot->SetSpellDelay(spellType, my_bot->GetDefaultSpellDelay(spellType)); - my_bot->SetSpellMinThreshold(spellType, my_bot->GetDefaultSpellMinThreshold(spellType)); - my_bot->SetSpellMaxThreshold(spellType, my_bot->GetDefaultSpellMaxThreshold(spellType)); - my_bot->SetSpellTypeAggroCheck(spellType, my_bot->GetDefaultSpellTypeAggroCheck(spellType)); - my_bot->SetSpellTypeMinManaLimit(spellType, my_bot->GetDefaultSpellTypeMinManaLimit(spellType)); - my_bot->SetSpellTypeMaxManaLimit(spellType, my_bot->GetDefaultSpellTypeMaxManaLimit(spellType)); - my_bot->SetSpellTypeMinHPLimit(spellType, my_bot->GetDefaultSpellTypeMinHPLimit(spellType)); - my_bot->SetSpellTypeMaxHPLimit(spellType, my_bot->GetDefaultSpellTypeMaxHPLimit(spellType)); - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, my_bot->GetClass())); - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, my_bot->GetClass())); - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, my_bot->GetClass())); - my_bot->SetSpellTypeAEOrGroupTargetCount(spellType, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType)); + myBot->SetSpellHold(spellType, myBot->GetDefaultSpellHold(spellType, botStance)); + myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); + myBot->SetSpellMinThreshold(spellType, myBot->GetDefaultSpellMinThreshold(spellType, botStance)); + myBot->SetSpellMaxThreshold(spellType, myBot->GetDefaultSpellMaxThreshold(spellType, botStance)); + myBot->SetSpellTypeAggroCheck(spellType, myBot->GetDefaultSpellTypeAggroCheck(spellType, botStance)); + myBot->SetSpellTypeMinManaLimit(spellType, myBot->GetDefaultSpellTypeMinManaLimit(spellType, botStance)); + myBot->SetSpellTypeMaxManaLimit(spellType, myBot->GetDefaultSpellTypeMaxManaLimit(spellType, botStance)); + myBot->SetSpellTypeMinHPLimit(spellType, myBot->GetDefaultSpellTypeMinHPLimit(spellType, botStance)); + myBot->SetSpellTypeMaxHPLimit(spellType, myBot->GetDefaultSpellTypeMaxHPLimit(spellType, botStance)); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + myBot->SetSpellTypeAEOrGroupTargetCount(spellType, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); + myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); + myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); + myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); + myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); + myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); + myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); + myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); + myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); } } @@ -408,26 +412,26 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "all")) { for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i)); + myBot->SetBotBaseSetting(i, myBot->GetDefaultBotBaseSetting(i, botStance)); } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i)); - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i)); - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i)); - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i)); - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i)); - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass())); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass())); - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i)); + myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); + myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); + myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); + myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); + myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); + myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); + myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); + myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); + myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); }; - my_bot->ResetBotSpellSettings(); + myBot->ResetBotSpellSettings(); output = "settings"; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 20715d0d7f..e60a17961f 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2211,14 +2211,15 @@ bool BotDatabase::LoadBotSettings(Mob* m) } uint32 mobID = (m->IsClient() ? m->CastToClient()->CharacterID() : m->CastToBot()->GetBotID()); + uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {}", mobID); + query = fmt::format("`char_id` = {} AND `stance` = {}", mobID, stanceID); } else { - query = fmt::format("`bot_id` = {}", mobID); + query = fmt::format("`bot_id` = {} AND `stance` = {}", mobID, stanceID); } const auto& l = BotSettingsRepository::GetWhere(database, query); @@ -2228,12 +2229,24 @@ bool BotDatabase::LoadBotSettings(Mob* m) } for (const auto& e : l) { - LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." - , m->GetCleanName() - , (e.setting_type == BotSettingCategories::BaseSetting ? m->CastToBot()->GetBotSettingCategoryName(e.setting_id) : m->CastToBot()->GetBotSpellCategoryName(e.setting_id)) - , e.setting_id - , e.value - ); //deleteme + if (e.setting_type == BotSettingCategories::BaseSetting) { + LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." + , m->GetCleanName() + , m->CastToBot()->GetBotSettingCategoryName(e.setting_type) + , e.setting_type + , e.value + ); //deleteme + } + else { + LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}]." + , m->GetCleanName() + , m->CastToBot()->GetBotSpellCategoryName(e.setting_type) + , e.setting_type + , m->GetSpellTypeNameByID(e.setting_id) + , e.setting_id + , e.value + ); //deleteme + } m->SetBotSetting(e.setting_type, e.setting_id, e.value); } @@ -2251,16 +2264,17 @@ bool BotDatabase::SaveBotSettings(Mob* m) return false; } - uint32 botID = (m->IsClient() ? 0 : m->CastToBot()->GetBotID()); + uint32 botID = (m->IsBot() ? m->CastToBot()->GetBotID() : 0); uint32 charID = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); + uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {}", charID); + query = fmt::format("`char_id` = {} AND `stance` = {}", charID, stanceID); } else { - query = fmt::format("`bot_id` = {}", botID); + query = fmt::format("`bot_id` = {} AND `stance` = {}", botID, stanceID); } BotSettingsRepository::DeleteWhere(database, query); @@ -2268,16 +2282,19 @@ bool BotDatabase::SaveBotSettings(Mob* m) std::vector v; if (m->IsBot()) { + uint8 botStance = m->CastToBot()->GetBotStance(); + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i)) { + if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, botStance)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = charID, - .bot_id = botID, - .setting_id = static_cast(i), - .setting_type = static_cast(BotSettingCategories::BaseSetting), - .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), - .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), - .setting_name = m->CastToBot()->GetBotSettingCategoryName(i) + .char_id = charID, + .bot_id = botID, + .stance = stanceID, + .setting_id = static_cast(i), + .setting_type = static_cast(BotSettingCategories::BaseSetting), + .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), + .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->CastToBot()->GetBotSettingCategoryName(i) }; v.emplace_back(e); @@ -2288,10 +2305,11 @@ bool BotDatabase::SaveBotSettings(Mob* m) for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { - if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x)) { + if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x, botStance)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, + .stance = stanceID, .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToBot()->GetSetting(i, x), @@ -2301,7 +2319,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x)); //deleteme + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, botStance)); //deleteme } } } @@ -2312,6 +2330,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, + .stance = stanceID, .setting_id = static_cast(BotBaseSettings::IllusionBlock), .setting_type = static_cast(BotSettingCategories::BaseSetting), .value = m->GetIllusionBlock(), @@ -2331,6 +2350,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, + .stance = stanceID, .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToClient()->GetBotSetting(i, x), diff --git a/zone/client.cpp b/zone/client.cpp index fe36a60567..dd22e2a230 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13080,6 +13080,8 @@ void Client::ClientToNpcAggroProcess() } void Client::LoadDefaultBotSettings() { + _spellSettings.clear(); + // Only illusion block supported currently SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme @@ -13132,21 +13134,21 @@ int Client::GetBotSetting(uint8 settingType, uint16 botSetting) { void Client::SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue) { switch (settingType) { - case BotSettingCategories::BaseSetting: - SetBaseSetting(botSetting, settingValue); - break; - case BotSettingCategories::SpellHold: - SetSpellHold(botSetting, settingValue); - break; - case BotSettingCategories::SpellDelay: - SetSpellDelay(botSetting, settingValue); - break; - case BotSettingCategories::SpellMinThreshold: - SetSpellMinThreshold(botSetting, settingValue); - break; - case BotSettingCategories::SpellMaxThreshold: - SetSpellMaxThreshold(botSetting, settingValue); - break; + case BotSettingCategories::BaseSetting: + SetBaseSetting(botSetting, settingValue); + break; + case BotSettingCategories::SpellHold: + SetSpellHold(botSetting, settingValue); + break; + case BotSettingCategories::SpellDelay: + SetSpellDelay(botSetting, settingValue); + break; + case BotSettingCategories::SpellMinThreshold: + SetSpellMinThreshold(botSetting, settingValue); + break; + case BotSettingCategories::SpellMaxThreshold: + SetSpellMaxThreshold(botSetting, settingValue); + break; } } diff --git a/zone/mob.cpp b/zone/mob.cpp index 926ea33822..e75ce00683 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9046,18 +9046,30 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { return spellTypeName; } -bool Mob::GetDefaultSpellHold(uint16 spellType) { +bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::DOT: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::Assist: + return true; + default: + return false; + } case BotSpellTypes::AENukes: case BotSpellTypes::AERains: - case BotSpellTypes::AEMez: case BotSpellTypes::AEStun: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: case BotSpellTypes::AEDoT: case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: + switch (stance) { + case Stance::AEBurn: + return false; + default: + return true; + } + case BotSpellTypes::AESnare: case BotSpellTypes::AERoot: case BotSpellTypes::Root: case BotSpellTypes::AEDispel: @@ -9065,28 +9077,46 @@ bool Mob::GetDefaultSpellHold(uint16 spellType) { case BotSpellTypes::AEFear: case BotSpellTypes::Fear: return true; - case BotSpellTypes::Snare: - if (GetClass() == Class::Wizard) { - return true; + case BotSpellTypes::AEMez: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::HateRedux: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; } - else { - return false; + case BotSpellTypes::Snare: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Assist: + return true; + default: + if (GetClass() == Class::Wizard) { + return true; + } + else { + return false; + } } case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: case BotSpellTypes::PreCombatBuffSong: if (GetClass() == Class::Bard) { - return true; + return false; } else { - return false; + return true; } default: return false; } } -uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { +uint16 Mob::GetDefaultSpellDelay(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: @@ -9094,12 +9124,31 @@ uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: return 2500; - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: return 4000; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 8000; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return 22000; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 2000; + case Stance::Efficient: + return 8000; + default: + return 4000; + } case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::PBAENuke: @@ -9112,43 +9161,68 @@ uint16 Mob::GetDefaultSpellDelay(uint16 spellType) { case BotSpellTypes::Slow: case BotSpellTypes::AEStun: case BotSpellTypes::Stun: - return 6000; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 3000; + case Stance::Efficient: + return 10000; + default: + return 6000; + } case BotSpellTypes::AERoot: case BotSpellTypes::Root: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::PetCompleteHeals: - return 8000; + return 8000; case BotSpellTypes::Fear: case BotSpellTypes::AEFear: return 15000; - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetHoTHeals: - return 22000; default: return 1; } } -uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType) { +uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance) { switch (spellType) { - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: case BotSpellTypes::AEDebuff: case BotSpellTypes::Debuff: case BotSpellTypes::AEDispel: case BotSpellTypes::Dispel: case BotSpellTypes::AESlow: case BotSpellTypes::Slow: - return 20; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 20; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 5; + } case BotSpellTypes::AEDoT: case BotSpellTypes::DOT: - return 35; - case BotSpellTypes::Charm: - return 50; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + case Stance::Efficient: + return 40; + default: + return 25; + } case BotSpellTypes::Mez: case BotSpellTypes::AEMez: return 85; @@ -9157,25 +9231,60 @@ uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType) { } } -uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { +uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::Escape: case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: - return 25; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 40; + case Stance::Efficient: + default: + return 25; + } case BotSpellTypes::AELifetap: case BotSpellTypes::Lifetap: case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: - return 40; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 55; + case Stance::Efficient: + return 35; + default: + return 40; + } case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: - return 60; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 70; + case Stance::Efficient: + return 50; + default: + return 60; + } case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::PetCompleteHeals: - return 80; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 90; + case Stance::Efficient: + return 65; + default: + return 80; + } case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::PBAENuke: @@ -9195,7 +9304,17 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { case BotSpellTypes::AEDebuff: case BotSpellTypes::Debuff: case BotSpellTypes::Stun: - return 99; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 100; + case Stance::Aggressive: + return 100; + case Stance::Efficient: + return 90; + default: + return 99; + } case BotSpellTypes::Buff: case BotSpellTypes::Charm: case BotSpellTypes::Cure: @@ -9220,7 +9339,16 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType) { return 60; } else { - return 90; + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 95; + case Stance::Efficient: + return 80; + default: + return 90; + } } default: return 100; diff --git a/zone/mob.h b/zone/mob.h index f2e8e9abdc..8e0a4728ce 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -437,10 +437,10 @@ class Mob : public Entity { std::string GetSpellTypeNameByID(uint16 spellType); std::string GetSpellTypeShortNameByID(uint16 spellType); - bool GetDefaultSpellHold(uint16 spellType); - uint16 GetDefaultSpellDelay(uint16 spellType); - uint8 GetDefaultSpellMinThreshold(uint16 spellType); - uint8 GetDefaultSpellMaxThreshold(uint16 spellType); + bool GetDefaultSpellHold(uint16 spellType, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellDelay(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance = Stance::Balanced); inline bool GetSpellHold(uint16 spellType) const { return _spellSettings[spellType].hold; } void SetSpellHold(uint16 spellType, bool holdStatus); From 1d6d6a0f36d18927d4419adb9391cbefd1805fb3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:30:24 -0600 Subject: [PATCH 107/394] Make rogue/monk evade logic more accurate to players --- zone/bot.cpp | 68 ++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 7 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 6bf9e5e250..1165f4aa66 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2610,8 +2610,16 @@ bool Bot::TryFacingTarget(Mob* tar) { bool Bot::TryEvade(Mob* tar) { - if (HasTargetReflection() && !tar->IsFeared() && !tar->IsStunned()) { - if (GetClass() == Class::Rogue && !GetSpellHold(BotSpellTypes::Escape)) { + if (GetSpellHold(BotSpellTypes::Escape)) { + return false; + } + + switch (GetClass()) { + case Class::Rogue: { + if (GetSkill(EQ::skills::SkillHide) == 0) { + return false; + } + if (m_rogue_evade_timer.Check(false)) { int timer_duration = (HideReuseTime - GetSkillReuseTime(EQ::skills::SkillHide)) * 1000; @@ -2620,18 +2628,47 @@ bool Bot::TryEvade(Mob* tar) { } m_rogue_evade_timer.Start(timer_duration); - BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) { + + } + + float hidechance = ((GetSkill(EQ::skills::SkillHide) / 250.0f) + .25) * 100; + float random = zone->random.Real(0, 100); + + if (random < hidechance) { //SendAppearancePacket(AT_Invis, Invisibility::Invisible); + + if (spellbonuses.ShroudofStealth || aabonuses.ShroudofStealth || itembonuses.ShroudofStealth) { + improved_hidden = true; + hidden = true; + } + else { + hidden = true; + } + } + + if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) { + BotGroupSay(this, "I have momentarily ducked away from the main combat."); RogueEvade(tar); } + else { + BotGroupSay(this, "My attempts at ducking clear of combat fail."); + } //SendAppearancePacket(AT_Invis, Invisibility::Visible); + hidden = false; + return true; } + + break; } - else if (GetClass() == Class::Monk && GetLevel() >= 17 && !GetSpellHold(BotSpellTypes::Escape)) { + case Class::Monk: { + if (GetSkill(EQ::skills::SkillFeignDeath) == 0) { + return false; + } + if (m_monk_evade_timer.Check(false)) { int timer_duration = (FeignDeathReuseTime - GetSkillReuseTime(EQ::skills::SkillFeignDeath)) * 1000; @@ -2640,13 +2677,26 @@ bool Bot::TryEvade(Mob* tar) { } m_monk_evade_timer.Start(timer_duration); - BotGroupSay(this, "Attempting to evade %s", tar->GetCleanName()); - if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillFeignDeath)) { + uint16 primfeign = GetSkill(EQ::skills::SkillFeignDeath); + uint16 secfeign = GetSkill(EQ::skills::SkillFeignDeath); + if (primfeign > 100) { + primfeign = 100; + secfeign = secfeign - 100; + secfeign = secfeign / 2; + } + else + secfeign = 0; + + uint16 totalfeign = primfeign + secfeign; + + if (zone->random.Real(0, 160) > totalfeign) { //SendAppearancePacket(AT_Anim, ANIM_DEATH); - entity_list.MessageCloseString(this, false, 200, 10, STRING_FEIGNFAILED, GetName()); + BotGroupSay(this, "I have fallen to the ground."); + SetFeigned(false); } else { + BotGroupSay(this, "I have successfully feigned my death."); SetFeigned(true); //SendAppearancePacket(AT_Anim, ANIM_DEATH); } @@ -7100,6 +7150,10 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl continue; } + if (caster == tar) { + continue; + } + uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(spellType) : 100; if (!caster->PrecastChecks(tar, spellType)) { From 95bf30740ffa5aee52590b204a43b0bd9e9907c9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 6 Nov 2024 07:31:48 -0600 Subject: [PATCH 108/394] more target validation for bots to prevent pets from getting hit with AEs and pets trying to attack invalid targets --- zone/aggro.cpp | 21 +++++++++++++++++++-- zone/bot.cpp | 7 +++++++ 2 files changed, 26 insertions(+), 2 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 1a8c7b9290..4f1d658319 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -745,10 +745,27 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); - if(target_owner && target_owner == this) + Mob* target_ultimate_owner = target->GetUltimateOwner(); + Mob* our_ultimate_owner = GetUltimateOwner(); + + if (target_owner && target_owner == this) { + return false; + } + else if ( + IsBot() && target_ultimate_owner && + ( + (target_ultimate_owner == our_ultimate_owner) || + (target_ultimate_owner->IsOfClientBot()) + ) + ) { return false; - else if(our_owner && our_owner == target) + } + else if (our_owner && our_owner == target) { return false; + } + else if (IsBot() && our_ultimate_owner && our_ultimate_owner == target) { + return false; + } // invalidate for swarm pets for later on if their owner is a corpse if (IsNPC() && CastToNPC()->GetSwarmInfo() && our_owner && diff --git a/zone/bot.cpp b/zone/bot.cpp index 1165f4aa66..abbf8f0a8d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2116,6 +2116,13 @@ void Bot::AI_Process() // TARGET VALIDATION if (!IsValidTarget(bot_owner, leash_owner, lo_distance, leash_distance, tar, tar_distance)) { + if (HasPet()) { + if (tar && GetPet()->GetTarget() && GetPet()->GetTarget() == tar) { + GetPet()->WipeHateList(); + GetPet()->SetTarget(nullptr); + } + } + return; } From 1048cb78aad3f53f93c9121ed3c9c244fdace11d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:33:59 -0600 Subject: [PATCH 109/394] misc command and rule cleanup --- common/ruletypes.h | 8 ++++---- zone/bot_commands/follow.cpp | 2 +- zone/bot_commands/illusion_block.cpp | 7 ------- 3 files changed, 5 insertions(+), 12 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 435c04e8e6..0ffffa345b 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -811,9 +811,9 @@ RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to RULE_INT(Bots, PercentChanceToCastHateRedux, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastFear, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastOtherType, 90, "The chance for a bot to attempt to cast the remaining spell types in combat. Default 0-%.") -RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 250, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 250, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 250ms.") RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2000ms.") -RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 125, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 200ms.") +RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 125, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 125ms.") RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 500ms.") RULE_INT(Bots, MezChance, 35, "35 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.") RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.") @@ -858,13 +858,13 @@ RULE_BOOL(Bots, PreventBotSpawnOnFD, true, "True Default. If true, players will RULE_BOOL(Bots, PreventBotSpawnOnEngaged, true, "True Default. If true, players will not be able to spawn bots while you, your group or raid are engaged.") RULE_BOOL(Bots, PreventBotCampOnEngaged, true, "True Default. If true, players will not be able to camp bots while you, your group or raid are engaged.") RULE_BOOL(Bots, CopySettingsOwnBotsOnly, true, "Determines whether a bot you are copying settings from must be a bot you own or not, default true.") -RULE_BOOL(Bots, AllowCopySettingsAnon, true, "If player's are allowed to copy settings of bots owned by anonymous players.") +RULE_BOOL(Bots, AllowCopySettingsAnon, false, "If player's are allowed to copy settings of bots owned by anonymous players.") RULE_BOOL(Bots, AllowCharmedPetBuffs, true, "Whether or not bots are allowed to cast buff charmed pets, default true.") RULE_BOOL(Bots, AllowCharmedPetHeals, true, "Whether or not bots are allowed to cast heal charmed pets, default true.") RULE_BOOL(Bots, AllowCharmedPetCures, true, "Whether or not bots are allowed to cast cure charmed pets, default true.") RULE_BOOL(Bots, ShowResistMessagesToOwner, true, "Default True. If enabled, when a bot's spell is resisted it will send a spell failure to their owner.") RULE_BOOL(Bots, BotBuffLevelRestrictions, true, "Buffs will not land on low level bots like live players") -RULE_BOOL(Bots, BotsUseLiveBlockedMessage, false, "Setting whether detailed spell block messages should be used for bots as players do on the live servers") +RULE_BOOL(Bots, BotsUseLiveBlockedMessage, true, "Setting whether detailed spell block messages should be used for bots as players do on the live servers") RULE_BOOL(Bots, BotSoftDeletes, true, "When bots are deleted, they are only soft deleted") RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass the anti-spam system") RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Default 120.") diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index c5b7f8f780..f970773d7b 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -200,7 +200,7 @@ void bot_command_follow(Client* c, const Seperator* sep) nextTar = target_mob; } } - LogTestDebug("{} is now following {}.", bot_iter->GetCleanName(), nextTar->GetCleanName()); //deleteme + chainList.push_back(bot_iter); ++it; ++count; diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 0a3625046a..3699fa0806 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -63,13 +63,6 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); return; } From e73f1259bea5c4281e6a48d7b6d1d9d608b46563 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 7 Nov 2024 16:34:20 -0600 Subject: [PATCH 110/394] bot movement cleanup and tweaks, move casterrange to distanceranged --- .../database_update_manifest_bots.cpp | 8 +- common/ruletypes.h | 5 +- zone/bot.cpp | 127 ++++++------------ zone/bot.h | 15 +-- zone/bot_command.cpp | 4 +- zone/bot_command.h | 2 +- zone/bot_commands/bot_settings.cpp | 2 +- zone/bot_commands/copy_settings.cpp | 2 +- zone/bot_commands/default_settings.cpp | 2 +- .../{caster_range.cpp => distance_ranged.cpp} | 39 +++--- 10 files changed, 76 insertions(+), 130 deletions(-) rename zone/bot_commands/{caster_range.cpp => distance_ranged.cpp} (68%) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 639d173b06..a57a6f0753 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -209,18 +209,18 @@ INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM b INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'CasterRange' +SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'DistanceRanged' FROM ( SELECT `bot_id`, (CASE WHEN (`class` IN (1, 7, 19, 16)) THEN 0 WHEN `class` = 8 THEN 0 ELSE 90 - END) AS `casterRange`, + END) AS `DistanceRanged`, `caster_range` FROM bot_data ) AS `subquery` -WHERE `casterRange` != `caster_range`; +WHERE `DistanceRanged` != `caster_range`; ALTER TABLE `bot_data` DROP COLUMN `show_helm`; @@ -241,7 +241,7 @@ UPDATE `bot_command_settings` SET `aliases`= 'bh' WHERE `bot_command`='behindmob UPDATE `bot_command_settings` SET `aliases`= 'bs|settings' WHERE `bot_command`='botsettings'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|followdistance') ELSE 'followd||followdistance' END WHERE `bot_command`='botfollowdistance' AND `aliases` NOT LIKE '%followdistance%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ranged|toggleranged|btr') ELSE 'ranged|toggleranged|btr' END WHERE `bot_command`='bottoggleranged' AND `aliases` NOT LIKE '%ranged|toggleranged|btr%'; -UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|cr') ELSE 'cr' END WHERE `bot_command`='casterrange' AND `aliases` NOT LIKE '%cr%'; +UPDATE `bot_command_settings` SET `aliases`= 'distranged|dr' WHERE `bot_command`='distanceranged'; UPDATE `bot_command_settings` SET `aliases`= 'copy' WHERE `bot_command`='copysettings'; UPDATE `bot_command_settings` SET `aliases`= 'default' WHERE `bot_command`='defaultsettings'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|enforce') ELSE 'enforce' END WHERE `bot_command`='enforcespellsettings' AND `aliases` NOT LIKE '%enforce%'; diff --git a/common/ruletypes.h b/common/ruletypes.h index 0ffffa345b..d9991c0f9a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -839,15 +839,13 @@ RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max stack size. Minimum stack size to give a bot (Arrows/Throwing).") RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") -RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") +RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") RULE_REAL(Bots, PercentMinMeleeDistance, 0.60, "Multiplier of the max melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.") RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .") RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.25, "Multiplier of max melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.") RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.") -RULE_BOOL(Bots, CastersStayJustOutOfMeleeRange, true, "True Default. If true, caster bots will stay just out of melee range. Otherwise they use Bots:PercentMinCasterRangeDistance.") -RULE_REAL(Bots, PercentMinCasterRangeDistance, 0.60, "Multiplier of the closest caster range at which a bot will stand while casting before trying to adjust. 0.60 Recommended.") RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.") RULE_REAL(Bots, DistanceTauntingBotsStickMainHate, 25.00, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, false, "False Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") @@ -875,6 +873,7 @@ RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TG RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") +RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index abbf8f0a8d..afbed603ce 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2151,8 +2151,9 @@ void Bot::AI_Process() float melee_distance_min = 0.0f; float melee_distance_max = 0.0f; float melee_distance = 0.0f; + tar_distance = sqrt(tar_distance); - CheckCombatRange(tar, sqrt(tar_distance), atCombatRange, behindMob, p_item, s_item, melee_distance_min, melee_distance_max, melee_distance, stopMeleeLevel); + CheckCombatRange(tar, tar_distance, atCombatRange, behindMob, p_item, s_item, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); // PULLING FLAG (ACTIONABLE RANGE) @@ -2230,7 +2231,7 @@ void Bot::AI_Process() return; } - if (IsBotRanged() && ranged_timer.Check(false)) { // Can shoot mezzed, stunned and dead!? + if (IsBotRanged() && ranged_timer.Check(false)) { TryRangedAttack(tar); if (!TargetValidation(tar)) { return; } @@ -2240,10 +2241,6 @@ void Bot::AI_Process() } } else if (!IsBotRanged() && GetLevel() < stopMeleeLevel) { - if (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())) { - return; - } - if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) { DoClassAttacks(tar); } @@ -2718,7 +2715,7 @@ bool Bot::TryEvade(Mob* tar) { return false; } -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance_max, float& melee_distance, uint8 stopMeleeLevel) { +void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { atCombatRange= false; p_item = GetBotItem(EQ::invslot::slotPrimary); @@ -2734,16 +2731,16 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo } // Calculate melee distances - CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_max, melee_distance, melee_distance_min, stopMeleeLevel); + CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); - //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be {} [{} - {}] away." + //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be between [{} - {}] away. MMR is {}." // , GetCleanName() - // , (tar_distance <= melee_distance ? "within range of" : "too far away from") + // , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) // , tar->GetCleanName() // , tar_distance // , (tar_distance <= melee_distance ? "but only needed" : "but need to be") - // , melee_distance // , melee_distance_min + // , melee_distance // , melee_distance_max //); //deleteme @@ -2752,16 +2749,14 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo } } -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_max, float& melee_distance, float& melee_distance_min, uint8 stopMeleeLevel) { +void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { float size_mod = GetSize(); float other_size_mod = tar->GetSize(); - bool resquareDistance = false; // For races with a fixed size if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) { // size_mod = 60.0f; } - else if (size_mod < 6.0f) { size_mod = 8.0f; } @@ -2770,7 +2765,6 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it if (tar->GetRace() == Race::LavaDragon || tar->GetRace() == Race::Wurm || tar->GetRace() == Race::GhostDragon) { other_size_mod = 60.0f; } - else if (other_size_mod < 6.0f) { other_size_mod = 8.0f; } @@ -2782,11 +2776,9 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it if (size_mod > 29.0f) { size_mod *= size_mod; } - else if (size_mod > 19.0f) { size_mod *= (size_mod * 2.0f); } - else { size_mod *= (size_mod * 4.0f); } @@ -2795,6 +2787,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it { size_mod *= 1.75; } + if (tar->GetRace() == Race::DragonSkeleton) // Dracoliche in Fear. Skeletal Dragon { size_mod *= 2.25; @@ -2808,6 +2801,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it } melee_distance_max = size_mod; + if (!RuleB(Bots, UseFlatNormalMeleeRange)) { switch (GetClass()) { case Class::Warrior: @@ -2875,31 +2869,26 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it melee_distance = melee_distance_max * RuleR(Bots, TauntNormalMeleeRangeDistance); } - if (!taunting && !IsBotRanged() && GetMaxMeleeRange()) { + bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; + + if (!taunting && !IsBotRanged() && !isStopMeleeLevel && GetMaxMeleeRange()) { melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); } - - /* Caster Range Checks */ - bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; - if (isStopMeleeLevel) { - melee_distance = GetBotCasterMaxRange(melee_distance_max); - if (RuleB(Bots, CastersStayJustOutOfMeleeRange)) { - melee_distance_min = melee_distance_max + 1; - } - else { - melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinCasterRangeDistance); - } + if (isStopMeleeLevel && !IsBotRanged()) { + float desiredRange = GetBotDistanceRanged(); + melee_distance_min = std::min(melee_distance, (desiredRange / 2)); + melee_distance = std::max((melee_distance + 1), desiredRange); } /* Archer Checks*/ if (IsBotRanged()) { - float archeryRange = GetBotRangedValue(); - float casterRange = GetBotCasterRange(); - float minArcheryRange = RuleI(Combat, MinRangedAttackDist); - melee_distance = std::min(archeryRange, (casterRange * 2)); - melee_distance_min = std::max(std::max(minArcheryRange, (melee_distance_max + 1)), std::min(casterRange, archeryRange)); + float minDistance = RuleI(Combat, MinRangedAttackDist); + float maxDistance = GetBotRangedValue(); + float desiredRange = GetBotDistanceRanged(); + melee_distance_min = std::max(minDistance, (desiredRange / 2)); + melee_distance = std::min(maxDistance, desiredRange); } } @@ -8480,29 +8469,6 @@ void Bot::SendSpellAnim(uint16 target_id, uint16 spell_id) entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles)); } -float Bot::GetBotCasterMaxRange(float melee_distance_max) {// Calculate caster distances - float caster_distance_max = 0.0f; - float caster_distance_min = 0.0f; - float caster_distance = 0.0f; - - caster_distance_max = GetBotCasterRange(); - - if (!GetBotCasterRange() && GetLevel() >= GetStopMeleeLevel() && GetClass() >= Class::Warrior && GetClass() <= Class::Berserker) { - caster_distance_max = GetDefaultBotBaseSetting(BotBaseSettings::CasterRange); - } - - if (caster_distance_max) { - caster_distance_min = melee_distance_max; - - if (caster_distance_max <= caster_distance_min) { - caster_distance_max = caster_distance_min * 1.25f; - } - } - - return caster_distance_max; -} - - int32 Bot::CalcItemATKCap() { return RuleI(Character, ItemATKCap) + itembonuses.ItemATKCap + spellbonuses.ItemATKCap + aabonuses.ItemATKCap; @@ -9972,8 +9938,8 @@ void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { case BotBaseSettings::BehindMob: SetBehindMob(settingValue); break; - case BotBaseSettings::CasterRange: - SetBotCasterRange(settingValue); + case BotBaseSettings::DistanceRanged: + SetBotDistanceRanged(settingValue); break; case BotBaseSettings::IllusionBlock: SetIllusionBlock(settingValue); @@ -10021,9 +9987,9 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { case BotBaseSettings::BehindMob: //LogBotSettingsDetail("Returning current GetBehindMob of [{}] for [{}]", GetBehindMob(), GetCleanName()); //deleteme return GetBehindMob(); - case BotBaseSettings::CasterRange: - //LogBotSettingsDetail("Returning current GetBotCasterRange of [{}] for [{}]", GetBotCasterRange(), GetCleanName()); //deleteme - return GetBotCasterRange(); + case BotBaseSettings::DistanceRanged: + //LogBotSettingsDetail("Returning current GetBotDistanceRanged of [{}] for [{}]", GetBotDistanceRanged(), GetCleanName()); //deleteme + return GetBotDistanceRanged(); case BotBaseSettings::IllusionBlock: //LogBotSettingsDetail("Returning current GetIllusionBlock of [{}] for [{}]", GetIllusionBlock(), GetCleanName()); //deleteme return GetIllusionBlock(); @@ -10070,7 +10036,7 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance) { else { return false; } - case BotBaseSettings::CasterRange: + case BotBaseSettings::DistanceRanged: switch (GetClass()) { case Class::Warrior: case Class::Monk: @@ -10707,8 +10673,8 @@ std::string Bot::GetBotSettingCategoryName(uint8 setting_type) { return "PetSetTypeSetting"; case BotBaseSettings::BehindMob: return "BehindMob"; - case BotBaseSettings::CasterRange: - return "CasterRange"; + case BotBaseSettings::DistanceRanged: + return "DistanceRanged"; case BotBaseSettings::IllusionBlock: return "IllusionBlock"; case BotBaseSettings::MaxMeleeRange: @@ -11026,7 +10992,7 @@ void Bot::SetCombatJitter() { void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob) { if (HasTargetReflection()) { - if (!tar->IsFeared() && !tar->IsStunned()) { + if (!taunting && !tar->IsFeared() && !tar->IsStunned()) { if (TryEvade(tar)) { return; } @@ -11034,14 +11000,14 @@ void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, flo if (tar->IsRooted() && !taunting) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay if (tar_distance <= melee_distance_max) { - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, false, true)) { + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, taunting)) { RunToGoalWithJitter(Goal); return; } } } - if (taunting && tar_distance < melee_distance_min) { // Back up any taunting bots that are too close + if (taunting && tar_distance < melee_distance_min) { // Back up any bots that are too close if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, taunting)) { RunToGoalWithJitter(Goal); return; @@ -11077,25 +11043,12 @@ void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, flo RunToGoalWithJitter(Goal); return; } - //else { - // if (stopMeleeLevel || IsBotArcher()) { - // if (IsBotArcher()) { - // float minArcheryRange = RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist); - // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, minArcheryRange, melee_distance, false, taunting)) { - // RunToGoalWithJitter(Goal); - // return; - // } - // } - // else { - // if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_max + 1, melee_distance, false, taunting)) { - // RunToGoalWithJitter(Goal); - // return; - // } - // } - // } - // DoFaceCheckWithJitter(tar); - // return; - //} + } + else if (tar->IsEnraged() && !taunting && !stopMeleeLevel && !behindMob) { // Move non-taunting melee bots behind target during enrage + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) { + RunToGoalWithJitter(Goal); + return; + } } } } diff --git a/zone/bot.h b/zone/bot.h index 553148ee51..a2e0a8ffbc 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -136,7 +136,7 @@ namespace BotBaseSettings { constexpr uint16 RangedSetting = 5; constexpr uint16 PetSetTypeSetting = 6; constexpr uint16 BehindMob = 7; - constexpr uint16 CasterRange = 8; + constexpr uint16 DistanceRanged = 8; constexpr uint16 IllusionBlock = 9; constexpr uint16 MaxMeleeRange = 10; constexpr uint16 MedInCombat = 11; @@ -506,8 +506,8 @@ class Bot : public NPC { void SetMaxMeleeRange(bool value) { _maxMeleeRangeStatus = value; } uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; } - uint32 GetBotCasterRange() const { return _casterRange; } - void SetBotCasterRange(uint32 casterRange) { _casterRange = casterRange; } + uint32 GetBotDistanceRanged() const { return _distanceRanged; } + void SetBotDistanceRanged(uint32 distanceRanged) { _distanceRanged = distanceRanged; } bool GetMedInCombat() const { return _medInCombat; } void SetMedInCombat(bool value) { _medInCombat = value; } uint8 GetHPWhenToMed() const { return _HPWhenToMed; } @@ -640,7 +640,6 @@ class Bot : public NPC { bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; } uint8 GetBotStance() { return _botStance; } uint8 GetChanceToCastBySpellType(uint16 spellType); - float GetBotCasterMaxRange(float melee_distance_max); bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; } bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; } bool IsGroupNuker() const { return m_CastingRoles.GroupNuker; } @@ -929,9 +928,9 @@ class Bot : public NPC { const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, - float& melee_distance_max, - float& melee_distance, float& melee_distance_min, + float& melee_distance, + float& melee_distance_max, uint8 stopMeleeLevel ); @@ -947,8 +946,8 @@ class Bot : public NPC { const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, - float& melee_distance_max, float& melee_distance, + float& melee_distance_max, uint8 stopMeleeLevel ); bool GetCombatJitterFlag() { return m_combat_jitter_flag; } @@ -1065,7 +1064,7 @@ class Bot : public NPC { bool _showHelm; bool _botRangedSetting; uint8 _stopMeleeLevel; - uint32 _casterRange; + uint32 _distanceRanged; bool _behindMobStatus; bool _maxMeleeRangeStatus; bool _medInCombat; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 3f6dfef5b1..80be595f89 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1285,7 +1285,7 @@ int bot_command_init(void) bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_command_update) || bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_command_woad) || bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || - bot_command_add("casterrange", "Controls the range casters will try to stay away from a mob (if too far, they will skip spells that are out-of-range)", AccountStatus::Player, bot_command_caster_range) || + bot_command_add("distanceranged", "Controls the range casters and ranged will try to stay away from a mob", AccountStatus::Player, bot_command_distance_ranged) || bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_circle) || bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || @@ -2256,7 +2256,6 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/bot.cpp" #include "bot_commands/bot_settings.cpp" #include "bot_commands/cast.cpp" -#include "bot_commands/caster_range.cpp" #include "bot_commands/charm.cpp" #include "bot_commands/class_race_list.cpp" #include "bot_commands/click_item.cpp" @@ -2265,6 +2264,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/default_settings.cpp" #include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" +#include "bot_commands/distance_ranged.cpp" #include "bot_commands/escape.cpp" #include "bot_commands/find_aliases.cpp" #include "bot_commands/follow.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 53cb2776bc..a8951a7cd4 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1673,7 +1673,7 @@ void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep); void bot_command_bot_settings(Client* c, const Seperator* sep); void bot_command_cast(Client* c, const Seperator* sep); -void bot_command_caster_range(Client* c, const Seperator* sep); +void bot_command_distance_ranged(Client* c, const Seperator* sep); void bot_command_charm(Client *c, const Seperator *sep); void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp index e94bea5f1a..4dc9babdf2 100644 --- a/zone/bot_commands/bot_settings.cpp +++ b/zone/bot_commands/bot_settings.cpp @@ -4,7 +4,7 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) { std::list subcommand_list; subcommand_list.push_back("behindmob"); - subcommand_list.push_back("casterrange"); + subcommand_list.push_back("distanceranged"); subcommand_list.push_back("copysettings"); subcommand_list.push_back("defaultsettings"); subcommand_list.push_back("enforcespelllist"); diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index fafead14c1..e2745442f7 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -74,7 +74,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) "[misc] copies all miscellaneous options such as:", "- ^showhelm, ^followd, ^stopmeleelevel", "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", - "- ^behindmob, ^casterrange, ^illusionblock", + "- ^behindmob, ^distanceranged, ^illusionblock", "- ^sitincombat, ^sithppercent and ^sitmanapercent", }; diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 87bb31b0f0..ddf809a751 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -68,7 +68,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) "[misc] restores all miscellaneous options such as:", "- ^showhelm, ^followd, ^stopmeleelevel", "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", - "- ^behindmob, ^casterrange, ^illusionblock", + "- ^behindmob, ^distanceranged, ^illusionblock", "- ^sitincombat, ^sithppercent and ^sitmanapercent", }; diff --git a/zone/bot_commands/caster_range.cpp b/zone/bot_commands/distance_ranged.cpp similarity index 68% rename from zone/bot_commands/caster_range.cpp rename to zone/bot_commands/distance_ranged.cpp index 87676c7d46..70d5e655b4 100644 --- a/zone/bot_commands/caster_range.cpp +++ b/zone/bot_commands/distance_ranged.cpp @@ -1,14 +1,13 @@ #include "../bot_command.h" -void bot_command_caster_range(Client* c, const Seperator* sep) +void bot_command_distance_ranged(Client* c, const Seperator* sep) { - if (helper_command_alias_fail(c, "bot_command_caster_range", sep->arg[0], "casterrange")) { + if (helper_command_alias_fail(c, "bot_command_distance_ranged", sep->arg[0], "distanceranged")) { return; } if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "note: Can only be used for Casters or Hybrids."); c->Message(Chat::White, "note: Use [current] to check the current setting."); c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target."); c->Message(Chat::White, "note: If they are too far for a spell, it will be skipped."); @@ -21,13 +20,13 @@ void bot_command_caster_range(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; int ab_arg = 1; bool current_check = false; - uint32 crange = 0; + uint32 value = 0; if (sep->IsNumber(1)) { ++ab_arg; - crange = atoi(sep->arg[1]); - if (crange < 0 || crange > 300) { - c->Message(Chat::White, "You must enter a value within the range of 0 - 300."); + value = atoi(sep->arg[1]); + if (value < 0 || value > RuleI(Bots, MaxDistanceRanged)) { + c->Message(Chat::Yellow, "You must enter a value within the range of 0 - 300."); return; } } @@ -36,7 +35,7 @@ void bot_command_caster_range(Client* c, const Seperator* sep) current_check = true; } else { - c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + c->Message(Chat::Yellow, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); return; } @@ -58,47 +57,43 @@ void bot_command_caster_range(Client* c, const Seperator* sep) int success_count = 0; for (auto my_bot : sbl) { - if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { - continue; - } - if (!first_found) { first_found = my_bot; } if (current_check) { c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} says, 'My current caster range is {}.'", + "{} says, 'My current Distance Ranged is {}.'", my_bot->GetCleanName(), - my_bot->GetBotCasterRange() + my_bot->GetBotDistanceRanged() ).c_str() ); } else { - my_bot->SetBotCasterRange(crange); + my_bot->SetBotDistanceRanged(value); ++success_count; } } if (!current_check) { if (success_count == 1 && first_found) { c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} says, 'My Caster Range was set to {}.'", + "{} says, 'My Distance Ranged was set to {}.'", first_found->GetCleanName(), - crange + value ).c_str() ); } else { c->Message( - Chat::White, + Chat::Green, fmt::format( - "{} of your bots set their Caster Range to {}.", + "{} of your bots set their Distance Ranged to {}.", success_count, - crange + value ).c_str() ); } From 686fdb9bdaee0bf612833223ac7fe038033570ec Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 8 Nov 2024 08:41:18 -0600 Subject: [PATCH 111/394] command cleanup --- zone/bot_commands/illusion_block.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 3699fa0806..e44ddf6011 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -63,6 +63,14 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + return; } From 77da29f94097f8ae85faa5c9195092e56c514da5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 8 Nov 2024 10:59:01 -0600 Subject: [PATCH 112/394] Add viral, fear, stun, knockback, gravityeffect support to bots --- zone/bot.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 7 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index afbed603ce..0e3e2811f7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -588,7 +588,7 @@ void Bot::ChangeBotRangedWeapons(bool isRanged) { BotAddEquipItem(EQ::invslot::slotAmmo, GetBotItemBySlot(EQ::invslot::slotAmmo)); BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotRange)); SetAttackTimer(); - BotGroupSay(this, "My bow is true and ready"); //TODO bot rewrite - make this say throwing or bow + BotGroupSay(this, "My blades are sheathed"); } } @@ -1593,6 +1593,7 @@ bool Bot::Process() { if (IsStunned() && stunned_timer.Check()) { Mob::UnStun(); + spun_timer.Disable(); } if (!GetBotOwner()) { @@ -1600,7 +1601,6 @@ bool Bot::Process() } if (GetDepop()) { - _botOwner = nullptr; _botOwnerCharacterID = 0; @@ -1623,7 +1623,7 @@ bool Bot::Process() BuffProcess(); CalcRestState(); - if (currently_fleeing) { + if (currently_fleeing || IsFeared()) { ProcessFlee(); } @@ -1658,6 +1658,16 @@ bool Bot::Process() } } + if (viral_timer.Check()) { // TODO bot rewrite -- necessary for bots? + VirusEffectProcess(); + } + + if (spellbonuses.GravityEffect == 1) { + if (gravity_timer.Check()) { + DoGravityEffect(); + } + } + if (GetAppearance() == eaDead && GetHP() > 0) { SetAppearance(eaStanding); } @@ -1666,7 +1676,6 @@ bool Bot::Process() ping_timer.Disable(); } else { - if (!ping_timer.Enabled()) { ping_timer.Start(BOT_KEEP_ALIVE_INTERVAL); } @@ -1687,7 +1696,19 @@ bool Bot::Process() auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); } - if (IsStunned() || IsMezzed()) { + if (ForcedMovement) { + ProcessForcedMovement(); + } + + if (IsMezzed()) { + return true; + } + + if (IsStunned()) { + if (spun_timer.Check()) { + Spin(); + } + return true; } @@ -2158,7 +2179,6 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { - //TODO bot rewrite - add ways to here to determine if throw stone is allowed, then check for ranged/spell/melee if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { return; } @@ -3133,6 +3153,25 @@ bool Bot::CheckIfIncapacitated() { return true; } + if (currently_fleeing) { + if (RuleB(Combat, EnableFearPathing) && AI_movement_timer->Check()) { + // Check if we have reached the last fear point + if (DistanceNoZ(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget) <= 5.0f) { + // Calculate a new point to run to + StopNavigation(); + CalculateNewFearpoint(); + } + + RunTo( + m_FearWalkTarget.x, + m_FearWalkTarget.y, + m_FearWalkTarget.z + ); + } + + return true; + } + return false; } @@ -10881,7 +10920,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { return false; } - switch (spellType) { //TODO bot rewrite - fix Buff/ResistBuff + switch (spellType) { case BotSpellTypes::Buff: if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return false; From aa29f6e0018bd585160752e05842bf43301d1bb7 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 9 Nov 2024 21:59:52 -0600 Subject: [PATCH 113/394] only load client spell types for clients --- zone/client.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/client.cpp b/zone/client.cpp index dd22e2a230..176bdaab00 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13087,6 +13087,10 @@ void Client::LoadDefaultBotSettings() { LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!IsClientBotSpellType(i)) { + continue; + } + BotSpellSettings_Struct t; t.spellType = i; From 7d670e674ddebab1602392a973757388c807a6be Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:00:20 -0600 Subject: [PATCH 114/394] add passive stance checks to commands and loading/saving. shouldn't be ` --- zone/bot.cpp | 19 ++++++++++++++++++- zone/bot.h | 3 ++- zone/bot_commands/behind_mob.cpp | 5 +++++ zone/bot_commands/bot.cpp | 10 +--------- zone/bot_commands/cast.cpp | 2 +- zone/bot_commands/click_item.cpp | 6 ++++++ zone/bot_commands/distance_ranged.cpp | 4 ++++ zone/bot_commands/illusion_block.cpp | 5 +++++ zone/bot_commands/max_melee_range.cpp | 5 +++++ zone/bot_commands/sit_hp_percent.cpp | 5 +++++ zone/bot_commands/sit_in_combat.cpp | 5 +++++ zone/bot_commands/sit_mana_percent.cpp | 5 +++++ zone/bot_commands/spell_aggro_checks.cpp | 5 +++++ zone/bot_commands/spell_delays.cpp | 5 +++++ zone/bot_commands/spell_engaged_priority.cpp | 5 +++++ zone/bot_commands/spell_idle_priority.cpp | 5 +++++ zone/bot_commands/spell_max_hp_pct.cpp | 5 +++++ zone/bot_commands/spell_max_mana_pct.cpp | 5 +++++ zone/bot_commands/spell_max_thresholds.cpp | 5 +++++ zone/bot_commands/spell_min_hp_pct.cpp | 5 +++++ zone/bot_commands/spell_min_mana_pct.cpp | 5 +++++ zone/bot_commands/spell_min_thresholds.cpp | 5 +++++ zone/bot_commands/spell_pursue_priority.cpp | 5 +++++ zone/bot_commands/spell_target_count.cpp | 5 +++++ zone/bot_database.cpp | 10 ++++++++++ 25 files changed, 132 insertions(+), 12 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0e3e2811f7..8b997f4bf5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9833,7 +9833,6 @@ bool Bot::IsMobEngagedByAnyone(Mob* tar) { if (m->GetTarget() == tar) { if ( m->IsBot() && - !m->CastToBot()->GetHoldFlag() && m->IsEngaged() && ( !m->CastToBot()->IsBotNonSpellFighter() || @@ -11421,3 +11420,21 @@ void Bot::ResetBotSpellSettings() AI_AddBotSpells(GetBotSpellID()); SetBotEnforceSpellSetting(false); } + +bool Bot::BotPassiveCheck() { + if (GetBotStance() == Stance::Passive) { + GetOwner()->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I am currently set to stance {} [#{}]. My settings cannot be modified.'", + GetCleanName(), + Stance::GetName(Stance::Passive), + Stance::Passive + ).c_str() + ); + + return true; + } + + return false; +} diff --git a/zone/bot.h b/zone/bot.h index a2e0a8ffbc..e0d857880e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -497,7 +497,8 @@ class Bot : public NPC { void SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit); inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spellType) const { return _spellSettings[spellType].AEOrGroupTargetCount; } void SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount); - + bool BotPassiveCheck(); + bool GetShowHelm() const { return _showHelm; } void SetShowHelm(bool showHelm) { _showHelm = showHelm; } bool GetBehindMob() const { return _behindMobStatus; } diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 8e69158607..a7710185f1 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -130,9 +130,14 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index d867b4e67a..ba0ae10dcb 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1440,7 +1440,6 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s [current | reset | sync | value: 0-255] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "note: Only caster or hybrid class bots may be modified"); c->Message(Chat::White, "note: Use [reset] to set stop melee level to server rule"); c->Message(Chat::White, "note: Use [sync] to set stop melee level to current bot level"); return; @@ -1508,14 +1507,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) int success_count = 0; for (auto my_bot : sbl) { - if (!IsCasterClass(my_bot->GetClass()) && !IsHybridClass(my_bot->GetClass())) { - c->Message( - Chat::White, - fmt::format( - "{} says, 'This command only works on caster or hybrid classes.'", - my_bot->GetCleanName() - ).c_str() - ); + if (my_bot->BotPassiveCheck()) { continue; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 70b6e659cc..fd7dc5aa83 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -234,7 +234,7 @@ void bot_command_cast(Client* c, const Seperator* sep) NEED TO CHECK: precombat, AE Dispel, AE Lifetap DO I NEED A PBAE CHECK??? */ - if (bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { + if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { continue; } diff --git a/zone/bot_commands/click_item.cpp b/zone/bot_commands/click_item.cpp index 80375b52c0..469750f59f 100644 --- a/zone/bot_commands/click_item.cpp +++ b/zone/bot_commands/click_item.cpp @@ -40,12 +40,18 @@ void bot_command_click_item(Client* c, const Seperator* sep) } std::list sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } + sbl.remove(nullptr); for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (RuleI(Bots, BotsClickItemsMinLvl) > my_bot->GetLevel()) { c->Message(Chat::White, "%s must be level %i to use clickable items.", my_bot->GetCleanName(), RuleI(Bots, BotsClickItemsMinLvl)); continue; diff --git a/zone/bot_commands/distance_ranged.cpp b/zone/bot_commands/distance_ranged.cpp index 70d5e655b4..7c7dec82a6 100644 --- a/zone/bot_commands/distance_ranged.cpp +++ b/zone/bot_commands/distance_ranged.cpp @@ -57,6 +57,10 @@ void bot_command_distance_ranged(Client* c, const Seperator* sep) int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index e44ddf6011..94faafc426 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -130,9 +130,14 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp index e876a97591..c9e32dec1b 100644 --- a/zone/bot_commands/max_melee_range.cpp +++ b/zone/bot_commands/max_melee_range.cpp @@ -129,9 +129,14 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index ac94a13fd8..fa4a183bf5 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -129,9 +129,14 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index ffcf4d8878..210372f562 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -129,9 +129,14 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 85afc8047e..a5751f9f43 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -129,9 +129,14 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index e9ae709eee..2dfeef4865 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -190,9 +190,14 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 3c3b153ca8..2a5de10174 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -196,9 +196,14 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 89d76f2b5b..70425ee114 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -194,9 +194,14 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 566a3f46a6..d6f0a0c359 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -194,9 +194,14 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 9d94fd722c..7f8cd150b4 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 9e22617b04..44cc7e8d53 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 3b4e0e85ff..7ff6515144 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -196,9 +196,14 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 739ab0d1cd..371b448201 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index e83362f762..b053c462cb 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -190,9 +190,14 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index 6df8fc9993..d690dede25 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -198,9 +198,14 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 593606437c..872444f322 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -194,9 +194,14 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index f87e232eeb..2f78a74687 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -190,9 +190,14 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + if (!first_found) { first_found = my_bot; } + if (current_check) { c->Message( Chat::Green, diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index e60a17961f..3f42d995dc 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2221,6 +2221,11 @@ bool BotDatabase::LoadBotSettings(Mob* m) else { query = fmt::format("`bot_id` = {} AND `stance` = {}", mobID, stanceID); } + + if (stanceID == Stance::Passive) { + LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive); + return true; + } const auto& l = BotSettingsRepository::GetWhere(database, query); @@ -2268,6 +2273,11 @@ bool BotDatabase::SaveBotSettings(Mob* m) uint32 charID = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); + if (stanceID == Stance::Passive) { + LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive); + return true; + } + std::string query = ""; if (m->IsClient()) { From 10ddcb0151b8afca9d061a520d7460d6a4f8e381 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:07:35 -0600 Subject: [PATCH 115/394] passivecheck response --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8b997f4bf5..328f5c7af0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11426,7 +11426,7 @@ bool Bot::BotPassiveCheck() { GetOwner()->Message( Chat::Yellow, fmt::format( - "{} says, 'I am currently set to stance {} [#{}]. My settings cannot be modified.'", + "{} says, 'I am currently set to stance {} [#{}]. I cannot do commands and my settings cannot be modified.'", GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive From 22f12651782a6a8a6871f20b6423eff8702f8d9e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 10:13:44 -0600 Subject: [PATCH 116/394] remove spelltype checks from clients causing bad data --- zone/bot_database.cpp | 2 +- zone/client.cpp | 4 ---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 3f42d995dc..60f389a9b0 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2356,7 +2356,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme - if (IsClientBotSpellType(x) && m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { + if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, .bot_id = botID, diff --git a/zone/client.cpp b/zone/client.cpp index 176bdaab00..dd22e2a230 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13087,10 +13087,6 @@ void Client::LoadDefaultBotSettings() { LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - BotSpellSettings_Struct t; t.spellType = i; From ec89a65aebe97ee0a02876d662870950d5e8c79a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 10:14:23 -0600 Subject: [PATCH 117/394] remove circle/teleport, tweak depart --- zone/bot_command.cpp | 3 - zone/bot_command.h | 2 - zone/bot_commands/depart.cpp | 74 +++++++++++++++----- zone/bot_commands/teleport.cpp | 119 --------------------------------- 4 files changed, 59 insertions(+), 139 deletions(-) delete mode 100644 zone/bot_commands/teleport.cpp diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 80be595f89..b47edd5e34 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1287,7 +1287,6 @@ int bot_command_init(void) bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || bot_command_add("distanceranged", "Controls the range casters and ranged will try to stay away from a mob", AccountStatus::Player, bot_command_distance_ranged) || bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || - bot_command_add("circle", "Orders a Druid bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_circle) || bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || @@ -1345,7 +1344,6 @@ int bot_command_init(void) bot_command_add("picklock", "Orders a capable bot to pick the lock of the closest door", AccountStatus::Player, bot_command_pick_lock) || bot_command_add("pickpocket", "Orders a capable bot to pickpocket a NPC", AccountStatus::Player, bot_command_pickpocket) || bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || - bot_command_add("portal", "Orders a Wizard bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_portal) || bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || bot_command_add("resistance", "Orders a bot to cast a specified resistance buff", AccountStatus::Player, bot_command_resistance) || @@ -2316,7 +2314,6 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" #include "bot_commands/taunt.cpp" -#include "bot_commands/teleport.cpp" #include "bot_commands/timer.cpp" #include "bot_commands/track.cpp" #include "bot_commands/view_combos.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index a8951a7cd4..373fbd1e54 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1775,7 +1775,6 @@ void bot_command_toggle_ranged(Client* c, const Seperator* sep); void bot_command_update(Client *c, const Seperator *sep); void bot_command_woad(Client *c, const Seperator *sep); -void bot_command_circle(Client *c, const Seperator *sep); void bot_command_heal_rotation_adaptive_targeting(Client *c, const Seperator *sep); void bot_command_heal_rotation_add_member(Client *c, const Seperator *sep); void bot_command_heal_rotation_add_target(Client *c, const Seperator *sep); @@ -1803,7 +1802,6 @@ void bot_command_inventory_window(Client *c, const Seperator *sep); void bot_command_pet_get_lost(Client *c, const Seperator *sep); void bot_command_pet_remove(Client *c, const Seperator *sep); void bot_command_pet_set_type(Client *c, const Seperator *sep); -void bot_command_portal(Client *c, const Seperator *sep); // bot command helpers diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index b337ef4042..d9b09d2d4e 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -1,59 +1,103 @@ #include "../bot_command.h" -void bot_command_depart(Client *c, const Seperator *sep) +void bot_command_depart(Client* c, const Seperator* sep) { bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) + if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); helper_send_usage_required_bots(c, BCEnum::SpT_Depart); + return; } - bool single = false; + std::list sbl; + MyBots::PopulateSBL_BySpawnedBots(c, sbl); + ActionableTarget::Types actionable_targets; + Bot* my_bot = nullptr; bool single = false; std::string single_arg = sep->arg[2]; - if (!single_arg.compare("single")) + + if (!single_arg.compare("single")) { single = true; + } std::string destination = sep->arg[1]; + if (!destination.compare("list")) { - Bot* my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid); - Bot* my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard); + Bot* my_druid_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Druid); + Bot* my_wizard_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Wizard); + + if ( + (!my_druid_bot && !my_wizard_bot) || + (my_druid_bot && !my_druid_bot->IsInGroupOrRaid(c)) || + (my_wizard_bot && !my_wizard_bot->IsInGroupOrRaid(c)) + ) { + c->Message(Chat::Yellow, "No compatible bots found for %s.", sep->arg[0]); + return; + } + helper_command_depart_list(c, my_druid_bot, my_wizard_bot, local_list, single); + return; } else if (destination.empty()) { c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); + return; } - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; + my_bot = nullptr; + sbl.clear(); MyBots::PopulateSBL_BySpawnedBots(c, sbl); - bool cast_success = false; + for (auto list_iter : *local_list) { auto local_entry = list_iter->SafeCastToDepart(); - if (helper_spell_check_fail(local_entry)) + + if (helper_spell_check_fail(local_entry)) { continue; - if (local_entry->single != single) + } + + if (local_entry->single != single) { continue; - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) + } + + if (destination.compare(spells[local_entry->spell_id].teleport_zone)) { continue; + } auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); + if (!target_mob) continue; my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) + + if (!my_bot) { continue; + } + + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!my_bot->IsInGroupOrRaid(c)) { + continue; + } + + if (local_entry->spell_id != 0 && my_bot->GetMana() < spells[local_entry->spell_id].mana) { + continue; + } cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); + break; } - helper_no_available_bots(c, my_bot); + if (!cast_success) { + helper_no_available_bots(c, my_bot); + } } diff --git a/zone/bot_commands/teleport.cpp b/zone/bot_commands/teleport.cpp deleted file mode 100644 index 94e8ee2b05..0000000000 --- a/zone/bot_commands/teleport.cpp +++ /dev/null @@ -1,119 +0,0 @@ -#include "../bot_command.h" - -void bot_command_circle(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_circle", sep->arg[0], "circle")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Druid); - return; - } - - bool single = false; - std::string single_arg = sep->arg[2]; - if (!single_arg.compare("single")) - single = true; - - std::string destination = sep->arg[1]; - if (!destination.compare("list")) { - auto my_druid_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Druid); - helper_command_depart_list(c, my_druid_bot, nullptr, local_list, single); - return; - } - else if (destination.empty()) { - c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToDepart(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->caster_class != Class::Druid) - continue; - if (local_entry->single != single) - continue; - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} - -void bot_command_portal(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_portal", sep->arg[0], "portal")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Depart, Class::Wizard); - return; - } - - bool single = false; - std::string single_arg = sep->arg[2]; - if (!single_arg.compare("single")) - single = true; - - std::string destination = sep->arg[1]; - if (!destination.compare("list")) { - auto my_wizard_bot = ActionableBots::AsGroupMember_ByClass(c, c, Class::Wizard); - helper_command_depart_list(c, nullptr, my_wizard_bot, local_list, single); - return; - } - else if (destination.empty()) { - c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToDepart(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->caster_class != Class::Wizard) - continue; - if (local_entry->single != single) - continue; - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} From b0768454c16bed7163870eb5e6a3d33157dc2b6e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:10:13 -0600 Subject: [PATCH 118/394] adjust spell hold checks to rely on caster and Implement pet resist buffs and pet damage shields --- common/spdat.cpp | 10 ++++++++-- common/spdat.h | 8 +++++--- zone/bot.cpp | 19 +++++++++++++++++-- zone/bot_commands/spell_holds.cpp | 7 ++----- zone/botspellsai.cpp | 4 ++++ zone/mob.cpp | 24 +++++++++++++++++++----- 6 files changed, 55 insertions(+), 17 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 48b6351fb2..8211ecfdcc 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2874,14 +2874,16 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { case BotSpellTypes::Buff: case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: - case BotSpellTypes::DamageShields: + case BotSpellTypes::DamageShields: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: case BotSpellTypes::Pet: case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: - case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: return true; case BotSpellTypes::InCombatBuff: @@ -2916,8 +2918,10 @@ bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: return true; default: return false; @@ -3050,8 +3054,10 @@ bool IsClientBotSpellType(uint16 spellType) { case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index b07bf7d966..60257a4014 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -705,10 +705,12 @@ namespace BotSpellTypes constexpr uint16 PetVeryFastHeals = 49; constexpr uint16 PetHoTHeals = 50; constexpr uint16 DamageShields = 51; - constexpr uint16 ResistBuffs = 52; + constexpr uint16 ResistBuffs = 52; + constexpr uint16 PetDamageShields = 53; + constexpr uint16 PetResistBuffs = 54; - constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::ResistBuffs; // Do not remove this, increment as needed + constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this + constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed } const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); diff --git a/zone/bot.cpp b/zone/bot.cpp index 328f5c7af0..de129d9eb8 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9505,7 +9505,9 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::DamageShields: - case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: if ( !( spells[spellid].target_type == ST_Target || @@ -10400,6 +10402,14 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, ui case BotSpellTypes::PreCombatBuffSong: priority = 23; + break; + case BotSpellTypes::PetResistBuffs: + priority = 24; + + break; + case BotSpellTypes::PetDamageShields: + priority = 25; + break; default: priority = 0; //unused @@ -10861,7 +10871,9 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: case BotSpellTypes::DamageShields: - case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: return BotSpellTypes::Buff; case BotSpellTypes::AEMez: case BotSpellTypes::Mez: @@ -10921,18 +10933,21 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { switch (spellType) { case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return false; } return true; case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: if (IsResistanceOnlySpell(spellid)) { return true; } return false; case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: if (IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { return true; } diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index effd97a7ae..fd17749d18 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -9,15 +9,12 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) if (helper_is_help_or_usage(sep->arg[1])) { std::vector description = { - "Toggles whether or not bots can cast or receive certain spell types" + "Toggles whether or not bots can cast certain spell types" }; std::vector notes = { - "- All pet types are based off the pet's owner's setting", - "- Any remaining types use the owner's setting when a pet is the target", - "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", - "- e.g., BotA is healing BotB using BotB's settings", + "- All pet types are based off the pet owner's setting when a pet is the target" }; std::vector example_format = diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index d7e6338223..b0ca5edf43 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -125,7 +125,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { return false; } @@ -2039,7 +2041,9 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: return RuleI(Bots, PercentChanceToCastBuff); case BotSpellTypes::Escape: return RuleI(Bots, PercentChanceToCastEscape); diff --git a/zone/mob.cpp b/zone/mob.cpp index e75ce00683..5ef232a75d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8869,6 +8869,12 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::ResistBuffs: spellTypeName = "Resist Buff"; break; + case BotSpellTypes::PetDamageShields: + spellTypeName = "Pet Damage Shield"; + break; + case BotSpellTypes::PetResistBuffs: + spellTypeName = "Pet Resist Buff"; + break; default: break; } @@ -9039,6 +9045,12 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::ResistBuffs: spellTypeName = "resistbuffs"; break; + case BotSpellTypes::PetDamageShields: + spellTypeName = "petdamageshields"; + break; + case BotSpellTypes::PetResistBuffs: + spellTypeName = "petresistbuffs"; + break; default: break; } @@ -9318,7 +9330,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::Buff: case BotSpellTypes::Charm: case BotSpellTypes::Cure: - case BotSpellTypes::DamageShields: + case BotSpellTypes::DamageShields: case BotSpellTypes::HateRedux: case BotSpellTypes::InCombatBuff: case BotSpellTypes::InCombatBuffSong: @@ -9329,6 +9341,8 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: return 100; @@ -9401,10 +9415,6 @@ bool Mob::GetUltimateSpellHold(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellHold(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { - return tar->GetSpellHold(spellType); - } - return GetSpellHold(spellType); } @@ -9486,6 +9496,10 @@ uint16 Mob::GetPetSpellType(uint16 spellType) { return BotSpellTypes::PetHoTHeals; case BotSpellTypes::Buff: return BotSpellTypes::PetBuffs; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; default: break; } From a0f9745c462ef650183b79a57fa3ff46a7a3286b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:13:04 -0600 Subject: [PATCH 119/394] correct GetBestBotMagicianPetSpell --- zone/bot.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.h b/zone/bot.h index e0d857880e..f29d86235a 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -595,7 +595,7 @@ class Bot : public NPC { static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE = false); bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid); static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); - static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); + static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Pet); static std::string GetBotMagicianPetType(Bot* botCaster); static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); From 8fe5112e074c7090612c109198b57c268234e606 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:13:27 -0600 Subject: [PATCH 120/394] add sanity check to campCount on ^camp --- zone/bot_commands/bot.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index ba0ae10dcb..da2352a197 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -63,7 +63,9 @@ void bot_command_camp(Client *c, const Seperator *sep) ++campCount; } - c->Message(Chat::White, "%i of your bots have been camped.", campCount); + if (campCount) { + c->Message(Chat::White, "%i of your bots have been camped.", campCount); + } } void bot_command_clone(Client *c, const Seperator *sep) From 1c8da32f7247bbc4250aaf1197f8fd1553a2cd46 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:14:34 -0600 Subject: [PATCH 121/394] add PercentChanceToCast rules for AE and group spells --- common/ruletypes.h | 2 ++ zone/botspellsai.cpp | 32 ++++++++++++++++++++------------ 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index d9991c0f9a..3f8cab0602 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -794,7 +794,9 @@ RULE_INT(Bots, SpellResistLimit, 150, "150 Default. This is the resist cap where RULE_INT(Bots, StunCastChanceIfCasting, 50, "50 Default. Chance for non-Paladins to cast a stun spell if the target is casting.") RULE_INT(Bots, StunCastChanceNormal, 15, "15 Default. Chance for non-Paladins to cast a stun spell on the target.") RULE_INT(Bots, StunCastChancePaladins, 75, "75 Default. Chance for Paladins to cast a stun spell if the target is casting.") +RULE_INT(Bots, PercentChanceToCastAEs, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastNuke, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastGroupHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") RULE_INT(Bots, PercentChanceToCastHeal, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") RULE_INT(Bots, PercentChanceToCastRoot, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastBuff, 90, "The chance for a bot to attempt to cast the given spell type in combat. Default 90%.") diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b0ca5edf43..37decd7799 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2034,6 +2034,22 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::AEStun: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEMez: + case BotSpellTypes::AESlow: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::AERoot: + case BotSpellTypes::PBAENuke: + return RuleI(Bots, PercentChanceToCastAEs); + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::GroupCures: + return RuleI(Bots, PercentChanceToCastGroupHeal); case BotSpellTypes::Nuke: return RuleI(Bots, PercentChanceToCastNuke); case BotSpellTypes::Root: @@ -2049,7 +2065,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj return RuleI(Bots, PercentChanceToCastEscape); case BotSpellTypes::Lifetap: return RuleI(Bots, PercentChanceToCastLifetap); - case BotSpellTypes::AESnare: case BotSpellTypes::Snare: return RuleI(Bots, PercentChanceToCastSnare); case BotSpellTypes::DOT: @@ -2057,30 +2072,23 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::Dispel: return RuleI(Bots, PercentChanceToCastDispel); case BotSpellTypes::InCombatBuff: - return RuleI(Bots, PercentChanceToCastInCombatBuff); - case BotSpellTypes::AEMez: + return RuleI(Bots, PercentChanceToCastInCombatBuff); case BotSpellTypes::Mez: - return RuleI(Bots, PercentChanceToCastMez); - case BotSpellTypes::AESlow: + return RuleI(Bots, PercentChanceToCastMez); case BotSpellTypes::Slow: - return RuleI(Bots, PercentChanceToCastSlow); - case BotSpellTypes::AEDebuff: + return RuleI(Bots, PercentChanceToCastSlow); case BotSpellTypes::Debuff: return RuleI(Bots, PercentChanceToCastDebuff); case BotSpellTypes::Cure: return RuleI(Bots, PercentChanceToCastCure); case BotSpellTypes::HateRedux: - return RuleI(Bots, PercentChanceToCastHateRedux); - case BotSpellTypes::AEFear: + return RuleI(Bots, PercentChanceToCastHateRedux); case BotSpellTypes::Fear: return RuleI(Bots, PercentChanceToCastFear); case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: case BotSpellTypes::FastHeals: case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetRegularHeals: case BotSpellTypes::PetCompleteHeals: From 2042e951637979d3985b69accebd766f51341067 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:15:08 -0600 Subject: [PATCH 122/394] Add AllowAIMez to allow bot auto mez to be toggled --- common/ruletypes.h | 1 + zone/botspellsai.cpp | 12 ++++++++++++ 2 files changed, 13 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 3f8cab0602..929bf5404d 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -876,6 +876,7 @@ RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") +RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 37decd7799..5adbdb3e98 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -639,6 +639,10 @@ bool Bot::AI_PursueCastCheck() { continue; } + if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + continue; + } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -698,6 +702,10 @@ bool Bot::AI_IdleCastCheck() { continue; } + if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + continue; + } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -744,6 +752,10 @@ bool Bot::AI_EngagedCastCheck() { continue; } + if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + continue; + } + if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } From 161d6b3b6609a41d23b392d74bf658d06e34283d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:31:59 -0600 Subject: [PATCH 123/394] apply ranged setting on spawn to show correct weapons --- zone/bot.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index de129d9eb8..4dc324a719 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3398,6 +3398,10 @@ bool Bot::Spawn(Client* botCharacterOwner) { } } + if (IsBotRanged()) { + ChangeBotRangedWeapons(true); + } + if (auto raid = entity_list.GetRaidByBotName(GetName())) { // Safety Check to confirm we have a valid raid auto owner = GetBotOwner(); From f3c0ddd4be2ba062eca4e0717875838ebc739b24 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 10 Nov 2024 23:35:28 -0600 Subject: [PATCH 124/394] correct and tweak all combat positioning and combat range --- common/ruletypes.h | 8 +-- zone/bot.cpp | 148 ++++++++++++++++++++++---------------- zone/bot.h | 4 +- zone/bot_commands/bot.cpp | 1 + 4 files changed, 95 insertions(+), 66 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 929bf5404d..872da6ca7e 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -842,15 +842,15 @@ RULE_INT(Bots, StackSizeMin, 20, "20 Default. -1 to disable and use default max RULE_INT(Bots, HasOrMayGetAggroThreshold, 90, "90 Default. Percent threshold of total hate where bots will stop casting spells that generate hate if they are set to try to not pull aggro via spells.") RULE_BOOL(Bots, UseFlatNormalMeleeRange, false, "False Default. If true, bots melee distance will be a flat distance set by Bots:NormalMeleeRangeDistance.") RULE_REAL(Bots, NormalMeleeRangeDistance, 0.75, "If UseFlatNormalMeleeRange is enabled, multiplier of the max melee range at which a bot will stand in melee combat. 0.75 Recommended, max melee for all abilities to land.") -RULE_REAL(Bots, PercentMinMeleeDistance, 0.60, "Multiplier of the max melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") +RULE_REAL(Bots, PercentMinMeleeDistance, 0.75, "Multiplier of the their melee range - Minimum distance from target a bot will stand while in melee combat before trying to adjust. 0.60 Recommended.") RULE_REAL(Bots, MaxDistanceForMelee, 20, "Maximum distance bots will stand for melee. Default 20 to allow all special attacks to land.") RULE_REAL(Bots, TauntNormalMeleeRangeDistance, 0.50, "Multiplier of the max melee range at which a taunting bot will stand in melee combat. 0.50 Recommended, closer than others .") -RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.25, "Multiplier of max melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") +RULE_REAL(Bots, PercentTauntMinMeleeDistance, 0.40, "Multiplier of their melee range - Minimum distance from target a taunting bot will stand while in melee combat before trying to adjust. 0.25 Recommended.") RULE_REAL(Bots, PercentMaxMeleeRangeDistance, 0.95, "Multiplier of the max melee range at which a bot will stand in melee combat. 0.95 Recommended, max melee while disabling special attacks/taunt.") RULE_REAL(Bots, PercentMinMaxMeleeRangeDistance, 0.75, "Multiplier of the closest max melee range at which a bot will stand in melee combat before trying to adjust. 0.75 Recommended, max melee while disabling special attacks/taunt.") RULE_BOOL(Bots, TauntingBotsFollowTopHate, true, "True Default. If true, bots that are taunting will attempt to stick with whoever currently is top hate.") -RULE_REAL(Bots, DistanceTauntingBotsStickMainHate, 25.00, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") -RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, false, "False Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") +RULE_INT(Bots, DistanceTauntingBotsStickMainHate, 10, "If TauntingBotsFollowTopHate is enabled, this is the distance bots will try to stick to whoever currently is Top Hate.") +RULE_BOOL(Bots, DisableSpecialAbilitiesAtMaxMelee, true, "True Default. If true, when bots are at max melee distance, special abilities including taunt will be disabled.") RULE_INT(Bots, MinJitterTimer, 500, "Minimum ms between bot movement jitter checks.") RULE_INT(Bots, MaxJitterTimer, 2500, "Maximum ms between bot movement jitter checks. Set to 0 to disable timer checks.") RULE_BOOL(Bots, PreventBotCampOnFD, true, "True Default. If true, players will not be able to camp bots while feign death.") diff --git a/zone/bot.cpp b/zone/bot.cpp index 4dc324a719..df4ec51489 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2165,7 +2165,8 @@ void Bot::AI_Process() // COMBAT RANGE CALCS bool atCombatRange = false; - bool behindMob = false; + bool behindMob = BehindMob(tar, GetX(), GetY()); + bool frontMob = InFrontMob(tar, GetX(), GetY()); uint8 stopMeleeLevel = GetStopMeleeLevel(); const EQ::ItemInstance* p_item; const EQ::ItemInstance* s_item; @@ -2232,7 +2233,7 @@ void Bot::AI_Process() } if (!jitterCooldown && AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { - DoCombatPositioning(tar, Goal, stopMeleeLevel, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behindMob); + DoCombatPositioning(tar, Goal, stopMeleeLevel, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behindMob, frontMob); return; } else { @@ -2365,10 +2366,10 @@ bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, g else { Goal = follow_mob->GetPosition(); } + float destination_distance = DistanceSquared(GetPosition(), Goal); if ((!bot_owner->GetBotPulling() || PULLING_BOT) && (destination_distance > GetFollowDistance())) { - if (!IsRooted()) { if (rest_timer.Enabled()) { rest_timer.Disable(); @@ -2382,7 +2383,6 @@ bool Bot::TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, g else { if (IsMoving()) { - StopMoving(); return true; } @@ -2735,7 +2735,7 @@ bool Bot::TryEvade(Mob* tar) { return false; } -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool& behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { +void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { atCombatRange= false; p_item = GetBotItem(EQ::invslot::slotPrimary); @@ -2744,32 +2744,46 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo bool backstab_weapon = false; if (GetBehindMob()) { - behindMob = BehindMob(tar, GetX(), GetY()); // Can be separated for other future use if (GetClass() == Class::Rogue) { backstab_weapon = p_item && p_item->GetItemBackstabDamage(); } } // Calculate melee distances - CalcMeleeDistances(tar, p_item, s_item, behindMob, backstab_weapon, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); - - //LogTestDebugDetail("{} is {} {}. They are currently {} away {} to be between [{} - {}] away. MMR is {}." - // , GetCleanName() - // , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) - // , tar->GetCleanName() - // , tar_distance - // , (tar_distance <= melee_distance ? "but only needed" : "but need to be") - // , melee_distance_min - // , melee_distance - // , melee_distance_max - //); //deleteme + CalcMeleeDistances(tar, p_item, s_item, backstab_weapon, behindMob, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); + + if (!GetCombatRoundForAlerts()) { + SetCombatRoundForAlerts(); + LogTestDebugDetail("{} says, 'I'm {} {}. I am currently {} away {} to be between [{} - {}] away. MMR is {}.'" + , GetCleanName() + , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) + , tar->GetCleanName() + , tar_distance + , (tar_distance <= melee_distance ? "but only needed" : "but need to be") + , melee_distance_min + , melee_distance + , melee_distance_max + ); //deleteme + LogTestDebugDetail("{} says, 'My stance is {} #{}, I am {} taunting. I am set to {} {}, {} at MMR, distanceranged {}, sml {} [{}]'" + , GetCleanName() + , Stance::GetName(GetBotStance()) + , GetBotStance() + , (IsTaunting() ? "currently" : "not") + , (BehindMob() ? "stay behind" : "not stay behind") + , tar->GetCleanName() + , (GetMaxMeleeRange() ? "I stay" : "I do not stay") + , GetBotDistanceRanged() + , GetStopMeleeLevel() + , ((GetLevel() <= GetStopMeleeLevel()) ? "disabled" : "enabled") + ); //deleteme + } if (tar_distance <= melee_distance) { atCombatRange = true; } } -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool behindMob, bool backstab_weapon, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { +void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool backstab_weapon, bool behindMob, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { float size_mod = GetSize(); float other_size_mod = tar->GetSize(); @@ -2882,27 +2896,26 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it melee_distance = RuleR(Bots, MaxDistanceForMelee); } - melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMeleeDistance); + melee_distance_min = melee_distance * RuleR(Bots, PercentMinMeleeDistance); - if (taunting) { - melee_distance_min = melee_distance_max * RuleR(Bots, PercentTauntMinMeleeDistance); - melee_distance = melee_distance_max * RuleR(Bots, TauntNormalMeleeRangeDistance); + if (IsTaunting()) { + melee_distance_min = melee_distance * RuleR(Bots, PercentTauntMinMeleeDistance); + melee_distance = melee_distance * RuleR(Bots, TauntNormalMeleeRangeDistance); } bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; - if (!taunting && !IsBotRanged() && !isStopMeleeLevel && GetMaxMeleeRange()) { - melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); + if (!IsTaunting() && !IsBotRanged() && !isStopMeleeLevel && GetMaxMeleeRange()) { melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); + melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); } if (isStopMeleeLevel && !IsBotRanged()) { float desiredRange = GetBotDistanceRanged(); - melee_distance_min = std::min(melee_distance, (desiredRange / 2)); + melee_distance_min = std::max(melee_distance, (desiredRange / 2)); melee_distance = std::max((melee_distance + 1), desiredRange); } - /* Archer Checks*/ if (IsBotRanged()) { float minDistance = RuleI(Combat, MinRangedAttackDist); float maxDistance = GetBotRangedValue(); @@ -3005,7 +3018,6 @@ Mob* Bot::GetBotTarget(Client* bot_owner) } bool Bot::ReturningFlagChecks(Client* bot_owner, float fm_distance) {// Need to make it back to group before clearing return flag - if (fm_distance <= GetFollowDistance()) { // Once we're back, clear blocking flags so everyone else can join in @@ -3024,13 +3036,12 @@ bool Bot::ReturningFlagChecks(Client* bot_owner, float fm_distance) {// Need to WipeHateList(); return false; } + return true; } bool Bot::PullingFlagChecks(Client* bot_owner) { - if (!GetTarget()) { - WipeHateList(); SetTarget(nullptr); SetPullingFlag(false); @@ -3044,7 +3055,6 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { return false; } else if (GetTarget()->GetHateList().size()) { - WipeHateList(); SetTarget(nullptr); SetPullingFlag(false); @@ -3066,7 +3076,6 @@ bool Bot::PullingFlagChecks(Client* bot_owner) { } void Bot::HealRotationChecks() { - if (IsMyHealRotationSet()) { if (AIHealRotation(HealRotationTarget(), UseHealRotationFastHeals())) { m_member_of_heal_rotation->SetMemberIsCasting(this); @@ -3081,7 +3090,6 @@ void Bot::HealRotationChecks() { } bool Bot::IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid) { - if (!bot_owner || (!bot_group && !raid) || !IsAIControlled()) { return false; } @@ -3091,11 +3099,11 @@ bool Bot::IsAIProcessValid(const Client* bot_owner, const Group* bot_group, cons SetBotOwner(nullptr); return false; } + return true; } bool Bot::CheckIfCasting(float fm_distance) { - if (IsCasting()) { if (IsHealRotationMember() && m_member_of_heal_rotation->CastingOverride() && @@ -3107,12 +3115,10 @@ bool Bot::CheckIfCasting(float fm_distance) { InterruptSpell(); } else if (AmICastingForHealRotation() && m_member_of_heal_rotation->CastingMember() == this) { - AdvanceHealRotation(false); return true; } else if (GetClass() != Class::Bard) { - if (IsEngaged()) { return true; } @@ -3130,13 +3136,12 @@ bool Bot::CheckIfCasting(float fm_distance) { else if (IsHealRotationMember()) { m_member_of_heal_rotation->SetMemberIsCasting(this, false); } + return false; } bool Bot::CheckIfIncapacitated() { - if (GetPauseAI() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { - if (IsCasting()) { InterruptSpell(); } @@ -3145,6 +3150,7 @@ bool Bot::CheckIfIncapacitated() { AdvanceHealRotation(false); m_member_of_heal_rotation->SetMemberIsCasting(this, false); } + return true; } @@ -3190,29 +3196,29 @@ void Bot::SetBerserkState() {// Berserk updates should occur if primary AI crite Mob* Bot::SetFollowMob(Client* leash_owner) { Mob* follow_mob = entity_list.GetMob(GetFollowID()); - if (!follow_mob) { + if (!follow_mob) { follow_mob = leash_owner; SetFollowID(leash_owner->GetID()); } + return follow_mob; } Client* Bot::SetLeashOwner(Client* bot_owner, Group* bot_group, Raid* raid, uint32 r_group) const { - Client* leash_owner = nullptr; + if (raid && r_group < MAX_RAID_GROUPS && raid->GetGroupLeader(r_group)) { leash_owner = raid->GetGroupLeader(r_group) && raid->GetGroupLeader(r_group)->IsClient() ? raid->GetGroupLeader(r_group)->CastToClient() : bot_owner; - } else if (bot_group) { leash_owner = (bot_group->GetLeader() && bot_group->GetLeader()->IsClient() ? bot_group->GetLeader()->CastToClient() : bot_owner); - } else { leash_owner = bot_owner; } + return leash_owner; } @@ -3237,6 +3243,7 @@ void Bot::SetOwnerTarget(Client* bot_owner) { AddToHateList(attack_target, 1); SetTarget(attack_target); SetAttackingFlag(); + if (GetPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { GetPet()->WipeHateList(); GetPet()->AddToHateList(attack_target, 1); @@ -3277,8 +3284,8 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { SetTarget(pull_target); SetPullingFlag(); bot_owner->SetBotPulling(); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { + if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); m_previous_pet_order = GetPet()->GetPetOrder(); @@ -5121,7 +5128,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if (taunting && target->IsNPC() && taunt_time) { + if (IsTaunting() && target->IsNPC() && taunt_time) { if (GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { BotGroupSay( this, @@ -6747,6 +6754,7 @@ void Bot::Zone() { bool Bot::IsAtRange(Mob *target) { bool result = false; + if (target) { float range = (GetBotRangedValue() + 5.0); range *= range; @@ -7490,6 +7498,7 @@ void EntityList::ScanCloseClientMobs(std::unordered_map& close_mob } float distance = DistanceSquared(scanning_mob->GetPosition(), mob->GetPosition()); + if (distance <= scan_range) { close_mobs.insert(std::pair(mob->GetID(), mob)); } @@ -9469,7 +9478,7 @@ bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrecheck return false; } - if (!IsCommandedSpell() && !taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { + if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme return false; } @@ -10820,7 +10829,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { Mob* tar = GetTarget(); - if (!taunting && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { + if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme return result; } @@ -11047,63 +11056,81 @@ void Bot::SetCombatJitter() { } } -void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob) { +void Bot::DoCombatPositioning( + Mob* tar, + glm::vec3 Goal, + bool stopMeleeLevel, + float tar_distance, + float melee_distance_min, + float melee_distance, + float melee_distance_max, + bool behindMob, + bool frontMob +) { + //LogTestDebug("{} says, 'DoCombatPositioning. {} #{}", GetCleanName(), __FILE__, __LINE__); //deleteme + if (HasTargetReflection()) { - if (!taunting && !tar->IsFeared() && !tar->IsStunned()) { + if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) { if (TryEvade(tar)) { return; } } - - if (tar->IsRooted() && !taunting) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay + else if (tar->IsRooted() && !IsTaunting()) { // Move non-taunters out of range - Above already checks if bot is targeted, otherwise they would stay if (tar_distance <= melee_distance_max) { - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, taunting)) { + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, (melee_distance_max + 1), (melee_distance_max * 2), false, IsTaunting())) { RunToGoalWithJitter(Goal); + return; } } } - - if (taunting && tar_distance < melee_distance_min) { // Back up any bots that are too close - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, taunting)) { + else if (IsTaunting() && ((tar_distance < melee_distance_min) || !frontMob)) { // Back up any bots that are too close + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, IsTaunting())) { RunToGoalWithJitter(Goal); + return; } } } else { if (!tar->IsFeared()) { - if (taunting) { // Taunting adjustments + if (IsTaunting()) { // Taunting adjustments Mob* mobTar = tar->GetTarget(); + if (!mobTar || mobTar == nullptr) { DoFaceCheckNoJitter(tar); + return; } if (RuleB(Bots, TauntingBotsFollowTopHate)) { // If enabled, taunting bots will stick to top hate - if ((DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + if (Distance(m_Position, mobTar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate)) { Goal = mobTar->GetPosition(); RunToGoalWithJitter(Goal); + return; } } else { // Otherwise, stick to any other bots that are taunting - if (mobTar->IsBot() && mobTar->CastToBot()->taunting && (DistanceSquared(m_Position, mobTar->GetPosition()) > pow(RuleR(Bots, DistanceTauntingBotsStickMainHate), 2))) { + if (mobTar->IsBot() && mobTar->CastToBot()->IsTaunting() && (Distance(m_Position, mobTar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))) { Goal = mobTar->GetPosition(); RunToGoalWithJitter(Goal); + return; } } } - else if (tar_distance < melee_distance_min || (GetBehindMob() && !behindMob) || !HasRequiredLoSForPositioning(tar)) { // Regular adjustment - if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), taunting)) { + else if (tar_distance < melee_distance_min || (GetBehindMob() && !behindMob) || (IsTaunting() && !frontMob)|| !HasRequiredLoSForPositioning(tar)) { // Regular adjustment + if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), IsTaunting())) { RunToGoalWithJitter(Goal); + return; } } - else if (tar->IsEnraged() && !taunting && !stopMeleeLevel && !behindMob) { // Move non-taunting melee bots behind target during enrage + else if (tar->IsEnraged() && !IsTaunting() && !stopMeleeLevel && !behindMob) { // Move non-taunting melee bots behind target during enrage if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) { RunToGoalWithJitter(Goal); + return; } } @@ -11111,6 +11138,7 @@ void Bot::DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, flo } DoFaceCheckNoJitter(tar); + return; } diff --git a/zone/bot.h b/zone/bot.h index f29d86235a..ff3c7c1296 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -943,7 +943,7 @@ class Bot : public NPC { Mob* tar, float tar_distance, bool& atCombatRange, - bool& behindMob, + bool behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, @@ -957,7 +957,7 @@ class Bot : public NPC { void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; } void SetCombatJitter(); void SetCombatOutOfRangeJitter(); - void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob); + void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob, bool frontMob); void DoFaceCheckWithJitter(Mob* tar); void DoFaceCheckNoJitter(Mob* tar); void RunToGoalWithJitter(glm::vec3 Goal); diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index da2352a197..f9a3ecfe91 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1695,6 +1695,7 @@ void bot_command_toggle_ranged(Client *c, const Seperator *sep) else { bot_iter->SetBotRangedSetting(ranged_state); } + bot_iter->ChangeBotRangedWeapons(bot_iter->IsBotRanged()); } } From abefd1cccea854aba2f2c133446bc95ed512f875 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 12 Nov 2024 22:19:16 -0600 Subject: [PATCH 125/394] Add loregroup 0 bypass for lore conflicts for bots like clients --- zone/bot.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index df4ec51489..139bcbbd91 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7157,8 +7157,9 @@ void Bot::CalcBotStats(bool showtext) { } bool Bot::CheckLoreConflict(const EQ::ItemData* item) { - if (!item || !(item->LoreFlag)) + if (!item || !(item->LoreFlag) || (item->LoreGroup == 0)) { return false; + } if (item->LoreGroup == -1) // Standard lore items; look everywhere except the shared bank, return the result return (m_inv.HasItem(item->ID, 0, invWhereWorn) != INVALID_INDEX); From ab446138a324bbf256db2d1d0b3611caa837b3be Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 13 Nov 2024 10:10:47 -0600 Subject: [PATCH 126/394] add bot camp timer to prevent /camp exploits --- common/ruletypes.h | 1 + zone/client.cpp | 2 ++ zone/client.h | 1 + zone/client_packet.cpp | 2 ++ zone/client_process.cpp | 4 ++++ 5 files changed, 10 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 872da6ca7e..9251a197fb 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -877,6 +877,7 @@ RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follo RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") +RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/client.cpp b/zone/client.cpp index dd22e2a230..bb1f11a86e 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -148,6 +148,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( ), hpupdate_timer(2000), camp_timer(29000), + bot_camp_timer((RuleI(Bots, CampTimer) * 1000)), process_timer(100), consume_food_timer(CONSUMPTION_TIMER), zoneinpacket_timer(1000), @@ -252,6 +253,7 @@ Client::Client(EQStreamInterface *ieqs) : Mob( fishing_timer.Disable(); dead_timer.Disable(); camp_timer.Disable(); + bot_camp_timer.Disable(); autosave_timer.Disable(); GetMercTimer()->Disable(); instalog = false; diff --git a/zone/client.h b/zone/client.h index 71310dd84f..77cf9a574c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2036,6 +2036,7 @@ class Client : public Mob PTimerList p_timers; //persistent timers Timer hpupdate_timer; Timer camp_timer; + Timer bot_camp_timer; Timer process_timer; Timer consume_food_timer; Timer zoneinpacket_timer; diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 06c7e7c60d..72ebaa72ce 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4285,6 +4285,7 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app) return; } camp_timer.Start(29000, true); + bot_camp_timer.Start((RuleI(Bots, CampTimer) * 1000), true); return; } @@ -14680,6 +14681,7 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app) SetFeigned(false); BindWound(this, false, true); camp_timer.Disable(); + bot_camp_timer.Disable(); } else if (sa->parameter == Animation::Sitting) { SetAppearance(eaSitting); diff --git a/zone/client_process.cpp b/zone/client_process.cpp index c2e20fe5b6..a1fb7080ac 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -193,6 +193,10 @@ bool Client::Process() { return false; //delete client } + if (bot_camp_timer.Check()) { + Bot::BotOrderCampAll(this); + } + if (camp_timer.Check()) { Raid *myraid = entity_list.GetRaidByClient(this); if (myraid) { From 11859769a62401100ad10bb8a894663ce3391a4f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 13 Nov 2024 22:19:11 -0600 Subject: [PATCH 127/394] Make command errors/failures yellow --- zone/bot_command.cpp | 46 ++++++++++++++++++++++---------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index b47edd5e34..81f7b21a12 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1237,7 +1237,7 @@ LinkedList cleanup_bot_command_list; int bot_command_not_avail(Client *c, const char *message) { - c->Message(Chat::White, "Bot commands not available."); + c->Message(Chat::Yellow, "Bot commands not available."); return -1; } @@ -1540,7 +1540,7 @@ int bot_command_real_dispatch(Client *c, const char *message) BotCommandRecord *cur = bot_command_list[cstr]; if(c->Admin() < cur->access){ - c->Message(Chat::White, "Your access level is not high enough to use this bot command."); + c->Message(Chat::Yellow, "Your access level is not high enough to use this bot command."); return(-1); } @@ -1569,13 +1569,13 @@ bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, BCEnum::AFType f { switch (fail_type) { case BCEnum::AFT_Value: - bot_owner->Message(Chat::White, "Failed to change '%s' for %s due to invalid value for this command", type_desc, my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid value for this command", type_desc, my_bot->GetCleanName()); return true; case BCEnum::AFT_GenderRace: - bot_owner->Message(Chat::White, "Failed to change '%s' for %s due to invalid bot gender and/or race for this command", type_desc, my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot gender and/or race for this command", type_desc, my_bot->GetCleanName()); return true; case BCEnum::AFT_Race: - bot_owner->Message(Chat::White, "Failed to change '%s' for %s due to invalid bot race for this command", type_desc, my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot race for this command", type_desc, my_bot->GetCleanName()); return true; default: return false; @@ -1587,7 +1587,7 @@ void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot) if (!MyBots::IsMyBot(bot_owner, my_bot)) return; if (!my_bot->Save()) { - bot_owner->Message(Chat::White, "Failed to save appearance change for %s due to unknown cause...", my_bot->GetCleanName()); + bot_owner->Message(Chat::Yellow, "Failed to save appearance change for %s due to unknown cause...", my_bot->GetCleanName()); return; } @@ -1631,7 +1631,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas if (!Bot::IsValidName(bot_name)) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z' and it must be between 4 and 15 characters. Mixed case {} allowed.", bot_name, RuleB(Bots, AllowCamelCaseNames) ? "is" : "is not" @@ -1643,7 +1643,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bool available_flag = false; if (!database.botdb.QueryNameAvailablity(bot_name, available_flag)) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "'{}' is already in use or an invalid name.", bot_name @@ -1654,7 +1654,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas if (!available_flag) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "The name '{}' is already being used. Please choose a different name", bot_name @@ -1668,7 +1668,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas const std::string bot_class_name = GetClassIDName(bot_class); bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "{} {} is an invalid race-class combination, would you like to {} proper combinations for {}?", bot_race_name, @@ -1704,7 +1704,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas uint32 bot_count = 0; uint32 bot_class_count = 0; if (!database.botdb.QueryBotCount(bot_owner->CharacterID(), bot_class, bot_count, bot_class_count)) { - bot_owner->Message(Chat::White, "Failed to query bot count."); + bot_owner->Message(Chat::Yellow, "Failed to query bot count."); return bot_id; } @@ -1721,7 +1721,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas message = "You cannot create any bots."; } - bot_owner->Message(Chat::White, message.c_str()); + bot_owner->Message(Chat::Yellow, message.c_str()); return bot_id; } @@ -1742,7 +1742,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas ); } - bot_owner->Message(Chat::White, message.c_str()); + bot_owner->Message(Chat::Yellow, message.c_str()); return bot_id; } @@ -1753,7 +1753,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->GetLevel() < bot_character_level ) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "You must be level {} to use bots.", bot_character_level @@ -1769,7 +1769,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->GetLevel() < bot_character_level_class ) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "You must be level {} to use {} bots.", bot_character_level_class, @@ -1784,7 +1784,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas if (!my_bot->Save()) { bot_owner->Message( - Chat::White, + Chat::Yellow, fmt::format( "Failed to create '{}' due to unknown cause.", my_bot->GetCleanName() @@ -1918,7 +1918,7 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool helper_command_disabled(Client* bot_owner, bool rule_value, const char* command) { if (!rule_value) { - bot_owner->Message(Chat::White, "Bot command %s is not enabled on this server.", command); + bot_owner->Message(Chat::Yellow, "Bot command %s is not enabled on this server.", command); return true; } @@ -1929,7 +1929,7 @@ bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, c { auto alias_iter = bot_command_aliases.find(&alias[1]); if (alias_iter == bot_command_aliases.end() || alias_iter->second.compare(command)) { - bot_owner->Message(Chat::White, "Undefined linker usage in %s (%s)", command_handler, &alias[1]); + bot_owner->Message(Chat::Yellow, "Undefined linker usage in %s (%s)", command_handler, &alias[1]); return true; } @@ -1951,12 +1951,12 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b } if (!druid_bot && !wizard_bot) { - bot_owner->Message(Chat::White, "No bots are capable of performing this action"); + bot_owner->Message(Chat::Yellow, "No bots are capable of performing this action"); return; } if (!local_list) { - bot_owner->Message(Chat::White, "There are no destinations you can be taken to."); + bot_owner->Message(Chat::Yellow, "There are no destinations you can be taken to."); return; } @@ -2030,7 +2030,7 @@ void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_b } if (!destination_count) { - bot_owner->Message(Chat::White, "There are no destinations you can be taken to."); + bot_owner->Message(Chat::Yellow, "There are no destinations you can be taken to."); } } @@ -2049,7 +2049,7 @@ bool helper_no_available_bots(Client *bot_owner, Bot *my_bot) if (!bot_owner) return true; if (!my_bot) { - bot_owner->Message(Chat::White, "No bots are capable of performing this action"); + bot_owner->Message(Chat::Yellow, "No bots are capable of performing this action"); return true; } @@ -2108,7 +2108,7 @@ bool helper_spell_check_fail(STBaseEntry* local_entry) bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type) { if (!spell_list || spell_list->empty()) { - bot_owner->Message(Chat::White, "%s", required_bots_map[spell_type].c_str()); + bot_owner->Message(Chat::Yellow, "%s", required_bots_map[spell_type].c_str()); return true; } From dbacd807604d2e9fc833e998c6bcff393c5a5784 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:08:57 -0600 Subject: [PATCH 128/394] Add more checks to bot names to prevent spacing or invalid characters --- zone/bot.cpp | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 139bcbbd91..b4679b396b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1323,7 +1323,7 @@ bool Bot::IsValidName() bool Bot::IsValidName(std::string& name) { - if (name.length() < 4 || name.length() > 15) { + if (name.empty() || name.length() < 4 || name.length() > 15) { return false; } @@ -1332,10 +1332,15 @@ bool Bot::IsValidName(std::string& name) } for (char c : name.substr(1)) { - if (!RuleB(Bots, AllowCamelCaseNames) && !islower(c)) { + if (c == '_') { + return false; + } + + if (!isalpha(c)) { return false; } - if (isdigit(c) || ispunct(c)) { + + if (!RuleB(Bots, AllowCamelCaseNames) && !islower(c)) { return false; } } From aa7632d3df952d02fa79010237758703aa02f062 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 14 Nov 2024 20:09:19 -0600 Subject: [PATCH 129/394] Add AllowBotEquipAnyClassGear to bot trades --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b4679b396b..75e990d7dd 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4209,7 +4209,7 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* } if ( - !trade_instance->IsClassEquipable(GetClass()) || + (!trade_instance->IsClassEquipable(GetClass()) && !RuleB(Bots, AllowBotEquipAnyClassGear))|| GetLevel() < trade_instance->GetItem()->ReqLevel || (!trade_instance->IsRaceEquipable(GetBaseRace()) && !RuleB(Bots, AllowBotEquipAnyRaceGear)) ) { From 19376c4957e57b4d1af2a70e73f1457f89016dd6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 15 Nov 2024 13:26:00 -0600 Subject: [PATCH 130/394] update and expand ^itemuse options and add lore checks --- zone/bot_commands/item_use.cpp | 224 ++++++++++++++++++++++++--------- 1 file changed, 168 insertions(+), 56 deletions(-) diff --git a/zone/bot_commands/item_use.cpp b/zone/bot_commands/item_use.cpp index 975d30b90d..0db5983dcb 100644 --- a/zone/bot_commands/item_use.cpp +++ b/zone/bot_commands/item_use.cpp @@ -4,16 +4,17 @@ void bot_command_item_use(Client* c, const Seperator* sep) { if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: [%s empty] will display only bots that can use the item in an empty slot.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s byclass classID] - Example: [%s byclass 7] will display only bots that match the class that can use the item. Example is a Monk, use [^create help] for a list of class IDs.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "usage: [%s casteronly] will display only caster bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s hybridonly] will display only hybrid bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s meleeonly] will display only melee bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s wiscasteronly] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s intcasteronly] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s plateonly] will display only Plate-wearing bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s chainonly] will display only Chain-wearing bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s leatheronly] will display only Leather-wearing bots that can use the item.", sep->arg[0]); - c->Message(Chat::White, "usage: [%s clothonly] will display only Cloth-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s caster] will display only caster bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s hybrid] will display only hybrid bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s melee] will display only melee bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s wiscaster] will display only Wisdom-based Caster bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s intcaster] will display only Intelligence-based Caster bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s plate] will display only Plate-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s chain] will display only Chain-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s leather] will display only Leather-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s cloth] will display only Cloth-wearing bots that can use the item.", sep->arg[0]); + c->Message(Chat::White, "usage: [%s haste] will display bots that have no or lesser haste than the item.", sep->arg[0]); + c->Message(Chat::White, "usage: You can also use empty or haste as an argument to narrow down further, for example [%s caster empty], [%s plate haste] or even [%s empty haste]", sep->arg[0], sep->arg[0], sep->arg[0]); return; } @@ -28,136 +29,252 @@ void bot_command_item_use(Client* c, const Seperator* sep) bool chain_only = false; bool leather_only = false; bool cloth_only = false; + bool haste_only = false; + int haste_value = 0; + int ab_arg = 2; std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; - if (arg1.compare("empty") == 0) { + + if (arg1.compare("empty") == 0 || arg2.compare("empty") == 0) { empty_only = true; + + if (arg2.compare("empty") == 0) { + ++ab_arg; + } } - else if (arg1.compare("byclass") == 0) { - if (Strings::IsNumber(sep->arg[2])) { - class_mask = Strings::ToUnsignedInt(sep->arg[2]); - if (!(class_mask >= Class::Warrior && class_mask <= Class::Berserker)) { - c->Message(Chat::White, "Invalid class range, you must choose between 1 (Warrior) and 15 (Beastlord)"); - return; - } + + if (arg1.compare("haste") == 0 || arg2.compare("haste") == 0) { + haste_only = true; + + if (arg2.compare("haste") == 0) { + ++ab_arg; } } - else if (arg1.compare("casteronly") == 0) { + + if (arg1.compare("caster") == 0) { caster_only = true; } - else if (arg1.compare("hybridonly") == 0) { + else if (arg1.compare("hybrid") == 0) { hybrid_only = true; } - else if (arg1.compare("meleeonly") == 0) { + else if (arg1.compare("melee") == 0) { melee_only = true; } - else if (arg1.compare("wiscasteronly") == 0) { + else if (arg1.compare("wiscaster") == 0) { wis_caster_only = true; } - else if (arg1.compare("intcasteronly") == 0) { + else if (arg1.compare("intcaster") == 0) { int_caster_only = true; } - else if (arg1.compare("plateonly") == 0) { + else if (arg1.compare("plate") == 0) { plate_only = true; } - else if (arg1.compare("chainonly") == 0) { + else if (arg1.compare("chain") == 0) { chain_only = true; } - else if (arg1.compare("leatheronly") == 0) { + else if (arg1.compare("leather") == 0) { leather_only = true; } - else if (arg1.compare("clothonly") == 0) { + else if (arg1.compare("cloth") == 0) { cloth_only = true; } - else if (!arg1.empty()) { - c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]); - return; + else { + if (arg1.empty()) { + --ab_arg; + } + else { + if (!(arg1.compare("empty") == 0) && !(arg1.compare("haste") == 0)) { + c->Message(Chat::White, "Please choose the correct subtype. For help use %s help.", sep->arg[0]); + + return; + } + } } + const auto item_instance = c->GetInv().GetItem(EQ::invslot::slotCursor); + if (!item_instance) { - c->Message(Chat::White, "No item found on cursor!"); + c->Message(Chat::Yellow, "No item found on cursor! For help use %s help.", sep->arg[0]); + return; } auto item_data = item_instance->GetItem(); + if (!item_data) { - c->Message(Chat::White, "No data found for cursor item!"); + c->Message(Chat::Yellow, "No data found for cursor item!"); + return; } if (item_data->ItemClass != EQ::item::ItemClassCommon || item_data->Slots == 0) { - c->Message(Chat::White, "'%s' is not an equipable item!", item_data->Name); + c->Message(Chat::Yellow, "'%s' is not an equipable item!", item_data->Name); + return; } std::vector equipable_slot_list; + for (int16 equipable_slot = EQ::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQ::invslot::EQUIPMENT_END; ++equipable_slot) { if (item_data->Slots & (1 << equipable_slot)) { equipable_slot_list.emplace_back(equipable_slot); } } - EQ::SayLinkEngine linker; - linker.SetLinkType(EQ::saylink::SayLinkItemInst); + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - if (class_mask) { - ActionableBots::Filter_ByClasses(c, sbl, GetPlayerClassBit(class_mask)); + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; } + sbl.remove(nullptr); + + EQ::SayLinkEngine linker; + linker.SetLinkType(EQ::saylink::SayLinkItemData); + for (const auto& bot_iter : sbl) { if (!bot_iter) { continue; } + if (caster_only && !IsCasterClass(bot_iter->GetClass())) { continue; } + if (hybrid_only && !IsSpellFighterClass(bot_iter->GetClass())) { continue; } + if (melee_only && !IsNonSpellFighterClass(bot_iter->GetClass())) { continue; } + if (wis_caster_only && !IsWISCasterClass(bot_iter->GetClass())) { continue; } + if (int_caster_only && !IsINTCasterClass(bot_iter->GetClass())) { continue; } + if (plate_only && !IsPlateClass(bot_iter->GetClass())) { continue; } + if (chain_only && !IsChainClass(bot_iter->GetClass())) { continue; } + if (leather_only && !IsLeatherClass(bot_iter->GetClass())) { continue; } + if (cloth_only && !IsClothClass(bot_iter->GetClass())) { continue; } - if (((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace())) || ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) { + + if ( + (!RuleB(Bots, AllowBotEquipAnyRaceGear) && ((~item_data->Races) & GetPlayerRaceBit(bot_iter->GetRace()))) || + (!RuleB(Bots, AllowBotEquipAnyClassGear) && ((~item_data->Classes) & GetPlayerClassBit(bot_iter->GetClass()))) + ) { continue; } + std::list refined_equipable_slot_list; + bool skip_bot = false; + const EQ::ItemData* equipped_item = nullptr; + const EQ::ItemInstance* equipped_inst = nullptr; + for (const auto& slot_iter : equipable_slot_list) { // needs more failure criteria - this should cover the bulk for now if (slot_iter == EQ::invslot::slotSecondary && item_data->Damage && !bot_iter->CanThisClassDualWield()) { continue; } - auto equipped_item = bot_iter->GetInv()[slot_iter]; + if (item_data->ReqLevel > bot_iter->GetLevel()) { + continue; + } + + haste_value = 0; + equipped_item = nullptr; + equipped_inst = nullptr; + + + for (int16 equipable_slot = EQ::invslot::EQUIPMENT_BEGIN; equipable_slot <= EQ::invslot::EQUIPMENT_END; ++equipable_slot) { + equipped_inst = bot_iter->GetInv()[equipable_slot]; + if (equipped_inst && equipped_inst->GetItem()) { + equipped_item = equipped_inst->GetItem(); + + if (item_data->CheckLoreConflict(equipped_item)) { + skip_bot = true; + break; + } + + if (haste_only) { + if (equipped_item->Haste > haste_value) { + haste_value = equipped_item->Haste; + } + } + } + } + + if (skip_bot) { + break; + } + + if (haste_only && item_data->Haste < haste_value) { + continue; + } + + equipped_inst = bot_iter->GetInv()[slot_iter]; + + if (equipped_inst && empty_only) { + continue; + } + + refined_equipable_slot_list.push_back(slot_iter); + } + + if (skip_bot) { + continue; + } + + if (refined_equipable_slot_list.empty()) { + continue; + } + + for (auto slot_iter : refined_equipable_slot_list) { + equipped_item = nullptr; + equipped_inst = nullptr; - if (equipped_item && !empty_only) { - linker.SetItemInst(equipped_item); + equipped_inst = bot_iter->GetInv()[slot_iter]; + + if (equipped_inst && equipped_inst->GetItem()) { + equipped_item = equipped_inst->GetItem(); + } + + if (equipped_item) { + linker.SetItemData(equipped_item); c->Message( Chat::Say, fmt::format( - "{} says, 'I can use that for my {} instead of my {}! Would you like to {} my {}?'", + "{} says, 'I can use that for my {} instead of my {}! Would you like to {}?'", Saylink::Silent( fmt::format( "^inventorygive byname {}", @@ -173,21 +290,16 @@ void bot_command_item_use(Client* c, const Seperator* sep) slot_iter, bot_iter->GetCleanName() ), - "remove" - ), - linker.GenerateLink() + "remove my item" + ) ).c_str() ); - - if (RuleB(Bots, DoResponseAnimations)) { - bot_iter->DoAnim(29); - } } - else if (!equipped_item) { + else { c->Message( Chat::Say, fmt::format( - "{} says, 'I can use that for my {}! Would you like to {} it to me?'", + "{} says, 'I can use that for my {}! Would you like to {}?'", Saylink::Silent( fmt::format( "^inventorygive byname {}", @@ -201,14 +313,14 @@ void bot_command_item_use(Client* c, const Seperator* sep) "^inventorygive byname {}", bot_iter->GetCleanName() ), - "give" + "give it to me" ) ).c_str() ); + } - if (RuleB(Bots, DoResponseAnimations)) { - bot_iter->DoAnim(29); - } + if (RuleB(Bots, DoResponseAnimations)) { + bot_iter->DoAnim(29); } } } From 484b88ddbc911b1db41c9be1a00430b15417b4dd Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:09:08 -0600 Subject: [PATCH 131/394] misc cleanup --- zone/bot.cpp | 220 +++++++++++++++++++++---------------------- zone/bot.h | 22 ++--- zone/botspellsai.cpp | 18 ++-- 3 files changed, 130 insertions(+), 130 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 75e990d7dd..370e689d9e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9395,14 +9395,14 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return true; } -bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { +bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { if (!tar) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme return false; } if (doPrechecks) { - if (spells[spellid].target_type == ST_Self && tar != this) { + if (spells[spell_id].target_type == ST_Self && tar != this) { tar = this; } @@ -9414,105 +9414,105 @@ bool Bot::CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrecheck LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme - if (!IsValidSpell(spellid)) { + if (!IsValidSpell(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme return false; } - if (spells[spellid].target_type == ST_Self && tar != this) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (spells[spell_id].target_type == ST_Self && tar != this) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!CheckSpellRecastTimer(spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (!CheckSpellRecastTimer(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (!BotHasEnoughMana(spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (!BotHasEnoughMana(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (zone->IsSpellBlocked(spellid, glm::vec3(GetPosition()))) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (!zone->CanLevitate() && IsEffectInSpell(spellid, SE_Levitate)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (spells[spellid].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (spells[spellid].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (spells[spellid].zone_type == 1 && !zone->CanCastOutdoor()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spellid)); //deleteme + if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } - if (!AECheck && !IsValidSpellRange(spellid, tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), tar->GetBodyType())) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (tar->IsImmuneToBotSpell(spellid, this)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (tar->IsImmuneToBotSpell(spell_id, this)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!DoResistCheckBySpellType(tar, spellid, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spellid) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if ( - (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spellid) != 0)) + (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spell_id) != 0)) && - tar->CanBuffStack(spellid, GetLevel(), true) < 0 + tar->CanBuffStack(spell_id, GetLevel(), true) < 0 ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (IsBeneficialSpell(spellid) && tar->BuffCount() >= tar->GetCurrentBuffSlots() && CalcBuffDuration(this, tar, spellid) != 0) { + if (IsBeneficialSpell(spell_id) && tar->BuffCount() >= tar->GetCurrentBuffSlots() && CalcBuffDuration(this, tar, spell_id) != 0) { return false; } - LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme - if (!CanCastSpellType(spellType, spellid, tar)) { + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!CanCastSpellType(spellType, spell_id, tar)) { return false; } return true; } -bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { - if (!spellid || !tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spellid ? GetSpellName(spellid) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme +bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { + if (!spell_id || !tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme return false; } @@ -9529,44 +9529,44 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PetResistBuffs: if ( !( - spells[spellid].target_type == ST_Target || - spells[spellid].target_type == ST_Pet || - (tar == this && spells[spellid].target_type != ST_TargetsTarget) || - spells[spellid].target_type == ST_Group || - spells[spellid].target_type == ST_GroupTeleport + spells[spell_id].target_type == ST_Target || + spells[spell_id].target_type == ST_Pet || + (tar == this && spells[spell_id].target_type != ST_TargetsTarget) || + spells[spell_id].target_type == ST_Group || + spells[spell_id].target_type == ST_GroupTeleport ) ) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (tar->IsBlockedBuff(spellid)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (tar->IsBlockedBuff(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (IsEffectInSpell(spellid, SE_Teleport) || IsEffectInSpell(spellid, SE_Succor)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spellid, SE_Illusion)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spell_id, SE_Illusion)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (spells[spellid].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (spells[spell_id].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if ((IsGroupSpell(spellid) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if ((IsGroupSpell(spell_id) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9576,21 +9576,21 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { if ( tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( - IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS)) || + (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; case Archetype::Melee: if ( - IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || - IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || - IsEffectInSpell(spellid, SE_CurrentMana) + IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || IsEffectInSpell(spell_id, SE_ManaPool) || + IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || + IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9602,14 +9602,14 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { // Differences for each type if (spellType != BotSpellTypes::InCombatBuff) { - if (IsEffectInSpell(spellid, SE_AbsorbMagicAtt) || IsEffectInSpell(spellid, SE_Rune)) { + if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { for (int i = 0; i < tar->GetMaxTotalSlots(); i++) { uint32 buff_count = tar->GetMaxTotalSlots(); for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { if (IsLichSpell(tar->GetBuffs()[j].spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } } @@ -9622,8 +9622,8 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9633,21 +9633,21 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { if ( tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && ( - IsEffectInSpell(spellid, SE_AttackSpeed) || IsEffectInSpell(spellid, SE_ReverseDS)) || - (SpellEffectsCount(spellid) == 1 && (IsEffectInSpell(spellid, SE_ATK) || IsEffectInSpell(spellid, SE_STR)) + IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS)) || + (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; case Archetype::Melee: if ( - IsEffectInSpell(spellid, SE_IncreaseSpellHaste) || IsEffectInSpell(spellid, SE_ManaPool) || - IsEffectInSpell(spellid, SE_CastingLevel) || IsEffectInSpell(spellid, SE_ManaRegen_v2) || - IsEffectInSpell(spellid, SE_CurrentMana) + IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || IsEffectInSpell(spell_id, SE_ManaPool) || + IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || + IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9662,7 +9662,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar) { break; } - LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spellid), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return true; } @@ -9681,9 +9681,9 @@ bool Bot::BotHasEnoughMana(uint16 spell_id) { return true; } -bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { +bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { - if (!tar || !spellid) { + if (!tar || !spell_id) { return true; } @@ -9706,18 +9706,18 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { m->IsCasting() && m->CastToBot()->casting_spell_targetid && entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(tar->GetID()) && - m->CastingSpellID() == spellid + m->CastingSpellID() == spell_id ) { return true; } else { - if (IsGroupSpell(spellid)) { + if (IsGroupSpell(spell_id)) { if ( m->IsBot() && m->IsCasting() && m->CastToBot()->casting_spell_targetid && - m->CastingSpellID() == spellid + m->CastingSpellID() == spell_id ) { std::vector x = GatherGroupSpellTargets(); @@ -9735,13 +9735,13 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid) { return false; } -bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { +bool Bot::DoResistCheck(Mob* tar, uint16 spell_id, int32 resist_limit) { - if (!tar || spellid == 0) { + if (!tar || spell_id == 0) { return false; } - int32 resist_difficulty = -spells[spellid].resist_difficulty; + int32 resist_difficulty = -spells[spell_id].resist_difficulty; int32 level_mod = (tar->GetLevel() - GetLevel()) * (tar->GetLevel() - GetLevel()) / 2; if (tar->GetLevel() - GetLevel() < 0) { @@ -9750,7 +9750,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { int32 targetResist = 0; - switch (GetSpellResistType(spellid)) { + switch (GetSpellResistType(spell_id)) { case RESIST_NONE: return true; case RESIST_MAGIC: @@ -9774,7 +9774,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { default: return true; } - //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spellid), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) + //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) if ((targetResist + level_mod - resist_difficulty) > resist_limit) { return false; } @@ -9782,8 +9782,8 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spellid, int32 resist_limit) { return true; } -bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType) { - if (!tar || !IsValidSpell(spellid)) { +bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType) { + if (!tar || !IsValidSpell(spell_id)) { return false; } @@ -9791,11 +9791,11 @@ bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType) { return true; } - return DoResistCheck(tar, spellid, GetSpellTypeResistLimit(spellType)); + return DoResistCheck(tar, spell_id, GetSpellTypeResistLimit(spellType)); } -bool Bot::IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType) { - if (!spellid) { +bool Bot::IsValidTargetType(uint16 spell_id, int targetType, uint8 bodyType) { + if (!spell_id) { return false; } @@ -9879,7 +9879,7 @@ bool Bot::IsMobEngagedByAnyone(Mob* tar) { return false; } -bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid) { +bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id) { if (npc->GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { return false; } @@ -9896,7 +9896,7 @@ bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid) { return false; } - if (!IsValidTargetType(spellid, GetSpellTargetType(spellid), npc->GetBodyType())) { + if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), npc->GetBodyType())) { return false; } @@ -10941,58 +10941,58 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { return spellType; } -bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid) { - if (IsAEBotSpellType(spellType) && !IsAnyAESpell(spellid)) { +bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { + if (IsAEBotSpellType(spellType) && !IsAnyAESpell(spell_id)) { return false; } - if (IsGroupBotSpellType(spellType) && !IsGroupSpell(spellid)) { + if (IsGroupBotSpellType(spellType) && !IsGroupSpell(spell_id)) { return false; } switch (spellType) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: - if (IsResistanceOnlySpell(spellid) || IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { + if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { return false; } return true; case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: - if (IsResistanceOnlySpell(spellid)) { + if (IsResistanceOnlySpell(spell_id)) { return true; } return false; case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - if (IsDamageShieldOnlySpell(spellid) || IsDamageShieldAndResistanceSpellOnly(spellid)) { + if (IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { return true; } return false; case BotSpellTypes::PBAENuke: - if (IsPBAENukeSpell(spellid) && !IsStunSpell(spellid)) { + if (IsPBAENukeSpell(spell_id) && !IsStunSpell(spell_id)) { return true; } return false; case BotSpellTypes::AERains: - if (IsAERainNukeSpell(spellid) && !IsStunSpell(spellid)) { + if (IsAERainNukeSpell(spell_id) && !IsStunSpell(spell_id)) { return true; } return false; case BotSpellTypes::AEStun: case BotSpellTypes::Stun: - if (IsStunSpell(spellid)) { + if (IsStunSpell(spell_id)) { return true; } return false; case BotSpellTypes::AENukes: case BotSpellTypes::Nuke: - if (!IsStunSpell(spellid)) { + if (!IsStunSpell(spell_id)) { return true; } @@ -11188,9 +11188,9 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } -bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar) { - int spellRange = botCaster->GetActSpellRange(spellid, spells[spellid].range); - int spellAERange = botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range); +bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar) { + int spellRange = botCaster->GetActSpellRange(spell_id, spells[spell_id].range); + int spellAERange = botCaster->GetActSpellRange(spell_id, spells[spell_id].aoe_range); int targetCount = 0; for (auto& close_mob : botCaster->m_close_mobs) { @@ -11233,14 +11233,14 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob continue; } - if (SpellBreaksMez(spellid) && m->IsMezzed()) { + if (SpellBreaksMez(spell_id) && m->IsMezzed()) { continue; } - if (IsPBAESpell(spellid)) { + if (IsPBAESpell(spell_id)) { if ( spellAERange >= Distance(botCaster->GetPosition(), m->GetPosition()) && - botCaster->CastChecks(spellid, m, spellType, true, true) + botCaster->CastChecks(spell_id, m, spellType, true, true) ) { ++targetCount; } @@ -11252,7 +11252,7 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob if ( spellAERange >= Distance(tar->GetPosition(), m->GetPosition()) && - botCaster->CastChecks(spellid, m, spellType, true, true) + botCaster->CastChecks(spell_id, m, spellType, true, true) ) { ++targetCount; } diff --git a/zone/bot.h b/zone/bot.h index ff3c7c1296..b3f2ae1712 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -445,13 +445,13 @@ class Bot : public NPC { std::vector GatherSpellTargets(bool entireRaid = false, bool noClients = false, bool noBots = false, bool noPets = false); bool PrecastChecks(Mob* tar, uint16 spellType); - bool CastChecks(uint16 spellid, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); - bool CanCastSpellType(uint16 spellType, uint16 spellid, Mob* tar); + bool CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); + bool CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar); bool BotHasEnoughMana(uint16 spell_id); - bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spellid); - bool DoResistCheck(Mob* target, uint16 spellid, int32 resist_limit); - bool DoResistCheckBySpellType(Mob* tar, uint16 spellid, uint16 spellType); - bool IsValidTargetType(uint16 spellid, int targetType, uint8 bodyType); + bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); + bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); + bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType); + bool IsValidTargetType(uint16 spell_id, int targetType, uint8 bodyType); bool IsMobEngagedByAnyone(Mob* tar); void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); void CopySettings(Bot* to, uint8 settingType, uint16 spellType = UINT16_MAX); @@ -522,11 +522,11 @@ class Bot : public NPC { std::list GetSpellTypesPrioritized(uint8 priorityType); uint16 GetSpellListSpellType(uint16 spellType); - bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spellid); + bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spellType); - bool HasValidAETarget(Bot* botCaster, uint16 spellid, uint16 spellType, Mob* tar); + bool HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar); void CheckBotSpells(); @@ -592,8 +592,8 @@ class Bot : public NPC { static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE = false); - bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spellid); + static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE = false); + bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Pet); static std::string GetBotMagicianPetType(Bot* botCaster); @@ -735,7 +735,7 @@ class Bot : public NPC { inline const InspectMessage_Struct& GetInspectMessage() const { return _botInspectMessage; } // "Quest API" Methods - bool HasBotSpellEntry(uint16 spellid); + bool HasBotSpellEntry(uint16 spell_id); void ApplySpell(int spell_id, int duration = 0, int level = -1, ApplySpellType apply_type = ApplySpellType::Solo, bool allow_pets = false, bool is_raid_group_only = true); void BreakInvis(); void Escape(); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 5adbdb3e98..0138d58ad7 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1429,11 +1429,11 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster, uint16 spellType) { return result; } -Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellType, bool AE) { +Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE) { Mob* result = nullptr; if (botCaster && botCaster->GetOwner()) { - int spellRange = (!AE ? botCaster->GetActSpellRange(spellid, spells[spellid].range) : botCaster->GetActSpellRange(spellid, spells[spellid].aoe_range)); + int spellRange = (!AE ? botCaster->GetActSpellRange(spell_id, spells[spell_id].range) : botCaster->GetActSpellRange(spell_id, spells[spell_id].aoe_range)); int buff_count = 0; NPC* npc = nullptr; @@ -1445,7 +1445,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy continue; } - if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), npc, spellid)) { + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), npc, spell_id)) { continue; } @@ -1459,11 +1459,11 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy continue; } - if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), m, spellid)) { + if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), m, spell_id)) { continue; } - if (IsPBAESpell(spellid)) { + if (IsPBAESpell(spell_id)) { if (spellRange < Distance(botCaster->GetPosition(), m->GetPosition())) { continue; } @@ -1474,7 +1474,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy } } - if (botCaster->CastChecks(spellid, m, spellType, true, true)) { + if (botCaster->CastChecks(spell_id, m, spellType, true, true)) { ++targetCount; } @@ -1499,7 +1499,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spellid, uint16 spellTy continue; } - if (!botCaster->CastChecks(spellid, npc, spellType, true)) { + if (!botCaster->CastChecks(spell_id, npc, spellType, true)) { continue; } @@ -2631,7 +2631,7 @@ void Bot::AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot) { } } -bool Bot::HasBotSpellEntry(uint16 spellid) { +bool Bot::HasBotSpellEntry(uint16 spell_id) { auto* spell_list = content_db.GetBotSpells(GetBotSpellID()); if (!spell_list) { @@ -2640,7 +2640,7 @@ bool Bot::HasBotSpellEntry(uint16 spellid) { // Check if Spell ID is found in Bot Spell Entries for (auto& e : spell_list->entries) { - if (spellid == e.spellid) { + if (spell_id == e.spellid) { return true; } } From 4702d74722a69e19c955c7168cc873443b79ef49 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:16:58 -0600 Subject: [PATCH 132/394] fix resistbuffs and damageshields spell type checks --- common/spdat.cpp | 21 ++------------------- common/spdat.h | 1 - zone/bot.cpp | 4 ++-- 3 files changed, 4 insertions(+), 22 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 8211ecfdcc..7b0e540894 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3214,14 +3214,6 @@ bool IsResistanceOnlySpell(uint16 spell_id) { } bool IsDamageShieldOnlySpell(uint16 spell_id) { - if (SpellEffectsCount(spell_id) == 1 && IsEffectInSpell(spell_id, SE_DamageShield)) { - return true; - } - - return false; -} - -bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id) { if (!IsValidSpell(spell_id)) { return false; } @@ -3234,19 +3226,10 @@ bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id) { } if ( - spell.effect_id[i] == SE_DamageShield || - spell.effect_id[i] == SE_ResistFire || - spell.effect_id[i] == SE_ResistCold || - spell.effect_id[i] == SE_ResistPoison || - spell.effect_id[i] == SE_ResistDisease || - spell.effect_id[i] == SE_ResistMagic || - spell.effect_id[i] == SE_ResistCorruption || - spell.effect_id[i] == SE_ResistAll + spell.effect_id[i] != SE_DamageShield ) { - continue; + return false; } - - return false; } return true; diff --git a/common/spdat.h b/common/spdat.h index 60257a4014..4c1b52ecaf 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1726,6 +1726,5 @@ bool IsResurrectSpell(uint16 spell_id); bool RequiresStackCheck(uint16 spellType); bool IsResistanceOnlySpell(uint16 spell_id); bool IsDamageShieldOnlySpell(uint16 spell_id); -bool IsDamageShieldAndResistanceSpellOnly(uint16 spell_id); #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 370e689d9e..ece357a231 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10953,7 +10953,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { switch (spellType) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: - if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { + if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id)) { return false; } @@ -10967,7 +10967,7 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { return false; case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - if (IsDamageShieldOnlySpell(spell_id) || IsDamageShieldAndResistanceSpellOnly(spell_id)) { + if (IsDamageShieldOnlySpell(spell_id)) { return true; } From 3d67009de580663d885a44abc65d08e53dddb0bf Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 11:18:11 -0600 Subject: [PATCH 133/394] Command help cleanup --- common/ruletypes.h | 1 + zone/bot.h | 2 - zone/bot_command.cpp | 79 ++++++++++++-------- zone/bot_command.h | 4 + zone/bot_commands/behind_mob.cpp | 22 +++--- zone/bot_commands/cast.cpp | 13 +++- zone/bot_commands/copy_settings.cpp | 24 +++--- zone/bot_commands/default_settings.cpp | 24 +++--- zone/bot_commands/illusion_block.cpp | 22 +++--- zone/bot_commands/max_melee_range.cpp | 22 +++--- zone/bot_commands/sit_hp_percent.cpp | 22 +++--- zone/bot_commands/sit_in_combat.cpp | 22 +++--- zone/bot_commands/sit_mana_percent.cpp | 22 +++--- zone/bot_commands/spell_aggro_checks.cpp | 25 +++---- zone/bot_commands/spell_delays.cpp | 25 +++---- zone/bot_commands/spell_engaged_priority.cpp | 25 +++---- zone/bot_commands/spell_holds.cpp | 25 +++---- zone/bot_commands/spell_idle_priority.cpp | 25 +++---- zone/bot_commands/spell_max_hp_pct.cpp | 25 +++---- zone/bot_commands/spell_max_mana_pct.cpp | 25 +++---- zone/bot_commands/spell_max_thresholds.cpp | 25 +++---- zone/bot_commands/spell_min_hp_pct.cpp | 25 +++---- zone/bot_commands/spell_min_mana_pct.cpp | 25 +++---- zone/bot_commands/spell_min_thresholds.cpp | 25 +++---- zone/bot_commands/spell_pursue_priority.cpp | 25 +++---- zone/bot_commands/spell_target_count.cpp | 25 +++---- zone/bot_commands/spelltypes.cpp | 9 +++ 27 files changed, 299 insertions(+), 314 deletions(-) create mode 100644 zone/bot_commands/spelltypes.cpp diff --git a/common/ruletypes.h b/common/ruletypes.h index 9251a197fb..8008683d99 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -878,6 +878,7 @@ RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be s RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") +RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.h b/zone/bot.h index b3f2ae1712..8e85448e08 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -517,8 +517,6 @@ class Bot : public NPC { void SetManaWhenToMed(uint8 value) { _ManaWhenToMed = value; } void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } bool HasLoS() const { return _hasLoS; } - - void SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt = false); std::list GetSpellTypesPrioritized(uint8 priorityType); uint16 GetSpellListSpellType(uint16 spellType); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 81f7b21a12..15ea7eba8a 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1375,6 +1375,8 @@ int bot_command_init(void) bot_command_add("spellsettingstoggle", "Toggle a bot spell use", AccountStatus::Player, bot_command_spell_settings_toggle) || bot_command_add("spellsettingsupdate", "Update a bot spell setting entry", 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("spelltypeids", "Lists spelltypes by ID", AccountStatus::Player, bot_command_spelltype_ids) || + bot_command_add("spelltypenames", "Lists spelltypes by shortname", AccountStatus::Player, bot_command_spelltype_names) || 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) || bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || @@ -2115,43 +2117,54 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp return false; } -void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, std::string arg2, bool helpPrompt) { - if (helpPrompt) { - c->Message( - Chat::Yellow, - fmt::format( - "Use {}, {}, {} for a list of spell types by ID", - Saylink::Silent( - fmt::format("{} listid 0-19", arg0) - ), - Saylink::Silent( - fmt::format("{} listid 20-39", arg0) - ), - Saylink::Silent( - fmt::format("{} listid 40+", arg0) - ) - ).c_str() - ); +void SendSpellTypePrompts(Client *c, bool commandedTypes) { + c->Message( + Chat::Yellow, + fmt::format( + "You can view spell types by ID or shortname: {}, {}, {} / {}, {}, {}", + Saylink::Silent( + fmt::format("^spelltypeids 0-19"), "ID 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypeids 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypeids 40+"), "40+" + ), + Saylink::Silent( + fmt::format("^spelltypenames 0-19"), "Shortname 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypenames 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypenames 40+"), "40+" + ) + ).c_str() + ); + if (commandedTypes) { c->Message( Chat::Yellow, fmt::format( - "Use {}, {}, {} for a list of spell types by short name", + "You can view commanded spell types by ID or shortname: {} / {}", Saylink::Silent( - fmt::format("{} listname 0-19", arg0) + fmt::format("^spelltypeids commanded"), "ID" ), Saylink::Silent( - fmt::format("{} listname 20-39", arg0) - ), - Saylink::Silent( - fmt::format("{} listname 40+", arg0) + fmt::format("^spelltypenames commanded"), "Shortname" ) ).c_str() ); - - return; } + return; +} + +void SendSpellTypeWindow(Client *c, const Seperator* sep) { + std::string arg0 = sep->arg[0]; + std::string arg1 = sep->arg[1]; + uint8 minCount = 0; uint8 maxCount = 0; @@ -2159,18 +2172,22 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st minCount = BotSpellTypes::START; maxCount = BotSpellTypes::END; } - else if (!arg2.compare("0-19")) { + else if (!arg1.compare("0-19")) { minCount = BotSpellTypes::START; maxCount = 19; } - else if (!arg2.compare("20-39")) { + else if (!arg1.compare("20-39")) { minCount = std::min(static_cast(20), static_cast(BotSpellTypes::END)); maxCount = std::min(static_cast(39), static_cast(BotSpellTypes::END)); } - else if (!arg2.compare("40+")) { + else if (!arg1.compare("40+")) { minCount = std::min(static_cast(40), static_cast(BotSpellTypes::END)); maxCount = BotSpellTypes::END; } + else if (!arg1.compare("commanded")) { + minCount = BotSpellTypes::COMMANDED_START; + maxCount = BotSpellTypes::COMMANDED_END; + } else { c->Message(Chat::Yellow, "You must choose a valid range option"); @@ -2199,7 +2216,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st DialogueWindow::TableCell( fmt::format( "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField)) + (!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField)) ) ) ); @@ -2231,7 +2248,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st DialogueWindow::TableCell( fmt::format( "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i))) + (!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i))) ) ) ); @@ -2242,7 +2259,6 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st c->SendPopupToClient("Spell Types", popup_text.c_str()); } - #include "bot_commands/actionable.cpp" #include "bot_commands/aggressive.cpp" #include "bot_commands/appearance.cpp" @@ -2310,6 +2326,7 @@ void Bot::SendSpellTypesWindow(Client* c, std::string arg0, std::string arg1, st #include "bot_commands/spell_min_thresholds.cpp" #include "bot_commands/spell_pursue_priority.cpp" #include "bot_commands/spell_target_count.cpp" +#include "bot_commands/spelltypes.cpp" #include "bot_commands/summon.cpp" #include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 373fbd1e54..8e200bd6b3 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1733,6 +1733,8 @@ 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_spelltype_ids(Client* c, const Seperator* sep); +void bot_command_spelltype_names(Client* c, const Seperator* sep); void bot_spell_info_dialogue_window(Client* c, const Seperator *sep); void bot_command_enforce_spell_list(Client* c, const Seperator* sep); void bot_command_summon_corpse(Client *c, const Seperator *sep); @@ -1820,4 +1822,6 @@ void helper_send_available_subcommands(Client *bot_owner, const char* command_si void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = Class::None); bool helper_spell_check_fail(STBaseEntry* local_entry); bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type); +void SendSpellTypePrompts(Client *c, bool commandedTypes = false); +void SendSpellTypeWindow(Client *c, const Seperator* sep); #endif diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index a7710185f1..3abde3c1ac 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -64,24 +64,22 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index fd7dc5aa83..ab02eb950e 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -94,7 +94,8 @@ void bot_command_cast(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); + SendSpellTypePrompts(c, true); + c->Message( Chat::Yellow, fmt::format( @@ -103,6 +104,16 @@ void bot_command_cast(Client* c, const Seperator* sep) ).c_str() ); + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + return; } diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index e2745442f7..8e41f971a7 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -97,25 +97,23 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + SendSpellTypePrompts(c); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 2; bool validOption = false; uint16 spellType = UINT16_MAX; diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index ddf809a751..8ed6733a68 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -91,25 +91,23 @@ void bot_command_default_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + SendSpellTypePrompts(c); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 2; bool validOption = false; uint16 spellType = UINT16_MAX; diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 94faafc426..47d2d6b264 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -63,13 +63,16 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; @@ -77,11 +80,6 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp index c9e32dec1b..6c4460ac2d 100644 --- a/zone/bot_commands/max_melee_range.cpp +++ b/zone/bot_commands/max_melee_range.cpp @@ -63,24 +63,22 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index fa4a183bf5..38b42ffd99 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -63,24 +63,22 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index 210372f562..eaf08e0ae0 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -63,24 +63,22 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index a5751f9f43..820be6a758 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -63,24 +63,22 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } std::string arg1 = sep->arg[1]; - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); - return; - } - int ab_arg = 1; bool current_check = false; uint32 typeValue = 0; diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index 2dfeef4865..cbcc4706bb 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -92,25 +92,22 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 2a5de10174..9fd37b70a0 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -98,25 +98,22 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 70425ee114..f27387b590 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -96,25 +96,22 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index fd17749d18..e30911bd43 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -82,25 +82,22 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index d6f0a0c359..cdb741d44f 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -96,25 +96,22 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 7f8cd150b4..bcd3614a58 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 44cc7e8d53..84cb9abd9a 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 7ff6515144..385258dddc 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -98,25 +98,22 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 371b448201..be6fe1b9e1 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index b053c462cb..f4ffe1bade 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -92,25 +92,22 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index d690dede25..ffcecf8b74 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -100,25 +100,22 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 872444f322..2721354226 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -96,25 +96,22 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index 2f78a74687..ad1b897b88 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -92,25 +92,22 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], "", "", true); - c->Message( - Chat::Yellow, - fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") - ).c_str() - ); - - return; - } + SendSpellTypePrompts(c); - std::string arg1 = sep->arg[1]; + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); return; } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; diff --git a/zone/bot_commands/spelltypes.cpp b/zone/bot_commands/spelltypes.cpp new file mode 100644 index 0000000000..f91b3ed7e1 --- /dev/null +++ b/zone/bot_commands/spelltypes.cpp @@ -0,0 +1,9 @@ +void bot_command_spelltype_ids(Client* c, const Seperator* sep) +{ + SendSpellTypeWindow(c, sep); +} + +void bot_command_spelltype_names(Client* c, const Seperator* sep) +{ + SendSpellTypeWindow(c, sep); +} From ee9651b4f1ecca6ff7ab2b97e0afbe093c61b95c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 26 Nov 2024 13:17:36 -0600 Subject: [PATCH 134/394] implement commanded cast types --- .../database_update_manifest_bots.cpp | 901 +++++++++++++++--- common/ruletypes.h | 4 + common/spdat.cpp | 57 ++ common/spdat.h | 18 + zone/bot.cpp | 181 ++++ zone/bot.h | 18 +- zone/bot_command.cpp | 38 +- zone/bot_command.h | 15 - zone/bot_commands/bind_affinity.cpp | 42 - zone/bot_commands/cast.cpp | 226 ++++- zone/bot_commands/charm.cpp | 54 -- zone/bot_commands/cure.cpp | 71 -- zone/bot_commands/escape.cpp | 44 - zone/bot_commands/identify.cpp | 38 - zone/bot_commands/invisibility.cpp | 57 -- zone/bot_commands/levitation.cpp | 38 - zone/bot_commands/mesmerize.cpp | 41 - zone/bot_commands/movement_speed.cpp | 50 - zone/bot_commands/resistance.cpp | 73 -- zone/bot_commands/resurrect.cpp | 57 -- zone/bot_commands/rune.cpp | 38 - zone/bot_commands/send_home.cpp | 47 - zone/bot_commands/size.cpp | 50 - zone/bot_commands/water_breathing.cpp | 38 - zone/botspellsai.cpp | 59 +- zone/mob.cpp | 125 +++ zone/mob.h | 3 +- 27 files changed, 1392 insertions(+), 991 deletions(-) delete mode 100644 zone/bot_commands/bind_affinity.cpp delete mode 100644 zone/bot_commands/charm.cpp delete mode 100644 zone/bot_commands/cure.cpp delete mode 100644 zone/bot_commands/escape.cpp delete mode 100644 zone/bot_commands/identify.cpp delete mode 100644 zone/bot_commands/invisibility.cpp delete mode 100644 zone/bot_commands/levitation.cpp delete mode 100644 zone/bot_commands/mesmerize.cpp delete mode 100644 zone/bot_commands/movement_speed.cpp delete mode 100644 zone/bot_commands/resistance.cpp delete mode 100644 zone/bot_commands/resurrect.cpp delete mode 100644 zone/bot_commands/rune.cpp delete mode 100644 zone/bot_commands/send_home.cpp delete mode 100644 zone/bot_commands/size.cpp delete mode 100644 zone/bot_commands/water_breathing.cpp diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index a57a6f0753..96627d8140 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -312,7 +312,7 @@ UPDATE `bot_spells_entries` SET `type` = 21 WHERE `type` = 2097152; .sql = R"( UPDATE bot_spells_entries b, spells_new s SET b.`type` = 22 -WHERE b.spellid = s.id +WHERE b.spell_id = s.id AND ( s.`effectid1` = 23 OR s.`effectid2` = 23 OR @@ -332,177 +332,764 @@ AND ( ManifestEntry{ .version = 9049, .description = "2024_05_18_correct_bot_spell_entries_types.sql", - .check = "SELECT * FROM `bot_spells_entries` where `npc_spells_id` = 3002 AND `spellid` = 14312", + .check = "SELECT * FROM `bot_spells_entries` where `npc_spells_id` = 3002 AND `spell_id` = 14312", .condition = "empty", .match = "", .sql = R"( -- Class fixes -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14312; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14313; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spellid` = 14314; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15186; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15187; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spellid` = 15188; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14446; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14447; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14467; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14468; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14469; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14955; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spellid` = 14956; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14387; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14388; -UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spellid` = 14389; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spell_id` = 14312; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spell_id` = 14313; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3002 WHERE b.`spell_id` = 14314; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spell_id` = 15186; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spell_id` = 15187; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3005 WHERE b.`spell_id` = 15188; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14446; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14447; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14467; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14468; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14469; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spell_id` = 14955; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3003 WHERE b.`spell_id` = 14956; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14387; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14388; +UPDATE bot_spells_entries b SET b.`npc_spells_id` = 3006 WHERE b.`spell_id` = 14389; -- Minlevel fixes -UPDATE bot_spells_entries SET `minlevel` = 34 WHERE `spellid` = 1445 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 2 WHERE `spellid` = 229 AND `npc_spells_id` = 3011; -UPDATE bot_spells_entries SET `minlevel` = 13 WHERE `spellid` = 333 AND `npc_spells_id` = 3013; -UPDATE bot_spells_entries SET `minlevel` = 29 WHERE `spellid` = 106 AND `npc_spells_id` = 3013; -UPDATE bot_spells_entries SET `minlevel` = 38 WHERE `spellid` = 754 AND `npc_spells_id` = 3010; -UPDATE bot_spells_entries SET `minlevel` = 58 WHERE `spellid` = 2589 AND `npc_spells_id` = 3003; -UPDATE bot_spells_entries SET `minlevel` = 67 WHERE `spellid` = 5305 AND `npc_spells_id` = 3004; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 23 WHERE `spellid` = 738 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 51 WHERE `spellid` = 1751 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 7 WHERE `spellid` = 734 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 5 WHERE `spellid` = 717 AND `npc_spells_id` = 3008; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15186 AND `npc_spells_id` = 3005; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15187 AND `npc_spells_id` = 3005; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 15188 AND `npc_spells_id` = 3005; -UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14955 AND `npc_spells_id` = 3003; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14956 AND `npc_spells_id` = 3003; -UPDATE bot_spells_entries SET `minlevel` = 78 WHERE `spellid` = 14387 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14388 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14389 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 34 WHERE `spell_id` = 1445 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 2 WHERE `spell_id` = 229 AND `npc_spells_id` = 3011; +UPDATE bot_spells_entries SET `minlevel` = 13 WHERE `spell_id` = 333 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 29 WHERE `spell_id` = 106 AND `npc_spells_id` = 3013; +UPDATE bot_spells_entries SET `minlevel` = 38 WHERE `spell_id` = 754 AND `npc_spells_id` = 3010; +UPDATE bot_spells_entries SET `minlevel` = 58 WHERE `spell_id` = 2589 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 67 WHERE `spell_id` = 5305 AND `npc_spells_id` = 3004; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 23 WHERE `spell_id` = 738 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 51 WHERE `spell_id` = 1751 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 7 WHERE `spell_id` = 734 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 5 WHERE `spell_id` = 717 AND `npc_spells_id` = 3008; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 15186 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 15187 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 15188 AND `npc_spells_id` = 3005; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spell_id` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 80 WHERE `spell_id` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 79 WHERE `spell_id` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14955 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14956 AND `npc_spells_id` = 3003; +UPDATE bot_spells_entries SET `minlevel` = 78 WHERE `spell_id` = 14387 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14388 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14389 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `minlevel` = 77 WHERE `spell_id` = 14314 AND `npc_spells_id` = 3002; -- Maxlevel fixes -UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14267 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14268 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spellid` = 14269 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14446 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14447 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14467 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14468 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spellid` = 14469 AND `npc_spells_id` = 3006; -UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14312 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14313 AND `npc_spells_id` = 3002; -UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spellid` = 14314 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spell_id` = 14267 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spell_id` = 14268 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 83 WHERE `spell_id` = 14269 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14446 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14447 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14467 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14468 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 84 WHERE `spell_id` = 14469 AND `npc_spells_id` = 3006; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spell_id` = 14312 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spell_id` = 14313 AND `npc_spells_id` = 3002; +UPDATE bot_spells_entries SET `maxlevel` = 81 WHERE `spell_id` = 14314 AND `npc_spells_id` = 3002; -- Type fixes -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 201; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 752; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 2117; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2542; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2544; -UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 2115; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1403; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1405; -UPDATE bot_spells_entries SET `type` = 9 WHERE `spellid` = 289; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 294; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 302; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 521; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 185; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 450; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 186; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 4074; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 195; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1712; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1703; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 3229; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 3345; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5509; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6826; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 281; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 505; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 526; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 110; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 506; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 162; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 111; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 507; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 527; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 163; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 112; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 1588; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1573; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1592; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1577; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1578; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 1576; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3386; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3387; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 4900; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3395; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5394; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 5392; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6827; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 5416; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1437; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 1436; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 5348; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 8008; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 2571; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 370; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1741; -UPDATE bot_spells_entries SET `type` = 17 WHERE `spellid` = 1296; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 270; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2634; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 2942; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 3462; -UPDATE bot_spells_entries SET `type` = 13 WHERE `spellid` = 6828; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14312; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14313; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 14314; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18392; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18393; -UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 18394; -UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 15186; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15187; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 15188; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14446; -UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 14447; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14467; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14468; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14469; -UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14267; -UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14268; -UPDATE bot_spells_entries SET `type` = 0 WHERE `spellid` = 14269; -UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14955; -UPDATE bot_spells_entries SET `type` = 10 WHERE `spellid` = 14956; -UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 14387; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14388; -UPDATE bot_spells_entries SET `type` = 3 WHERE `spellid` = 14389; -UPDATE bot_spells_entries SET `type` = 4 WHERE `spellid` = 10436; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 201; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 752; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 2117; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2542; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2544; +UPDATE bot_spells_entries SET `type` = 6 WHERE `spell_id` = 2115; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1403; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1405; +UPDATE bot_spells_entries SET `type` = 9 WHERE `spell_id` = 289; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 294; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 302; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 521; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 185; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 450; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 4074; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 195; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 1712; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1703; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 3229; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 3345; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 5509; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 6826; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 270; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 281; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 505; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 526; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 110; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 506; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 162; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 111; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 507; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 527; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 163; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 112; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 1588; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1573; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1592; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1577; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1578; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 1576; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3386; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3387; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 4900; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3395; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 5394; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 5392; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 6827; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 5416; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1437; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 1436; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 5348; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 8008; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 2571; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 370; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 1741; +UPDATE bot_spells_entries SET `type` = 17 WHERE `spell_id` = 1296; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 270; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2634; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 2942; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 3462; +UPDATE bot_spells_entries SET `type` = 13 WHERE `spell_id` = 6828; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 14312; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 14313; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 14314; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 18392; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 18393; +UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 18394; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spell_id` = 15186; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 15187; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 15188; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 14446; +UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 14447; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14467; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14468; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14469; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spell_id` = 14267; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spell_id` = 14268; +UPDATE bot_spells_entries SET `type` = 0 WHERE `spell_id` = 14269; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spell_id` = 14955; +UPDATE bot_spells_entries SET `type` = 10 WHERE `spell_id` = 14956; +UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 14387; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14388; +UPDATE bot_spells_entries SET `type` = 3 WHERE `spell_id` = 14389; +UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 10436; --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 3440; -- Ro's Illumination [#3440] from DoT [#8] to Debuff [#14] [Should be 0] --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 303; -- Whirl till you hurl [#303] from Nuke [#0] to Debuff [#14] [Should be 0] --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 619; -- Dyn's Dizzying Draught [#619] from Nuke [#0] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 3440; -- Ro's Illumination [#3440] from DoT [#8] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 303; -- Whirl till you hurl [#303] from Nuke [#0] to Debuff [#14] [Should be 0] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 619; -- Dyn's Dizzying Draught [#619] from Nuke [#0] to Debuff [#14] [Should be 0] --- UPDATE bot_spells_entries SET `type` = 14 WHERE `spellid` = 74; -- Mana Sieve [#74] from Nuke [#0] to Debuff [#14] --- UPDATE bot_spells_entries SET `type` = 6 WHERE `spellid` = 1686; -- Theft of Thought [#1686] from Nuke [#0] to Lifetap [#6] +-- UPDATE bot_spells_entries SET `type` = 14 WHERE `spell_id` = 74; -- Mana Sieve [#74] from Nuke [#0] to Debuff [#14] +-- UPDATE bot_spells_entries SET `type` = 6 WHERE `spell_id` = 1686; -- Theft of Thought [#1686] from Nuke [#0] to Lifetap [#6] --- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 3694; -- Stoicism [#3694] from In-Combat Buff [#10] to Regular Heal [#1] --- UPDATE bot_spells_entries SET `type` = 1 WHERE `spellid` = 4899; -- Breath of Trushar [#4899] from In-Combat Buff [#10] to Regular Heal [#1] +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 3694; -- Stoicism [#3694] from In-Combat Buff [#10] to Regular Heal [#1] +-- UPDATE bot_spells_entries SET `type` = 1 WHERE `spell_id` = 4899; -- Breath of Trushar [#4899] from In-Combat Buff [#10] to Regular Heal [#1] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 8 WHERE `spellid` = 1748; -- Angstlich's Assonance [#1748] from Slow [#13] to DoT [#8] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] --- UPDATE bot_spells_entries SET `type` = 7 WHERE `spellid` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 8 WHERE `spell_id` = 1748; -- Angstlich's Assonance [#1748] from Slow [#13] to DoT [#8] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 1751; -- Largo's Assonant Binding [#1751] from Slow [#13] to Snare [#7] +-- UPDATE bot_spells_entries SET `type` = 7 WHERE `spell_id` = 738; -- Selo's Consonant Chain [#738] from Slow [#13] to Snare [#7] +)" + }, + ManifestEntry{ + .version = 9050, + .description = "2024_11_26_add_commanded_spelltypes.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 100", + .condition = "empty", + .match = "", + .sql = R"( +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) +VALUES +(3006, 9957, 100, 20, 254), +(3006, 9956, 100, 20, 254), +(3006, 552, 100, 25, 254), +(3006, 550, 100, 25, 254), +(3006, 553, 100, 25, 254), +(3006, 2432, 100, 26, 254), +(3006, 2020, 100, 26, 254), +(3006, 551, 100, 27, 254), +(3006, 3792, 100, 28, 254), +(3006, 2419, 100, 29, 254), +(3006, 554, 100, 30, 254), +(3006, 557, 100, 31, 254), +(3006, 1434, 100, 32, 254), +(3006, 555, 100, 32, 254), +(3006, 25898, 100, 32, 254), +(3006, 25904, 100, 32, 254), +(3006, 556, 100, 32, 254), +(3006, 25698, 100, 33, 254), +(3006, 1517, 100, 33, 254), +(3006, 2424, 100, 33, 254), +(3006, 25689, 100, 33, 254), +(3006, 25899, 100, 34, 254), +(3006, 25690, 100, 35, 254), +(3006, 25903, 100, 35, 254), +(3006, 25900, 100, 35, 254), +(3006, 558, 100, 36, 254), +(3006, 2429, 100, 37, 254), +(3006, 1438, 100, 38, 254), +(3006, 3184, 100, 38, 254), +(3006, 25697, 100, 38, 254), +(3006, 25902, 100, 39, 254), +(3006, 25695, 100, 39, 254), +(3006, 25901, 100, 40, 254), +(3006, 25694, 100, 40, 254), +(3006, 1398, 100, 40, 254), +(3006, 25905, 100, 41, 254), +(3006, 25696, 100, 42, 254), +(3006, 1440, 100, 42, 254), +(3006, 25906, 100, 43, 254), +(3006, 25693, 100, 44, 254), +(3006, 25699, 100, 45, 254), +(3006, 24773, 100, 46, 254), +(3006, 8965, 100, 52, 254), +(3006, 24771, 100, 52, 254), +(3006, 8235, 100, 52, 254), +(3006, 24775, 100, 52, 254), +(3006, 4966, 100, 54, 254), +(3006, 6184, 100, 55, 254), +(3006, 5731, 100, 55, 254), +(3006, 24776, 100, 56, 254), +(3006, 25700, 100, 56, 254), +(3006, 25691, 100, 57, 254), +(3006, 24772, 100, 57, 254), +(3006, 25692, 100, 57, 254), +(3006, 11981, 100, 59, 254), +(3006, 9953, 100, 60, 254), +(3006, 9954, 100, 60, 254), +(3006, 11980, 100, 64, 254), +(3006, 6179, 100, 64, 254), +(3006, 24774, 100, 67, 254), +(3006, 9950, 100, 70, 254), +(3006, 9951, 100, 70, 254), +(3006, 15886, 100, 75, 254), +(3006, 15887, 100, 75, 254), +(3006, 21989, 100, 80, 254), +(3006, 20539, 100, 80, 254), +(3006, 21984, 100, 80, 254), +(3006, 20538, 100, 80, 254), +(3006, 17883, 100, 85, 254), +(3006, 17884, 100, 85, 254), +(3006, 28997, 100, 90, 254), +(3006, 28998, 100, 90, 254), +(3006, 29000, 100, 92, 254), +(3006, 29001, 100, 92, 254), +(3006, 34832, 100, 95, 254), +(3006, 40217, 100, 95, 254), +(3006, 34833, 100, 95, 254), +(3006, 40216, 100, 95, 254), +(3012, 10881, 100, 20, 254), +(3012, 10880, 100, 20, 254), +(3012, 562, 100, 25, 254), +(3012, 563, 100, 27, 254), +(3012, 3793, 100, 27, 254), +(3012, 561, 100, 28, 254), +(3012, 2420, 100, 29, 254), +(3012, 2944, 100, 29, 254), +(3012, 564, 100, 32, 254), +(3012, 565, 100, 33, 254), +(3012, 1418, 100, 33, 254), +(3012, 2425, 100, 33, 254), +(3012, 1516, 100, 34, 254), +(3012, 1338, 100, 35, 254), +(3012, 3833, 100, 35, 254), +(3012, 566, 100, 35, 254), +(3012, 1336, 100, 36, 254), +(3012, 2943, 100, 36, 254), +(3012, 1423, 100, 36, 254), +(3012, 567, 100, 36, 254), +(3012, 568, 100, 37, 254), +(3012, 1337, 100, 37, 254), +(3012, 3180, 100, 38, 254), +(3012, 1339, 100, 38, 254), +(3012, 2421, 100, 39, 254), +(3012, 2430, 100, 39, 254), +(3012, 1372, 100, 40, 254), +(3012, 2426, 100, 41, 254), +(3012, 1371, 100, 41, 254), +(3012, 1399, 100, 42, 254), +(3012, 1374, 100, 42, 254), +(3012, 1373, 100, 43, 254), +(3012, 1425, 100, 43, 254), +(3012, 1375, 100, 44, 254), +(3012, 3181, 100, 45, 254), +(3012, 2022, 100, 45, 254), +(3012, 666, 100, 46, 254), +(3012, 3849, 100, 46, 254), +(3012, 674, 100, 46, 254), +(3012, 2023, 100, 46, 254), +(3012, 2024, 100, 47, 254), +(3012, 2025, 100, 48, 254), +(3012, 2431, 100, 49, 254), +(3012, 8966, 100, 51, 254), +(3012, 8236, 100, 51, 254), +(3012, 4965, 100, 54, 254), +(3012, 8969, 100, 55, 254), +(3012, 8239, 100, 55, 254), +(3012, 6183, 100, 55, 254), +(3012, 5732, 100, 55, 254), +(3012, 4964, 100, 57, 254), +(3012, 6182, 100, 58, 254), +(3012, 5735, 100, 60, 254), +(3012, 10877, 100, 60, 254), +(3012, 10878, 100, 60, 254), +(3012, 6178, 100, 64, 254), +(3012, 6177, 100, 67, 254), +(3012, 11984, 100, 69, 254), +(3012, 10874, 100, 70, 254), +(3012, 10875, 100, 70, 254), +(3012, 11983, 100, 74, 254), +(3012, 15889, 100, 75, 254), +(3012, 15890, 100, 75, 254), +(3012, 21988, 100, 80, 254), +(3012, 20542, 100, 80, 254), +(3012, 21985, 100, 80, 254), +(3012, 20541, 100, 80, 254), +(3012, 17886, 100, 85, 254), +(3012, 17887, 100, 85, 254), +(3012, 29840, 100, 90, 254), +(3012, 29841, 100, 90, 254), +(3012, 29843, 100, 92, 254), +(3012, 29844, 100, 92, 254), +(3012, 40443, 100, 95, 254), +(3012, 35715, 100, 95, 254), +(3012, 40442, 100, 95, 254), +(3012, 35714, 100, 95, 254), +(3002, 208, 101, 1, 4), +(3002, 501, 101, 5, 14), +(3002, 47, 101, 15, 35), +(3002, 45, 101, 36, 64), +(3002, 1541, 101, 55, 254), +(3002, 3197, 101, 65, 254), +(3002, 5274, 101, 70, 70), +(3002, 9798, 101, 71, 75), +(3002, 9799, 101, 71, 75), +(3002, 9797, 101, 71, 75), +(3002, 14288, 101, 76, 80), +(3002, 14289, 101, 76, 80), +(3002, 14290, 101, 76, 80), +(3002, 18309, 101, 81, 85), +(3002, 18310, 101, 81, 85), +(3002, 18311, 101, 81, 85), +(3002, 25103, 101, 86, 90), +(3002, 25101, 101, 86, 90), +(3002, 25102, 101, 86, 90), +(3002, 28102, 101, 91, 95), +(3002, 28100, 101, 91, 95), +(3002, 28101, 101, 91, 95), +(3002, 34096, 101, 96, 254), +(3002, 34094, 101, 96, 254), +(3002, 34095, 101, 96, 254), +(3003, 208, 101, 10, 24), +(3003, 501, 101, 25, 42), +(3003, 47, 101, 43, 48), +(3003, 45, 101, 49, 254), +(3003, 25294, 101, 86, 90), +(3003, 25295, 101, 86, 90), +(3003, 25296, 101, 86, 90), +(3003, 28340, 101, 91, 95), +(3003, 28338, 101, 91, 95), +(3003, 28339, 101, 91, 95), +(3003, 34346, 101, 96, 254), +(3003, 34344, 101, 96, 254), +(3003, 34345, 101, 96, 254), +(3004, 240, 101, 4, 30), +(3004, 250, 101, 22, 254), +(3004, 513, 101, 31, 254), +(3004, 3601, 101, 39, 254), +(3004, 5316, 101, 68, 70), +(3004, 10112, 101, 71, 75), +(3004, 10110, 101, 71, 75), +(3004, 10111, 101, 71, 75), +(3004, 15037, 101, 76, 80), +(3004, 15035, 101, 76, 80), +(3004, 15036, 101, 76, 80), +(3004, 19168, 101, 81, 85), +(3004, 19169, 101, 81, 85), +(3004, 19167, 101, 81, 85), +(3004, 25417, 101, 86, 87), +(3004, 25418, 101, 86, 90), +(3004, 25419, 101, 86, 90), +(3004, 25466, 101, 88, 90), +(3004, 25467, 101, 88, 90), +(3004, 25465, 101, 88, 90), +(3004, 28479, 101, 91, 92), +(3004, 28480, 101, 91, 95), +(3004, 28481, 101, 91, 95), +(3004, 28542, 101, 93, 95), +(3004, 28543, 101, 93, 95), +(3004, 28544, 101, 93, 95), +(3004, 34500, 101, 96, 97), +(3004, 34502, 101, 96, 254), +(3004, 34501, 101, 96, 254), +(3004, 34565, 101, 98, 254), +(3004, 34563, 101, 98, 254), +(3004, 34564, 101, 98, 254), +(3005, 347, 101, 9, 51), +(3005, 448, 101, 52, 254), +(3006, 240, 101, 1, 14), +(3006, 250, 101, 5, 254), +(3006, 513, 101, 15, 254), +(3006, 3601, 101, 29, 254), +(3006, 5347, 101, 67, 70), +(3006, 9851, 101, 71, 75), +(3006, 9852, 101, 71, 75), +(3006, 9853, 101, 71, 75), +(3006, 14369, 101, 76, 80), +(3006, 14367, 101, 76, 80), +(3006, 14368, 101, 76, 80), +(3006, 18409, 101, 81, 85), +(3006, 18407, 101, 81, 85), +(3006, 18408, 101, 81, 85), +(3006, 25736, 101, 86, 90), +(3006, 25734, 101, 86, 90), +(3006, 25735, 101, 86, 90), +(3006, 28831, 101, 91, 95), +(3006, 28832, 101, 91, 95), +(3006, 28830, 101, 91, 95), +(3006, 34863, 101, 96, 254), +(3006, 34864, 101, 96, 254), +(3006, 34862, 101, 96, 254), +(3007, 4614, 101, 35, 49), +(3007, 4683, 101, 50, 56), +(3007, 4684, 101, 57, 63), +(3007, 4698, 101, 64, 64), +(3007, 5019, 101, 65, 254), +(3007, 5020, 101, 65, 254), +(3007, 6175, 101, 69, 70), +(3007, 10949, 101, 71, 75), +(3007, 10947, 101, 71, 75), +(3007, 10948, 101, 71, 75), +(3007, 14800, 101, 76, 80), +(3007, 14801, 101, 76, 80), +(3007, 14799, 101, 76, 80), +(3007, 18905, 101, 81, 85), +(3007, 18906, 101, 81, 85), +(3007, 18904, 101, 81, 85), +(3007, 25912, 101, 86, 90), +(3007, 25913, 101, 86, 90), +(3007, 25911, 101, 86, 90), +(3007, 29006, 101, 91, 95), +(3007, 29007, 101, 91, 95), +(3007, 29008, 101, 91, 95), +(3007, 35047, 101, 96, 254), +(3007, 35048, 101, 96, 254), +(3007, 35049, 101, 96, 254), +(3008, 728, 101, 8, 60), +(3008, 3361, 101, 61, 254), +(3008, 5370, 101, 66, 70), +(3008, 10403, 101, 71, 75), +(3008, 10401, 101, 71, 75), +(3008, 10402, 101, 71, 75), +(3008, 14002, 101, 76, 80), +(3008, 14000, 101, 76, 80), +(3008, 14001, 101, 76, 80), +(3008, 18001, 101, 81, 85), +(3008, 18002, 101, 81, 85), +(3008, 18000, 101, 81, 85), +(3008, 25978, 101, 86, 90), +(3008, 25979, 101, 86, 90), +(3008, 25977, 101, 86, 90), +(3008, 29079, 101, 91, 95), +(3008, 29080, 101, 91, 95), +(3008, 29078, 101, 91, 95), +(3008, 35131, 101, 96, 254), +(3008, 35132, 101, 96, 254), +(3008, 35133, 101, 96, 254), +(3011, 347, 101, 2, 22), +(3011, 448, 101, 23, 254), +(3014, 208, 101, 1, 5), +(3014, 501, 101, 6, 17), +(3014, 47, 101, 18, 34), +(3014, 45, 101, 35, 61), +(3014, 1541, 101, 51, 254), +(3014, 3197, 101, 62, 254), +(3014, 5506, 101, 67, 71), +(3014, 10601, 101, 72, 76), +(3014, 10599, 101, 72, 76), +(3014, 10600, 101, 72, 76), +(3014, 14510, 101, 77, 81), +(3014, 14511, 101, 77, 81), +(3014, 14509, 101, 77, 81), +(3014, 18568, 101, 82, 86), +(3014, 18569, 101, 82, 86), +(3014, 18567, 101, 82, 86), +(3014, 26921, 101, 87, 91), +(3014, 26922, 101, 87, 91), +(3014, 26920, 101, 87, 91), +(3014, 30054, 101, 92, 96), +(3014, 30055, 101, 92, 96), +(3014, 30056, 101, 92, 96), +(3014, 36116, 101, 97, 254), +(3014, 36117, 101, 97, 254), +(3014, 36118, 101, 97, 254), +(3006, 2183, 102, 18, 56), +(3006, 1567, 102, 57, 254), +(3012, 2184, 102, 18, 56), +(3012, 2558, 102, 56, 64), +(3012, 1628, 102, 57, 254), +(3012, 3244, 102, 65, 254), +(3002, 35, 103, 10, 254), +(3006, 35, 103, 12, 254), +(3010, 35, 103, 14, 254), +(3011, 35, 103, 12, 254), +(3012, 35, 103, 12, 254), +(3013, 35, 103, 12, 254), +(3014, 35, 103, 12, 254), +(3008, 737, 104, 14, 254), +(3011, 305, 104, 17, 254), +(3012, 305, 104, 14, 254), +(3013, 305, 104, 13, 254), +(3014, 305, 104, 15, 254), +(3004, 261, 105, 35, 64), +(3004, 2517, 105, 65, 254), +(3006, 261, 105, 14, 49), +(3006, 2894, 105, 50, 53), +(3006, 2517, 105, 54, 254), +(3006, 3185, 105, 62, 254), +(3008, 718, 105, 31, 50), +(3008, 1750, 105, 51, 254), +(3010, 261, 105, 10, 50), +(3010, 2894, 105, 51, 254), +(3011, 457, 105, 41, 254), +(3011, 1391, 105, 45, 254), +(3012, 261, 105, 22, 49), +(3012, 2894, 105, 50, 254), +(3014, 261, 105, 15, 50), +(3014, 2894, 105, 51, 254), +(3015, 261, 105, 32, 254), +(3003, 1743, 106, 55, 254), +(3008, 714, 106, 41, 254), +(3008, 748, 106, 47, 57), +(3008, 1450, 106, 49, 254), +(3008, 1752, 106, 52, 254), +(3008, 1763, 106, 58, 72), +(3008, 11881, 106, 73, 77), +(3008, 11879, 106, 73, 77), +(3008, 11880, 106, 73, 77), +(3008, 14055, 106, 78, 82), +(3008, 14056, 106, 78, 82), +(3008, 14054, 106, 78, 82), +(3008, 18040, 106, 83, 87), +(3008, 18041, 106, 83, 87), +(3008, 18039, 106, 83, 87), +(3008, 26026, 106, 88, 92), +(3008, 26027, 106, 88, 92), +(3008, 26025, 106, 88, 92), +(3008, 29120, 106, 93, 97), +(3008, 29121, 106, 93, 97), +(3008, 29122, 106, 93, 97), +(3008, 35170, 106, 98, 254), +(3008, 35171, 106, 98, 254), +(3008, 35172, 106, 98, 254), +(3012, 2559, 106, 58, 254), +(3014, 481, 106, 13, 21), +(3014, 21, 106, 19, 21), +(3014, 482, 106, 22, 32), +(3014, 483, 106, 33, 39), +(3014, 648, 106, 38, 39), +(3014, 484, 106, 40, 57), +(3014, 176, 106, 47, 57), +(3014, 1689, 106, 52, 57), +(3014, 1713, 106, 58, 60), +(3014, 3343, 106, 61, 66), +(3014, 6739, 106, 61, 68), +(3014, 3351, 106, 63, 66), +(3014, 5504, 106, 67, 70), +(3014, 5514, 106, 69, 70), +(3014, 6671, 106, 69, 254), +(3014, 10598, 106, 71, 75), +(3014, 10596, 106, 71, 75), +(3014, 10597, 106, 71, 75), +(3014, 10643, 106, 74, 75), +(3014, 10641, 106, 74, 75), +(3014, 10642, 106, 74, 75), +(3014, 11887, 106, 74, 78), +(3014, 11885, 106, 74, 78), +(3014, 11886, 106, 74, 78), +(3014, 14507, 106, 76, 80), +(3014, 14508, 106, 76, 80), +(3014, 14506, 106, 76, 80), +(3014, 14543, 106, 79, 80), +(3014, 14544, 106, 79, 80), +(3014, 14542, 106, 79, 80), +(3014, 14582, 106, 79, 83), +(3014, 14583, 106, 79, 83), +(3014, 14581, 106, 79, 83), +(3014, 18564, 106, 81, 85), +(3014, 18565, 106, 81, 85), +(3014, 18566, 106, 81, 85), +(3014, 18600, 106, 84, 85), +(3014, 18601, 106, 84, 85), +(3014, 18602, 106, 84, 85), +(3014, 18641, 106, 84, 88), +(3014, 18639, 106, 84, 88), +(3014, 18640, 106, 84, 88), +(3014, 26901, 106, 86, 90), +(3014, 26899, 106, 86, 90), +(3014, 26900, 106, 86, 90), +(3014, 26999, 106, 89, 90), +(3014, 26997, 106, 89, 90), +(3014, 26998, 106, 89, 90), +(3014, 27025, 106, 89, 93), +(3014, 27026, 106, 89, 93), +(3014, 27024, 106, 89, 93), +(3014, 30028, 106, 91, 95), +(3014, 30029, 106, 91, 95), +(3014, 30027, 106, 91, 95), +(3014, 30132, 106, 93, 95), +(3014, 30133, 106, 93, 95), +(3014, 30131, 106, 93, 95), +(3014, 30140, 106, 94, 95), +(3014, 30141, 106, 94, 95), +(3014, 30142, 106, 94, 95), +(3014, 30167, 106, 94, 98), +(3014, 30168, 106, 94, 98), +(3014, 30169, 106, 94, 98), +(3014, 36091, 106, 96, 254), +(3014, 36089, 106, 96, 254), +(3014, 36090, 106, 96, 254), +(3014, 36189, 106, 98, 254), +(3014, 36187, 106, 98, 254), +(3014, 36188, 106, 98, 254), +(3014, 36216, 106, 99, 254), +(3014, 36196, 106, 99, 254), +(3014, 36217, 106, 99, 254), +(3014, 36194, 106, 99, 254), +(3014, 36215, 106, 99, 254), +(3014, 36195, 106, 99, 254), +(3004, 86, 107, 20, 254), +(3006, 86, 107, 6, 49), +(3006, 2881, 107, 50, 254), +(3008, 729, 107, 16, 254), +(3010, 86, 107, 12, 50), +(3010, 2881, 107, 51, 254), +(3011, 457, 107, 41, 254), +(3011, 1391, 107, 45, 254), +(3014, 86, 107, 12, 43), +(3014, 3696, 107, 44, 50), +(3014, 2881, 107, 51, 254), +(3015, 86, 107, 25, 254), +(3010, 345, 108, 15, 254), +(3010, 2522, 108, 16, 254), +(3015, 345, 108, 23, 254), +(3002, 235, 109, 11, 254), +(3002, 1726, 109, 51, 254), +(3002, 6125, 109, 66, 254), +(3002, 14348, 109, 77, 81), +(3002, 14346, 109, 77, 81), +(3002, 14347, 109, 77, 81), +(3002, 18369, 109, 82, 254), +(3002, 18367, 109, 82, 254), +(3002, 18368, 109, 82, 254), +(3003, 235, 109, 17, 254), +(3004, 247, 109, 14, 46), +(3004, 80, 109, 32, 64), +(3004, 34, 109, 47, 254), +(3004, 2517, 109, 65, 254), +(3005, 235, 109, 4, 254), +(3006, 247, 109, 4, 17), +(3006, 255, 109, 8, 254), +(3006, 80, 109, 13, 53), +(3006, 34, 109, 18, 254), +(3006, 2516, 109, 52, 254), +(3006, 4058, 109, 52, 254), +(3006, 2517, 109, 54, 254), +(3006, 3185, 109, 62, 254), +(3006, 6123, 109, 68, 254), +(3008, 719, 109, 19, 50), +(3008, 735, 109, 24, 254), +(3008, 1750, 109, 51, 254), +(3010, 79, 109, 7, 55), +(3010, 255, 109, 10, 254), +(3010, 42, 109, 27, 254), +(3010, 1575, 109, 56, 254), +(3010, 2886, 109, 58, 254), +(3011, 235, 109, 1, 254), +(3011, 457, 109, 41, 254), +(3011, 1391, 109, 45, 254), +(3011, 6124, 109, 68, 254), +(3012, 80, 109, 4, 39), +(3012, 42, 109, 16, 254), +(3012, 3811, 109, 40, 254), +(3012, 6120, 109, 67, 254), +(3012, 15513, 109, 76, 80), +(3012, 15511, 109, 76, 80), +(3012, 15512, 109, 76, 80), +(3012, 19701, 109, 81, 254), +(3012, 19699, 109, 81, 254), +(3012, 19700, 109, 81, 254), +(3013, 42, 109, 8, 254), +(3013, 80, 109, 16, 254), +(3014, 42, 109, 4, 254), +(3014, 80, 109, 6, 43), +(3014, 235, 109, 14, 254), +(3014, 3696, 109, 44, 254), +(3014, 6122, 109, 66, 254), +(3015, 79, 109, 29, 64), +(3015, 42, 109, 43, 254), +(3015, 1575, 109, 65, 254), +(3004, 278, 110, 28, 64), +(3004, 4054, 110, 41, 64), +(3004, 4055, 110, 49, 254), +(3004, 2517, 110, 65, 254), +(3006, 278, 110, 10, 53), +(3006, 424, 110, 26, 254), +(3006, 4054, 110, 30, 53), +(3006, 169, 110, 35, 61), +(3006, 4055, 110, 35, 254), +(3006, 3579, 110, 45, 254), +(3006, 4058, 110, 52, 254), +(3006, 1554, 110, 53, 254), +(3006, 2517, 110, 54, 254), +(3006, 3185, 110, 62, 254), +(3008, 717, 110, 5, 48), +(3008, 4395, 110, 25, 48), +(3008, 2605, 110, 49, 50), +(3008, 1750, 110, 51, 254), +(3010, 278, 110, 9, 254), +(3010, 424, 110, 22, 254), +(3010, 4054, 110, 29, 254), +(3010, 4055, 110, 34, 254), +(3010, 2524, 110, 36, 254), +(3010, 1554, 110, 52, 254), +(3015, 278, 110, 24, 254), +(3015, 4054, 110, 39, 254), +(3015, 4055, 110, 44, 254), +(3012, 1422, 111, 50, 254), +(3012, 1334, 111, 52, 254), +(3005, 2213, 112, 12, 34), +(3005, 3, 112, 35, 56), +(3005, 1773, 112, 57, 70), +(3005, 10042, 112, 71, 75), +(3005, 14823, 112, 76, 80), +(3005, 18928, 112, 81, 85), +(3005, 25555, 112, 86, 90), +(3005, 28632, 112, 91, 95), +(3005, 34662, 112, 96, 254), +(3011, 2213, 112, 12, 34), +(3011, 3, 112, 35, 56), +(3011, 1773, 112, 57, 70), +(3011, 10042, 112, 71, 75), +(3011, 14823, 112, 76, 80), +(3011, 18928, 112, 81, 85), +(3011, 25555, 112, 86, 90), +(3011, 28632, 112, 91, 95), +(3011, 34662, 112, 96, 254); )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/ruletypes.h b/common/ruletypes.h index 8008683d99..da5a5f35de 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -877,6 +877,10 @@ RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follo RULE_INT(Bots, MaxFollowDistance, 300, "Default 300. Max distance a bot can be set to follow behind.") RULE_INT(Bots, MaxDistanceRanged, 300, "Default 300. Max distance a bot can be set to ranged.") RULE_BOOL(Bots, AllowAIMez, true, "If enabled bots will automatically mez/AE mez eligible targets.") +RULE_BOOL(Bots, AllowCommandedCharm, true, "If enabled bots can be commanded to charm NPCs.") +RULE_BOOL(Bots, AllowCommandedMez, true, "If enabled bots can be commanded to mez NPCs.") +RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded to resurrect players.") +RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_CATEGORY_END() diff --git a/common/spdat.cpp b/common/spdat.cpp index 7b0e540894..a691d51972 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2842,6 +2842,7 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { case BotSpellTypes::AEDoT: case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: + case BotSpellTypes::Lull: return true; case BotSpellTypes::InCombatBuff: if (cls == Class::ShadowKnight) { @@ -2885,6 +2886,18 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { case BotSpellTypes::PetResistBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return true; case BotSpellTypes::InCombatBuff: if (cls == Class::ShadowKnight) { @@ -2922,6 +2935,18 @@ bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { case BotSpellTypes::PetBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return true; default: return false; @@ -2953,6 +2978,7 @@ bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { case BotSpellTypes::Stun: case BotSpellTypes::AEMez: case BotSpellTypes::Mez: + case BotSpellTypes::Lull: return true; default: return false; @@ -3234,3 +3260,34 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { return true; } + +bool IsCommandedSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Lull: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: + //case BotSpellTypes::Charm: + //case BotSpellTypes::Resurrect: + //case BotSpellTypes::Cure: + //case BotSpellTypes::GroupCures: + //case BotSpellTypes::DamageShields: + //case BotSpellTypes::PetDamageShields: + //case BotSpellTypes::ResistBuffs: + //case BotSpellTypes::PetResistBuffs: + return true; + default: + return false; + } + + return false; +} diff --git a/common/spdat.h b/common/spdat.h index 4c1b52ecaf..cefac6784d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -708,9 +708,26 @@ namespace BotSpellTypes constexpr uint16 ResistBuffs = 52; constexpr uint16 PetDamageShields = 53; constexpr uint16 PetResistBuffs = 54; + + // Command Spell Types + constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic + constexpr uint16 Lull = 101; + constexpr uint16 Succor = 102; + constexpr uint16 BindAffinity = 103; + constexpr uint16 Identify = 104; + constexpr uint16 Levitate = 105; + constexpr uint16 Rune = 106; + constexpr uint16 WaterBreathing = 107; + constexpr uint16 Size = 108; + constexpr uint16 Invisibility = 109; + constexpr uint16 MovementSpeed = 110; + constexpr uint16 SendHome = 111; + constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed + constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this + constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); @@ -730,6 +747,7 @@ bool IsClientBotSpellType(uint16 spellType); bool IsHealBotSpellType(uint16 spellType); bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); +bool IsCommandedSpellType(uint16 spellType); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only diff --git a/zone/bot.cpp b/zone/bot.cpp index ece357a231..32a70ceb3c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9527,6 +9527,17 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: if ( !( spells[spell_id].target_type == ST_Target || @@ -10893,6 +10904,18 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return BotSpellTypes::Buff; case BotSpellTypes::AEMez: case BotSpellTypes::Mez: @@ -10934,6 +10957,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::Resurrect: + case BotSpellTypes::Lull: default: return spellType; } @@ -10996,6 +11020,84 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { return true; } + return false; + case BotSpellTypes::Lull: + if (!IsHarmonySpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Teleport: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + return true; + } + + return false; + case BotSpellTypes::Succor: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + return true; + } + + return false; + case BotSpellTypes::BindAffinity: + if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + return true; + } + + return false; + case BotSpellTypes::Identify: + if (IsEffectInSpell(spell_id, SE_Identify)) { + return true; + } + + return false; + case BotSpellTypes::Levitate: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + return true; + } + + return false; + case BotSpellTypes::Rune: + if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + return true; + } + + return false; + case BotSpellTypes::WaterBreathing: + if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { + return true; + } + + return false; + case BotSpellTypes::Size: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + return true; + } + + return false; + case BotSpellTypes::Invisibility: + if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::MovementSpeed: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + return true; + } + + return false; + case BotSpellTypes::SendHome: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + return true; + } + + return false; + case BotSpellTypes::SummonCorpse: + if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + return true; + } + return false; default: return true; @@ -11491,3 +11593,82 @@ bool Bot::BotPassiveCheck() { return false; } + +bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell_id) { + if (subType == UINT16_MAX) { + return true; + } + + switch (subType) { + case CommandedSubTypes::SingleTarget: + if (!IsAnyAESpell(spell_id) && !IsGroupSpell(spell_id)) { + return true; + } + + break; + case CommandedSubTypes::GroupTarget: + if (IsGroupSpell(spell_id)) { + return true; + } + + break; + case CommandedSubTypes::AETarget: + if (IsAnyAESpell(spell_id)) { + return true; + } + + break; + case CommandedSubTypes::SeeInvis: + if (IsEffectInSpell(spell_id, SE_SeeInvis)) { + return true; + } + + break; + case CommandedSubTypes::Invis: + if (IsEffectInSpell(spell_id, SE_Invisibility) || IsEffectInSpell(spell_id, SE_Invisibility2)) { + return true; + } + + break; + case CommandedSubTypes::InvisUndead: + if (IsEffectInSpell(spell_id, SE_InvisVsUndead) || IsEffectInSpell(spell_id, SE_InvisVsUndead2)) { + return true; + } + + break; + case CommandedSubTypes::InvisAnimals: + if (IsEffectInSpell(spell_id, SE_InvisVsAnimals) || IsEffectInSpell(spell_id, SE_ImprovedInvisAnimals)) { + return true; + } + + break; + case CommandedSubTypes::Shrink: + if ( + (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) < 100) || + (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) < 100) + ) { + return true; + } + + break; + case CommandedSubTypes::Grow: + if ( + (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) > 100) || + (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) > 100) + ) { + return true; + } + + break; + case CommandedSubTypes::Selo: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed) && IsBardSong(spell_id)) { + return true; + } + + break; + default: + break; + } + + return false; +} diff --git a/zone/bot.h b/zone/bot.h index 8e85448e08..2360d73b4d 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -147,6 +147,19 @@ namespace BotBaseSettings { constexpr uint16 END = BotBaseSettings::ManaWhenToMed; // Increment as needed }; +namespace CommandedSubTypes { + constexpr uint16 SingleTarget = 1; + constexpr uint16 GroupTarget = 2; + constexpr uint16 AETarget = 3; + constexpr uint16 SeeInvis = 4; + constexpr uint16 Invis = 5; + constexpr uint16 InvisUndead = 6; + constexpr uint16 InvisAnimals = 7; + constexpr uint16 Shrink = 8; + constexpr uint16 Grow = 9; + constexpr uint16 Selo = 10; +}; + class Bot : public NPC { friend class Mob; public: @@ -387,7 +400,7 @@ class Bot : public NPC { void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); // AI Methods - bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType); + bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); bool AttemptAICastSpell(uint16 spellType); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; @@ -523,6 +536,7 @@ class Bot : public NPC { bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spellType); + bool IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell_id); bool HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar); @@ -577,7 +591,7 @@ class Bot : public NPC { static std::list GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect); static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType); static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); - static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false); + static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 15ea7eba8a..7af4109f5b 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1251,7 +1251,6 @@ int bot_command_init(void) bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) || bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) || bot_command_add("behindmob", "Toggles whether or not your bot tries to stay behind a mob", AccountStatus::Player, bot_command_behind_mob) || - bot_command_add("bindaffinity", "Orders a bot to attempt an affinity binding", AccountStatus::Player, bot_command_bind_affinity) || bot_command_add("bot", "Lists the available bot management [subcommands]", AccountStatus::Player, bot_command_bot) || bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", AccountStatus::Player, bot_command_appearance) || bot_command_add("botbeardcolor", "Changes the beard color of a bot", AccountStatus::Player, bot_command_beard_color) || @@ -1286,16 +1285,13 @@ int bot_command_init(void) bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_command_woad) || bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || bot_command_add("distanceranged", "Controls the range casters and ranged will try to stay away from a mob", AccountStatus::Player, bot_command_distance_ranged) || - bot_command_add("charm", "Attempts to have a bot charm your target", AccountStatus::Player, bot_command_charm) || bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || - bot_command_add("cure", "Orders a bot to remove any ailments", AccountStatus::Player, bot_command_cure) || bot_command_add("defaultsettings", "Restores a bot back to default settings", AccountStatus::Player, bot_command_default_settings) || bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || - bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || + bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || //TODO bot rewrite - deleteme bot_command_add("enforcespellsettings", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || - bot_command_add("escape", "Orders a bot to send a target group to a safe location within the zone", AccountStatus::Player, bot_command_escape) || bot_command_add("findaliases", "Find available aliases for a bot command", AccountStatus::Player, bot_command_find_aliases) || bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", AccountStatus::Player, bot_command_follow) || bot_command_add("guard", "Orders bots to guard their current positions", AccountStatus::Player, bot_command_guard) || @@ -1322,20 +1318,15 @@ int bot_command_init(void) bot_command_add("healrotationstop", "Stops a heal rotation", AccountStatus::Player, bot_command_heal_rotation_stop) || bot_command_add("help", "List available commands and their description - specify partial command as argument to search", AccountStatus::Player, bot_command_help) || bot_command_add("hold", "Prevents a bot from attacking until released", AccountStatus::Player, bot_command_hold) || - bot_command_add("identify", "Orders a bot to cast an item identification spell", AccountStatus::Player, bot_command_identify) || bot_command_add("illusionblock", "Control whether or not illusion effects will land on the bot if casted by another player or bot", AccountStatus::Player, bot_command_illusion_block) || bot_command_add("inventory", "Lists the available bot inventory [subcommands]", AccountStatus::Player, bot_command_inventory) || bot_command_add("inventorygive", "Gives the item on your cursor to a bot", AccountStatus::Player, bot_command_inventory_give) || bot_command_add("inventorylist", "Lists all items in a bot's inventory", AccountStatus::Player, bot_command_inventory_list) || bot_command_add("inventoryremove", "Removes an item from a bot's inventory", AccountStatus::Player, bot_command_inventory_remove) || bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", AccountStatus::Player, bot_command_inventory_window) || - bot_command_add("invisibility", "Orders a bot to cast a cloak of invisibility, or allow them to be seen", AccountStatus::Player, bot_command_invisibility) || bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", AccountStatus::Player, bot_command_item_use) || - bot_command_add("levitation", "Orders a bot to cast a levitation spell", AccountStatus::Player, bot_command_levitation) || - bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || + bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || //TODO bot rewrite - IMPLEMENT bot_command_add("maxmeleerange", "Toggles whether your bot is at max melee range or not. This will disable all special abilities, including taunt.", AccountStatus::Player, bot_command_max_melee_range) || - bot_command_add("mesmerize", "Orders a bot to cast a mesmerization spell", AccountStatus::Player, bot_command_mesmerize) || - bot_command_add("movementspeed", "Orders a bot to cast a movement speed enhancement spell", AccountStatus::Player, bot_command_movement_speed) || bot_command_add("owneroption", "Sets options available to bot owners", AccountStatus::Player, bot_command_owner_option) || bot_command_add("pet", "Lists the available bot pet [subcommands]", AccountStatus::Player, bot_command_pet) || bot_command_add("petgetlost", "Orders a bot to remove its summoned pet", AccountStatus::Player, bot_command_pet_get_lost) || @@ -1346,14 +1337,9 @@ int bot_command_init(void) bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || - bot_command_add("resistance", "Orders a bot to cast a specified resistance buff", AccountStatus::Player, bot_command_resistance) || - bot_command_add("resurrect", "Orders a bot to resurrect a player's (players') corpse(s)", AccountStatus::Player, bot_command_resurrect) || - 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("sithppercent", "HP threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_hp_percent) || bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || - bot_command_add("size", "Orders a bot to change a player's size", AccountStatus::Player, bot_command_size) || bot_command_add("spellaggrochecks", "Toggles whether or not bots will cast a spell type if they think it will get them aggro", AccountStatus::Player, bot_command_spell_aggro_checks) || bot_command_add("spellengagedpriority", "Controls the order of casts by spell type when engaged in combat", AccountStatus::Player, bot_command_spell_engaged_priority) || bot_command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, bot_command_spell_delays) || @@ -1374,15 +1360,14 @@ int bot_command_init(void) bot_command_add("spellsettingsdelete", "Delete a bot spell setting entry", AccountStatus::Player, bot_command_spell_settings_delete) || bot_command_add("spellsettingstoggle", "Toggle a bot spell use", AccountStatus::Player, bot_command_spell_settings_toggle) || bot_command_add("spellsettingsupdate", "Update a bot spell setting entry", 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("spelltypeids", "Lists spelltypes by ID", AccountStatus::Player, bot_command_spelltype_ids) || bot_command_add("spelltypenames", "Lists spelltypes by shortname", AccountStatus::Player, bot_command_spelltype_names) || + bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) || //TODO bot rewrite - IMPLEMENT 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) || bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || bot_command_add("track", "Orders a capable bot to track enemies", AccountStatus::Player, bot_command_track) || - bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) || - bot_command_add("waterbreathing", "Orders a bot to cast a water breathing spell", AccountStatus::Player, bot_command_water_breathing) + bot_command_add("viewcombos", "Views bot race class combinations", AccountStatus::Player, bot_command_view_combos) ) { bot_command_deinit(); return -1; @@ -2266,36 +2251,27 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/apply_potion.cpp" #include "bot_commands/attack.cpp" #include "bot_commands/behind_mob.cpp" -#include "bot_commands/bind_affinity.cpp" #include "bot_commands/bot.cpp" #include "bot_commands/bot_settings.cpp" #include "bot_commands/cast.cpp" -#include "bot_commands/charm.cpp" #include "bot_commands/class_race_list.cpp" #include "bot_commands/click_item.cpp" #include "bot_commands/copy_settings.cpp" -#include "bot_commands/cure.cpp" #include "bot_commands/default_settings.cpp" #include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" #include "bot_commands/distance_ranged.cpp" -#include "bot_commands/escape.cpp" #include "bot_commands/find_aliases.cpp" #include "bot_commands/follow.cpp" #include "bot_commands/guard.cpp" #include "bot_commands/heal_rotation.cpp" #include "bot_commands/help.cpp" #include "bot_commands/hold.cpp" -#include "bot_commands/identify.cpp" #include "bot_commands/illusion_block.cpp" #include "bot_commands/inventory.cpp" -#include "bot_commands/invisibility.cpp" #include "bot_commands/item_use.cpp" -#include "bot_commands/levitation.cpp" #include "bot_commands/lull.cpp" #include "bot_commands/max_melee_range.cpp" -#include "bot_commands/mesmerize.cpp" -#include "bot_commands/movement_speed.cpp" #include "bot_commands/name.cpp" #include "bot_commands/owner_option.cpp" #include "bot_commands/pet.cpp" @@ -2304,14 +2280,9 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/precombat.cpp" #include "bot_commands/pull.cpp" #include "bot_commands/release.cpp" -#include "bot_commands/resistance.cpp" -#include "bot_commands/resurrect.cpp" -#include "bot_commands/rune.cpp" -#include "bot_commands/send_home.cpp" #include "bot_commands/sit_hp_percent.cpp" #include "bot_commands/sit_in_combat.cpp" #include "bot_commands/sit_mana_percent.cpp" -#include "bot_commands/size.cpp" #include "bot_commands/spell.cpp" #include "bot_commands/spell_aggro_checks.cpp" #include "bot_commands/spell_delays.cpp" @@ -2334,4 +2305,3 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/timer.cpp" #include "bot_commands/track.cpp" #include "bot_commands/view_combos.cpp" -#include "bot_commands/water_breathing.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 8e200bd6b3..56be996809 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1669,36 +1669,27 @@ void bot_command_apply_poison(Client *c, const Seperator *sep); void bot_command_apply_potion(Client* c, const Seperator* sep); void bot_command_attack(Client *c, const Seperator *sep); void bot_command_behind_mob(Client* c, const Seperator* sep); -void bot_command_bind_affinity(Client *c, const Seperator *sep); void bot_command_bot(Client *c, const Seperator *sep); void bot_command_bot_settings(Client* c, const Seperator* sep); void bot_command_cast(Client* c, const Seperator* sep); void bot_command_distance_ranged(Client* c, const Seperator* sep); -void bot_command_charm(Client *c, const Seperator *sep); void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); void bot_command_copy_settings(Client* c, const Seperator* sep); -void bot_command_cure(Client *c, const Seperator *sep); void bot_command_default_settings(Client* c, const Seperator* sep); void bot_command_defensive(Client *c, const Seperator *sep); void bot_command_depart(Client *c, const Seperator *sep); -void bot_command_escape(Client *c, const Seperator *sep); void bot_command_find_aliases(Client *c, const Seperator *sep); void bot_command_follow(Client *c, const Seperator *sep); void bot_command_guard(Client *c, const Seperator *sep); void bot_command_heal_rotation(Client *c, const Seperator *sep); void bot_command_help(Client *c, const Seperator *sep); void bot_command_hold(Client *c, const Seperator *sep); -void bot_command_identify(Client *c, const Seperator *sep); void bot_command_illusion_block(Client* c, const Seperator* sep); void bot_command_inventory(Client *c, const Seperator *sep); -void bot_command_invisibility(Client *c, const Seperator *sep); void bot_command_item_use(Client *c, const Seperator *sep); -void bot_command_levitation(Client *c, const Seperator *sep); void bot_command_lull(Client *c, const Seperator *sep); void bot_command_max_melee_range(Client* c, const Seperator* sep); -void bot_command_mesmerize(Client *c, const Seperator *sep); -void bot_command_movement_speed(Client *c, const Seperator *sep); void bot_command_owner_option(Client *c, const Seperator *sep); void bot_command_pet(Client *c, const Seperator *sep); void bot_command_pick_lock(Client *c, const Seperator *sep); @@ -1706,14 +1697,9 @@ void bot_command_pickpocket(Client* c, const Seperator* sep); void bot_command_precombat(Client* c, const Seperator* sep); void bot_command_pull(Client *c, const Seperator *sep); void bot_command_release(Client *c, const Seperator *sep); -void bot_command_resistance(Client *c, const Seperator *sep); -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_sit_hp_percent(Client* c, const Seperator* sep); void bot_command_sit_in_combat(Client* c, const Seperator* sep); void bot_command_sit_mana_percent(Client* c, const Seperator* sep); -void bot_command_size(Client *c, const Seperator *sep); void bot_command_spell_aggro_checks(Client* c, const Seperator* sep); void bot_command_spell_delays(Client* c, const Seperator* sep); void bot_command_spell_engaged_priority(Client* c, const Seperator* sep); @@ -1743,7 +1729,6 @@ void bot_command_taunt(Client *c, const Seperator *sep); void bot_command_timer(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); // Bot Subcommands void bot_command_appearance(Client *c, const Seperator *sep); diff --git a/zone/bot_commands/bind_affinity.cpp b/zone/bot_commands/bind_affinity.cpp deleted file mode 100644 index a59c5947ae..0000000000 --- a/zone/bot_commands/bind_affinity.cpp +++ /dev/null @@ -1,42 +0,0 @@ -#include "../bot_command.h" - -void bot_command_bind_affinity(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_BindAffinity]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_BindAffinity) || helper_command_alias_fail(c, "bot_command_bind_affinity", sep->arg[0], "bindaffinity")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - c->Message(Chat::White, "note: Orders a bot to attempt an affinity binding", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_BindAffinity); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - // Cast effect message is not being generated - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id)) - c->Message(Chat::White, "Successfully bound %s to this location", target_mob->GetCleanName()); - else - c->Message(Chat::White, "Failed to bind %s to this location", target_mob->GetCleanName()); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index ab02eb950e..49112b8693 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -99,8 +99,8 @@ void bot_command_cast(Client* c, const Seperator* sep) c->Message( Chat::Yellow, fmt::format( - "Use {} for information about race/class IDs.", - Saylink::Silent("^classracelist") + "Use help after any command type for more subtypes to use, for example: {}.", + Saylink::Silent("^cast invisibility help") ).c_str() ); @@ -118,9 +118,58 @@ void bot_command_cast(Client* c, const Seperator* sep) } std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + + //Commanded type help prompts + if (!arg2.compare("help")) { + c->Message(Chat::Yellow, "You can also use [single], [group], [ae]. Ex: ^cast movementspeed group.", sep->arg[0]); + } + + if (!arg1.compare("invisibility") && !arg2.compare("help")) { + c->Message( + Chat::Yellow, + fmt::format( + "Available options for {} are: {}, {}, {}, {}.", + sep->arg[0], + Saylink::Silent("^cast invisibility see", "see"), + Saylink::Silent("^cast invisibility invis", "invis"), + Saylink::Silent("^cast invisibility undead", "undead"), + Saylink::Silent("^cast invisibility animals", "animals") + ).c_str() + ); - if (!arg1.compare("listid") || !arg1.compare("listname")) { - c->CastToBot()->SendSpellTypesWindow(c, sep->arg[0], sep->arg[1], sep->arg[2]); + return; + } + + if (!arg1.compare("size") && !arg2.compare("help")) { + c->Message( + Chat::Yellow, + fmt::format( + "Available options for {} are: {}, {}.", + sep->arg[0], + Saylink::Silent("^cast size grow", "grow"), + Saylink::Silent("^cast size shrink", "shrink") + ).c_str() + ); + + return; + } + + if (!arg1.compare("movementspeed") && !arg2.compare("help")) { + c->Message( + Chat::Yellow, + fmt::format( + "Available options for {} are: {}, {}.", + sep->arg[0], + Saylink::Silent("^cast movementspeed selo"), "selo" + ).c_str() + ); + + return; + } + + if (!arg2.compare("help")) { + c->Message(Chat::Yellow, "There are no additional options for {}.", sep->arg[0]); return; } @@ -131,8 +180,16 @@ void bot_command_cast(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spellType = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); return; } @@ -156,6 +213,88 @@ void bot_command_cast(Client* c, const Seperator* sep) } } + switch (spellType) { //Allowed command checks + case BotSpellTypes::Charm: + if (!RuleB(Bots, AllowCommandedCharm)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + if (!RuleB(Bots, AllowCommandedMez)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + case BotSpellTypes::Resurrect: + if (!RuleB(Bots, AllowCommandedResurrect)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + case BotSpellTypes::SummonCorpse: + if (!RuleB(Bots, AllowCommandedSummonCorpse)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + break; + default: + break; + } + + std::string argString = sep->arg[ab_arg]; + uint16 subType = UINT16_MAX; + uint16 subTargetType = UINT16_MAX; + + if (!argString.compare("shrink")) { + subType = CommandedSubTypes::Shrink; + ++ab_arg; + } + else if (!argString.compare("grow")) { + subType = CommandedSubTypes::Grow; + ++ab_arg; + } + else if (!argString.compare("see")) { + subType = CommandedSubTypes::SeeInvis; + ++ab_arg; + } + else if (!argString.compare("invis")) { + subType = CommandedSubTypes::Invis; + ++ab_arg; + } + else if (!argString.compare("undead")) { + subType = CommandedSubTypes::InvisUndead; + ++ab_arg; + } + else if (!argString.compare("animals")) { + subType = CommandedSubTypes::InvisAnimals; + ++ab_arg; + } + else if (!argString.compare("selo")) { + subType = CommandedSubTypes::Selo; + ++ab_arg; + } + + argString = sep->arg[ab_arg]; + + if (!argString.compare("single")) { + subTargetType = CommandedSubTypes::SingleTarget; + ++ab_arg; + } + else if (!argString.compare("group")) { + subTargetType = CommandedSubTypes::GroupTarget; + ++ab_arg; + } + else if (!argString.compare("ae")) { + subTargetType = CommandedSubTypes::AETarget; + ++ab_arg; + } + if ( spellType == BotSpellTypes::PetBuffs || spellType == BotSpellTypes::PetCompleteHeals || @@ -169,47 +308,63 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* tar = c->GetTarget(); - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme - if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { - if (!tar) { + LogTestDebug("{}: 'Attempting {} [{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + + if (!tar) { + if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { c->Message(Chat::Yellow, "You need a target for that."); return; } + } - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { - c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); - return; - } + switch (spellType) { //Target Checks + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + c->Message(Chat::Yellow, "[%s] is not a player's corpse.", tar->GetCleanName()); - if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { - if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { - c->Message(Chat::Yellow, "[%s] is an invalid target.", tar->GetCleanName()); - return; - } - } - } - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme - switch (spellType) { - case BotSpellTypes::Stun: - case BotSpellTypes::AEStun: - if (tar->GetSpecialAbility(SpecialAbility::StunImmunity)) { - c->Message(Chat::Yellow, "[%s] is immune to stuns.", tar->GetCleanName()); return; } - + break; - case BotSpellTypes::Resurrect: - if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { - c->Message(Chat::Yellow, "[%s] is an invalid target. I can only resurrect player corpses.", tar->GetCleanName()); + case BotSpellTypes::Identify: + case BotSpellTypes::SendHome: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::SummonCorpse: + if (!tar->IsClient() || !c->IsInGroupOrRaid(tar)) { + c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + return; } break; default: + if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { + c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); + + return; + } + + if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { + if ( + (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) || + ((tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner()))) + ) { + c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + + return; + } + } + break; } const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; @@ -219,7 +374,7 @@ void bot_command_cast(Client* c, const Seperator* sep) std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -240,7 +395,8 @@ void bot_command_cast(Client* c, const Seperator* sep) /* TODO bot rewrite - - FIX: Snares, Group Cures, OOC Song, Precombat, HateRedux, Fear/AE Fear + FIX: Depart, SummonCorpse, Lull, + Group Cures, Precombat, Fear/AE Fear ICB (SK) casting hate on friendly but not hostile? NEED TO CHECK: precombat, AE Dispel, AE Lifetap DO I NEED A PBAE CHECK??? @@ -250,7 +406,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* newTar = tar; - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { newTar = bot_iter; } @@ -279,11 +435,11 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - LogTestDebug("{}: Attempting {} on {}", __LINE__, c->GetSpellTypeNameByID(spellType), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); - if (bot_iter->AICastSpell(newTar, 100, spellType)) { + if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { if (!firstFound) { firstFound = bot_iter; } diff --git a/zone/bot_commands/charm.cpp b/zone/bot_commands/charm.cpp deleted file mode 100644 index 2df4694b66..0000000000 --- a/zone/bot_commands/charm.cpp +++ /dev/null @@ -1,54 +0,0 @@ -#include "../bot_command.h" - -void bot_command_charm(Client *c, const Seperator *sep) -{ - auto local_list = &bot_command_spells[BCEnum::SpT_Charm]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Charm) || helper_command_alias_fail(c, "bot_command_charm", sep->arg[0], "charm")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: dire])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Charm); - return; - } - - bool dire = false; - std::string dire_arg = sep->arg[1]; - if (!dire_arg.compare("dire")) - dire = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToCharm(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->dire != dire) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY); - if (!target_mob) - continue; - if (target_mob->IsCharmed()) { - c->Message(Chat::White, "Your is already charmed"); - return; - } - - if (spells[local_entry->spell_id].max_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob, true); - if (!my_bot) - continue; - - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/cure.cpp b/zone/bot_commands/cure.cpp deleted file mode 100644 index 4452f8088d..0000000000 --- a/zone/bot_commands/cure.cpp +++ /dev/null @@ -1,71 +0,0 @@ -#include "../bot_command.h" - -void bot_command_cure(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Cure]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Cure) || helper_command_alias_fail(c, "bot_command_cure", sep->arg[0], "cure")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [ailment: blindness | disease | poison | curse | corruption]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Cure); - return; - } - - std::string ailment_arg = sep->arg[1]; - - auto ailment_type = BCEnum::AT_None; - if (!ailment_arg.compare("blindness")) - ailment_type = BCEnum::AT_Blindness; - else if (!ailment_arg.compare("disease")) - ailment_type = BCEnum::AT_Disease; - else if (!ailment_arg.compare("poison")) - ailment_type = BCEnum::AT_Poison; - else if (!ailment_arg.compare("curse")) - ailment_type = BCEnum::AT_Curse; - else if (!ailment_arg.compare("corruption")) - ailment_type = BCEnum::AT_Corruption; - - if (ailment_type == BCEnum::AT_None) { - c->Message(Chat::White, "You must specify a cure [ailment] to use this command"); - return; - } - - local_list->sort([ailment_type](STBaseEntry* l, STBaseEntry* r) { - auto _l = l->SafeCastToCure(), _r = r->SafeCastToCure(); - if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] < _r->cure_value[AILMENTIDTOINDEX(ailment_type)]) - return true; - if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] == _r->cure_value[AILMENTIDTOINDEX(ailment_type)] && spells[_l->spell_id].mana < spells[_r->spell_id].mana) - return true; - if (_l->cure_value[AILMENTIDTOINDEX(ailment_type)] == _r->cure_value[AILMENTIDTOINDEX(ailment_type)] && spells[_l->spell_id].mana == spells[_r->spell_id].mana && _l->cure_total < _r->cure_total) - return true; - - return false; - }); - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToCure(); - if (helper_spell_check_fail(local_entry)) - continue; - if (!local_entry->cure_value[AILMENTIDTOINDEX(ailment_type)]) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/escape.cpp b/zone/bot_commands/escape.cpp deleted file mode 100644 index 31fde82a6b..0000000000 --- a/zone/bot_commands/escape.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "../bot_command.h" - -void bot_command_escape(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Escape]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Escape) || helper_command_alias_fail(c, "bot_command_escape", sep->arg[0], "escape")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: lesser])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Escape); - return; - } - - bool use_lesser = false; - if (!strcasecmp(sep->arg[1], "lesser")) - use_lesser = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToEscape(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->lesser != use_lesser) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/identify.cpp b/zone/bot_commands/identify.cpp deleted file mode 100644 index 7ac90a4e4e..0000000000 --- a/zone/bot_commands/identify.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_identify(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Identify]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Identify) || helper_command_alias_fail(c, "bot_command_identify", sep->arg[0], "identify")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Identify); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/invisibility.cpp b/zone/bot_commands/invisibility.cpp deleted file mode 100644 index dd826ebc8a..0000000000 --- a/zone/bot_commands/invisibility.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "../bot_command.h" - -void bot_command_invisibility(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Invisibility]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Invisibility) || helper_command_alias_fail(c, "bot_command_invisibility", sep->arg[0], "invisibility")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [invisibility: living | undead | animal | see]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Invisibility); - return; - } - - std::string invisibility = sep->arg[1]; - - BCEnum::IType invisibility_type = BCEnum::IT_None; - if (!invisibility.compare("living")) - invisibility_type = BCEnum::IT_Living; - else if (!invisibility.compare("undead")) - invisibility_type = BCEnum::IT_Undead; - else if (!invisibility.compare("animal")) - invisibility_type = BCEnum::IT_Animal; - else if (!invisibility.compare("see")) - invisibility_type = BCEnum::IT_See; - - if (invisibility_type == BCEnum::IT_None) { - c->Message(Chat::White, "You must specify an [invisibility]"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToInvisibility(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->invis_type != invisibility_type) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/levitation.cpp b/zone/bot_commands/levitation.cpp deleted file mode 100644 index d4b5cee28e..0000000000 --- a/zone/bot_commands/levitation.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_levitation(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Levitation]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Levitation) || helper_command_alias_fail(c, "bot_command_levitation", sep->arg[0], "levitation")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Levitation); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/mesmerize.cpp b/zone/bot_commands/mesmerize.cpp deleted file mode 100644 index d86fba9f71..0000000000 --- a/zone/bot_commands/mesmerize.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include "../bot_command.h" - -void bot_command_mesmerize(Client *c, const Seperator *sep) -{ - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Mesmerize); - return; - } - - bool isSuccess = false; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto bot_iter : sbl) { - std::list botSpellList = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Mez, c->GetTarget(), IsAEBotSpellType(BotSpellTypes::Mez)); - - for (const auto& s : botSpellList) { - if (!IsValidSpell(s.SpellId)) { - continue; - } - - if (!bot_iter->IsInGroupOrRaid(c)) { - continue; - } - - if (!bot_iter->CastChecks(s.SpellId, c->GetTarget(), BotSpellTypes::Mez, false, false)) { - continue; - } - - if (bot_iter->CommandedDoSpellCast(s.SpellIndex, c->GetTarget(), s.ManaCost)) { - bot_iter->BotGroupSay(bot_iter, "Casting %s [%s] on %s.", GetSpellName(s.SpellId), bot_iter->GetSpellTypeNameByID(BotSpellTypes::Mez), c->GetTarget()->GetCleanName()); - isSuccess = true; - } - } - } - - if (!isSuccess) { - helper_no_available_bots(c); - } -} diff --git a/zone/bot_commands/movement_speed.cpp b/zone/bot_commands/movement_speed.cpp deleted file mode 100644 index d1b8841546..0000000000 --- a/zone/bot_commands/movement_speed.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "../bot_command.h" - -void bot_command_movement_speed(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_MovementSpeed]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_MovementSpeed) || helper_command_alias_fail(c, "bot_command_movement_speed", sep->arg[0], "movementspeed")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([group | sow])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_MovementSpeed); - return; - } - - bool group = false; - bool sow = false; - std::string arg1 = sep->arg[1]; - if (!arg1.compare("group")) - group = true; - else if (!arg1.compare("sow")) - sow = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToMovementSpeed(); - if (helper_spell_check_fail(local_entry)) - continue; - if (!sow && (local_entry->group != group)) - continue; - if (sow && (local_entry->spell_id != 278)) // '278' = single-target "Spirit of Wolf" - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/resistance.cpp b/zone/bot_commands/resistance.cpp deleted file mode 100644 index 0b84b4b88a..0000000000 --- a/zone/bot_commands/resistance.cpp +++ /dev/null @@ -1,73 +0,0 @@ -#include "../bot_command.h" - -void bot_command_resistance(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resistance]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resistance) || helper_command_alias_fail(c, "bot_command_resistance", sep->arg[0], "resistance")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [resistance: fire | cold | poison | disease | magic | corruption]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Resistance); - return; - } - - std::string resistance_arg = sep->arg[1]; - - auto resistance_type = BCEnum::RT_None; - if (!resistance_arg.compare("fire")) - resistance_type = BCEnum::RT_Fire; - else if (!resistance_arg.compare("cold")) - resistance_type = BCEnum::RT_Cold; - else if (!resistance_arg.compare("poison")) - resistance_type = BCEnum::RT_Poison; - else if (!resistance_arg.compare("disease")) - resistance_type = BCEnum::RT_Disease; - else if (!resistance_arg.compare("magic")) - resistance_type = BCEnum::RT_Magic; - else if (!resistance_arg.compare("corruption")) - resistance_type = BCEnum::RT_Corruption; - - if (resistance_type == BCEnum::RT_None) { - c->Message(Chat::White, "You must specify a [resistance]"); - return; - } - - local_list->sort([resistance_type](STBaseEntry* l, STBaseEntry* r) { - auto _l = l->SafeCastToResistance(), _r = r->SafeCastToResistance(); - if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] > _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)]) - return true; - if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana < spells[_r->spell_id].mana) - return true; - if (_l->resist_value[RESISTANCEIDTOINDEX(resistance_type)] == _r->resist_value[RESISTANCEIDTOINDEX(resistance_type)] && spells[_l->spell_id].mana == spells[_r->spell_id].mana && _l->resist_total > _r->resist_total) - return true; - - return false; - }); - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToResistance(); - if (helper_spell_check_fail(local_entry)) - continue; - if (!local_entry->resist_value[RESISTANCEIDTOINDEX(resistance_type)]) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/resurrect.cpp b/zone/bot_commands/resurrect.cpp deleted file mode 100644 index 0b15ef3e11..0000000000 --- a/zone/bot_commands/resurrect.cpp +++ /dev/null @@ -1,57 +0,0 @@ -#include "../bot_command.h" - -void bot_command_resurrect(Client *c, const Seperator *sep) -{ - // Obscure bot spell code prohibits the aoe portion from working correctly... - - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Resurrect]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Resurrect) || helper_command_alias_fail(c, "bot_command_resurrect", sep->arg[0], "resurrect")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - //c->Message(Chat::White, "usage: %s ([option: aoe])", sep->arg[0]); - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Resurrect); - return; - } - - bool aoe = false; - //std::string aoe_arg = sep->arg[1]; - //if (!aoe_arg.compare("aoe")) - // aoe = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToResurrect(); - if (helper_spell_check_fail(local_entry)) - continue; - //if (local_entry->aoe != aoe) - // continue; - if (local_entry->aoe) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - //if (!target_mob && !local_entry->aoe) - // continue; - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - //if (local_entry->aoe) - // target_mob = my_bot; - - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/rune.cpp b/zone/bot_commands/rune.cpp deleted file mode 100644 index 71b1cf572a..0000000000 --- a/zone/bot_commands/rune.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_rune(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Rune]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Rune) || helper_command_alias_fail(c, "bot_command_rune", sep->arg[0], "rune")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Rune); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/send_home.cpp b/zone/bot_commands/send_home.cpp deleted file mode 100644 index 6950e2bee9..0000000000 --- a/zone/bot_commands/send_home.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bot_command.h" - -void bot_command_send_home(Client *c, const Seperator *sep) -{ - // Obscure bot spell code prohibits the aoe portion from working correctly... - - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_SendHome]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_SendHome) || helper_command_alias_fail(c, "bot_command_send_home", sep->arg[0], "sendhome")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s ([option: group])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_SendHome); - return; - } - - bool group = false; - std::string group_arg = sep->arg[1]; - if (!group_arg.compare("group")) - group = true; - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToSendHome(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->group != group) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/size.cpp b/zone/bot_commands/size.cpp deleted file mode 100644 index 69e2fd1a2f..0000000000 --- a/zone/bot_commands/size.cpp +++ /dev/null @@ -1,50 +0,0 @@ -#include "../bot_command.h" - -void bot_command_size(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Size]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Size) || helper_command_alias_fail(c, "bot_command_size", sep->arg[0], "size")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s [grow | shrink]", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Size); - return; - } - - std::string size_arg = sep->arg[1]; - auto size_type = BCEnum::SzT_Reduce; - if (!size_arg.compare("grow")) { - size_type = BCEnum::SzT_Enlarge; - } - else if (size_arg.compare("shrink")) { - c->Message(Chat::White, "This command requires a [grow | shrink] argument"); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToSize(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->size_type != size_type) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/water_breathing.cpp b/zone/bot_commands/water_breathing.cpp deleted file mode 100644 index cb9b792c47..0000000000 --- a/zone/bot_commands/water_breathing.cpp +++ /dev/null @@ -1,38 +0,0 @@ -#include "../bot_command.h" - -void bot_command_water_breathing(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_WaterBreathing]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_WaterBreathing) || helper_command_alias_fail(c, "bot_command_water_breathing", sep->arg[0], "waterbreathing")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: () %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_WaterBreathing); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); - if (!target_mob) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 0138d58ad7..241854dfa5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -21,7 +21,7 @@ #include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/npc_spells_repository.h" -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType, uint16 subType) { if (!tar) { return false; } @@ -48,7 +48,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { return false; } - if (spellType != BotSpellTypes::Resurrect && tar->GetAppearance() == eaDead) { + if ((spellType != BotSpellTypes::Resurrect && spellType != BotSpellTypes::SummonCorpse) && tar->GetAppearance() == eaDead) { if (!((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot())) { return false; } @@ -83,7 +83,12 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { } break; - //SpecialAbility::PacifyImmunity -- TODO bot rewrite + case BotSpellTypes::Lull: + if (tar->GetSpecialAbility(SpecialAbility::PacifyImmunity)) { + return false; + } + + break; case BotSpellTypes::Fear: if (tar->GetSpecialAbility(SpecialAbility::FearImmunity)) { return false; @@ -128,6 +133,17 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { return false; } @@ -182,6 +198,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { return BotCastPet(tar, botClass, botSpell, spellType); case BotSpellTypes::Resurrect: + case BotSpellTypes::SummonCorpse: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { return false; } @@ -197,7 +214,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType) { break; } - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType), subTargetType, subType); for (const auto& s : botSpellList) { @@ -643,7 +660,7 @@ bool Bot::AI_PursueCastCheck() { continue; } - if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -706,7 +723,7 @@ bool Bot::AI_IdleCastCheck() { continue; } - if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -756,7 +773,7 @@ bool Bot::AI_EngagedCastCheck() { continue; } - if (currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. continue; } @@ -979,7 +996,7 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp return result; } -std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE) { +std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE, uint16 subTargetType, uint16 subType) { std::list result; if (botCaster && botCaster->AI_HasSpells()) { @@ -1001,6 +1018,16 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) ) { + if ( + botCaster->IsCommandedSpell() && + ( + !botCaster->IsValidSpellTypeSubType(spellType, subTargetType, botSpellList[i].spellid) || + !botCaster->IsValidSpellTypeSubType(spellType, subType, botSpellList[i].spellid) + ) + ) { + continue; + } + if (!AE && IsAnyAESpell(botSpellList[i].spellid) && !IsGroupSpell(botSpellList[i].spellid)) { continue; } @@ -2040,7 +2067,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) return result; } -uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adjust, move AEs to own rule? +uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: @@ -2072,6 +2099,18 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) //TODO bot rewrite - adj case BotSpellTypes::PetResistBuffs: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: return RuleI(Bots, PercentChanceToCastBuff); case BotSpellTypes::Escape: return RuleI(Bots, PercentChanceToCastEscape); @@ -2949,6 +2988,7 @@ void Bot::CheckBotSpells() { break; } break; + //TODO bot rewrite - add commanded types default: break; @@ -3044,6 +3084,7 @@ void Bot::CheckBotSpells() { else if (IsEffectInSpell(spell_id, SE_Revive)) { correctType = BotSpellTypes::Resurrect; } + //TODO bot rewrite - add commanded types if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" diff --git a/zone/mob.cpp b/zone/mob.cpp index 5ef232a75d..ac435fdb03 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8703,6 +8703,12 @@ uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { } } + for (int i = BotSpellTypes::COMMANDED_START; i <= BotSpellTypes::COMMANDED_END; ++i) { + if (!Strings::ToLower(spellTypeString).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + return UINT16_MAX; } @@ -8875,6 +8881,45 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "Pet Resist Buff"; break; + case BotSpellTypes::Lull: + spellTypeName = "Lull"; + break; + case BotSpellTypes::Teleport: + spellTypeName = "Teleport"; + break; + case BotSpellTypes::Succor: + spellTypeName = "Succor"; + break; + case BotSpellTypes::BindAffinity: + spellTypeName = "Bind Affinity"; + break; + case BotSpellTypes::Identify: + spellTypeName = "Identify"; + break; + case BotSpellTypes::Levitate: + spellTypeName = "Levitate"; + break; + case BotSpellTypes::Rune: + spellTypeName = "Rune"; + break; + case BotSpellTypes::WaterBreathing: + spellTypeName = "Water Breathing"; + break; + case BotSpellTypes::Size: + spellTypeName = "Size"; + break; + case BotSpellTypes::Invisibility: + spellTypeName = "Invisibility"; + break; + case BotSpellTypes::MovementSpeed: + spellTypeName = "Movement Speed"; + break; + case BotSpellTypes::SendHome: + spellTypeName = "Send Home"; + break; + case BotSpellTypes::SummonCorpse: + spellTypeName = "Summon Corpse"; + break; default: break; } @@ -9051,6 +9096,45 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "petresistbuffs"; break; + case BotSpellTypes::Lull: + spellTypeName = "lull"; + break; + case BotSpellTypes::Teleport: + spellTypeName = "teleport"; + break; + case BotSpellTypes::Succor: + spellTypeName = "succor"; + break; + case BotSpellTypes::BindAffinity: + spellTypeName = "bindaffinity"; + break; + case BotSpellTypes::Identify: + spellTypeName = "identify"; + break; + case BotSpellTypes::Levitate: + spellTypeName = "levitate"; + break; + case BotSpellTypes::Rune: + spellTypeName = "rune"; + break; + case BotSpellTypes::WaterBreathing: + spellTypeName = "waterbreathing"; + break; + case BotSpellTypes::Size: + spellTypeName = "size"; + break; + case BotSpellTypes::Invisibility: + spellTypeName = "invisibility"; + break; + case BotSpellTypes::MovementSpeed: + spellTypeName = "movementspeed"; + break; + case BotSpellTypes::SendHome: + spellTypeName = "sendhome"; + break; + case BotSpellTypes::SummonCorpse: + spellTypeName = "summoncorpse"; + break; default: break; } @@ -9058,6 +9142,47 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { return spellTypeName; } +std::string Mob::GetSubTypeNameByID(uint16 subType) { + std::string subTypeName = "null"; + + switch (subType) { + case CommandedSubTypes::SingleTarget: + subTypeName = "SingleTarget"; + break; + case CommandedSubTypes::GroupTarget: + subTypeName = "GroupTarget"; + break; + case CommandedSubTypes::AETarget: + subTypeName = "AETarget"; + break; + case CommandedSubTypes::SeeInvis: + subTypeName = "SeeInvis"; + break; + case CommandedSubTypes::Invis: + subTypeName = "Invis"; + break; + case CommandedSubTypes::InvisUndead: + subTypeName = "InvisUndead"; + break; + case CommandedSubTypes::InvisAnimals: + subTypeName = "InvisAnimals"; + break; + case CommandedSubTypes::Shrink: + subTypeName = "Shrink"; + break; + case CommandedSubTypes::Grow: + subTypeName = "Grow"; + break; + case CommandedSubTypes::Selo: + subTypeName = "Selo"; + break; + default: + break; + } + + return subTypeName; +} + bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { switch (spellType) { case BotSpellTypes::Nuke: diff --git a/zone/mob.h b/zone/mob.h index 8e0a4728ce..8c43f04b96 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -435,7 +435,8 @@ class Mob : public Entity { uint16 GetSpellTypeIDByShortName(std::string spellTypeString); std::string GetSpellTypeNameByID(uint16 spellType); - std::string GetSpellTypeShortNameByID(uint16 spellType); + std::string GetSpellTypeShortNameByID(uint16 spellType); + std::string GetSubTypeNameByID(uint16 subType); bool GetDefaultSpellHold(uint16 spellType, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellDelay(uint16 spellType, uint8 stance = Stance::Balanced); From b2590b50566f11fd4e40e5f85c8f618808335cfd Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 13:51:37 -0600 Subject: [PATCH 135/394] Implement more commanded types properly, move shadownight hate to hateline type... Add incapacitated checks to casting logic and checks. Add candocombat zone check, summon other's corpse for bot, in/out combat spell checks, mute checks, level restriction --- .../database_update_manifest_bots.cpp | 125 ++++++-- common/ruletypes.h | 1 + common/spdat.cpp | 67 ++-- common/spdat.h | 5 +- zone/bot.cpp | 288 +++++++++++------- zone/bot_commands/cast.cpp | 26 +- zone/botspellsai.cpp | 241 +++++++++++---- zone/mob.cpp | 66 +++- zone/spells.cpp | 15 +- 9 files changed, 587 insertions(+), 247 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 96627d8140..72e26142c2 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -513,7 +513,7 @@ UPDATE bot_spells_entries SET `type` = 4 WHERE `spell_id` = 10436; .match = "", .sql = R"( INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) -VALUES +VALUES (3006, 9957, 100, 20, 254), (3006, 9956, 100, 20, 254), (3006, 552, 100, 25, 254), @@ -764,31 +764,6 @@ VALUES (3006, 34863, 101, 96, 254), (3006, 34864, 101, 96, 254), (3006, 34862, 101, 96, 254), -(3007, 4614, 101, 35, 49), -(3007, 4683, 101, 50, 56), -(3007, 4684, 101, 57, 63), -(3007, 4698, 101, 64, 64), -(3007, 5019, 101, 65, 254), -(3007, 5020, 101, 65, 254), -(3007, 6175, 101, 69, 70), -(3007, 10949, 101, 71, 75), -(3007, 10947, 101, 71, 75), -(3007, 10948, 101, 71, 75), -(3007, 14800, 101, 76, 80), -(3007, 14801, 101, 76, 80), -(3007, 14799, 101, 76, 80), -(3007, 18905, 101, 81, 85), -(3007, 18906, 101, 81, 85), -(3007, 18904, 101, 81, 85), -(3007, 25912, 101, 86, 90), -(3007, 25913, 101, 86, 90), -(3007, 25911, 101, 86, 90), -(3007, 29006, 101, 91, 95), -(3007, 29007, 101, 91, 95), -(3007, 29008, 101, 91, 95), -(3007, 35047, 101, 96, 254), -(3007, 35048, 101, 96, 254), -(3007, 35049, 101, 96, 254), (3008, 728, 101, 8, 60), (3008, 3361, 101, 61, 254), (3008, 5370, 101, 66, 70), @@ -1090,6 +1065,104 @@ VALUES (3011, 25555, 112, 86, 90), (3011, 28632, 112, 91, 95), (3011, 34662, 112, 96, 254); +)" + }, + ManifestEntry{ + .version = 9051, + .description = "2024_11_26_remove_sk_icb.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 55", + .condition = "empty", + .match = "", + .sql = R"( +DELETE +FROM bot_spells_entries +WHERE `npc_spells_id` = 3005 +AND `type` = 10; + +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) +VALUES +(3003, 10175, 55, 72, 76), +(3003, 10173, 55, 72, 76), +(3003, 10174, 55, 72, 76), +(3003, 14956, 55, 77, 81), +(3003, 14954, 55, 77, 81), +(3003, 14955, 55, 77, 81), +(3003, 19070, 55, 82, 86), +(3003, 19068, 55, 82, 86), +(3003, 19069, 55, 82, 86), +(3003, 25298, 55, 87, 91), +(3003, 25299, 55, 87, 91), +(3003, 25297, 55, 87, 91), +(3003, 28348, 55, 92, 96), +(3003, 28349, 55, 92, 96), +(3003, 28347, 55, 92, 96), +(3003, 34351, 55, 97, 254), +(3003, 34352, 55, 97, 254), +(3003, 34350, 55, 97, 254), +(3003, 40078, 55, 98, 254), +(3003, 40079, 55, 98, 254), +(3003, 40080, 55, 98, 254), +(3005, 1221, 55, 33, 41), +(3005, 1222, 55, 42, 52), +(3005, 1223, 55, 53, 58), +(3005, 1224, 55, 59, 62), +(3005, 3405, 55, 63, 66), +(3005, 5329, 55, 67, 70), +(3005, 5336, 55, 69, 73), +(3005, 10258, 55, 71, 71), +(3005, 10259, 55, 71, 71), +(3005, 10257, 55, 71, 71), +(3005, 10261, 55, 72, 76), +(3005, 10262, 55, 72, 76), +(3005, 10260, 55, 72, 76), +(3005, 10291, 55, 74, 78), +(3005, 10292, 55, 74, 78), +(3005, 10293, 55, 74, 78), +(3005, 15160, 55, 76, 76), +(3005, 15161, 55, 76, 76), +(3005, 15162, 55, 76, 76), +(3005, 15165, 55, 77, 81), +(3005, 15163, 55, 77, 81), +(3005, 15164, 55, 77, 81), +(3005, 15186, 55, 79, 83), +(3005, 15184, 55, 79, 83), +(3005, 15185, 55, 79, 83), +(3005, 19315, 55, 81, 81), +(3005, 19313, 55, 81, 81), +(3005, 19314, 55, 81, 81), +(3005, 19317, 55, 82, 86), +(3005, 19318, 55, 82, 86), +(3005, 19316, 55, 82, 86), +(3005, 19338, 55, 84, 88), +(3005, 19339, 55, 84, 88), +(3005, 19337, 55, 84, 88), +(3005, 25581, 55, 86, 86), +(3005, 25582, 55, 86, 86), +(3005, 25580, 55, 86, 86), +(3005, 25586, 55, 87, 91), +(3005, 25587, 55, 87, 91), +(3005, 25588, 55, 87, 91), +(3005, 25641, 55, 89, 93), +(3005, 25642, 55, 89, 93), +(3005, 25643, 55, 89, 93), +(3005, 28659, 55, 91, 91), +(3005, 28657, 55, 91, 91), +(3005, 28658, 55, 91, 91), +(3005, 28665, 55, 92, 96), +(3005, 28663, 55, 92, 96), +(3005, 28664, 55, 92, 96), +(3005, 28735, 55, 94, 98), +(3005, 28733, 55, 94, 98), +(3005, 28734, 55, 94, 98), +(3005, 34688, 55, 96, 96), +(3005, 34689, 55, 96, 96), +(3005, 34687, 55, 96, 96), +(3005, 34694, 55, 97, 254), +(3005, 34695, 55, 97, 254), +(3005, 34693, 55, 97, 254), +(3005, 34752, 55, 99, 254), +(3005, 34753, 55, 99, 254), +(3005, 34751, 55, 99, 254); )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/ruletypes.h b/common/ruletypes.h index da5a5f35de..574b348006 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -806,6 +806,7 @@ RULE_INT(Bots, PercentChanceToCastSnare, 75, "The chance for a bot to attempt to RULE_INT(Bots, PercentChanceToCastDOT, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastDispel, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastInCombatBuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastHateLine, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") diff --git a/common/spdat.cpp b/common/spdat.cpp index a691d51972..c4086c0ac3 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2843,13 +2843,8 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: case BotSpellTypes::Lull: + case BotSpellTypes::HateLine: return true; - case BotSpellTypes::InCombatBuff: - if (cls == Class::ShadowKnight) { - return true; - } - - return false; default: return false; } @@ -2898,12 +2893,6 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: - return true; - case BotSpellTypes::InCombatBuff: - if (cls == Class::ShadowKnight) { - return false; - } - return true; default: return false; @@ -3134,12 +3123,7 @@ bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { case BotSpellTypes::PetFastHeals: case BotSpellTypes::PetVeryFastHeals: case BotSpellTypes::PetHoTHeals: - return false; case BotSpellTypes::InCombatBuff: - if (cls && cls == Class::ShadowKnight) { - return true; - } - return false; default: return true; @@ -3150,13 +3134,44 @@ bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls) { switch (spellType) { - case BotSpellTypes::Escape: - if (cls == Class::ShadowKnight) { - return false; - } - - return true; case BotSpellTypes::Pet: + case BotSpellTypes::Succor: + return false; + default: + return true; + } + + return true; +} + +bool SpellTypeRequiresCastChecks(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AEFear: + case BotSpellTypes::AELifetap: + case BotSpellTypes::AEMez: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AERoot: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEStun: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Mez: + case BotSpellTypes::SummonCorpse: + return false; + default: + return true; + } + + return true; +} + +bool SpellTypeRequiresAEChecks(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::AEMez: return false; default: return true; @@ -3263,6 +3278,10 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { bool IsCommandedSpellType(uint16 spellType) { switch (spellType) { + case BotSpellTypes::Charm: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::Resurrect: case BotSpellTypes::Lull: case BotSpellTypes::Teleport: case BotSpellTypes::Succor: @@ -3276,8 +3295,6 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: - //case BotSpellTypes::Charm: - //case BotSpellTypes::Resurrect: //case BotSpellTypes::Cure: //case BotSpellTypes::GroupCures: //case BotSpellTypes::DamageShields: diff --git a/common/spdat.h b/common/spdat.h index cefac6784d..1c3bcf52cc 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -708,6 +708,7 @@ namespace BotSpellTypes constexpr uint16 ResistBuffs = 52; constexpr uint16 PetDamageShields = 53; constexpr uint16 PetResistBuffs = 54; + constexpr uint16 HateLine = 55; // Command Spell Types constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic @@ -725,7 +726,7 @@ namespace BotSpellTypes constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed + constexpr uint16 END = BotSpellTypes::HateLine; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } @@ -747,6 +748,8 @@ bool IsClientBotSpellType(uint16 spellType); bool IsHealBotSpellType(uint16 spellType); bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); +bool SpellTypeRequiresCastChecks(uint16 spellType); +bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); // These should not be used to determine spell category.. diff --git a/zone/bot.cpp b/zone/bot.cpp index 32a70ceb3c..b3158ac35b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5664,9 +5664,7 @@ bool Bot::CastSpell( casting_spell_id || delaytimer || spellend_timer.Enabled() || - IsStunned() || - IsFeared() || - IsMezzed() || + ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) || (IsSilenced() && !IsDiscipline(spell_id)) || (IsAmnesiad() && IsDiscipline(spell_id)) ) { @@ -9403,7 +9401,12 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec if (doPrechecks) { if (spells[spell_id].target_type == ST_Self && tar != this) { - tar = this; + if (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse)) { + //tar = this; + } + else { + tar = this; + } } if (!PrecastChecks(tar, spellType)) { @@ -9419,11 +9422,30 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (spells[spell_id].target_type == ST_Self && tar != this) { + if (IsFeared() || IsSilenced() || IsAmnesiad()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if ( + spells[spell_id].target_type == ST_Self + && tar != this && + (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } + if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if (!CheckSpellRecastTimer(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; @@ -9439,6 +9461,42 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (this == tar && IsSacrificeSpell(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + + if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + + if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { + if (IsBeneficialSpell(spell_id)) { + if (IsEngaged()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + } + } + else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { + if (IsBeneficialSpell(spell_id)) { + if (!IsEngaged()) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + } + } + + if (!IsDiscipline(spell_id)) { + int chance = GetFocusEffect(focusFcMute, spell_id); + + if (chance && zone->random.Roll(chance)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return(false); + } + } + if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; @@ -9523,6 +9581,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::InCombatBuff: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: @@ -9556,6 +9615,11 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { return false; } + if (!tar->CheckSpellLevelRestriction(this, spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9668,6 +9732,13 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } } + break; + case BotSpellTypes::Lull: + if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + break; default: break; @@ -10494,50 +10565,48 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, return 20; case BotSpellTypes::Mez: return 21; - case BotSpellTypes::AEDispel: + case BotSpellTypes::HateLine: return 22; - case BotSpellTypes::Dispel: + case BotSpellTypes::AEDispel: return 23; - case BotSpellTypes::AEDebuff: + case BotSpellTypes::Dispel: return 24; - case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: return 25; - case BotSpellTypes::AESnare: + case BotSpellTypes::Debuff: return 26; - case BotSpellTypes::Snare: + case BotSpellTypes::AESnare: return 27; - case BotSpellTypes::AEFear: + case BotSpellTypes::Snare: return 28; - case BotSpellTypes::Fear: - return 29; case BotSpellTypes::AESlow: - return 30; + return 29; case BotSpellTypes::Slow: - return 31; + return 30; case BotSpellTypes::AERoot: - return 32; + return 31; case BotSpellTypes::Root: - return 33; + return 32; case BotSpellTypes::AEDoT: - return 34; + return 33; case BotSpellTypes::DOT: - return 35; + return 34; case BotSpellTypes::AEStun: - return 36; + return 35; case BotSpellTypes::PBAENuke: - return 37; + return 36; case BotSpellTypes::AENukes: - return 38; + return 37; case BotSpellTypes::AERains: - return 39; + return 38; case BotSpellTypes::Stun: - return 40; + return 39; case BotSpellTypes::Nuke: - return 41; + return 40; case BotSpellTypes::InCombatBuff: - return 42; + return 41; case BotSpellTypes::InCombatBuffSong: - return 43; + return 42; default: return 0; } @@ -10904,8 +10973,6 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::PetDamageShields: case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: - case BotSpellTypes::Teleport: - case BotSpellTypes::Succor: case BotSpellTypes::BindAffinity: case BotSpellTypes::Identify: case BotSpellTypes::Levitate: @@ -10914,8 +10981,6 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Size: case BotSpellTypes::Invisibility: case BotSpellTypes::MovementSpeed: - case BotSpellTypes::SendHome: - case BotSpellTypes::SummonCorpse: return BotSpellTypes::Buff; case BotSpellTypes::AEMez: case BotSpellTypes::Mez: @@ -10950,6 +11015,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Charm: case BotSpellTypes::Escape: case BotSpellTypes::HateRedux: + case BotSpellTypes::HateLine: case BotSpellTypes::InCombatBuff: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: @@ -11021,84 +11087,84 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { } return false; - case BotSpellTypes::Lull: - if (!IsHarmonySpell(spell_id)) { - return true; - } - - return false; - case BotSpellTypes::Teleport: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - return true; - } - - return false; - case BotSpellTypes::Succor: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - return true; - } - - return false; - case BotSpellTypes::BindAffinity: - if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - return true; - } - - return false; - case BotSpellTypes::Identify: - if (IsEffectInSpell(spell_id, SE_Identify)) { - return true; - } - - return false; - case BotSpellTypes::Levitate: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - return true; - } - - return false; - case BotSpellTypes::Rune: - if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - return true; - } - - return false; - case BotSpellTypes::WaterBreathing: - if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { - return true; - } - - return false; - case BotSpellTypes::Size: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - return true; - } - - return false; - case BotSpellTypes::Invisibility: - if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - return true; - } - - return false; - case BotSpellTypes::MovementSpeed: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - return true; - } - - return false; - case BotSpellTypes::SendHome: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - return true; - } - - return false; - case BotSpellTypes::SummonCorpse: - if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - return true; - } - - return false; + //case BotSpellTypes::Lull: + // if (IsHarmonySpell(spell_id)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Teleport: + // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Succor: + // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::BindAffinity: + // if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Identify: + // if (IsEffectInSpell(spell_id, SE_Identify)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Levitate: + // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Rune: + // if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::WaterBreathing: + // if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Size: + // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + // return true; + // } + // + // return false; + //case BotSpellTypes::Invisibility: + // if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::MovementSpeed: + // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::SendHome: + // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + // return true; + // } + // + // return false; + //case BotSpellTypes::SummonCorpse: + // if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + // return true; + // } + // + // return false; default: return true; } @@ -11331,7 +11397,7 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo break; } - if (!m->IsNPC() || !m->CastToNPC()->IsOnHatelist(botCaster->GetOwner())) { + if (!m->IsNPC() || (!IsCommandedSpell() && !m->CastToNPC()->IsOnHatelist(botCaster->GetOwner()))) { continue; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 49112b8693..10238aac20 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -308,7 +308,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* tar = c->GetTarget(); - LogTestDebug("{}: 'Attempting {} [{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme + //LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (!tar) { if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { @@ -338,7 +338,17 @@ void bot_command_cast(Client* c, const Seperator* sep) break; default: - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) { + if ( + (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) || + ( + spellType == BotSpellTypes::Charm && + ( + tar->IsClient() || + tar->IsCorpse() || + tar->GetOwner() + ) + ) + ) { c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); return; @@ -395,18 +405,16 @@ void bot_command_cast(Client* c, const Seperator* sep) /* TODO bot rewrite - - FIX: Depart, SummonCorpse, Lull, - Group Cures, Precombat, Fear/AE Fear - ICB (SK) casting hate on friendly but not hostile? + FIX: Depart + Group Cures, Precombat NEED TO CHECK: precombat, AE Dispel, AE Lifetap - DO I NEED A PBAE CHECK??? */ - if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsStunned() || bot_iter->IsMezzed() || bot_iter->DivineAura() || bot_iter->GetHP() < 0) { + if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; } Mob* newTar = tar; - LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { newTar = bot_iter; } @@ -435,7 +443,7 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - LogTestDebug("{}: {} says, 'Attempting {} [{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 241854dfa5..d3123bf86d 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -116,11 +116,13 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; case BotSpellTypes::InCombatBuff: - if (GetClass() == Class::ShadowKnight && (tar->IsOfClientBot() || (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()))) { + if (!IsCommandedSpell() && GetClass() != Class::Shaman && spellType == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { return false; } - if (!IsCommandedSpell() && GetClass() != Class::Shaman && spellType == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { + break; + case BotSpellTypes::HateLine: + if (!tar->IsNPC()) { return false; } @@ -198,7 +200,6 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return BotCastPet(tar, botClass, botSpell, spellType); case BotSpellTypes::Resurrect: - case BotSpellTypes::SummonCorpse: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { return false; } @@ -267,6 +268,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return true; } + else { LogTestDebug("{} says, '{} [#{}] - [{}] FAILED AIDoSpellCast on {}.'", GetCleanName(), spells[s.SpellId].name, s.SpellId, GetSpellTypeNameByID(spellType), tar->GetCleanName()); } //deleteme } return false; @@ -631,7 +633,7 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } bool Bot::AI_PursueCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -656,11 +658,11 @@ bool Bot::AI_PursueCastCheck() { continue; } - if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. continue; } @@ -680,7 +682,7 @@ bool Bot::AI_PursueCastCheck() { } bool Bot::AI_IdleCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -719,11 +721,11 @@ bool Bot::AI_IdleCastCheck() { continue; } - if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. continue; } @@ -743,7 +745,7 @@ bool Bot::AI_IdleCastCheck() { } bool Bot::AI_EngagedCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0) { + if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -769,11 +771,11 @@ bool Bot::AI_EngagedCastCheck() { continue; } - if (RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType) || currentCast.spellType == BotSpellTypes::Resurrect || currentCast.spellType == BotSpellTypes::Charm) { // Unsupported by AI currently. + if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. continue; } @@ -1045,28 +1047,19 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa } if ( - ( - !botCaster->IsCommandedSpell() || - ( - botCaster->IsCommandedSpell() && - (spellType != BotSpellTypes::Mez && spellType != BotSpellTypes::AEMez) - ) - ) - && - ( - !IsPBAESpell(botSpellList[i].spellid) && - !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType)) - ) + (!botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && SpellTypeRequiresCastChecks(spellType))) && + (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) ) { continue; } if ( - botCaster->IsCommandedSpell() || + botCaster->IsCommandedSpell() || !AE || - (spellType == BotSpellTypes::GroupCures) || - (spellType == BotSpellTypes::AEMez) || - (AE && botCaster->HasValidAETarget(botCaster, botSpellList[i].spellid, spellType, tar)) + ( + SpellTypeRequiresAEChecks(spellType) && + botCaster->HasValidAETarget(botCaster, botSpellList[i].spellid, spellType, tar) + ) ) { BotSpell_wPriority botSpell; botSpell.SpellId = botSpellList[i].spellid; @@ -2099,18 +2092,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) case BotSpellTypes::PetResistBuffs: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - case BotSpellTypes::Teleport: - case BotSpellTypes::Succor: - case BotSpellTypes::BindAffinity: - case BotSpellTypes::Identify: - case BotSpellTypes::Levitate: - case BotSpellTypes::Rune: - case BotSpellTypes::WaterBreathing: - case BotSpellTypes::Size: - case BotSpellTypes::Invisibility: - case BotSpellTypes::MovementSpeed: - case BotSpellTypes::SendHome: - case BotSpellTypes::SummonCorpse: return RuleI(Bots, PercentChanceToCastBuff); case BotSpellTypes::Escape: return RuleI(Bots, PercentChanceToCastEscape); @@ -2124,6 +2105,8 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) return RuleI(Bots, PercentChanceToCastDispel); case BotSpellTypes::InCombatBuff: return RuleI(Bots, PercentChanceToCastInCombatBuff); + case BotSpellTypes::HateLine: + return RuleI(Bots, PercentChanceToCastHateLine); case BotSpellTypes::Mez: return RuleI(Bots, PercentChanceToCastMez); case BotSpellTypes::Slow: @@ -2842,65 +2825,71 @@ void Bot::CheckBotSpells() { switch (s.type) { - case BotSpellTypes::Nuke: //DONE + case BotSpellTypes::Nuke: if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::RegularHeal: //DONE - //if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { + case BotSpellTypes::RegularHeal: if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Root: //DONE + case BotSpellTypes::Root: if (IsEffectInSpell(spell_id, SE_Root)) { valid = true; break; } break; - case BotSpellTypes::Buff: //DONE + case BotSpellTypes::Buff: if (IsAnyBuffSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Pet: //DONE + case BotSpellTypes::Pet: if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { valid = true; break; } break; - case BotSpellTypes::Lifetap: //DONE + case BotSpellTypes::Lifetap: if (IsLifetapSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Snare: //DONE + case BotSpellTypes::Snare: if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::DOT: //DONE + case BotSpellTypes::DOT: if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Dispel: //DONE + case BotSpellTypes::Dispel: if (IsDispelSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::InCombatBuff: //DONE + case BotSpellTypes::InCombatBuff: if ( IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) || + IsAnyBuffSpell(spell_id) + ) { + valid = true; + break; + } + break; + case BotSpellTypes::HateLine: + if ( (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) ) { @@ -2908,37 +2897,37 @@ void Bot::CheckBotSpells() { break; } break; - case BotSpellTypes::Mez: //DONE + case BotSpellTypes::Mez: if (IsMesmerizeSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Charm: //DONE + case BotSpellTypes::Charm: if (IsCharmSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Slow: //DONE + case BotSpellTypes::Slow: if (IsSlowSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Debuff: //DONE + case BotSpellTypes::Debuff: if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::Cure: //DONE + case BotSpellTypes::Cure: if (IsCureSpell(spell_id)) { valid = true; break; } break; - case BotSpellTypes::PreCombatBuff: //DONE + case BotSpellTypes::PreCombatBuff: if ( IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id) && @@ -2950,9 +2939,9 @@ void Bot::CheckBotSpells() { break; } break; - case BotSpellTypes::InCombatBuffSong: //DONE - case BotSpellTypes::OutOfCombatBuffSong: //DONE - case BotSpellTypes::PreCombatBuffSong: //DONE + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: if ( IsBuffSpell(spell_id) && IsBeneficialSpell(spell_id) && @@ -2964,7 +2953,7 @@ void Bot::CheckBotSpells() { break; } break; - case BotSpellTypes::Fear: //DONE + case BotSpellTypes::Fear: if (IsFearSpell(spell_id)) { valid = true; break; @@ -2988,7 +2977,84 @@ void Bot::CheckBotSpells() { break; } break; - //TODO bot rewrite - add commanded types + case BotSpellTypes::Lull: + if (IsHarmonySpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::Teleport: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + valid = true; + break; + } + break; + case BotSpellTypes::Succor: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + valid = true; + break; + } + break; + case BotSpellTypes::BindAffinity: + if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + valid = true; + break; + } + break; + case BotSpellTypes::Identify: + if (IsEffectInSpell(spell_id, SE_Identify)) { + valid = true; + break; + } + break; + case BotSpellTypes::Levitate: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + valid = true; + break; + } + break; + case BotSpellTypes::Rune: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + valid = true; + break; + } + break; + case BotSpellTypes::WaterBreathing: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { + valid = true; + break; + } + break; + case BotSpellTypes::Size: + if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + valid = true; + break; + } + break; + case BotSpellTypes::Invisibility: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + valid = true; + break; + } + break; + case BotSpellTypes::MovementSpeed: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + valid = true; + break; + } + break; + case BotSpellTypes::SendHome: + if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + valid = true; + break; + } + break; + case BotSpellTypes::SummonCorpse: + if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + valid = true; + break; + } + break; default: break; @@ -2997,7 +3063,6 @@ void Bot::CheckBotSpells() { if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { correctType = BotSpellTypes::Nuke; } - //else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id) && (IsRegularPetHealSpell(spell_id) || !IsCureSpell(spell_id))) { else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { correctType = BotSpellTypes::RegularHeal; } @@ -3024,11 +3089,15 @@ void Bot::CheckBotSpells() { } else if ( IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) || + IsAnyBuffSpell(spell_id) + ) { + correctType = BotSpellTypes::InCombatBuff; + } + else if ( (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) ) { - correctType = BotSpellTypes::InCombatBuff; + correctType = BotSpellTypes::HateLine; } else if (IsMesmerizeSpell(spell_id)) { correctType = BotSpellTypes::Mez; @@ -3084,7 +3153,45 @@ void Bot::CheckBotSpells() { else if (IsEffectInSpell(spell_id, SE_Revive)) { correctType = BotSpellTypes::Resurrect; } - //TODO bot rewrite - add commanded types + else if (IsHarmonySpell(spell_id)) { + correctType = BotSpellTypes::Lull; + } + else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + correctType = BotSpellTypes::Teleport; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + correctType = BotSpellTypes::Succor; + } + else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + correctType = BotSpellTypes::BindAffinity; + } + else if (IsEffectInSpell(spell_id, SE_Identify)) { + correctType = BotSpellTypes::Identify; + } + else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + correctType = BotSpellTypes::Levitate; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + correctType = BotSpellTypes::Rune; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { + correctType = BotSpellTypes::WaterBreathing; + } + else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + correctType = BotSpellTypes::Size; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { + correctType = BotSpellTypes::Invisibility; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + correctType = BotSpellTypes::MovementSpeed; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { + correctType = BotSpellTypes::SendHome; + } + else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + correctType = BotSpellTypes::SummonCorpse; + } if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" diff --git a/zone/mob.cpp b/zone/mob.cpp index ac435fdb03..ec1e18efc6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8881,6 +8881,9 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "Pet Resist Buff"; break; + case BotSpellTypes::HateLine: + spellTypeName = "Hate Line"; + break; case BotSpellTypes::Lull: spellTypeName = "Lull"; break; @@ -9096,6 +9099,9 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::PetResistBuffs: spellTypeName = "petresistbuffs"; break; + case BotSpellTypes::HateLine: + spellTypeName = "hateline"; + break; case BotSpellTypes::Lull: spellTypeName = "lull"; break; @@ -9214,8 +9220,11 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case BotSpellTypes::AEFear: case BotSpellTypes::Fear: return true; + case BotSpellTypes::Mez: case BotSpellTypes::AEMez: + case BotSpellTypes::Debuff: case BotSpellTypes::AEDebuff: + case BotSpellTypes::Slow: case BotSpellTypes::AESlow: case BotSpellTypes::HateRedux: switch (stance) { @@ -9232,12 +9241,7 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case Stance::Assist: return true; default: - if (GetClass() == Class::Wizard) { - return true; - } - else { - return false; - } + return false; } case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: @@ -9248,6 +9252,55 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { else { return true; } + case BotSpellTypes::HateLine: + if (GetClass() == Class::ShadowKnight || GetClass() == Class::Paladin) { + switch (stance) { + case Stance::Aggressive: + return false; + default: + return true; + } + } + else { + return true; + } + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::RegularHeal: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::Pet: + case BotSpellTypes::Escape: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Buff: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::PreCombatBuff: default: return false; } @@ -9470,6 +9523,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::PetResistBuffs: case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: + case BotSpellTypes::HateLine: return 100; case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: diff --git a/zone/spells.cpp b/zone/spells.cpp index a57093ee22..4eb7a9fc52 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -796,7 +796,12 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp if (IsGroupSpell(spell_id)) { return true; } else if (spells[spell_id].target_type == ST_Self) { - spell_target = this; + if (IsBot() && (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse))) { + //spell_target = this; + } + else { + spell_target = this; + } } } else { if (IsGroupSpell(spell_id) && spell_target != this) { @@ -1937,7 +1942,13 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // single target spells case ST_Self: { - spell_target = this; + if (IsBot() && (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse))) { + //spell_target = this; + } + else { + spell_target = this; + } + CastAction = SingleTarget; break; } From 2f4d5ebde90000eb08d6e09e4361548c75d0da60 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 19:02:37 -0600 Subject: [PATCH 136/394] more command cleanup --- zone/bot_command.cpp | 60 ++---- zone/bot_command.h | 6 +- zone/bot_commands/cast.cpp | 2 +- zone/bot_commands/copy_settings.cpp | 2 +- zone/bot_commands/default_settings.cpp | 2 +- zone/bot_commands/lull.cpp | 43 ----- zone/bot_commands/spell_aggro_checks.cpp | 2 +- zone/bot_commands/spell_delays.cpp | 2 +- zone/bot_commands/spell_engaged_priority.cpp | 2 +- zone/bot_commands/spell_holds.cpp | 2 +- zone/bot_commands/spell_idle_priority.cpp | 2 +- zone/bot_commands/spell_max_hp_pct.cpp | 2 +- zone/bot_commands/spell_max_mana_pct.cpp | 2 +- zone/bot_commands/spell_max_thresholds.cpp | 2 +- zone/bot_commands/spell_min_hp_pct.cpp | 2 +- zone/bot_commands/spell_min_mana_pct.cpp | 2 +- zone/bot_commands/spell_min_thresholds.cpp | 2 +- zone/bot_commands/spell_pursue_priority.cpp | 2 +- zone/bot_commands/spell_target_count.cpp | 2 +- zone/bot_commands/summon_corpse.cpp | 47 ----- zone/client.cpp | 60 ++++++ zone/client.h | 1 + zone/command.cpp | 2 +- zone/command.h | 2 +- zone/gm_commands/spell_delays.cpp | 185 ++++++++---------- zone/gm_commands/spell_holds.cpp | 186 ++++++++----------- zone/gm_commands/spell_max_thresholds.cpp | 186 ++++++++----------- zone/gm_commands/spell_min_thresholds.cpp | 186 ++++++++----------- 28 files changed, 389 insertions(+), 607 deletions(-) delete mode 100644 zone/bot_commands/lull.cpp delete mode 100644 zone/bot_commands/summon_corpse.cpp diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 7af4109f5b..650f4a5e86 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1325,7 +1325,6 @@ int bot_command_init(void) bot_command_add("inventoryremove", "Removes an item from a bot's inventory", AccountStatus::Player, bot_command_inventory_remove) || bot_command_add("inventorywindow", "Displays all items in a bot's inventory in a pop-up window", AccountStatus::Player, bot_command_inventory_window) || bot_command_add("itemuse", "Elicits a report from spawned bots that can use the item on your cursor (option 'empty' yields only empty slots)", AccountStatus::Player, bot_command_item_use) || - bot_command_add("lull", "Orders a bot to cast a pacification spell", AccountStatus::Player, bot_command_lull) || //TODO bot rewrite - IMPLEMENT bot_command_add("maxmeleerange", "Toggles whether your bot is at max melee range or not. This will disable all special abilities, including taunt.", AccountStatus::Player, bot_command_max_melee_range) || bot_command_add("owneroption", "Sets options available to bot owners", AccountStatus::Player, bot_command_owner_option) || bot_command_add("pet", "Lists the available bot pet [subcommands]", AccountStatus::Player, bot_command_pet) || @@ -1362,7 +1361,6 @@ int bot_command_init(void) bot_command_add("spellsettingsupdate", "Update a bot spell setting entry", AccountStatus::Player, bot_command_spell_settings_update) || bot_command_add("spelltypeids", "Lists spelltypes by ID", AccountStatus::Player, bot_command_spelltype_ids) || bot_command_add("spelltypenames", "Lists spelltypes by shortname", AccountStatus::Player, bot_command_spelltype_names) || - bot_command_add("summoncorpse", "Orders a bot to summon a corpse to its feet", AccountStatus::Player, bot_command_summon_corpse) || //TODO bot rewrite - IMPLEMENT 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) || bot_command_add("timer", "Checks or clears timers of the chosen type.", AccountStatus::GMMgmt, bot_command_timer) || @@ -2102,56 +2100,13 @@ bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::Sp return false; } -void SendSpellTypePrompts(Client *c, bool commandedTypes) { - c->Message( - Chat::Yellow, - fmt::format( - "You can view spell types by ID or shortname: {}, {}, {} / {}, {}, {}", - Saylink::Silent( - fmt::format("^spelltypeids 0-19"), "ID 0-19" - ), - Saylink::Silent( - fmt::format("^spelltypeids 20-39"), "20-39" - ), - Saylink::Silent( - fmt::format("^spelltypeids 40+"), "40+" - ), - Saylink::Silent( - fmt::format("^spelltypenames 0-19"), "Shortname 0-19" - ), - Saylink::Silent( - fmt::format("^spelltypenames 20-39"), "20-39" - ), - Saylink::Silent( - fmt::format("^spelltypenames 40+"), "40+" - ) - ).c_str() - ); - - if (commandedTypes) { - c->Message( - Chat::Yellow, - fmt::format( - "You can view commanded spell types by ID or shortname: {} / {}", - Saylink::Silent( - fmt::format("^spelltypeids commanded"), "ID" - ), - Saylink::Silent( - fmt::format("^spelltypenames commanded"), "Shortname" - ) - ).c_str() - ); - } - - return; -} - -void SendSpellTypeWindow(Client *c, const Seperator* sep) { +void SendSpellTypeWindow(Client* c, const Seperator* sep) { std::string arg0 = sep->arg[0]; std::string arg1 = sep->arg[1]; uint8 minCount = 0; uint8 maxCount = 0; + bool clientOnly = false; if (BotSpellTypes::END <= 19) { minCount = BotSpellTypes::START; @@ -2173,6 +2128,11 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { minCount = BotSpellTypes::COMMANDED_START; maxCount = BotSpellTypes::COMMANDED_END; } + else if (!arg1.compare("client")) { + minCount = BotSpellTypes::START; + maxCount = BotSpellTypes::END; + clientOnly = true; + } else { c->Message(Chat::Yellow, "You must choose a valid range option"); @@ -2222,6 +2182,10 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { ); for (int i = minCount; i <= maxCount; ++i) { + if (clientOnly && !IsClientBotSpellType(i)) { + continue; + } + popup_text += DialogueWindow::TableRow( DialogueWindow::TableCell( fmt::format( @@ -2270,7 +2234,6 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/illusion_block.cpp" #include "bot_commands/inventory.cpp" #include "bot_commands/item_use.cpp" -#include "bot_commands/lull.cpp" #include "bot_commands/max_melee_range.cpp" #include "bot_commands/name.cpp" #include "bot_commands/owner_option.cpp" @@ -2299,7 +2262,6 @@ void SendSpellTypeWindow(Client *c, const Seperator* sep) { #include "bot_commands/spell_target_count.cpp" #include "bot_commands/spelltypes.cpp" #include "bot_commands/summon.cpp" -#include "bot_commands/summon_corpse.cpp" #include "bot_commands/suspend.cpp" #include "bot_commands/taunt.cpp" #include "bot_commands/timer.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 56be996809..ecce9485d7 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1688,7 +1688,6 @@ void bot_command_hold(Client *c, const Seperator *sep); void bot_command_illusion_block(Client* c, const Seperator* sep); void bot_command_inventory(Client *c, const Seperator *sep); void bot_command_item_use(Client *c, const Seperator *sep); -void bot_command_lull(Client *c, const Seperator *sep); void bot_command_max_melee_range(Client* c, const Seperator* sep); void bot_command_owner_option(Client *c, const Seperator *sep); void bot_command_pet(Client *c, const Seperator *sep); @@ -1723,7 +1722,6 @@ void bot_command_spelltype_ids(Client* c, const Seperator* sep); void bot_command_spelltype_names(Client* c, const Seperator* sep); void bot_spell_info_dialogue_window(Client* c, const Seperator *sep); void bot_command_enforce_spell_list(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); void bot_command_timer(Client* c, const Seperator* sep); @@ -1807,6 +1805,6 @@ void helper_send_available_subcommands(Client *bot_owner, const char* command_si void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = Class::None); bool helper_spell_check_fail(STBaseEntry* local_entry); bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type); -void SendSpellTypePrompts(Client *c, bool commandedTypes = false); -void SendSpellTypeWindow(Client *c, const Seperator* sep); +void SendSpellTypeWindow(Client* c, const Seperator* sep); + #endif diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 10238aac20..eb1361d8d8 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -94,7 +94,7 @@ void bot_command_cast(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c, true); + c->SendSpellTypePrompts(true); c->Message( Chat::Yellow, diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 8e41f971a7..359ac8c22e 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -97,7 +97,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 8ed6733a68..b12e183b40 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -91,7 +91,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/lull.cpp b/zone/bot_commands/lull.cpp deleted file mode 100644 index d39f534d16..0000000000 --- a/zone/bot_commands/lull.cpp +++ /dev/null @@ -1,43 +0,0 @@ -#include "../bot_command.h" - -void bot_command_lull(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Lull]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Lull) || helper_command_alias_fail(c, "bot_command_lull", sep->arg[0], "lull")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Lull); - return; - } - - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = actionable_targets.Select(c, local_entry->target_type, ENEMY); - if (!target_mob) - continue; - - //if (spells[local_entry->spell_id].max[EFFECTIDTOINDEX(3)] && spells[local_entry->spell_id].max[EFFECTIDTOINDEX(3)] < target_mob->GetLevel()) - // continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - uint32 dont_root_before = 0; - if (helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id, true, &dont_root_before)) - target_mob->SetDontRootMeBefore(dont_root_before); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index cbcc4706bb..0be650d696 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -92,7 +92,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 9fd37b70a0..d1fb2d6646 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -98,7 +98,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index f27387b590..2c5208d145 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -96,7 +96,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index e30911bd43..d83b5369b7 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -82,7 +82,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index cdb741d44f..9576d4a8f0 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -96,7 +96,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index bcd3614a58..42c552cdb5 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 84cb9abd9a..da8ad9d369 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 385258dddc..ed3316c012 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -98,7 +98,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index be6fe1b9e1..3c512ab3ad 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index f4ffe1bade..43c37ed8cb 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -92,7 +92,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index ffcecf8b74..4c1b11a3a3 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -100,7 +100,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 2721354226..864bc2ba93 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -96,7 +96,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index ad1b897b88..066fe95cc0 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -92,7 +92,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - SendSpellTypePrompts(c); + c->SendSpellTypePrompts(); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/summon_corpse.cpp b/zone/bot_commands/summon_corpse.cpp deleted file mode 100644 index 2a7a5b80f5..0000000000 --- a/zone/bot_commands/summon_corpse.cpp +++ /dev/null @@ -1,47 +0,0 @@ -#include "../bot_command.h" - -void bot_command_summon_corpse(Client *c, const Seperator *sep) -{ - // Same methodology as old command..but, does not appear to work... (note: didn't work there, either...) - - // temp - c->Message(Chat::White, "This command is currently unavailable..."); - return; - - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_SummonCorpse]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_SummonCorpse) || helper_command_alias_fail(c, "bot_command_summon_corpse", sep->arg[0], "summoncorpse")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_SummonCorpse); - return; - } - - Bot* my_bot = nullptr; - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - - bool cast_success = false; - for (auto list_iter : *local_list) { - auto local_entry = list_iter; - if (helper_spell_check_fail(local_entry)) - continue; - - auto target_mob = ActionableTarget::AsSingle_ByPlayer(c); - if (!target_mob) - continue; - - if (spells[local_entry->spell_id].base_value[EFFECTIDTOINDEX(1)] < target_mob->GetLevel()) - continue; - - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); - if (!my_bot) - continue; - - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); - - break; - } - - helper_no_available_bots(c, my_bot); -} diff --git a/zone/client.cpp b/zone/client.cpp index bb1f11a86e..6544ac33a4 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13338,3 +13338,63 @@ std::string Client::SplitCommandHelpText(std::vector msg, std::stri return returnText; } + +void Client::SendSpellTypePrompts(bool commandedTypes, bool clientOnlyTypes) { + if (clientOnlyTypes) { + Message( + Chat::Yellow, + fmt::format( + "You can view spell types by {} or {}.", + Saylink::Silent( + fmt::format("^spelltypeids client"), "ID" + ), + Saylink::Silent( + fmt::format("^spelltypenames client"), "Shortname" + ) + ).c_str() + ); + } + else { + Message( + Chat::Yellow, + fmt::format( + "You can view spell types by {}, {}, {} or by {}, {}, {}.", + Saylink::Silent( + fmt::format("^spelltypeids 0-19"), "ID 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypeids 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypeids 40+"), "40+" + ), + Saylink::Silent( + fmt::format("^spelltypenames 0-19"), "Shortname 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypenames 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypenames 40+"), "40+" + ) + ).c_str() + ); + } + + if (commandedTypes) { + Message( + Chat::Yellow, + fmt::format( + "You can view commanded spell types by {} or {}.", + Saylink::Silent( + fmt::format("^spelltypeids commanded"), "ID" + ), + Saylink::Silent( + fmt::format("^spelltypenames commanded"), "Shortname" + ) + ).c_str() + ); + } + + return; +} diff --git a/zone/client.h b/zone/client.h index 77cf9a574c..2a090eeac4 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1263,6 +1263,7 @@ class Client : public Mob ); std::string GetCommandHelpHeader(std::string color, std::string header); std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor = false, std::string secondaryColor = ""); + void SendSpellTypePrompts(bool commandedTypes = false, bool clientOnlyTypes = false); // Task System Methods void LoadClientTaskState(); diff --git a/zone/command.cpp b/zone/command.cpp index 21f57a7099..8fea9152bb 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -219,7 +219,7 @@ int command_init(void) command_add("spawneditmass", "[Search Criteria] [Edit Option] [Edit Value] [Apply] Mass editing spawn command (Apply is optional, 0 = False, 1 = True, default is False)", AccountStatus::GMLeadAdmin, command_spawneditmass) || command_add("spawnfix", "Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, command_spell_delays) || - command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, command_spell_holds) || + //command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, command_spell_holds) || //currently unusued command_add("spellmaxthresholds", "Controls the minimum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_max_thresholds) || command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_min_thresholds) || command_add("stun", "[duration] - Stuns you or your target for duration", AccountStatus::GMAdmin, command_stun) || diff --git a/zone/command.h b/zone/command.h index aa5771da99..39fc87e170 100644 --- a/zone/command.h +++ b/zone/command.h @@ -172,7 +172,7 @@ void command_spawn(Client *c, const Seperator *sep); void command_spawneditmass(Client *c, const Seperator *sep); void command_spawnfix(Client *c, const Seperator *sep); void command_spell_delays(Client* c, const Seperator* sep); -void command_spell_holds(Client* c, const Seperator* sep); +//void command_spell_holds(Client* c, const Seperator* sep); //currently unusued void command_spell_max_thresholds(Client* c, const Seperator* sep); void command_spell_min_thresholds(Client* c, const Seperator* sep); void command_stun(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp index 09b2b0c5d5..f7fda40ee7 100644 --- a/zone/gm_commands/spell_delays.cpp +++ b/zone/gm_commands/spell_delays.cpp @@ -7,101 +7,85 @@ void command_spell_delays(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 4000] or [%s cures 4000] would allow bots to cast cures on you every 4 seconds.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message( - Chat::White, + std::vector description = + { + "Controls how often bots can cast certain spell types on you" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set Very Fast Heals to be received every 1 second:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 1000", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::VeryFastHeals) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 1000", + sep->arg[0], + BotSpellTypes::VeryFastHeals ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current Regular Heal delay:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::RegularHeal) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::RegularHeal ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -112,18 +96,8 @@ void command_spell_delays(Client* c, const Seperator* sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -133,20 +107,8 @@ void command_spell_delays(Client* c, const Seperator* sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -164,10 +126,11 @@ void command_spell_delays(Client* c, const Seperator* sep) } } + // Enable/Disable/Current checks if (sep->IsNumber(2)) { typeValue = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 1 || typeValue > 60000) { + if (typeValue < 0 || typeValue > 60000) { c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); return; @@ -195,18 +158,18 @@ void command_spell_delays(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your current {} delay is {} seconds.", + "Your [{}] delay is currently {} seconds.'", c->GetSpellTypeNameByID(spellType), c->GetSpellDelay(spellType) / 1000.00 ).c_str() ); } else { - c->SetSpellDelay(spellType, typeValue); + c->SetSpellHold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your {} delay was set to {} seconds.", + "Your [{}] delay was set to {} seconds.'", c->GetSpellTypeNameByID(spellType), c->GetSpellDelay(spellType) / 1000.00 ).c_str() diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp index 90a725dfbb..c9c3641d75 100644 --- a/zone/gm_commands/spell_holds.cpp +++ b/zone/gm_commands/spell_holds.cpp @@ -2,108 +2,94 @@ void command_spell_holds(Client *c, const Seperator *sep) { + //unused for clients + c->Message(Chat::Yellow, "Spell Holds for players is currently unused."); + return; + const int arguments = sep->argnum; if (arguments) { const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 1] or [%s cures 1] would prevent bots from casting cures on you.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message(Chat::White, "note: Set to 0 to unhold the given spell type."); - c->Message(Chat::White, "note: Set to 1 to hold the given spell type."); - c->Message( - Chat::White, + std::vector description = + { + "Toggles whether or not bots can cast certain spell types on you" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set DoTs to be held:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 1", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 1", + sep->arg[0], + BotSpellTypes::DOT ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current DoT settings:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::DOT ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -114,18 +100,8 @@ void command_spell_holds(Client *c, const Seperator *sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -135,20 +111,8 @@ void command_spell_holds(Client *c, const Seperator *sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -198,7 +162,7 @@ void command_spell_holds(Client *c, const Seperator *sep) c->Message( Chat::Green, fmt::format( - "Your current Hold {}s status is {}.", + "Your [{}] spell hold is currently [{}].'", c->GetSpellTypeNameByID(spellType), c->GetSpellHold(spellType) ? "enabled" : "disabled" ).c_str() @@ -209,7 +173,7 @@ void command_spell_holds(Client *c, const Seperator *sep) c->Message( Chat::Green, fmt::format( - "Your Hold {}s status was {}.", + "Your [{}] spell hold was [{}].'", c->GetSpellTypeNameByID(spellType), c->GetSpellHold(spellType) ? "enabled" : "disabled" ).c_str() diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp index a67660d2e8..3e0188baa9 100644 --- a/zone/gm_commands/spell_max_thresholds.cpp +++ b/zone/gm_commands/spell_max_thresholds.cpp @@ -7,101 +7,85 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 95] or [%s cures 95] would allow bots to cast cures on you when you are under 95%% health.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message( - Chat::White, + std::vector description = + { + "Threshold of your own health when bots will start casting the chosen spell type" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set Complete Heals to start at 90% health:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 90", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::CompleteHeal) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 90", + sep->arg[0], + BotSpellTypes::CompleteHeal ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current HoT Heal settings:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::HoTHeals) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::HoTHeals ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -112,18 +96,8 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -133,20 +107,8 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -168,8 +130,8 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { typeValue = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 150) { - c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); return; } @@ -196,18 +158,18 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your current max threshold for {}s is {}%%.", + "Your [{}] maximum hold is currently [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMaxThreshold(spellType) ).c_str() ); } else { - c->SetSpellMaxThreshold(spellType, typeValue); + c->SetSpellHold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your max threshold for {}s was set to {}%%.", + "Your [{}] maximum hold was set to [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMaxThreshold(spellType) ).c_str() diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp index 1b0fcfdf2a..c2b3893d7b 100644 --- a/zone/gm_commands/spell_min_thresholds.cpp +++ b/zone/gm_commands/spell_min_thresholds.cpp @@ -7,101 +7,85 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - c->Message(Chat::White, "usage: %s [spelltype ID | spelltype Shortname] [current | value: 0-1].", sep->arg[0]); - c->Message(Chat::White, "example: [%s 15 15] or [%s cures 15] would prevent bots from casting cures on you when you are under 15%% health.", sep->arg[0], sep->arg[0]); - c->Message(Chat::White, "note: Use [current] to check your current setting."); - c->Message( - Chat::White, + std::vector description = + { + "Threshold of your own health when bots will stop casting the chosen spell type" + }; + + std::vector notes = + { + "- All pet types are control your how your pet will be affected" + }; + + std::vector example_format = + { fmt::format( - "note: Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - std::string arg1 = sep->arg[1]; - - if (!arg1.compare("listid") || !arg1.compare("listname")) { - const std::string& color_red = "red_1"; - const std::string& color_blue = "royal_blue"; - const std::string& color_green = "forest_green"; - const std::string& bright_green = "green"; - const std::string& bright_red = "red"; - const std::string& heroic_color = "gold"; - - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; - - std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} [Type Shortname] [value]" + , sep->arg[0] + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(bright_green, spellTypeField) + "{} [Type ID] [value]" + , sep->arg[0] ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_one = + { + "To set Fast Heals to be stopped at 65% health:", fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(bright_green, idField) : DialogueWindow::ColorMessage(bright_green, shortnameField)) - ) - ) - ); - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( + "{} {} 65", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + ), fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) + "{} {} 65", + sep->arg[0], + BotSpellTypes::FastHeals ) - ) + - DialogueWindow::TableCell( + }; + std::vector examples_two = + { + "To check your current Cure settings:", fmt::format( - "{}", - DialogueWindow::ColorMessage(heroic_color, fillerLine) - ) - ) - ); - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!IsClientBotSpellType(i)) { - continue; - } - - popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}{}", - DialogueWindow::ColorMessage(color_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(color_green, pluralS) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg1.compare("listid") ? DialogueWindow::ColorMessage(color_blue, std::to_string(i)) : DialogueWindow::ColorMessage(color_blue, c->GetSpellTypeShortNameByID(i))) - ) + "{} {} current", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + ), + fmt::format( + "{} {} current", + sep->arg[0], + BotSpellTypes::Cure ) + }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three ); - } - popup_text = DialogueWindow::Table(popup_text); + popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient("Spell Types", popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(false, true); - return; + return; + } } + std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; @@ -112,18 +96,8 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) spellType = atoi(sep->arg[1]); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); return; } @@ -133,20 +107,8 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) spellType = c->GetSpellTypeIDByShortName(arg1); if (!IsClientBotSpellType(spellType)) { - c->Message( - Chat::White, - fmt::format( - "You must choose a valid spell type. Use {} for a list of spell types by ID or {} for a list of spell types by short name.", - Saylink::Silent( - fmt::format("{} listid", sep->arg[0]) - ), - Saylink::Silent( - fmt::format("{} listname", sep->arg[0]) - ) - ).c_str() - ); - - return; + c->Message(Chat::Yellow, "Invalid spell type."); + c->SendSpellTypePrompts(false, true); } } else { @@ -168,8 +130,8 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { typeValue = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 150) { - c->Message(Chat::Yellow, "You must enter a value between 0-150 (0%% to 150%% of health)."); + if (typeValue < 0 || typeValue > 100) { + c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); return; } @@ -196,18 +158,18 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your current min threshold for {}s is {}%%.", + "Your [{}] minimum hold is currently [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMinThreshold(spellType) ).c_str() ); } else { - c->SetSpellMinThreshold(spellType, typeValue); + c->SetSpellHold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your min threshold for {}s was set to {}%%.", + "Your [{}] minimum hold was set to [{}]%%.'", c->GetSpellTypeNameByID(spellType), c->GetSpellMinThreshold(spellType) ).c_str() From 4bb1fd9dcd7dd56993cb9dc60959414a73bcbeeb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:50:21 -0600 Subject: [PATCH 137/394] add aehateline spell type --- common/spdat.cpp | 1 + common/spdat.h | 3 ++- zone/bot.cpp | 49 ++++++++++++++++++++++---------------- zone/bot_commands/cast.cpp | 2 +- zone/botspellsai.cpp | 1 + zone/mob.cpp | 8 +++++++ 6 files changed, 41 insertions(+), 23 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index c4086c0ac3..bef0507df5 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2844,6 +2844,7 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { case BotSpellTypes::PBAENuke: case BotSpellTypes::Lull: case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return true; default: return false; diff --git a/common/spdat.h b/common/spdat.h index 1c3bcf52cc..3f87672b4d 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -709,6 +709,7 @@ namespace BotSpellTypes constexpr uint16 PetDamageShields = 53; constexpr uint16 PetResistBuffs = 54; constexpr uint16 HateLine = 55; + constexpr uint16 AEHateLine = 56; // Command Spell Types constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic @@ -726,7 +727,7 @@ namespace BotSpellTypes constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::HateLine; // Do not remove this, increment as needed + constexpr uint16 END = BotSpellTypes::AEHateLine; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } diff --git a/zone/bot.cpp b/zone/bot.cpp index b3158ac35b..aee9cf207d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10565,48 +10565,50 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, return 20; case BotSpellTypes::Mez: return 21; - case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return 22; - case BotSpellTypes::AEDispel: + case BotSpellTypes::HateLine: return 23; - case BotSpellTypes::Dispel: + case BotSpellTypes::AEDispel: return 24; - case BotSpellTypes::AEDebuff: + case BotSpellTypes::Dispel: return 25; - case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: return 26; - case BotSpellTypes::AESnare: + case BotSpellTypes::Debuff: return 27; - case BotSpellTypes::Snare: + case BotSpellTypes::AESnare: return 28; - case BotSpellTypes::AESlow: + case BotSpellTypes::Snare: return 29; - case BotSpellTypes::Slow: + case BotSpellTypes::AESlow: return 30; - case BotSpellTypes::AERoot: + case BotSpellTypes::Slow: return 31; - case BotSpellTypes::Root: + case BotSpellTypes::AERoot: return 32; - case BotSpellTypes::AEDoT: + case BotSpellTypes::Root: return 33; - case BotSpellTypes::DOT: + case BotSpellTypes::AEDoT: return 34; - case BotSpellTypes::AEStun: + case BotSpellTypes::DOT: return 35; - case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEStun: return 36; - case BotSpellTypes::AENukes: + case BotSpellTypes::PBAENuke: return 37; - case BotSpellTypes::AERains: + case BotSpellTypes::AENukes: return 38; - case BotSpellTypes::Stun: + case BotSpellTypes::AERains: return 39; - case BotSpellTypes::Nuke: + case BotSpellTypes::Stun: return 40; - case BotSpellTypes::InCombatBuff: + case BotSpellTypes::Nuke: return 41; - case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::InCombatBuff: return 42; + case BotSpellTypes::InCombatBuffSong: + return 43; default: return 0; } @@ -11016,6 +11018,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Escape: case BotSpellTypes::HateRedux: case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: case BotSpellTypes::InCombatBuff: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: @@ -11393,6 +11396,10 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo } break; + case BotSpellTypes::AEHateLine: + if (!m->IsNPC()) { + continue; + } default: break; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index eb1361d8d8..fb7f97209a 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -407,7 +407,7 @@ void bot_command_cast(Client* c, const Seperator* sep) TODO bot rewrite - FIX: Depart Group Cures, Precombat - NEED TO CHECK: precombat, AE Dispel, AE Lifetap + NEED TO CHECK: precombat */ if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index d3123bf86d..35cec06d1a 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2076,6 +2076,7 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) case BotSpellTypes::AELifetap: case BotSpellTypes::AERoot: case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEHateLine: return RuleI(Bots, PercentChanceToCastAEs); case BotSpellTypes::GroupHeals: case BotSpellTypes::GroupCompleteHeals: diff --git a/zone/mob.cpp b/zone/mob.cpp index ec1e18efc6..fc4dcd6bd5 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8884,6 +8884,9 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::HateLine: spellTypeName = "Hate Line"; break; + case BotSpellTypes::AEHateLine: + spellTypeName = "AE Hate Line"; + break; case BotSpellTypes::Lull: spellTypeName = "Lull"; break; @@ -9102,6 +9105,9 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::HateLine: spellTypeName = "hateline"; break; + case BotSpellTypes::AEHateLine: + spellTypeName = "aehateline"; + break; case BotSpellTypes::Lull: spellTypeName = "lull"; break; @@ -9219,6 +9225,7 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case BotSpellTypes::Dispel: case BotSpellTypes::AEFear: case BotSpellTypes::Fear: + case BotSpellTypes::AEHateLine: return true; case BotSpellTypes::Mez: case BotSpellTypes::AEMez: @@ -9524,6 +9531,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::ResistBuffs: case BotSpellTypes::Resurrect: case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return 100; case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: From 9279eaa767528038a1f96d1c2989a7d0c25006bb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 20:50:56 -0600 Subject: [PATCH 138/394] debug cleanup --- zone/bot.cpp | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index aee9cf207d..53b7d181bc 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2759,28 +2759,6 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo if (!GetCombatRoundForAlerts()) { SetCombatRoundForAlerts(); - LogTestDebugDetail("{} says, 'I'm {} {}. I am currently {} away {} to be between [{} - {}] away. MMR is {}.'" - , GetCleanName() - , (tar_distance < melee_distance_min ? "too close to" : (tar_distance <= melee_distance ? "within range of" : "too far away from")) - , tar->GetCleanName() - , tar_distance - , (tar_distance <= melee_distance ? "but only needed" : "but need to be") - , melee_distance_min - , melee_distance - , melee_distance_max - ); //deleteme - LogTestDebugDetail("{} says, 'My stance is {} #{}, I am {} taunting. I am set to {} {}, {} at MMR, distanceranged {}, sml {} [{}]'" - , GetCleanName() - , Stance::GetName(GetBotStance()) - , GetBotStance() - , (IsTaunting() ? "currently" : "not") - , (BehindMob() ? "stay behind" : "not stay behind") - , tar->GetCleanName() - , (GetMaxMeleeRange() ? "I stay" : "I do not stay") - , GetBotDistanceRanged() - , GetStopMeleeLevel() - , ((GetLevel() <= GetStopMeleeLevel()) ? "disabled" : "enabled") - ); //deleteme } if (tar_distance <= melee_distance) { From cd3cd9ef1a7899377b2deb65f7fc2ba85c55e9a2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 27 Nov 2024 21:09:51 -0600 Subject: [PATCH 139/394] commanded spell fixes. All should be working now minus depart --- zone/bot.cpp | 75 ++++++++++++++++++++++---------------- zone/bot_commands/cast.cpp | 6 --- zone/botspellsai.cpp | 9 ++--- 3 files changed, 46 insertions(+), 44 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 53b7d181bc..3e95c30a8c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9372,12 +9372,12 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { } bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { - if (!tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme - return false; - } - if (doPrechecks) { + if (!tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + if (spells[spell_id].target_type == ST_Self && tar != this) { if (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse)) { //tar = this; @@ -9393,7 +9393,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } } - LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "nobody")); //deleteme if (!IsValidSpell(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme @@ -9401,26 +9401,17 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (IsFeared() || IsSilenced() || IsAmnesiad()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if ( - spells[spell_id].target_type == ST_Self - && tar != this && - (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) - ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } @@ -9439,11 +9430,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (this == tar && IsSacrificeSpell(spell_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme - return false; - } - if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { LogBotPreChecks("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; @@ -9495,11 +9481,39 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (SpellTypeRequiresTarget(spellType) && !tar) { + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + return false; + } + + if ( + spells[spell_id].target_type == ST_Self + && tar != this && + (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) + ) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (this == tar && IsSacrificeSpell(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + return false; + } + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!CanCastSpellType(spellType, spell_id, tar)) { + return false; + } + + if (IsCommandedSpell()) { //stop checks here for commanded spells + return true; + } + if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9515,13 +9529,13 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9538,11 +9552,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - if (!CanCastSpellType(spellType, spell_id, tar)) { - return false; - } - return true; } @@ -11378,6 +11387,8 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo if (!m->IsNPC()) { continue; } + + break; default: break; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index fb7f97209a..ef5074e89b 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -403,12 +403,6 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - /* - TODO bot rewrite - - FIX: Depart - Group Cures, Precombat - NEED TO CHECK: precombat - */ if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 35cec06d1a..a86b8e6c81 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1042,14 +1042,11 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa IsGroupSpell(botSpellList[i].spellid) && !IsTGBCompatibleSpell(botSpellList[i].spellid) && !botCaster->IsInGroupOrRaid(tar, true) - ) { + ) { continue; } - - if ( - (!botCaster->IsCommandedSpell() || (botCaster->IsCommandedSpell() && SpellTypeRequiresCastChecks(spellType))) && - (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) - ) { + if (spellType == debugSpellType) { LogTestDebugDetail("{} - #{}: [{} #{}] - {} says, '{} #{} - Passed TGB checks.'", __FILE__, __LINE__, botCaster->GetSpellTypeNameByID(spellType), spellType, botCaster->GetCleanName(), spells[botSpellList[i].spellid].name, botSpellList[i].spellid); }; //deleteme + if (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } From 53c227a8b1c94147e91ff56571be4228fc73974e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 00:56:55 -0600 Subject: [PATCH 140/394] add check for self on isingrouporraid --- zone/mob.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/mob.cpp b/zone/mob.cpp index fc4dcd6bd5..d9fad01cff 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9778,6 +9778,10 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { return false; } + if (this == other) { + return true; + } + auto* r = GetRaid(); auto* rO = other->GetRaid(); From 99926ee1024cab8aa525a5e69597a5710e49fd57 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 22:15:02 -0600 Subject: [PATCH 141/394] Allow bots to bypass los checks for positioning if no detrimental types allowed --- common/spdat.cpp | 39 +++++++++++++++++++++++++++++++++++++++ common/spdat.h | 1 + zone/bot.cpp | 20 +++++++------------- 3 files changed, 47 insertions(+), 13 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index bef0507df5..32782c3c27 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3309,3 +3309,42 @@ bool IsCommandedSpellType(uint16 spellType) { return false; } + +bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls) { + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Root: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Mez: + //case BotSpellTypes::Charm: // commanded + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::HateRedux: + //case BotSpellTypes::Fear: // commanded + case BotSpellTypes::Stun: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEMez: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + //case BotSpellTypes::AEFear: // commanded + case BotSpellTypes::AEDispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + // case BotSpellTypes::Lull: // commanded + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: + return true; + default: + return false; + } + + return false; +} diff --git a/common/spdat.h b/common/spdat.h index 3f87672b4d..9d7c0910a8 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -752,6 +752,7 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); +bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only diff --git a/zone/bot.cpp b/zone/bot.cpp index 3e95c30a8c..2491266a81 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11318,14 +11318,14 @@ bool Bot::RequiresLoSForPositioning() { if (GetLevel() < GetStopMeleeLevel()) { return true; } - else if (GetClass() == Class::Bard) { - return false; - } - else if (GetClass() == Class::Cleric) { //TODO bot rewrite - add check to see if spell requires los - return false; + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (BOT_SPELL_TYPES_DETRIMENTAL(i) && !GetSpellHold(i)) { + return true; + } } - return true; + return false; } bool Bot::HasRequiredLoSForPositioning(Mob* tar) { @@ -11333,13 +11333,7 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } - if (GetClass() == Class::Cleric) { //add check to see if spell requires los - return true; - } - else if (GetClass() == Class::Bard && GetLevel() >= GetStopMeleeLevel()) { - return true; - } - if (!DoLosChecks(this, tar)) { + if (RequiresLoSForPositioning() && !DoLosChecks(this, tar)) { return false; } From 7abd67ff263407481733146f5e6f24a3d53e88a2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 22:15:34 -0600 Subject: [PATCH 142/394] add invalid spell id cleanup to bot spell list inserts --- common/database/database_update_manifest_bots.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 72e26142c2..235c160b35 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -1065,6 +1065,13 @@ VALUES (3011, 25555, 112, 86, 90), (3011, 28632, 112, 91, 95), (3011, 34662, 112, 96, 254); + +DELETE +FROM bot_spells_entries +WHERE NOT EXISTS +(SELECT * +FROM spells_new +WHERE bot_spells_entries.spell_id = spells_new.id); )" }, ManifestEntry{ @@ -1163,6 +1170,13 @@ VALUES (3005, 34752, 55, 99, 254), (3005, 34753, 55, 99, 254), (3005, 34751, 55, 99, 254); + +DELETE +FROM bot_spells_entries +WHERE NOT EXISTS +(SELECT * +FROM spells_new +WHERE bot_spells_entries.spell_id = spells_new.id); )" } // -- template; copy/paste this when you need to create a new entry From 8840ecb2216ad0d374cdc4653ae1d3871b4251db Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 22:33:30 -0600 Subject: [PATCH 143/394] misc cleanup --- common/spdat.cpp | 23 ----------------------- zone/bot.cpp | 6 +++--- zone/bot.h | 1 - zone/entity.h | 2 +- 4 files changed, 4 insertions(+), 28 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 32782c3c27..50b7651f4a 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -678,29 +678,6 @@ bool IsAnyNukeOrStunSpell(uint16 spell_id) { } bool IsAnyAESpell(uint16 spell_id) { - //if ( - // spells[spell_id].target_type == ST_Target || - // spells[spell_id].target_type == ST_Self || - // spells[spell_id].target_type == ST_Animal || - // spells[spell_id].target_type == ST_Undead || - // spells[spell_id].target_type == ST_Summoned || - // spells[spell_id].target_type == ST_Tap || - // spells[spell_id].target_type == ST_Pet || - // spells[spell_id].target_type == ST_Corpse || - // spells[spell_id].target_type == ST_Plant || - // spells[spell_id].target_type == ST_Giant || - // spells[spell_id].target_type == ST_Dragon || - // spells[spell_id].target_type == ST_HateList || - // spells[spell_id].target_type == ST_LDoNChest_Cursed || - // spells[spell_id].target_type == ST_Muramite || - // spells[spell_id].target_type == ST_SummonedPet || - // spells[spell_id].target_type == ST_TargetsTarget || - // spells[spell_id].target_type == ST_PetMaster //|| - // //spells[spell_id].target_type == ST_AEBard //TODO needed? - //) { - // return false; - //} - if (IsAESpell(spell_id) || IsPBAENukeSpell(spell_id) || IsPBAESpell(spell_id) || IsAERainSpell(spell_id) || IsAERainNukeSpell(spell_id) || IsAEDurationSpell(spell_id)) { return true; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 2491266a81..37306b6ae3 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1663,7 +1663,7 @@ bool Bot::Process() } } - if (viral_timer.Check()) { // TODO bot rewrite -- necessary for bots? + if (viral_timer.Check()) { VirusEffectProcess(); } @@ -7149,7 +7149,7 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType) { +bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType) { if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, caster->GetClass())) { LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); @@ -10916,7 +10916,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { } if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), BotAISpellRange, spellType)) { + if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), spellType)) { return result; } } diff --git a/zone/bot.h b/zone/bot.h index 2360d73b4d..8d1a600472 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -46,7 +46,6 @@ constexpr uint32 MAG_EPIC_1_0 = 28034; extern WorldServer worldserver; -constexpr int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this constexpr int NegativeItemReuse = -1; // Unlinked timer for items constexpr uint8 SumWater = 1; diff --git a/zone/entity.h b/zone/entity.h index 814a5feb5c..b55c1c7a63 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -628,7 +628,7 @@ class EntityList Client* GetBotOwnerByBotID(const uint32 bot_id); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); - bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate + bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); From 3302869e29091e8d96d76201b025d911b11441c7 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:35:21 -0600 Subject: [PATCH 144/394] implement depart to use spell lists --- zone/bot_command.cpp | 2 +- zone/bot_commands/depart.cpp | 312 ++++++++++++++++++++++++++++------- 2 files changed, 254 insertions(+), 60 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 650f4a5e86..1b00d701a6 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1290,7 +1290,7 @@ int bot_command_init(void) bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || bot_command_add("defaultsettings", "Restores a bot back to default settings", AccountStatus::Player, bot_command_default_settings) || bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || - bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || //TODO bot rewrite - deleteme + bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || bot_command_add("enforcespellsettings", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || bot_command_add("findaliases", "Find available aliases for a bot command", AccountStatus::Player, bot_command_find_aliases) || bot_command_add("follow", "Orders bots to follow a designated target (option 'chain' auto-links eligible spawned bots)", AccountStatus::Player, bot_command_follow) || diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index d9b09d2d4e..80c3c4e221 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -2,102 +2,296 @@ void bot_command_depart(Client* c, const Seperator* sep) { - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Depart) || helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) { - return; - } - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [list | destination] ([option: single])", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Depart); + std::vector description = + { + "Tells bots to list their port locations or port to a specific location" + }; + + std::vector notes = + { + "- This will interrupt any spell currently being cast by bots told to use the command.", + "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells" + }; + + std::vector example_format = + { + fmt::format( + "{} [list | zone shortname] [optional: single | group | ae] [actionable, default: spawned]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To tell everyone to list their portable locations:", + fmt::format( + "{} list spawned", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To tell all bots to port to Nexus:", + fmt::format( + "{} nexus spawned", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To tell Druidbot to single target port to Butcher:", + fmt::format( + "{} butcher single byname Druidbot", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } return; } - std::list sbl; - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - ActionableTarget::Types actionable_targets; - Bot* my_bot = nullptr; bool single = false; + bool single = false; + bool group = true; + bool ae = false; std::string single_arg = sep->arg[2]; - + bool list = false; + std::string destination = sep->arg[1]; + int ab_arg = 2; + if (!single_arg.compare("single")) { + ++ab_arg; single = true; + group = false; + } + else if (!single_arg.compare("group")) { + ++ab_arg; + single = false; + group = true; + } + else if (!single_arg.compare("ae")) { + ++ab_arg; + single = false; + group = false; + ae = true; } - std::string destination = sep->arg[1]; + if (!destination.compare("list") || destination.empty()) { + list = true; - if (!destination.compare("list")) { - Bot* my_druid_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Druid); - Bot* my_wizard_bot = ActionableBots::Select_ByMinLevelAndClass(c, BCEnum::TT_None, sbl, 1, Class::Wizard); - - if ( - (!my_druid_bot && !my_wizard_bot) || - (my_druid_bot && !my_druid_bot->IsInGroupOrRaid(c)) || - (my_wizard_bot && !my_wizard_bot->IsInGroupOrRaid(c)) - ) { - c->Message(Chat::Yellow, "No compatible bots found for %s.", sep->arg[0]); - return; + if (destination.empty()) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); } + } - helper_command_depart_list(c, my_druid_bot, my_wizard_bot, local_list, single); + Mob* tar = c->GetTarget(); - return; + if (!tar) { + tar = c; + } + + std::string argString = sep->arg[ab_arg]; + + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } - else if (destination.empty()) { - c->Message(Chat::White, "A [destination] or [list] argument is required to use this command"); + std::list sbl; + + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - my_bot = nullptr; - sbl.clear(); - MyBots::PopulateSBL_BySpawnedBots(c, sbl); - bool cast_success = false; + sbl.remove(nullptr); - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToDepart(); + BotSpell botSpell; + botSpell.SpellId = 0; + botSpell.SpellIndex = 0; + botSpell.ManaCost = 0; - if (helper_spell_check_fail(local_entry)) { - continue; - } + bool isSuccess = false; + std::map> listZones; - if (local_entry->single != single) { + for (auto bot_iter : sbl) { + if (!bot_iter->IsInGroupOrRaid(tar, !single)) { continue; } - if (destination.compare(spells[local_entry->spell_id].teleport_zone)) { + if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { continue; } - auto target_mob = actionable_targets.Select(c, local_entry->target_type, FRIENDLY); + std::list botSpellListItr = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Teleport, tar); - if (!target_mob) - continue; + for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + if (!IsValidSpell(itr->SpellId)) { + continue; + } - my_bot = ActionableBots::Select_ByMinLevelAndClass(c, local_entry->target_type, sbl, local_entry->spell_level, local_entry->caster_class, target_mob); + if ( + (single && spells[itr->SpellId].target_type != ST_Target) || + (group && !IsGroupSpell(itr->SpellId)) || + (ae && !IsAnyAESpell(itr->SpellId)) + ) { + continue; + } - if (!my_bot) { - continue; - } + if (list) { + auto it = listZones.find(spells[itr->SpellId].teleport_zone); - if (my_bot->BotPassiveCheck()) { - continue; - } + if (it != listZones.end()) { + const auto& [val1, val2] = it->second; - if (!my_bot->IsInGroupOrRaid(c)) { - continue; - } + if (val1 == spells[itr->SpellId].target_type && val2 == bot_iter->GetClass()) { + continue; + } + } - if (local_entry->spell_id != 0 && my_bot->GetMana() < spells[local_entry->spell_id].mana) { - continue; - } + Bot::BotGroupSay( + bot_iter, + fmt::format( + "I can port you to {}.", + Saylink::Silent( + fmt::format( + "{} {} {} byname {}", + sep->arg[0], + spells[itr->SpellId].teleport_zone, + (spells[itr->SpellId].target_type == ST_Target ? "single" : (IsGroupSpell(itr->SpellId) ? "group" : "ae")), + bot_iter->GetCleanName() + ).c_str() + , ZoneLongName(ZoneID(spells[itr->SpellId].teleport_zone))) + ).c_str() + ); + + listZones.insert({ spells[itr->SpellId].teleport_zone, {spells[itr->SpellId].target_type, bot_iter->GetClass()} }); + + continue; + } + + if (destination.compare(spells[itr->SpellId].teleport_zone)) { + continue; + } + + bot_iter->SetCommandedSpell(true); + + if (!IsValidSpellAndLoS(itr->SpellId, bot_iter->HasLoS())) { + continue; + } + + if (IsInvulnerabilitySpell(itr->SpellId)) { + tar = bot_iter; //target self for invul type spells + } - cast_success = helper_cast_standard_spell(my_bot, target_mob, local_entry->spell_id); + if (bot_iter->IsCommandedSpell() && bot_iter->IsCasting()) { + Bot::BotGroupSay( + bot_iter, + fmt::format( + "Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.", + bot_iter->CastingSpellID() ? spells[bot_iter->CastingSpellID()].name : "my spell", + bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), + spells[itr->SpellId].name, + tar->GetCleanName() + ).c_str() + ); - break; + bot_iter->InterruptSpell(); + } + + if (bot_iter->CastSpell(itr->SpellId, tar->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1)) { + if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(BotSpellTypes::Teleport)) { + bot_iter->SetCastedSpellType(UINT16_MAX); + } + else { + bot_iter->SetCastedSpellType(BotSpellTypes::Teleport); + } + + if (bot_iter->GetClass() != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + Bot::BotGroupSay( + bot_iter, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(itr->SpellId), + bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), + (tar == bot_iter ? "myself" : tar->GetCleanName()) + ).c_str() + ); + } + + isSuccess = true; + } + + bot_iter->SetCommandedSpell(false); + + if (isSuccess) { + return; + } + else { + continue; + } + } } - if (!cast_success) { - helper_no_available_bots(c, my_bot); + if ( + (list && listZones.empty()) || + (!list && !isSuccess) + ) { + c->Message( + Chat::Yellow, + fmt::format( + "No bots are capable of that on {}. Be sure they are in the same group, raid or raid group if necessary.", + tar ? tar->GetCleanName() : "you" + ).c_str() + ); } } From f123bcbf805eb8b61abe92e259db2883769eb6c2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 28 Nov 2024 23:35:35 -0600 Subject: [PATCH 145/394] add default spawned note to ^cast --- zone/bot_commands/cast.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index ef5074e89b..9808215419 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -17,11 +17,11 @@ void bot_command_cast(Client* c, const Seperator* sep) std::vector example_format = { fmt::format( - "{} [Type Shortname] [actionable]" + "{} [Type Shortname] [actionable, default: spawned]" , sep->arg[0] ), fmt::format( - "{} [Type ID] [actionable]" + "{} [Type ID] [actionable, default: spawned]" , sep->arg[0] ) }; From c54cb2f423909890703ad4a6f391dd404f2688a0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:36:27 -0600 Subject: [PATCH 146/394] fix AA loading and expansionbitmask saving/loading --- zone/bot.cpp | 7 +++---- zone/bot.h | 1 + zone/bot_database.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 37306b6ae3..e5b6b68e93 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -248,12 +248,12 @@ Bot::Bot( database.botdb.LoadTimers(this); - LoadAAs(); - LoadDefaultBotSettings(); database.botdb.LoadBotSettings(this); + LoadAAs(); + if (database.botdb.LoadBuffs(this)) { //reapply some buffs uint32 buff_count = GetMaxBuffSlots(); @@ -1258,7 +1258,6 @@ int32 Bot::GenerateBaseHitPoints() { } void Bot::LoadAAs() { - aa_ranks.clear(); int id = 0; @@ -10202,7 +10201,7 @@ void Bot::LoadDefaultBotSettings() { uint8 botStance = GetBotStance(); - for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, botStance)); LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); //deleteme } diff --git a/zone/bot.h b/zone/bot.h index 8d1a600472..8426a9b7fc 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -142,6 +142,7 @@ namespace BotBaseSettings { constexpr uint16 HPWhenToMed = 12; constexpr uint16 ManaWhenToMed = 13; + constexpr uint16 START_ALL = ExpansionBitmask; constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players constexpr uint16 END = BotBaseSettings::ManaWhenToMed; // Increment as needed }; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 60f389a9b0..a4d4c83858 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2294,7 +2294,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) if (m->IsBot()) { uint8 botStance = m->CastToBot()->GetBotStance(); - for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, botStance)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, From 49ecb62cc77bde01043b6e14ad5aa4cca3bf0454 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:46:17 -0600 Subject: [PATCH 147/394] Add taunting update on stance change when necessary --- zone/bot_commands/bot.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index f9a3ecfe91..b91d5a0a78 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1404,6 +1404,25 @@ void bot_command_stance(Client *c, const Seperator *sep) bot_iter->SetBotStance(value); bot_iter->LoadDefaultBotSettings(); database.botdb.LoadBotSettings(bot_iter); + + if ( + (bot_iter->GetClass() == Class::Warrior || bot_iter->GetClass() == Class::Paladin || bot_iter->GetClass() == Class::ShadowKnight) && + (bot_iter->GetBotStance() == Stance::Aggressive) + ) { + bot_iter->SetTaunting(true); + + if (bot_iter->HasPet() && bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) { + bot_iter->GetPet()->CastToNPC()->SetTaunting(true); + } + } + else { + bot_iter->SetTaunting(false); + + if (bot_iter->HasPet() && bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) { + bot_iter->GetPet()->CastToNPC()->SetTaunting(false); + } + } + bot_iter->Save(); ++success_count; } From 7a68b4d70c9954233d2a47337e99717973a9bfed Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 20:46:50 -0600 Subject: [PATCH 148/394] Clean up and fix any melee attacks to line up with clients --- zone/bot.cpp | 149 +++++++++++++++++++++++++++------------------------ zone/bot.h | 3 +- 2 files changed, 79 insertions(+), 73 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e5b6b68e93..8840f98320 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1781,29 +1781,43 @@ void Bot::BotMeditate(bool isSitting) { } } -void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { - if (!TargetValidation(other)) { return; } +bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { + if (!other || !IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { + return false; + } + + if ( + !GetPullingFlag() && + ( + (GetBotStance() != Stance::Aggressive && GetBotStance() != Stance::Burn && GetBotStance() != Stance::AEBurn) && + other->GetHPRatio() > 99.0f + ) + ) { + return false; + } if (!CanDoubleAttack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { LogCombatDetail("Bot ranged attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); - Message(0, "Error: Timer not up. Attack %d, ranged %d", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); - return; + return false; } const auto rangedItem = GetBotItem(EQ::invslot::slotRange); const EQ::ItemData* RangeWeapon = nullptr; + if (rangedItem) { RangeWeapon = rangedItem->GetItem(); } if (!RangeWeapon) { - return; + return false; } const auto ammoItem = GetBotItem(EQ::invslot::slotAmmo); const EQ::ItemData* Ammo = nullptr; - if (ammoItem) + + if (ammoItem) { Ammo = ammoItem->GetItem(); + } // Bow requires arrows if ( @@ -1829,7 +1843,7 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { } } - return; + return false; } LogCombatDetail("Ranged attacking [{}] with {} [{}] ([{}]){}{}{}", @@ -1842,10 +1856,6 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? std::to_string(Ammo->ID) : "") ); - if (!IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { - return; - } - SendItemAnimation(other, Ammo, (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? EQ::skills::SkillArchery : EQ::skills::SkillThrowing)); if (RangeWeapon->ItemType == EQ::item::ItemTypeBow) { DoArcheryAttackDmg(other, rangedItem, ammoItem); // watch @@ -1863,7 +1873,7 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { ) ) { ammoItem->SetCharges((ammoItem->GetCharges() - 1)); - LogCombat("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); + LogCombatDetail("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); if (ammoItem->GetCharges() < 1) { RemoveBotItemBySlot(EQ::invslot::slotAmmo); @@ -1871,10 +1881,10 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { } } else if (!consumes_ammo) { - LogCombat("Archery Ammo Consumption is disabled."); + LogCombatDetail("Archery Ammo Consumption is disabled."); } else { - LogCombat("Endless Quiver prevented Ammo Consumption."); + LogCombatDetail("Endless Quiver prevented Ammo Consumption."); } } else { @@ -1882,7 +1892,7 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { // Consume Ammo, unless Ammo Consumption is disabled if (RuleB(Bots, BotThrowingConsumesAmmo)) { ammoItem->SetCharges((ammoItem->GetCharges() - 1)); - LogCombat("Consumed Throwing Ammo from slot {}.", EQ::invslot::slotAmmo); + LogCombatDetail("Consumed Throwing Ammo from slot {}.", EQ::invslot::slotAmmo); if (ammoItem->GetCharges() < 1) { RemoveBotItemBySlot(EQ::invslot::slotAmmo); @@ -1890,45 +1900,51 @@ void Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { } } else { - LogCombat("Throwing Ammo Consumption is disabled."); + LogCombatDetail("Throwing Ammo Consumption is disabled."); } } CommonBreakInvisibleFromCombat(); + + return true; } bool Bot::CheckBotDoubleAttack(bool tripleAttack) { //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) - uint32 bonusGiveDA = (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack); - // If you don't have the double attack skill, return - if (!GetSkill(EQ::skills::SkillDoubleAttack) && !(GetClass() == Class::Bard || GetClass() == Class::Beastlord)) + uint32 bonus_give_double_attack = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; + + if (!GetSkill(EQ::skills::SkillDoubleAttack) && !bonus_give_double_attack) { return false; + } - // You start with no chance of double attacking float chance = 0.0f; uint16 skill = GetSkill(EQ::skills::SkillDoubleAttack); - int32 bonusDA = (aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance); - //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. - if (skill) - chance = ((float(skill + GetLevel()) * (float(100.0f + bonusDA + bonusGiveDA) / 100.0f)) / 500.0f); - else - chance = ((float(bonusGiveDA) * (float(100.0f + bonusDA) / 100.0f)) / 100.0f); - //Live now uses a static Triple Attack skill (lv 46 = 2% lv 60 = 20%) - We do not have this skill on EMU ATM. - //A reasonable forumla would then be TA = 20% * chance - //AA's can also give triple attack skill over cap. (ie Burst of Power) NOTE: Skill ID in spell data is 76 (Triple Attack) - //Kayen: Need to decide if we can implement triple attack skill before working in over the cap effect. - if (tripleAttack) { - // Only some Double Attack classes get Triple Attack [This is already checked in client_processes.cpp] - int32 triple_bonus = (spellbonuses.TripleAttackChance + itembonuses.TripleAttackChance); - chance *= 0.2f; //Baseline chance is 20% of your double attack chance. - chance *= (float(100.0f + triple_bonus) / 100.0f); //Apply modifiers. + int32 bonus_double_attack = 0; + if ((GetClass() == Class::Paladin || GetClass() == Class::ShadowKnight) && (!HasTwoHanderEquipped())) { + LogCombatDetail("Knight class without a 2 hand weapon equipped = No DA Bonus!"); + } + else { + bonus_double_attack = aabonuses.DoubleAttackChance + spellbonuses.DoubleAttackChance + itembonuses.DoubleAttackChance; } - if (zone->random.Real(0, 1) < chance) - return true; + //Use skill calculations otherwise, if you only have AA applied GiveDoubleAttack chance then use that value as the base. + if (skill) { + chance = (float(skill + GetLevel()) * (float(100.0f + bonus_double_attack + bonus_give_double_attack) / 100.0f)) / 500.0f; + } + else { + chance = (float(bonus_give_double_attack + bonus_double_attack) / 100.0f); + } - return false; + LogCombatDetail( + "skill [{}] bonus_give_double_attack [{}] bonus_double_attack [{}] chance [{}]", + skill, + bonus_give_double_attack, + bonus_double_attack, + chance + ); + + return zone->random.Roll(chance); } bool Bot::CheckTripleAttack() @@ -2202,17 +2218,16 @@ void Bot::AI_Process() } } else { - if (atCombatRange && IsBotRanged()){ - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - - TryRangedAttack(tar); - - if (!TargetValidation(tar)) { return; } + //TODO bot rewrite - add casting AI to this + if (atCombatRange && IsBotRanged() && ranged_timer.Check(false)) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - if (CheckDoubleRangedAttack()) { + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { BotRangedAttack(tar, true); } + ranged_timer.Start(); + return; } } @@ -2257,13 +2272,11 @@ void Bot::AI_Process() } if (IsBotRanged() && ranged_timer.Check(false)) { - TryRangedAttack(tar); - - if (!TargetValidation(tar)) { return; } - - if (CheckDoubleRangedAttack()) { + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { BotRangedAttack(tar, true); } + + ranged_timer.Start(); } else if (!IsBotRanged() && GetLevel() < stopMeleeLevel) { if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) { @@ -2553,7 +2566,7 @@ void Bot::DoAttackRounds(Mob* target, int hand) { (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack) > 0; if (candouble) { - if (CastToClient()->CheckDoubleAttack()) { + if (CheckBotDoubleAttack()) { Attack(target, hand, false, false); if (hand == EQ::invslot::slotPrimary) { @@ -2595,8 +2608,8 @@ void Bot::DoAttackRounds(Mob* target, int hand) { if (hand == EQ::invslot::slotPrimary && CanThisClassTripleAttack()) { if (CheckTripleAttack()) { Attack(target, hand, false, false); - int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance + - itembonuses.FlurryChance; + + int flurry_chance = aabonuses.FlurryChance + spellbonuses.FlurryChance + itembonuses.FlurryChance; if (flurry_chance && zone->random.Roll(flurry_chance)) { Attack(target, hand, false, false); @@ -2604,7 +2617,15 @@ void Bot::DoAttackRounds(Mob* target, int hand) { if (zone->random.Roll(flurry_chance)) { Attack(target, hand, false, false); } - //MessageString(Chat::NPCFlurry, YOU_FLURRY); //TODO bot rewrite - add output to others hits with flurry message + + if (GetOwner()) { + GetOwner()->MessageString( + Chat::NPCFlurry, + NPC_FLURRY, + GetCleanName(), + target->GetCleanName() + ); //TODO bot rewrite - add output to others hits with flurry message + } } } } @@ -2612,22 +2633,6 @@ void Bot::DoAttackRounds(Mob* target, int hand) { } } -bool Bot::TryRangedAttack(Mob* tar) { - - if (IsBotRanged() && ranged_timer.Check(false)) { - - if (!TargetValidation(tar)) { return false; } - - if (GetPullingFlag() || GetTarget()->GetHPRatio() <= 99.0f) { - BotRangedAttack(tar); - } - - return true; - } - - return false; -} - bool Bot::TryFacingTarget(Mob* tar) { if (!IsSitting() && !IsFacingMob(tar)) { FaceTarget(tar); @@ -11307,8 +11312,10 @@ void Bot::DoCombatPositioning( bool Bot::CheckDoubleRangedAttack() { int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; - if (chance && zone->random.Roll(chance)) + + if (chance && zone->random.Roll(chance)) { return true; + } return false; } diff --git a/zone/bot.h b/zone/bot.h index 8426a9b7fc..c8f6e4a8eb 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -979,14 +979,13 @@ class Bot : public NPC { // Try Combat Methods bool TryEvade(Mob* tar); bool TryFacingTarget(Mob* tar); - bool TryRangedAttack(Mob* tar); bool TryPursueTarget(float leash_distance, glm::vec3& Goal); bool TryMeditate(); bool TryAutoDefend(Client* bot_owner, float leash_distance); bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); bool TryBardMovementCasts(); - void BotRangedAttack(Mob* other, bool CanDoubleAttack = false); + bool BotRangedAttack(Mob* other, bool CanDoubleAttack = false); bool CheckDoubleRangedAttack(); // Public "Refactor" Methods From 53d4bc4e48aa7818fb16fb474109f8635ce8d5a1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:05:50 -0600 Subject: [PATCH 149/394] remove unnecessary messages on silence /block for bots --- zone/bot.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8840f98320..fd410b3f94 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5653,13 +5653,13 @@ bool Bot::CastSpell( LogSpellsDetail("Spell casting canceled: not able to cast now. Valid? [{}] casting [{}] waiting? [{}] spellend? [{}] stunned? [{}] feared? [{}] mezed? [{}] silenced? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); - if (IsSilenced() && !IsDiscipline(spell_id)) { - MessageString(Chat::White, SILENCED_STRING); - } - - if (IsAmnesiad() && IsDiscipline(spell_id)) { - MessageString(Chat::White, MELEE_SILENCE); - } + //if (IsSilenced() && !IsDiscipline(spell_id)) { + // MessageString(Chat::White, SILENCED_STRING); + //} + // + //if (IsAmnesiad() && IsDiscipline(spell_id)) { + // MessageString(Chat::White, MELEE_SILENCE); + //} if (casting_spell_id) { AI_Bot_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); @@ -5670,7 +5670,7 @@ bool Bot::CastSpell( } if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - MessageString(Chat::White, SPELL_WOULDNT_HOLD); + //MessageString(Chat::White, SPELL_WOULDNT_HOLD); if (casting_spell_id) { AI_Bot_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); } From 003598a348cae0104c735aaf10c7a1180cd57c72 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:31:39 -0600 Subject: [PATCH 150/394] fix changes made to mercs by mistake --- zone/merc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/merc.cpp b/zone/merc.cpp index d66501efad..c54585f9eb 100644 --- a/zone/merc.cpp +++ b/zone/merc.cpp @@ -1500,7 +1500,7 @@ bool Merc::AI_IdleCastCheck() { bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) { - if (BOT_SPELL_TYPES_DETRIMENTAL(iSpellTypes)) { + if ((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) { //according to live, you can buff and heal through walls... //now with PCs, this only applies if you can TARGET the target, but // according to Rogean, Live NPCs will just cast through walls/floors, no problem.. @@ -1569,7 +1569,7 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon float dist2 = 0; - if (mercSpell.type == SpellType_Escape) { + if (mercSpell.type & SpellType_Escape) { dist2 = 0; } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); From 8c923a6c517564e3748ea2c54475c1ff5e67522e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:33:21 -0600 Subject: [PATCH 151/394] rename BOT_SPELL_TYPE functions --- common/spdat.cpp | 10 +++++----- common/spdat.h | 8 ++++---- zone/bot.cpp | 14 +++++++------- zone/bot_commands/cast.cpp | 8 ++++---- zone/bot_commands/depart.cpp | 2 +- zone/botspellsai.cpp | 6 +++--- zone/mob.cpp | 8 ++++---- 7 files changed, 28 insertions(+), 28 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 50b7651f4a..5c8064f8c5 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2791,7 +2791,7 @@ bool IsLichSpell(uint16 spell_id) return false; } -bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { +bool IsBotSpellTypeDetrimental(uint16 spellType, uint8 cls) { switch (spellType) { case BotSpellTypes::Nuke: case BotSpellTypes::Root: @@ -2830,7 +2830,7 @@ bool BOT_SPELL_TYPES_DETRIMENTAL(uint16 spellType, uint8 cls) { return false; } -bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { +bool IsBotSpellTypeBeneficial(uint16 spellType, uint8 cls) { switch (spellType) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: @@ -2879,7 +2879,7 @@ bool BOT_SPELL_TYPES_BENEFICIAL(uint16 spellType, uint8 cls) { return false; } -bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { +bool IsBotSpellTypeOtherBeneficial(uint16 spellType) { switch (spellType) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: @@ -2922,7 +2922,7 @@ bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType) { return false; } -bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { +bool IsBotSpellTypeInnate(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: case BotSpellTypes::AERains: @@ -2955,7 +2955,7 @@ bool BOT_SPELL_TYPES_INNATE(uint16 spellType) { } bool IsBotSpellType(uint16 spellType) { - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && BOT_SPELL_TYPES_BENEFICIAL(spellType) && BOT_SPELL_TYPES_INNATE(spellType)) { + if (IsBotSpellTypeDetrimental(spellType) && IsBotSpellTypeBeneficial(spellType) && IsBotSpellTypeInnate(spellType)) { return true; } diff --git a/common/spdat.h b/common/spdat.h index 9d7c0910a8..adf9c05e47 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -736,10 +736,10 @@ const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellT const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); -bool BOT_SPELL_TYPES_DETRIMENTAL (uint16 spellType, uint8 cls = 0); -bool BOT_SPELL_TYPES_BENEFICIAL (uint16 spellType, uint8 cls = 0); -bool BOT_SPELL_TYPES_OTHER_BENEFICIAL(uint16 spellType); -bool BOT_SPELL_TYPES_INNATE (uint16 spellType); +bool IsBotSpellTypeDetrimental (uint16 spellType, uint8 cls = 0); +bool IsBotSpellTypeBeneficial (uint16 spellType, uint8 cls = 0); +bool IsBotSpellTypeOtherBeneficial(uint16 spellType); +bool IsBotSpellTypeInnate (uint16 spellType); bool IsBotSpellType (uint16 spellType); bool IsAEBotSpellType(uint16 spellType); bool IsGroupBotSpellType(uint16 spellType); diff --git a/zone/bot.cpp b/zone/bot.cpp index fd410b3f94..853acf1fbc 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7155,7 +7155,7 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType) { - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, caster->GetClass())) { + if (IsBotSpellTypeDetrimental(spellType, caster->GetClass())) { LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); return false; } @@ -10247,7 +10247,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spellType, Mob* tar, bool preCast) { return; } - if (!preCast && BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + if (!preCast && IsBotSpellTypeOtherBeneficial(spellType)) { return; } @@ -10265,7 +10265,7 @@ void Bot::SetBotSpellRecastTimer(uint16 spellType, Mob* tar, bool preCast) { if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { return tar->GetOwner()->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); } - else if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + else if (IsBotSpellTypeOtherBeneficial(spellType)) { tar->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); } else { @@ -10396,7 +10396,7 @@ uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, ui } uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance) { - if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, botClass)) { + if (!IsBotSpellTypeBeneficial(spellType, botClass)) { return 0; } @@ -10658,7 +10658,7 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance) { - if (!BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + if (!IsBotSpellTypeBeneficial(spellType, GetClass())) { return RuleI(Bots, SpellResistLimit); } else { @@ -10913,7 +10913,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { return result; } - if (BOT_SPELL_TYPES_BENEFICIAL(spellType, GetClass())) { + if (IsBotSpellTypeBeneficial(spellType, GetClass())) { if (!PrecastChecks(this, spellType) || !AICastSpell(this, GetChanceToCastBySpellType(spellType), spellType)) { if (GetClass() == Class::Bard) { return result; @@ -11326,7 +11326,7 @@ bool Bot::RequiresLoSForPositioning() { } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (BOT_SPELL_TYPES_DETRIMENTAL(i) && !GetSpellHold(i)) { + if (IsBotSpellTypeDetrimental(i) && !GetSpellHold(i)) { return true; } } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 9808215419..9b92636703 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -339,7 +339,7 @@ void bot_command_cast(Client* c, const Seperator* sep) break; default: if ( - (BOT_SPELL_TYPES_DETRIMENTAL(spellType) && !c->IsAttackAllowed(tar)) || + (IsBotSpellTypeDetrimental(spellType) && !c->IsAttackAllowed(tar)) || ( spellType == BotSpellTypes::Charm && ( @@ -354,7 +354,7 @@ void bot_command_cast(Client* c, const Seperator* sep) return; } - if (BOT_SPELL_TYPES_BENEFICIAL(spellType)) { + if (IsBotSpellTypeBeneficial(spellType)) { if ( (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) || ((tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner()))) @@ -418,14 +418,14 @@ void bot_command_cast(Client* c, const Seperator* sep) } if ( - BOT_SPELL_TYPES_BENEFICIAL(spellType) && + IsBotSpellTypeBeneficial(spellType) && !RuleB(Bots, CrossRaidBuffingAndHealing) && !bot_iter->IsInGroupOrRaid(newTar, true) ) { continue; } - if (BOT_SPELL_TYPES_DETRIMENTAL(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { + if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { bot_iter->BotGroupSay( bot_iter, fmt::format( diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 80c3c4e221..1facaef1fc 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -249,7 +249,7 @@ void bot_command_depart(Client* c, const Seperator* sep) } if (bot_iter->CastSpell(itr->SpellId, tar->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1)) { - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(BotSpellTypes::Teleport)) { + if (IsBotSpellTypeOtherBeneficial(BotSpellTypes::Teleport)) { bot_iter->SetCastedSpellType(UINT16_MAX); } else { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index a86b8e6c81..a5718a2dc7 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -243,7 +243,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + if (IsBotSpellTypeOtherBeneficial(spellType)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { @@ -293,7 +293,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellT } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType)) { + if (IsBotSpellTypeOtherBeneficial(spellType)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { @@ -2514,7 +2514,7 @@ DBbotspells_Struct* ZoneDatabase::GetBotSpells(uint32 bot_spell_id) entry.bucket_comparison = e.bucket_comparison; // some spell types don't make much since to be priority 0, so fix that - if (!BOT_SPELL_TYPES_INNATE(entry.type) && entry.priority == 0) { + if (!IsBotSpellTypeInnate(entry.type) && entry.priority == 0) { entry.priority = 1; } diff --git a/zone/mob.cpp b/zone/mob.cpp index d9fad01cff..355292b69f 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9614,7 +9614,7 @@ uint16 Mob::GetUltimateSpellDelay(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->GetSpellDelay(spellType); } @@ -9630,7 +9630,7 @@ bool Mob::GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar) { return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->SpellTypeRecastCheck(spellType); } @@ -9646,7 +9646,7 @@ uint8 Mob::GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->GetSpellMinThreshold(spellType); } @@ -9662,7 +9662,7 @@ uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar) { return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spellType)); } - if (BOT_SPELL_TYPES_OTHER_BENEFICIAL(spellType) && tar->IsOfClientBot()) { + if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { return tar->GetSpellMaxThreshold(spellType); } From 6ef444a77122bb35386b7265aa87a258ce88ac4b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:34:40 -0600 Subject: [PATCH 152/394] remove unused functions --- common/spdat.cpp | 8 -------- zone/bot.h | 2 -- 2 files changed, 10 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 5c8064f8c5..e9aec626ca 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2954,14 +2954,6 @@ bool IsBotSpellTypeInnate(uint16 spellType) { return false; } -bool IsBotSpellType(uint16 spellType) { - if (IsBotSpellTypeDetrimental(spellType) && IsBotSpellTypeBeneficial(spellType) && IsBotSpellTypeInnate(spellType)) { - return true; - } - - return false; -} - bool IsAEBotSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AEDebuff: diff --git a/zone/bot.h b/zone/bot.h index c8f6e4a8eb..bf785cfc56 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -991,8 +991,6 @@ class Bot : public NPC { // Public "Refactor" Methods static bool CheckCampSpawnConditions(Client* c); - inline bool CommandedDoSpellCast(int32 i, Mob* tar, int32 mana_cost) { return AIDoSpellCast(i, tar, mana_cost); } - protected: void BotMeditate(bool isSitting); bool CheckBotDoubleAttack(bool Triple = false); From 036b5244598fcacd372f93df8c07e4e72d231bff Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:35:21 -0600 Subject: [PATCH 153/394] add !commandedspell() check to aggro checks on cast --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 853acf1fbc..fc8e110cd5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10908,7 +10908,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { Mob* tar = GetTarget(); - if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { + if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme return result; } From f74fd59b62f0ad3de96a19c2665a3c726f1bdb15 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 1 Dec 2024 23:37:39 -0600 Subject: [PATCH 154/394] Update cast.cpp --- zone/bot_commands/cast.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 9b92636703..a524068105 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -451,6 +451,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } bot_iter->SetCommandedSpell(false); + continue; } From faf372d31d7afc3cc079baebcc5c4433b6ac59b9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 2 Dec 2024 07:32:30 -0600 Subject: [PATCH 155/394] Implement spell AI pulling, fix throw stone --- common/ruletypes.h | 4 +++- common/spdat.cpp | 19 ++++++++++++++++++ common/spdat.h | 2 +- zone/bot.cpp | 49 ++++++++++++++++++++++++++++++++++------------ zone/bot.h | 3 +++ 5 files changed, 62 insertions(+), 15 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 574b348006..0d66fd1cf8 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -834,8 +834,10 @@ RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summ RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.") -RULE_BOOL(Bots, AllowSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") +RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots") +RULE_BOOL(Bots, AllowRangedPulling, true, "If enabled bots will pull with their ranged items if set to ranged.") +RULE_BOOL(Bots, AllowAISpellPulling, true, "If enabled bots will rely on their detrimental AI to pull when within range.") RULE_BOOL(Bots, AllowBotEquipAnyClassGear, false, "Allows Bots to wear Equipment even if their class is not valid") RULE_BOOL(Bots, BotArcheryConsumesAmmo, true, "Set to false to disable Archery Ammo Consumption") RULE_BOOL(Bots, BotThrowingConsumesAmmo, true, "Set to false to disable Throwing Ammo Consumption") diff --git a/common/spdat.cpp b/common/spdat.cpp index e9aec626ca..8a21e23b94 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3279,6 +3279,25 @@ bool IsCommandedSpellType(uint16 spellType) { return false; } +bool IsPullingSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + case BotSpellTypes::HateLine: + return true; + default: + return false; + } + + return false; +} + bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls) { switch (spellType) { case BotSpellTypes::Nuke: diff --git a/common/spdat.h b/common/spdat.h index adf9c05e47..e7a266459b 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -740,7 +740,6 @@ bool IsBotSpellTypeDetrimental (uint16 spellType, uint8 cls = 0); bool IsBotSpellTypeBeneficial (uint16 spellType, uint8 cls = 0); bool IsBotSpellTypeOtherBeneficial(uint16 spellType); bool IsBotSpellTypeInnate (uint16 spellType); -bool IsBotSpellType (uint16 spellType); bool IsAEBotSpellType(uint16 spellType); bool IsGroupBotSpellType(uint16 spellType); bool IsGroupTargetOnlyBotSpellType(uint16 spellType); @@ -752,6 +751,7 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); +bool IsPullingSpellType(uint16 spellType); bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls); // These should not be used to determine spell category.. diff --git a/zone/bot.cpp b/zone/bot.cpp index fc8e110cd5..8bf13d6b48 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -101,6 +101,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm LoadDefaultBotSettings(); SetCastedSpellType(UINT16_MAX); SetCommandedSpell(false); + SetPullingSpell(false); //DisableBotSpellTimers(); // Do this once and only in this constructor @@ -178,6 +179,7 @@ Bot::Bot( SetBotCharmer(false); SetCastedSpellType(UINT16_MAX); SetCommandedSpell(false); + SetPullingSpell(false); bool stance_flag = false; if (!database.botdb.LoadStance(this, stance_flag) && bot_owner) { @@ -2200,36 +2202,53 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { - if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { - return; + if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells()) { + SetPullingSpell(true); + + if (AI_EngagedCastCheck()) { + SetPullingSpell(false); + return; + } + + SetPullingSpell(false); } - if (RuleB(Bots, AllowSpellPulling)) { + if (RuleB(Bots, UseSpellPulling)) { uint16 pullSpell = RuleI(Bots, PullSpellID); - if (tar_distance <= (spells[pullSpell].range * spells[pullSpell].range)) { + if (tar_distance <= spells[pullSpell].range) { StopMoving(); if (!TargetValidation(tar)) { return; } CastSpell(pullSpell, tar->GetID()); + SetPullingSpell(false); return; } } else { - //TODO bot rewrite - add casting AI to this - if (atCombatRange && IsBotRanged() && ranged_timer.Check(false)) { - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); + if (atCombatRange) { + if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { - BotRangedAttack(tar, true); - } + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } - ranged_timer.Start(); + ranged_timer.Start(); + SetPullingSpell(false); - return; + return; + } + else if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { + SetPullingSpell(false); + + return; + } } + + return; } } @@ -2624,7 +2643,7 @@ void Bot::DoAttackRounds(Mob* target, int hand) { NPC_FLURRY, GetCleanName(), target->GetCleanName() - ); //TODO bot rewrite - add output to others hits with flurry message + ); } } } @@ -9346,6 +9365,10 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return false; } + if (IsPullingSpell() && IsPullingSpellType(spellType)) { //Skip remaining checks for commanded + return true; + } + if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; diff --git a/zone/bot.h b/zone/bot.h index bf785cfc56..5a9cbf7fbf 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -410,6 +410,8 @@ class Bot : public NPC { void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; } bool IsCommandedSpell() const { return _commandedSpell; } void SetCommandedSpell(bool value) { _commandedSpell = value; } + bool IsPullingSpell() const { return _pullingSpell; } + void SetPullingSpell(bool value) { _pullingSpell = value; } void SetGuardMode(); void SetHoldMode(); @@ -1083,6 +1085,7 @@ class Bot : public NPC { uint16 _castedSpellType; bool _hasLoS; bool _commandedSpell; + bool _pullingSpell; // Private "base stats" Members int32 _baseMR; From efbf0b1fe94e17147877de33c53f8e986ef8f0cb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 2 Dec 2024 09:16:11 -0600 Subject: [PATCH 156/394] more pull tweaks --- zone/bot.cpp | 50 +++++++++++++++----------------------- zone/bot_commands/pull.cpp | 8 +++++- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8bf13d6b48..7f044046c8 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2202,15 +2202,28 @@ void Bot::AI_Process() // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { - if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells()) { - SetPullingSpell(true); + if (!TargetValidation(tar)) { return; } + + if (atCombatRange) { + if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); + + if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { + BotRangedAttack(tar, true); + } + + ranged_timer.Start(); - if (AI_EngagedCastCheck()) { - SetPullingSpell(false); return; } - SetPullingSpell(false); + if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && AI_HasSpells()) { + SetPullingSpell(true); + AI_EngagedCastCheck(); + SetPullingSpell(false); + + return; + } } if (RuleB(Bots, UseSpellPulling)) { @@ -2218,38 +2231,15 @@ void Bot::AI_Process() if (tar_distance <= spells[pullSpell].range) { StopMoving(); - - if (!TargetValidation(tar)) { return; } - + SetPullingSpell(true); CastSpell(pullSpell, tar->GetID()); SetPullingSpell(false); return; } } - else { - if (atCombatRange) { - if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { - StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); - if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { - BotRangedAttack(tar, true); - } - - ranged_timer.Start(); - SetPullingSpell(false); - - return; - } - else if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { - SetPullingSpell(false); - - return; - } - } - - return; - } + return; } // ENGAGED AT COMBAT RANGE diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index fa222fcfad..5357437122 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -15,6 +15,12 @@ void bot_command_pull(Client *c, const Seperator *sep) std::string arg1 = sep->arg[1]; int ab_arg = 1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; @@ -24,7 +30,7 @@ void bot_command_pull(Client *c, const Seperator *sep) std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } From cc5efa4372687ca81973731a6c8da19a8bfe497c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 2 Dec 2024 21:25:06 -0600 Subject: [PATCH 157/394] holding check at start of ai process --- zone/bot.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 7f044046c8..a1ee0f6914 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2089,7 +2089,7 @@ void Bot::AI_Process() return; } - if (raid && r_group == RAID_GROUPLESS) { + if (HOLDING || (raid && r_group == RAID_GROUPLESS)) { glm::vec3 Goal(0, 0, 0); TryNonCombatMovementChecks(bot_owner, follow_mob, Goal); @@ -2271,7 +2271,7 @@ void Bot::AI_Process() } } - if (!IsBotNonSpellFighter() && !HOLDING && AI_HasSpells() && AI_EngagedCastCheck()) { + if (!IsBotNonSpellFighter() && AI_HasSpells() && AI_EngagedCastCheck()) { return; } @@ -2332,6 +2332,7 @@ void Bot::AI_Process() SetAttackFlag(false); SetCombatRoundForAlerts(false); SetAttackingFlag(false); + if (!bot_owner->GetBotPulling()) { SetPullingFlag(false); @@ -2347,7 +2348,6 @@ void Bot::AI_Process() SetTarget(nullptr); if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { - GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -2362,10 +2362,10 @@ void Bot::AI_Process() if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { return; } - if (!HOLDING && AI_HasSpells() && TryIdleChecks(fm_distance)) { + if (AI_HasSpells() && TryIdleChecks(fm_distance)) { return; } - if (!HOLDING && AI_HasSpells() && TryBardMovementCasts()) { + if (AI_HasSpells() && TryBardMovementCasts()) { return; } } From 6c19f3dbdae4b7c657c927a66f9575fc9c066728 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 4 Dec 2024 20:56:09 -0600 Subject: [PATCH 158/394] fully implement ^pull logic to always return, can still be overidden by ^attack --- zone/bot.cpp | 50 +++++++++++++++++++++++++++++++++++++------------- zone/bot.h | 2 +- 2 files changed, 38 insertions(+), 14 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a1ee0f6914..ceeefb898d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2119,6 +2119,7 @@ void Bot::AI_Process() //ALT COMBAT (ACQUIRE HATE) glm::vec3 Goal(0, 0, 0); + // We have aggro to choose from if (IsEngaged()) { if (rest_timer.Enabled()) { @@ -2135,16 +2136,18 @@ void Bot::AI_Process() // RETURNING FLAG - else if (GetReturningFlag()) { - if (!ReturningFlagChecks(bot_owner, fm_distance)) { - return; - } + if (GetReturningFlag()) { + LogTestDebugDetail("#{}: {} has ReturningFlag", __LINE__, GetCleanName()); //deleteme + ReturningFlagChecks(bot_owner, leash_owner, fm_distance); + + return; } // DEFAULT (ACQUIRE TARGET) // VERIFY TARGET AND STANCE auto tar = GetBotTarget(bot_owner); + if (!tar) { return; } @@ -2204,6 +2207,9 @@ void Bot::AI_Process() if (GetPullingFlag()) { if (!TargetValidation(tar)) { return; } + if (!DoLosChecks(this, tar)) { + return; + } if (atCombatRange) { if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); @@ -2239,7 +2245,9 @@ void Bot::AI_Process() } } + TryPursueTarget(leash_distance, Goal); return; + //TODO bot rewrite - need pulling checks below to prevent assist } // ENGAGED AT COMBAT RANGE @@ -2334,7 +2342,6 @@ void Bot::AI_Process() SetAttackingFlag(false); if (!bot_owner->GetBotPulling()) { - SetPullingFlag(false); SetReturningFlag(false); } @@ -3013,24 +3020,41 @@ Mob* Bot::GetBotTarget(Client* bot_owner) return t; } -bool Bot::ReturningFlagChecks(Client* bot_owner, float fm_distance) {// Need to make it back to group before clearing return flag - if (fm_distance <= GetFollowDistance()) { - - // Once we're back, clear blocking flags so everyone else can join in +bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance) { + if ( + (NOT_GUARDING && fm_distance <= GetFollowDistance()) || + (GUARDING && DistanceSquared(GetPosition(), GetGuardPoint()) <= GetFollowDistance()) + ) { // Once we're back, clear blocking flags so everyone else can join in SetReturningFlag(false); bot_owner->SetBotPulling(false); if (GetPet()) { GetPet()->SetPetOrder(m_previous_pet_order); } + + return false; } // Need to keep puller out of combat until they reach their 'return to' destination - if (HasTargetReflection()) { + WipeHateList(); - SetTarget(nullptr); - WipeHateList(); - return false; + if (!IsMoving()) { + glm::vec3 Goal(0, 0, 0); + + if (GUARDING) { + Goal = GetGuardPoint(); + } + else { + Mob* follow_mob = entity_list.GetMob(GetFollowID()); + + if (!follow_mob) { + follow_mob = leash_owner; + SetFollowID(leash_owner->GetID()); + } + + Goal = follow_mob->GetPosition(); + } + RunTo(Goal.x, Goal.y, Goal.z); } return true; diff --git a/zone/bot.h b/zone/bot.h index 5a9cbf7fbf..9e187d216e 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -932,7 +932,7 @@ class Bot : public NPC { ); bool PullingFlagChecks(Client* bot_owner); - bool ReturningFlagChecks(Client* bot_owner, float fm_distance); + bool ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance); void BotPullerProcess(Client* bot_owner, Raid* raid); From 9207be96d334e2d467c6fcf0d8912f8ec81116ed Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 5 Dec 2024 07:21:34 -0600 Subject: [PATCH 159/394] Rewrite ^pull logic and handling. **MORE** Add ^setassistee command to set who your bots will assist. Bots will always assist you first before anyone else. If the rule Bots, AllowCrossGroupRaidAssist is enabled bots will assist the group or raid main assists. Rewrites logic in handling of pull and returning to ensure bots make it back to their location. --- common/ruletypes.h | 1 + zone/bot.cpp | 154 ++++++++++++++++++++++++----- zone/bot_command.cpp | 2 + zone/bot_command.h | 1 + zone/bot_commands/set_assistee.cpp | 59 +++++++++++ zone/client.h | 3 + 6 files changed, 197 insertions(+), 23 deletions(-) create mode 100644 zone/bot_commands/set_assistee.cpp diff --git a/common/ruletypes.h b/common/ruletypes.h index 0d66fd1cf8..08cac9b0ed 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -886,6 +886,7 @@ RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") +RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index ceeefb898d..8fdf57d50e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2041,6 +2041,7 @@ void Bot::AI_Process() auto raid = entity_list.GetRaidByBotName(GetName()); uint32 r_group = RAID_GROUPLESS; + if (raid) { raid->VerifyRaid(); r_group = raid->GetGroup(GetName()); @@ -2137,7 +2138,6 @@ void Bot::AI_Process() // RETURNING FLAG if (GetReturningFlag()) { - LogTestDebugDetail("#{}: {} has ReturningFlag", __LINE__, GetCleanName()); //deleteme ReturningFlagChecks(bot_owner, leash_owner, fm_distance); return; @@ -2210,6 +2210,7 @@ void Bot::AI_Process() if (!DoLosChecks(this, tar)) { return; } + if (atCombatRange) { if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); @@ -2246,8 +2247,8 @@ void Bot::AI_Process() } TryPursueTarget(leash_distance, Goal); + return; - //TODO bot rewrite - need pulling checks below to prevent assist } // ENGAGED AT COMBAT RANGE @@ -2459,42 +2460,150 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { if ( m_auto_defend_timer.Check() && - bot_owner->GetAggroCount() && NOT_HOLDING && NOT_PASSIVE ) { - auto xhaters = bot_owner->GetXTargetAutoMgr(); + XTargetAutoHaters* tempHaters; + std::vector assisteeHaters; + std::vector assisteeMembers; + bool found = false; - if (xhaters && !xhaters->empty()) { - for (auto hater_iter : xhaters->get_list()) { - if (!hater_iter.spawn_id) { - continue; + if (bot_owner->GetAggroCount()) { + tempHaters = bot_owner->GetXTargetAutoMgr(); + + if (tempHaters && !tempHaters->empty()) { + assisteeHaters.emplace_back(tempHaters); + assisteeMembers.emplace_back(bot_owner); + } + } + + if ( + (!bot_owner->GetAssistee() || !entity_list.GetClientByCharID(bot_owner->GetAssistee())) && + RuleB(Bots, AllowCrossGroupRaidAssist) + ) { + XTargetAutoHaters* temp_xhaters = bot_owner->GetXTargetAutoMgr(); + bool assisteeFound = false; + + if (IsRaidGrouped()) { + Raid* r = entity_list.GetRaidByBotName(GetName()); + if (r) { + for (const auto& m : r->members) { + if ( + m.member && + m.member->IsClient() && + m.member->GetAggroCount() && + r->IsAssister(m.member_name) + ) { + temp_xhaters = m.member->GetXTargetAutoMgr(); + + if (!temp_xhaters || temp_xhaters->empty()) { + continue; + } + + assisteeHaters.emplace_back(temp_xhaters); + assisteeMembers.emplace_back(m.member); + } + } } + } + else if (HasGroup()) { + Group* g = GetGroup(); + if (g) { + for (auto& m : g->members) { + if ( + m && + m->IsClient() && + m->CastToClient()->GetAggroCount() && + g->AmIMainAssist(m->GetName()) + ) { + temp_xhaters = m->CastToClient()->GetXTargetAutoMgr(); - if (bot_owner->GetBotPulling() && bot_owner->GetTarget() && hater_iter.spawn_id == bot_owner->GetTarget()->GetID()) { - continue; + if (!temp_xhaters || temp_xhaters->empty()) { + continue; + } + + assisteeHaters.emplace_back(temp_xhaters); + assisteeMembers.emplace_back(m->CastToClient()); + } + } } + } + else { + return false; + } + } + else { + if (bot_owner->GetAssistee()) { + Client* c = entity_list.GetClientByCharID(bot_owner->GetAssistee()); - auto hater = entity_list.GetMob(hater_iter.spawn_id); - if (hater && hater->CastToNPC()->IsOnHatelist(bot_owner) && !hater->IsMezzed() && DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) { - // This is roughly equivilent to npc attacking a client pet owner - AddToHateList(hater, 1); - SetTarget(hater); - SetAttackingFlag(); + if (bot_owner->IsInGroupOrRaid(c) && c->GetAggroCount()) { + tempHaters = bot_owner->GetXTargetAutoMgr(); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { - GetPet()->AddToHateList(hater, 1); - GetPet()->SetTarget(hater); + if (tempHaters && !tempHaters->empty()) { + assisteeHaters.emplace_back(tempHaters); + assisteeMembers.emplace_back(c); } + } + } + } + + if (!assisteeHaters.empty()) { + for (XTargetAutoHaters* xHaters : assisteeHaters) { + if (!xHaters->empty()) { + for (auto hater_iter : xHaters->get_list()) { + if (!hater_iter.spawn_id) { + continue; + } + + Mob* hater = nullptr; + + for (Client* xMember : assisteeMembers) { + if ( + xMember && + xMember->GetBotPulling() && + xMember->GetTarget() && + (hater_iter.spawn_id == xMember->GetTarget()->GetID()) + ) { + continue; + } - m_auto_defend_timer.Disable(); + hater = entity_list.GetMob(hater_iter.spawn_id); - return true; + if ( + hater && + !hater->IsMezzed() && + (DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) && + hater->CastToNPC()->IsOnHatelist(xMember) + ) { + break; + } + + hater = nullptr; + } + + if (hater) { + AddToHateList(hater, 1); + SetTarget(hater); + SetAttackingFlag(); + + if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + GetPet()->AddToHateList(hater, 1); + GetPet()->SetTarget(hater); + } + + m_auto_defend_timer.Disable(); + + return true; + } + } } } } + + return false; } } + return false; } @@ -3054,6 +3163,7 @@ bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_dist Goal = follow_mob->GetPosition(); } + RunTo(Goal.x, Goal.y, Goal.z); } @@ -11272,8 +11382,6 @@ void Bot::DoCombatPositioning( bool behindMob, bool frontMob ) { - //LogTestDebug("{} says, 'DoCombatPositioning. {} #{}", GetCleanName(), __FILE__, __LINE__); //deleteme - if (HasTargetReflection()) { if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) { if (TryEvade(tar)) { diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 1b00d701a6..a2a25a71f2 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1336,6 +1336,7 @@ int bot_command_init(void) bot_command_add("precombat", "Sets flag used to determine pre-combat behavior", AccountStatus::Player, bot_command_precombat) || bot_command_add("pull", "Orders a designated bot to 'pull' an enemy", AccountStatus::Player, bot_command_pull) || bot_command_add("release", "Releases a suspended bot's AI processing (with hate list wipe)", AccountStatus::Player, bot_command_release) || + bot_command_add("setassistee", "Sets your target to be the person your bots assist. Bots will always assist you before others", AccountStatus::Player, bot_command_set_assistee) || bot_command_add("sithppercent", "HP threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_hp_percent) || bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || @@ -2243,6 +2244,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/precombat.cpp" #include "bot_commands/pull.cpp" #include "bot_commands/release.cpp" +#include "bot_commands/set_assistee.cpp" #include "bot_commands/sit_hp_percent.cpp" #include "bot_commands/sit_in_combat.cpp" #include "bot_commands/sit_mana_percent.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index ecce9485d7..d6644bbf60 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1747,6 +1747,7 @@ void bot_command_heritage(Client *c, const Seperator *sep); void bot_command_inspect_message(Client *c, const Seperator *sep); void bot_command_list_bots(Client *c, const Seperator *sep); void bot_command_report(Client *c, const Seperator *sep); +void bot_command_set_assistee(Client* c, const Seperator* sep); void bot_command_spawn(Client *c, const Seperator *sep); void bot_command_stance(Client *c, const Seperator *sep); void bot_command_stop_melee_level(Client *c, const Seperator *sep); diff --git a/zone/bot_commands/set_assistee.cpp b/zone/bot_commands/set_assistee.cpp new file mode 100644 index 0000000000..d2be953e27 --- /dev/null +++ b/zone/bot_commands/set_assistee.cpp @@ -0,0 +1,59 @@ +#include "../bot_command.h" + +void bot_command_set_assistee(Client* c, const Seperator* sep) +{ + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Sets your bots to assist your target in addition to yourself" + }; + + std::vector notes = + { + "- Your target must be another player in your group or raid.", + "- This needs to be set on every zone/camp you do." + }; + + std::vector example_format = { }; + std::vector examples_one = { }; + std::vector examples_two = { }; + std::vector examples_three = { }; + + std::vector actionables = { }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + return; + } + + Mob* assistee = c->GetTarget(); + + if (assistee && assistee->IsClient() && c->IsInGroupOrRaid(assistee)) { + c->SetAssistee(assistee->CastToClient()->CharacterID()); + c->Message(Chat::Green, "Your bots will now assist %s.", assistee->GetCleanName()); + + return; + } + + c->Message(Chat::Yellow, "You can only set your bots to assist clients that are in your group or raid."); + + return; +} diff --git a/zone/client.h b/zone/client.h index 2a090eeac4..c4ffd54609 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2221,6 +2221,8 @@ class Client : public Mob bool GetBotPulling() { return m_bot_pulling; } void SetBotPulling(bool flag = true) { m_bot_pulling = flag; } + uint32 GetAssistee() { return _assistee; } + void SetAssistee(uint32 id = 0) { _assistee = id; } bool GetBotPrecombat() { return m_bot_precombat; } void SetBotPrecombat(bool flag = true) { m_bot_precombat = flag; } @@ -2257,6 +2259,7 @@ class Client : public Mob uint8 cure_min_threshold; uint8 cure_threshold; bool illusion_block; + uint32 _assistee; bool CanTradeFVNoDropItem(); void SendMobPositions(); From 625c1fb06bf93fc1528cdbdb0319b474c2c1d639 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 5 Dec 2024 23:46:37 -0600 Subject: [PATCH 160/394] Move HateLine to a better ID --- .../database_update_manifest_bots.cpp | 166 +++++++++--------- common/spdat.h | 68 +++---- zone/botspellsai.cpp | 2 +- 3 files changed, 118 insertions(+), 118 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 235c160b35..d93ec37bf2 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -1077,7 +1077,7 @@ WHERE bot_spells_entries.spell_id = spells_new.id); ManifestEntry{ .version = 9051, .description = "2024_11_26_remove_sk_icb.sql", - .check = "SELECT * FROM `bot_spells_entries` where `type` = 55", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 24", .condition = "empty", .match = "", .sql = R"( @@ -1088,88 +1088,88 @@ AND `type` = 10; INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) VALUES -(3003, 10175, 55, 72, 76), -(3003, 10173, 55, 72, 76), -(3003, 10174, 55, 72, 76), -(3003, 14956, 55, 77, 81), -(3003, 14954, 55, 77, 81), -(3003, 14955, 55, 77, 81), -(3003, 19070, 55, 82, 86), -(3003, 19068, 55, 82, 86), -(3003, 19069, 55, 82, 86), -(3003, 25298, 55, 87, 91), -(3003, 25299, 55, 87, 91), -(3003, 25297, 55, 87, 91), -(3003, 28348, 55, 92, 96), -(3003, 28349, 55, 92, 96), -(3003, 28347, 55, 92, 96), -(3003, 34351, 55, 97, 254), -(3003, 34352, 55, 97, 254), -(3003, 34350, 55, 97, 254), -(3003, 40078, 55, 98, 254), -(3003, 40079, 55, 98, 254), -(3003, 40080, 55, 98, 254), -(3005, 1221, 55, 33, 41), -(3005, 1222, 55, 42, 52), -(3005, 1223, 55, 53, 58), -(3005, 1224, 55, 59, 62), -(3005, 3405, 55, 63, 66), -(3005, 5329, 55, 67, 70), -(3005, 5336, 55, 69, 73), -(3005, 10258, 55, 71, 71), -(3005, 10259, 55, 71, 71), -(3005, 10257, 55, 71, 71), -(3005, 10261, 55, 72, 76), -(3005, 10262, 55, 72, 76), -(3005, 10260, 55, 72, 76), -(3005, 10291, 55, 74, 78), -(3005, 10292, 55, 74, 78), -(3005, 10293, 55, 74, 78), -(3005, 15160, 55, 76, 76), -(3005, 15161, 55, 76, 76), -(3005, 15162, 55, 76, 76), -(3005, 15165, 55, 77, 81), -(3005, 15163, 55, 77, 81), -(3005, 15164, 55, 77, 81), -(3005, 15186, 55, 79, 83), -(3005, 15184, 55, 79, 83), -(3005, 15185, 55, 79, 83), -(3005, 19315, 55, 81, 81), -(3005, 19313, 55, 81, 81), -(3005, 19314, 55, 81, 81), -(3005, 19317, 55, 82, 86), -(3005, 19318, 55, 82, 86), -(3005, 19316, 55, 82, 86), -(3005, 19338, 55, 84, 88), -(3005, 19339, 55, 84, 88), -(3005, 19337, 55, 84, 88), -(3005, 25581, 55, 86, 86), -(3005, 25582, 55, 86, 86), -(3005, 25580, 55, 86, 86), -(3005, 25586, 55, 87, 91), -(3005, 25587, 55, 87, 91), -(3005, 25588, 55, 87, 91), -(3005, 25641, 55, 89, 93), -(3005, 25642, 55, 89, 93), -(3005, 25643, 55, 89, 93), -(3005, 28659, 55, 91, 91), -(3005, 28657, 55, 91, 91), -(3005, 28658, 55, 91, 91), -(3005, 28665, 55, 92, 96), -(3005, 28663, 55, 92, 96), -(3005, 28664, 55, 92, 96), -(3005, 28735, 55, 94, 98), -(3005, 28733, 55, 94, 98), -(3005, 28734, 55, 94, 98), -(3005, 34688, 55, 96, 96), -(3005, 34689, 55, 96, 96), -(3005, 34687, 55, 96, 96), -(3005, 34694, 55, 97, 254), -(3005, 34695, 55, 97, 254), -(3005, 34693, 55, 97, 254), -(3005, 34752, 55, 99, 254), -(3005, 34753, 55, 99, 254), -(3005, 34751, 55, 99, 254); +(3003, 10175, 24, 72, 76), +(3003, 10173, 24, 72, 76), +(3003, 10174, 24, 72, 76), +(3003, 14956, 24, 77, 81), +(3003, 14954, 24, 77, 81), +(3003, 14955, 24, 77, 81), +(3003, 19070, 24, 82, 86), +(3003, 19068, 24, 82, 86), +(3003, 19069, 24, 82, 86), +(3003, 25298, 24, 87, 91), +(3003, 25299, 24, 87, 91), +(3003, 25297, 24, 87, 91), +(3003, 28348, 24, 92, 96), +(3003, 28349, 24, 92, 96), +(3003, 28347, 24, 92, 96), +(3003, 34351, 24, 97, 254), +(3003, 34352, 24, 97, 254), +(3003, 34350, 24, 97, 254), +(3003, 40078, 24, 98, 254), +(3003, 40079, 24, 98, 254), +(3003, 40080, 24, 98, 254), +(3005, 1221, 24, 33, 41), +(3005, 1222, 24, 42, 52), +(3005, 1223, 24, 53, 58), +(3005, 1224, 24, 59, 62), +(3005, 3405, 24, 63, 66), +(3005, 5329, 24, 67, 70), +(3005, 5336, 24, 69, 73), +(3005, 10258, 24, 71, 71), +(3005, 10259, 24, 71, 71), +(3005, 10257, 24, 71, 71), +(3005, 10261, 24, 72, 76), +(3005, 10262, 24, 72, 76), +(3005, 10260, 24, 72, 76), +(3005, 10291, 24, 74, 78), +(3005, 10292, 24, 74, 78), +(3005, 10293, 24, 74, 78), +(3005, 15160, 24, 76, 76), +(3005, 15161, 24, 76, 76), +(3005, 15162, 24, 76, 76), +(3005, 15165, 24, 77, 81), +(3005, 15163, 24, 77, 81), +(3005, 15164, 24, 77, 81), +(3005, 15186, 24, 79, 83), +(3005, 15184, 24, 79, 83), +(3005, 15185, 24, 79, 83), +(3005, 19315, 24, 81, 81), +(3005, 19313, 24, 81, 81), +(3005, 19314, 24, 81, 81), +(3005, 19317, 24, 82, 86), +(3005, 19318, 24, 82, 86), +(3005, 19316, 24, 82, 86), +(3005, 19338, 24, 84, 88), +(3005, 19339, 24, 84, 88), +(3005, 19337, 24, 84, 88), +(3005, 25581, 24, 86, 86), +(3005, 25582, 24, 86, 86), +(3005, 25580, 24, 86, 86), +(3005, 25586, 24, 87, 91), +(3005, 25587, 24, 87, 91), +(3005, 25588, 24, 87, 91), +(3005, 25641, 24, 89, 93), +(3005, 25642, 24, 89, 93), +(3005, 25643, 24, 89, 93), +(3005, 28659, 24, 91, 91), +(3005, 28657, 24, 91, 91), +(3005, 28658, 24, 91, 91), +(3005, 28665, 24, 92, 96), +(3005, 28663, 24, 92, 96), +(3005, 28664, 24, 92, 96), +(3005, 28735, 24, 94, 98), +(3005, 28733, 24, 94, 98), +(3005, 28734, 24, 94, 98), +(3005, 34688, 24, 96, 96), +(3005, 34689, 24, 96, 96), +(3005, 34687, 24, 96, 96), +(3005, 34694, 24, 97, 254), +(3005, 34695, 24, 97, 254), +(3005, 34693, 24, 97, 254), +(3005, 34752, 24, 99, 254), +(3005, 34753, 24, 99, 254), +(3005, 34751, 24, 99, 254); DELETE FROM bot_spells_entries diff --git a/common/spdat.h b/common/spdat.h index e7a266459b..fe94a82a40 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -677,39 +677,39 @@ namespace BotSpellTypes constexpr uint16 PreCombatBuffSong = 21; constexpr uint16 Fear = 22; constexpr uint16 Stun = 23; - constexpr uint16 GroupCures = 24; - constexpr uint16 CompleteHeal = 25; - constexpr uint16 FastHeals = 26; - constexpr uint16 VeryFastHeals = 27; - constexpr uint16 GroupHeals = 28; - constexpr uint16 GroupCompleteHeals = 29; - constexpr uint16 GroupHoTHeals = 30; - constexpr uint16 HoTHeals = 31; - constexpr uint16 AENukes = 32; - constexpr uint16 AERains = 33; - constexpr uint16 AEMez = 34; - constexpr uint16 AEStun = 35; - constexpr uint16 AEDebuff = 36; - constexpr uint16 AESlow = 37; - constexpr uint16 AESnare = 38; - constexpr uint16 AEFear = 39; - constexpr uint16 AEDispel = 40; - constexpr uint16 AERoot = 41; - constexpr uint16 AEDoT = 42; - constexpr uint16 AELifetap = 43; - constexpr uint16 PBAENuke = 44; - constexpr uint16 PetBuffs = 45; - constexpr uint16 PetRegularHeals = 46; - constexpr uint16 PetCompleteHeals = 47; - constexpr uint16 PetFastHeals = 48; - constexpr uint16 PetVeryFastHeals = 49; - constexpr uint16 PetHoTHeals = 50; - constexpr uint16 DamageShields = 51; - constexpr uint16 ResistBuffs = 52; - constexpr uint16 PetDamageShields = 53; - constexpr uint16 PetResistBuffs = 54; - constexpr uint16 HateLine = 55; - constexpr uint16 AEHateLine = 56; + constexpr uint16 HateLine = 24; + constexpr uint16 GroupCures = 25; + constexpr uint16 CompleteHeal = 26; + constexpr uint16 FastHeals = 27; + constexpr uint16 VeryFastHeals = 28; + constexpr uint16 GroupHeals = 29; + constexpr uint16 GroupCompleteHeals = 30; + constexpr uint16 GroupHoTHeals = 31; + constexpr uint16 HoTHeals = 32; + constexpr uint16 AENukes = 33; + constexpr uint16 AERains = 34; + constexpr uint16 AEMez = 35; + constexpr uint16 AEStun = 36; + constexpr uint16 AEDebuff = 37; + constexpr uint16 AESlow = 38; + constexpr uint16 AESnare = 39; + constexpr uint16 AEFear = 40; + constexpr uint16 AEDispel = 41; + constexpr uint16 AERoot = 42; + constexpr uint16 AEDoT = 43; + constexpr uint16 AELifetap = 44; + constexpr uint16 AEHateLine = 45; + constexpr uint16 PBAENuke = 46; + constexpr uint16 PetBuffs = 47; + constexpr uint16 PetRegularHeals = 48; + constexpr uint16 PetCompleteHeals = 49; + constexpr uint16 PetFastHeals = 50; + constexpr uint16 PetVeryFastHeals = 51; + constexpr uint16 PetHoTHeals = 52; + constexpr uint16 DamageShields = 53; + constexpr uint16 ResistBuffs = 54; + constexpr uint16 PetDamageShields = 55; + constexpr uint16 PetResistBuffs = 56; // Command Spell Types constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic @@ -727,7 +727,7 @@ namespace BotSpellTypes constexpr uint16 SummonCorpse = 112; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::AEHateLine; // Do not remove this, increment as needed + constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index a5718a2dc7..f83455cc83 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1045,7 +1045,7 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa ) { continue; } - if (spellType == debugSpellType) { LogTestDebugDetail("{} - #{}: [{} #{}] - {} says, '{} #{} - Passed TGB checks.'", __FILE__, __LINE__, botCaster->GetSpellTypeNameByID(spellType), spellType, botCaster->GetCleanName(), spells[botSpellList[i].spellid].name, botSpellList[i].spellid); }; //deleteme + if (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) { continue; } From 912b6f0a2812cb6353f56105d5c1037674956134 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:38:37 -0600 Subject: [PATCH 161/394] cleanup ST_Self logic in CastChecks --- zone/bot.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8fdf57d50e..6ec2bd6d2c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9530,10 +9530,10 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (spells[spell_id].target_type == ST_Self && tar != this) { - if (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse)) { - //tar = this; - } - else { + if ( + !IsEffectInSpell(spell_id, SE_SummonCorpse) || + (IsEffectInSpell(spell_id, SE_SummonCorpse) && !RuleB(Bots, AllowCommandedSummonCorpse)) + ) { tar = this; } } From 3b7edc032fb3ef72aa5a780f3b4878d87ffedf2e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 11:38:59 -0600 Subject: [PATCH 162/394] Removed unused BotSpellTypeRequiresLoS --- common/spdat.cpp | 39 --------------------------------------- common/spdat.h | 1 - 2 files changed, 40 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 8a21e23b94..2d36d49163 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3297,42 +3297,3 @@ bool IsPullingSpellType(uint16 spellType) { return false; } - -bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls) { - switch (spellType) { - case BotSpellTypes::Nuke: - case BotSpellTypes::Root: - case BotSpellTypes::Lifetap: - case BotSpellTypes::Snare: - case BotSpellTypes::DOT: - case BotSpellTypes::Dispel: - case BotSpellTypes::Mez: - //case BotSpellTypes::Charm: // commanded - case BotSpellTypes::Slow: - case BotSpellTypes::Debuff: - case BotSpellTypes::HateRedux: - //case BotSpellTypes::Fear: // commanded - case BotSpellTypes::Stun: - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AEMez: - case BotSpellTypes::AEStun: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: - //case BotSpellTypes::AEFear: // commanded - case BotSpellTypes::AEDispel: - case BotSpellTypes::AERoot: - case BotSpellTypes::AEDoT: - case BotSpellTypes::AELifetap: - case BotSpellTypes::PBAENuke: - // case BotSpellTypes::Lull: // commanded - case BotSpellTypes::HateLine: - case BotSpellTypes::AEHateLine: - return true; - default: - return false; - } - - return false; -} diff --git a/common/spdat.h b/common/spdat.h index fe94a82a40..01cdf85af6 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -752,7 +752,6 @@ bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); bool IsPullingSpellType(uint16 spellType); -bool BotSpellTypeRequiresLoS(uint16 spellType, uint8 cls); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only From 88b0e4f15ecbc2083c0637b6cd8b693b4f85cd6b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:32:34 -0600 Subject: [PATCH 163/394] Move fizzle message to define --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 6ec2bd6d2c..60505e4a3a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5823,7 +5823,7 @@ bool Bot::CastSpell( if (DivineAura()) { LogSpellsDetail("Spell casting canceled: cannot cast while Divine Aura is in effect"); - InterruptSpell(173, 0x121, false); + InterruptSpell(SPELL_FIZZLE, 0x121, false); return false; } From 30f1960003762cc24fdadfcf1bba5f2c4b6015e2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 6 Dec 2024 22:33:32 -0600 Subject: [PATCH 164/394] add timer checks to Idle/Engaged/Pursue CastCheck to early terminate --- zone/botspellsai.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index f83455cc83..410aa7e69d 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -633,7 +633,7 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } bool Bot::AI_PursueCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { + if (GetAppearance() == eaDead || delaytimer || spellend_timer.Enabled() || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -682,7 +682,7 @@ bool Bot::AI_PursueCastCheck() { } bool Bot::AI_IdleCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { + if (GetAppearance() == eaDead || delaytimer || spellend_timer.Enabled() || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } @@ -745,7 +745,7 @@ bool Bot::AI_IdleCastCheck() { } bool Bot::AI_EngagedCastCheck() { - if (GetAppearance() == eaDead || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { + if (GetAppearance() == eaDead || delaytimer || spellend_timer.Enabled() || IsFeared() || IsSilenced() || IsAmnesiad() || GetHP() < 0) { return false; } From 0081d7673c04e86b97bccd7ef0285404f6811362 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:32:49 -0600 Subject: [PATCH 165/394] Add back !IsBotNonSpellFighter() check to the different CastCheck --- zone/bot.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 60505e4a3a..c465b999b8 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2370,10 +2370,10 @@ void Bot::AI_Process() if (TryNonCombatMovementChecks(bot_owner, follow_mob, Goal)) { return; } - if (AI_HasSpells() && TryIdleChecks(fm_distance)) { + if (!IsBotNonSpellFighter() && AI_HasSpells() && TryIdleChecks(fm_distance)) { return; } - if (AI_HasSpells() && TryBardMovementCasts()) { + if (!IsBotNonSpellFighter() && AI_HasSpells() && TryBardMovementCasts()) { return; } } @@ -2665,7 +2665,7 @@ bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { } // This is a mob that is fleeing either because it has been feared or is low on hitpoints - if (!HOLDING && AI_HasSpells()) { + if (!HOLDING && !IsBotNonSpellFighter() && AI_HasSpells()) { AI_PursueCastCheck(); } From a8f048c4e47a2ff98872bf0c6145974f6b69da21 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:35:36 -0600 Subject: [PATCH 166/394] Correct IsValidSpellRange --- zone/bot.cpp | 128 +++++++++++++++++++++++++++++++++++++++---- zone/botspellsai.cpp | 41 ++++++++------ 2 files changed, 143 insertions(+), 26 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c465b999b8..13fea7cafa 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9679,16 +9679,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - - if (!IsCommandedSpell() && !IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } if ( (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spell_id) != 0)) @@ -9703,6 +9693,20 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (spellType == UINT16_MAX) { //AA cast checks, return here + return true; + } + + if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + return true; } @@ -11084,6 +11088,81 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { return result; } +bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { + if (!tar) { + tar = this; + } + + if (!DoLosChecks(this, tar)) { + return false; + } + + if (CheckSpellRecastTimer(spell_id)) { + if (IsBeneficialSpell(spell_id)) { + if ( + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !GetOwner()->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !GetOwner()->IsInGroupOrRaid(tar)) + ) { + GetBotOwner()->Message(Chat::Yellow, "[%s] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName()); + + return false; + } + } + + if (IsDetrimentalSpell(spell_id) && !IsAttackAllowed(tar)) { + GetBotOwner()->Message(Chat::Yellow, "%s says, 'I cannot attack [%s]'.", GetCleanName(), tar->GetCleanName()); + + return false; + } + + if (!CastChecks(spell_id, tar, UINT16_MAX)) { + GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", GetCleanName()); + + return false; + } + + + if (CastSpell(spell_id, tar->GetID())) { + BotGroupSay( + this, + fmt::format( + "Casting {} on {}.", + GetSpellName(spell_id), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); + + int timer_duration = (rank->recast_time - GetAlternateAdvancementCooldownReduction(rank)) * 1000; + + if (timer_duration < 0) { + timer_duration = 0; + } + + SetSpellRecastTimer(spell_id, timer_duration); + } + else { + GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", GetCleanName()); + + return false; + } + } + else { + GetBotOwner()->Message( + Chat::Yellow, + fmt::format( + "{} says, 'Ability recovery time not yet met. {} remaining.'", + GetCleanName(), + Strings::SecondsToTime(GetSpellRecastRemainingTime(spell_id), true) + ).c_str() + ); + + return false; + } + + return true; +} + uint16 Bot::GetSpellListSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: @@ -11879,3 +11958,32 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell return false; } + +uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) { + uint16 spell_id = 0; + std::pair aa_ability = std::make_pair(nullptr, nullptr); + AA::Ability* ability = zone->GetAlternateAdvancementAbility(id); + + if (!ability || !ability->first_rank_id) { + return spell_id; + } + + uint32 points = GetAA(ability->first_rank_id); + //if (points) { LogTestDebug("{}: {} says, '{} points for {} [#{} - {}] rank {}'", __LINE__, GetCleanName(), points, zone->GetAAName(aa_ability.first->id), aa_ability.first->id, aa_ability.second->id, points); } //deleteme + if (points > 0) { + aa_ability = zone->GetAlternateAdvancementAbilityAndRank(ability->id, points); + } + + rank = aa_ability.second; + + if (!points || !rank) { + LogTestDebug("{}: {} says, 'No {} found'", __LINE__, GetCleanName(), (!points ? "points" : "rank")); //deleteme + return spell_id; + } + + spell_id = rank->spell; + + LogTestDebug("{}: {} says, 'Found {} [#{}]'", __LINE__, GetCleanName(), spells[spell_id].name, spell_id); //deleteme + + return spell_id; +} diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 410aa7e69d..b4588811d0 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2669,31 +2669,40 @@ bool Bot::HasBotSpellEntry(uint16 spell_id) { } bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { - if (!IsValidSpell(spell_id)) { + if (!IsValidSpell(spell_id) || !tar) { return false; } - if (tar) { - float range = spells[spell_id].range; - - if (tar != this) { - range += GetRangeDistTargetSizeMod(tar); - } + float range = spells[spell_id].range + GetRangeDistTargetSizeMod(tar); + + if (IsAnyAESpell(spell_id)) { + range = GetAOERange(spell_id); + } + + if (RuleB(Bots, EnableBotTGB) && IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id)) { + range = spells[spell_id].aoe_range; + } - range = GetActSpellRange(spell_id, range); + range = GetActSpellRange(spell_id, range); - if (range >= Distance(m_Position, tar->GetPosition())) { - return true; - } + if (IsIllusionSpell(spell_id) && (HasProjectIllusion())) { + range = 100; + } - range = GetActSpellRange(spell_id, spells[spell_id].aoe_range); + float dist2 = DistanceSquared(m_Position, tar->GetPosition()); + float range2 = range * range; + float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range; - if (range >= Distance(m_Position, tar->GetPosition())) { - return true; - } + if (dist2 > range2) { + //target is out of range. + return false; + } + else if (dist2 < min_range2) { + //target is too close range. + return false; } - return false; + return true; } BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE, Mob* tar) { From e9534e3f75e2ac4117733bd839870dd35d5bade8 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 22:36:56 -0600 Subject: [PATCH 167/394] Implement AAs and harmtouch/layonhands to ^cast --- fix IsValidSpellRange --- zone/bot.cpp | 4 +- zone/bot.h | 2 + zone/bot_commands/cast.cpp | 378 ++++++++++++++++++++++--------------- zone/spells.cpp | 2 +- 4 files changed, 226 insertions(+), 160 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 13fea7cafa..e78bfa8ce4 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9739,7 +9739,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::Invisibility: case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: - if ( + if ( // TODO bot rewrite - fix this, missing other target types (43 for example) !( spells[spell_id].target_type == ST_Target || spells[spell_id].target_type == ST_Pet || @@ -9748,7 +9748,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { spells[spell_id].target_type == ST_GroupTeleport ) ) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme return false; } diff --git a/zone/bot.h b/zone/bot.h index 9e187d216e..3371a6b998 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -402,6 +402,7 @@ class Bot : public NPC { // AI Methods bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); bool AttemptAICastSpell(uint16 spellType); + bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; @@ -478,6 +479,7 @@ class Bot : public NPC { void LoadDefaultBotSettings(); void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); + uint16 GetSpellByAA(int id, AA::Rank* &rank); std::string GetBotSpellCategoryName(uint8 setting_type); std::string GetBotSettingCategoryName(uint8 setting_type); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index a524068105..7915a491c9 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -11,7 +11,13 @@ void bot_command_cast(Client* c, const Seperator* sep) std::vector notes = { "- This will interrupt any spell currently being cast by bots told to use the command.", - "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells" + "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells", + fmt::format( + "- You can use {} aa # to cast any clickable AA or specifically {} harmtouch / {} layonhands" + , sep->arg[0] + , sep->arg[0] + , sep->arg[0] + ) }; std::vector example_format = @@ -29,12 +35,12 @@ void bot_command_cast(Client* c, const Seperator* sep) { "To tell everyone to Nuke the target:", fmt::format( - "{} {} spawned", + "{} {}", sep->arg[0], c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( - "{} {} spawned", + "{} {}", sep->arg[0], BotSpellTypes::Nuke ) @@ -57,16 +63,14 @@ void bot_command_cast(Client* c, const Seperator* sep) }; std::vector examples_three = { - "To tell Clrbot to resurrect the targeted corpse:", + "To tell Skbot to Harm Touch the target:", fmt::format( - "{} {} byname Clrbot", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Resurrect) + "{} aa 6000 byname Skbot", + sep->arg[0] ), fmt::format( - "{} {} byname Clrbot", - sep->arg[0], - BotSpellTypes::Resurrect + "{} harmtouch byname Skbot", + sep->arg[0] ) }; @@ -120,6 +124,11 @@ void bot_command_cast(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; + //AA help + if (!arg1.compare("aa") && !arg2.compare("help")) { + c->Message(Chat::Yellow, "Enter the ID of an AA to attempt to cast.", sep->arg[0]); + } + //Commanded type help prompts if (!arg2.compare("help")) { c->Message(Chat::Yellow, "You can also use [single], [group], [ae]. Ex: ^cast movementspeed group.", sep->arg[0]); @@ -174,144 +183,168 @@ void bot_command_cast(Client* c, const Seperator* sep) } int ab_arg = 2; - uint16 spellType = 0; - - // String/Int type checks - if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); - - if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) { - c->Message( - Chat::Yellow, - fmt::format( - "You must choose a valid spell type. Use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); + uint16 spellType = UINT16_MAX; + uint16 subType = UINT16_MAX; + uint16 subTargetType = UINT16_MAX; + bool aaType = false; + int aaID = 0; - return; + if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) { + if (!arg1.compare("harmtouch")) { + aaID = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id; } - } - else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + else if (!arg1.compare("layonhands")) { + aaID = zone->GetAlternateAdvancementAbilityByRank(aaLayonHands)->id; } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - + else if (!sep->IsNumber(2) || !zone->GetAlternateAdvancementAbility(Strings::ToInt(arg2))) { + c->Message(Chat::Yellow, "You must enter an AA ID."); return; } + else { + ++ab_arg; + aaID = Strings::ToInt(arg2); + } + + aaType = true; } - switch (spellType) { //Allowed command checks - case BotSpellTypes::Charm: - if (!RuleB(Bots, AllowCommandedCharm)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); - return; - } + if (!aaType) { + // String/Int type checks + if (sep->IsNumber(1)) { + spellType = atoi(sep->arg[1]); + + if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); - break; - case BotSpellTypes::AEMez: - case BotSpellTypes::Mez: - if (!RuleB(Bots, AllowCommandedMez)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); return; } - - break; - case BotSpellTypes::Resurrect: - if (!RuleB(Bots, AllowCommandedResurrect)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); - return; + } + else { + if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spellType = c->GetSpellTypeIDByShortName(arg1); } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); - break; - case BotSpellTypes::SummonCorpse: - if (!RuleB(Bots, AllowCommandedSummonCorpse)) { - c->Message(Chat::Yellow, "This commanded type is currently disabled."); return; } + } - break; - default: - break; - } + switch (spellType) { //Allowed command checks + case BotSpellTypes::Charm: + if (!RuleB(Bots, AllowCommandedCharm)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - std::string argString = sep->arg[ab_arg]; - uint16 subType = UINT16_MAX; - uint16 subTargetType = UINT16_MAX; - - if (!argString.compare("shrink")) { - subType = CommandedSubTypes::Shrink; - ++ab_arg; - } - else if (!argString.compare("grow")) { - subType = CommandedSubTypes::Grow; - ++ab_arg; - } - else if (!argString.compare("see")) { - subType = CommandedSubTypes::SeeInvis; - ++ab_arg; - } - else if (!argString.compare("invis")) { - subType = CommandedSubTypes::Invis; - ++ab_arg; - } - else if (!argString.compare("undead")) { - subType = CommandedSubTypes::InvisUndead; - ++ab_arg; - } - else if (!argString.compare("animals")) { - subType = CommandedSubTypes::InvisAnimals; - ++ab_arg; - } - else if (!argString.compare("selo")) { - subType = CommandedSubTypes::Selo; - ++ab_arg; - } + break; + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + if (!RuleB(Bots, AllowCommandedMez)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - argString = sep->arg[ab_arg]; + break; + case BotSpellTypes::Resurrect: + if (!RuleB(Bots, AllowCommandedResurrect)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - if (!argString.compare("single")) { - subTargetType = CommandedSubTypes::SingleTarget; - ++ab_arg; - } - else if (!argString.compare("group")) { - subTargetType = CommandedSubTypes::GroupTarget; - ++ab_arg; - } - else if (!argString.compare("ae")) { - subTargetType = CommandedSubTypes::AETarget; - ++ab_arg; - } + break; + case BotSpellTypes::SummonCorpse: + if (!RuleB(Bots, AllowCommandedSummonCorpse)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } - if ( - spellType == BotSpellTypes::PetBuffs || - spellType == BotSpellTypes::PetCompleteHeals || - spellType == BotSpellTypes::PetFastHeals || - spellType == BotSpellTypes::PetHoTHeals || - spellType == BotSpellTypes::PetRegularHeals || - spellType == BotSpellTypes::PetVeryFastHeals - ) { - c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); - return; + break; + default: + break; + } + + std::string argString = sep->arg[ab_arg]; + + + if (!argString.compare("shrink")) { + subType = CommandedSubTypes::Shrink; + ++ab_arg; + } + else if (!argString.compare("grow")) { + subType = CommandedSubTypes::Grow; + ++ab_arg; + } + else if (!argString.compare("see")) { + subType = CommandedSubTypes::SeeInvis; + ++ab_arg; + } + else if (!argString.compare("invis")) { + subType = CommandedSubTypes::Invis; + ++ab_arg; + } + else if (!argString.compare("undead")) { + subType = CommandedSubTypes::InvisUndead; + ++ab_arg; + } + else if (!argString.compare("animals")) { + subType = CommandedSubTypes::InvisAnimals; + ++ab_arg; + } + else if (!argString.compare("selo")) { + subType = CommandedSubTypes::Selo; + ++ab_arg; + } + + argString = sep->arg[ab_arg]; + + if (!argString.compare("single")) { + subTargetType = CommandedSubTypes::SingleTarget; + ++ab_arg; + } + else if (!argString.compare("group")) { + subTargetType = CommandedSubTypes::GroupTarget; + ++ab_arg; + } + else if (!argString.compare("ae")) { + subTargetType = CommandedSubTypes::AETarget; + ++ab_arg; + } + + if ( + spellType == BotSpellTypes::PetBuffs || + spellType == BotSpellTypes::PetCompleteHeals || + spellType == BotSpellTypes::PetFastHeals || + spellType == BotSpellTypes::PetHoTHeals || + spellType == BotSpellTypes::PetRegularHeals || + spellType == BotSpellTypes::PetVeryFastHeals + ) { + c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); + return; + } } Mob* tar = c->GetTarget(); //LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (!tar) { - if (spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { + if (!aaType && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { c->Message(Chat::Yellow, "You need a target for that."); return; } @@ -356,10 +389,11 @@ void bot_command_cast(Client* c, const Seperator* sep) if (IsBotSpellTypeBeneficial(spellType)) { if ( - (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) || - ((tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner()))) + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) ) { - c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + c->Message(Chat::Yellow, "[%s] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName()); return; } @@ -408,49 +442,79 @@ void bot_command_cast(Client* c, const Seperator* sep) } Mob* newTar = tar; - //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme - if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { - newTar = bot_iter; - } - if (!newTar) { - continue; - } + if (!aaType) { + //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { + newTar = bot_iter; + } - if ( - IsBotSpellTypeBeneficial(spellType) && - !RuleB(Bots, CrossRaidBuffingAndHealing) && - !bot_iter->IsInGroupOrRaid(newTar, true) - ) { - continue; - } + if (!newTar) { + continue; + } - if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { - bot_iter->BotGroupSay( - bot_iter, - fmt::format( - "I cannot attack [{}].", - newTar->GetCleanName() - ).c_str() - ); + if ( + IsBotSpellTypeBeneficial(spellType) && + !RuleB(Bots, CrossRaidBuffingAndHealing) && + !bot_iter->IsInGroupOrRaid(newTar, true) + ) { + continue; + } - continue; + if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { + bot_iter->BotGroupSay( + bot_iter, + fmt::format( + "I cannot attack [{}].", + newTar->GetCleanName() + ).c_str() + ); + + continue; + } } - LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + if (aaType) { + if (!bot_iter->GetAA(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)) { + continue; + } + + LogTestDebug("{}: {} says, 'aaID is {}'", __LINE__, bot_iter->GetCleanName(), aaID); //deleteme + AA::Rank* tempRank = nullptr; + AA::Rank*& rank = tempRank; + uint16 spell_id = bot_iter->GetSpellByAA(aaID, rank); - bot_iter->SetCommandedSpell(true); + if (!IsValidSpell(spell_id)) { + continue; + } - if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { - if (!firstFound) { - firstFound = bot_iter; + if (!bot_iter->AttemptAACastSpell(tar, spell_id, rank)) { + continue; } isSuccess = true; ++successCount; } + else { + LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + bot_iter->SetCommandedSpell(true); + + if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + else { + bot_iter->GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", bot_iter->GetCleanName()); - bot_iter->SetCommandedSpell(false); + continue; + } + + bot_iter->SetCommandedSpell(false); + } continue; } @@ -460,7 +524,7 @@ void bot_command_cast(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "No bots are capable of casting [{}] on {}.", - c->GetSpellTypeNameByID(spellType), + (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), tar ? tar->GetCleanName() : "your target" ).c_str() ); @@ -471,7 +535,7 @@ void bot_command_cast(Client* c, const Seperator* sep) "{} {} [{}]{}", ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), - c->GetSpellTypeNameByID(spellType), + (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." ).c_str() ); diff --git a/zone/spells.cpp b/zone/spells.cpp index 4eb7a9fc52..9e73d64f26 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2566,7 +2566,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in } range = GetActSpellRange(spell_id, range); - if(IsClient() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){ + if(IsOfClientBot() && IsIllusionSpell(spell_id) && (HasProjectIllusion())){ range = 100; } From d919cf26b9c777eb4e142b83d38bf2333906038f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 8 Dec 2024 23:10:49 -0600 Subject: [PATCH 168/394] Add PetDamageShields and PetResistBuffs to IsPetBotSpellType() --- common/spdat.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/spdat.cpp b/common/spdat.cpp index 2d36d49163..42e64116ea 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3012,6 +3012,8 @@ bool IsPetBotSpellType(uint16 spellType) { case BotSpellTypes::PetFastHeals: case BotSpellTypes::PetVeryFastHeals: case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: return true; default: return false; From e5c491690f98870b7f1dccd03414919fecae424a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:58:54 -0600 Subject: [PATCH 169/394] Add priorities to HateLine inserts for db update --- .../database_update_manifest_bots.cpp | 166 +++++++++--------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index d93ec37bf2..bd35d776ff 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -1086,90 +1086,90 @@ FROM bot_spells_entries WHERE `npc_spells_id` = 3005 AND `type` = 10; -INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`, `priority`) VALUES -(3003, 10175, 24, 72, 76), -(3003, 10173, 24, 72, 76), -(3003, 10174, 24, 72, 76), -(3003, 14956, 24, 77, 81), -(3003, 14954, 24, 77, 81), -(3003, 14955, 24, 77, 81), -(3003, 19070, 24, 82, 86), -(3003, 19068, 24, 82, 86), -(3003, 19069, 24, 82, 86), -(3003, 25298, 24, 87, 91), -(3003, 25299, 24, 87, 91), -(3003, 25297, 24, 87, 91), -(3003, 28348, 24, 92, 96), -(3003, 28349, 24, 92, 96), -(3003, 28347, 24, 92, 96), -(3003, 34351, 24, 97, 254), -(3003, 34352, 24, 97, 254), -(3003, 34350, 24, 97, 254), -(3003, 40078, 24, 98, 254), -(3003, 40079, 24, 98, 254), -(3003, 40080, 24, 98, 254), -(3005, 1221, 24, 33, 41), -(3005, 1222, 24, 42, 52), -(3005, 1223, 24, 53, 58), -(3005, 1224, 24, 59, 62), -(3005, 3405, 24, 63, 66), -(3005, 5329, 24, 67, 70), -(3005, 5336, 24, 69, 73), -(3005, 10258, 24, 71, 71), -(3005, 10259, 24, 71, 71), -(3005, 10257, 24, 71, 71), -(3005, 10261, 24, 72, 76), -(3005, 10262, 24, 72, 76), -(3005, 10260, 24, 72, 76), -(3005, 10291, 24, 74, 78), -(3005, 10292, 24, 74, 78), -(3005, 10293, 24, 74, 78), -(3005, 15160, 24, 76, 76), -(3005, 15161, 24, 76, 76), -(3005, 15162, 24, 76, 76), -(3005, 15165, 24, 77, 81), -(3005, 15163, 24, 77, 81), -(3005, 15164, 24, 77, 81), -(3005, 15186, 24, 79, 83), -(3005, 15184, 24, 79, 83), -(3005, 15185, 24, 79, 83), -(3005, 19315, 24, 81, 81), -(3005, 19313, 24, 81, 81), -(3005, 19314, 24, 81, 81), -(3005, 19317, 24, 82, 86), -(3005, 19318, 24, 82, 86), -(3005, 19316, 24, 82, 86), -(3005, 19338, 24, 84, 88), -(3005, 19339, 24, 84, 88), -(3005, 19337, 24, 84, 88), -(3005, 25581, 24, 86, 86), -(3005, 25582, 24, 86, 86), -(3005, 25580, 24, 86, 86), -(3005, 25586, 24, 87, 91), -(3005, 25587, 24, 87, 91), -(3005, 25588, 24, 87, 91), -(3005, 25641, 24, 89, 93), -(3005, 25642, 24, 89, 93), -(3005, 25643, 24, 89, 93), -(3005, 28659, 24, 91, 91), -(3005, 28657, 24, 91, 91), -(3005, 28658, 24, 91, 91), -(3005, 28665, 24, 92, 96), -(3005, 28663, 24, 92, 96), -(3005, 28664, 24, 92, 96), -(3005, 28735, 24, 94, 98), -(3005, 28733, 24, 94, 98), -(3005, 28734, 24, 94, 98), -(3005, 34688, 24, 96, 96), -(3005, 34689, 24, 96, 96), -(3005, 34687, 24, 96, 96), -(3005, 34694, 24, 97, 254), -(3005, 34695, 24, 97, 254), -(3005, 34693, 24, 97, 254), -(3005, 34752, 24, 99, 254), -(3005, 34753, 24, 99, 254), -(3005, 34751, 24, 99, 254); +(3003, 10173, 24, 72, 76, 3), +(3003, 10174, 24, 72, 76, 2), +(3003, 10175, 24, 72, 76, 1), +(3003, 14954, 24, 77, 81, 3), +(3003, 14955, 24, 77, 81, 2), +(3003, 14956, 24, 77, 81, 1), +(3003, 19068, 24, 82, 86, 3), +(3003, 19069, 24, 82, 86, 2), +(3003, 19070, 24, 82, 86, 1), +(3003, 25297, 24, 87, 91, 3), +(3003, 25298, 24, 87, 91, 2), +(3003, 25299, 24, 87, 91, 1), +(3003, 28347, 24, 92, 96, 3), +(3003, 28348, 24, 92, 96, 2), +(3003, 28349, 24, 92, 96, 1), +(3003, 34350, 24, 97, 254, 3), +(3003, 34351, 24, 97, 254, 2), +(3003, 34352, 24, 97, 254, 1), +(3003, 40078, 24, 98, 254, 3), +(3003, 40079, 24, 98, 254, 2), +(3003, 40080, 24, 98, 254, 1), +(3005, 1221, 24, 33, 41, 1), +(3005, 1222, 24, 42, 52, 1), +(3005, 1223, 24, 53, 58, 1), +(3005, 1224, 24, 59, 62, 1), +(3005, 3405, 24, 63, 66, 1), +(3005, 5329, 24, 67, 70, 5), +(3005, 5336, 24, 69, 73, 4), +(3005, 10257, 24, 71, 71, 3), +(3005, 10258, 24, 71, 71, 2), +(3005, 10259, 24, 71, 71, 1), +(3005, 10260, 24, 72, 76, 3), +(3005, 10261, 24, 72, 76, 2), +(3005, 10262, 24, 72, 76, 1), +(3005, 10291, 24, 74, 78, 3), +(3005, 10292, 24, 74, 78, 2), +(3005, 10293, 24, 74, 78, 1), +(3005, 15160, 24, 76, 76, 3), +(3005, 15161, 24, 76, 76, 2), +(3005, 15162, 24, 76, 76, 1), +(3005, 15163, 24, 77, 81, 3), +(3005, 15164, 24, 77, 81, 2), +(3005, 15165, 24, 77, 81, 1), +(3005, 15184, 24, 79, 83, 3), +(3005, 15185, 24, 79, 83, 2), +(3005, 15186, 24, 79, 83, 1), +(3005, 19313, 24, 81, 81, 3), +(3005, 19314, 24, 81, 81, 2), +(3005, 19315, 24, 81, 81, 1), +(3005, 19316, 24, 82, 86, 3), +(3005, 19317, 24, 82, 86, 2), +(3005, 19318, 24, 82, 86, 1), +(3005, 19337, 24, 84, 88, 3), +(3005, 19338, 24, 84, 88, 2), +(3005, 19339, 24, 84, 88, 1), +(3005, 25580, 24, 86, 86, 3), +(3005, 25581, 24, 86, 86, 2), +(3005, 25582, 24, 86, 86, 1), +(3005, 25586, 24, 87, 91, 3), +(3005, 25587, 24, 87, 91, 2), +(3005, 25588, 24, 87, 91, 1), +(3005, 25641, 24, 89, 93, 3), +(3005, 25642, 24, 89, 93, 2), +(3005, 25643, 24, 89, 93, 1), +(3005, 28657, 24, 91, 91, 3), +(3005, 28658, 24, 91, 91, 2), +(3005, 28659, 24, 91, 91, 1), +(3005, 28663, 24, 92, 96, 3), +(3005, 28664, 24, 92, 96, 2), +(3005, 28665, 24, 92, 96, 1), +(3005, 28733, 24, 94, 98, 3), +(3005, 28734, 24, 94, 98, 2), +(3005, 28735, 24, 94, 98, 1), +(3005, 34687, 24, 96, 96, 3), +(3005, 34688, 24, 96, 96, 2), +(3005, 34689, 24, 96, 96, 1), +(3005, 34693, 24, 97, 254, 3), +(3005, 34694, 24, 97, 254, 2), +(3005, 34695, 24, 97, 254, 1), +(3005, 34751, 24, 99, 254, 3), +(3005, 34752, 24, 99, 254, 2), +(3005, 34753, 24, 99, 254, 1); DELETE FROM bot_spells_entries From c42d84b59ad99de8106a1704b5ab5a0cab869a74 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:59:17 -0600 Subject: [PATCH 170/394] Remove SpellTypeRequiresCastChecks --- common/spdat.cpp | 25 ------------------------- common/spdat.h | 1 - 2 files changed, 26 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 42e64116ea..ff4a3006ae 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3116,31 +3116,6 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls) { return true; } -bool SpellTypeRequiresCastChecks(uint16 spellType) { - switch (spellType) { - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AEDispel: - case BotSpellTypes::AEDoT: - case BotSpellTypes::AEFear: - case BotSpellTypes::AELifetap: - case BotSpellTypes::AEMez: - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AERoot: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: - case BotSpellTypes::AEStun: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Mez: - case BotSpellTypes::SummonCorpse: - return false; - default: - return true; - } - - return true; -} - bool SpellTypeRequiresAEChecks(uint16 spellType) { switch (spellType) { case BotSpellTypes::AEMez: diff --git a/common/spdat.h b/common/spdat.h index 01cdf85af6..7b533328b0 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -748,7 +748,6 @@ bool IsClientBotSpellType(uint16 spellType); bool IsHealBotSpellType(uint16 spellType); bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); -bool SpellTypeRequiresCastChecks(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); bool IsPullingSpellType(uint16 spellType); From 7834222b5fa368cc3708900b0c2a83fd69efdfcc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:59:48 -0600 Subject: [PATCH 171/394] Add bot check to DetermineSpellTargets for IsIllusionSpell --- zone/spells.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 9e73d64f26..b6540d0ccf 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1925,6 +1925,7 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce && spell_target != nullptr // null ptr crash safeguard && !spell_target->IsNPC() // still self only if NPC targetted && IsClient() + && IsOfClientBot() && (IsGrouped() // still self only if not grouped || IsRaidGrouped()) && (HasProjectIllusion())){ From 70fe633d5bf789aaf015a48da4fbaf1be4b44e65 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 13:59:59 -0600 Subject: [PATCH 172/394] merge with previous --- zone/spells.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index b6540d0ccf..6daa430ceb 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1924,7 +1924,6 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(IsIllusionSpell(spell_id) && spell_target != nullptr // null ptr crash safeguard && !spell_target->IsNPC() // still self only if NPC targetted - && IsClient() && IsOfClientBot() && (IsGrouped() // still self only if not grouped || IsRaidGrouped()) From 746bccf4fd48c612be38eaa93fd0ede93b0f91ac Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:00:17 -0600 Subject: [PATCH 173/394] Correct bot checks for ST_GroupClientAndPet --- zone/spells.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/zone/spells.cpp b/zone/spells.cpp index 6daa430ceb..9bc775aa3c 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2307,6 +2307,19 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce if(GetOwner()) group_id_caster = (GetRaid()->GetGroup(GetOwner()->CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(GetOwner()->CastToClient()) + 1); } + if (IsGrouped()) + { + if (Group* group = GetGroup()) { + group_id_caster = group->GetID(); + } + } + else if (IsRaidGrouped()) + { + if (Raid* raid = GetRaid()) { + uint32 group_id = raid->GetGroup(GetName()); + group_id_caster = (group_id == 0xFFFFFFFF) ? 0 : (group_id + 1); + } + } } if(spell_target->IsClient()) From 2bffc1b79c9e3c3d34ea364b44155eb85cbec663 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:01:29 -0600 Subject: [PATCH 174/394] Remove misc target_type checks --- zone/bot.cpp | 10 +--------- zone/botspellsai.cpp | 43 ++++++++++++++++++++++--------------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e78bfa8ce4..72da57caed 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9739,15 +9739,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::Invisibility: case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: - if ( // TODO bot rewrite - fix this, missing other target types (43 for example) - !( - spells[spell_id].target_type == ST_Target || - spells[spell_id].target_type == ST_Pet || - (tar == this && spells[spell_id].target_type != ST_TargetsTarget) || - spells[spell_id].target_type == ST_Group || - spells[spell_id].target_type == ST_GroupTeleport - ) - ) { + if (tar == this && spells[spell_id].target_type == ST_TargetsTarget) { LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme return false; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b4588811d0..aa24443a34 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -595,27 +595,28 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); - if ( - ( - ( - ( - (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || - spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || - spells[AIBot_spells[i].spellid].target_type ==ST_Group || - spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || - ( - tar == this && spells[AIBot_spells[i].spellid].target_type != ST_TargetsTarget - ) - ) && - dist2 <= spells[AIBot_spells[i].spellid].aoe_range*spells[AIBot_spells[i].spellid].aoe_range - ) || - dist2 <= GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range)*GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range) - ) && - ( - mana_cost <= GetMana() || - IsBotNonSpellFighter() - ) - ) { + //if ( + // ( + // ( + // ( + // (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || + // spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || + // spells[AIBot_spells[i].spellid].target_type ==ST_Group || + // spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || + // ( + // tar == this && spells[AIBot_spells[i].spellid].target_type != ST_TargetsTarget + // ) + // ) && + // dist2 <= spells[AIBot_spells[i].spellid].aoe_range*spells[AIBot_spells[i].spellid].aoe_range + // ) || + // dist2 <= GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range)*GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range) + // ) && + // ( + // mana_cost <= GetMana() || + // IsBotNonSpellFighter() + // ) + //) { + if (IsValidSpellRange(AIBot_spells[i].spellid, tar) && (mana_cost <= GetMana() || IsBotNonSpellFighter())) { casting_spell_AIindex = i; LogAI("spellid [{}] tar [{}] mana [{}] Name [{}]", AIBot_spells[i].spellid, tar->GetName(), mana_cost, spells[AIBot_spells[i].spellid].name); result = Mob::CastSpell(AIBot_spells[i].spellid, tar->GetID(), EQ::spells::CastingSlot::Gem2, spells[AIBot_spells[i].spellid].cast_time, AIBot_spells[i].manacost == -2 ? 0 : mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIBot_spells[i].resist_adjust)); From bf4f2f5623d52ea5e4694ac27ccb7572df4a5e93 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:03:33 -0600 Subject: [PATCH 175/394] Add lull/aelull to ^cast --- common/ruletypes.h | 1 + common/spdat.cpp | 7 +++++++ common/spdat.h | 3 ++- zone/bot.cpp | 7 +++++-- zone/bot_commands/cast.cpp | 8 ++++++++ zone/botspellsai.cpp | 1 + zone/mob.cpp | 6 ++++++ 7 files changed, 30 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 08cac9b0ed..463268f324 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -884,6 +884,7 @@ RULE_BOOL(Bots, AllowCommandedCharm, true, "If enabled bots can be commanded to RULE_BOOL(Bots, AllowCommandedMez, true, "If enabled bots can be commanded to mez NPCs.") RULE_BOOL(Bots, AllowCommandedResurrect, true, "If enabled bots can be commanded to resurrect players.") RULE_BOOL(Bots, AllowCommandedSummonCorpse, true, "If enabled bots can be commanded to summon other's corpses.") +RULE_BOOL(Bots, AllowCommandedLull, true, "If enabled bots can be commanded to lull targets.") RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.") diff --git a/common/spdat.cpp b/common/spdat.cpp index ff4a3006ae..d5ec840928 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2820,6 +2820,7 @@ bool IsBotSpellTypeDetrimental(uint16 spellType, uint8 cls) { case BotSpellTypes::AELifetap: case BotSpellTypes::PBAENuke: case BotSpellTypes::Lull: + case BotSpellTypes::AELull: case BotSpellTypes::HateLine: case BotSpellTypes::AEHateLine: return true; @@ -2946,6 +2947,9 @@ bool IsBotSpellTypeInnate(uint16 spellType) { case BotSpellTypes::AEMez: case BotSpellTypes::Mez: case BotSpellTypes::Lull: + case BotSpellTypes::AELull: + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return true; default: return false; @@ -2969,6 +2973,8 @@ bool IsAEBotSpellType(uint16 spellType) { case BotSpellTypes::PBAENuke: case BotSpellTypes::AELifetap: case BotSpellTypes::AERoot: + case BotSpellTypes::AEHateLine: + case BotSpellTypes::AELull: return true; default: return false; @@ -3242,6 +3248,7 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: + case BotSpellTypes::AELull: //case BotSpellTypes::Cure: //case BotSpellTypes::GroupCures: //case BotSpellTypes::DamageShields: diff --git a/common/spdat.h b/common/spdat.h index 7b533328b0..e030143c7c 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -725,11 +725,12 @@ namespace BotSpellTypes constexpr uint16 MovementSpeed = 110; constexpr uint16 SendHome = 111; constexpr uint16 SummonCorpse = 112; + constexpr uint16 AELull = 113; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this - constexpr uint16 COMMANDED_END = BotSpellTypes::SummonCorpse; // Do not remove this, increment as needed + constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed } const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); diff --git a/zone/bot.cpp b/zone/bot.cpp index 72da57caed..f803ffce43 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9867,6 +9867,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } break; + case BotSpellTypes::AELull: case BotSpellTypes::Lull: if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme @@ -11223,6 +11224,9 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::AELifetap: case BotSpellTypes::Lifetap: return BotSpellTypes::Lifetap; + case BotSpellTypes::AELull: + case BotSpellTypes::Lull: + return BotSpellTypes::Lull; case BotSpellTypes::Charm: case BotSpellTypes::Escape: case BotSpellTypes::HateRedux: @@ -11234,8 +11238,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { case BotSpellTypes::Pet: case BotSpellTypes::PreCombatBuff: case BotSpellTypes::PreCombatBuffSong: - case BotSpellTypes::Resurrect: - case BotSpellTypes::Lull: + case BotSpellTypes::Resurrect: default: return spellType; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 7915a491c9..5866bacf41 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -268,6 +268,14 @@ void bot_command_cast(Client* c, const Seperator* sep) return; } + break; + case BotSpellTypes::AELull: + case BotSpellTypes::Lull: + if (!RuleB(Bots, AllowCommandedLull)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + break; case BotSpellTypes::SummonCorpse: if (!RuleB(Bots, AllowCommandedSummonCorpse)) { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index aa24443a34..93c38ccf68 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -83,6 +83,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge } break; + case BotSpellTypes::AELull: case BotSpellTypes::Lull: if (tar->GetSpecialAbility(SpecialAbility::PacifyImmunity)) { return false; diff --git a/zone/mob.cpp b/zone/mob.cpp index 355292b69f..b47908a172 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8926,6 +8926,9 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::SummonCorpse: spellTypeName = "Summon Corpse"; break; + case BotSpellTypes::AELull: + spellTypeName = "AE Lull"; + break; default: break; } @@ -9147,6 +9150,9 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::SummonCorpse: spellTypeName = "summoncorpse"; break; + case BotSpellTypes::AELull: + spellTypeName = "aelull"; + break; default: break; } From 9d5b679fc49a2d4bba38733e7cf95f5fd1a48872 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:04:07 -0600 Subject: [PATCH 176/394] Add more checks for CommandedSubTypes::AETarget --- zone/bot.cpp | 2 +- zone/botspellsai.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index f803ffce43..ef5012ce37 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11894,7 +11894,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell break; case CommandedSubTypes::AETarget: - if (IsAnyAESpell(spell_id)) { + if (IsAnyAESpell(spell_id) && !IsGroupSpell(spell_id)) { return true; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 93c38ccf68..c6ac703976 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -216,7 +216,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; } - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType), subTargetType, subType); + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, (IsAEBotSpellType(spellType) || subTargetType == CommandedSubTypes::AETarget), subTargetType, subType); for (const auto& s : botSpellList) { From d3617a56f5c3544d1ed34f8959d43ad92216a2b9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 10 Dec 2024 14:07:21 -0600 Subject: [PATCH 177/394] remove unneeded checks on IsValidSpellTypeBySpellID --- zone/bot.cpp | 82 ++-------------------------------------------------- 1 file changed, 2 insertions(+), 80 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ef5012ce37..73af03af6a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9750,12 +9750,12 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } if (!tar->CheckSpellLevelRestriction(this, spell_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -11302,84 +11302,6 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { } return false; - //case BotSpellTypes::Lull: - // if (IsHarmonySpell(spell_id)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Teleport: - // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Succor: - // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::BindAffinity: - // if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Identify: - // if (IsEffectInSpell(spell_id, SE_Identify)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Levitate: - // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Rune: - // if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::WaterBreathing: - // if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Size: - // if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - // return true; - // } - // - // return false; - //case BotSpellTypes::Invisibility: - // if (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::MovementSpeed: - // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::SendHome: - // if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - // return true; - // } - // - // return false; - //case BotSpellTypes::SummonCorpse: - // if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - // return true; - // } - // - // return false; default: return true; } From 711100158a920396b756d6f3e6b5232304344b94 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:16:43 -0600 Subject: [PATCH 178/394] add to aelull --- common/spdat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index d5ec840928..c912553947 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3235,6 +3235,7 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::AEFear: case BotSpellTypes::Fear: case BotSpellTypes::Resurrect: + case BotSpellTypes::AELull: case BotSpellTypes::Lull: case BotSpellTypes::Teleport: case BotSpellTypes::Succor: @@ -3248,7 +3249,6 @@ bool IsCommandedSpellType(uint16 spellType) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: - case BotSpellTypes::AELull: //case BotSpellTypes::Cure: //case BotSpellTypes::GroupCures: //case BotSpellTypes::DamageShields: From 92ba83e308362f56db7f43eed38e68f13fcf2a82 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:18:12 -0600 Subject: [PATCH 179/394] rewrite GetCorrectSpellType --- common/ruletypes.h | 3 + common/spdat.cpp | 250 ++++++++++++++++++++++++- common/spdat.h | 3 + zone/bot.cpp | 2 +- zone/botspellsai.cpp | 421 ++++--------------------------------------- 5 files changed, 291 insertions(+), 388 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 463268f324..57721efa78 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -830,6 +830,9 @@ RULE_INT(Bots, MinTargetsForAESpell, 3, "Minimum number of targets in valid rang RULE_INT(Bots, MinTargetsForGroupSpell, 3, "Minimum number of targets in valid range that are required for an group spell to cast. Default 3.") RULE_BOOL(Bots, AllowBuffingHealingFamiliars, false, "Determines if bots are allowed to buff and heal familiars. Default false.") RULE_BOOL(Bots, RunSpellTypeChecksOnSpawn, false, "This will run a serious of checks on spell types and output errors to LogBotSpellTypeChecks") +RULE_BOOL(Bots, UseParentSpellTypeForChecks, true, "This will check only the parent instead of AE/Group/Pet types (ex: AENukes/AERains/PBAENukes fall under Nukes or PetBuffs fall under buffs) when RunSpellTypeChecksOnSpawn fires") +RULE_BOOL(Bots, AllowForcedCastsBySpellID, true, "If enabled, players can use ^cast spellid # to cast a specific spell by ID that is in their spell list") +RULE_BOOL(Bots, AllowCastAAs, true, "If enabled, players can use ^cast aa to cast a clickable AA") RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summon their epic pets following the rules AllowMagicianEpicPetLevel") RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") diff --git a/common/spdat.cpp b/common/spdat.cpp index c912553947..cbff317312 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -1582,11 +1582,11 @@ bool IsRegularPetHealSpell(uint16 spell_id) if (spell_id) { if ( - spells[spell_id].target_type == ST_Pet && + (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) && !IsCompleteHealSpell(spell_id) && !IsHealOverTimeSpell(spell_id) && !IsGroupSpell(spell_id) - ) { + ) { for (int i = 0; i < EFFECT_COUNT; i++) { if ( spells[spell_id].base_value[i] > 0 && @@ -1594,8 +1594,8 @@ bool IsRegularPetHealSpell(uint16 spell_id) ( spells[spell_id].effect_id[i] == SE_CurrentHP || spells[spell_id].effect_id[i] == SE_CurrentHPOnce - ) - ) { + ) + ) { return true; } } @@ -3229,6 +3229,17 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { return true; } +bool IsHateSpell(uint16 spell_id) { + if ( + (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || + (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) + ) { + return true; + } + + return false; +} + bool IsCommandedSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::Charm: @@ -3281,3 +3292,234 @@ bool IsPullingSpellType(uint16 spellType) { return false; } + +uint16 GetCorrectSpellType(uint16 spellType, uint16 spell_id) { + uint16 correctType = UINT16_MAX; + SPDat_Spell_Struct spell = spells[spell_id]; + std::string teleportZone = spell.teleport_zone; + + if (IsCharmSpell(spell_id)) { + correctType = BotSpellTypes::Charm; + } + else if (IsFearSpell(spell_id)) { + correctType = BotSpellTypes::Fear; + } + else if (IsEffectInSpell(spell_id, SE_Revive)) { + correctType = BotSpellTypes::Resurrect; + } + else if (IsHarmonySpell(spell_id)) { + correctType = BotSpellTypes::Lull; + } + else if (teleportZone.compare("") && !IsEffectInSpell(spell_id, SE_GateToHomeCity) && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + correctType = BotSpellTypes::Teleport; + } + else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { + correctType = BotSpellTypes::Succor; + } + else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + correctType = BotSpellTypes::BindAffinity; + } + else if (IsEffectInSpell(spell_id, SE_Identify)) { + correctType = BotSpellTypes::Identify; + } + else if (spellType == BotSpellTypes::Levitate && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + correctType = BotSpellTypes::Levitate; + } + else if (spellType == BotSpellTypes::Rune && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))) { + correctType = BotSpellTypes::Rune; + } + else if (spellType == BotSpellTypes::WaterBreathing && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { + correctType = BotSpellTypes::WaterBreathing; + } + else if (spellType == BotSpellTypes::Size && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + correctType = BotSpellTypes::Size; + } + else if (spellType == BotSpellTypes::Invisibility && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))) { + correctType = BotSpellTypes::Invisibility; + } + else if (spellType == BotSpellTypes::MovementSpeed && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + correctType = BotSpellTypes::MovementSpeed; + } + else if (!teleportZone.compare("") && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))) { + correctType = BotSpellTypes::SendHome; + } + else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + correctType = BotSpellTypes::SummonCorpse; + } + + if (correctType == UINT16_MAX) { + if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { + correctType = BotSpellTypes::Pet; + } + else if (IsMesmerizeSpell(spell_id)) { + correctType = BotSpellTypes::Mez; + } + else if (IsEscapeSpell(spell_id)) { + correctType = BotSpellTypes::Escape; + } + else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_Root)) { + if (IsAnyAESpell(spell_id)) { + correctType = BotSpellTypes::AERoot; + } + else { + correctType = BotSpellTypes::Root; + } + } + else if (IsDetrimentalSpell(spell_id) && IsLifetapSpell(spell_id)) { + correctType = BotSpellTypes::Lifetap; + } + else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + correctType = BotSpellTypes::Snare; + } + else if (IsDetrimentalSpell(spell_id) && (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))) { + correctType = BotSpellTypes::DOT; + } + else if (IsDispelSpell(spell_id)) { + correctType = BotSpellTypes::Dispel; + } + else if (IsDetrimentalSpell(spell_id) && IsSlowSpell(spell_id)) { + correctType = BotSpellTypes::Slow; + } + else if (IsDebuffSpell(spell_id) && !IsHateReduxSpell(spell_id) && !IsHateSpell(spell_id)) { + correctType = BotSpellTypes::Debuff; + } + else if (IsHateReduxSpell(spell_id)) { + correctType = BotSpellTypes::HateRedux; + } + else if (IsDetrimentalSpell(spell_id) && IsHateSpell(spell_id)) { + correctType = BotSpellTypes::HateLine; + } + else if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) + ) { + if ( + spellType == BotSpellTypes::InCombatBuffSong || + spellType == BotSpellTypes::OutOfCombatBuffSong || + spellType == BotSpellTypes::PreCombatBuffSong + ) { + correctType = spellType; + } + else { + correctType = BotSpellTypes::OutOfCombatBuffSong; + } + } + else if ( + !IsBardSong(spell_id) && + ( + (IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) || + (spellType == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) + ) + ) { + correctType = BotSpellTypes::InCombatBuff; + } + else if ( + spellType == BotSpellTypes::PreCombatBuff && + IsAnyBuffSpell(spell_id) && + !IsBardSong(spell_id) + ) { + correctType = BotSpellTypes::PreCombatBuff; + } + else if ( + (IsCureSpell(spell_id) && spellType == BotSpellTypes::Cure) || + (IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id)) + ) { + correctType = BotSpellTypes::Cure; + } + else if (IsAnyNukeOrStunSpell(spell_id)) { + if (IsAnyAESpell(spell_id)) { + if (IsAERainSpell(spell_id)) { + correctType = BotSpellTypes::AERains; + } + else if (IsPBAENukeSpell(spell_id)) { + correctType = BotSpellTypes::PBAENuke; + } + else if (IsStunSpell(spell_id)) { + correctType = BotSpellTypes::AEStun; + } + else { + correctType = BotSpellTypes::AENukes; + } + } + else if (IsStunSpell(spell_id)) { + correctType = BotSpellTypes::Stun; + } + else { + correctType = BotSpellTypes::Nuke; + } + } + else if (IsAnyHealSpell(spell_id)) { + if (IsGroupSpell(spell_id)) { + if (IsGroupCompleteHealSpell(spell_id)) { + correctType = BotSpellTypes::GroupCompleteHeals; + } + else if (IsGroupHealOverTimeSpell(spell_id)) { + correctType = BotSpellTypes::GroupHoTHeals; + } + else if (IsRegularGroupHealSpell(spell_id)) { + correctType = BotSpellTypes::GroupHeals; + } + } + else { + if (IsVeryFastHealSpell(spell_id)) { + correctType = BotSpellTypes::VeryFastHeals; + } + else if (IsFastHealSpell(spell_id)) { + correctType = BotSpellTypes::FastHeals; + } + else if (IsCompleteHealSpell(spell_id)) { + correctType = BotSpellTypes::CompleteHeal; + } + else if (IsHealOverTimeSpell(spell_id)) { + correctType = BotSpellTypes::HoTHeals; + } + else if (IsRegularSingleTargetHealSpell(spell_id)) { + correctType = BotSpellTypes::RegularHeal; + } + else if (IsRegularPetHealSpell(spell_id)) { + correctType = BotSpellTypes::RegularHeal; + } + } + } + else if (IsAnyBuffSpell(spell_id)) { + if (IsResistanceOnlySpell(spell_id)) { + correctType = BotSpellTypes::ResistBuffs; + } + else if (IsDamageShieldOnlySpell(spell_id)) { + correctType = BotSpellTypes::DamageShields; + } + else { + correctType = BotSpellTypes::Buff; + } + } + } + + + return correctType; +} + +uint16 GetPetSpellType(uint16 spellType) { + switch (spellType) { + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; + default: + return spellType; + } + + return spellType; +} diff --git a/common/spdat.h b/common/spdat.h index e030143c7c..ffe3525ade 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -752,6 +752,8 @@ bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); bool IsPullingSpellType(uint16 spellType); +uint16 GetCorrectSpellType(uint16 spellType, uint16 spell_id); +uint16 GetPetSpellType(uint16 spellType); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only @@ -1748,5 +1750,6 @@ bool IsResurrectSpell(uint16 spell_id); bool RequiresStackCheck(uint16 spellType); bool IsResistanceOnlySpell(uint16 spell_id); bool IsDamageShieldOnlySpell(uint16 spell_id); +bool IsHateSpell(uint16 spell_id); #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 73af03af6a..48902541f9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3563,7 +3563,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { } if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) { - OwnerMessage("Running SpellType checks. There may be some spells that are flagged as incorrect but actually are correct. Use this as a guideline."); + OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline."); CheckBotSpells(); //This runs through a serious of checks and outputs any spells that are set to the wrong spell type in the database } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index c6ac703976..835e990400 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2753,10 +2753,11 @@ BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, u } void Bot::CheckBotSpells() { - bool valid = false; - uint16 correctType; auto spellList = BotSpellsEntriesRepository::All(content_db); uint16 spell_id; + SPDat_Spell_Struct spell; + uint16 correctType; + uint16 parentType; for (const auto& s : spellList) { if (!IsValidSpell(s.spell_id)) { @@ -2764,64 +2765,65 @@ void Bot::CheckBotSpells() { continue; } - spell_id = s.spell_id; + spell = spells[s.spell_id]; + spell_id = spell.id; - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] >= 255) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) { LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); //deleteme } else { - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.minlevel) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) { LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the min level is currently set to {}." , GetSpellName(spell_id) , spell_id - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.minlevel ); //deleteme LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , spell_id , s.npc_spells_id , GetSpellName(spell_id) , spell_id , s.minlevel - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id ); //deleteme } - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] < s.minlevel) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) { LogBotSpellTypeChecks("{} [#{}] could be used starting at level {} for a {} [#{}] instead of the current min level of {}." , GetSpellName(spell_id) , spell_id - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.minlevel ); //deleteme LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , spell_id , s.npc_spells_id , GetSpellName(spell_id) , spell_id , s.minlevel - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id ); //deleteme } - if (spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] > s.maxlevel) { + if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.maxlevel) { LogBotSpellTypeChecks("{} [#{}] is not usable until level {} for a {} [#{}] and the max level is currently set to {}." , GetSpellName(spell_id) , spell_id - , spells[spell_id].classes[s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX] + , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.maxlevel @@ -2829,380 +2831,33 @@ void Bot::CheckBotSpells() { } } - correctType = UINT16_MAX; - valid = false; + correctType = GetCorrectSpellType(s.type, spell_id); + parentType = GetSpellListSpellType(correctType); - - switch (s.type) { - case BotSpellTypes::Nuke: - if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::RegularHeal: - if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Root: - if (IsEffectInSpell(spell_id, SE_Root)) { - valid = true; - break; - } - break; - case BotSpellTypes::Buff: - if (IsAnyBuffSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Pet: - if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { - valid = true; - break; - } - break; - case BotSpellTypes::Lifetap: - if (IsLifetapSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Snare: - if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::DOT: - if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Dispel: - if (IsDispelSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::InCombatBuff: - if ( - IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::HateLine: - if ( - (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || - (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::Mez: - if (IsMesmerizeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Charm: - if (IsCharmSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Slow: - if (IsSlowSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Debuff: - if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Cure: - if (IsCureSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::PreCombatBuff: - if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - !IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::PreCombatBuffSong: - if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - valid = true; - break; - } - break; - case BotSpellTypes::Fear: - if (IsFearSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Escape: - if (IsEscapeSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::HateRedux: - if (IsHateReduxSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Resurrect: - if (IsEffectInSpell(spell_id, SE_Revive)) { - valid = true; - break; - } - break; - case BotSpellTypes::Lull: - if (IsHarmonySpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::Teleport: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - valid = true; - break; - } - break; - case BotSpellTypes::Succor: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - valid = true; - break; - } - break; - case BotSpellTypes::BindAffinity: - if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - valid = true; - break; - } - break; - case BotSpellTypes::Identify: - if (IsEffectInSpell(spell_id, SE_Identify)) { - valid = true; - break; - } - break; - case BotSpellTypes::Levitate: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - valid = true; - break; - } - break; - case BotSpellTypes::Rune: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - valid = true; - break; - } - break; - case BotSpellTypes::WaterBreathing: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { - valid = true; - break; - } - break; - case BotSpellTypes::Size: - if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - valid = true; - break; - } - break; - case BotSpellTypes::Invisibility: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - valid = true; - break; - } - break; - case BotSpellTypes::MovementSpeed: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - valid = true; - break; - } - break; - case BotSpellTypes::SendHome: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - valid = true; - break; - } - break; - case BotSpellTypes::SummonCorpse: - if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - valid = true; - break; - } - break; - default: - break; - - } - - if (IsAnyNukeOrStunSpell(spell_id) && !IsEffectInSpell(spell_id, SE_Root) && !IsDebuffSpell(spell_id)) { - correctType = BotSpellTypes::Nuke; - } - else if (IsAnyHealSpell(spell_id) && !IsEscapeSpell(spell_id)) { - correctType = BotSpellTypes::RegularHeal; - } - else if (IsEffectInSpell(spell_id, SE_Root)) { - correctType = BotSpellTypes::Root; - } - else if (IsAnyBuffSpell(spell_id)) { - correctType = BotSpellTypes::Buff; - } - else if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { - correctType = BotSpellTypes::Pet; - } - else if (IsLifetapSpell(spell_id)) { - correctType = BotSpellTypes::Lifetap; - } - else if (IsEffectInSpell(spell_id, SE_MovementSpeed) && IsDetrimentalSpell(spell_id)) { - correctType = BotSpellTypes::Snare; - } - else if (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) { - correctType = BotSpellTypes::DOT; - } - else if (IsDispelSpell(spell_id)) { - correctType = BotSpellTypes::Dispel; - } - else if ( - IsSelfConversionSpell(spell_id) || - IsAnyBuffSpell(spell_id) - ) { - correctType = BotSpellTypes::InCombatBuff; - } - else if ( - (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || - (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) - ) { - correctType = BotSpellTypes::HateLine; - } - else if (IsMesmerizeSpell(spell_id)) { - correctType = BotSpellTypes::Mez; - } - else if (IsCharmSpell(spell_id)) { - correctType = BotSpellTypes::Charm; - } - else if (IsSlowSpell(spell_id)) { - correctType = BotSpellTypes::Slow; - } - else if (IsDebuffSpell(spell_id) && !IsEscapeSpell(spell_id) && !IsHateReduxSpell(spell_id)) { - correctType = BotSpellTypes::Debuff; - } - else if (IsCureSpell(spell_id)) { - correctType = BotSpellTypes::Cure; - } - else if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - if ( - s.type == BotSpellTypes::InCombatBuffSong || - s.type == BotSpellTypes::OutOfCombatBuffSong || - s.type == BotSpellTypes::PreCombatBuffSong - ) { - correctType = s.type; - } - else { - correctType = BotSpellTypes::OutOfCombatBuffSong; + if (RuleB(Bots, UseParentSpellTypeForChecks)) { + if (s.type == parentType || s.type == correctType) { + continue; } } - else if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - !IsBardSong(spell_id) && - !IsEscapeSpell(spell_id) && - (!IsSummonPetSpell(spell_id) && !IsEffectInSpell(spell_id, SE_TemporaryPets)) - ) { - correctType = BotSpellTypes::PreCombatBuff; - } - else if (IsFearSpell(spell_id)) { - correctType = BotSpellTypes::Fear; - } - else if (IsEscapeSpell(spell_id)) { - correctType = BotSpellTypes::Escape; - } - else if (IsHateReduxSpell(spell_id)) { - correctType = BotSpellTypes::HateRedux; - } - else if (IsEffectInSpell(spell_id, SE_Revive)) { - correctType = BotSpellTypes::Resurrect; - } - else if (IsHarmonySpell(spell_id)) { - correctType = BotSpellTypes::Lull; - } - else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - correctType = BotSpellTypes::Teleport; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - correctType = BotSpellTypes::Succor; - } - else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - correctType = BotSpellTypes::BindAffinity; - } - else if (IsEffectInSpell(spell_id, SE_Identify)) { - correctType = BotSpellTypes::Identify; - } - else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - correctType = BotSpellTypes::Levitate; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { - correctType = BotSpellTypes::Rune; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { - correctType = BotSpellTypes::WaterBreathing; - } - else if (IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - correctType = BotSpellTypes::Size; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) { - correctType = BotSpellTypes::Invisibility; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - correctType = BotSpellTypes::MovementSpeed; + else { + if (IsPetBotSpellType(s.type)) { + correctType = GetPetSpellType(correctType); + } } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_GateToHomeCity)) { - correctType = BotSpellTypes::SendHome; + + if (correctType == s.type) { + continue; } - else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - correctType = BotSpellTypes::SummonCorpse; + + if (correctType == UINT16_MAX) { + LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown." + , GetSpellName(spell_id) + , spell_id + , GetSpellTypeNameByID(s.type) + , s.type + ); //deleteme } - - if (!valid || (correctType == UINT16_MAX) || (s.type != correctType)) { + else { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" , GetSpellName(spell_id) , spell_id @@ -3211,7 +2866,7 @@ void Bot::CheckBotSpells() { , GetSpellTypeNameByID(correctType) , correctType ); //deleteme - LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spellid` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" + LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" , correctType , spell_id , GetSpellName(spell_id) From c345187464d39ca154ff0f84c833f147dabd81a3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:18:40 -0600 Subject: [PATCH 180/394] Add IsBlockedBuff to CastChecks --- zone/bot.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 48902541f9..ee4417ba81 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9641,7 +9641,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec spells[spell_id].target_type == ST_Self && tar != this && (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) - ) { + ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9656,6 +9656,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (IsBeneficialSpell(spell_id) && tar->IsBlockedBuff(spell_id)) { + LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme if (!CanCastSpellType(spellType, spell_id, tar)) { return false; From 49e15d894400c87f9faf4114c00a253a2700d0b1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 11 Dec 2024 07:19:17 -0600 Subject: [PATCH 181/394] Add spellid option to ^cast to allow casting of a specific spell by ID --- zone/bot.cpp | 135 ++++++++++++++++++++- zone/bot.h | 2 +- zone/bot_commands/cast.cpp | 233 ++++++++++++++++++++++++++----------- 3 files changed, 300 insertions(+), 70 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ee4417ba81..ff8214f1a1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9698,7 +9698,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (spellType == UINT16_MAX) { //AA cast checks, return here + if (spellType == UINT16_MAX) { //AA/Forced cast checks, return here return true; } @@ -11087,7 +11087,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType) { } bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { - if (!tar) { + if (!tar || spells[spell_id].target_type == ST_Self) { tar = this; } @@ -11121,11 +11121,25 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { } + if (IsCasting()) { + BotGroupSay( + this, + fmt::format( + "Interrupting {}. I have been commanded to try to cast an AA - {} on {}.", + CastingSpellID() ? spells[CastingSpellID()].name : "my spell", + spells[spell_id].name, + tar->GetCleanName() + ).c_str() + ); + + InterruptSpell(); + } + if (CastSpell(spell_id, tar->GetID())) { BotGroupSay( this, fmt::format( - "Casting {} on {}.", + "Casting an AA - {} on {}.", GetSpellName(spell_id), (tar == this ? "myself" : tar->GetCleanName()) ).c_str() @@ -11161,6 +11175,121 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { return true; } +bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { + SPDat_Spell_Struct spell = spells[spell_id]; + uint16 forcedSpellID = spell.id; + + if (!tar || (spells[spell_id].target_type == ST_Self && tar != this)) { + LogTestDebug("{} set my target to myself for {} [#{}] due to !tar.", GetCleanName(), spell.name, forcedSpellID); //deleteme + tar = this; + } + + if (IsBeneficialSpell(forcedSpellID)) { + if ( + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar)) + ) { + GetBotOwner()->Message( + Chat::Yellow, + fmt::format( + "[{}] is an invalid target. Only players or their pet in your group or raid are eligible targets." + , tar->GetCleanName() + ).c_str() + ); + + return false; + } + } + + if (IsDetrimentalSpell(forcedSpellID) && (!GetBotOwner()->IsAttackAllowed(tar) || !IsAttackAllowed(tar))) { + GetBotOwner()->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I cannot attack [{}]'.", + GetCleanName(), + tar->GetCleanName() + ).c_str() + ); + + return false; + } + + if (!CheckSpellRecastTimer(forcedSpellID)) { + LogTestDebug("{} failed CheckSpellRecastTimer for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + return false; + } + + if ( + !RuleB(Bots, EnableBotTGB) && + IsGroupSpell(forcedSpellID) && + !IsTGBCompatibleSpell(forcedSpellID) && + !IsInGroupOrRaid(tar, true) + ) { + LogTestDebug("{} failed TGB for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + return false; + } + + if (!DoLosChecks(this, tar)) { + LogTestDebug("{} failed LoS for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + return false; + } + + if (!CastChecks(forcedSpellID, tar, UINT16_MAX)) { + LogTestDebug("{} failed CastChecks for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + GetBotOwner()->Message( + Chat::Red, + fmt::format( + "{} says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", + GetBotOwner()->GetCleanName() + ).c_str() + ); + + return false; + } + + if (IsCasting()) { + BotGroupSay( + this, + fmt::format( + "Interrupting {}. I have been commanded to try to cast {} on {}.", + CastingSpellID() ? spells[CastingSpellID()].name : "my spell", + spell.name, + tar->GetCleanName() + ).c_str() + ); + + InterruptSpell(); + } + + if (CastSpell(forcedSpellID, tar->GetID())) { + BotGroupSay( + this, + fmt::format( + "Casting {} on {}.", + GetSpellName(forcedSpellID), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); + + int timer_duration = CalcBuffDuration(tar, this, forcedSpellID); + + if (timer_duration) { // negatives are perma buffs + timer_duration = GetActSpellDuration(forcedSpellID, timer_duration); + } + + if (timer_duration < 0) { + timer_duration = 0; + } + + SetSpellRecastTimer(forcedSpellID, timer_duration); + + return true; + } + + return false; +} + uint16 Bot::GetSpellListSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: diff --git a/zone/bot.h b/zone/bot.h index 3371a6b998..98475e9099 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -403,6 +403,7 @@ class Bot : public NPC { bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); bool AttemptAICastSpell(uint16 spellType); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); + bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; @@ -546,7 +547,6 @@ class Bot : public NPC { void CheckBotSpells(); - [[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; } [[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; } [[nodiscard]] int GetMaxDiscSlots() const final { return EQ::spells::DISC_BUFFS; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 5866bacf41..65d03ad2b6 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -47,29 +47,21 @@ void bot_command_cast(Client* c, const Seperator* sep) }; std::vector examples_two = { - "To tell all Enchanters to slow the target:", + "To tell Skbot to Harm Touch the target:", fmt::format( - "{} {} byclass {}", - sep->arg[0], - Class::Enchanter, - c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) + "{} aa 6000 byname Skbot", + sep->arg[0] ), fmt::format( - "{} {} byclass {}", - sep->arg[0], - Class::Enchanter, - BotSpellTypes::Slow + "{} harmtouch byname Skbot", + sep->arg[0] ) }; std::vector examples_three = { - "To tell Skbot to Harm Touch the target:", - fmt::format( - "{} aa 6000 byname Skbot", - sep->arg[0] - ), + "To tell all bots to try to cast spell #93 (Burst of Flame)", fmt::format( - "{} harmtouch byname Skbot", + "{} spellid 93", sep->arg[0] ) }; @@ -188,8 +180,15 @@ void bot_command_cast(Client* c, const Seperator* sep) uint16 subTargetType = UINT16_MAX; bool aaType = false; int aaID = 0; + bool bySpellID = false; + uint16 chosenSpellID = UINT16_MAX; if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) { + if (!RuleB(Bots, AllowForcedCastsBySpellID)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + if (!arg1.compare("harmtouch")) { aaID = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id; } @@ -208,8 +207,25 @@ void bot_command_cast(Client* c, const Seperator* sep) aaType = true; } - if (!aaType) { - // String/Int type checks + if (!arg1.compare("spellid")) { + if (!RuleB(Bots, AllowCastAAs)) { + c->Message(Chat::Yellow, "This commanded type is currently disabled."); + return; + } + + if (sep->IsNumber(2) && IsValidSpell(atoi(sep->arg[2]))) { + ++ab_arg; + chosenSpellID = atoi(sep->arg[2]); + bySpellID = true; + } + else { + c->Message(Chat::Yellow, "You must enter a valid spell ID."); + + return; + } + } + + if (!aaType && !bySpellID) { if (sep->IsNumber(1)) { spellType = atoi(sep->arg[1]); @@ -342,7 +358,7 @@ void bot_command_cast(Client* c, const Seperator* sep) spellType == BotSpellTypes::PetHoTHeals || spellType == BotSpellTypes::PetRegularHeals || spellType == BotSpellTypes::PetVeryFastHeals - ) { + ) { c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); return; } @@ -352,62 +368,88 @@ void bot_command_cast(Client* c, const Seperator* sep) //LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (!tar) { - if (!aaType && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { + if ((!aaType && !bySpellID) && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { c->Message(Chat::Yellow, "You need a target for that."); return; } } - switch (spellType) { //Target Checks - case BotSpellTypes::Resurrect: - if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { - c->Message(Chat::Yellow, "[%s] is not a player's corpse.", tar->GetCleanName()); + if (!aaType && !bySpellID) { + switch (spellType) { //Target Checks + case BotSpellTypes::Resurrect: + if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] is not a player's corpse.", + tar->GetCleanName() + ).c_str() + ); - return; - } + return; + } - break; - case BotSpellTypes::Identify: - case BotSpellTypes::SendHome: - case BotSpellTypes::BindAffinity: - case BotSpellTypes::SummonCorpse: - if (!tar->IsClient() || !c->IsInGroupOrRaid(tar)) { - c->Message(Chat::Yellow, "[%s] is an invalid target. Only players in your group or raid are eligible targets.", tar->GetCleanName()); + break; + case BotSpellTypes::Identify: + case BotSpellTypes::SendHome: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::SummonCorpse: + if (!tar->IsClient() || !c->IsInGroupOrRaid(tar)) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] is an invalid target. Only players in your group or raid are eligible targets.", + tar->GetCleanName() + ).c_str() + ); - return; - } + return; + } - break; - default: - if ( - (IsBotSpellTypeDetrimental(spellType) && !c->IsAttackAllowed(tar)) || - ( - spellType == BotSpellTypes::Charm && + break; + default: + if ( + (IsBotSpellTypeDetrimental(spellType) && !c->IsAttackAllowed(tar)) || ( - tar->IsClient() || - tar->IsCorpse() || - tar->GetOwner() + spellType == BotSpellTypes::Charm && + ( + tar->IsClient() || + tar->IsCorpse() || + tar->GetOwner() + ) ) - ) - ) { - c->Message(Chat::Yellow, "You cannot attack [%s].", tar->GetCleanName()); - - return; - } - - if (IsBotSpellTypeBeneficial(spellType)) { - if ( - (tar->IsNPC() && !tar->GetOwner()) || - (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) || - (tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) ) { - c->Message(Chat::Yellow, "[%s] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName()); + c->Message( + Chat::Yellow, + fmt::format( + "You cannot attack [{}].", + tar->GetCleanName() + ).c_str() + ); return; } - } - break; + if (IsBotSpellTypeBeneficial(spellType)) { + if ( + (tar->IsNPC() && !tar->GetOwner()) || + (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) || + (tar->IsOfClientBot() && !c->IsInGroupOrRaid(tar)) + ) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] is an invalid target. Only players or their pet in your group or raid are eligible targets.", + tar->GetCleanName() + ).c_str() + ); + + return; + } + } + + break; + } } const int ab_mask = ActionableBots::ABM_Type1; @@ -451,7 +493,7 @@ void bot_command_cast(Client* c, const Seperator* sep) Mob* newTar = tar; - if (!aaType) { + if (!aaType && !bySpellID) { //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { newTar = bot_iter; @@ -502,9 +544,48 @@ void bot_command_cast(Client* c, const Seperator* sep) isSuccess = true; ++successCount; + + continue; + } + else if (bySpellID) { + SPDat_Spell_Struct spell = spells[chosenSpellID]; + + LogTestDebug("Starting bySpellID checks."); //deleteme + if (!bot_iter->HasBotSpellEntry(chosenSpellID)) { + LogTestDebug("{} does not have {} [#{}].", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme + continue; + } + + if (!tar || (spell.target_type == ST_Self && tar != bot_iter)) { + LogTestDebug("{} set my target to myself for {} [#{}] due to !tar.", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme + tar = bot_iter; + } + + if (bot_iter->AttemptForcedCastSpell(tar, chosenSpellID)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + else { + c->Message( + Chat::Red, + fmt::format( + "{} says, '{} [#{}] failed to cast on [{}]. This could be due to this to any number of things: range, mana, immune, etc.'", + bot_iter->GetCleanName(), + spell.name, + chosenSpellID, + tar->GetCleanName() + ).c_str() + ); + } + + continue; } else { - LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme + LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on [{}]'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { @@ -516,34 +597,54 @@ void bot_command_cast(Client* c, const Seperator* sep) ++successCount; } else { - bot_iter->GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", bot_iter->GetCleanName()); - - continue; + c->Message( + Chat::Red, + fmt::format( + "{} says, 'Ability failed to cast [{}]. This could be due to this to any number of things: range, mana, immune, etc.'", + bot_iter->GetCleanName(), + tar->GetCleanName() + ).c_str() + ); } bot_iter->SetCommandedSpell(false); + + continue; } continue; } + std::string type = ""; + + if (aaType) { + type = zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id); + } + else if (bySpellID) { + type = "Forced"; + } + else { + type = c->GetSpellTypeNameByID(spellType); + } + if (!isSuccess) { c->Message( Chat::Yellow, fmt::format( "No bots are capable of casting [{}] on {}.", - (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), + (bySpellID ? spells[chosenSpellID].name : type), tar ? tar->GetCleanName() : "your target" ).c_str() ); } else { - c->Message( Chat::Yellow, + c->Message( + Chat::Yellow, fmt::format( "{} {} [{}]{}", ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), - (!aaType ? c->GetSpellTypeNameByID(spellType) : zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)), + (bySpellID ? spells[chosenSpellID].name : type), tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." ).c_str() ); From 693abdd42cce8caa255f99eefd0f7fdab4905c20 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 07:59:54 -0600 Subject: [PATCH 182/394] ^cast adjustments for spellid casts --- zone/bot.cpp | 8 ++++++++ zone/bot.h | 1 + zone/bot_commands/cast.cpp | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ff8214f1a1..39ba175005 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11184,6 +11184,14 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { tar = this; } + if ((IsCharmSpell(forcedSpellID) || IsPetSpell(forcedSpellID) && HasPet())) { + return false; + } + + if (IsResurrectSpell(forcedSpellID) && (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse())) { + return false; + } + if (IsBeneficialSpell(forcedSpellID)) { if ( (tar->IsNPC() && !tar->GetOwner()) || diff --git a/zone/bot.h b/zone/bot.h index 98475e9099..287721ae94 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -752,6 +752,7 @@ class Bot : public NPC { // "Quest API" Methods bool HasBotSpellEntry(uint16 spell_id); + bool CanUseBotSpell(uint16 spell_id); void ApplySpell(int spell_id, int duration = 0, int level = -1, ApplySpellType apply_type = ApplySpellType::Solo, bool allow_pets = false, bool is_raid_group_only = true); void BreakInvis(); void Escape(); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 65d03ad2b6..afc923118b 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -551,7 +551,7 @@ void bot_command_cast(Client* c, const Seperator* sep) SPDat_Spell_Struct spell = spells[chosenSpellID]; LogTestDebug("Starting bySpellID checks."); //deleteme - if (!bot_iter->HasBotSpellEntry(chosenSpellID)) { + if (!bot_iter->CanUseBotSpell(chosenSpellID)) { LogTestDebug("{} does not have {} [#{}].", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme continue; } From 1aa71598a0ae265f9d9dae7ace4e04ca0f66cb7f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:00:12 -0600 Subject: [PATCH 183/394] Add missing alert round for ranged attacks --- zone/bot.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 39ba175005..b7e41a25ba 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1841,7 +1841,8 @@ bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { ) { if (!Ammo || ammoItem->GetCharges() < 1) { if (!GetCombatRoundForAlerts()) { - GetOwner()->Message(Chat::Yellow, "I do not have enough any ammo."); + SetCombatRoundForAlerts(); + BotGroupSay(this, "I do not have enough any ammo!"); } } From f715924b995cc78cddf5c15e8fcd2d518786614f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:01:15 -0600 Subject: [PATCH 184/394] More castcheck improvements --- zone/bot.cpp | 25 +++++++++------ zone/botspellsai.cpp | 23 +------------- zone/spells.cpp | 76 ++++++++++++++++++++------------------------ 3 files changed, 50 insertions(+), 74 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b7e41a25ba..6fd086f641 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9652,6 +9652,21 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (tar->GetSpecialAbility(SpecialAbility::MagicImmunity)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + + if (tar->IsImmuneToBotSpell(spell_id, this)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9675,16 +9690,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - - if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IMMUNE_CASTING_FROM_RANGE.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if (tar->IsImmuneToBotSpell(spell_id, this)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } if ( (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spell_id) != 0)) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 835e990400..d1eb10ecdd 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -207,7 +207,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; case BotSpellTypes::Charm: - if (tar->IsCharmed() || !tar->IsNPC() || tar->GetSpecialAbility(SpecialAbility::CharmImmunity)) { + if (HasPet() || tar->IsCharmed() || !tar->IsNPC() || tar->GetSpecialAbility(SpecialAbility::CharmImmunity)) { return false; } @@ -596,27 +596,6 @@ bool Bot::AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain } else dist2 = DistanceSquared(m_Position, tar->GetPosition()); - //if ( - // ( - // ( - // ( - // (spells[AIBot_spells[i].spellid].target_type==ST_GroupTeleport && AIBot_spells[i].type == BotSpellTypes::RegularHeal) || - // spells[AIBot_spells[i].spellid].target_type ==ST_AECaster || - // spells[AIBot_spells[i].spellid].target_type ==ST_Group || - // spells[AIBot_spells[i].spellid].target_type ==ST_AEBard || - // ( - // tar == this && spells[AIBot_spells[i].spellid].target_type != ST_TargetsTarget - // ) - // ) && - // dist2 <= spells[AIBot_spells[i].spellid].aoe_range*spells[AIBot_spells[i].spellid].aoe_range - // ) || - // dist2 <= GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range)*GetActSpellRange(AIBot_spells[i].spellid, spells[AIBot_spells[i].spellid].range) - // ) && - // ( - // mana_cost <= GetMana() || - // IsBotNonSpellFighter() - // ) - //) { if (IsValidSpellRange(AIBot_spells[i].spellid, tar) && (mana_cost <= GetMana() || IsBotNonSpellFighter())) { casting_spell_AIindex = i; LogAI("spellid [{}] tar [{}] mana [{}] Name [{}]", AIBot_spells[i].spellid, tar->GetName(), mana_cost, spells[AIBot_spells[i].spellid].name); diff --git a/zone/spells.cpp b/zone/spells.cpp index 9bc775aa3c..bce05ecd15 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -7561,8 +7561,9 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) { int effect_index; - if (caster == nullptr) + if (caster == nullptr) { return(false); + } //TODO: this function loops through the effect list for //this spell like 10 times, this could easily be consolidated @@ -7570,14 +7571,19 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) LogSpells("Checking to see if we are immune to spell [{}] cast by [{}]", spell_id, caster->GetName()); - if (!IsValidSpell(spell_id)) + if (!IsValidSpell(spell_id)) { return true; + } - if (IsBeneficialSpell(spell_id) && (caster->GetNPCTypeID())) //then skip the rest, stop NPCs aggroing each other with buff spells. 2013-03-05 + if (IsDispelSpell(spell_id) && GetSpecialAbility(SpecialAbility::DispellImmunity)) { return false; + } - if (IsMesmerizeSpell(spell_id)) - { + if (IsHarmonySpell(spell_id) && GetSpecialAbility(SpecialAbility::PacifyImmunity)) { + return false; + } + + if (IsMesmerizeSpell(spell_id)) { if (GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { return true; } @@ -7586,90 +7592,76 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) effect_index = GetSpellEffectIndex(spell_id, SE_Mez); assert(effect_index >= 0); // NPCs get to ignore the max level - if ((GetLevel() > spells[spell_id].max_value[effect_index]) && - (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity)))) - { + if ( + (GetLevel() > spells[spell_id].max_value[effect_index]) && + (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))) + ) { return true; } } // slow and haste spells - if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) - { + if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) { return true; } // client vs client fear - if (IsEffectInSpell(spell_id, SE_Fear)) - { + if (IsEffectInSpell(spell_id, SE_Fear)) { effect_index = GetSpellEffectIndex(spell_id, SE_Fear); + if (GetSpecialAbility(SpecialAbility::FearImmunity)) { return true; } - else if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) - { + else if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) { LogSpells("Clients cannot fear eachother!"); caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up return true; } - else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) - { + else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { return true; } - else if (CheckAATimer(aaTimerWarcry)) - { + else if (CheckAATimer(aaTimerWarcry)) { return true; } } - if (IsCharmSpell(spell_id)) - { - if (GetSpecialAbility(SpecialAbility::CharmImmunity)) - { + if (IsCharmSpell(spell_id)) { + if (GetSpecialAbility(SpecialAbility::CharmImmunity)) { return true; } - if (this == caster) - { + if (this == caster) { return true; } //let npcs cast whatever charm on anyone - if (!caster->IsNPC()) - { + if (!caster->IsNPC()) { // check level limit of charm spell effect_index = GetSpellEffectIndex(spell_id, SE_Charm); assert(effect_index >= 0); - if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) - { + if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { return true; } } } - if - ( - IsEffectInSpell(spell_id, SE_Root) || - IsEffectInSpell(spell_id, SE_MovementSpeed) - ) - { + if ( + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) { if (GetSpecialAbility(SpecialAbility::SnareImmunity)) { return true; } } - if (IsLifetapSpell(spell_id)) - { - if (this == caster) - { + if (IsLifetapSpell(spell_id)) { + if (this == caster) { return true; } } - if (IsSacrificeSpell(spell_id)) - { - if (this == caster) - { + if (IsSacrificeSpell(spell_id)) { + if (this == caster) { return true; } } From 83979ed273c52e841494808165e946f17f91112b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 08:01:26 -0600 Subject: [PATCH 185/394] CanUseBotSpell for ^cast --- zone/botspellsai.cpp | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index d1eb10ecdd..e61a407d1b 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2649,6 +2649,30 @@ bool Bot::HasBotSpellEntry(uint16 spell_id) { return false; } +bool Bot::CanUseBotSpell(uint16 spell_id) { + if (AIBot_spells.empty()) { + return false; + } + + for (const auto& s : AIBot_spells) { + if (!IsValidSpell(s.spellid)) { + return false; + } + + if (s.spellid != spell_id) { + continue; + } + + if (s.minlevel > GetLevel()) { + return false; + } + + return true; + } + + return false; +} + bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { if (!IsValidSpell(spell_id) || !tar) { return false; From 6ef9753df35e8f9ca4ffc600c57a18aace556195 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 12:58:25 -0600 Subject: [PATCH 186/394] remove ht/loh from attack ai --- zone/bot.cpp | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 6fd086f641..d6f800f713 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5212,7 +5212,6 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { bool taunt_time = taunt_timer.Check(); bool ca_time = classattack_timer.Check(false); bool ma_time = monkattack_timer.Check(false); - bool ka_time = knightattack_timer.Check(false); if (taunt_time) { @@ -5229,36 +5228,10 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } } - if ((ca_time || ma_time || ka_time) && !IsAttackAllowed(target)) { + if ((ca_time || ma_time) && !IsAttackAllowed(target)) { return; } - if (ka_time) { - - switch (GetClass()) { - case Class::ShadowKnight: { - CastSpell(SPELL_NPC_HARM_TOUCH, target->GetID()); - knightattack_timer.Start(HarmTouchReuseTime * 1000); - - break; - } - case Class::Paladin: { - if (GetHPRatio() < 20) { - CastSpell(SPELL_LAY_ON_HANDS, GetID()); - knightattack_timer.Start(LayOnHandsReuseTime * 1000); - } - else { - knightattack_timer.Start(2000); - } - - break; - } - default: { - break; - } - } - } - if (IsTaunting() && target->IsNPC() && taunt_time) { if (GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { BotGroupSay( From 8094352eaad8a78d0d2c50335a0bb192bc3d11b4 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:35:55 -0600 Subject: [PATCH 187/394] remove SetCombatRoundForAlerts that triggered every engagement --- zone/bot.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index d6f800f713..0582807008 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2887,10 +2887,6 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo // Calculate melee distances CalcMeleeDistances(tar, p_item, s_item, backstab_weapon, behindMob, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); - if (!GetCombatRoundForAlerts()) { - SetCombatRoundForAlerts(); - } - if (tar_distance <= melee_distance) { atCombatRange = true; } From 96bc292ad622f2b95f41122fbc070f75e2591a76 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 14:36:09 -0600 Subject: [PATCH 188/394] Add RangedAttackImmunity checks before trying to ranged attack --- zone/bot.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0582807008..00f8b3775c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2213,7 +2213,7 @@ void Bot::AI_Process() } if (atCombatRange) { - if (RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + if (!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { @@ -2290,7 +2290,7 @@ void Bot::AI_Process() return; } - if (IsBotRanged() && ranged_timer.Check(false)) { + if (!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && IsBotRanged() && ranged_timer.Check(false)) { if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { BotRangedAttack(tar, true); } From f44af609e11d6615c681ecd4282421d622b2567c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 15:33:11 -0600 Subject: [PATCH 189/394] move bot backstab to mob --- zone/attack.cpp | 3 +- zone/bot.cpp | 28 +++++++++---- zone/bot.h | 3 -- zone/special_attacks.cpp | 85 ++++++++++++++++++++++++++++++---------- 4 files changed, 88 insertions(+), 31 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 0d8e420ca1..e5b9baa844 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -6446,8 +6446,9 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac } else { int ass = TryAssassinate(defender, hit.skill); - if (ass > 0) + if (ass > 0) { hit.damage_done = ass; + } } } else if (hit.skill == EQ::skills::SkillFrenzy && GetClass() == Class::Berserker && GetLevel() > 50) { diff --git a/zone/bot.cpp b/zone/bot.cpp index 00f8b3775c..a35ee3bc4a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5372,18 +5372,32 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } if (skill_to_use == EQ::skills::SkillFrenzy) { - int AtkRounds = 3; + int AtkRounds = 1; + float HasteMod = (FrenzyReuseTime - 1) / (GetHaste() * 0.01f); + reuse = (FrenzyReuseTime * 1000); DoAnim(anim2HSlashing); - reuse = (FrenzyReuseTime * 1000); - did_attack = true; - while(AtkRounds > 0) { - if (GetTarget() && (AtkRounds == 1 || zone->random.Int(0, 100) < 75)) { - DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, reuse, true); - } + // bards can do riposte frenzy for some reason + if (!IsRiposte && GetClass() == Class::Berserker) { + int chance = GetLevel() * 2 + GetSkill(EQ::skills::SkillFrenzy); + if (zone->random.Roll0(450) < chance) + AtkRounds++; + if (zone->random.Roll0(450) < chance) + AtkRounds++; + } + + while (AtkRounds > 0) { + if (GetTarget() != this) + DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, HasteMod); AtkRounds--; } + + if (reuse > 0 && IsRiposte) { + reuse = 0; + } + + did_attack = true; } if (skill_to_use == EQ::skills::SkillKick) { diff --git a/zone/bot.h b/zone/bot.h index 287721ae94..457f7eebf1 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -249,9 +249,6 @@ class Bot : public NPC { inline uint16 MaxSkill(EQ::skills::SkillType skillid) { return MaxSkill(skillid, GetClass(), GetLevel()); } int GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target = nullptr) override; void DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 max_damage, int32 min_damage = 1, int32 hate_override = -1, int ReuseTime = 10, bool HitChance = false); - void TryBackstab(Mob *other,int ReuseTime = 10) override; - void RogueBackstab(Mob* other, bool min_damage = false, int ReuseTime = 10) override; - void RogueAssassinate(Mob* other) override; void DoClassAttacks(Mob *target, bool IsRiposte=false); void CalcBonuses() override; diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 5e9c6f7920..cd8b8ac912 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -709,54 +709,78 @@ int Mob::MonkSpecialAttack(Mob *other, uint8 unchecked_type) } void Mob::TryBackstab(Mob *other, int ReuseTime) { - if(!other) + if (!other) { return; + } bool bIsBehind = false; bool bCanFrontalBS = false; //make sure we have a proper weapon if we are a client. - if(IsClient()) { + if (IsClient()) { const EQ::ItemInstance *wpn = CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); + if (!wpn || (wpn->GetItem()->ItemType != EQ::item::ItemType1HPiercing)){ MessageString(Chat::Red, BACKSTAB_WEAPON); return; } } + else if (IsBot()) { + const EQ::ItemInstance* inst = CastToBot()->GetBotItem(EQ::invslot::slotPrimary); + const EQ::ItemData* botpiercer = nullptr; + + if (inst) { + botpiercer = inst->GetItem(); + } + + if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { + if (!CastToBot()->GetCombatRoundForAlerts()) { + CastToBot()->SetCombatRoundForAlerts(); + CastToBot()->BotGroupSay(this, "I can't backstab with this weapon!"); + } + + return; + } + } //Live AA - Triple Backstab int tripleChance = itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab; - if (BehindMob(other, GetX(), GetY())) + if (BehindMob(other, GetX(), GetY())) { bIsBehind = true; - + } else { //Live AA - Seized Opportunity int FrontalBSChance = itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance; - if (FrontalBSChance && zone->random.Roll(FrontalBSChance)) + if (FrontalBSChance && zone->random.Roll(FrontalBSChance)) { bCanFrontalBS = true; + } } if (bIsBehind || bCanFrontalBS || (IsNPC() && CanFacestab())) { // Player is behind other OR can do Frontal Backstab - if (bCanFrontalBS && IsClient()) // I don't think there is any message ... - CastToClient()->Message(Chat::White,"Your fierce attack is executed with such grace, your target did not see it coming!"); + if (bCanFrontalBS && IsClient()) { // I don't think there is any message ... + CastToClient()->Message(Chat::White, "Your fierce attack is executed with such grace, your target did not see it coming!"); + } RogueBackstab(other,false,ReuseTime); + if (level >= RuleI(Combat, DoubleBackstabLevelRequirement)) { // TODO: 55-59 doesn't appear to match just checking double attack, 60+ does though - if(IsClient() && CastToClient()->CheckDoubleAttack()) - { - if(other->GetHP() > 0) - RogueBackstab(other,false,ReuseTime); + if(IsOfClientBot() && CastToClient()->CheckDoubleAttack()) { + if (other->GetHP() > 0) { + RogueBackstab(other, false, ReuseTime); + } - if (tripleChance && other->GetHP() > 0 && zone->random.Roll(tripleChance)) - RogueBackstab(other,false,ReuseTime); + if (tripleChance && other->GetHP() > 0 && zone->random.Roll(tripleChance)) { + RogueBackstab(other, false, ReuseTime); + } } } - if(IsClient()) + if (IsClient()) { CastToClient()->CheckIncreaseSkill(EQ::skills::SkillBackstab, other, 10); + } } //Live AA - Chaotic Backstab @@ -766,14 +790,20 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { //we can stab from any angle, we do min damage though. // chaotic backstab can't double etc Seized can, but that's because it's a chance to do normal BS // Live actually added SPA 473 which grants chance to double here when they revamped chaotic/seized + RogueBackstab(other, true, ReuseTime); - if(IsClient()) + + if (IsClient()) { CastToClient()->CheckIncreaseSkill(EQ::skills::SkillBackstab, other, 10); + } + m_specialattacks = eSpecialAttacks::None; int double_bs_front = aabonuses.Double_Backstab_Front + itembonuses.Double_Backstab_Front + spellbonuses.Double_Backstab_Front; - if (double_bs_front && other->GetHP() > 0 && zone->random.Roll(double_bs_front)) + + if (double_bs_front && other->GetHP() > 0 && zone->random.Roll(double_bs_front)) { RogueBackstab(other, false, ReuseTime); + } } else { //We do a single regular attack if we attack from the front without chaotic stab Attack(other, EQ::invslot::slotPrimary); @@ -790,10 +820,25 @@ void Mob::RogueBackstab(Mob* other, bool min_damage, int ReuseTime) // make sure we can hit (bane, magical, etc) if (IsClient()) { - const EQ::ItemInstance *wpn = CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); - if (!GetWeaponDamage(other, wpn)) + const EQ::ItemInstance* wpn = CastToClient()->GetInv().GetItem(EQ::invslot::slotPrimary); + + if (!GetWeaponDamage(other, wpn)) { + return; + } + } + else if (IsBot()) { + EQ::ItemInstance* botweaponInst = CastToBot()->GetBotItem(EQ::invslot::slotPrimary); + + if (botweaponInst) { + if (!GetWeaponDamage(other, botweaponInst)) { + return; + } + } + else if (!GetWeaponDamage(other, (const EQ::ItemData*)nullptr)) { return; - } else if (!GetWeaponDamage(other, (const EQ::ItemData*)nullptr)){ + } + } + else if (!GetWeaponDamage(other, (const EQ::ItemData*)nullptr)) { return; } @@ -2454,7 +2499,7 @@ int Mob::TryAssassinate(Mob *defender, EQ::skills::SkillType skillInUse) int chance = GetDEX(); if (skillInUse == EQ::skills::SkillBackstab) { chance = 100 * chance / (chance + 3500); - if (IsClient() || IsBot()) { + if (IsOfClientBot()) { chance += GetHeroicDEX(); } chance *= 10; From adedd5969d832e351691fb9227da5e8445327d9a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 16:40:22 -0600 Subject: [PATCH 190/394] fix MinStatusToBypassCreateLimit --- zone/client_bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index bfe4cdb58f..9d796cef10 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -20,7 +20,7 @@ uint32 Client::GetBotCreationLimit(uint8 class_id) uint32 bot_creation_limit = RuleI(Bots, CreationLimit); if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { - return RuleI(Bots, MinStatusToBypassCreateLimit); + return RuleI(Bots, StatusCreateLimit); } const auto bucket_name = fmt::format( From 94717ee459f7d36121515d450b72fdb2bb9fcb0a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:06:54 -0600 Subject: [PATCH 191/394] more backstab to mob cleanup --- zone/bot.cpp | 98 +++------------------------------------------------- 1 file changed, 5 insertions(+), 93 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a35ee3bc4a..e04a4d8210 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5110,96 +5110,6 @@ void Bot::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 max TrySkillProc(who, skill, (ReuseTime * 1000), true); } -void Bot::TryBackstab(Mob *other, int ReuseTime) { - if (!other) - return; - - bool bIsBehind = false; - bool bCanFrontalBS = false; - const EQ::ItemInstance* inst = GetBotItem(EQ::invslot::slotPrimary); - const EQ::ItemData* botpiercer = nullptr; - if (inst) - botpiercer = inst->GetItem(); - - if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { - if (!GetCombatRoundForAlerts()) { - SetCombatRoundForAlerts(); - BotGroupSay(this, "I can't backstab with this weapon!"); - } - - return; - } - - int tripleChance = (itembonuses.TripleBackstab + spellbonuses.TripleBackstab + aabonuses.TripleBackstab); - if (BehindMob(other, GetX(), GetY())) - bIsBehind = true; - else { - int FrontalBSChance = (itembonuses.FrontalBackstabChance + spellbonuses.FrontalBackstabChance + aabonuses.FrontalBackstabChance); - if (FrontalBSChance && (FrontalBSChance > zone->random.Int(0, 100))) - bCanFrontalBS = true; - } - - if (bIsBehind || bCanFrontalBS) { - int chance = (10 + (GetDEX() / 10) + (itembonuses.HeroicDEX / 10)); - if (level >= 60 && other->GetLevel() <= 45 && !other->CastToNPC()->IsEngaged() && other->GetHP()<= 32000 && other->IsNPC() && zone->random.Real(0, 99) < chance) { - entity_list.MessageCloseString(this, false, 200, Chat::MeleeCrit, ASSASSINATES, GetName()); - RogueAssassinate(other); - } else { - RogueBackstab(other); - if (level > 54) { - float DoubleAttackProbability = ((GetSkill(EQ::skills::SkillDoubleAttack) + GetLevel()) / 500.0f); - if (zone->random.Real(0, 1) < DoubleAttackProbability) { - if (other->GetHP() > 0) - RogueBackstab(other,false,ReuseTime); - - if (tripleChance && other->GetHP() > 0 && tripleChance > zone->random.Int(0, 100)) - RogueBackstab(other,false,ReuseTime); - } - } - } - } else if (aabonuses.FrontalBackstabMinDmg || itembonuses.FrontalBackstabMinDmg || spellbonuses.FrontalBackstabMinDmg) { - m_specialattacks = eSpecialAttacks::ChaoticStab; - RogueBackstab(other, true); - m_specialattacks = eSpecialAttacks::None; - } - else - Attack(other, EQ::invslot::slotPrimary); -} - -void Bot::RogueBackstab(Mob *other, bool min_damage, int ReuseTime) -{ - if (!other) - return; - - EQ::ItemInstance *botweaponInst = GetBotItem(EQ::invslot::slotPrimary); - if (botweaponInst) { - if (!GetWeaponDamage(other, botweaponInst)) - return; - } else if (!GetWeaponDamage(other, (const EQ::ItemData *)nullptr)) { - return; - } - - int64 hate = 0; - - int base_damage = GetBaseSkillDamage(EQ::skills::SkillBackstab, other); - hate = base_damage; - - DoSpecialAttackDamage(other, EQ::skills::SkillBackstab, base_damage, 0, hate, ReuseTime); - DoAnim(anim1HPiercing); -} - -void Bot::RogueAssassinate(Mob* other) { - EQ::ItemInstance* botweaponInst = GetBotItem(EQ::invslot::slotPrimary); - if (botweaponInst) { - if (GetWeaponDamage(other, botweaponInst)) - other->Damage(this, 32000, SPELL_UNKNOWN, EQ::skills::SkillBackstab); - else - other->Damage(this, -5, SPELL_UNKNOWN, EQ::skills::SkillBackstab); - } - - DoAnim(anim1HPiercing); -} - void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if (!target || GetAppearance() == eaDead || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) { return; @@ -5388,7 +5298,6 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { while (AtkRounds > 0) { if (GetTarget() != this) - DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, HasteMod); AtkRounds--; } @@ -5468,11 +5377,14 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if (skill_to_use == EQ::skills::SkillBackstab) { reuse = (BackstabReuseTime * 1000); did_attack = true; - if (IsRiposte) + + if (IsRiposte) { reuse = 0; + } - TryBackstab(target,reuse); + Mob::TryBackstab(target, reuse); } + classattack_timer.Start(reuse / HasteModifier); } From 5457d14d85f408bc848ba3c7a70447153bf8644f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:07:54 -0600 Subject: [PATCH 192/394] add bot checks to tryheadshot / tryassassinate --- zone/attack.cpp | 1 + zone/special_attacks.cpp | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index e5b9baa844..ceca940b70 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -6446,6 +6446,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac } else { int ass = TryAssassinate(defender, hit.skill); + if (ass > 0) { hit.damage_done = ass; } diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index cd8b8ac912..e7c5350be5 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -2453,7 +2453,7 @@ int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) // Only works on YOUR target. if ( defender && - !defender->IsClient() && + !defender->IsOfClientBot() && skillInUse == EQ::skills::SkillArchery && GetTarget() == defender && (defender->GetBodyType() == BodyType::Humanoid || !RuleB(Combat, HeadshotOnlyHumanoids)) && @@ -2466,7 +2466,7 @@ int Mob::TryHeadShot(Mob *defender, EQ::skills::SkillType skillInUse) if (HeadShot_Dmg && HeadShot_Level && (defender->GetLevel() <= HeadShot_Level)) { int chance = GetDEX(); chance = 100 * chance / (chance + 3500); - if (IsClient() || IsBot()) { + if (IsOfClientBot()) { chance += GetHeroicDEX() / 25; } chance *= 10; @@ -2490,7 +2490,7 @@ int Mob::TryAssassinate(Mob *defender, EQ::skills::SkillType skillInUse) { if ( defender && - !defender->IsClient() && + !defender->IsOfClientBot() && GetLevel() >= RuleI(Combat, AssassinateLevelRequirement) && (skillInUse == EQ::skills::SkillBackstab || skillInUse == EQ::skills::SkillThrowing) && (defender->GetBodyType() == BodyType::Humanoid || !RuleB(Combat, AssassinateOnlyHumanoids)) && From 046006cfaa0d1f7d9148cb0b995472090330f4d1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:16:23 -0600 Subject: [PATCH 193/394] adjust version number for bots --- common/version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index 232d3afc5f..d115f814bd 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9286 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9045 +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9051 //TODO bot rewrite #endif From cac5bcb9431db46f84b221ff66bac3fc0aedc594 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:19:07 -0600 Subject: [PATCH 194/394] add back m_mob_check_moving_timer, necessary? --- zone/bot.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index e04a4d8210..393e6cf8fb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1617,6 +1617,10 @@ bool Bot::Process() entity_list.ScanCloseMobs(this); } + if (m_mob_check_moving_timer.Check()) { //TODO bot rewrite - is this necessary + CheckScanCloseMobsMovingTimer(); + } + SpellProcess(); if (tic_timer.Check()) { From 1905debb140baf8b89a34cc574c29188f821d5ab Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:45:52 -0600 Subject: [PATCH 195/394] add sanity checks for classattacks --- zone/bot.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 393e6cf8fb..0ea24cef5f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5197,7 +5197,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } auto classic = RuleB(Combat, ClassicMasterWu); - while (extra) { + while (extra && TargetValidation(GetTarget())) { MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : EQ::skills::SkillTigerClaw)); --extra; } @@ -5301,8 +5301,9 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } while (AtkRounds > 0) { - if (GetTarget() != this) + if (GetTarget() != this && TargetValidation(GetTarget())) { DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, HasteMod); + } AtkRounds--; } @@ -5368,7 +5369,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { } auto classic = RuleB(Combat, ClassicMasterWu); - while (extra) { + while (extra && TargetValidation(GetTarget())) { MonkSpecialAttack(GetTarget(), (classic ? MonkSPA[zone->random.Int(0, 4)] : skill_to_use)); --extra; } From 3009942ca3aba763d88f41dbbe8d2565c5c403fb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:48:59 -0600 Subject: [PATCH 196/394] Get rid of Bots:BotGroupXP and change logic to support Bots:SameRaidGroupForXP Bots won't do anything if not in the same group so this should more accurately control only when in the same raid group. --- common/ruletypes.h | 2 +- zone/attack.cpp | 20 +++++--------------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 57721efa78..832878b403 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -763,7 +763,7 @@ RULE_REAL(Bots, ManaRegen, 2.0, "Adjust mana regen. Acts as a final multiplier, RULE_BOOL(Bots, PreferNoManaCommandSpells, true, "Give sorting priority to newer no-mana spells (i.e., 'Bind Affinity')") RULE_BOOL(Bots, QuestableSpawnLimit, false, "Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl") RULE_INT(Bots, SpawnLimit, 71, "Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid") -RULE_BOOL(Bots, BotGroupXP, false, "Determines whether client gets experience for bots outside their group") +RULE_BOOL(Bots, SameRaidGroupForXP, false, "Whether or not bots are required to be in the same raid group to give exp. Default false") 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, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks") diff --git a/zone/attack.cpp b/zone/attack.cpp index ceca940b70..d1c862096b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2624,27 +2624,17 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy bool owner_in_group = false; if ( - ( - give_exp->HasGroup() && - give_exp->GetGroup()->IsGroupMember(give_exp->GetUltimateOwner()) - ) || - ( - give_exp->IsPet() && - ( - give_exp->GetOwner()->IsClient() || - ( - give_exp->GetOwner()->HasGroup() && - give_exp->GetOwner()->GetGroup()->IsGroupMember(give_exp->GetOwner()->GetUltimateOwner()) - ) - ) - ) + give_exp->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) || + give_exp->IsPet() && + give_exp->GetOwner() && + give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) ) { owner_in_group = true; } give_exp = give_exp->GetUltimateOwner(); - if (!RuleB(Bots, BotGroupXP) && !owner_in_group) { + if (!owner_in_group) { give_exp = nullptr; } } From 1c72d879cfd84335e1539e49619ba50b1a8d589d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:58:54 -0600 Subject: [PATCH 197/394] add "confirm" check to ^delete --- zone/bot_commands/bot.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index b91d5a0a78..78921d9a05 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -453,6 +453,15 @@ void bot_command_delete(Client *c, const Seperator *sep) return; } + std::string delete_confirm = sep->arg[1]; + + std::string deleted_check = "confirm"; + + if (!(delete_confirm.find(deleted_check) != std::string::npos)) { + c->Message(Chat::White, "You must type %s confirm to confirm the deletion of %s.", sep->arg[0], my_bot->GetCleanName()); + return; + } + if (!my_bot->DeleteBot()) { c->Message(Chat::White, "Failed to delete '%s' due to database error", my_bot->GetCleanName()); return; From 606cc110409f02ff6ce01cb649b141ffb7d50af8 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 13 Dec 2024 15:39:35 -0600 Subject: [PATCH 198/394] Update bot.cpp --- zone/bot.cpp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b632accf4d..d791020ad1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2086,13 +2086,6 @@ void Bot::AI_Process() } } - SetFollowID(follow_mob->GetID()); - - if (!follow_mob || !IsInGroupOrRaid(follow_mob)) { - follow_mob = leash_owner; - } - } - SetFollowID(follow_mob->GetID()); SetBerserkState(); @@ -2340,11 +2333,6 @@ void Bot::AI_Process() } } - if (!TargetValidation(tar)) { return; } - - } - } - // ENGAGED NOT AT COMBAT RANGE else if (!TryPursueTarget(leash_distance, Goal)) { From d144d58abc942af69ca06d7e33fbd4a50776c582 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 15 Dec 2024 23:08:23 -0600 Subject: [PATCH 199/394] Remove `id` from bot_settings, correct types --- .../database_update_manifest_bots.cpp | 37 +++--- .../base/base_bot_settings_repository.h | 110 ++++++++---------- 2 files changed, 68 insertions(+), 79 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index bd35d776ff..fb21a4b677 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -171,29 +171,30 @@ CHANGE COLUMN `spellid` `spell_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER .match = "", .sql = R"( CREATE TABLE `bot_settings` ( - `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, - `char_id` INT UNSIGNED NOT NULL, - `bot_id` INT UNSIGNED NOT NULL, - `stance` INT UNSIGNED NOT NULL, - `setting_id` INT UNSIGNED NOT NULL, - `setting_type` INT UNSIGNED NOT NULL, - `value` INT UNSIGNED NOT NULL, - `category_name` VARCHAR(64) NULL DEFAULT '', - `setting_name` VARCHAR(64) NULL DEFAULT '', - PRIMARY KEY (`id`) USING BTREE + `char_id` INT(10) UNSIGNED NOT NULL, + `bot_id` INT(10) UNSIGNED NOT NULL, + `stance` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `setting_id` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0', + `setting_type` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', + `value` INT(10) UNSIGNED NOT NULL, + `category_name` VARCHAR(64) NULL DEFAULT '' COLLATE 'utf8mb4_general_ci', + `setting_name` VARCHAR(64) NULL DEFAULT '' COLLATE 'utf8mb4_general_ci', + PRIMARY KEY (`char_id`, `bot_id`, `stance`, `setting_id`, `setting_type`) USING BTREE ) -COLLATE='utf8mb4_general_ci'; +COLLATE='utf8mb4_general_ci' +ENGINE=InnoDB +; -INSERT INTO bot_settings SELECT NULL, 0, bd.`bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd +INSERT INTO bot_settings SELECT 0, bd.`bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 0, 0, bd.`expansion_bitmask`, 'BaseSetting', 'ExpansionBitmask' FROM bot_data bd JOIN rule_values rv WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' AND bd.expansion_bitmask != rv.rule_value; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' +SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' FROM ( SELECT `bot_id`, (CASE @@ -205,11 +206,11 @@ FROM ( ) AS `subquery` WHERE `sml` != `stop_melee_level`; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; -INSERT INTO bot_settings SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; INSERT INTO bot_settings -SELECT NULL, 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'DistanceRanged' +SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'DistanceRanged' FROM ( SELECT `bot_id`, (CASE diff --git a/common/repositories/base/base_bot_settings_repository.h b/common/repositories/base/base_bot_settings_repository.h index 92d4aba15f..7595a49333 100644 --- a/common/repositories/base/base_bot_settings_repository.h +++ b/common/repositories/base/base_bot_settings_repository.h @@ -19,26 +19,24 @@ class BaseBotSettingsRepository { public: struct BotSettings { - uint32_t id; - uint32_t char_id; - uint32_t bot_id; - uint8_t stance; - uint16_t setting_id; - uint8_t setting_type; - int32_t value; + uint32_t char_id; + uint32_t bot_id; + uint8_t stance; + uint16_t setting_id; + uint8_t setting_type; + int32_t value; std::string category_name; std::string setting_name; }; static std::string PrimaryKey() { - return std::string("id"); + return std::string("char_id"); } static std::vector Columns() { return { - "id", "char_id", "bot_id", "stance", @@ -53,7 +51,6 @@ class BaseBotSettingsRepository { static std::vector SelectColumns() { return { - "id", "char_id", "bot_id", "stance", @@ -102,15 +99,14 @@ class BaseBotSettingsRepository { { BotSettings e{}; - e.id = 0; - e.char_id = 0; - e.bot_id = 0; - e.stance = 0; - e.setting_id = 0; - e.setting_type = 0; - e.value = 0; + e.char_id = 0; + e.bot_id = 0; + e.stance = 0; + e.setting_id = 0; + e.setting_type = 0; + e.value = 0; e.category_name = ""; - e.setting_name = ""; + e.setting_name = ""; return e; } @@ -121,7 +117,7 @@ class BaseBotSettingsRepository { ) { for (auto &bot_settings : bot_settingss) { - if (bot_settings.id == bot_settings_id) { + if (bot_settings.char_id == bot_settings_id) { return bot_settings; } } @@ -147,15 +143,14 @@ class BaseBotSettingsRepository { if (results.RowCount() == 1) { BotSettings e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.category_name = row[7] ? row[7] : ""; - e.setting_name = row[8] ? row[8] : ""; + e.char_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.bot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.stance = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(atoi(row[5])) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; return e; } @@ -189,15 +184,14 @@ class BaseBotSettingsRepository { auto columns = Columns(); - v.push_back(columns[0] + " = " + std::to_string(e.id)); - v.push_back(columns[1] + " = " + std::to_string(e.char_id)); - v.push_back(columns[2] + " = " + std::to_string(e.bot_id)); - v.push_back(columns[3] + " = " + std::to_string(e.stance)); - v.push_back(columns[4] + " = " + std::to_string(e.setting_id)); - v.push_back(columns[5] + " = " + std::to_string(e.setting_type)); - v.push_back(columns[6] + " = " + std::to_string(e.value)); - v.push_back(columns[7] + " = '" + Strings::Escape(e.category_name) + "'"); - v.push_back(columns[8] + " = '" + Strings::Escape(e.setting_name) + "'"); + v.push_back(columns[0] + " = " + std::to_string(e.char_id)); + v.push_back(columns[1] + " = " + std::to_string(e.bot_id)); + v.push_back(columns[2] + " = " + std::to_string(e.stance)); + v.push_back(columns[3] + " = " + std::to_string(e.setting_id)); + v.push_back(columns[4] + " = " + std::to_string(e.setting_type)); + v.push_back(columns[5] + " = " + std::to_string(e.value)); + v.push_back(columns[6] + " = '" + Strings::Escape(e.category_name) + "'"); + v.push_back(columns[7] + " = '" + Strings::Escape(e.setting_name) + "'"); auto results = db.QueryDatabase( fmt::format( @@ -205,7 +199,7 @@ class BaseBotSettingsRepository { TableName(), Strings::Implode(", ", v), PrimaryKey(), - e.bot_id + e.char_id ) ); @@ -219,7 +213,6 @@ class BaseBotSettingsRepository { { std::vector v; - v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); @@ -238,7 +231,7 @@ class BaseBotSettingsRepository { ); if (results.Success()) { - e.id = results.LastInsertedID(); + e.char_id = results.LastInsertedID(); return e; } @@ -257,7 +250,6 @@ class BaseBotSettingsRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); @@ -299,15 +291,14 @@ class BaseBotSettingsRepository { for (auto row = results.begin(); row != results.end(); ++row) { BotSettings e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.category_name = row[7] ? row[7] : ""; - e.setting_name = row[8] ? row[8] : ""; + e.char_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.bot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.stance = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(atoi(row[5])) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; all_entries.push_back(e); } @@ -332,15 +323,14 @@ class BaseBotSettingsRepository { for (auto row = results.begin(); row != results.end(); ++row) { BotSettings e{}; - e.id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; - e.char_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; - e.bot_id = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; - e.stance = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; - e.setting_id = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; - e.setting_type = row[5] ? static_cast(strtoul(row[5], nullptr, 10)) : 0; - e.value = row[6] ? static_cast(strtoul(row[6], nullptr, 10)) : 0; - e.category_name = row[7] ? row[7] : ""; - e.setting_name = row[8] ? row[8] : ""; + e.char_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.bot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; + e.stance = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; + e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; + e.setting_type = row[4] ? static_cast(strtoul(row[4], nullptr, 10)) : 0; + e.value = row[5] ? static_cast(atoi(row[5])) : 0; + e.category_name = row[6] ? row[6] : ""; + e.setting_name = row[7] ? row[7] : ""; all_entries.push_back(e); } @@ -415,7 +405,6 @@ class BaseBotSettingsRepository { { std::vector v; - v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); @@ -446,7 +435,6 @@ class BaseBotSettingsRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.id)); v.push_back(std::to_string(e.char_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); From e2201631531f2c7f89c74a228e8e20d93cfd324a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 15 Dec 2024 23:09:32 -0600 Subject: [PATCH 200/394] Implement blocked_buffs and blocked_pet_buffs --- .../database_update_manifest_bots.cpp | 19 + .../base/base_bot_blocked_buffs_repository.h | 416 ++++++++++++++ .../bot_blocked_buffs_repository.h | 50 ++ common/ruletypes.h | 1 + common/version.h | 2 +- zone/bot.cpp | 189 ++++++- zone/bot.h | 9 + zone/bot_command.cpp | 3 + zone/bot_command.h | 2 + zone/bot_commands/blocked_buffs.cpp | 518 ++++++++++++++++++ zone/bot_commands/cast.cpp | 2 +- zone/bot_database.cpp | 108 ++++ zone/bot_database.h | 3 + zone/bot_structs.h | 7 + zone/spells.cpp | 5 +- 15 files changed, 1323 insertions(+), 11 deletions(-) create mode 100644 common/repositories/base/base_bot_blocked_buffs_repository.h create mode 100644 common/repositories/bot_blocked_buffs_repository.h create mode 100644 zone/bot_commands/blocked_buffs.cpp diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index fb21a4b677..1b9b2e439e 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -1178,6 +1178,25 @@ WHERE NOT EXISTS (SELECT * FROM spells_new WHERE bot_spells_entries.spell_id = spells_new.id); +)" + }, +ManifestEntry{ + .version = 9052, + .description = "2024_12_15_bot_blocked_buffs.sql", + .check = "SHOW TABLES LIKE 'bot_blocked_buffs'", + .condition = "empty", + .match = "", + .sql = R"( +CREATE TABLE `bot_blocked_buffs` ( + `bot_id` INT(11) UNSIGNED NOT NULL, + `spell_id` INT(11) UNSIGNED NOT NULL, + `blocked` TINYINT(4) UNSIGNED NULL DEFAULT '0', + `blocked_pet` TINYINT(4) UNSIGNED NULL DEFAULT '0', + PRIMARY KEY (`bot_id`, `spell_id`) USING BTREE +) +COLLATE='latin1_swedish_ci' +ENGINE=InnoDB +; )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/repositories/base/base_bot_blocked_buffs_repository.h b/common/repositories/base/base_bot_blocked_buffs_repository.h new file mode 100644 index 0000000000..77b0ee2cdf --- /dev/null +++ b/common/repositories/base/base_bot_blocked_buffs_repository.h @@ -0,0 +1,416 @@ +/** + * DO NOT MODIFY THIS FILE + * + * This repository was automatically generated and is NOT to be modified directly. + * Any repository modifications are meant to be made to the repository extending the base. + * Any modifications to base repositories are to be made by the generator only + * + * @generator ./utils/scripts/generators/repository-generator.pl + * @docs https://docs.eqemu.io/developer/repositories + */ + +#ifndef EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H +#define EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H + +#include "../../database.h" +#include "../../strings.h" +#include + +class BaseBotBlockedBuffsRepository { +public: + struct BotBlockedBuffs { + uint32_t bot_id; + uint32_t spell_id; + uint8_t blocked; + uint8_t blocked_pet; + }; + + static std::string PrimaryKey() + { + return std::string("bot_id"); + } + + static std::vector Columns() + { + return { + "bot_id", + "spell_id", + "blocked", + "blocked_pet", + }; + } + + static std::vector SelectColumns() + { + return { + "bot_id", + "spell_id", + "blocked", + "blocked_pet", + }; + } + + static std::string ColumnsRaw() + { + return std::string(Strings::Implode(", ", Columns())); + } + + static std::string SelectColumnsRaw() + { + return std::string(Strings::Implode(", ", SelectColumns())); + } + + static std::string TableName() + { + return std::string("bot_blocked_buffs"); + } + + static std::string BaseSelect() + { + return fmt::format( + "SELECT {} FROM {}", + SelectColumnsRaw(), + TableName() + ); + } + + static std::string BaseInsert() + { + return fmt::format( + "INSERT INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static BotBlockedBuffs NewEntity() + { + BotBlockedBuffs e{}; + + e.bot_id = 0; + e.spell_id = 0; + e.blocked = 0; + e.blocked_pet = 0; + + return e; + } + + static BotBlockedBuffs GetBotBlockedBuffs( + const std::vector &bot_blocked_buffss, + int bot_blocked_buffs_id + ) + { + for (auto &bot_blocked_buffs : bot_blocked_buffss) { + if (bot_blocked_buffs.bot_id == bot_blocked_buffs_id) { + return bot_blocked_buffs; + } + } + + return NewEntity(); + } + + static BotBlockedBuffs FindOne( + Database& db, + int bot_blocked_buffs_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {} = {} LIMIT 1", + BaseSelect(), + PrimaryKey(), + bot_blocked_buffs_id + ) + ); + + auto row = results.begin(); + if (results.RowCount() == 1) { + BotBlockedBuffs e{}; + + e.bot_id = row[0] ? static_cast(atoi(row[0])) : 0; + e.spell_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.blocked = row[2] ? static_cast(atoi(row[2])) : 0; + e.blocked_pet = row[3] ? static_cast(atoi(row[3])) : 0; + + return e; + } + + return NewEntity(); + } + + static int DeleteOne( + Database& db, + int bot_blocked_buffs_id + ) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {} = {}", + TableName(), + PrimaryKey(), + bot_blocked_buffs_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int UpdateOne( + Database& db, + const BotBlockedBuffs &e + ) + { + std::vector v; + + auto columns = Columns(); + + v.push_back(columns[0] + " = " + std::to_string(e.bot_id)); + v.push_back(columns[1] + " = " + std::to_string(e.spell_id)); + v.push_back(columns[2] + " = " + std::to_string(e.blocked)); + v.push_back(columns[3] + " = " + std::to_string(e.blocked_pet)); + + auto results = db.QueryDatabase( + fmt::format( + "UPDATE {} SET {} WHERE {} = {}", + TableName(), + Strings::Implode(", ", v), + PrimaryKey(), + e.bot_id + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static BotBlockedBuffs InsertOne( + Database& db, + BotBlockedBuffs e + ) + { + std::vector v; + + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.spell_id)); + v.push_back(std::to_string(e.blocked)); + v.push_back(std::to_string(e.blocked_pet)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseInsert(), + Strings::Implode(",", v) + ) + ); + + if (results.Success()) { + e.bot_id = results.LastInsertedID(); + return e; + } + + e = NewEntity(); + + return e; + } + + static int InsertMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.spell_id)); + v.push_back(std::to_string(e.blocked)); + v.push_back(std::to_string(e.blocked_pet)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseInsert(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static std::vector All(Database& db) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{}", + BaseSelect() + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + BotBlockedBuffs e{}; + + e.bot_id = row[0] ? static_cast(atoi(row[0])) : 0; + e.spell_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.blocked = row[2] ? static_cast(atoi(row[2])) : 0; + e.blocked_pet = row[3] ? static_cast(atoi(row[3])) : 0; + + all_entries.push_back(e); + } + + return all_entries; + } + + static std::vector GetWhere(Database& db, const std::string &where_filter) + { + std::vector all_entries; + + auto results = db.QueryDatabase( + fmt::format( + "{} WHERE {}", + BaseSelect(), + where_filter + ) + ); + + all_entries.reserve(results.RowCount()); + + for (auto row = results.begin(); row != results.end(); ++row) { + BotBlockedBuffs e{}; + + e.bot_id = row[0] ? static_cast(atoi(row[0])) : 0; + e.spell_id = row[1] ? static_cast(atoi(row[1])) : 0; + e.blocked = row[2] ? static_cast(atoi(row[2])) : 0; + e.blocked_pet = row[3] ? static_cast(atoi(row[3])) : 0; + + all_entries.push_back(e); + } + + return all_entries; + } + + static int DeleteWhere(Database& db, const std::string &where_filter) + { + auto results = db.QueryDatabase( + fmt::format( + "DELETE FROM {} WHERE {}", + TableName(), + where_filter + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int Truncate(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "TRUNCATE TABLE {}", + TableName() + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int64 GetMaxId(Database& db) + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COALESCE(MAX({}), 0) FROM {}", + PrimaryKey(), + TableName() + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static int64 Count(Database& db, const std::string &where_filter = "") + { + auto results = db.QueryDatabase( + fmt::format( + "SELECT COUNT(*) FROM {} {}", + TableName(), + (where_filter.empty() ? "" : "WHERE " + where_filter) + ) + ); + + return (results.Success() && results.begin()[0] ? strtoll(results.begin()[0], nullptr, 10) : 0); + } + + static std::string BaseReplace() + { + return fmt::format( + "REPLACE INTO {} ({}) ", + TableName(), + ColumnsRaw() + ); + } + + static int ReplaceOne( + Database& db, + const BotBlockedBuffs &e + ) + { + std::vector v; + + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.spell_id)); + v.push_back(std::to_string(e.blocked)); + v.push_back(std::to_string(e.blocked_pet)); + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES ({})", + BaseReplace(), + Strings::Implode(",", v) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } + + static int ReplaceMany( + Database& db, + const std::vector &entries + ) + { + std::vector insert_chunks; + + for (auto &e: entries) { + std::vector v; + + v.push_back(std::to_string(e.bot_id)); + v.push_back(std::to_string(e.spell_id)); + v.push_back(std::to_string(e.blocked)); + v.push_back(std::to_string(e.blocked_pet)); + + insert_chunks.push_back("(" + Strings::Implode(",", v) + ")"); + } + + std::vector v; + + auto results = db.QueryDatabase( + fmt::format( + "{} VALUES {}", + BaseReplace(), + Strings::Implode(",", insert_chunks) + ) + ); + + return (results.Success() ? results.RowsAffected() : 0); + } +}; + +#endif //EQEMU_BASE_BOT_BLOCKED_BUFFS_REPOSITORY_H diff --git a/common/repositories/bot_blocked_buffs_repository.h b/common/repositories/bot_blocked_buffs_repository.h new file mode 100644 index 0000000000..be6466cbbe --- /dev/null +++ b/common/repositories/bot_blocked_buffs_repository.h @@ -0,0 +1,50 @@ +#ifndef EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H +#define EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H + +#include "../database.h" +#include "../strings.h" +#include "base/base_bot_blocked_buffs_repository.h" + +class BotBlockedBuffsRepository: public BaseBotBlockedBuffsRepository { +public: + + /** + * This file was auto generated and can be modified and extended upon + * + * Base repository methods are automatically + * generated in the "base" version of this repository. The base repository + * is immutable and to be left untouched, while methods in this class + * are used as extension methods for more specific persistence-layer + * accessors or mutators. + * + * Base Methods (Subject to be expanded upon in time) + * + * Note: Not all tables are designed appropriately to fit functionality with all base methods + * + * InsertOne + * UpdateOne + * DeleteOne + * FindOne + * GetWhere(std::string where_filter) + * DeleteWhere(std::string where_filter) + * InsertMany + * All + * + * Example custom methods in a repository + * + * BotBlockedBuffsRepository::GetByZoneAndVersion(int zone_id, int zone_version) + * BotBlockedBuffsRepository::GetWhereNeverExpires() + * BotBlockedBuffsRepository::GetWhereXAndY() + * BotBlockedBuffsRepository::DeleteWhereXAndY() + * + * Most of the above could be covered by base methods, but if you as a developer + * find yourself re-using logic for other parts of the code, its best to just make a + * method that can be re-used easily elsewhere especially if it can use a base repository + * method and encapsulate filters there + */ + + // Custom extended repository methods here + +}; + +#endif //EQEMU_BOT_BLOCKED_BUFFS_REPOSITORY_H diff --git a/common/ruletypes.h b/common/ruletypes.h index 832878b403..86974af586 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -891,6 +891,7 @@ RULE_BOOL(Bots, AllowCommandedLull, true, "If enabled bots can be commanded to l RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bots camp out.") RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.") +RULE_BOOL(Bots, AllowBotBlockedBuffs, true, "If enabled, you can create blocked buffs for each bot and for their pets.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/common/version.h b/common/version.h index d115f814bd..731ffab162 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9286 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9051 //TODO bot rewrite +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9052 //TODO bot rewrite #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index d791020ad1..70a57cf6a0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -108,6 +108,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm GenerateAppearance(); GenerateBaseStats(); bot_timers.clear(); + bot_blocked_buffs.clear(); // Calculate HitPoints Last As It Uses Base Stats current_hp = GenerateBaseHitPoints(); @@ -247,13 +248,16 @@ Bot::Bot( GenerateBaseStats(); bot_timers.clear(); - database.botdb.LoadTimers(this); LoadDefaultBotSettings(); - database.botdb.LoadBotSettings(this); + if (RuleB(Bots, AllowBotBlockedBuffs)) { + bot_blocked_buffs.clear(); + database.botdb.LoadBotBlockedBuffs(this); + } + LoadAAs(); if (database.botdb.LoadBuffs(this)) { @@ -1374,6 +1378,10 @@ bool Bot::Save() database.botdb.SaveStance(this); database.botdb.SaveBotSettings(this); + if (RuleB(Bots, AllowBotBlockedBuffs)) { + database.botdb.SaveBotBlockedBuffs(this); + } + if (!SavePet()) bot_owner->Message(Chat::White, "Failed to save pet for '%s'", GetCleanName()); @@ -1439,6 +1447,10 @@ bool Bot::DeleteBot() return false; } + if (!database.botdb.DeleteBotBlockedBuffs(GetBotID())) { + return false; + } + if (!database.botdb.DeleteBot(GetBotID())) { return false; } @@ -9573,11 +9585,22 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (IsBeneficialSpell(spell_id) && tar->IsBlockedBuff(spell_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if (RuleB(Spells, EnableBlockedBuffs) && IsBeneficialSpell(spell_id) && tar->IsClient() && tar->IsBlockedBuff(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } + if (RuleB(Bots, AllowBotBlockedBuffs) && IsBeneficialSpell(spell_id)) { + if (tar->IsBot() && tar->IsBlockedBuff(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + else if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsBot() && tar->GetOwner()->IsBlockedPetBuff(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + } + LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme if (!CanCastSpellType(spellType, spell_id, tar)) { return false; @@ -11887,7 +11910,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell return true; } - break; +break; case CommandedSubTypes::InvisAnimals: if (IsEffectInSpell(spell_id, SE_InvisVsAnimals) || IsEffectInSpell(spell_id, SE_ImprovedInvisAnimals)) { return true; @@ -11898,7 +11921,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell if ( (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) < 100) || (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) < 100) - ) { + ) { return true; } @@ -11907,7 +11930,7 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell if ( (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) > 100) || (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) > 100) - ) { + ) { return true; } @@ -11945,7 +11968,7 @@ uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) { if (!points || !rank) { LogTestDebug("{}: {} says, 'No {} found'", __LINE__, GetCleanName(), (!points ? "points" : "rank")); //deleteme return spell_id; - } + } spell_id = rank->spell; @@ -11953,3 +11976,153 @@ uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) { return spell_id; } + +void Bot::SetBotBlockedBuff(uint16 spell_id, bool block) { + if (!IsValidSpell(spell_id)) { + OwnerMessage("Failed to set blocked buff."); + + return; + } + + if (!bot_blocked_buffs.empty()) { + bool found = false; + + for (int i = 0; i < bot_blocked_buffs.size(); i++) { + if (bot_blocked_buffs[i].spell_id != spell_id) { + continue; + } + + bot_blocked_buffs[i].blocked = block; + found = true; + } + + if (!found) { + BotBlockedBuffs_Struct t; + + t.spell_id = spell_id; + t.blocked = block; + + bot_blocked_buffs.push_back(t); + } + } + else { + + BotBlockedBuffs_Struct t; + + t.spell_id = spell_id; + t.blocked = block; + + bot_blocked_buffs.push_back(t); + } + + CleanBotBlockedBuffs(); +} + +bool Bot::IsBlockedBuff(int32 spell_id) +{ + bool result = false; + + if (!IsValidSpell(spell_id)) { + OwnerMessage("Failed to get blocked buff."); + + return result; + } + + CleanBotBlockedBuffs(); + + if (!bot_blocked_buffs.empty()) { + for (int i = 0; i < bot_blocked_buffs.size(); i++) { + if (bot_blocked_buffs[i].spell_id != spell_id) { + continue; + } + + return bot_blocked_buffs[i].blocked; + } + } + + return result; +} + +void Bot::SetBotBlockedPetBuff(uint16 spell_id, bool block) { + if (!IsValidSpell(spell_id)) { + OwnerMessage("Failed to set blocked pet buff."); + + return; + } + + if (!bot_blocked_buffs.empty()) { + bool found = false; + + for (int i = 0; i < bot_blocked_buffs.size(); i++) { + if (bot_blocked_buffs[i].spell_id != spell_id) { + continue; + } + + bot_blocked_buffs[i].blocked_pet = block; + found = true; + } + + if (!found) { + BotBlockedBuffs_Struct t; + + t.spell_id = spell_id; + t.blocked_pet = block; + + bot_blocked_buffs.push_back(t); + } + } + else { + BotBlockedBuffs_Struct t; + + t.spell_id = spell_id; + t.blocked_pet = block; + + bot_blocked_buffs.push_back(t); + } + + CleanBotBlockedBuffs(); +} + +bool Bot::IsBlockedPetBuff(int32 spell_id) +{ + bool result = false; + + if (!IsValidSpell(spell_id)) { + OwnerMessage("Failed to get blocked pet buff."); + + return result; + } + + CleanBotBlockedBuffs(); + + if (!bot_blocked_buffs.empty()) { + for (int i = 0; i < bot_blocked_buffs.size(); i++) { + if (bot_blocked_buffs[i].spell_id != spell_id) { + continue; + } + + return bot_blocked_buffs[i].blocked_pet; + } + } + + return result; +} + +void Bot::CleanBotBlockedBuffs() +{ + if (!bot_blocked_buffs.empty()) { + int current = 0; + int end = bot_blocked_buffs.size(); + + while (current < end) { + if (!IsValidSpell(bot_blocked_buffs[current].spell_id) || (bot_blocked_buffs[current].blocked == 0 && bot_blocked_buffs[current].blocked_pet == 0)) { + bot_blocked_buffs.erase(bot_blocked_buffs.begin() + current); + } + else { + current++; + } + + end = bot_blocked_buffs.size(); + } + } +} diff --git a/zone/bot.h b/zone/bot.h index 457f7eebf1..cffadc4d3b 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -478,6 +478,12 @@ class Bot : public NPC { void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); uint16 GetSpellByAA(int id, AA::Rank* &rank); + void CleanBotBlockedBuffs(); + void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); } + bool IsBlockedBuff(int32 spell_id) override; //TODO bot rewrite - fix these to call from mob.h + bool IsBlockedPetBuff(int32 spell_id) override; //TODO bot rewrite - fix these to call from mob.h + void SetBotBlockedBuff(uint16 spell_id, bool block); + void SetBotBlockedPetBuff(uint16 spell_id, bool block); std::string GetBotSpellCategoryName(uint8 setting_type); std::string GetBotSettingCategoryName(uint8 setting_type); @@ -882,6 +888,8 @@ class Bot : public NPC { bool DeleteBot(); std::vector GetBotTimers() { return bot_timers; } void SetBotTimers(std::vector timers) { bot_timers = timers; } + std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } + void SetBotBlockedBuffs(std::vector blockedBuff) { bot_blocked_buffs = blockedBuff; } uint32 GetLastZoneID() const { return _lastZoneId; } int32 GetBaseAC() const { return _baseAC; } int32 GetBaseATK() const { return _baseATK; } @@ -1010,6 +1018,7 @@ class Bot : public NPC { std::vector AIBot_spells; std::vector AIBot_spells_enforced; std::vector bot_timers; + std::vector bot_blocked_buffs; private: // Class Members diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index a2a25a71f2..6e86ecdd41 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1251,6 +1251,8 @@ int bot_command_init(void) bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) || bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) || bot_command_add("behindmob", "Toggles whether or not your bot tries to stay behind a mob", AccountStatus::Player, bot_command_behind_mob) || + bot_command_add("blockedbuffs", "Set, view and clear blocked buffs for the selected bot(s)", AccountStatus::Player, bot_command_blocked_buffs) || + bot_command_add("blockedpetbuffs", "Set, view and clear blocked pet buffs for the selected bot(s)", AccountStatus::Player, bot_command_blocked_pet_buffs) || bot_command_add("bot", "Lists the available bot management [subcommands]", AccountStatus::Player, bot_command_bot) || bot_command_add("botappearance", "Lists the available bot appearance [subcommands]", AccountStatus::Player, bot_command_appearance) || bot_command_add("botbeardcolor", "Changes the beard color of a bot", AccountStatus::Player, bot_command_beard_color) || @@ -2216,6 +2218,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/apply_potion.cpp" #include "bot_commands/attack.cpp" #include "bot_commands/behind_mob.cpp" +#include "bot_commands/blocked_buffs.cpp" #include "bot_commands/bot.cpp" #include "bot_commands/bot_settings.cpp" #include "bot_commands/cast.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index d6644bbf60..875c6f3ace 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1669,6 +1669,8 @@ void bot_command_apply_poison(Client *c, const Seperator *sep); void bot_command_apply_potion(Client* c, const Seperator* sep); void bot_command_attack(Client *c, const Seperator *sep); void bot_command_behind_mob(Client* c, const Seperator* sep); +void bot_command_blocked_buffs(Client* c, const Seperator* sep); +void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep); void bot_command_bot(Client *c, const Seperator *sep); void bot_command_bot_settings(Client* c, const Seperator* sep); void bot_command_cast(Client* c, const Seperator* sep); diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp new file mode 100644 index 0000000000..a89c75e981 --- /dev/null +++ b/zone/bot_commands/blocked_buffs.cpp @@ -0,0 +1,518 @@ +#include "../bot_command.h" + +void bot_command_blocked_buffs(Client* c, const Seperator* sep) +{ + if (!RuleB(Bots, AllowBotBlockedBuffs)) { + c->Message(Chat::Yellow, "This command is disabled."); + + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Allows you to set, view and wipe blocked buffs for the selected bots" + }; + + std::vector notes = + { + "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list." + }; + + std::vector example_format = + { + fmt::format( + "{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To add Courage (Spell ID #202) to the targeted bot's blocked list:", + fmt::format( + "{} add 202", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ) + }; + std::vector examples_two = + { + "To view the targeted bot's blocked buff list:", + fmt::format( + "{} list", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To wipe all Warriors bots' blocked buff list:", + fmt::format( + "{} wipe byclass {}", + sep->arg[0], + Class::Warrior + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + c->Message( + Chat::Yellow, + fmt::format( + "You can also control bot buffs ({}).", + Saylink::Silent("^blockedbuffs help", "^blockedbuffs") + ).c_str() + ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + + return; + } + + std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool add = false; + bool remove = false; + bool list = false; + bool wipe = false; + uint16 spell_id; + + //AA help + if (!arg1.compare("add")) { + if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) { + c->Message(Chat::Yellow, "You must enter a valid spell ID."); + return; + } + + add = true; + spell_id = atoi(sep->arg[2]); + ++ab_arg; + } + else if (!arg1.compare("remove")) { + if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) { + c->Message(Chat::Yellow, "You must enter a valid spell ID."); + return; + } + + remove = true; + spell_id = atoi(sep->arg[2]); + ++ab_arg; + } + else if (!arg1.compare("list")) { + list = true; + } + else if (!arg1.compare("wipe")) { + wipe = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "target"; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + bool isSuccess = false; + uint16 successCount = 0; + Bot* firstFound = nullptr; + + for (auto bot_iter : sbl) { + if (!bot_iter->IsInGroupOrRaid(c)) { + continue; + } + + if (!firstFound) { + firstFound = bot_iter; + } + + if (add) { + bot_iter->SetBotBlockedBuff(spell_id, true); + } + else if (remove) { + bot_iter->SetBotBlockedBuff(spell_id, false); + } + else if (list) { + std::vector blockedBuffs = bot_iter->GetBotBlockedBuffs(); + bool found = false; + + if (!blockedBuffs.empty()) { + for (auto& blocked_buff : blockedBuffs) { + if (blocked_buff.blocked == 1 && IsValidSpell(blocked_buff.spell_id)) { + found = true; + c->Message( + Chat::Yellow, + fmt::format( + "{} says, '{} [#{}] is currently blocked.'", + bot_iter->GetCleanName(), + spells[blocked_buff.spell_id].name, + blocked_buff.spell_id + ).c_str() + ); + } + } + } + + if (!found) { + c->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I am not currently blocking any buffs.'", + bot_iter->GetCleanName() + ).c_str() + ); + } + } + else if (wipe) { + bot_iter->ClearBotBlockedBuffs(); + + c->Message( + Chat::Yellow, + fmt::format( + "{} says, I have wiped my blocked buffs list.'", + bot_iter->GetCleanName() + ).c_str() + ); + } + + isSuccess = true; + ++successCount; + } + + if (!isSuccess) { + c->Message(Chat::Yellow, "No bots were selected."); + } + else { + if (add || remove) { + c->Message( + Chat::Yellow, + fmt::format( + "{} {} {} blocking {} [#{}]", + ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), + ((successCount == 1 && firstFound) ? "is" : "of your bots"), + (add ? "now" : "no longer"), + spells[spell_id].name, + spell_id + ).c_str() + ); + } + } +} + + +void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) +{ + if (!RuleB(Bots, AllowBotBlockedBuffs)) { + c->Message(Chat::Yellow, "This command is disabled."); + + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Allows you to set, view and wipe blocked pet buffs for the selected bots" + }; + + std::vector notes = + { + "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list.", + "- This controls whether or not any pet the selected bot(s) own will prevent certain buffs from being cast." + }; + + std::vector example_format = + { + fmt::format( + "{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To add Courage (Spell ID #202) to the targeted bot's blocked list:", + fmt::format( + "{} add 202", + sep->arg[0], + c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + ) + }; + std::vector examples_two = + { + "To view the targeted bot's blocked buff list:", + fmt::format( + "{} list", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To wipe all Warriors bots' blocked buff list:", + fmt::format( + "{} wipe byclass {}", + sep->arg[0], + Class::Warrior + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + c->Message( + Chat::Yellow, + fmt::format( + "You can also control pet buffs ({}).", + Saylink::Silent("^blockedpetbuffs help", "^blockedpetbuffs") + ).c_str() + ); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + + return; + } + + + std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool add = false; + bool remove = false; + bool list = false; + bool wipe = false; + uint16 spell_id; + + //AA help + if (!arg1.compare("add")) { + if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) { + c->Message(Chat::Yellow, "You must enter a valid spell ID."); + return; + } + + add = true; + spell_id = atoi(sep->arg[2]); + ++ab_arg; + } + else if (!arg1.compare("remove")) { + if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) { + c->Message(Chat::Yellow, "You must enter a valid spell ID."); + return; + } + + remove = true; + spell_id = atoi(sep->arg[2]); + ++ab_arg; + } + else if (!arg1.compare("list")) { + list = true; + } + else if (!arg1.compare("wipe")) { + wipe = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "target"; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.remove(nullptr); + + bool isSuccess = false; + uint16 successCount = 0; + Bot* firstFound = nullptr; + + for (auto bot_iter : sbl) { + if (!bot_iter->IsInGroupOrRaid(c)) { + continue; + } + + if (!firstFound) { + firstFound = bot_iter; + } + + if (add) { + bot_iter->SetBotBlockedPetBuff(spell_id, true); + } + else if (remove) { + bot_iter->SetBotBlockedPetBuff(spell_id, false); + } + else if (list) { + std::vector blockedBuffs = bot_iter->GetBotBlockedBuffs(); + bool found = false; + + if (!blockedBuffs.empty()) { + for (auto& blocked_buff : blockedBuffs) { + if (blocked_buff.blocked_pet == 1 && IsValidSpell(blocked_buff.spell_id)) { + found = true; + c->Message( + Chat::Yellow, + fmt::format( + "{} says, '{} [#{}] is currently blocked for my pet.'", + bot_iter->GetCleanName(), + spells[blocked_buff.spell_id].name, + blocked_buff.spell_id + ).c_str() + ); + } + } + } + + if (!found) { + c->Message( + Chat::Yellow, + fmt::format( + "{} says, 'I am not currently blocking any pet buffs.'", + bot_iter->GetCleanName() + ).c_str() + ); + } + } + else if (wipe) { + bot_iter->ClearBotBlockedBuffs(); + + c->Message( + Chat::Yellow, + fmt::format( + "{} says, I have wiped my blocked buffs list.'", + bot_iter->GetCleanName() + ).c_str() + ); + } + + isSuccess = true; + ++successCount; + } + + if (!isSuccess) { + c->Message(Chat::Yellow, "No bots were selected."); + } + else { + if (add || remove) { + c->Message( + Chat::Yellow, + fmt::format( + "{} {} {} blocking {} [#{}] on their pet.", + ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), + ((successCount == 1 && firstFound) ? "is" : "of your bots"), + (add ? "now" : "no longer"), + spells[spell_id].name, + spell_id + ).c_str() + ); + } + } +} diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index afc923118b..2b721913a1 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -10,7 +10,7 @@ void bot_command_cast(Client* c, const Seperator* sep) std::vector notes = { - "- This will interrupt any spell currently being cast by bots told to use the command.", + "- This will interrupt any spell currently being cast by bots told to use the command", "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells", fmt::format( "- You can use {} aa # to cast any clickable AA or specifically {} harmtouch / {} layonhands" diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index a4d4c83858..c401604c3b 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -22,6 +22,7 @@ #include "../common/strings.h" #include "../common/eqemu_logsys.h" +#include "../common/repositories/bot_blocked_buffs_repository.h" #include "../common/repositories/bot_buffs_repository.h" #include "../common/repositories/bot_create_combinations_repository.h" #include "../common/repositories/bot_data_repository.h" @@ -2403,3 +2404,110 @@ bool BotDatabase::DeleteBotSettings(const uint32 bot_id) return true; } + +bool BotDatabase::LoadBotBlockedBuffs(Bot* b) +{ + if (!b) { + return false; + } + + const auto& l = BotBlockedBuffsRepository::GetWhere( + database, + fmt::format( + "`bot_id` = {}", + b->GetBotID() + ) + ); + + std::vector v; + + BotBlockedBuffs_Struct t{ }; + + for (const auto& e : l) { + t.spell_id = e.spell_id; + t.blocked = e.blocked; + t.blocked_pet = e.blocked_pet; + + v.push_back(t); + } + + if (!v.empty()) { + b->SetBotBlockedBuffs(v); + } + + return true; +} + +bool BotDatabase::SaveBotBlockedBuffs(Bot* b) +{ + if (!b) { + return false; + } + + if (!DeleteBotBlockedBuffs(b->GetBotID())) { + return false; + } + + std::vector v = b->GetBotBlockedBuffs(); + + if (v.empty()) { + return true; + } + + std::vector l; + + if (!v.empty()) { + for (auto& blocked_buff : v) { + if (blocked_buff.blocked == 0 && blocked_buff.blocked_pet == 0) { + continue; + } + + auto e = BotBlockedBuffsRepository::BotBlockedBuffs{ + .bot_id = b->GetBotID(), + .spell_id = blocked_buff.spell_id, + .blocked = blocked_buff.blocked, + .blocked_pet = blocked_buff.blocked_pet + }; + + l.push_back(e); + } + + if (l.empty()) { + return true; + } + + BotBlockedBuffsRepository::DeleteWhere( + database, + fmt::format( + "`bot_id` = {}", + b->GetBotID() + ) + ); + + const int inserted = BotBlockedBuffsRepository::InsertMany(database, l); + + if (!inserted) { + DeleteBotBlockedBuffs(b->GetBotID()); + return false; + } + } + + return true; +} + +bool BotDatabase::DeleteBotBlockedBuffs(const uint32 bot_id) +{ + if (!bot_id) { + return false; + } + + BotBlockedBuffsRepository::DeleteWhere( + database, + fmt::format( + "`bot_id` = {}", + bot_id + ) + ); + + return true; +} diff --git a/zone/bot_database.h b/zone/bot_database.h index 3539db5c6a..61fdc63016 100644 --- a/zone/bot_database.h +++ b/zone/bot_database.h @@ -74,6 +74,9 @@ class BotDatabase bool SaveTimers(Bot* b); bool DeleteTimers(const uint32 bot_id); + bool LoadBotBlockedBuffs(Bot* b); + bool SaveBotBlockedBuffs(Bot* b); + bool DeleteBotBlockedBuffs(const uint32 bot_id); /* Bot inventory functions */ bool QueryInventoryCount(const uint32 bot_id, uint32& item_count); diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 0cfc5913ea..9a05a70e43 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -97,4 +97,11 @@ struct BotSpellTypeOrder { uint16 priority; }; +struct BotBlockedBuffs_Struct { + uint32_t bot_id; + uint32_t spell_id; + uint8_t blocked; + uint8_t blocked_pet; +}; + #endif // BOT_STRUCTS diff --git a/zone/spells.cpp b/zone/spells.cpp index bce05ecd15..4b5aa8fc41 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4080,7 +4080,10 @@ bool Mob::SpellOnTarget( } // now check if the spell is allowed to land - if (RuleB(Spells, EnableBlockedBuffs)) { + if ( + (!spelltar->IsBot() && RuleB(Spells, EnableBlockedBuffs)) || + (spelltar->IsBot() && RuleB(Bots, AllowBotBlockedBuffs)) + ) { // We return true here since the caster's client should act like normal if (spelltar->IsBlockedBuff(spell_id)) { LogSpells( From f33ed3d4cd53717c60dedaf8ce467b0aa34f3d8e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:09:22 -0600 Subject: [PATCH 201/394] more blocked buff tweaks --- zone/bot.cpp | 29 +++++++++++++++++++---------- zone/bot.h | 4 ++-- zone/spells.cpp | 6 ++++-- 3 files changed, 25 insertions(+), 14 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 70a57cf6a0..0c5a77b257 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9585,22 +9585,31 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (RuleB(Spells, EnableBlockedBuffs) && IsBeneficialSpell(spell_id) && tar->IsClient() && tar->IsBlockedBuff(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if (RuleB(Bots, AllowBotBlockedBuffs) && IsBeneficialSpell(spell_id)) { - if (tar->IsBot() && tar->IsBlockedBuff(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + if ( + IsBeneficialSpell(spell_id) && + ( + (RuleB(Spells, EnableBlockedBuffs) && tar->IsClient()) || + (RuleB(Bots, AllowBotBlockedBuffs) && tar->IsBot()) + ) + ) { + if (tar->IsBlockedBuff(spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } - else if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsBot() && tar->GetOwner()->IsBlockedPetBuff(spell_id)) { + } + + if ( + IsBeneficialSpell(spell_id) && tar->IsPet() && + ( + (RuleB(Spells, EnableBlockedBuffs) && tar->GetOwner() && tar->GetOwner()->IsClient()) || + (RuleB(Bots, AllowBotBlockedBuffs) && tar->GetOwner() && tar->GetOwner()->IsBot()) + ) + ) { + if (tar->GetOwner()->IsBlockedPetBuff(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } } - LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme if (!CanCastSpellType(spellType, spell_id, tar)) { return false; diff --git a/zone/bot.h b/zone/bot.h index cffadc4d3b..20a2c479eb 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -480,8 +480,8 @@ class Bot : public NPC { uint16 GetSpellByAA(int id, AA::Rank* &rank); void CleanBotBlockedBuffs(); void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); } - bool IsBlockedBuff(int32 spell_id) override; //TODO bot rewrite - fix these to call from mob.h - bool IsBlockedPetBuff(int32 spell_id) override; //TODO bot rewrite - fix these to call from mob.h + bool IsBlockedBuff(int32 spell_id) override; + bool IsBlockedPetBuff(int32 spell_id) override; void SetBotBlockedBuff(uint16 spell_id, bool block); void SetBotBlockedPetBuff(uint16 spell_id, bool block); diff --git a/zone/spells.cpp b/zone/spells.cpp index 4b5aa8fc41..2cb4151d74 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4081,8 +4081,10 @@ bool Mob::SpellOnTarget( // now check if the spell is allowed to land if ( - (!spelltar->IsBot() && RuleB(Spells, EnableBlockedBuffs)) || - (spelltar->IsBot() && RuleB(Bots, AllowBotBlockedBuffs)) + (RuleB(Spells, EnableBlockedBuffs) && spelltar->IsClient()) || + (RuleB(Spells, EnableBlockedBuffs) && spelltar->IsPet() && spelltar->GetOwner() && spelltar->GetOwner()->IsClient()) || + (RuleB(Bots, AllowBotBlockedBuffs) && spelltar->IsBot()) || + (RuleB(Bots, AllowBotBlockedBuffs) && spelltar->IsPet() && spelltar->GetOwner() && spelltar->GetOwner()->IsBot()) ) { // We return true here since the caster's client should act like normal if (spelltar->IsBlockedBuff(spell_id)) { From 7ce37819f70883f7af1ddaeab81e9e4df01181fe Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:10:24 -0600 Subject: [PATCH 202/394] add beneficial check to ^blockedbuffs --- zone/bot_commands/blocked_buffs.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index a89c75e981..1a873b1768 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -108,9 +108,8 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) bool wipe = false; uint16 spell_id; - //AA help if (!arg1.compare("add")) { - if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) { + if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2]))) { c->Message(Chat::Yellow, "You must enter a valid spell ID."); return; } @@ -368,9 +367,8 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) bool wipe = false; uint16 spell_id; - //AA help if (!arg1.compare("add")) { - if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2]))) { + if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2])) { c->Message(Chat::Yellow, "You must enter a valid spell ID."); return; } From e1d4a68d898fe0fc5a2292e349213875ed5bfb82 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 17 Dec 2024 13:10:34 -0600 Subject: [PATCH 203/394] command grammar --- zone/bot_commands/blocked_buffs.cpp | 4 +- zone/bot_commands/bot_settings.cpp | 2 + zone/bot_commands/cast.cpp | 2 +- zone/bot_commands/copy_settings.cpp | 57 +++++++++++++----------- zone/bot_commands/default_settings.cpp | 2 +- zone/bot_commands/depart.cpp | 2 +- zone/bot_commands/spell_aggro_checks.cpp | 2 +- zone/bot_commands/spell_delays.cpp | 2 +- 8 files changed, 40 insertions(+), 33 deletions(-) diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index 1a873b1768..0b029ba58c 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -55,7 +55,7 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = { }; @@ -313,7 +313,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = { }; diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp index 4dc9babdf2..d42df0b077 100644 --- a/zone/bot_commands/bot_settings.cpp +++ b/zone/bot_commands/bot_settings.cpp @@ -4,6 +4,8 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) { std::list subcommand_list; subcommand_list.push_back("behindmob"); + subcommand_list.push_back("blockedbuffs"); + subcommand_list.push_back("blockedpetbuffs"); subcommand_list.push_back("distanceranged"); subcommand_list.push_back("copysettings"); subcommand_list.push_back("defaultsettings"); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 2b721913a1..6b3a201a95 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -68,7 +68,7 @@ void bot_command_cast(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = { }; diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 359ac8c22e..34a5cfcd25 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -50,18 +50,12 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid", - "targetgroup, namesgroup, healrotationtargets", - "mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = { - "all, misc, spellsettings, spelltypesettings", - "holds, delays, minthresholds, maxthresholds", - "minmanapct, maxmanapct, minhppct, maxhppct", - "idlepriority, engagedpriority, pursuepriority", - "aggrochecks, targetcounts" + "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, showhelm, followd, stopmeleelevel, enforcespellsettings, bottoggleranged, petsettype, behindmob, distanceranged, illusionblock, sitincombat, sithppercent, sitmanapercent, blockedbuffs, blockedpetbuffs" }; std::vector options_one = { @@ -72,10 +66,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) std::vector options_two = { "[misc] copies all miscellaneous options such as:", - "- ^showhelm, ^followd, ^stopmeleelevel", - "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", - "- ^behindmob, ^distanceranged, ^illusionblock", - "- ^sitincombat, ^sithppercent and ^sitmanapercent", + "- ^showhelm, ^followd, ^stopmeleelevel, ^enforcespellsettings, ^bottoggleranged, ^petsettype, ^behindmob, ^distanceranged, ^illusionblock, ^sitincombat, ^sithppercent, ^sitmanapercent, ^blockedbuffs, ^blockedpetbuffs", }; std::vector options_three = @@ -120,22 +111,36 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) std::vector options = { "all", - "misc" + "misc", "spellsettings", "spelltypesettings", - "holds", - "delays", - "minthresholds", - "maxthresholds", - "aggrochecks", - "minmanapct", - "maxmanapct", - "minhppct", - "maxhppct", - "idlepriority", - "engagedpriority", - "pursuepriority", - "targetcounts" + "spellholds", + "spelldelays", + "spellminthresholds", + "spellmaxthresholds", + "spellminmanapct", + "spellmaxmanapct", + "spellminhppct", + "spellmaxhppct", + "spellidlepriority", + "spellengagedpriority", + "spellpursuepriority", + "spellaggrochecks", + "spelltargetcounts", + "showhelm", + "followd", + "stopmeleelevel", + "enforcespellsettings", + "bottoggleranged", + "petsettype", + "behindmob", + "distanceranged", + "illusionblock", + "sitincombat", + "sithppercent", + "sitmanapercent", + "blockedbuffs", + "blockedpetbuffs" }; for (int i = 0; i < options.size(); i++) { diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index b12e183b40..5066074016 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -50,7 +50,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 1facaef1fc..e7c50156ac 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -48,7 +48,7 @@ void bot_command_depart(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = { }; diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index 0be650d696..c1b8241016 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -70,7 +70,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = { }; diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index d1fb2d6646..165770cbc9 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -76,7 +76,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) std::vector actionables = { - "target, byname, ownergroup, ownerraid targetgroup, namesgroup, healrotationtargets mmr, byclass, byrace, spawned" + "target, byname, ownergroup, ownerraid targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::vector options = { }; From 83777975238e8c484f7a934a5990cb606ee538d6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 17 Dec 2024 15:15:11 -0600 Subject: [PATCH 204/394] missing ) --- zone/bot_commands/blocked_buffs.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index 0b029ba58c..69a1aa13aa 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -368,7 +368,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) uint16 spell_id; if (!arg1.compare("add")) { - if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2])) { + if (!sep->IsNumber(2) || !IsValidSpell(atoi(sep->arg[2])) || !IsBeneficialSpell(atoi(sep->arg[2]))) { c->Message(Chat::Yellow, "You must enter a valid spell ID."); return; } From ffd92017e0b2478749e61bbb7c6ddd2ef0783fbc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:03:42 -0600 Subject: [PATCH 205/394] Move getnames for categories and settings to mob, rename hptomed/manatomed --- zone/bot.cpp | 102 ++++--------------------- zone/bot.h | 22 +++--- zone/bot_commands/sit_hp_percent.cpp | 6 +- zone/bot_commands/sit_mana_percent.cpp | 6 +- zone/bot_database.cpp | 26 +++---- zone/mob.cpp | 87 +++++++++++++++++++++ zone/mob.h | 3 + 7 files changed, 132 insertions(+), 120 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0c5a77b257..ccbcae8a4c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1787,7 +1787,7 @@ void Bot::SpellProcess() { } void Bot::BotMeditate(bool isSitting) { - if (GetManaRatio() < GetManaWhenToMed() || (GetHPRatio() < GetHPWhenToMed() && GetLevel() < GetStopMeleeLevel())) { + if (GetManaRatio() < GetSitManaPct() || (GetHPRatio() < GetSitHPPct() && GetLevel() < GetStopMeleeLevel())) { if ((!IsEngaged() || (IsEngaged() && GetMedInCombat() && !HasTargetReflection())) && !isSitting) { Sit(); } @@ -10170,11 +10170,11 @@ void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { case BotBaseSettings::MedInCombat: SetMedInCombat(settingValue); break; - case BotBaseSettings::HPWhenToMed: - SetHPWhenToMed(settingValue); + case BotBaseSettings::SitHPPct: + SetSitHPPct(settingValue); break; - case BotBaseSettings::ManaWhenToMed: - SetManaWhenToMed(settingValue); + case BotBaseSettings::SitManaPct: + SetSitManaPct(settingValue); break; default: break; @@ -10219,12 +10219,12 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { case BotBaseSettings::MedInCombat: //LogBotSettingsDetail("Returning current GetMedInCombate of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme return GetMedInCombat(); - case BotBaseSettings::HPWhenToMed: - //LogBotSettingsDetail("Returning current GetHPWhenToMed of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme - return GetHPWhenToMed(); - case BotBaseSettings::ManaWhenToMed: - //LogBotSettingsDetail("Returning current GetManaWhenToMed of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme - return GetManaWhenToMed(); + case BotBaseSettings::SitHPPct: + //LogBotSettingsDetail("Returning current GetSitHPPct of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetSitHPPct(); + case BotBaseSettings::SitManaPct: + //LogBotSettingsDetail("Returning current GetSitManaPct of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme + return GetSitManaPct(); default: return true; } @@ -10274,8 +10274,8 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance) { } return false; - case BotBaseSettings::HPWhenToMed: - case BotBaseSettings::ManaWhenToMed: + case BotBaseSettings::SitHPPct: + case BotBaseSettings::SitManaPct: return 80; case BotBaseSettings::EnforceSpellSettings: case BotBaseSettings::RangedSetting: @@ -10883,82 +10883,6 @@ void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount) _spellSettings[spellType].AEOrGroupTargetCount = targetCount; } -std::string Bot::GetBotSettingCategoryName(uint8 setting_type) { - switch (setting_type) { - case BotBaseSettings::ExpansionBitmask: - return "ExpansionBitmask"; - case BotBaseSettings::ShowHelm: - return "ShowHelm"; - case BotBaseSettings::FollowDistance: - return "FollowDistance"; - case BotBaseSettings::StopMeleeLevel: - return "StopMeleeLevel"; - case BotBaseSettings::EnforceSpellSettings: - return "EnforceSpellSettings"; - case BotBaseSettings::RangedSetting: - return "RangedSetting"; - case BotBaseSettings::PetSetTypeSetting: - return "PetSetTypeSetting"; - case BotBaseSettings::BehindMob: - return "BehindMob"; - case BotBaseSettings::DistanceRanged: - return "DistanceRanged"; - case BotBaseSettings::IllusionBlock: - return "IllusionBlock"; - case BotBaseSettings::MaxMeleeRange: - return "MaxMeleeRange"; - case BotBaseSettings::MedInCombat: - return "MedInCombat"; - case BotBaseSettings::HPWhenToMed: - return "HPWhenToMed"; - case BotBaseSettings::ManaWhenToMed: - return "ManaWhenToMed"; - default: - return "Null"; - } - - return "Null"; -} - -std::string Bot::GetBotSpellCategoryName(uint8 setting_type) { - switch (setting_type) { - case BotSettingCategories::BaseSetting: - return "BaseSetting"; - case BotSettingCategories::SpellHold: - return "SpellHold"; - case BotSettingCategories::SpellDelay: - return "SpellDelay"; - case BotSettingCategories::SpellMinThreshold: - return "SpellMinThreshold"; - case BotSettingCategories::SpellMaxThreshold: - return "SpellMaxThreshold"; - case BotSettingCategories::SpellTypeAggroCheck: - return "SpellTypeAggroCheck"; - case BotSettingCategories::SpellTypeMinManaPct: - return "SpellTypeMinManaPct"; - case BotSettingCategories::SpellTypeMaxManaPct: - return "SpellTypeMaxManaPct"; - case BotSettingCategories::SpellTypeMinHPPct: - return "SpellTypeMinHPPct"; - case BotSettingCategories::SpellTypeMaxHPPct: - return "SpellTypeMaxHPPct"; - case BotSettingCategories::SpellTypeIdlePriority: - return "SpellTypeIdlePriority"; - case BotSettingCategories::SpellTypeEngagedPriority: - return "SpellTypeEngagedPriority"; - case BotSettingCategories::SpellTypePursuePriority: - return "SpellTypePursuePriority"; - case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - return "SpellTypeAEOrGroupTargetCount"; - case BotSettingCategories::SpellTypeRecastDelay: - return "SpellTypeRecastDelay"; - default: - return "Null"; - } - - return "Null"; -} - std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { std::list castOrder; std::list tempCastOrder; diff --git a/zone/bot.h b/zone/bot.h index 20a2c479eb..c96632d8e9 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -139,12 +139,12 @@ namespace BotBaseSettings { constexpr uint16 IllusionBlock = 9; constexpr uint16 MaxMeleeRange = 10; constexpr uint16 MedInCombat = 11; - constexpr uint16 HPWhenToMed = 12; - constexpr uint16 ManaWhenToMed = 13; + constexpr uint16 SitHPPct = 12; + constexpr uint16 SitManaPct = 13; constexpr uint16 START_ALL = ExpansionBitmask; constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players - constexpr uint16 END = BotBaseSettings::ManaWhenToMed; // Increment as needed + constexpr uint16 END = BotBaseSettings::SitManaPct; // Increment as needed }; namespace CommandedSubTypes { @@ -471,6 +471,7 @@ class Bot : public NPC { void CopySettings(Bot* to, uint8 settingType, uint16 spellType = UINT16_MAX); void CopyBotSpellSettings(Bot* to); void ResetBotSpellSettings(); + void CopyBotBlockedBuffs(Bot* to); int GetBotBaseSetting(uint16 botSetting); int GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance = Stance::Balanced); void SetBotBaseSetting(uint16 botSetting, int settingValue); @@ -485,9 +486,6 @@ class Bot : public NPC { void SetBotBlockedBuff(uint16 spell_id, bool block); void SetBotBlockedPetBuff(uint16 spell_id, bool block); - std::string GetBotSpellCategoryName(uint8 setting_type); - std::string GetBotSettingCategoryName(uint8 setting_type); - int GetDefaultSetting(uint16 settingCategory, uint16 settingType, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); @@ -532,10 +530,10 @@ class Bot : public NPC { void SetBotDistanceRanged(uint32 distanceRanged) { _distanceRanged = distanceRanged; } bool GetMedInCombat() const { return _medInCombat; } void SetMedInCombat(bool value) { _medInCombat = value; } - uint8 GetHPWhenToMed() const { return _HPWhenToMed; } - void SetHPWhenToMed(uint8 value) { _HPWhenToMed = value; } - uint8 GetManaWhenToMed() const { return _ManaWhenToMed; } - void SetManaWhenToMed(uint8 value) { _ManaWhenToMed = value; } + uint8 GetSitHPPct() const { return _SitHPPct; } + void SetSitHPPct(uint8 value) { _SitHPPct = value; } + uint8 GetSitManaPct() const { return _SitManaPct; } + void SetSitManaPct(uint8 value) { _SitManaPct = value; } void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } bool HasLoS() const { return _hasLoS; } @@ -1089,8 +1087,8 @@ class Bot : public NPC { bool _behindMobStatus; bool _maxMeleeRangeStatus; bool _medInCombat; - uint8 _HPWhenToMed; - uint8 _ManaWhenToMed; + uint8 _SitHPPct; + uint8 _SitManaPct; uint16 _castedSpellType; bool _hasLoS; bool _commandedSpell; diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index 38b42ffd99..a8f6d76ff1 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -141,12 +141,12 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) fmt::format( "{} says, 'I sit in combat whem at or below [{}%%] HP.'", my_bot->GetCleanName(), - my_bot->GetHPWhenToMed() + my_bot->GetSitHPPct() ).c_str() ); } else { - my_bot->SetHPWhenToMed(typeValue); + my_bot->SetSitHPPct(typeValue); ++success_count; } } @@ -157,7 +157,7 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) fmt::format( "{} says, 'I will now sit in combat whem at or below [{}%%] HP.'", first_found->GetCleanName(), - first_found->GetHPWhenToMed() + first_found->GetSitHPPct() ).c_str() ); } diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 820be6a758..0c538023cf 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -141,12 +141,12 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) fmt::format( "{} says, 'I sit in combat whem at or below [{}%%] mana.'", my_bot->GetCleanName(), - my_bot->GetManaWhenToMed() + my_bot->GetSitManaPct() ).c_str() ); } else { - my_bot->SetManaWhenToMed(typeValue); + my_bot->SetSitManaPct(typeValue); ++success_count; } } @@ -157,7 +157,7 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) fmt::format( "{} says, 'I will now sit in combat whem at or below [{}%%] mana.'", first_found->GetCleanName(), - first_found->GetManaWhenToMed() + first_found->GetSitManaPct() ).c_str() ); } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index c401604c3b..dc7bcf38d0 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2238,7 +2238,7 @@ bool BotDatabase::LoadBotSettings(Mob* m) if (e.setting_type == BotSettingCategories::BaseSetting) { LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." , m->GetCleanName() - , m->CastToBot()->GetBotSettingCategoryName(e.setting_type) + , m->GetBotSettingCategoryName(e.setting_type) , e.setting_type , e.value ); //deleteme @@ -2246,7 +2246,7 @@ bool BotDatabase::LoadBotSettings(Mob* m) else { LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}]." , m->GetCleanName() - , m->CastToBot()->GetBotSpellCategoryName(e.setting_type) + , m->GetBotSpellCategoryName(e.setting_type) , e.setting_type , m->GetSpellTypeNameByID(e.setting_id) , e.setting_id @@ -2304,13 +2304,13 @@ bool BotDatabase::SaveBotSettings(Mob* m) .setting_id = static_cast(i), .setting_type = static_cast(BotSettingCategories::BaseSetting), .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), - .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), - .setting_name = m->CastToBot()->GetBotSettingCategoryName(i) + .category_name = m->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->GetBotSettingCategoryName(i) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); //deleteme + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); //deleteme } } @@ -2324,13 +2324,13 @@ bool BotDatabase::SaveBotSettings(Mob* m) .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToBot()->GetSetting(i, x), - .category_name = m->CastToBot()->GetBotSpellCategoryName(i), + .category_name = m->GetBotSpellCategoryName(i), .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, botStance)); //deleteme + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, botStance)); //deleteme } } } @@ -2345,18 +2345,18 @@ bool BotDatabase::SaveBotSettings(Mob* m) .setting_id = static_cast(BotBaseSettings::IllusionBlock), .setting_type = static_cast(BotSettingCategories::BaseSetting), .value = m->GetIllusionBlock(), - .category_name = m->CastToBot()->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), - .setting_name = m->CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock) + .category_name = m->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = m->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->GetIllusionBlock()); //deleteme + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->GetIllusionBlock()); //deleteme } for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { - LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, @@ -2365,13 +2365,13 @@ bool BotDatabase::SaveBotSettings(Mob* m) .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToClient()->GetBotSetting(i, x), - .category_name = m->CastToBot()->GetBotSpellCategoryName(i), + .category_name = m->GetBotSpellCategoryName(i), .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->CastToBot()->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme } } } diff --git a/zone/mob.cpp b/zone/mob.cpp index b47908a172..44151a06f4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8712,6 +8712,93 @@ uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { return UINT16_MAX; } +std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { + switch (setting_type) { + case BotSettingCategories::BaseSetting: + return "BaseSetting"; + case BotSettingCategories::SpellHold: + return "SpellHolds"; + case BotSettingCategories::SpellDelay: + return "SpellDelays"; + case BotSettingCategories::SpellMinThreshold: + return "SpellMinThresholds"; + case BotSettingCategories::SpellMaxThreshold: + return "SpellMaxThresholds"; + case BotSettingCategories::SpellTypeAggroCheck: + return "SpellAggroChecks"; + case BotSettingCategories::SpellTypeMinManaPct: + return "SpellMinManaPct"; + case BotSettingCategories::SpellTypeMaxManaPct: + return "SpellMaxManaPct"; + case BotSettingCategories::SpellTypeMinHPPct: + return "SpellMinHPPct"; + case BotSettingCategories::SpellTypeMaxHPPct: + return "SpellMaxHPPct"; + case BotSettingCategories::SpellTypeIdlePriority: + return "SpellIdlePriority"; + case BotSettingCategories::SpellTypeEngagedPriority: + return "SpellEngagedPriority"; + case BotSettingCategories::SpellTypePursuePriority: + return "SpellPursuePriority"; + case BotSettingCategories::SpellTypeAEOrGroupTargetCount: + return "SpellTargetCounts"; + case BotSettingCategories::SpellTypeRecastDelay: + return "SpellRecastDelay"; + default: + return "Null"; + } + + return "Null"; +} + +std::string Mob::GetBotSettingCategoryName(uint8 setting_type) { + switch (setting_type) { + case BotBaseSettings::ExpansionBitmask: + return "ExpansionBitmask"; + case BotBaseSettings::ShowHelm: + return "ShowHelm"; + case BotBaseSettings::FollowDistance: + return "FollowDistance"; + case BotBaseSettings::StopMeleeLevel: + return "StopMeleeLevel"; + case BotBaseSettings::EnforceSpellSettings: + return "EnforceSpellSettings"; + case BotBaseSettings::RangedSetting: + return "RangedSetting"; + case BotBaseSettings::PetSetTypeSetting: + return "PetSetTypeSetting"; + case BotBaseSettings::BehindMob: + return "BehindMob"; + case BotBaseSettings::DistanceRanged: + return "DistanceRanged"; + case BotBaseSettings::IllusionBlock: + return "IllusionBlock"; + case BotBaseSettings::MaxMeleeRange: + return "MaxMeleeRange"; + case BotBaseSettings::MedInCombat: + return "MedInCombat"; + case BotBaseSettings::SitHPPct: + return "SitHPPct"; + case BotBaseSettings::SitManaPct: + return "SitManaPct"; + default: + return "Null"; + } + + return "Null"; +} + +uint16 Mob::GetBaseSettingIDByShortName(std::string settingString) { + + for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { + if (!Strings::ToLower(settingString).compare(GetBotSettingCategoryName(i))) { + return i; + } + } + + return UINT16_MAX; +} + std::string Mob::GetSpellTypeNameByID(uint16 spellType) { std::string spellTypeName = "null"; diff --git a/zone/mob.h b/zone/mob.h index 8c43f04b96..29baea67ed 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -434,6 +434,9 @@ class Mob : public Entity { uint16 GetSpellTypeIDByShortName(std::string spellTypeString); + std::string GetBotSpellCategoryName(uint8 setting_type); + std::string GetBotSettingCategoryName(uint8 setting_type); + uint16 GetBaseSettingIDByShortName(std::string settingString); std::string GetSpellTypeNameByID(uint16 spellType); std::string GetSpellTypeShortNameByID(uint16 spellType); std::string GetSubTypeNameByID(uint16 subType); From 21f8bc0f95c1c65e90cf5f1d95f5aab11582c441 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 17 Dec 2024 23:20:39 -0600 Subject: [PATCH 206/394] add GetBotSpellCategoryIDByShortName and CopyBotBlockedPetBuffs, update ^defaultsettings command --- zone/bot.cpp | 36 ++++++++++++++++++++++++++ zone/bot.h | 1 + zone/bot_commands/default_settings.cpp | 7 +++-- zone/mob.cpp | 13 ++++++++-- zone/mob.h | 1 + 5 files changed, 54 insertions(+), 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index ccbcae8a4c..9fc169319b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11734,6 +11734,10 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { void Bot::CopyBotSpellSettings(Bot* to) { + if (!to) { + return; + } + to->ResetBotSpellSettings(); to->bot_spell_settings.clear(); @@ -11784,6 +11788,38 @@ void Bot::ResetBotSpellSettings() SetBotEnforceSpellSetting(false); } +void Bot::CopyBotBlockedBuffs(Bot* to) { + if (!to) { + return; + } + + to->ClearBotBlockedBuffs(); + + std::vector blockedBuffs = GetBotBlockedBuffs(); + + if (!blockedBuffs.empty()) { + for (auto& blocked_buff : blockedBuffs) { + to->SetBotBlockedBuff(blocked_buff.spell_id, blocked_buff.blocked); + } + } +} + +void Bot::CopyBotBlockedPetBuffs(Bot* to) { + if (!to) { + return; + } + + to->ClearBotBlockedBuffs(); + + std::vector blockedBuffs = GetBotBlockedBuffs(); + + if (!blockedBuffs.empty()) { + for (auto& blocked_buff : blockedBuffs) { + to->SetBotBlockedPetBuff(blocked_buff.spell_id, blocked_buff.blocked_pet); + } + } +} + bool Bot::BotPassiveCheck() { if (GetBotStance() == Stance::Passive) { GetOwner()->Message( diff --git a/zone/bot.h b/zone/bot.h index c96632d8e9..7eb348ec30 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -472,6 +472,7 @@ class Bot : public NPC { void CopyBotSpellSettings(Bot* to); void ResetBotSpellSettings(); void CopyBotBlockedBuffs(Bot* to); + void CopyBotBlockedPetBuffs(Bot* to); int GetBotBaseSetting(uint16 botSetting); int GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance = Stance::Balanced); void SetBotBaseSetting(uint16 botSetting, int settingValue); diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 5066074016..2e3ecc9c2b 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -358,7 +358,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "targetcounts")) { if (spellType != UINT16_MAX) { - myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); + myBot->SetSpellTypeAEOrGroupTargetCount(spellType, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType, botStance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -366,7 +366,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } } - output = "ae/group count settings"; + output = "target count settings"; } else if (!strcasecmp(sep->arg[1], "spellsettings")) { myBot->ResetBotSpellSettings(); @@ -430,6 +430,9 @@ void bot_command_default_settings(Client* c, const Seperator* sep) }; myBot->ResetBotSpellSettings(); + myBot->ClearBotBlockedBuffs(); + + myBot->Save(); output = "settings"; diff --git a/zone/mob.cpp b/zone/mob.cpp index 44151a06f4..af159dfbc6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8751,6 +8751,16 @@ std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { return "Null"; } +uint16 Mob::GetBotSpellCategoryIDByShortName(std::string settingString) { + for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { + if (!Strings::ToLower(settingString).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) { + return i; + } + } + + return UINT16_MAX; +} + std::string Mob::GetBotSettingCategoryName(uint8 setting_type) { switch (setting_type) { case BotBaseSettings::ExpansionBitmask: @@ -8789,9 +8799,8 @@ std::string Mob::GetBotSettingCategoryName(uint8 setting_type) { } uint16 Mob::GetBaseSettingIDByShortName(std::string settingString) { - for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { - if (!Strings::ToLower(settingString).compare(GetBotSettingCategoryName(i))) { + if (!Strings::ToLower(settingString).compare(Strings::ToLower(GetBotSettingCategoryName(i)))) { return i; } } diff --git a/zone/mob.h b/zone/mob.h index 29baea67ed..e38e13337a 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -435,6 +435,7 @@ class Mob : public Entity { uint16 GetSpellTypeIDByShortName(std::string spellTypeString); std::string GetBotSpellCategoryName(uint8 setting_type); + uint16 GetBotSpellCategoryIDByShortName(std::string settingString); std::string GetBotSettingCategoryName(uint8 setting_type); uint16 GetBaseSettingIDByShortName(std::string settingString); std::string GetSpellTypeNameByID(uint16 spellType); From eaa1f67802f1edb062ee5c3f7a08ab5100c73de0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:44:49 -0600 Subject: [PATCH 207/394] cls cleanup --- common/spdat.cpp | 20 ++++++++++---------- common/spdat.h | 8 ++++---- zone/bot.cpp | 5 ++--- zone/botspellsai.cpp | 2 +- 4 files changed, 17 insertions(+), 18 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index cbff317312..e81574aa77 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2791,7 +2791,7 @@ bool IsLichSpell(uint16 spell_id) return false; } -bool IsBotSpellTypeDetrimental(uint16 spellType, uint8 cls) { +bool IsBotSpellTypeDetrimental(uint16 spellType) { switch (spellType) { case BotSpellTypes::Nuke: case BotSpellTypes::Root: @@ -2831,7 +2831,7 @@ bool IsBotSpellTypeDetrimental(uint16 spellType, uint8 cls) { return false; } -bool IsBotSpellTypeBeneficial(uint16 spellType, uint8 cls) { +bool IsBotSpellTypeBeneficial(uint16 spellType) { switch (spellType) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: @@ -2999,12 +2999,12 @@ bool IsGroupBotSpellType(uint16 spellType) { bool IsGroupTargetOnlyBotSpellType(uint16 spellType) { switch (spellType) { - case BotSpellTypes::GroupCures: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHeals: - return true; - default: - return false; + case BotSpellTypes::GroupCures: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + return true; + default: + return false; } return false; @@ -3082,7 +3082,7 @@ bool IsHealBotSpellType(uint16 spellType) { return false; } -bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { +bool SpellTypeRequiresLoS(uint16 spellType) { if (IsAEBotSpellType(spellType)) { // These gather their own targets later return false; } @@ -3110,7 +3110,7 @@ bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls) { return true; } -bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls) { +bool SpellTypeRequiresTarget(uint16 spellType) { switch (spellType) { case BotSpellTypes::Pet: case BotSpellTypes::Succor: diff --git a/common/spdat.h b/common/spdat.h index ffe3525ade..cf182aabd9 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -737,8 +737,8 @@ const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellT const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); -bool IsBotSpellTypeDetrimental (uint16 spellType, uint8 cls = 0); -bool IsBotSpellTypeBeneficial (uint16 spellType, uint8 cls = 0); +bool IsBotSpellTypeDetrimental (uint16 spellType); +bool IsBotSpellTypeBeneficial (uint16 spellType); bool IsBotSpellTypeOtherBeneficial(uint16 spellType); bool IsBotSpellTypeInnate (uint16 spellType); bool IsAEBotSpellType(uint16 spellType); @@ -747,8 +747,8 @@ bool IsGroupTargetOnlyBotSpellType(uint16 spellType); bool IsPetBotSpellType(uint16 spellType); bool IsClientBotSpellType(uint16 spellType); bool IsHealBotSpellType(uint16 spellType); -bool SpellTypeRequiresLoS(uint16 spellType, uint16 cls = 0); -bool SpellTypeRequiresTarget(uint16 spellType, uint16 cls = 0); +bool SpellTypeRequiresLoS(uint16 spellType); +bool SpellTypeRequiresTarget(uint16 spellType); bool SpellTypeRequiresAEChecks(uint16 spellType); bool IsCommandedSpellType(uint16 spellType); bool IsPullingSpellType(uint16 spellType); diff --git a/zone/bot.cpp b/zone/bot.cpp index 9fc169319b..9eec3aa916 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9661,7 +9661,6 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } uint8 botClass = GetClass(); - //uint8 botLevel = GetLevel(); switch (spellType) { case BotSpellTypes::Buff: @@ -10484,7 +10483,7 @@ uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, ui } uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance) { - if (!IsBotSpellTypeBeneficial(spellType, botClass)) { + if (!IsBotSpellTypeBeneficial(spellType)) { return 0; } @@ -10746,7 +10745,7 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance) { - if (!IsBotSpellTypeBeneficial(spellType, GetClass())) { + if (!IsBotSpellTypeBeneficial(spellType)) { return RuleI(Bots, SpellResistLimit); } else { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index e61a407d1b..79074fab07 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -63,7 +63,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge botSpell.SpellIndex = 0; botSpell.ManaCost = 0; - if (SpellTypeRequiresLoS(spellType, botClass) && tar != this) { + if (SpellTypeRequiresLoS(spellType) && tar != this) { SetHasLoS(DoLosChecks(this, tar)); } else { From fbecac093819cf1e24aab97fd177b93e7c67a71f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:29:59 -0600 Subject: [PATCH 208/394] Allow bots to clear HasProjectIllusion flag --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 2cb4151d74..6dbd3e122b 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2664,7 +2664,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in } if(IsIllusionSpell(spell_id) - && IsClient() + && IsOfClientBot() && (HasProjectIllusion())){ LogAA("Effect Project Illusion for [{}] on spell id: [{}] was ON", GetName(), spell_id); SetProjectIllusion(false); From 54b98255377bffeb364a3700cefa69ac130f122f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:33:47 -0600 Subject: [PATCH 209/394] Add PercentChanceToCastGroupCure --- common/ruletypes.h | 1 + zone/botspellsai.cpp | 2 ++ 2 files changed, 3 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 86974af586..c113b4dda5 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -811,6 +811,7 @@ RULE_INT(Bots, PercentChanceToCastMez, 75, "The chance for a bot to attempt to c RULE_INT(Bots, PercentChanceToCastSlow, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastDebuff, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") +RULE_INT(Bots, PercentChanceToCastGroupCure, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastHateRedux, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastFear, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastOtherType, 90, "The chance for a bot to attempt to cast the remaining spell types in combat. Default 0-%.") diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 79074fab07..b7a7127571 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2094,6 +2094,8 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) return RuleI(Bots, PercentChanceToCastDebuff); case BotSpellTypes::Cure: return RuleI(Bots, PercentChanceToCastCure); + case BotSpellTypes::GroupCures: + return RuleI(Bots, PercentChanceToCastGroupCure); case BotSpellTypes::HateRedux: return RuleI(Bots, PercentChanceToCastHateRedux); case BotSpellTypes::Fear: From 322f8c5ffda64bfe7b2c024be9cde3c86ddd9ce1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:38:40 -0600 Subject: [PATCH 210/394] Implmenet PetCures, add some missing types for defaults/chance to cast --- common/spdat.cpp | 6 ++ common/spdat.h | 9 ++- zone/bot.cpp | 83 ++++++++++++--------- zone/botspellsai.cpp | 4 +- zone/mob.cpp | 173 ++++++++++++++++++++++++------------------- 5 files changed, 157 insertions(+), 118 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index e81574aa77..e2b00a5039 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2849,6 +2849,7 @@ bool IsBotSpellTypeBeneficial(uint16 spellType) { case BotSpellTypes::Buff: case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: case BotSpellTypes::DamageShields: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: @@ -2898,6 +2899,7 @@ bool IsBotSpellTypeOtherBeneficial(uint16 spellType) { case BotSpellTypes::Buff: case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetBuffs: @@ -3020,6 +3022,7 @@ bool IsPetBotSpellType(uint16 spellType) { case BotSpellTypes::PetHoTHeals: case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::PetCures: return true; default: return false; @@ -3046,6 +3049,7 @@ bool IsClientBotSpellType(uint16 spellType) { case BotSpellTypes::Buff: case BotSpellTypes::Cure: case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: case BotSpellTypes::PetBuffs: @@ -3513,6 +3517,8 @@ uint16 GetPetSpellType(uint16 spellType) { return BotSpellTypes::PetVeryFastHeals; case BotSpellTypes::HoTHeals: return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::Cures: + return BotSpellTypes::PetCures; case BotSpellTypes::DamageShields: return BotSpellTypes::PetDamageShields; case BotSpellTypes::ResistBuffs: diff --git a/common/spdat.h b/common/spdat.h index cf182aabd9..b518dc7250 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -706,10 +706,11 @@ namespace BotSpellTypes constexpr uint16 PetFastHeals = 50; constexpr uint16 PetVeryFastHeals = 51; constexpr uint16 PetHoTHeals = 52; - constexpr uint16 DamageShields = 53; - constexpr uint16 ResistBuffs = 54; - constexpr uint16 PetDamageShields = 55; - constexpr uint16 PetResistBuffs = 56; + constexpr uint16 PetCures = 53; + constexpr uint16 DamageShields = 54; + constexpr uint16 ResistBuffs = 55; + constexpr uint16 PetDamageShields = 56; + constexpr uint16 PetResistBuffs = 57; // Command Spell Types constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic diff --git a/zone/bot.cpp b/zone/bot.cpp index 9eec3aa916..be519bb39f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10550,45 +10550,49 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, ui priority = 15; break; - case BotSpellTypes::Pet: + case BotSpellTypes::PetCures: priority = 16; break; - case BotSpellTypes::Buff: + case BotSpellTypes::Pet: priority = 17; break; - case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Buff: priority = 18; break; - case BotSpellTypes::ResistBuffs: + case BotSpellTypes::OutOfCombatBuffSong: priority = 19; break; - case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: priority = 20; break; - case BotSpellTypes::PetBuffs: + case BotSpellTypes::DamageShields: priority = 21; break; - case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PetBuffs: priority = 22; break; - case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::PreCombatBuff: priority = 23; break; - case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::PreCombatBuffSong: priority = 24; break; - case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: priority = 25; + break; + case BotSpellTypes::PetDamageShields: + priority = 26; + break; default: priority = 0; //unused @@ -10633,60 +10637,62 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, return 15; case BotSpellTypes::PetHoTHeals: return 16; - case BotSpellTypes::AELifetap: + case BotSpellTypes::PetCures: return 17; - case BotSpellTypes::Lifetap: + case BotSpellTypes::AELifetap: return 18; - case BotSpellTypes::HateRedux: + case BotSpellTypes::Lifetap: return 19; - case BotSpellTypes::AEMez: + case BotSpellTypes::HateRedux: return 20; - case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: return 21; - case BotSpellTypes::AEHateLine: + case BotSpellTypes::Mez: return 22; - case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: return 23; - case BotSpellTypes::AEDispel: + case BotSpellTypes::HateLine: return 24; - case BotSpellTypes::Dispel: + case BotSpellTypes::AEDispel: return 25; - case BotSpellTypes::AEDebuff: + case BotSpellTypes::Dispel: return 26; - case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: return 27; - case BotSpellTypes::AESnare: + case BotSpellTypes::Debuff: return 28; - case BotSpellTypes::Snare: + case BotSpellTypes::AESnare: return 29; - case BotSpellTypes::AESlow: + case BotSpellTypes::Snare: return 30; - case BotSpellTypes::Slow: + case BotSpellTypes::AESlow: return 31; - case BotSpellTypes::AERoot: + case BotSpellTypes::Slow: return 32; - case BotSpellTypes::Root: + case BotSpellTypes::AERoot: return 33; - case BotSpellTypes::AEDoT: + case BotSpellTypes::Root: return 34; - case BotSpellTypes::DOT: + case BotSpellTypes::AEDoT: return 35; - case BotSpellTypes::AEStun: + case BotSpellTypes::DOT: return 36; - case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEStun: return 37; - case BotSpellTypes::AENukes: + case BotSpellTypes::PBAENuke: return 38; - case BotSpellTypes::AERains: + case BotSpellTypes::AENukes: return 39; - case BotSpellTypes::Stun: + case BotSpellTypes::AERains: return 40; - case BotSpellTypes::Nuke: + case BotSpellTypes::Stun: return 41; - case BotSpellTypes::InCombatBuff: + case BotSpellTypes::Nuke: return 42; - case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::InCombatBuff: return 43; + case BotSpellTypes::InCombatBuffSong: + return 44; default: return 0; } @@ -10738,6 +10744,8 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, return 21; case BotSpellTypes::PetHoTHeals: return 22; + case BotSpellTypes::PetCures: + return 23; default: return 0; } @@ -11215,6 +11223,7 @@ uint16 Bot::GetSpellListSpellType(uint16 spellType) { return BotSpellTypes::Fear; case BotSpellTypes::GroupCures: case BotSpellTypes::Cure: + case BotSpellTypes::PetCures: return BotSpellTypes::Cure; case BotSpellTypes::AERoot: case BotSpellTypes::Root: diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b7a7127571..85052dbfc5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -38,7 +38,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge (spellType == BotSpellTypes::PreCombatBuffSong && tar->IsPet()) || (!RuleB(Bots, AllowBuffingHealingFamiliars) && tar->IsFamiliar()) || (tar->IsPet() && tar->IsCharmed() && spellType == BotSpellTypes::PetBuffs && !RuleB(Bots, AllowCharmedPetBuffs)) || - (tar->IsPet() && tar->IsCharmed() && (spellType == BotSpellTypes::Cure || spellType == BotSpellTypes::GroupCures) && !RuleB(Bots, AllowCharmedPetCures)) || + (tar->IsPet() && tar->IsCharmed() && spellType == BotSpellTypes::PetCures && !RuleB(Bots, AllowCharmedPetCures)) || (tar->IsPet() && tar->IsCharmed() && IsHealBotSpellType(spellType) && !RuleB(Bots, AllowCharmedPetHeals)) ) { return false; @@ -189,6 +189,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return BotCastHeal(tar, botClass, botSpell, spellType); case BotSpellTypes::GroupCures: case BotSpellTypes::Cure: + case BotSpellTypes::PetCures: if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { return false; } @@ -2093,6 +2094,7 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) case BotSpellTypes::Debuff: return RuleI(Bots, PercentChanceToCastDebuff); case BotSpellTypes::Cure: + case BotSpellTypes::PetCures: return RuleI(Bots, PercentChanceToCastCure); case BotSpellTypes::GroupCures: return RuleI(Bots, PercentChanceToCastGroupCure); diff --git a/zone/mob.cpp b/zone/mob.cpp index af159dfbc6..a7f81ab162 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8863,6 +8863,9 @@ std::string Mob::GetSpellTypeNameByID(uint16 spellType) { case BotSpellTypes::GroupCures: spellTypeName = "Group Cure"; break; + case BotSpellTypes::PetCures: + spellTypeName = "Pet Cure"; + break; case BotSpellTypes::Resurrect: spellTypeName = "Resurrect"; break; @@ -9087,6 +9090,9 @@ std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { case BotSpellTypes::GroupCures: spellTypeName = "groupcures"; break; + case BotSpellTypes::PetCures: + spellTypeName = "petcure"; + break; case BotSpellTypes::Resurrect: spellTypeName = "resurrect"; break; @@ -9298,7 +9304,56 @@ std::string Mob::GetSubTypeNameByID(uint16 subType) { } bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { + uint8 botClass = GetClass(); + switch (spellType) { + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::Pet: + case BotSpellTypes::Escape: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::DamageShields: + return false; + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::RegularHeal: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: + if (botClass == Class::Bard) { + return false; + } + else { + return true; + } case BotSpellTypes::Nuke: case BotSpellTypes::DOT: case BotSpellTypes::Stun: @@ -9320,15 +9375,6 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { default: return true; } - case BotSpellTypes::AESnare: - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AEFear: - case BotSpellTypes::Fear: - case BotSpellTypes::AEHateLine: - return true; case BotSpellTypes::Mez: case BotSpellTypes::AEMez: case BotSpellTypes::Debuff: @@ -9352,17 +9398,8 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { default: return false; } - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::PreCombatBuffSong: - if (GetClass() == Class::Bard) { - return false; - } - else { - return true; - } case BotSpellTypes::HateLine: - if (GetClass() == Class::ShadowKnight || GetClass() == Class::Paladin) { + if (botClass == Class::ShadowKnight || botClass == Class::Paladin) { switch (stance) { case Stance::Aggressive: return false; @@ -9373,45 +9410,23 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { else { return true; } - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - switch (stance) { - case Stance::Aggressive: - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetFastHeals: + case BotSpellTypes::Charm: + case BotSpellTypes::Resurrect: + case BotSpellTypes::AESnare: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::AEHateLine: + case BotSpellTypes::PetCures: case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::RegularHeal: - switch (stance) { - case Stance::Aggressive: - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::FastHeals: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::Pet: - case BotSpellTypes::Escape: - case BotSpellTypes::Lifetap: - case BotSpellTypes::Buff: - case BotSpellTypes::InCombatBuff: - case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: default: - return false; + return true; } } @@ -9531,6 +9546,8 @@ uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance) { } uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { + uint8 botClass = GetClass(); + switch (spellType) { case BotSpellTypes::Escape: case BotSpellTypes::VeryFastHeals: @@ -9599,6 +9616,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::Snare: case BotSpellTypes::AEFear: case BotSpellTypes::Fear: + case BotSpellTypes::AEDispel: case BotSpellTypes::Dispel: case BotSpellTypes::AEDebuff: case BotSpellTypes::Debuff: @@ -9614,10 +9632,30 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { default: return 99; } + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + if (botClass == Class::Necromancer || botClass == Class::Shaman) { + return 60; + } + else { + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 95; + case Stance::Efficient: + return 80; + default: + return 90; + } + } case BotSpellTypes::Buff: case BotSpellTypes::Charm: case BotSpellTypes::Cure: - case BotSpellTypes::DamageShields: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::DamageShields: case BotSpellTypes::HateRedux: case BotSpellTypes::InCombatBuff: case BotSpellTypes::InCombatBuffSong: @@ -9634,25 +9672,6 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::Resurrect: case BotSpellTypes::HateLine: case BotSpellTypes::AEHateLine: - return 100; - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetHoTHeals: - if (GetClass() == Class::Necromancer || GetClass() == Class::Shaman) { - return 60; - } - else { - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 95; - case Stance::Efficient: - return 80; - default: - return 90; - } - } default: return 100; } @@ -9785,6 +9804,8 @@ uint16 Mob::GetPetSpellType(uint16 spellType) { return BotSpellTypes::PetHoTHeals; case BotSpellTypes::Buff: return BotSpellTypes::PetBuffs; + case BotSpellTypes::Cure: + return BotSpellTypes::PetCures; case BotSpellTypes::DamageShields: return BotSpellTypes::PetDamageShields; case BotSpellTypes::ResistBuffs: From d64af967ea3718650f0c62aabcc6edd09febb80b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:40:01 -0600 Subject: [PATCH 211/394] Change GetRaidByBotName to GetRaidByBot --- zone/bot.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index be519bb39f..2cc85a3b3b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2056,7 +2056,7 @@ void Bot::AI_Process() return; } - auto raid = entity_list.GetRaidByBotName(GetName()); + auto raid = entity_list.GetRaidByBot(this); uint32 r_group = RAID_GROUPLESS; if (raid) { @@ -2503,7 +2503,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { bool assisteeFound = false; if (IsRaidGrouped()) { - Raid* r = entity_list.GetRaidByBotName(GetName()); + Raid* r = entity_list.GetRaidByBot(this); if (r) { for (const auto& m : r->members) { if ( @@ -3553,7 +3553,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { ChangeBotRangedWeapons(true); } - if (auto raid = entity_list.GetRaidByBotName(GetName())) { + if (auto raid = entity_list.GetRaidByBot(this)) { // Safety Check to confirm we have a valid raid auto owner = GetBotOwner(); if (owner && !raid->IsRaidMember(owner->GetCleanName())) { @@ -3564,7 +3564,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { raid->VerifyRaid(); } } - else if (auto group = entity_list.GetGroupByMobName(GetName())) { + else if (auto group = entity_list.GetGroupByMob(this)) { // Safety Check to confirm we have a valid group auto owner = GetBotOwner(); if (owner && !group->IsGroupMember(owner->GetCleanName())) { @@ -6047,7 +6047,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spells::CastingSlot slot, bool& stopLogic) { bool isMainGroupMGB = false; - Raid* raid = entity_list.GetRaidByBotName(GetName()); + Raid* raid = entity_list.GetRaidByBot(this); if (isMainGroupMGB && (GetClass() != Class::Bard)) { BotGroupSay( @@ -6783,7 +6783,7 @@ void Bot::Camp(bool save_to_database) { } void Bot::Zone() { - if (auto raid = entity_list.GetRaidByBotName(GetName())) { + if (auto raid = entity_list.GetRaidByBot(this)) { raid->MemberZoned(CastToClient()); } else if (HasGroup()) { @@ -7027,7 +7027,7 @@ void Bot::ProcessBotGroupDisband(Client* c, const std::string& botName) { // Processes a raid disband request from a Client for a Bot. void Bot::RemoveBotFromRaid(Bot* bot) { - Raid* bot_raid = entity_list.GetRaidByBotName(bot->GetName()); + Raid* bot_raid = entity_list.GetRaidByBot(bot); if (bot_raid) { uint32 gid = bot_raid->GetGroup(bot->GetName()); bot_raid->SendRaidGroupRemove(bot->GetName(), gid); @@ -7048,7 +7048,7 @@ void Bot::ProcessClientZoneChange(Client* botOwner) { Bot* tempBot = *itr; if (tempBot) { - Raid* raid = entity_list.GetRaidByBotName(tempBot->GetName()); + Raid* raid = entity_list.GetRaidByBot(tempBot); if (raid) { tempBot->Zone(); } @@ -7781,7 +7781,7 @@ void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) { va_end(ap); if (speaker->IsRaidGrouped()) { - Raid* r = entity_list.GetRaidByBotName(speaker->GetName()); + Raid* r = entity_list.GetRaidByBot(speaker->CastToBot()); if (r) { for (const auto& m : r->members) { if (m.member && !m.is_bot) { From d120b45880da29d69af225123279423aec10c98c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:40:47 -0600 Subject: [PATCH 212/394] Typo on PetBuffs implement --- common/spdat.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index e2b00a5039..8990b46733 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -3517,7 +3517,7 @@ uint16 GetPetSpellType(uint16 spellType) { return BotSpellTypes::PetVeryFastHeals; case BotSpellTypes::HoTHeals: return BotSpellTypes::PetHoTHeals; - case BotSpellTypes::Cures: + case BotSpellTypes::Cure: return BotSpellTypes::PetCures; case BotSpellTypes::DamageShields: return BotSpellTypes::PetDamageShields; From e031b4d1e0201febaa410c065874debd4aedb989 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:42:01 -0600 Subject: [PATCH 213/394] Change GetSpellListSpellType to GetParentSpellType --- zone/bot.cpp | 2 +- zone/bot.h | 2 +- zone/botspellsai.cpp | 14 +++++++------- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2cc85a3b3b..b5af8d6f13 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11168,7 +11168,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { return false; } -uint16 Bot::GetSpellListSpellType(uint16 spellType) { +uint16 Bot::GetParentSpellType(uint16 spellType) { switch (spellType) { case BotSpellTypes::AENukes: case BotSpellTypes::AERains: diff --git a/zone/bot.h b/zone/bot.h index 7eb348ec30..d365d24317 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -539,7 +539,7 @@ class Bot : public NPC { bool HasLoS() const { return _hasLoS; } std::list GetSpellTypesPrioritized(uint8 priorityType); - uint16 GetSpellListSpellType(uint16 spellType); + uint16 GetParentSpellType(uint16 spellType); bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spellType); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 85052dbfc5..bacc650766 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -885,7 +885,7 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spell if ( botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && (IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect)) ) { @@ -923,7 +923,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, if ( botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && ( IsEffectInSpell(botSpellList[i].spellid, spellEffect) || @@ -964,7 +964,7 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp if ( botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) ) { BotSpell botSpell; @@ -999,7 +999,7 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa if ( botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) ) { if ( @@ -1080,7 +1080,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { if ( botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) ) { result.SpellId = botSpellList[i].spellid; @@ -1186,7 +1186,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster, Mob* tar, uint16 } if ( - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetSpellListSpellType(spellType)) && + (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && IsCompleteHealSpell(botSpellList[i].spellid) && botCaster->CastChecks(botSpellList[i].spellid, tar, spellType) @@ -2839,7 +2839,7 @@ void Bot::CheckBotSpells() { } correctType = GetCorrectSpellType(s.type, spell_id); - parentType = GetSpellListSpellType(correctType); + parentType = GetParentSpellType(correctType); if (RuleB(Bots, UseParentSpellTypeForChecks)) { if (s.type == parentType || s.type == correctType) { From 66356a3d6e836ca7d56a26ebb2e954fe04913da4 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:43:00 -0600 Subject: [PATCH 214/394] missing from GetChanceToCastBySpellType --- zone/botspellsai.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index bacc650766..da1b27bb22 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2060,7 +2060,6 @@ uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) case BotSpellTypes::GroupHeals: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::GroupCures: return RuleI(Bots, PercentChanceToCastGroupHeal); case BotSpellTypes::Nuke: return RuleI(Bots, PercentChanceToCastNuke); From e710fbea3421c2e7910755caa77242001f080a8a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:43:51 -0600 Subject: [PATCH 215/394] Fix performance in IsValidSpellRange by flipping HasProjectIllusion --- zone/botspellsai.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index da1b27bb22..79c6569a62 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2694,6 +2694,7 @@ bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { range = GetActSpellRange(spell_id, range); if (IsIllusionSpell(spell_id) && (HasProjectIllusion())) { + if (HasProjectIllusion() && IsIllusionSpell(spell_id)) { range = 100; } From 7870499fa561d9217c7fdecb99535a40a55fcf1f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:44:00 -0600 Subject: [PATCH 216/394] merge with prev --- zone/botspellsai.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 79c6569a62..b2705023ea 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2693,7 +2693,6 @@ bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { range = GetActSpellRange(spell_id, range); - if (IsIllusionSpell(spell_id) && (HasProjectIllusion())) { if (HasProjectIllusion() && IsIllusionSpell(spell_id)) { range = 100; } From e085780c0a9e3ae28bfbca87d191ec501050282a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:45:39 -0600 Subject: [PATCH 217/394] merge with cls cleanup --- zone/bot_commands/cast.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 6b3a201a95..d02c9f2a92 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -495,7 +495,7 @@ void bot_command_cast(Client* c, const Seperator* sep) if (!aaType && !bySpellID) { //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme - if (!SpellTypeRequiresTarget(spellType, bot_iter->GetClass())) { + if (!SpellTypeRequiresTarget(spellType)) { newTar = bot_iter; } @@ -511,7 +511,7 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - if (IsBotSpellTypeDetrimental(spellType, bot_iter->GetClass()) && !bot_iter->IsAttackAllowed(newTar)) { + if (IsBotSpellTypeDetrimental(spellType) && !bot_iter->IsAttackAllowed(newTar)) { bot_iter->BotGroupSay( bot_iter, fmt::format( From da5cb9c9365c3c7f2312427155b0e3c683c1fcff Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:48:06 -0600 Subject: [PATCH 218/394] Reorder IsTargetAlreadyReceivingSpell/CheckSpellLevelRestriction/IsBlockedBuff --- zone/bot.cpp | 30 ++++++++++-------------------- 1 file changed, 10 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b5af8d6f13..d2c9286737 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9580,6 +9580,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (!tar->CheckSpellLevelRestriction(this, spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9651,6 +9656,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + return true; } @@ -9687,16 +9697,6 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { return false; } - if (tar->IsBlockedBuff(spell_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to IsBlockedBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - - if (!tar->CheckSpellLevelRestriction(this, spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; @@ -9717,11 +9717,6 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { return false; } - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - if (!IsCommandedSpell()) { switch (tar->GetArchetype()) { case Archetype::Caster: @@ -9774,11 +9769,6 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: - if (!IsCommandedSpell() && IsTargetAlreadyReceivingSpell(tar, spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - if (!IsCommandedSpell()) { switch (tar->GetArchetype()) { case Archetype::Caster: From 665d7e3641717abce65374acad59b8019e54141a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:50:07 -0600 Subject: [PATCH 219/394] Combine GatherGroupSpellTargets and GatherSpellTargets --- zone/bot.cpp | 107 ++++++++----------------------------------- zone/bot.h | 3 +- zone/botspellsai.cpp | 27 +++-------- 3 files changed, 26 insertions(+), 111 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index d2c9286737..a9f455a30f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6007,12 +6007,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe if (!noGroupSpell) { std::vector v; - if (RuleB(Bots, RaidBuffing)) { - v = GatherSpellTargets(true); - } - else { - v = GatherGroupSpellTargets(); - } + v = GatherSpellTargets(RuleB(Bots, RaidBuffing)); for (Mob* m : v) { if (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune)) { @@ -6072,7 +6067,7 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel v = GatherSpellTargets(true); } else { - v = GatherGroupSpellTargets(spellTarget); + v = GatherSpellTargets(false, spellTarget); } for (Mob* m : v) { @@ -9287,74 +9282,11 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 }; -std::vector Bot::GatherGroupSpellTargets(Mob* target, bool noClients, bool noBots) { - std::vector valid_spell_targets; - - if (IsRaidGrouped()) { - if (auto raid = entity_list.GetRaidByBotName(GetName())) { - std::vector raidGroupMembers; - if (target) { - auto raidGroup = raid->GetGroup(target->GetName()); - - if (raidGroup != RAID_GROUPLESS) { - raidGroupMembers = raid->GetRaidGroupMembers(raidGroup); - } - else { - return valid_spell_targets; - } - } - else { - auto raidGroup = raid->GetGroup(GetName()); - - if (raidGroup != RAID_GROUPLESS) { - raidGroupMembers = raid->GetRaidGroupMembers(raidGroup); - } - else { - return valid_spell_targets; - } - } - - for (const auto& m : raidGroupMembers) { - if ( - m.member && m.group_number != RAID_GROUPLESS && - ( - (m.member->IsClient() && !noClients) || - (m.member->IsBot() && !noBots) - ) - ) { - valid_spell_targets.emplace_back(m.member); - } - } - } - } - else if (IsGrouped()) { - Group* group = GetGroup(); - if (group) { - for (const auto& m : group->members) { - if ( - m && - ( - (m->IsClient() && !noClients) || - (m->IsBot() && !noBots) - ) - ) { - valid_spell_targets.emplace_back(m); - } - } - } - } - else { - valid_spell_targets.emplace_back(this); - } - - return valid_spell_targets; -} - -std::vector Bot::GatherSpellTargets(bool entireRaid, bool noClients, bool noBots, bool noPets) { +std::vector Bot::GatherSpellTargets(bool entireRaid, Mob* target, bool noClients, bool noBots, bool noPets) { std::vector valid_spell_targets; if (IsRaidGrouped()) { - if (auto raid = entity_list.GetRaidByBotName(GetName())) { + if (auto raid = GetRaid()) { if (entireRaid) { for (const auto& m : raid->members) { if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { @@ -9363,8 +9295,15 @@ std::vector Bot::GatherSpellTargets(bool entireRaid, bool noClients, bool } } else { - std::vector raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); + std::vector raidGroup; + if (target) { + raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(target->GetName())); + } + else { + raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); + } + for (const auto& m : raidGroup) { if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { valid_spell_targets.emplace_back(m.member); @@ -9375,6 +9314,7 @@ std::vector Bot::GatherSpellTargets(bool entireRaid, bool noClients, bool } else if (IsGrouped()) { Group* group = GetGroup(); + if (group) { for (const auto& m : group->members) { if (m && ((m->IsClient() && !noClients) || (m->IsBot() && !noBots))) { @@ -9384,9 +9324,9 @@ std::vector Bot::GatherSpellTargets(bool entireRaid, bool noClients, bool } } else { - valid_spell_targets.emplace_back(this); + valid_spell_targets.emplace_back(this); } - + return valid_spell_targets; } @@ -9832,24 +9772,15 @@ bool Bot::BotHasEnoughMana(uint16 spell_id) { } bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { - if (!tar || !spell_id) { return true; } - if (IsNPC() && CastToNPC()->GetSwarmOwner()) { - return true; - } - std::vector v; - - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { - v = GatherSpellTargets(true); - } - else { - v = GatherGroupSpellTargets(); - } - + uint16 targetID = tar->GetID(); + + v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); + for (Mob* m : v) { if ( m->IsBot() && diff --git a/zone/bot.h b/zone/bot.h index d365d24317..d9340e8a1c 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -455,8 +455,7 @@ class Bot : public NPC { { return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); } void DoAttackRounds(Mob* target, int hand); - std::vector GatherGroupSpellTargets(Mob* target = nullptr, bool noClients = false, bool noBots = false); - std::vector GatherSpellTargets(bool entireRaid = false, bool noClients = false, bool noBots = false, bool noPets = false); + std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool noClients = false, bool noBots = false, bool noPets = false); bool PrecastChecks(Mob* tar, uint16 spellType); bool CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b2705023ea..b5ea1766d5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -350,7 +350,7 @@ bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell ).c_str() ); - const std::vector v = GatherGroupSpellTargets(tar); + const std::vector v = GatherSpellTargets(false, tar); if (!IsCommandedSpell()) { @@ -533,7 +533,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell ); if (botClass != Class::Bard) { - const std::vector v = GatherGroupSpellTargets(tar); + const std::vector v = GatherSpellTargets(false, tar); if (!IsCommandedSpell()) { for (Mob* m : v) { @@ -1264,12 +1264,7 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spell std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); std::vector v; - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { - v = botCaster->GatherSpellTargets(true); - } - else { - v = botCaster->GatherGroupSpellTargets(); - } + v = botCaster->GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); int targetCount = 0; @@ -1313,12 +1308,7 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); std::vector v; - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { - v = botCaster->GatherSpellTargets(true); - } - else { - v = botCaster->GatherGroupSpellTargets(); - } + v = botCaster->GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); int targetCount = 0; @@ -1362,12 +1352,7 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); std::vector v; - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { - v = botCaster->GatherSpellTargets(true); - } - else { - v = botCaster->GatherGroupSpellTargets(); - } + v = botCaster->GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); int targetCount = 0; @@ -1970,7 +1955,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) std::list botSpellListItr = GetPrioritizedBotSpellsBySpellType(botCaster, spellType, tar); if (IsGroupBotSpellType(spellType)) { - const std::vector v = botCaster->GatherGroupSpellTargets(tar); + const std::vector v = botCaster->GatherSpellTargets(false, tar); int countNeedsCured = 0; uint16 countPoisoned = 0; uint16 countDiseased = 0; From 1d0899fa0a4d376c8181958984a8d0566ea7d90f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 09:50:27 -0600 Subject: [PATCH 220/394] Cleanup IsTargetAlreadyReceivingSpell --- zone/bot.cpp | 32 ++++++++++++-------------------- 1 file changed, 12 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a9f455a30f..225f0af148 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9783,33 +9783,25 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { for (Mob* m : v) { if ( - m->IsBot() && - m->IsCasting() && - m->CastToBot()->casting_spell_targetid && - entity_list.GetMobID(m->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(tar->GetID()) && + m->IsBot() && + m->IsCasting() && + m->CastToBot()->casting_spell_targetid && m->CastingSpellID() == spell_id ) { - - return true; - } - else { if (IsGroupSpell(spell_id)) { - if ( - m->IsBot() && - m->IsCasting() && - m->CastToBot()->casting_spell_targetid && - m->CastingSpellID() == spell_id - ) { - - std::vector x = GatherGroupSpellTargets(); + std::vector t = GatherSpellTargets(false, tar); - for (Mob* t : x) { - if (entity_list.GetMobID(t->CastToBot()->casting_spell_targetid) == entity_list.GetMobID(t->GetID())) { - return true; - } + for (Mob* x : t) { + if (x->GetID() == m->CastToBot()->casting_spell_targetid) { + return true; } } } + else { + if (m->CastToBot()->casting_spell_targetid == targetID) { + return true; + } + } } } From 62111da16d137cd790f792ee79e6bc59352483af Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:09:42 -0600 Subject: [PATCH 221/394] Fix ^petsettype to account for usable levels of spells and remove hardcoded level limits. --- .../database_update_manifest_bots.cpp | 1 + common/ruletypes.h | 1 + zone/bot_commands/pet.cpp | 338 ++++++++++++++++-- zone/botspellsai.cpp | 126 ++++--- 4 files changed, 385 insertions(+), 81 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 1b9b2e439e..999f504783 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -252,6 +252,7 @@ UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THE UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|ir') ELSE 'invremove|ir' END WHERE `bot_command`='inventoryremove' AND `aliases` NOT LIKE '%ir%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|iw') ELSE 'invwindow|iw' END WHERE `bot_command`='inventorywindow' AND `aliases` NOT LIKE '%iw%'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|iu') ELSE 'iu' END WHERE `bot_command`='itemuse' AND `aliases` NOT LIKE '%iu%'; +UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|pst') ELSE 'pset||pst' END WHERE `bot_command`='petsettype' AND `aliases` NOT LIKE '%pst%'; UPDATE `bot_command_settings` SET `aliases`= 'mmr' WHERE `bot_command`='maxmeleerange'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|pp') ELSE 'pp' END WHERE `bot_command`='pickpocket' AND `aliases` NOT LIKE '%pp%'; UPDATE `bot_command_settings` SET `aliases`= 'sithp' WHERE `bot_command`='sithppercent'; diff --git a/common/ruletypes.h b/common/ruletypes.h index c113b4dda5..bb7b56340a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -838,6 +838,7 @@ RULE_BOOL(Bots, AllowMagicianEpicPet, false, "If enabled, magician bots can summ RULE_INT(Bots, AllowMagicianEpicPetLevel, 50, "If AllowMagicianEpicPet is enabled, bots can start using their epic pets at this level") RULE_INT(Bots, RequiredMagicianEpicPetItemID, 28034, "If AllowMagicianEpicPet is enabled and this is set, bots will be required to have this item ID equipped to cast their epic. Takes in to account AllowMagicianEpicPetLevel as well. Set to 0 to disable requirement") RULE_STRING(Bots, EpicPetSpellName, "", "'teleport_zone' in the spell to be cast for epic pets. This must be in their spell list to cast.") +RULE_INT(Bots, ReclaimEnergySpellID, 331, "Spell ID for reclaim energy when using ^petsettype. Default 331") RULE_BOOL(Bots, UseSpellPulling, true, "If enabled bots will use a spell to pull when within range. Uses PullSpellID.") RULE_INT(Bots, PullSpellID, 5225, "Default 5225 - Throw Stone. Spell that will be cast to pull by bots") RULE_BOOL(Bots, AllowRangedPulling, true, "If enabled bots will pull with their ranged items if set to ranged.") diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index c8b48368c4..d0345ced26 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -1,4 +1,5 @@ #include "../bot_command.h" +#include "../bot.h" void bot_command_pet(Client *c, const Seperator *sep) { @@ -95,82 +96,175 @@ void bot_command_pet_remove(Client *c, const Seperator *sep) void bot_command_pet_set_type(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_pet_set_type", sep->arg[0], "petsettype")) + if (helper_command_alias_fail(c, "bot_command_pet_set_type", sep->arg[0], "petsettype")) { return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s [type: auto | water | fire | air | earth | monster | epic] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "if set to 'auto', bots will choose their own pet type"); - c->Message(Chat::White, "requires one of the following bot classes:"); - c->Message(Chat::White, "Magician(1)"); + std::vector description = + { + "Allows you to change the type of pet Magician bots will cast" + }; + + std::vector notes = {}; + + std::vector example_format = + { + fmt::format( + "{} [current | water | fire | air | earth | monster | epic] [actionable, default: target]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To set all spawned bots to use Water pets:", + fmt::format( + "{} fire spawned", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To set Magelulz to use Fire pets:", + fmt::format( + "{} fire byname Magelulz", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To check the current pet type for all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + return; } - int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); // this can be expanded without code modification - - std::string pet_arg = sep->arg[1]; + std::string arg1 = sep->arg[1]; + int ab_arg = 2; + bool current_check = false; uint8 pet_type = 255; uint8 level_req = 255; - if (!pet_arg.compare("auto")) { + + if (!arg1.compare("auto")) { pet_type = 0; - level_req = 1; } - else if (!pet_arg.compare("water")) { + else if (!arg1.compare("water")) { pet_type = 1; - level_req = 1; } - else if (!pet_arg.compare("fire")) { + else if (!arg1.compare("fire")) { pet_type = 2; - level_req = 3; } - else if (!pet_arg.compare("air")) { + else if (!arg1.compare("air")) { pet_type = 3; - level_req = 4; } - else if (!pet_arg.compare("earth")) { + else if (!arg1.compare("earth")) { pet_type = 4; - level_req = 5; } - else if (!pet_arg.compare("monster")) { + else if (!arg1.compare("monster")) { pet_type = 5; - level_req = 30; } - else if (!pet_arg.compare("epic")) { - pet_type = 6; + else if (!arg1.compare("epic")) { if (!RuleB(Bots, AllowMagicianEpicPet)) { c->Message(Chat::Yellow, "Epic pets are currently disabled for bots."); return; } - level_req = RuleI(Bots,AllowMagicianEpicPetLevel); + + pet_type = 6; + } + else if (!arg1.compare("current")) { + current_check = true; } - if (pet_type == 255) { - c->Message(Chat::White, "You must specify a pet [type: auto | water | fire | air | earth | monster | epic]"); + if (!current_check && pet_type == 255) { + c->Message( + Chat::Yellow, + fmt::format( + "You must specify a pet [type: auto | water | fire | air | earth | monster | epic]. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); return; } - std::list sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None) - return; + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; - uint16 class_mask = player_class_bitmasks[Class::Magician]; - ActionableBots::Filter_ByClasses(c, sbl, class_mask); - if (sbl.empty()) { - c->Message(Chat::White, "You have no spawned Magician bots"); - return; + if (actionableArg.empty()) { + actionableArg = "target"; } - ActionableBots::Filter_ByMinLevel(c, sbl, level_req); - if (sbl.empty()) { - c->Message(Chat::White, "You have no spawned Magician bots capable of using this pet type: '%s'", pet_arg.c_str()); + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::list sbl; + + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - uint16 reclaim_energy_id = 331; + sbl.remove(nullptr); + + std::string currentType; + uint16 reclaim_energy_id = RuleI(Bots, ReclaimEnergySpellID); + bool isSuccess = false; + uint16 successCount = 0; + Bot* firstFound = nullptr; + for (auto bot_iter : sbl) { - if (!bot_iter) + if (bot_iter->GetClass() != Class::Magician) { + continue; + } + + if (!bot_iter->IsInGroupOrRaid(c)) { continue; + } - if (RuleI(Bots, RequiredMagicianEpicPetItemID) > 0) { + if (pet_type == 6 && RuleI(Bots, RequiredMagicianEpicPetItemID) > 0) { bool has_item = bot_iter->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX; if (!has_item) { @@ -187,11 +281,177 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) } } + if (current_check) { + switch (bot_iter->GetPetChooserID()) { + case 0: + currentType = "auto"; + break; + case SumWater: + currentType = "water"; + break; + case SumFire: + currentType = "fire"; + break; + case SumAir: + currentType = "air"; + break; + case SumEarth: + currentType = "earth"; + break; + case MonsterSum: + currentType = "monster"; + break; + case SumMageMultiElement: + currentType = "epic"; + break; + default: + currentType = "null"; + break; + } + + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I'm currently summoning {} pets.'", + bot_iter->GetCleanName(), + currentType + ).c_str() + ); + + continue; + } + + uint8 airMinLevel = 255; + uint8 fireMinLevel = 255; + uint8 waterMinLevel = 255; + uint8 earthMinLevel = 255; + uint8 monsterMinLevel = 255; + uint8 epicMinLevel = 255; + std::list botSpellList = bot_iter->GetBotSpellsBySpellType(bot_iter, BotSpellTypes::Pet); + + for (const auto& s : botSpellList) { + if (!IsValidSpell(s.SpellId)) { + continue; + } + + if (!IsEffectInSpell(s.SpellId, SE_SummonPet)) { + continue; + } + + auto spell = spells[s.SpellId]; + + if (!strncmp(spell.teleport_zone, "SumWater", 8) && spell.classes[Class::Magician - 1] < waterMinLevel) { + waterMinLevel = spell.classes[Class::Magician - 1]; + } + else if (!strncmp(spell.teleport_zone, "SumFire", 7) && spell.classes[Class::Magician - 1] < fireMinLevel) { + fireMinLevel = spell.classes[Class::Magician - 1]; + } + else if (!strncmp(spell.teleport_zone, "SumAir", 6) && spell.classes[Class::Magician - 1] < airMinLevel) { + airMinLevel = spell.classes[Class::Magician - 1]; + } + else if (!strncmp(spell.teleport_zone, "SumEarth", 8) && spell.classes[Class::Magician - 1] < earthMinLevel) { + earthMinLevel = spell.classes[Class::Magician - 1]; + } + else if (!strncmp(spell.teleport_zone, "MonsterSum", 10) && spell.classes[Class::Magician - 1] < monsterMinLevel) { + monsterMinLevel = spell.classes[Class::Magician - 1]; + } + } + + uint8 minLevel = std::min({ + waterMinLevel, + fireMinLevel, + airMinLevel, + earthMinLevel, + monsterMinLevel + }); + + epicMinLevel = RuleI(Bots, AllowMagicianEpicPetLevel); + + LogTestDebug("{} says, 'minLevel = {} | waterMinLevel = {} | fireMinLevel = {} | airMinLevel = {} | earthMinLevel = {} | monsterMinLevel = {} | epicMinLevel = {}'", + bot_iter->GetCleanName(), + minLevel, + waterMinLevel, + fireMinLevel, + airMinLevel, + earthMinLevel, + monsterMinLevel, + epicMinLevel + ); //deleteme + + switch (pet_type) { + case 0: + level_req = minLevel; + break; + case SumWater: + level_req = waterMinLevel; + break; + case SumFire: + level_req = fireMinLevel; + break; + case SumAir: + level_req = airMinLevel; + break; + case SumEarth: + level_req = earthMinLevel; + break; + case MonsterSum: + level_req = monsterMinLevel; + break; + case SumMageMultiElement: + level_req = epicMinLevel; + break; + default: + break; + } + + if (bot_iter->GetLevel() < level_req) { + continue; + } + bot_iter->SetPetChooserID(pet_type); + if (bot_iter->GetPet()) { auto pet_id = bot_iter->GetPetID(); bot_iter->SetPetID(0); bot_iter->CastSpell(reclaim_energy_id, pet_id); } + + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + + if (current_check) { + return; + } + + if (!isSuccess) { + c->Message(Chat::Yellow, "No bots were selected."); + + return; + } + + if (successCount == 1 && firstFound) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I will now summon {} pets.'", + firstFound->GetCleanName(), + currentType + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots will now summon {} pets.", + successCount, + arg1 + ).c_str() + ); } } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index b5ea1766d5..4ec829a2f5 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -1541,9 +1541,17 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { std::string result; if (botCaster) { + uint8 petType = botCaster->GetPetChooserID(); + uint8 botLevel = botCaster->GetLevel(); bool epicAllowed = false; + std::string epicSpellName = RuleS(Bots, EpicPetSpellName); + + if (epicSpellName.empty()) { + epicSpellName = "SumMageMultiElement"; + } + if (RuleB(Bots, AllowMagicianEpicPet)) { - if (botCaster->GetLevel() >= RuleI(Bots, AllowMagicianEpicPetLevel)) { + if (botLevel >= RuleI(Bots, AllowMagicianEpicPetLevel)) { if (!RuleI(Bots, RequiredMagicianEpicPetItemID)) { epicAllowed = true; } @@ -1557,8 +1565,8 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { } } - if (botCaster->GetPetChooserID() > 0) { - switch (botCaster->GetPetChooserID()) { + if (petType > 0) { + switch (petType) { case SumWater: result = std::string("SumWater"); break; @@ -1576,66 +1584,100 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { break; case SumMageMultiElement: if (epicAllowed) { - result = std::string(RuleS(Bots, EpicPetSpellName)); - - if (result.empty()) { - result = "SumMageMultiElement"; - } + result = epicSpellName; } else { - result = std::string("MonsterSum"); + botCaster->SetPetChooserID(0); } break; } } else { - if (epicAllowed) { - result = std::string(RuleS(Bots, EpicPetSpellName)); + uint8 airMinLevel = 255; + uint8 fireMinLevel = 255; + uint8 waterMinLevel = 255; + uint8 earthMinLevel = 255; + uint8 monsterMinLevel = 255; + uint8 epicMinLevel = 255; + std::list botSpellList = botCaster->GetBotSpellsBySpellType(botCaster, BotSpellTypes::Pet); + + for (const auto& s : botSpellList) { + if (!IsValidSpell(s.SpellId)) { + continue; + } - if (result.empty()) { - result = "SumMageMultiElement"; + if (!IsEffectInSpell(s.SpellId, SE_SummonPet)) { + continue; } - } - else { - if (botCaster->GetLevel() == 2) { - result = std::string("SumWater"); + + auto spell = spells[s.SpellId]; + + if (!strncmp(spell.teleport_zone, "SumWater", 8) && spell.classes[Class::Magician - 1] < waterMinLevel) { + waterMinLevel = spell.classes[Class::Magician - 1]; } - else if (botCaster->GetLevel() == 3) { - result = std::string("SumFire"); + else if (!strncmp(spell.teleport_zone, "SumFire", 7) && spell.classes[Class::Magician - 1] < fireMinLevel) { + fireMinLevel = spell.classes[Class::Magician - 1]; } - else if (botCaster->GetLevel() == 4) { - result = std::string("SumAir"); + else if (!strncmp(spell.teleport_zone, "SumAir", 6) && spell.classes[Class::Magician - 1] < airMinLevel) { + airMinLevel = spell.classes[Class::Magician - 1]; } - else if (botCaster->GetLevel() == 5) { - result = std::string("SumEarth"); + else if (!strncmp(spell.teleport_zone, "SumEarth", 8) && spell.classes[Class::Magician - 1] < earthMinLevel) { + earthMinLevel = spell.classes[Class::Magician - 1]; } - else { - int counter; + else if (!strncmp(spell.teleport_zone, "MonsterSum", 10) && spell.classes[Class::Magician - 1] < monsterMinLevel) { + monsterMinLevel = spell.classes[Class::Magician - 1]; + } + else if (!strncmp(spell.teleport_zone, epicSpellName.c_str(), epicSpellName.length()) && spell.classes[Class::Magician - 1] < epicMinLevel) { + epicMinLevel = spell.classes[Class::Magician - 1]; + } + } - if (botCaster->GetLevel() < 30) { - counter = zone->random.Int(1, 4); - } - else { - counter = zone->random.Int(1, 5); - } + if (epicAllowed) { + epicMinLevel = std::max(int(epicMinLevel), RuleI(Bots, AllowMagicianEpicPetLevel)); + + if (botLevel >= epicMinLevel) { + result = epicSpellName; + } + } + else { + bool found = false; + uint8 count = 0; + + while (count <= 4 && !found) { + int counter = zone->random.Int(1, 4); switch (counter) { - case 1: - result = std::string("SumWater"); - break; - case 2: - result = std::string("SumFire"); + case SumWater: + if (botLevel >= waterMinLevel) { + result = std::string("SumWater"); + } + + found = true; break; - case 3: - result = std::string("SumAir"); + case SumFire: + if (botLevel >= fireMinLevel) { + result = std::string("SumFire"); + } + + found = true; break; - case 4: - result = std::string("SumEarth"); + case SumAir: + if (botLevel >= airMinLevel) { + result = std::string("SumAir"); + } + + found = true; break; - default: - result = std::string("MonsterSum"); + case SumEarth: + if (botLevel >= earthMinLevel) { + result = std::string("SumEarth"); + } + + found = true; break; } + + ++count; } } } From a46478468c2b3eec8f0d6cd6b1eb560fb2fad6e2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 13:10:50 -0600 Subject: [PATCH 222/394] Remove Bot_AICheckCloseBeneficialSpells and use AttemptCloseBeneficialSpells for better performance --- zone/bot.cpp | 102 +++++++++++++++++-------------------------- zone/bot.h | 3 +- zone/botspellsai.cpp | 27 ++++++++++++ zone/entity.h | 1 - 4 files changed, 70 insertions(+), 63 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 225f0af148..2208b4ba19 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7205,72 +7205,47 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType) { - - if (IsBotSpellTypeDetrimental(spellType, caster->GetClass())) { - LogError("[EntityList::Bot_AICheckCloseBeneficialSpells] detrimental spells requested"); - return false; - } - - std::vector v; - - if (IsGroupTargetOnlyBotSpellType(spellType)) { - v = caster->GatherGroupSpellTargets(); - } - else { - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { - v = caster->GatherSpellTargets(true); - } - else { - v = caster->GatherGroupSpellTargets(); - } - } - +bool Bot::AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector targetList) { + bool result = false; Mob* tar = nullptr; - for (Mob* m : v) { + for (Mob* m : targetList) { tar = m; if (!tar) { continue; } - if (caster == tar) { - continue; - } - - uint8 chanceToCast = caster->IsEngaged() ? caster->GetChanceToCastBySpellType(spellType) : 100; - - if (!caster->PrecastChecks(tar, spellType)) { - continue; - } - - if (caster->AICastSpell(tar, chanceToCast, spellType)) { - return true; + if (IsGroupTargetOnlyBotSpellType(spellType)) { + if (raid && (raid->GetGroup(GetName()) == raid->GetGroup(tar->GetName()))) { + continue; + } } - if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { - tar = m->GetPet(); + result = AttemptAICastSpell(spellType, tar); - if (!tar) { - continue; - } + if (!result) { + if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { + tar = m->GetPet(); - if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { - continue; - } + if (!tar) { + continue; + } - if (!caster->PrecastChecks(tar, spellType)) { - continue; - } + if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { + continue; + } - if (caster->AICastSpell(tar, chanceToCast, spellType)) { - return true; + result = AttemptAICastSpell(spellType, tar); } } + + if (result) { + break; + } } - return false; + return result; } Mob* EntityList::GetMobByBotID(uint32 botID) { @@ -10835,31 +10810,36 @@ std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { return castOrder; } -bool Bot::AttemptAICastSpell(uint16 spellType) { +bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { bool result = false; - Mob* tar = GetTarget(); + if (!tar) { + if (GetTarget()) { + tar = GetTarget(); + } + else { + if (!IsBotSpellTypeBeneficial(spellType)) { + return result; + } + } + } if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme return result; } - if (IsBotSpellTypeBeneficial(spellType, GetClass())) { - if (!PrecastChecks(this, spellType) || !AICastSpell(this, GetChanceToCastBySpellType(spellType), spellType)) { - if (GetClass() == Class::Bard) { - return result; - } + if (IsBotSpellTypeBeneficial(spellType)) { + if (GetClass() == Class::Bard) { + tar = this; + } - if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { - if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, GetChanceToCastBySpellType(spellType), spellType)) { - return result; - } - } + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + return result; } } else { - if (!tar || !PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { return result; } } diff --git a/zone/bot.h b/zone/bot.h index d9340e8a1c..4920e808d9 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -398,9 +398,10 @@ class Bot : public NPC { // AI Methods bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); - bool AttemptAICastSpell(uint16 spellType); + bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id); + bool AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector targetList); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 4ec829a2f5..2df5bfedc2 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -633,6 +633,9 @@ bool Bot::AI_PursueCastCheck() { } auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Pursue); + Mob* tar = nullptr; + Raid* raid = GetRaid(); + std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { @@ -650,6 +653,10 @@ bool Bot::AI_PursueCastCheck() { result = AttemptAICastSpell(currentCast.spellType); + if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { + result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + } + if (result) { break; } @@ -692,6 +699,9 @@ bool Bot::AI_IdleCastCheck() { } auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Idle); + Mob* tar = nullptr; + Raid* raid = GetRaid(); + std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { @@ -711,11 +721,21 @@ bool Bot::AI_IdleCastCheck() { continue; } + if (!IsBotSpellTypeBeneficial(currentCast.spellType)) { + continue; + } + result = AttemptAICastSpell(currentCast.spellType); if (result) { break; } + + result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + + if (result) { + break; + } } if (!AIautocastspell_timer->Enabled()) { @@ -746,6 +766,9 @@ bool Bot::AI_EngagedCastCheck() { } auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Engaged); + Mob* tar = nullptr; + Raid* raid = GetRaid(); + std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { @@ -763,6 +786,10 @@ bool Bot::AI_EngagedCastCheck() { result = AttemptAICastSpell(currentCast.spellType); + if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { + result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + } + if (result) { break; } diff --git a/zone/entity.h b/zone/entity.h index b55c1c7a63..c9d48bc7d0 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -628,7 +628,6 @@ class EntityList Client* GetBotOwnerByBotID(const uint32 bot_id); std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); - bool Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, uint16 spellType); // TODO: Evaluate this closesly in hopes to eliminate void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff void ScanCloseClientMobs(std::unordered_map& close_mobs, Mob* scanning_mob); From e930a1b7fc5ee1ecf8af8ad2ddfd0ecec97d24dc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 21:19:22 -0600 Subject: [PATCH 223/394] remove default hold for resist buffa --- zone/mob.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index a7f81ab162..321ea6af21 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9316,7 +9316,8 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case BotSpellTypes::PetBuffs: case BotSpellTypes::InCombatBuff: case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::DamageShields: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: return false; case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::GroupHeals: From c02259aceb18d37cd7fb61b1c8992e22ea1d45b1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 21:20:38 -0600 Subject: [PATCH 224/394] move IsValidSpellRange further down castchecks --- zone/bot.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2208b4ba19..616468225b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9500,11 +9500,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (!AECheck && !IsValidSpellRange(spell_id, tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - if ( IsBeneficialSpell(spell_id) && ( @@ -9571,6 +9566,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if (spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; From d05dcb5d600c54291f478ebe1c8943bf84f1e2ea Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 20 Dec 2024 21:21:14 -0600 Subject: [PATCH 225/394] raid optimizations --- common/ruletypes.h | 8 ++++---- zone/attack.cpp | 4 ++-- zone/bot.cpp | 27 +++++++++++++-------------- zone/bot.h | 8 ++++---- zone/bot_commands/cast.cpp | 4 ++-- zone/bot_commands/depart.cpp | 2 +- zone/botspellsai.cpp | 20 ++++++++++++-------- zone/mob.cpp | 28 ++++++++++++++++------------ zone/mob.h | 2 +- 9 files changed, 55 insertions(+), 48 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index bb7b56340a..415b070e81 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -815,10 +815,10 @@ RULE_INT(Bots, PercentChanceToCastGroupCure, 75, "The chance for a bot to attemp RULE_INT(Bots, PercentChanceToCastHateRedux, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastFear, 75, "The chance for a bot to attempt to cast the given spell type in combat. Default 75%.") RULE_INT(Bots, PercentChanceToCastOtherType, 90, "The chance for a bot to attempt to cast the remaining spell types in combat. Default 0-%.") -RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 250, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 250ms.") -RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2000ms.") -RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 125, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 125ms.") -RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MinDelayBetweenInCombatCastAttempts, 500, "The minimum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 500ms.") +RULE_INT(Bots, MaxDelayBetweenInCombatCastAttempts, 2000, "The maximum delay in milliseconds between cast attempts while in-combat. This is rolled between the min and max. Default 2500ms.") +RULE_INT(Bots, MinDelayBetweenOutCombatCastAttempts, 1000, "The minimum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 1000ms.") +RULE_INT(Bots, MaxDelayBetweenOutCombatCastAttempts, 2500, "The maximum delay in milliseconds between cast attempts while out-of-combat. This is rolled between the min and max. Default 2500ms.") RULE_INT(Bots, MezChance, 35, "35 Default. Chance for a bot to attempt to Mez a target after validating it is eligible.") RULE_INT(Bots, AEMezChance, 35, "35 Default. Chance for a bot to attempt to AE Mez targets after validating they are eligible.") RULE_INT(Bots, MezSuccessDelay, 3500, "3500 (3.5 sec) Default. Delay between successful Mez attempts.") diff --git a/zone/attack.cpp b/zone/attack.cpp index d1c862096b..130d5e2934 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2624,10 +2624,10 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy bool owner_in_group = false; if ( - give_exp->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) || + give_exp->IsInGroupOrRaid(give_exp->GetUltimateOwner(), nullptr, RuleB(Bots, SameRaidGroupForXP)) || give_exp->IsPet() && give_exp->GetOwner() && - give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) + give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), nullptr, RuleB(Bots, SameRaidGroupForXP)) ) { owner_in_group = true; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 616468225b..7ec75f3de5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2093,7 +2093,7 @@ void Bot::AI_Process() else { follow_mob = entity_list.GetMob(GetFollowID()); - if (!follow_mob || !IsInGroupOrRaid(follow_mob)) { + if (!follow_mob || !IsInGroupOrRaid(follow_mob, raid)) { follow_mob = leash_owner; } } @@ -2367,7 +2367,7 @@ void Bot::AI_Process() // AUTO DEFEND - if (TryAutoDefend(bot_owner, leash_distance) ) { + if (TryAutoDefend(bot_owner, leash_distance, raid) ) { return; } @@ -2466,7 +2466,7 @@ bool Bot::TryIdleChecks(float fm_distance) { // This is as close as I could get without modifying the aggro mechanics and making it an expensive process... // 'class Client' doesn't make use of hate_list -bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { +bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance, Raid* raid) { if (RuleB(Bots, AllowOwnerOptionAutoDefend) && bot_owner->GetBotOption(Client::booAutoDefend)) { @@ -2503,14 +2503,13 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { bool assisteeFound = false; if (IsRaidGrouped()) { - Raid* r = entity_list.GetRaidByBot(this); - if (r) { - for (const auto& m : r->members) { + if (raid) { + for (const auto& m : raid->members) { if ( m.member && m.member->IsClient() && m.member->GetAggroCount() && - r->IsAssister(m.member_name) + raid->IsAssister(m.member_name) ) { temp_xhaters = m.member->GetXTargetAutoMgr(); @@ -2554,7 +2553,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { if (bot_owner->GetAssistee()) { Client* c = entity_list.GetClientByCharID(bot_owner->GetAssistee()); - if (bot_owner->IsInGroupOrRaid(c) && c->GetAggroCount()) { + if (bot_owner->IsInGroupOrRaid(c, raid) && c->GetAggroCount()) { tempHaters = bot_owner->GetXTargetAutoMgr(); if (tempHaters && !tempHaters->empty()) { @@ -7222,7 +7221,7 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector } } - result = AttemptAICastSpell(spellType, tar); + result = AttemptAICastSpell(spellType, tar, raid); if (!result) { if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { @@ -7236,7 +7235,7 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector continue; } - result = AttemptAICastSpell(spellType, tar); + result = AttemptAICastSpell(spellType, tar, raid); } } @@ -10810,7 +10809,7 @@ std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { return castOrder; } -bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { +bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar, Raid* raid) { bool result = false; if (!tar) { @@ -10834,12 +10833,12 @@ bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { tar = this; } - if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType, raid)) { return result; } } else { - if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType, raid)) { return result; } } @@ -10995,7 +10994,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { !RuleB(Bots, EnableBotTGB) && IsGroupSpell(forcedSpellID) && !IsTGBCompatibleSpell(forcedSpellID) && - !IsInGroupOrRaid(tar, true) + !IsInGroupOrRaid(tar, nullptr, true) ) { LogTestDebug("{} failed TGB for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme return false; diff --git a/zone/bot.h b/zone/bot.h index 4920e808d9..eb7a9ae6cc 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -397,8 +397,8 @@ class Bot : public NPC { void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); // AI Methods - bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); - bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr); + bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, Raid* raid = nullptr, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); + bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr, Raid* raid = nullptr); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id); bool AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector targetList); @@ -597,7 +597,7 @@ class Bot : public NPC { static std::list GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect); static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType); static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); - static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); + static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, Raid* raid = nullptr, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); @@ -990,7 +990,7 @@ class Bot : public NPC { bool TryFacingTarget(Mob* tar); bool TryPursueTarget(float leash_distance, glm::vec3& Goal); bool TryMeditate(); - bool TryAutoDefend(Client* bot_owner, float leash_distance); + bool TryAutoDefend(Client* bot_owner, float leash_distance, Raid* raid = nullptr); bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); bool TryBardMovementCasts(); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index d02c9f2a92..b62162e751 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -506,7 +506,7 @@ void bot_command_cast(Client* c, const Seperator* sep) if ( IsBotSpellTypeBeneficial(spellType) && !RuleB(Bots, CrossRaidBuffingAndHealing) && - !bot_iter->IsInGroupOrRaid(newTar, true) + !bot_iter->IsInGroupOrRaid(newTar, nullptr, true) ) { continue; } @@ -588,7 +588,7 @@ void bot_command_cast(Client* c, const Seperator* sep) LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on [{}]'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); - if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { + if (bot_iter->AICastSpell(newTar, 100, spellType, nullptr, subTargetType, subType)) { if (!firstFound) { firstFound = bot_iter; } diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index e7c50156ac..6fb4fdb29e 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -164,7 +164,7 @@ void bot_command_depart(Client* c, const Seperator* sep) std::map> listZones; for (auto bot_iter : sbl) { - if (!bot_iter->IsInGroupOrRaid(tar, !single)) { + if (!bot_iter->IsInGroupOrRaid(tar, nullptr, !single)) { continue; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 2df5bfedc2..c637f2c89e 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -21,7 +21,7 @@ #include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/npc_spells_repository.h" -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType, uint16 subType) { +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, Raid* raid, uint16 subTargetType, uint16 subType) { if (!tar) { return false; } @@ -217,7 +217,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; } - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, (IsAEBotSpellType(spellType) || subTargetType == CommandedSubTypes::AETarget), subTargetType, subType); + std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, (IsAEBotSpellType(spellType) || subTargetType == CommandedSubTypes::AETarget), raid, subTargetType, subType); for (const auto& s : botSpellList) { @@ -651,7 +651,7 @@ bool Bot::AI_PursueCastCheck() { continue; } - result = AttemptAICastSpell(currentCast.spellType); + result = AttemptAICastSpell(currentCast.spellType, nullptr, raid); if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); @@ -725,7 +725,7 @@ bool Bot::AI_IdleCastCheck() { continue; } - result = AttemptAICastSpell(currentCast.spellType); + result = AttemptAICastSpell(currentCast.spellType, nullptr, raid); if (result) { break; @@ -784,7 +784,7 @@ bool Bot::AI_EngagedCastCheck() { continue; } - result = AttemptAICastSpell(currentCast.spellType); + result = AttemptAICastSpell(currentCast.spellType, nullptr, raid); if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); @@ -1007,9 +1007,13 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp return result; } -std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE, uint16 subTargetType, uint16 subType) { +std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE, Raid* raid, uint16 subTargetType, uint16 subType) { std::list result; + if (!raid) { + raid = botCaster->GetRaid(); + } + if (botCaster && botCaster->AI_HasSpells()) { std::vector botSpellList = botCaster->AIBot_spells; @@ -1050,7 +1054,7 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa !RuleB(Bots, EnableBotTGB) && IsGroupSpell(botSpellList[i].spellid) && !IsTGBCompatibleSpell(botSpellList[i].spellid) && - !botCaster->IsInGroupOrRaid(tar, true) + !botCaster->IsInGroupOrRaid(tar, raid, true) ) { continue; } @@ -2009,7 +2013,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { return result; } -BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) { //TODO bot rewrite - add raid nd target list? BotSpell_wPriority result; result.SpellId = 0; diff --git a/zone/mob.cpp b/zone/mob.cpp index 321ea6af21..1814414e84 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9897,7 +9897,7 @@ void Mob::ClearDataBucketCache() } } -bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { +bool Mob::IsInGroupOrRaid(Mob *other, Raid* raid, bool sameRaidGroup) { if (!other || !IsOfClientBotMerc() || !other->IsOfClientBotMerc()) { return false; } @@ -9906,29 +9906,33 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { return true; } - auto* r = GetRaid(); - auto* rO = other->GetRaid(); + if (IsRaidGrouped() && !raid) { + raid = GetRaid(); - if (r) { - if (!rO || r != rO) { + if (!raid) { return false; } + } - auto rG = r->GetGroup(GetCleanName()); - auto rOG = rO->GetGroup(other->GetCleanName()); + if (raid) { + if (!other->IsRaidGrouped()) { + return false; + } + auto rGroup = raid->GetGroup(GetCleanName()); + auto rOGroup = raid->GetGroup(other->GetCleanName()); - if (rG == RAID_GROUPLESS || rOG == RAID_GROUPLESS || (sameRaidGroup && rG != rOG)) { + if (rGroup == RAID_GROUPLESS || rOGroup == RAID_GROUPLESS || (sameRaidGroup && rGroup != rOGroup)) { return false; } return true; } else { - auto* g = GetGroup(); - auto* gO = other->GetGroup(); + auto* group = GetGroup(); + auto* groupOther = other->GetGroup(); - if (g) { - if (!gO || g != gO) { + if (group) { + if (!groupOther || group != groupOther) { return false; } diff --git a/zone/mob.h b/zone/mob.h index e38e13337a..90dbd7b0f3 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -779,7 +779,7 @@ class Mob : public Entity { virtual bool HasGroup() = 0; virtual Raid* GetRaid() = 0; virtual Group* GetGroup() = 0; - bool IsInGroupOrRaid(Mob* other, bool sameRaidGroup = false); + bool IsInGroupOrRaid(Mob* other, Raid* raid = nullptr, bool sameRaidGroup = false); //Faction virtual inline int32 GetPrimaryFaction() const { return 0; } From 600376274fbb4c3dfa53e194cef3b0efb504626f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 21 Dec 2024 23:47:30 -0600 Subject: [PATCH 226/394] correct name checking to match players --- zone/bot_command.cpp | 18 ++++++------------ zone/bot_commands/bot.cpp | 24 +++++++++--------------- 2 files changed, 15 insertions(+), 27 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 6e86ecdd41..482cab3e32 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1621,33 +1621,27 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas bot_owner->Message( Chat::Yellow, fmt::format( - "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z' and it must be between 4 and 15 characters. Mixed case {} allowed.", + "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z' and it must be between 4 and 15 characters with no spaces. Mixed case {} allowed.", bot_name, RuleB(Bots, AllowCamelCaseNames) ? "is" : "is not" ).c_str() ); + return bot_id; } bool available_flag = false; - if (!database.botdb.QueryNameAvailablity(bot_name, available_flag)) { - bot_owner->Message( - Chat::Yellow, - fmt::format( - "'{}' is already in use or an invalid name.", - bot_name - ).c_str() - ); - return bot_id; - } + + !database.botdb.QueryNameAvailablity(bot_name, available_flag); if (!available_flag) { bot_owner->Message( Chat::Yellow, fmt::format( - "The name '{}' is already being used. Please choose a different name", + "The name '{}' is already being used or prohibited. Please choose a different name", bot_name ).c_str() ); + return bot_id; } diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 78921d9a05..c836888b27 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -121,35 +121,29 @@ void bot_command_clone(Client *c, const Seperator *sep) if (!Bot::IsValidName(bot_name)) { c->Message( - Chat::White, + Chat::Yellow, fmt::format( - "'{}' is an invalid name. You may only use characters 'A-Z', 'a-z' and '_'.", - bot_name + "'{}' is an invalid name. You may only use characters 'A-Z' or 'a-z' and it must be between 4 and 15 characters. Mixed case {} allowed.", + bot_name, RuleB(Bots, AllowCamelCaseNames) ? "is" : "is not" ).c_str() ); + return; } bool available_flag = false; - if (!database.botdb.QueryNameAvailablity(bot_name, available_flag)) { - c->Message( - Chat::White, - fmt::format( - "Failed to query name availability for '{}'.", - bot_name - ).c_str() - ); - return; - } + + !database.botdb.QueryNameAvailablity(bot_name, available_flag); if (!available_flag) { c->Message( - Chat::White, + Chat::Yellow, fmt::format( - "The name '{}' is already being used. Please choose a different name.", + "The name '{}' is already being used or prohibited. Please choose a different name", bot_name ).c_str() ); + return; } From 2460f78015ecf3178cc9270f0b92b849e79fd872 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:05:45 -0600 Subject: [PATCH 227/394] more name checks and add proper soft deletes to bots --- .../base/base_bot_data_repository.h | 19 ++++++++--- zone/bot.cpp | 32 ++++++++++--------- zone/bot_database.cpp | 7 ++-- 3 files changed, 37 insertions(+), 21 deletions(-) diff --git a/common/repositories/base/base_bot_data_repository.h b/common/repositories/base/base_bot_data_repository.h index eb0cdcb991..2cd4b1d041 100644 --- a/common/repositories/base/base_bot_data_repository.h +++ b/common/repositories/base/base_bot_data_repository.h @@ -349,14 +349,25 @@ class BaseBotDataRepository { int bot_data_id ) { - auto results = db.QueryDatabase( - fmt::format( + std::string query; + + if (RuleB(Bots, BotSoftDeletes)) { + query = fmt::format( + "UPDATE {} SET name = SUBSTRING(CONCAT(name, '-deleted-', UNIX_TIMESTAMP()), 1, 64) WHERE {} = {}", + TableName(), + PrimaryKey(), + bot_data_id + ); + } + else { + query = fmt::format( "DELETE FROM {} WHERE {} = {}", TableName(), PrimaryKey(), bot_data_id - ) - ); + ); + } + auto results = db.QueryDatabase(query); return (results.Success() ? results.RowsAffected() : 0); } diff --git a/zone/bot.cpp b/zone/bot.cpp index 7ec75f3de5..abd437c26a 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1328,25 +1328,27 @@ bool Bot::IsValidName() bool Bot::IsValidName(std::string& name) { - if (name.empty() || name.length() < 4 || name.length() > 15) { + if (name.empty()) { // can't be empty return false; } - if (!isupper(name[0])) { - return false; + if (islower(name[0])) { // capitalize first letter if not + name[0] = toupper(name[0]); } - for (char c : name.substr(1)) { - if (c == '_') { - return false; - } + if (!EQ::ValueWithin(name.size(), 4, 15)) { // must be between 4 and 15 characters + return false; + } - if (!isalpha(c)) { - return false; - } + if (std::any_of(name.begin(), name.end(), [](char c) { return c == ' ' || c == '_'; })) { // cannot contain spaces or _ + return false; + } - if (!RuleB(Bots, AllowCamelCaseNames) && !islower(c)) { - return false; + if (!RuleB(Bots, AllowCamelCaseNames)) { + for (int i = 1; i < name.size(); ++i) { + if (isupper(name[i])) { + return false; + } } } @@ -1450,10 +1452,10 @@ bool Bot::DeleteBot() if (!database.botdb.DeleteBotBlockedBuffs(GetBotID())) { return false; } + } - if (!database.botdb.DeleteBot(GetBotID())) { - return false; - } + if (!database.botdb.DeleteBot(GetBotID())) { + return false; } return true; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index dc7bcf38d0..84fa0dfa46 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -186,6 +186,7 @@ bool BotDatabase::QueryNameAvailablity(const std::string& bot_name, bool& availa if ( bot_name.empty() || bot_name.size() > 60 || + !database.CheckNameFilter(bot_name) || database.IsNameUsed(bot_name) ) { return false; @@ -246,6 +247,8 @@ bool BotDatabase::LoadBotsList(const uint32 owner_id, std::list Date: Sun, 22 Dec 2024 00:22:10 -0600 Subject: [PATCH 228/394] organize some checks in IsImmuneToBotSpell --- zone/botspellsai.cpp | 99 ++++++++++++++++++-------------------------- zone/spells.cpp | 33 ++++++--------- 2 files changed, 54 insertions(+), 78 deletions(-) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index c637f2c89e..50980b6646 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -21,7 +21,7 @@ #include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/npc_spells_repository.h" -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, Raid* raid, uint16 subTargetType, uint16 subType) { +bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType, uint16 subType) { if (!tar) { return false; } @@ -217,7 +217,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, Raid* raid, uin break; } - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, (IsAEBotSpellType(spellType) || subTargetType == CommandedSubTypes::AETarget), raid, subTargetType, subType); + std::vector botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, (IsAEBotSpellType(spellType) || subTargetType == CommandedSubTypes::AETarget), subTargetType, subType); for (const auto& s : botSpellList) { @@ -277,7 +277,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, Raid* raid, uin } bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); + std::vector botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); for (const auto& s : botSpellList) { if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { @@ -352,7 +352,6 @@ bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell const std::vector v = GatherSpellTargets(false, tar); - if (!IsCommandedSpell()) { for (Mob* m : v) { SetBotSpellRecastTimer(spellType, m, true); @@ -464,7 +463,7 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell } if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { - std::list botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); + std::vector botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); for (const auto& s : botSpellList) { if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { @@ -624,7 +623,7 @@ bool Bot::AI_PursueCastCheck() { if (GetTarget() && AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Pursue autocast check triggered: [{}]", GetCleanName()); - LogBotPreChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); //deleteme AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -634,12 +633,10 @@ bool Bot::AI_PursueCastCheck() { auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Pursue); Mob* tar = nullptr; - Raid* raid = GetRaid(); - std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + //LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme continue; } @@ -651,10 +648,10 @@ bool Bot::AI_PursueCastCheck() { continue; } - result = AttemptAICastSpell(currentCast.spellType, nullptr, raid); + result = AttemptAICastSpell(currentCast.spellType, nullptr); if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { - result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + result = AttemptCloseBeneficialSpells(currentCast.spellType); } if (result) { @@ -680,7 +677,7 @@ bool Bot::AI_IdleCastCheck() { if (AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Non-Engaged autocast check triggered: [{}]", GetCleanName()); - LogBotPreChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); //deleteme AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -700,12 +697,10 @@ bool Bot::AI_IdleCastCheck() { auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Idle); Mob* tar = nullptr; - Raid* raid = GetRaid(); - std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + //LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme continue; } @@ -725,13 +720,13 @@ bool Bot::AI_IdleCastCheck() { continue; } - result = AttemptAICastSpell(currentCast.spellType, nullptr, raid); + result = AttemptAICastSpell(currentCast.spellType, nullptr); if (result) { break; } - result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + result = AttemptCloseBeneficialSpells(currentCast.spellType); if (result) { break; @@ -757,7 +752,7 @@ bool Bot::AI_EngagedCastCheck() { if (GetTarget() && AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Engaged autocast check triggered: [{}]", GetCleanName()); - LogBotPreChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); //deleteme AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -767,12 +762,10 @@ bool Bot::AI_EngagedCastCheck() { auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Engaged); Mob* tar = nullptr; - Raid* raid = GetRaid(); - std::vector v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + //LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme continue; } @@ -784,10 +777,10 @@ bool Bot::AI_EngagedCastCheck() { continue; } - result = AttemptAICastSpell(currentCast.spellType, nullptr, raid); + result = AttemptAICastSpell(currentCast.spellType, nullptr); if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { - result = AttemptCloseBeneficialSpells(currentCast.spellType, raid, v); + result = AttemptCloseBeneficialSpells(currentCast.spellType); } if (result) { @@ -1007,12 +1000,8 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp return result; } -std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE, Raid* raid, uint16 subTargetType, uint16 subType) { - std::list result; - - if (!raid) { - raid = botCaster->GetRaid(); - } +std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE, uint16 subTargetType, uint16 subType) { + std::vector result; if (botCaster && botCaster->AI_HasSpells()) { std::vector botSpellList = botCaster->AIBot_spells; @@ -1054,7 +1043,7 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa !RuleB(Bots, EnableBotTGB) && IsGroupSpell(botSpellList[i].spellid) && !IsTGBCompatibleSpell(botSpellList[i].spellid) && - !botCaster->IsInGroupOrRaid(tar, raid, true) + !botCaster->IsInGroupOrRaid(tar, true) ) { continue; } @@ -1077,17 +1066,15 @@ std::list Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa botSpell.ManaCost = botSpellList[i].manacost; botSpell.Priority = botSpellList[i].priority; - result.push_back(botSpell); + result.emplace_back(botSpell); } } } if (result.size() > 1) { - result.sort( - [](BotSpell_wPriority const& l, BotSpell_wPriority const& r) { - return l.Priority < r.Priority; - } - ); + std::sort(result.begin(), result.end(), [](BotSpell_wPriority const& l, BotSpell_wPriority const& r) { + return l.Priority < r.Priority; + }); } } @@ -1293,10 +1280,7 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spell if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); - std::vector v; - - v = botCaster->GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); - + int targetCount = 0; for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1305,7 +1289,7 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spell if (!botCaster->IsCommandedSpell()) { targetCount = 0; - for (Mob* m : v) { + for (Mob* m : botCaster->GetSpellTargetList()) { if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { ++targetCount; } @@ -1337,9 +1321,6 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); - std::vector v; - - v = botCaster->GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); int targetCount = 0; @@ -1349,7 +1330,7 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint if (!botCaster->IsCommandedSpell()) { targetCount = 0; - for (Mob* m : v) { + for (Mob* m : botCaster->GetSpellTargetList()) { if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { ++targetCount; } @@ -1381,10 +1362,7 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint if (botCaster) { std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); - std::vector v; - - v = botCaster->GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); - + int targetCount = 0; for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { @@ -1393,7 +1371,7 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint if (!botCaster->IsCommandedSpell()) { targetCount = 0; - for (Mob* m : v) { + for (Mob* m : botCaster->GetSpellTargetList()) { if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { ++targetCount; } @@ -2013,7 +1991,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { return result; } -BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) { //TODO bot rewrite - add raid nd target list? +BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) { BotSpell_wPriority result; result.SpellId = 0; @@ -2025,22 +2003,27 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) } if (botCaster) { - std::list botSpellListItr = GetPrioritizedBotSpellsBySpellType(botCaster, spellType, tar); + std::vector botSpellListItr = GetPrioritizedBotSpellsBySpellType(botCaster, spellType, tar); if (IsGroupBotSpellType(spellType)) { - const std::vector v = botCaster->GatherSpellTargets(false, tar); int countNeedsCured = 0; uint16 countPoisoned = 0; uint16 countDiseased = 0; uint16 countCursed = 0; uint16 countCorrupted = 0; - for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + for (std::vector::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { if (!IsValidSpell(itr->SpellId) || !IsGroupSpell(itr->SpellId)) { continue; } - for (Mob* m : v) { + for (Mob* m : botCaster->GetSpellTargetList()) { + if (IsGroupBotSpellType(spellType)) { + if (!botCaster->IsInGroupOrRaid(m, true)) { + continue; + } + } + if (botCaster->IsCommandedSpell() || botCaster->GetNeedsCured(m)) { if (botCaster->CastChecks(itr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { if (m->FindType(SE_PoisonCounter)) { @@ -2074,7 +2057,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) } } else { - for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + for (std::vector::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { if (!IsValidSpell(itr->SpellId) || IsGroupSpell(itr->SpellId)) { continue; } @@ -2825,7 +2808,7 @@ void Bot::CheckBotSpells() { for (const auto& s : spellList) { if (!IsValidSpell(s.spell_id)) { - LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); //deleteme + //LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); //deleteme continue; } @@ -2833,7 +2816,7 @@ void Bot::CheckBotSpells() { spell_id = spell.id; if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) { - LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); //deleteme + //LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); //deleteme } else { if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) { diff --git a/zone/spells.cpp b/zone/spells.cpp index 6dbd3e122b..3559e56d31 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -7580,19 +7580,15 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) return true; } - if (IsDispelSpell(spell_id) && GetSpecialAbility(SpecialAbility::DispellImmunity)) { - return false; + if (GetSpecialAbility(SpecialAbility::DispellImmunity) && IsDispelSpell(spell_id)) { + return true; } - if (IsHarmonySpell(spell_id) && GetSpecialAbility(SpecialAbility::PacifyImmunity)) { - return false; + if (GetSpecialAbility(SpecialAbility::PacifyImmunity) && IsHarmonySpell(spell_id)) { + return true; } - if (IsMesmerizeSpell(spell_id)) { - if (GetSpecialAbility(SpecialAbility::MesmerizeImmunity)) { - return true; - } - + if (!GetSpecialAbility(SpecialAbility::MesmerizeImmunity) && IsMesmerizeSpell(spell_id)) { // check max level for spell effect_index = GetSpellEffectIndex(spell_id, SE_Mez); assert(effect_index >= 0); @@ -7611,13 +7607,10 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) } // client vs client fear - if (IsEffectInSpell(spell_id, SE_Fear)) { + if (!GetSpecialAbility(SpecialAbility::FearImmunity) && IsEffectInSpell(spell_id, SE_Fear)) { effect_index = GetSpellEffectIndex(spell_id, SE_Fear); - if (GetSpecialAbility(SpecialAbility::FearImmunity)) { - return true; - } - else if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) { + if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) { LogSpells("Clients cannot fear eachother!"); caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up return true; @@ -7630,10 +7623,7 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) } } - if (IsCharmSpell(spell_id)) { - if (GetSpecialAbility(SpecialAbility::CharmImmunity)) { - return true; - } + if (!GetSpecialAbility(SpecialAbility::CharmImmunity) && IsCharmSpell(spell_id)) { if (this == caster) { return true; @@ -7651,8 +7641,11 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) } if ( - IsEffectInSpell(spell_id, SE_Root) || - IsEffectInSpell(spell_id, SE_MovementSpeed) + GetSpecialAbility(SpecialAbility::SnareImmunity) && + ( + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) ) { if (GetSpecialAbility(SpecialAbility::SnareImmunity)) { return true; From e6081e0d2c1fcf4792dc064077ad5c60d37a4365 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:22:27 -0600 Subject: [PATCH 229/394] Fix GetRaidByBotName and GetRaidByBot checks to not loop unnecessarily --- zone/entity.cpp | 29 ++++++----------------------- 1 file changed, 6 insertions(+), 23 deletions(-) diff --git a/zone/entity.cpp b/zone/entity.cpp index b34f62d3d2..3176639271 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2233,44 +2233,27 @@ Raid* EntityList::GetRaidByClient(Client* client) } Raid* EntityList::GetRaidByBotName(const char* name) { - std::list rm; - auto GetMembersWithNames = [&rm](Raid const* r) -> std::list { - for (const auto& m : r->members) { - if (strlen(m.member_name) > 0) - rm.push_back(m); - } - return rm; - }; - for (const auto& r : raid_list) { - for (const auto& m : GetMembersWithNames(r)) { - if (strcmp(m.member_name, name) == 0) { + for (const auto& m : r->members) { + if (m.is_bot && strcmp(m.member_name, name) == 0) { return r; } } } + return nullptr; } Raid* EntityList::GetRaidByBot(const Bot* bot) { - std::list rm; - auto GetMembersWhoAreBots = [&rm](Raid* r) -> std::list { - for (auto const& m : r->members) { - if (m.is_bot) { - rm.push_back(m); - } - } - return rm; - }; - for (const auto& r : raid_list) { - for (const auto& m : GetMembersWhoAreBots(r)) { - if (m.member->CastToBot() == bot) { + for (const auto& m : r->members) { + if (m.is_bot && m.member->CastToBot() == bot) { return r; } } } + return nullptr; } From 2a149f2949e97ca373d3c7253c8debdef0ceedea Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:24:31 -0600 Subject: [PATCH 230/394] Move GatherSpellTargets to mob --- zone/bot.cpp | 48 ------------------------------------------- zone/bot.h | 2 -- zone/mob.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++++ zone/mob.h | 2 ++ 4 files changed, 59 insertions(+), 50 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index abd437c26a..01e3db6c3f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9258,54 +9258,6 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 }; -std::vector Bot::GatherSpellTargets(bool entireRaid, Mob* target, bool noClients, bool noBots, bool noPets) { - std::vector valid_spell_targets; - - if (IsRaidGrouped()) { - if (auto raid = GetRaid()) { - if (entireRaid) { - for (const auto& m : raid->members) { - if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { - valid_spell_targets.emplace_back(m.member); - } - } - } - else { - std::vector raidGroup; - - if (target) { - raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(target->GetName())); - } - else { - raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); - } - - for (const auto& m : raidGroup) { - if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { - valid_spell_targets.emplace_back(m.member); - } - } - } - } - } - else if (IsGrouped()) { - Group* group = GetGroup(); - - if (group) { - for (const auto& m : group->members) { - if (m && ((m->IsClient() && !noClients) || (m->IsBot() && !noBots))) { - valid_spell_targets.emplace_back(m); - } - } - } - } - else { - valid_spell_targets.emplace_back(this); - } - - return valid_spell_targets; -} - bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { if (!tar) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); //deleteme diff --git a/zone/bot.h b/zone/bot.h index eb7a9ae6cc..a083ab21fa 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -456,8 +456,6 @@ class Bot : public NPC { { return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); } void DoAttackRounds(Mob* target, int hand); - std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool noClients = false, bool noBots = false, bool noPets = false); - bool PrecastChecks(Mob* tar, uint16 spellType); bool CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); bool CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar); diff --git a/zone/mob.cpp b/zone/mob.cpp index 1814414e84..f1b6a17388 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8695,6 +8695,63 @@ void Mob::CheckScanCloseMobsMovingTimer() } } +std::vector Mob::GatherSpellTargets(bool entireRaid, Mob* target, bool noClients, bool noBots, bool noPets) { + std::vector valid_spell_targets; + + if (IsRaidGrouped()) { + Raid* raid = nullptr; + + if (IsBot()) { + raid = CastToBot()->GetStoredRaid(); + } + else { + raid = GetRaid(); + } + + if (raid) { + if (entireRaid) { + for (const auto& m : raid->members) { + if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m.member); + } + } + } + else { + std::vector raidGroup; + + if (target) { + raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(target->GetName())); + } + else { + raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); + } + + for (const auto& m : raidGroup) { + if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m.member); + } + } + } + } + } + else if (IsGrouped()) { + Group* group = GetGroup(); + + if (group) { + for (const auto& m : group->members) { + if (m && ((m->IsClient() && !noClients) || (m->IsBot() && !noBots))) { + valid_spell_targets.emplace_back(m); + } + } + } + } + else { + valid_spell_targets.emplace_back(this); + } + + return valid_spell_targets; +} + uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { diff --git a/zone/mob.h b/zone/mob.h index 90dbd7b0f3..ba81182f1b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -432,6 +432,8 @@ class Mob : public Entity { inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient() ? true : _spellSettings[spellType].recastTimer.GetRemainingTime() > 0 ? false : true); } + std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool noClients = false, bool noBots = false, bool noPets = false); + uint16 GetSpellTypeIDByShortName(std::string spellTypeString); std::string GetBotSpellCategoryName(uint8 setting_type); From 0c0fee1c6734b7c3820be022f300684c45602966 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:26:01 -0600 Subject: [PATCH 231/394] Change GetPrioritizedBotSpellsBySpellType to vector Some slipped through in "organize some checks in IsImmuneToBotSpell" --- zone/bot.h | 2 +- zone/bot_commands/depart.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/bot.h b/zone/bot.h index a083ab21fa..381b4de543 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -595,7 +595,7 @@ class Bot : public NPC { static std::list GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect); static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType); static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); - static std::list GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, Raid* raid = nullptr, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); + static std::vector GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 6fb4fdb29e..a78f891683 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -172,9 +172,9 @@ void bot_command_depart(Client* c, const Seperator* sep) continue; } - std::list botSpellListItr = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Teleport, tar); + std::vector botSpellListItr = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Teleport, tar); - for (std::list::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + for (std::vector::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { if (!IsValidSpell(itr->SpellId)) { continue; } From f6fa28681b13c3210bdb18230556297d21dd2b24 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:29:15 -0600 Subject: [PATCH 232/394] Move GatherSpellTargets and Raid to stored variables. Missing some in "organize some checks in IsImmuneToBotSpell" --- zone/bot.cpp | 72 +++++++++++++++++++++++++------------- zone/bot.h | 22 ++++++++---- zone/bot_commands/cast.cpp | 2 +- zone/mob.cpp | 31 +++++++++++++--- 4 files changed, 91 insertions(+), 36 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 01e3db6c3f..a18ba699c9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -109,6 +109,9 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm GenerateBaseStats(); bot_timers.clear(); bot_blocked_buffs.clear(); + _spellTargetList.clear(); + _groupSpellTargetList.clear(); + _storedRaid = nullptr; // Calculate HitPoints Last As It Uses Base Stats current_hp = GenerateBaseHitPoints(); @@ -258,6 +261,9 @@ Bot::Bot( database.botdb.LoadBotBlockedBuffs(this); } + _spellTargetList.clear(); + _groupSpellTargetList.clear(); + _storedRaid = nullptr; LoadAAs(); if (database.botdb.LoadBuffs(this)) { @@ -2095,7 +2101,7 @@ void Bot::AI_Process() else { follow_mob = entity_list.GetMob(GetFollowID()); - if (!follow_mob || !IsInGroupOrRaid(follow_mob, raid)) { + if (!follow_mob || !IsInGroupOrRaid(follow_mob)) { follow_mob = leash_owner; } } @@ -2126,6 +2132,12 @@ void Bot::AI_Process() return; } + SetStoredRaid(raid); + std::vector spellTargetList = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); + SetSpellTargetList(spellTargetList); + std::vector groupSpellTargetList = GatherSpellTargets(); + SetGroupSpellTargetList(groupSpellTargetList); + // HEAL ROTATION CASTING CHECKS HealRotationChecks(); @@ -2369,7 +2381,7 @@ void Bot::AI_Process() // AUTO DEFEND - if (TryAutoDefend(bot_owner, leash_distance, raid) ) { + if (TryAutoDefend(bot_owner, leash_distance) ) { return; } @@ -2468,7 +2480,7 @@ bool Bot::TryIdleChecks(float fm_distance) { // This is as close as I could get without modifying the aggro mechanics and making it an expensive process... // 'class Client' doesn't make use of hate_list -bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance, Raid* raid) { +bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { if (RuleB(Bots, AllowOwnerOptionAutoDefend) && bot_owner->GetBotOption(Client::booAutoDefend)) { @@ -2505,6 +2517,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance, Raid* raid) { bool assisteeFound = false; if (IsRaidGrouped()) { + Raid* raid = GetStoredRaid(); if (raid) { for (const auto& m : raid->members) { if ( @@ -2555,7 +2568,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance, Raid* raid) { if (bot_owner->GetAssistee()) { Client* c = entity_list.GetClientByCharID(bot_owner->GetAssistee()); - if (bot_owner->IsInGroupOrRaid(c, raid) && c->GetAggroCount()) { + if (bot_owner->IsInGroupOrRaid(c) && c->GetAggroCount()) { tempHaters = bot_owner->GetXTargetAutoMgr(); if (tempHaters && !tempHaters->empty()) { @@ -5976,7 +5989,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe if ( spellTarget && GetClass() != Class::Bard && - (IsGrouped() || (IsRaidGrouped() && GetRaid()->GetGroup(GetCleanName()) != RAID_GROUPLESS)) && + (IsGrouped() || (IsRaidGrouped() && GetStoredRaid()->GetGroup(GetCleanName()) != RAID_GROUPLESS)) && (spellTarget->IsBot() || spellTarget->IsClient()) && (RuleB(Bots, GroupBuffing) || RuleB(Bots, RaidBuffing)) ) { @@ -6008,7 +6021,12 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe if (!noGroupSpell) { std::vector v; - v = GatherSpellTargets(RuleB(Bots, RaidBuffing)); + if (RuleB(Bots, RaidBuffing)) { + v = GetSpellTargetList(); + } + else { + v = GatherSpellTargets(false, spellTarget); + } for (Mob* m : v) { if (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune)) { @@ -6043,7 +6061,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spells::CastingSlot slot, bool& stopLogic) { bool isMainGroupMGB = false; - Raid* raid = entity_list.GetRaidByBot(this); + Raid* raid = GetStoredRaid(); if (isMainGroupMGB && (GetClass() != Class::Bard)) { BotGroupSay( @@ -6065,7 +6083,7 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel std::vector v; if (RuleB(Bots, RaidBuffing)) { - v = GatherSpellTargets(true); + v = GetSpellTargetList(); } else { v = GatherSpellTargets(false, spellTarget); @@ -7206,11 +7224,11 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool Bot::AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector targetList) { +bool Bot::AttemptCloseBeneficialSpells(uint16 spellType) { bool result = false; Mob* tar = nullptr; - for (Mob* m : targetList) { + for (Mob* m : GetSpellTargetList()) { tar = m; if (!tar) { @@ -7218,12 +7236,13 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector } if (IsGroupTargetOnlyBotSpellType(spellType)) { + Raid* raid = GetStoredRaid(); if (raid && (raid->GetGroup(GetName()) == raid->GetGroup(tar->GetName()))) { continue; } } - result = AttemptAICastSpell(spellType, tar, raid); + result = AttemptAICastSpell(spellType, tar); if (!result) { if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { @@ -7237,7 +7256,7 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector continue; } - result = AttemptAICastSpell(spellType, tar, raid); + result = AttemptAICastSpell(spellType, tar); } } @@ -7752,7 +7771,7 @@ void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) { va_end(ap); if (speaker->IsRaidGrouped()) { - Raid* r = entity_list.GetRaidByBot(speaker->CastToBot()); + Raid* r = speaker->CastToBot()->GetStoredRaid(); if (r) { for (const auto& m : r->members) { if (m.member && !m.is_bot) { @@ -9699,16 +9718,21 @@ bool Bot::BotHasEnoughMana(uint16 spell_id) { return true; } -bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { +bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { //TODO bot rewrite - add raid and spell targets if (!tar || !spell_id) { return true; } std::vector v; uint16 targetID = tar->GetID(); - - v = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); - + + if (RuleB(Bots, CrossRaidBuffingAndHealing)) { + v = GetSpellTargetList(); + } + else { + v = GetGroupSpellTargetList(); + } + for (Mob* m : v) { if ( m->IsBot() && @@ -9716,7 +9740,7 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { m->CastToBot()->casting_spell_targetid && m->CastingSpellID() == spell_id ) { - if (IsGroupSpell(spell_id)) { + if (RuleB(Bots, CrossRaidBuffingAndHealing) && IsGroupSpell(spell_id)) { std::vector t = GatherSpellTargets(false, tar); for (Mob* x : t) { @@ -9849,9 +9873,7 @@ bool Bot::IsMobEngagedByAnyone(Mob* tar) { return false; } - const std::vector v = GatherSpellTargets(true); - - for (Mob* m : v) { + for (Mob* m : GetSpellTargetList()) { if (m->GetTarget() == tar) { if ( m->IsBot() && @@ -10763,7 +10785,7 @@ std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { return castOrder; } -bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar, Raid* raid) { +bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { bool result = false; if (!tar) { @@ -10787,12 +10809,12 @@ bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar, Raid* raid) { tar = this; } - if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType, raid)) { + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { return result; } } else { - if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType, raid)) { + if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { return result; } } @@ -10948,7 +10970,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { !RuleB(Bots, EnableBotTGB) && IsGroupSpell(forcedSpellID) && !IsTGBCompatibleSpell(forcedSpellID) && - !IsInGroupOrRaid(tar, nullptr, true) + !IsInGroupOrRaid(tar, true) ) { LogTestDebug("{} failed TGB for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme return false; diff --git a/zone/bot.h b/zone/bot.h index 381b4de543..7edb42517d 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -397,11 +397,11 @@ class Bot : public NPC { void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); // AI Methods - bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, Raid* raid = nullptr, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); - bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr, Raid* raid = nullptr); + bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); + bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id); - bool AttemptCloseBeneficialSpells(uint16 spellType, Raid* raid, std::vector targetList); + bool AttemptCloseBeneficialSpells(uint16 spellType); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; @@ -460,6 +460,12 @@ class Bot : public NPC { bool CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); bool CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar); bool BotHasEnoughMana(uint16 spell_id); + std::vector GetSpellTargetList() { return _spellTargetList; } + void SetSpellTargetList(std::vector spellTargetList) { _spellTargetList = spellTargetList; } + std::vector GetGroupSpellTargetList() { return _groupSpellTargetList; } + void SetGroupSpellTargetList(std::vector spellTargetList) { _groupSpellTargetList = spellTargetList; } + Raid* GetStoredRaid() { return _storedRaid; } + void SetStoredRaid(Raid* storedRaid) { _storedRaid = storedRaid; } bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType); @@ -476,7 +482,6 @@ class Bot : public NPC { void SetBotBaseSetting(uint16 botSetting, int settingValue); void LoadDefaultBotSettings(); void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); - BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); uint16 GetSpellByAA(int id, AA::Rank* &rank); void CleanBotBlockedBuffs(); void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); } @@ -598,6 +603,7 @@ class Bot : public NPC { static std::vector GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); + BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); @@ -608,7 +614,7 @@ class Bot : public NPC { static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE = false); + static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE); bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Pet); @@ -988,7 +994,7 @@ class Bot : public NPC { bool TryFacingTarget(Mob* tar); bool TryPursueTarget(float leash_distance, glm::vec3& Goal); bool TryMeditate(); - bool TryAutoDefend(Client* bot_owner, float leash_distance, Raid* raid = nullptr); + bool TryAutoDefend(Client* bot_owner, float leash_distance); bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); bool TryBardMovementCasts(); @@ -1093,6 +1099,10 @@ class Bot : public NPC { bool _commandedSpell; bool _pullingSpell; + std::vector _spellTargetList; // TODO bot rewrite - implement this and raid + std::vector _groupSpellTargetList; + Raid* _storedRaid; + // Private "base stats" Members int32 _baseMR; int32 _baseCR; diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index b62162e751..709e919562 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -588,7 +588,7 @@ void bot_command_cast(Client* c, const Seperator* sep) LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on [{}]'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); - if (bot_iter->AICastSpell(newTar, 100, spellType, nullptr, subTargetType, subType)) { + if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { if (!firstFound) { firstFound = bot_iter; } diff --git a/zone/mob.cpp b/zone/mob.cpp index f1b6a17388..bed5340712 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9954,7 +9954,7 @@ void Mob::ClearDataBucketCache() } } -bool Mob::IsInGroupOrRaid(Mob *other, Raid* raid, bool sameRaidGroup) { +bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { if (!other || !IsOfClientBotMerc() || !other->IsOfClientBotMerc()) { return false; } @@ -9963,9 +9963,18 @@ bool Mob::IsInGroupOrRaid(Mob *other, Raid* raid, bool sameRaidGroup) { return true; } - if (IsRaidGrouped() && !raid) { - raid = GetRaid(); + Raid* raid = nullptr; + if (IsBot) { + CastToBot()->GetStoredRaid(); + } + else { + if (IsRaidGrouped()) { + raid = GetRaid(); + } + } + + if (IsRaidGrouped()) { if (!raid) { return false; } @@ -9975,8 +9984,22 @@ bool Mob::IsInGroupOrRaid(Mob *other, Raid* raid, bool sameRaidGroup) { if (!other->IsRaidGrouped()) { return false; } + + Raid* otherRaid = nullptr; + + if (other->IsBot()) { + otherRaid = other->CastToBot()->GetStoredRaid(); + } + else { + otherRaid = other->GetRaid(); + } + + if (!otherRaid) { + return false; + } + auto rGroup = raid->GetGroup(GetCleanName()); - auto rOGroup = raid->GetGroup(other->GetCleanName()); + auto rOGroup = otherRaid->GetGroup(other->GetCleanName()); if (rGroup == RAID_GROUPLESS || rOGroup == RAID_GROUPLESS || (sameRaidGroup && rGroup != rOGroup)) { return false; From 39b1f57ef3b47d72f57e639e1e7abd25b00cb90f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:29:58 -0600 Subject: [PATCH 233/394] comment out precheck, delays, thresholds, etc logging missed some in "organize some checks in IsImmuneToBotSpell" --- zone/bot.cpp | 122 +++++++++++++++++++++---------------------- zone/botspellsai.cpp | 2 +- zone/mob.cpp | 10 ++-- 3 files changed, 67 insertions(+), 67 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a18ba699c9..0cd7e72722 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9279,14 +9279,14 @@ uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][St bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { if (!tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); //deleteme return false; } - LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme if (GetUltimateSpellHold(spellType, tar)) { - LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + //LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; } @@ -9295,17 +9295,17 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { } if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; } if (GetHPRatio() < GetSpellTypeMinHPLimit(spellType) || GetHPRatio() > GetSpellTypeMaxHPLimit(spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; } if (!GetUltimateSpellDelayCheck(spellType, tar)) { - LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + //LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; } @@ -9315,7 +9315,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return true; default: if (GetHPRatioForSpellType(spellType, tar) < GetUltimateSpellMinThreshold(spellType, tar) || GetHPRatioForSpellType(spellType, tar) > GetUltimateSpellMaxThreshold(spellType, tar)) { - LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + //LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme return false; } } @@ -9326,7 +9326,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { if (doPrechecks) { if (!tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme return false; } @@ -9340,57 +9340,57 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (!PrecastChecks(tar, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); //deleteme return false; } } - LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "nobody")); //deleteme + //LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "nobody")); //deleteme if (!IsValidSpell(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme return false; } if (IsFeared() || IsSilenced() || IsAmnesiad()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme return false; } if (!CheckSpellRecastTimer(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (!BotHasEnoughMana(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { if (IsBeneficialSpell(spell_id)) { if (IsEngaged()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } } @@ -9398,7 +9398,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { if (IsBeneficialSpell(spell_id)) { if (!IsEngaged()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } } @@ -9408,33 +9408,33 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec int chance = GetFocusEffect(focusFcMute, spell_id); if (chance && zone->random.Roll(chance)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return(false); } } if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (SpellTypeRequiresTarget(spellType) && !tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme return false; } @@ -9443,32 +9443,32 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec && tar != this && (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (this == tar && IsSacrificeSpell(spell_id)) { - LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme return false; } if (tar->GetSpecialAbility(SpecialAbility::MagicImmunity)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (tar->IsImmuneToBotSpell(spell_id, this)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (!tar->CheckSpellLevelRestriction(this, spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9480,7 +9480,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec ) ) { if (tar->IsBlockedBuff(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } } @@ -9493,11 +9493,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec ) ) { if (tar->GetOwner()->IsBlockedPetBuff(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } } - LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme if (!CanCastSpellType(spellType, spell_id, tar)) { return false; } @@ -9507,7 +9507,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9516,7 +9516,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec && tar->CanBuffStack(spell_id, GetLevel(), true) < 0 ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9529,22 +9529,22 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (!AECheck && !IsValidSpellRange(spell_id, tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9553,7 +9553,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { if (!spell_id || !tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme return false; } @@ -9580,27 +9580,27 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: if (tar == this && spells[spell_id].target_type == ST_TargetsTarget) { - LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme + //LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme return false; } if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spell_id, SE_Illusion)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if (spells[spell_id].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } if ((IsGroupSpell(spell_id) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9614,7 +9614,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9624,7 +9624,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9643,7 +9643,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { if (IsLichSpell(tar->GetBuffs()[j].spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } } @@ -9666,7 +9666,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9676,7 +9676,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } break; @@ -9690,7 +9690,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::AELull: case BotSpellTypes::Lull: if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; } @@ -9699,7 +9699,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { break; } - LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return true; } @@ -9799,7 +9799,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spell_id, int32 resist_limit) { default: return true; } - //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) + ////LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) if ((targetResist + level_mod - resist_difficulty) > resist_limit) { return false; } @@ -10158,7 +10158,7 @@ void Bot::LoadDefaultBotSettings() { for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, botStance)); - LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); //deleteme + //LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); //deleteme } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -10185,10 +10185,10 @@ void Bot::LoadDefaultBotSettings() { _spellSettings.push_back(t); - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(botStance), botStance); //deleteme - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, botStance), GetDefaultSpellDelay(i, botStance), GetDefaultSpellMinThreshold(i, botStance), GetDefaultSpellMaxThreshold(i, botStance)); //deleteme - LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, botStance), GetDefaultSpellTypeMinManaLimit(i, botStance), GetDefaultSpellTypeMaxManaLimit(i, botStance), GetDefaultSpellTypeMinHPLimit(i, botStance), GetDefaultSpellTypeMaxHPLimit(i, botStance)); //deleteme - LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), botStance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), botStance), GetDefaultSpellTypePursuePriority(i, GetClass(), botStance), GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); //deleteme + //LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(botStance), botStance); //deleteme + //LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, botStance), GetDefaultSpellDelay(i, botStance), GetDefaultSpellMinThreshold(i, botStance), GetDefaultSpellMaxThreshold(i, botStance)); //deleteme + //LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, botStance), GetDefaultSpellTypeMinManaLimit(i, botStance), GetDefaultSpellTypeMaxManaLimit(i, botStance), GetDefaultSpellTypeMinHPLimit(i, botStance), GetDefaultSpellTypeMaxHPLimit(i, botStance)); //deleteme + //LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), botStance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), botStance), GetDefaultSpellTypePursuePriority(i, GetClass(), botStance), GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); //deleteme } } @@ -10800,7 +10800,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { } if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme + //LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme return result; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 50980b6646..2f68593ffb 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -26,7 +26,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return false; } - LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme if ( !AI_HasSpells() || diff --git a/zone/mob.cpp b/zone/mob.cpp index bed5340712..0ef303bfe6 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9753,11 +9753,11 @@ void Mob::SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue) { void Mob::SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime) { _spellSettings[spellType].recastTimer.Start(recastTime); - LogBotDelayChecksDetail("{} says, 'My {} Delay was to {} seconds.'" - , GetCleanName() - , GetSpellTypeNameByID(spellType) - , (recastTime / 1000.00) - ); //deleteme + //LogBotDelayChecksDetail("{} says, 'My {} Delay was to {} seconds.'" + // , GetCleanName() + // , GetSpellTypeNameByID(spellType) + // , (recastTime / 1000.00) + //); //deleteme } void Mob::StartBotSpellTimers() { From 998f34842b75c954060879a5889ec72a7208572e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 00:35:29 -0600 Subject: [PATCH 234/394] Missing IsInGroupOrRaid cleanup --- zone/attack.cpp | 4 ++-- zone/bot_commands/cast.cpp | 2 +- zone/bot_commands/depart.cpp | 2 +- zone/mob.cpp | 2 +- zone/mob.h | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 130d5e2934..d1c862096b 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2624,10 +2624,10 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy bool owner_in_group = false; if ( - give_exp->IsInGroupOrRaid(give_exp->GetUltimateOwner(), nullptr, RuleB(Bots, SameRaidGroupForXP)) || + give_exp->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) || give_exp->IsPet() && give_exp->GetOwner() && - give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), nullptr, RuleB(Bots, SameRaidGroupForXP)) + give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) ) { owner_in_group = true; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 709e919562..d02c9f2a92 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -506,7 +506,7 @@ void bot_command_cast(Client* c, const Seperator* sep) if ( IsBotSpellTypeBeneficial(spellType) && !RuleB(Bots, CrossRaidBuffingAndHealing) && - !bot_iter->IsInGroupOrRaid(newTar, nullptr, true) + !bot_iter->IsInGroupOrRaid(newTar, true) ) { continue; } diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index a78f891683..23bb375b16 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -164,7 +164,7 @@ void bot_command_depart(Client* c, const Seperator* sep) std::map> listZones; for (auto bot_iter : sbl) { - if (!bot_iter->IsInGroupOrRaid(tar, nullptr, !single)) { + if (!bot_iter->IsInGroupOrRaid(tar, !single)) { continue; } diff --git a/zone/mob.cpp b/zone/mob.cpp index 0ef303bfe6..e28b0b8a91 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9965,7 +9965,7 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { Raid* raid = nullptr; - if (IsBot) { + if (IsBot()) { CastToBot()->GetStoredRaid(); } else { diff --git a/zone/mob.h b/zone/mob.h index ba81182f1b..3d2ab96b42 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -781,7 +781,7 @@ class Mob : public Entity { virtual bool HasGroup() = 0; virtual Raid* GetRaid() = 0; virtual Group* GetGroup() = 0; - bool IsInGroupOrRaid(Mob* other, Raid* raid = nullptr, bool sameRaidGroup = false); + bool IsInGroupOrRaid(Mob* other, bool sameRaidGroup = false); //Faction virtual inline int32 GetPrimaryFaction() const { return 0; } From 83d41f00d2ed0b58d97d2bd9e165aa3ecc2bf4ed Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 22:38:52 -0600 Subject: [PATCH 235/394] Implement AIBot_spells_by_type to reduce looping when searching for spells --- zone/bot.cpp | 46 ++++++++++++++++++++++++++++-- zone/bot.h | 9 ++++-- zone/bot_structs.h | 18 ++++++++++++ zone/botspellsai.cpp | 67 +++++++++++++++++++++++--------------------- 4 files changed, 103 insertions(+), 37 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0cd7e72722..4d9214c718 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6061,7 +6061,6 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spells::CastingSlot slot, bool& stopLogic) { bool isMainGroupMGB = false; - Raid* raid = GetStoredRaid(); if (isMainGroupMGB && (GetClass() != Class::Bard)) { BotGroupSay( @@ -9718,7 +9717,7 @@ bool Bot::BotHasEnoughMana(uint16 spell_id) { return true; } -bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { //TODO bot rewrite - add raid and spell targets +bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { if (!tar || !spell_id) { return true; } @@ -11971,3 +11970,46 @@ void Bot::CleanBotBlockedBuffs() } } } + +std::vector Bot::BotGetSpellsByType(uint16 spellType) { + if (!AIBot_spells_by_type[spellType].empty()) { + return AIBot_spells_by_type[spellType]; + } + else { + spellType = GetParentSpellType(spellType); + + return AIBot_spells_by_type[spellType]; + } +} + +void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type) { + AIBot_spells_by_type.clear(); + + for (size_t i = 0; i < AIBot_spells.size(); ++i) { + const auto& spell = AIBot_spells[i]; + + if (spell.spellid <= 0) { + continue; + } + + BotSpells_Struct_wIndex spellWithIndex{ + static_cast(i), + spell.type, + spell.spellid, + spell.manacost, + spell.time_cancast, + spell.recast_delay, + spell.priority, + spell.resist_adjust, + spell.minlevel, + spell.maxlevel, + spell.min_hp, + spell.max_hp, + spell.bucket_name, + spell.bucket_value, + spell.bucket_comparison + }; + + AIBot_spells_by_type[spell.type].emplace_back(spellWithIndex); + } +} diff --git a/zone/bot.h b/zone/bot.h index 7edb42517d..d2488671b8 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -236,6 +236,7 @@ class Bot : public NPC { uint16 BotGetSpells(int spellslot) { return AIBot_spells[spellslot].spellid; } uint32 BotGetSpellType(int spellslot) { return AIBot_spells[spellslot].type; } uint16 BotGetSpellPriority(int spellslot) { return AIBot_spells[spellslot].priority; } + std::vector BotGetSpellsByType(uint16 spellType); float GetProcChances(float ProcBonus, uint16 hand) override; int GetHandToHandDamage(void) override; bool TryFinishingBlow(Mob *defender, int64 &damage) override; @@ -466,6 +467,7 @@ class Bot : public NPC { void SetGroupSpellTargetList(std::vector spellTargetList) { _groupSpellTargetList = spellTargetList; } Raid* GetStoredRaid() { return _storedRaid; } void SetStoredRaid(Raid* storedRaid) { _storedRaid = storedRaid; } + void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType); @@ -622,9 +624,9 @@ class Bot : public NPC { static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target, uint16 spellType); - static BotSpell GetDebuffBotSpell(Bot* botCaster, Mob* target); + static BotSpell GetDebuffBotSpell(Bot* botCaster, Mob* target, uint16 spellType); static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target, uint16 spellType); - static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target); + static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target, uint16 spellType); static BotSpell GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE = false, Mob* tar = nullptr); static BotSpell GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellType); static BotSpell GetBestBotSpellForCharm(Bot* botCaster, Mob* target, uint16 spellType); @@ -1020,6 +1022,7 @@ class Bot : public NPC { std::vector AIBot_spells; std::vector AIBot_spells_enforced; + std::unordered_map> AIBot_spells_by_type; std::vector bot_timers; std::vector bot_blocked_buffs; @@ -1099,7 +1102,7 @@ class Bot : public NPC { bool _commandedSpell; bool _pullingSpell; - std::vector _spellTargetList; // TODO bot rewrite - implement this and raid + std::vector _spellTargetList; std::vector _groupSpellTargetList; Raid* _storedRaid; diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 9a05a70e43..22d33957ef 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -81,6 +81,24 @@ struct BotSpells_Struct { uint8 bucket_comparison; }; +struct BotSpells_Struct_wIndex { + uint32 index; //index of AIBot_spells + uint32 type; // 0 = never, must be one (and only one) of the defined values + int16 spellid; // <= 0 = no spell + int16 manacost; // -1 = use spdat, -2 = no cast time + uint32 time_cancast; // when we can cast this spell next + int32 recast_delay; + int16 priority; + int16 resist_adjust; + uint8 minlevel; + uint8 maxlevel; + int16 min_hp; // >0 won't cast if HP is below + int16 max_hp; // >0 won't cast if HP is above + std::string bucket_name; + std::string bucket_value; + uint8 bucket_comparison; +}; + struct BotTimer_Struct { uint32 timer_id; uint32 timer_value; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 2f68593ffb..5f3c3d7842 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -896,7 +896,7 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spell } if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { @@ -911,7 +911,7 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spell ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = i; + botSpell.SpellIndex = botSpellList[i].index; botSpell.ManaCost = botSpellList[i].manacost; result.push_back(botSpell); @@ -934,7 +934,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, } if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { @@ -953,7 +953,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = i; + botSpell.SpellIndex = botSpellList[i].index; botSpell.ManaCost = botSpellList[i].manacost; result.push_back(botSpell); } @@ -975,7 +975,7 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp } if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { @@ -989,7 +989,7 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp ) { BotSpell botSpell; botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = i; + botSpell.SpellIndex = botSpellList[i].index; botSpell.ManaCost = botSpellList[i].manacost; result.push_back(botSpell); @@ -1004,7 +1004,7 @@ std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* bot std::vector result; if (botCaster && botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { @@ -1062,7 +1062,7 @@ std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* bot ) { BotSpell_wPriority botSpell; botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = i; + botSpell.SpellIndex = botSpellList[i].index; botSpell.ManaCost = botSpellList[i].manacost; botSpell.Priority = botSpellList[i].priority; @@ -1089,7 +1089,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { result.ManaCost = 0; if (botCaster && botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { @@ -1102,7 +1102,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) ) { result.SpellId = botSpellList[i].spellid; - result.SpellIndex = i; + result.SpellIndex = botSpellList[i].index; result.ManaCost = botSpellList[i].manacost; break; @@ -1197,7 +1197,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster, Mob* tar, uint16 result.ManaCost = 0; if (botCaster && botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpell(botSpellList[i].spellid)) { continue; @@ -1210,7 +1210,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster, Mob* tar, uint16 botCaster->CastChecks(botSpellList[i].spellid, tar, spellType) ) { result.SpellId = botSpellList[i].spellid; - result.SpellIndex = i; + result.SpellIndex = botSpellList[i].index; result.ManaCost = botSpellList[i].manacost; break; @@ -1906,7 +1906,7 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ return result; } -BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) { +BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -1917,7 +1917,7 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) { return result; if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { @@ -1929,7 +1929,7 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) { && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { result.SpellId = botSpellList[i].spellid; - result.SpellIndex = i; + result.SpellIndex = botSpellList[i].index; result.ManaCost = botSpellList[i].manacost; break; @@ -1940,7 +1940,7 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) { return result; } -BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { +BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar, uint16 spellType) { BotSpell result; result.SpellId = 0; @@ -1963,7 +1963,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { bool needsDiseaseResistDebuff = (tar->GetDR() + level_mod) > 100; if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->AIBot_spells; + std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); for (int i = botSpellList.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { @@ -1980,7 +1980,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) { && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { result.SpellId = botSpellList[i].spellid; - result.SpellIndex = i; + result.SpellIndex = botSpellList[i].index; result.ManaCost = botSpellList[i].manacost; break; @@ -2165,6 +2165,8 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { npc_spells_id = bot_spell_id; AIBot_spells.clear(); AIBot_spells_enforced.clear(); + AIBot_spells_by_type.clear(); + if (!bot_spell_id) { AIautocastspell_timer->Disable(); return false; @@ -2455,6 +2457,7 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { AIautocastspell_timer->Disable(); } else { AIautocastspell_timer->Trigger(); + AssignBotSpellsToTypes(AIBot_spells, AIBot_spells_by_type); // Assign AIBot_spells to AIBot_spells_by_type with an index } return true; } @@ -2630,20 +2633,20 @@ void Bot::AddSpellToBotEnforceList( HasAISpell = true; BotSpells_Struct t; - t.priority = iPriority; - t.spellid = iSpellID; - t.type = iType; - t.manacost = iManaCost; - t.recast_delay = iRecastDelay; - t.time_cancast = 0; - t.resist_adjust = iResistAdjust; - t.minlevel = min_level; - t.maxlevel = maxlevel; - t.min_hp = min_hp; - t.max_hp = max_hp; - t.bucket_name = bucket_name; - t.bucket_value = bucket_value; - t.bucket_comparison = bucket_comparison; + t.priority = iPriority; + t.spellid = iSpellID; + t.type = iType; + t.manacost = iManaCost; + t.recast_delay = iRecastDelay; + t.time_cancast = 0; + t.resist_adjust = iResistAdjust; + t.minlevel = min_level; + t.maxlevel = maxlevel; + t.min_hp = min_hp; + t.max_hp = max_hp; + t.bucket_name = bucket_name; + t.bucket_value = bucket_value; + t.bucket_comparison = bucket_comparison; AIBot_spells_enforced.push_back(t); } From 69b4c3a5a4b3d12da6caa38871ad0253afba8b9f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 22:39:27 -0600 Subject: [PATCH 236/394] Add _tempSpellType as placeholder for any future passthru --- zone/bot.cpp | 1 + zone/bot.h | 3 +++ zone/botspellsai.cpp | 3 ++- 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4d9214c718..a8bc91638f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2137,6 +2137,7 @@ void Bot::AI_Process() SetSpellTargetList(spellTargetList); std::vector groupSpellTargetList = GatherSpellTargets(); SetGroupSpellTargetList(groupSpellTargetList); + SetTempSpellType(UINT16_MAX); // HEAL ROTATION CASTING CHECKS HealRotationChecks(); diff --git a/zone/bot.h b/zone/bot.h index d2488671b8..e0177e2574 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -467,6 +467,8 @@ class Bot : public NPC { void SetGroupSpellTargetList(std::vector spellTargetList) { _groupSpellTargetList = spellTargetList; } Raid* GetStoredRaid() { return _storedRaid; } void SetStoredRaid(Raid* storedRaid) { _storedRaid = storedRaid; } + uint16 GetTempSpellType() { return _tempSpellType; } + void SetTempSpellType(uint16 spellType) { _tempSpellType = spellType; } void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); @@ -1105,6 +1107,7 @@ class Bot : public NPC { std::vector _spellTargetList; std::vector _groupSpellTargetList; Raid* _storedRaid; + uint16 _tempSpellType; // Private "base stats" Members int32 _baseMR; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 5f3c3d7842..054b82ac96 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -56,7 +56,8 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge uint8 botClass = GetClass(); - SetCastedSpellType(UINT16_MAX); + SetCastedSpellType(UINT16_MAX); // this is for recast timers + SetTempSpellType(spellType); // this is for spell checks BotSpell botSpell; botSpell.SpellId = 0; From c7741efbe551c03c11e10a5f49eb7c8f4d879a6c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 22 Dec 2024 22:39:39 -0600 Subject: [PATCH 237/394] todo --- zone/bot.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a8bc91638f..a891c6367b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5996,7 +5996,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe ) { bool noGroupSpell = false; uint16 thespell = spell_id; - for (int i = 0; i < AIBot_spells.size(); i++) { + for (int i = 0; i < AIBot_spells.size(); i++) { // TODO bot rewrite - fix this to reduce loop with AIBot_spells_by_type? int j = BotGetSpells(i); int spelltype = BotGetSpellType(i); bool spellequal = (j == thespell); From ef983c3d47b2795671e0a130b566cc4f1d35e7f5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 23 Dec 2024 20:50:36 -0600 Subject: [PATCH 238/394] Move bot_list from std::list to std::unordered_map like other entities --- zone/bot.cpp | 121 +++++++++---------- zone/bot_command.cpp | 2 +- zone/bot_command.h | 89 +++++++------- zone/bot_commands/aggressive.cpp | 4 +- zone/bot_commands/appearance.cpp | 2 +- zone/bot_commands/attack.cpp | 4 +- zone/bot_commands/behind_mob.cpp | 4 +- zone/bot_commands/blocked_buffs.cpp | 8 +- zone/bot_commands/bot.cpp | 26 ++-- zone/bot_commands/cast.cpp | 4 +- zone/bot_commands/click_item.cpp | 4 +- zone/bot_commands/default_settings.cpp | 4 +- zone/bot_commands/defensive.cpp | 4 +- zone/bot_commands/depart.cpp | 4 +- zone/bot_commands/distance_ranged.cpp | 4 +- zone/bot_commands/follow.cpp | 4 +- zone/bot_commands/guard.cpp | 4 +- zone/bot_commands/heal_rotation.cpp | 40 +++--- zone/bot_commands/hold.cpp | 4 +- zone/bot_commands/illusion_block.cpp | 4 +- zone/bot_commands/inventory.cpp | 8 +- zone/bot_commands/item_use.cpp | 4 +- zone/bot_commands/max_melee_range.cpp | 4 +- zone/bot_commands/pet.cpp | 10 +- zone/bot_commands/pick_lock.cpp | 2 +- zone/bot_commands/pickpocket.cpp | 2 +- zone/bot_commands/pull.cpp | 4 +- zone/bot_commands/release.cpp | 4 +- zone/bot_commands/sit_hp_percent.cpp | 4 +- zone/bot_commands/sit_in_combat.cpp | 4 +- zone/bot_commands/sit_mana_percent.cpp | 4 +- zone/bot_commands/spell_aggro_checks.cpp | 4 +- zone/bot_commands/spell_delays.cpp | 4 +- zone/bot_commands/spell_engaged_priority.cpp | 4 +- zone/bot_commands/spell_holds.cpp | 4 +- zone/bot_commands/spell_idle_priority.cpp | 4 +- zone/bot_commands/spell_max_hp_pct.cpp | 4 +- zone/bot_commands/spell_max_mana_pct.cpp | 4 +- zone/bot_commands/spell_max_thresholds.cpp | 4 +- zone/bot_commands/spell_min_hp_pct.cpp | 4 +- zone/bot_commands/spell_min_mana_pct.cpp | 4 +- zone/bot_commands/spell_min_thresholds.cpp | 4 +- zone/bot_commands/spell_pursue_priority.cpp | 4 +- zone/bot_commands/spell_target_count.cpp | 4 +- zone/bot_commands/suspend.cpp | 4 +- zone/bot_commands/taunt.cpp | 4 +- zone/bot_commands/timer.cpp | 4 +- zone/bot_commands/track.cpp | 2 +- zone/entity.cpp | 41 +++---- zone/entity.h | 8 +- zone/gm_commands/list.cpp | 14 ++- zone/lua_entity_list.cpp | 11 +- zone/perl_entity.cpp | 9 +- 53 files changed, 265 insertions(+), 274 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index a891c6367b..5d9da07273 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5529,9 +5529,9 @@ void Bot::BotOrderCampAll(Client* c, uint8 class_id) { void Bot::ProcessBotOwnerRefDelete(Mob* botOwner) { if (botOwner && botOwner->IsClient()) { - std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(botOwner->CastToClient()->CharacterID()); + std::vector BotList = entity_list.GetBotsByBotOwnerCharacterID(botOwner->CastToClient()->CharacterID()); if (!BotList.empty()) { - for (std::list::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { + for (std::vector::iterator botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { Bot* tempBot = *botListItr; if (tempBot) { tempBot->SetTarget(nullptr); @@ -6966,7 +6966,7 @@ void Bot::UpdateGroupCastingRoles(const Group* group, bool disband) Bot* Bot::GetBotByBotClientOwnerAndBotName(Client* c, const std::string& botName) { Bot* Result = nullptr; if (c) { - std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); + std::vector BotList = entity_list.GetBotsByBotOwnerCharacterID(c->CharacterID()); if (!BotList.empty()) { for (auto botListItr = BotList.begin(); botListItr != BotList.end(); ++botListItr) { if (std::string((*botListItr)->GetCleanName()) == botName) { @@ -7056,9 +7056,9 @@ void Bot::RemoveBotFromRaid(Bot* bot) { // Handles all client zone change event void Bot::ProcessClientZoneChange(Client* botOwner) { if (botOwner) { - std::list BotList = entity_list.GetBotsByBotOwnerCharacterID(botOwner->CharacterID()); + std::vector BotList = entity_list.GetBotsByBotOwnerCharacterID(botOwner->CharacterID()); - for (std::list::iterator itr = BotList.begin(); itr != BotList.end(); ++itr) { + for (std::vector::iterator itr = BotList.begin(); itr != BotList.end(); ++itr) { Bot* tempBot = *itr; if (tempBot) { @@ -7285,65 +7285,54 @@ Mob* EntityList::GetMobByBotID(uint32 botID) { } Bot* EntityList::GetBotByBotID(uint32 botID) { - Bot* Result = nullptr; - if (botID > 0) { - for (std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { - Bot* tempBot = *botListItr; - if (tempBot && tempBot->GetBotID() == botID) { - Result = tempBot; - break; - } - } + auto it = bot_list.begin(); + while (it != bot_list.end()) { + if (it->second->GetBotID() == botID) + return it->second; + ++it; } - return Result; + return nullptr; } -Bot* EntityList::GetBotByBotName(std::string_view botName) { - Bot* Result = nullptr; - if (!botName.empty()) { - for (const auto b : bot_list) { - if (b && std::string_view(b->GetName()) == botName) { - Result = b; - break; - } +Bot* EntityList::GetBotByBotName(std::string botName) { + for (const auto& e : bot_list) { + if (e.second && Strings::EqualFold(e.second->GetName(), botName)) { + return e.second; } } - return Result; + + return nullptr; } Client* EntityList::GetBotOwnerByBotEntityID(uint32 entity_id) { - Client* c = nullptr; - if (entity_id) { - for (const auto& b : bot_list) { - if (b && b->GetID() == entity_id) { - c = b->GetBotOwner()->CastToClient(); - break; - } + auto it = bot_list.begin(); + while (it != bot_list.end()) { + if (it->second->GetID() == entity_id) + return it->second->GetBotOwner()->CastToClient(); + ++it; } } - - return c; + return nullptr; } Client* EntityList::GetBotOwnerByBotID(const uint32 bot_id) { - Client* c = nullptr; - if (bot_id) { - const auto owner_id = database.botdb.GetOwnerID(bot_id); - if (owner_id) { - c = GetClientByCharID(owner_id); + auto it = bot_list.begin(); + while (it != bot_list.end()) { + if (it->second->GetBotID() == bot_id) + return it->second->GetBotOwner()->CastToClient(); + ++it; } } - - return c; + return nullptr; } void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) { if (new_bot) { new_bot->SetID(GetFreeID()); - bot_list.push_back(new_bot); - mob_list.insert(std::pair(new_bot->GetID(), new_bot)); + bot_list.emplace(std::pair(new_bot->GetID(), new_bot)); + mob_list.emplace(std::pair(new_bot->GetID(), new_bot)); if (parse->BotHasQuestSub(EVENT_SPAWN)) { parse->EventBot(EVENT_SPAWN, new_bot, nullptr, "", 0); @@ -7372,31 +7361,32 @@ void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) { } } -std::list EntityList::GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID) { - std::list Result; - if (botOwnerCharacterID > 0) { - for (std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { - Bot* tempBot = *botListItr; - if (tempBot && tempBot->GetBotOwnerCharacterID() == botOwnerCharacterID) - Result.push_back(tempBot); +std::vector EntityList::GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID) { + std::vector client_bot_list; + + if (botOwnerCharacterID <= 0) { + return client_bot_list; + } + + auto it = bot_list.begin(); + + while (it != bot_list.end()) { + if (it->second->GetOwner() && it->second->GetBotOwnerCharacterID() == botOwnerCharacterID) { + client_bot_list.push_back(it->second); } + ++it; } - return Result; + + return client_bot_list; } bool EntityList::RemoveBot(uint16 entityID) { - bool Result = false; - if (entityID > 0) { - for (std::list::iterator botListItr = bot_list.begin(); botListItr != bot_list.end(); ++botListItr) { - Bot* tempBot = *botListItr; - if (tempBot && tempBot->GetID() == entityID) { - bot_list.erase(botListItr); - Result = true; - break; - } - } + auto it = bot_list.find(entityID); + if (it != bot_list.end()) { + bot_list.erase(it); // Already deleted + return true; } - return Result; + return false; } void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { @@ -11973,14 +11963,11 @@ void Bot::CleanBotBlockedBuffs() } std::vector Bot::BotGetSpellsByType(uint16 spellType) { - if (!AIBot_spells_by_type[spellType].empty()) { - return AIBot_spells_by_type[spellType]; - } - else { + if (AIBot_spells_by_type[spellType].empty()) { spellType = GetParentSpellType(spellType); - - return AIBot_spells_by_type[spellType]; } + + return AIBot_spells_by_type[spellType]; } void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type) { diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 482cab3e32..4eb2526172 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1814,7 +1814,7 @@ int helper_bot_follow_option_chain(Client* bot_owner) return 0; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_BySpawnedBots(bot_owner, sbl); if (sbl.empty()) { return 0; diff --git a/zone/bot_command.h b/zone/bot_command.h index 875c6f3ace..6f66e87888 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -272,13 +272,13 @@ namespace MyBots return (grouped_player->GetGroup() == grouped_bot->GetGroup()); } - static void UniquifySBL(std::list &sbl) { - sbl.remove(nullptr); - sbl.sort(); - sbl.unique(); + static void UniquifySBL(std::vector &sbl) { + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + std::sort(sbl.begin(), sbl.end()); + sbl.erase(std::unique(sbl.begin(), sbl.end()), sbl.end()); } - static void PopulateSBL_ByTargetedBot(Client *bot_owner, std::list &sbl, bool clear_list = true) { + static void PopulateSBL_ByTargetedBot(Client *bot_owner, std::vector &sbl, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -292,7 +292,7 @@ namespace MyBots } } - static void PopulateSBL_ByNamedBot(Client *bot_owner, std::list &sbl, const char* name, bool clear_list = true) { + static void PopulateSBL_ByNamedBot(Client *bot_owner, std::vector &sbl, const char* name, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -314,7 +314,7 @@ namespace MyBots } } - static void PopulateSBL_ByMyGroupedBots(Client *bot_owner, std::list &sbl, bool clear_list = true) { + static void PopulateSBL_ByMyGroupedBots(Client *bot_owner, std::vector &sbl, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -363,7 +363,7 @@ namespace MyBots } } - static void PopulateSBL_ByMyRaidBots(Client* bot_owner, std::list& sbl, bool clear_list = true) { + static void PopulateSBL_ByMyRaidBots(Client* bot_owner, std::vector& sbl, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -396,7 +396,7 @@ namespace MyBots } } - static void PopulateSBL_ByTargetsGroupedBots(Client *bot_owner, std::list &sbl, bool clear_list = true) { + static void PopulateSBL_ByTargetsGroupedBots(Client *bot_owner, std::vector &sbl, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -448,7 +448,7 @@ namespace MyBots } } - static void PopulateSBL_ByNamesGroupedBots(Client *bot_owner, std::list &sbl, const char* name, bool clear_list = true) { + static void PopulateSBL_ByNamesGroupedBots(Client *bot_owner, std::vector &sbl, const char* name, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -507,7 +507,7 @@ namespace MyBots } } - static void PopulateSBL_ByHealRotation(Client *bot_owner, std::list &sbl, const char* name, bool clear_list = true) { + static void PopulateSBL_ByHealRotation(Client *bot_owner, std::vector &sbl, const char* name, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -516,7 +516,7 @@ namespace MyBots return; } - std::list selectable_bot_list; + std::vector selectable_bot_list; if (name) { PopulateSBL_ByNamedBot(bot_owner, selectable_bot_list, name); } @@ -545,7 +545,7 @@ namespace MyBots UniquifySBL(sbl); } - static void PopulateSBL_ByHealRotationMembers(Client *bot_owner, std::list &sbl, const char* name, bool clear_list = true) { + static void PopulateSBL_ByHealRotationMembers(Client *bot_owner, std::vector &sbl, const char* name, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -554,7 +554,7 @@ namespace MyBots return; } - std::list selectable_bot_list; + std::vector selectable_bot_list; if (name) { PopulateSBL_ByNamedBot(bot_owner, selectable_bot_list, name); } @@ -578,7 +578,7 @@ namespace MyBots } } - static void PopulateSBL_ByHealRotationTargets(Client *bot_owner, std::list &sbl, const char* name, bool clear_list = true) { + static void PopulateSBL_ByHealRotationTargets(Client *bot_owner, std::vector &sbl, const char* name, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -587,7 +587,7 @@ namespace MyBots return; } - std::list selectable_bot_list; + std::vector selectable_bot_list; if (name) { PopulateSBL_ByNamedBot(bot_owner, selectable_bot_list, name); } @@ -611,17 +611,17 @@ namespace MyBots } } - static void PopulateSBL_BySpawnedBots(Client *bot_owner, std::list &sbl) { // should be used for most spell casting commands + static void PopulateSBL_BySpawnedBots(Client *bot_owner, std::vector &sbl) { // should be used for most spell casting commands sbl.clear(); if (!bot_owner) { return; } sbl = entity_list.GetBotsByBotOwnerCharacterID(bot_owner->CharacterID()); - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); } - static void PopulateSBL_BySpawnedBotsClass(Client * bot_owner, std::list &sbl, uint16 cls, bool clear_list = true) { + static void PopulateSBL_BySpawnedBotsClass(Client * bot_owner, std::vector &sbl, uint16 cls, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -644,7 +644,7 @@ namespace MyBots } } - static void PopulateSBL_BySpawnedBotsRace(Client* bot_owner, std::list& sbl, uint16 race, bool clear_list = true) { + static void PopulateSBL_BySpawnedBotsRace(Client* bot_owner, std::vector& sbl, uint16 race, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -667,7 +667,7 @@ namespace MyBots } } - static void PopulateSBL_ByAtMMR(Client* bot_owner, std::list& sbl, bool clear_list = true) { + static void PopulateSBL_ByAtMMR(Client* bot_owner, std::vector& sbl, bool clear_list = true) { if (clear_list) { sbl.clear(); } @@ -927,7 +927,7 @@ namespace ActionableBots }; // Populates 'sbl' - static ABType PopulateSBL(Client* bot_owner, std::string ab_type_arg, std::list &sbl, int ab_mask, const char* name = nullptr, uint16 classrace = 0, bool clear_list = true, bool suppress_message = false) { + static ABType PopulateSBL(Client* bot_owner, std::string ab_type_arg, std::vector &sbl, int ab_mask, const char* name = nullptr, uint16 classrace = 0, bool clear_list = true, bool suppress_message = false) { if (clear_list) { sbl.clear(); } @@ -1164,7 +1164,7 @@ namespace ActionableBots return nullptr; } - static Bot* AsSpawned_ByClass(Client *bot_owner, std::list &sbl, uint8 cls, bool petless = false) { + static Bot* AsSpawned_ByClass(Client *bot_owner, std::vector &sbl, uint8 cls, bool petless = false) { if (!bot_owner) { return nullptr; } @@ -1188,7 +1188,7 @@ namespace ActionableBots return nullptr; } - static Bot* AsSpawned_ByMinLevelAndClass(Client *bot_owner, std::list &sbl, uint8 minlvl, uint8 cls, bool petless = false) { + static Bot* AsSpawned_ByMinLevelAndClass(Client *bot_owner, std::vector &sbl, uint8 minlvl, uint8 cls, bool petless = false) { // This function can be nixed if we can enforce bot level as owner level..and the level check can then be moved to the spell loop in the command function if (!bot_owner) return nullptr; @@ -1218,7 +1218,7 @@ namespace ActionableBots if (!bot_owner || bot_name.empty()) return nullptr; - std::list selectable_bot_list; + std::vector selectable_bot_list; MyBots::PopulateSBL_BySpawnedBots(bot_owner, selectable_bot_list); for (auto bot_iter : selectable_bot_list) { if (!bot_name.compare(bot_iter->GetCleanName())) @@ -1228,7 +1228,7 @@ namespace ActionableBots return nullptr; } - static Bot* Select_ByClass(Client* bot_owner, BCEnum::TType target_type, std::list& sbl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { + static Bot* Select_ByClass(Client* bot_owner, BCEnum::TType target_type, std::vector& sbl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { if (!bot_owner || sbl.empty()) return nullptr; @@ -1252,7 +1252,7 @@ namespace ActionableBots return nullptr; } - static Bot* Select_ByMinLevelAndClass(Client* bot_owner, BCEnum::TType target_type, std::list& sbl, uint8 minlvl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { + static Bot* Select_ByMinLevelAndClass(Client* bot_owner, BCEnum::TType target_type, std::vector& sbl, uint8 minlvl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { if (!bot_owner || sbl.empty()) return nullptr; @@ -1277,23 +1277,23 @@ namespace ActionableBots } // Filters actual 'sbl' list - static void Filter_ByClasses(Client* bot_owner, std::list& sbl, uint16 class_mask) { - sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); - sbl.remove_if([class_mask](const Bot* l) { return (GetPlayerClassBit(l->GetClass()) & (~class_mask)); }); + static void Filter_ByClasses(Client* bot_owner, std::vector& sbl, uint16 class_mask) { + std::erase_if(sbl, [bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); + std::erase_if(sbl, [class_mask](Bot* l) { return (GetPlayerClassBit(l->GetClass()) & (~class_mask)); }); } - static void Filter_ByMinLevel(Client* bot_owner, std::list& sbl, uint8 min_level) { - sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); - sbl.remove_if([min_level](const Bot* l) { return (l->GetLevel() < min_level); }); + static void Filter_ByMinLevel(Client* bot_owner, std::vector& sbl, uint8 min_level) { + std::erase_if(sbl, [bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); + std::erase_if(sbl, [min_level](Bot* l) { return (l->GetLevel() < min_level); }); } - static void Filter_ByRanged(Client* bot_owner, std::list& sbl) { - sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); - sbl.remove_if([bot_owner](Bot* l) { return (!l->IsBotRanged()); }); + static void Filter_ByRanged(Client* bot_owner, std::vector& sbl) { + std::erase_if(sbl, [bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); + std::erase_if(sbl, [bot_owner](Bot* l) { return (l->IsBotRanged()); }); } - static void Filter_ByHighestSkill(Client* bot_owner, std::list& sbl, EQ::skills::SkillType skill_type, float& skill_value) { - sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); + static void Filter_ByHighestSkill(Client* bot_owner, std::vector& sbl, EQ::skills::SkillType skill_type, float& skill_value) { + std::erase_if(sbl, [bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); skill_value = 0.0f; float mod_skill_value = 0.0f; @@ -1319,15 +1319,14 @@ namespace ActionableBots skilled_bot = bot_iter; } } - - sbl.remove_if([skilled_bot](const Bot* l) { return (l != skilled_bot); }); + std::erase_if(sbl, [skilled_bot](Bot* l) { return (l != skilled_bot); }); } - static void Filter_ByHighestPickLock(Client* bot_owner, std::list& sbl, float& pick_lock_value) { - sbl.remove_if([bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); - sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() != Class::Rogue && l->GetClass() != Class::Bard); }); - sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() == Class::Rogue && l->GetLevel() < 5); }); - sbl.remove_if([bot_owner](const Bot* l) { return (l->GetClass() == Class::Bard && l->GetLevel() < 40); }); + static void Filter_ByHighestPickLock(Client* bot_owner, std::vector& sbl, float& pick_lock_value) { + std::erase_if(sbl, [bot_owner](Bot* l) { return (!MyBots::IsMyBot(bot_owner, l)); }); + std::erase_if(sbl, [bot_owner](Bot* l) { return (l->GetClass() != Class::Rogue && l->GetClass() != Class::Bard); }); + std::erase_if(sbl, [bot_owner](Bot* l) { return (l->GetClass() == Class::Rogue && l->GetLevel() < 5); }); + std::erase_if(sbl, [bot_owner](Bot* l) { return (l->GetClass() == Class::Bard && l->GetLevel() < 40); }); ActionableBots::Filter_ByHighestSkill(bot_owner, sbl, EQ::skills::SkillPickLock, pick_lock_value); } diff --git a/zone/bot_commands/aggressive.cpp b/zone/bot_commands/aggressive.cpp index b40bf71232..7f26b2a49d 100644 --- a/zone/bot_commands/aggressive.cpp +++ b/zone/bot_commands/aggressive.cpp @@ -24,13 +24,13 @@ void bot_command_aggressive(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); int success_count = 0; int candidate_count = sbl.size(); diff --git a/zone/bot_commands/appearance.cpp b/zone/bot_commands/appearance.cpp index c6aed9e08f..9ca820e60e 100644 --- a/zone/bot_commands/appearance.cpp +++ b/zone/bot_commands/appearance.cpp @@ -214,7 +214,7 @@ void bot_command_dye_armor(Client *c, const Seperator *sep) uint32 rgb_value = ((uint32)red_value << 16) | ((uint32)green_value << 8) | ((uint32)blue_value); - std::list sbl; + std::vector sbl; auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[5], sbl, ab_mask); if (ab_type == ActionableBots::ABT_None) { return; diff --git a/zone/bot_commands/attack.cpp b/zone/bot_commands/attack.cpp index 8cce85db1b..6b8a929cbe 100644 --- a/zone/bot_commands/attack.cpp +++ b/zone/bot_commands/attack.cpp @@ -34,7 +34,7 @@ void bot_command_attack(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, ab_arg.c_str(), sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { return; @@ -47,7 +47,7 @@ void bot_command_attack(Client *c, const Seperator *sep) size_t attacker_count = 0; Bot *first_attacker = nullptr; - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto bot_iter : sbl) { if (bot_iter->GetAppearance() != eaDead && bot_iter->GetBotStance() != Stance::Passive) { diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 3abde3c1ac..0385ad5b20 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -118,12 +118,12 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index 69a1aa13aa..d41dd54bc5 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -162,13 +162,13 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); bool isSuccess = false; uint16 successCount = 0; @@ -421,13 +421,13 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); bool isSuccess = false; uint16 successCount = 0; diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index c836888b27..6678cb5211 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -51,7 +51,7 @@ void bot_command_camp(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, !class_race_check ? sep->arg[2] : nullptr, class_race_check ? atoi(sep->arg[2]) : 0) == ActionableBots::ABT_None) { return; } @@ -611,13 +611,13 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); int botCount = 0; for (auto bot_iter : sbl) { @@ -704,7 +704,7 @@ void bot_command_inspect_message(Client *c, const Seperator *sep) return; } - std::list sbl; + std::vector sbl; auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]); if (ab_type == ActionableBots::ABT_None) return; @@ -947,7 +947,7 @@ void bot_command_report(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -1377,7 +1377,7 @@ void bot_command_stance(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -1519,13 +1519,13 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; @@ -1614,12 +1614,12 @@ void bot_command_summon(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto bot_iter : sbl) { if (!bot_iter) { @@ -1695,7 +1695,7 @@ void bot_command_toggle_ranged(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; @@ -1747,7 +1747,7 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) ++ab_arg; } - std::list sbl; + std::vector sbl; auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]); if (ab_type == ActionableBots::ABT_None) return; @@ -1844,7 +1844,7 @@ void bot_command_update(Client *c, const Seperator *sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_BySpawnedBots(c, sbl); if (sbl.empty()) { c->Message(Chat::White, "You currently have no spawned bots"); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index d02c9f2a92..9abd8eb559 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -466,13 +466,13 @@ void bot_command_cast(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); BotSpell botSpell; botSpell.SpellId = 0; diff --git a/zone/bot_commands/click_item.cpp b/zone/bot_commands/click_item.cpp index 469750f59f..a101dfbd26 100644 --- a/zone/bot_commands/click_item.cpp +++ b/zone/bot_commands/click_item.cpp @@ -39,13 +39,13 @@ void bot_command_click_item(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto my_bot : sbl) { if (my_bot->BotPassiveCheck()) { diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 2e3ecc9c2b..0843e9a8fd 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -187,12 +187,12 @@ void bot_command_default_settings(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/defensive.cpp b/zone/bot_commands/defensive.cpp index 081e07f342..4fcf0fce2b 100644 --- a/zone/bot_commands/defensive.cpp +++ b/zone/bot_commands/defensive.cpp @@ -22,12 +22,12 @@ void bot_command_defensive(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); int success_count = 0; int candidate_count = sbl.size(); diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 23bb375b16..c82acd1baa 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -147,13 +147,13 @@ void bot_command_depart(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); BotSpell botSpell; botSpell.SpellId = 0; diff --git a/zone/bot_commands/distance_ranged.cpp b/zone/bot_commands/distance_ranged.cpp index 7c7dec82a6..35db3ac759 100644 --- a/zone/bot_commands/distance_ranged.cpp +++ b/zone/bot_commands/distance_ranged.cpp @@ -46,12 +46,12 @@ void bot_command_distance_ranged(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index f970773d7b..34e16b7f36 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -117,12 +117,12 @@ void bot_command_follow(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); auto botCount = sbl.size(); Mob* follow_mob = nullptr; diff --git a/zone/bot_commands/guard.cpp b/zone/bot_commands/guard.cpp index 1cacd27426..324bfb03cf 100644 --- a/zone/bot_commands/guard.cpp +++ b/zone/bot_commands/guard.cpp @@ -30,12 +30,12 @@ void bot_command_guard(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto bot_iter : sbl) { if (clear) { diff --git a/zone/bot_commands/heal_rotation.cpp b/zone/bot_commands/heal_rotation.cpp index b755016fcc..0dd57eb377 100644 --- a/zone/bot_commands/heal_rotation.cpp +++ b/zone/bot_commands/heal_rotation.cpp @@ -63,7 +63,7 @@ void bot_command_heal_rotation_adaptive_targeting(Client* c, const Seperator* se std::string adaptive_targeting_arg; - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (!sbl.empty()) { adaptive_targeting_arg = sep->arg[2]; @@ -123,7 +123,7 @@ void bot_command_heal_rotation_add_member(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { c->Message(Chat::White, "You must [name] a new member as a bot that you own to use this command"); @@ -191,7 +191,7 @@ void bot_command_heal_rotation_add_target(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[2]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -283,7 +283,7 @@ void bot_command_heal_rotation_adjust_critical(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[3]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -383,7 +383,7 @@ void bot_command_heal_rotation_adjust_safe(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[3]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -460,7 +460,7 @@ void bot_command_heal_rotation_casting_override(Client* c, const Seperator* sep) std::string casting_override_arg; - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (!sbl.empty()) { casting_override_arg = sep->arg[2]; @@ -534,7 +534,7 @@ void bot_command_heal_rotation_change_interval(Client* c, const Seperator* sep) std::string change_interval_arg; - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (!sbl.empty()) { change_interval_arg = sep->arg[2]; @@ -603,7 +603,7 @@ void bot_command_heal_rotation_clear_hot(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -649,7 +649,7 @@ void bot_command_heal_rotation_clear_targets(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -703,7 +703,7 @@ void bot_command_heal_rotation_create(Client* c, const Seperator* sep) std::string adaptive_targeting_arg; std::string casting_override_arg; - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (!sbl.empty()) { interval_arg = sep->arg[2]; @@ -858,7 +858,7 @@ void bot_command_heal_rotation_delete(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[name_arg]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -896,7 +896,7 @@ void bot_command_heal_rotation_fast_heals(Client* c, const Seperator* sep) std::string fast_heals_arg; - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (!sbl.empty()) { fast_heals_arg = sep->arg[2]; @@ -956,7 +956,7 @@ void bot_command_heal_rotation_list(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -1076,7 +1076,7 @@ void bot_command_heal_rotation_remove_member(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -1123,7 +1123,7 @@ void bot_command_heal_rotation_remove_target(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[2]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -1181,7 +1181,7 @@ void bot_command_heal_rotation_reset_limits(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -1223,7 +1223,7 @@ void bot_command_heal_rotation_save(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -1272,7 +1272,7 @@ void bot_command_heal_rotation_set_hot(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[2]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -1337,7 +1337,7 @@ void bot_command_heal_rotation_start(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); @@ -1384,7 +1384,7 @@ void bot_command_heal_rotation_stop(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]); if (sbl.empty()) { MyBots::PopulateSBL_ByTargetedBot(c, sbl); diff --git a/zone/bot_commands/hold.cpp b/zone/bot_commands/hold.cpp index 0fad872dc7..e4528a9909 100644 --- a/zone/bot_commands/hold.cpp +++ b/zone/bot_commands/hold.cpp @@ -30,12 +30,12 @@ void bot_command_hold(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[name_arg] : nullptr, class_race_check ? atoi(sep->arg[name_arg]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto bot_iter : sbl) { if (clear) { diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 47d2d6b264..1ad5e8465e 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -118,12 +118,12 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/inventory.cpp b/zone/bot_commands/inventory.cpp index 7371475ed3..b83ce228c0 100644 --- a/zone/bot_commands/inventory.cpp +++ b/zone/bot_commands/inventory.cpp @@ -34,7 +34,7 @@ void bot_command_inventory_give(Client* c, const Seperator* sep) int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) { return; } @@ -67,7 +67,7 @@ void bot_command_inventory_list(Client* c, const Seperator* sep) int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) { return; } @@ -168,7 +168,7 @@ void bot_command_inventory_remove(Client* c, const Seperator* sep) return; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[2], sbl, ab_mask, sep->arg[3]) == ActionableBots::ABT_None) { return; } @@ -311,7 +311,7 @@ void bot_command_inventory_window(Client* c, const Seperator* sep) int ab_mask = ActionableBots::ABM_Target; - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) { return; } diff --git a/zone/bot_commands/item_use.cpp b/zone/bot_commands/item_use.cpp index 0db5983dcb..f76c5e48b2 100644 --- a/zone/bot_commands/item_use.cpp +++ b/zone/bot_commands/item_use.cpp @@ -136,13 +136,13 @@ void bot_command_item_use(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); EQ::SayLinkEngine linker; linker.SetLinkType(EQ::saylink::SayLinkItemData); diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp index 6c4460ac2d..a8df8eeccb 100644 --- a/zone/bot_commands/max_melee_range.cpp +++ b/zone/bot_commands/max_melee_range.cpp @@ -117,12 +117,12 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index d0345ced26..ce01596ad8 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -25,7 +25,7 @@ void bot_command_pet_get_lost(Client *c, const Seperator *sep) } int ab_mask = ActionableBots::ABM_NoFilter; - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) return; @@ -56,7 +56,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep) } int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) return; @@ -66,7 +66,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep) c->Message(Chat::White, "You have no spawned bots capable of charming"); return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); int charmed_pet = 0; int summoned_pet = 0; @@ -241,13 +241,13 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); std::string currentType; uint16 reclaim_energy_id = RuleI(Bots, ReclaimEnergySpellID); diff --git a/zone/bot_commands/pick_lock.cpp b/zone/bot_commands/pick_lock.cpp index 7c4379458e..d34ab6f983 100644 --- a/zone/bot_commands/pick_lock.cpp +++ b/zone/bot_commands/pick_lock.cpp @@ -11,7 +11,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_BySpawnedBots(c, sbl); float pick_lock_value = 0.0f; diff --git a/zone/bot_commands/pickpocket.cpp b/zone/bot_commands/pickpocket.cpp index 5aec323832..8b5339cca1 100644 --- a/zone/bot_commands/pickpocket.cpp +++ b/zone/bot_commands/pickpocket.cpp @@ -15,7 +15,7 @@ void bot_command_pickpocket(Client *c, const Seperator *sep) return; } - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_BySpawnedBots(c, sbl); // Check for capable rogue diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index 5357437122..9d60408bc9 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -28,13 +28,13 @@ void bot_command_pull(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single); if (!target_mob) { diff --git a/zone/bot_commands/release.cpp b/zone/bot_commands/release.cpp index bf8741d33d..9bd9d979c2 100644 --- a/zone/bot_commands/release.cpp +++ b/zone/bot_commands/release.cpp @@ -10,11 +10,11 @@ void bot_command_release(Client *c, const Seperator *sep) } const int ab_mask = ActionableBots::ABM_NoFilter; - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) return; - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto bot_iter : sbl) { bot_iter->WipeHateList(); bot_iter->SetPauseAI(false); diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index a8f6d76ff1..e374ac5e28 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -117,12 +117,12 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index eaf08e0ae0..e40c79dc26 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -117,12 +117,12 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 0c538023cf..6c2f2fd43d 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -117,12 +117,12 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index c1b8241016..e4c64a24c7 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -177,12 +177,12 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 165770cbc9..ea7250e8c4 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -183,12 +183,12 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 2c5208d145..b934d5ccb9 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -181,12 +181,12 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index d83b5369b7..4f14129c43 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -167,12 +167,12 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 9576d4a8f0..82676db3c8 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -181,12 +181,12 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 42c552cdb5..50c83e6dd3 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -177,12 +177,12 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index da8ad9d369..2bd4aa3230 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -177,12 +177,12 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index ed3316c012..432e506fd4 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -183,12 +183,12 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 3c512ab3ad..359ca1d4fe 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -177,12 +177,12 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index 43c37ed8cb..7963f14a12 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -177,12 +177,12 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index 4c1b11a3a3..2c0257ccbe 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -185,12 +185,12 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 864bc2ba93..fb90ccd1ef 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -181,12 +181,12 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index 066fe95cc0..ff9ea75873 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -177,12 +177,12 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); Bot* first_found = nullptr; int success_count = 0; diff --git a/zone/bot_commands/suspend.cpp b/zone/bot_commands/suspend.cpp index 287db739cd..63c728fd3d 100644 --- a/zone/bot_commands/suspend.cpp +++ b/zone/bot_commands/suspend.cpp @@ -11,12 +11,12 @@ void bot_command_suspend(Client *c, const Seperator *sep) } const int ab_mask = ActionableBots::ABM_NoFilter; - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto bot_iter : sbl) { bot_iter->SetPauseAI(true); } diff --git a/zone/bot_commands/taunt.cpp b/zone/bot_commands/taunt.cpp index 866e9899c4..39d1675ff5 100644 --- a/zone/bot_commands/taunt.cpp +++ b/zone/bot_commands/taunt.cpp @@ -36,13 +36,13 @@ void bot_command_taunt(Client *c, const Seperator *sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[(ab_arg + 1)] : nullptr, class_race_check ? atoi(sep->arg[(ab_arg + 1)]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); int taunting_count = 0; for (auto bot_iter : sbl) { diff --git a/zone/bot_commands/timer.cpp b/zone/bot_commands/timer.cpp index 30b495d3ab..99ad38001b 100644 --- a/zone/bot_commands/timer.cpp +++ b/zone/bot_commands/timer.cpp @@ -95,13 +95,13 @@ void bot_command_timer(Client* c, const Seperator* sep) class_race_check = true; } - std::list sbl; + std::vector sbl; if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - sbl.remove(nullptr); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); for (auto my_bot : sbl) { bool found = false; diff --git a/zone/bot_commands/track.cpp b/zone/bot_commands/track.cpp index 9ac0904a2a..f134c45965 100644 --- a/zone/bot_commands/track.cpp +++ b/zone/bot_commands/track.cpp @@ -13,7 +13,7 @@ void bot_command_track(Client *c, const Seperator *sep) std::string tracking_scope = sep->arg[1]; - std::list sbl; + std::vector sbl; MyBots::PopulateSBL_BySpawnedBots(c, sbl); uint16 class_mask = (player_class_bitmasks[Class::Ranger] | player_class_bitmasks[Class::Druid] | player_class_bitmasks[Class::Bard]); diff --git a/zone/entity.cpp b/zone/entity.cpp index 3176639271..a756c9fdc2 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -1900,13 +1900,13 @@ Bot* EntityList::GetRandomBot(const glm::vec3& location, float distance, Bot* ex for (const auto& b : bot_list) { if ( - b != exclude_bot && + b.second != exclude_bot && ( is_whole_zone || - DistanceSquared(static_cast(b->GetPosition()), location) <= distance_squared - ) + DistanceSquared(static_cast(b.second->GetPosition()), location) <= distance_squared + ) ) { - bots_in_range.push_back(b); + bots_in_range.push_back(b.second); } } @@ -5121,9 +5121,10 @@ void EntityList::GetClientList(std::list &c_list) void EntityList::GetBotList(std::list &b_list) { b_list.clear(); - - for (const auto& b : bot_list) { - b_list.push_back(b); + auto it = bot_list.begin(); + while (it != bot_list.end()) { + b_list.push_back(it->second); + ++it; } } @@ -5135,14 +5136,13 @@ std::vector EntityList::GetBotListByCharacterID(uint32 character_id, uint return client_bot_list; } - for (const auto& b : bot_list) { - if ( - b->GetOwner() && - b->GetBotOwnerCharacterID() == character_id && - (!class_id || b->GetClass() == class_id) - ) { - client_bot_list.push_back(b); + auto it = bot_list.begin(); + + while (it != bot_list.end()) { + if (it->second->GetOwner() && it->second->GetBotOwnerCharacterID() == character_id && (!class_id || it->second->GetClass() == class_id)) { + client_bot_list.push_back(it->second); } + ++it; } return client_bot_list; @@ -5156,14 +5156,13 @@ std::vector EntityList::GetBotListByClientName(std::string client_name, u return client_bot_list; } - for (const auto& b : bot_list) { - if ( - b->GetOwner() && - Strings::ToLower(b->GetOwner()->GetCleanName()) == Strings::ToLower(client_name) && - (!class_id || b->GetClass() == class_id) - ) { - client_bot_list.push_back(b); + auto it = bot_list.begin(); + + while (it != bot_list.end()) { + if (it->second->GetOwner() && Strings::ToLower(it->second->GetOwner()->GetCleanName()) == Strings::ToLower(client_name) && (!class_id || it->second->GetClass() == class_id)) { + client_bot_list.push_back(it->second); } + ++it; } return client_bot_list; diff --git a/zone/entity.h b/zone/entity.h index c9d48bc7d0..4b5c9b6908 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -547,7 +547,7 @@ class EntityList inline const std::unordered_map &GetNPCList() { return npc_list; } inline const std::unordered_map &GetMercList() { return merc_list; } inline const std::unordered_map &GetClientList() { return client_list; } - inline const std::list &GetBotList() { return bot_list; } + inline const std::unordered_map &GetBotList() { return bot_list; } std::vector GetBotListByCharacterID(uint32 character_id, uint8 class_id = Class::None); std::vector GetBotListByClientName(std::string client_name, uint8 class_id = Class::None); void SignalAllBotsByOwnerCharacterID(uint32 character_id, int signal_id); @@ -623,10 +623,10 @@ class EntityList bool RemoveBot(uint16 entityID); Mob* GetMobByBotID(uint32 botID); Bot* GetBotByBotID(uint32 botID); - Bot* GetBotByBotName(std::string_view botName); + Bot* GetBotByBotName(std::string botName); Client* GetBotOwnerByBotEntityID(uint32 entity_id); Client* GetBotOwnerByBotID(const uint32 bot_id); - std::list GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); + std::vector GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID); void ShowSpawnWindow(Client* client, int Distance, bool NamedOnly); // TODO: Implement ShowSpawnWindow in the bot class but it needs entity list stuff @@ -634,7 +634,7 @@ class EntityList void GetBotList(std::list &b_list); private: - std::list bot_list; + std::unordered_map bot_list; }; class BulkZoneSpawnPacket { diff --git a/zone/gm_commands/list.cpp b/zone/gm_commands/list.cpp index 5019c09b84..7e05a1d6a2 100755 --- a/zone/gm_commands/list.cpp +++ b/zone/gm_commands/list.cpp @@ -74,20 +74,22 @@ void command_list(Client *c, const Seperator *sep) if (is_bots) { const auto& l = entity_list.GetBotList(); - for (const auto& e: l) { + for (const auto& e : l) { + Bot* entity = e.second; + entity_count++; - const std::string& entity_name = Strings::ToLower(e->GetName()); + const std::string& entity_name = Strings::ToLower(entity->GetName()); if (!search_string.empty() && !Strings::Contains(entity_name, search_string)) { continue; } unique_entities.emplace_back( UniqueEntity{ - .entity_id = e->GetID(), - .entity_name = e->GetName(), - .unique_id = e->GetBotID(), - .position = e->GetPosition() + .entity_id = entity->GetID(), + .entity_name = entity->GetName(), + .unique_id = entity->GetBotID(), + .position = entity->GetPosition() } ); diff --git a/zone/lua_entity_list.cpp b/zone/lua_entity_list.cpp index 45c0e66210..4b99c869ec 100644 --- a/zone/lua_entity_list.cpp +++ b/zone/lua_entity_list.cpp @@ -382,12 +382,13 @@ Lua_Bot Lua_EntityList::GetBotByName(std::string bot_name) { Lua_Bot_List Lua_EntityList::GetBotList() { Lua_Safe_Call_Class(Lua_Bot_List); Lua_Bot_List ret; - auto &bot_list = self->GetBotList(); - if (bot_list.size()) { - for (auto bot : bot_list) { - ret.entries.emplace_back(Lua_Bot(bot)); - } + auto ¤t_bot_list = self->GetBotList(); + auto it = current_bot_list.begin(); + + while (it != current_bot_list.end()) { + ret.entries.emplace_back(it->second); + ++it; } return ret; diff --git a/zone/perl_entity.cpp b/zone/perl_entity.cpp index 64860f4a27..eaad24db5e 100644 --- a/zone/perl_entity.cpp +++ b/zone/perl_entity.cpp @@ -419,10 +419,13 @@ perl::array Perl_EntityList_GetBotList(EntityList* self) // @categories Script U { perl::array result; auto current_bot_list = self->GetBotList(); - for (Bot* bot_iterator : current_bot_list) - { - result.push_back(bot_iterator); + auto it = current_bot_list.begin(); + + while (it != current_bot_list.end()) { + result.push_back(it->second); + ++it; } + return result; } From 6ced4e6a9cf9be90225ac40e8c3562d94a77cad9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:27:42 -0600 Subject: [PATCH 239/394] Fix missing raid assignment for GetStoredRaid in IsInGroupOrRaid --- zone/mob.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index e28b0b8a91..918326067e 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9966,7 +9966,7 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { Raid* raid = nullptr; if (IsBot()) { - CastToBot()->GetStoredRaid(); + raid = CastToBot()->GetStoredRaid(); } else { if (IsRaidGrouped()) { From f438650e86e2b20e092ebcb05bcab1b58b5cc34e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:28:07 -0600 Subject: [PATCH 240/394] TempPet owned by bots that get the kill will now give exp like a client would --- zone/attack.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index d1c862096b..5da213bb0f 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2625,9 +2625,11 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy if ( give_exp->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) || - give_exp->IsPet() && - give_exp->GetOwner() && - give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) + ( + give_exp->IsPet() && + give_exp->GetOwner() && + give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) + ) ) { owner_in_group = true; } @@ -2639,7 +2641,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy } } - if (give_exp && give_exp->IsTempPet() && give_exp->IsPetOwnerClient()) { + if (give_exp && give_exp->IsTempPet() && (give_exp->IsPetOwnerClient() || give_exp->IsPetOwnerBot())) { if (give_exp->IsNPC() && give_exp->CastToNPC()->GetSwarmOwner()) { Mob* temp_owner = entity_list.GetMobID(give_exp->CastToNPC()->GetSwarmOwner()); if (temp_owner) { From 25ff56ca2352398cd5cc4ecc64ce4791dbb67431 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:28:49 -0600 Subject: [PATCH 241/394] Remove unnecessary checks in bot process (closescanmoving timer, verify raid, send hp/mana/end packet --- zone/bot.cpp | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 5d9da07273..46d762ad16 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1637,10 +1637,10 @@ bool Bot::Process() entity_list.ScanCloseMobs(this); } - if (m_mob_check_moving_timer.Check()) { //TODO bot rewrite - is this necessary - CheckScanCloseMobsMovingTimer(); - } - + //if (m_mob_check_moving_timer.Check()) { //TODO bot rewrite - is this necessary + // CheckScanCloseMobsMovingTimer(); + //} + // SpellProcess(); if (tic_timer.Check()) { @@ -2065,19 +2065,20 @@ void Bot::AI_Process() } auto raid = entity_list.GetRaidByBot(this); + SetStoredRaid(raid); uint32 r_group = RAID_GROUPLESS; if (raid) { - raid->VerifyRaid(); + //raid->VerifyRaid(); r_group = raid->GetGroup(GetName()); - - if (mana_timer.Check(false)) { - raid->SendHPManaEndPacketsFrom(this); - } - - if (send_hp_update_timer.Check(false)) { - raid->SendHPManaEndPacketsFrom(this); - } + + //if (mana_timer.Check(false)) { + // raid->SendHPManaEndPacketsFrom(this); + //} + // + //if (send_hp_update_timer.Check(false)) { + // raid->SendHPManaEndPacketsFrom(this); + //} } auto bot_group = GetGroup(); @@ -2132,7 +2133,6 @@ void Bot::AI_Process() return; } - SetStoredRaid(raid); std::vector spellTargetList = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); SetSpellTargetList(spellTargetList); std::vector groupSpellTargetList = GatherSpellTargets(); From 9b5eca3b2d0ada7e230c32c52ed21b3a299b7cae Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:44:48 -0600 Subject: [PATCH 242/394] Fix client spell commands from saving the wrong setting --- zone/gm_commands/spell_delays.cpp | 2 +- zone/gm_commands/spell_max_thresholds.cpp | 6 +++--- zone/gm_commands/spell_min_thresholds.cpp | 6 +++--- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp index f7fda40ee7..1a8a6c40c4 100644 --- a/zone/gm_commands/spell_delays.cpp +++ b/zone/gm_commands/spell_delays.cpp @@ -165,7 +165,7 @@ void command_spell_delays(Client* c, const Seperator* sep) ); } else { - c->SetSpellHold(spellType, typeValue); + c->SetSpellDelay(spellType, typeValue); c->Message( Chat::Green, fmt::format( diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp index 3e0188baa9..f4aa137f8c 100644 --- a/zone/gm_commands/spell_max_thresholds.cpp +++ b/zone/gm_commands/spell_max_thresholds.cpp @@ -158,18 +158,18 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your [{}] maximum hold is currently [{}]%%.'", + "Your [{}] maximum hold is currently [{}%%].'", c->GetSpellTypeNameByID(spellType), c->GetSpellMaxThreshold(spellType) ).c_str() ); } else { - c->SetSpellHold(spellType, typeValue); + c->SetSpellMaxThreshold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your [{}] maximum hold was set to [{}]%%.'", + "Your [{}] maximum hold was set to [{}%%].'", c->GetSpellTypeNameByID(spellType), c->GetSpellMaxThreshold(spellType) ).c_str() diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp index c2b3893d7b..79eabc5959 100644 --- a/zone/gm_commands/spell_min_thresholds.cpp +++ b/zone/gm_commands/spell_min_thresholds.cpp @@ -158,18 +158,18 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) c->Message( Chat::Green, fmt::format( - "Your [{}] minimum hold is currently [{}]%%.'", + "Your [{}] minimum hold is currently [{}%%].'", c->GetSpellTypeNameByID(spellType), c->GetSpellMinThreshold(spellType) ).c_str() ); } else { - c->SetSpellHold(spellType, typeValue); + c->SetSpellMinThreshold(spellType, typeValue); c->Message( Chat::Green, fmt::format( - "Your [{}] minimum hold was set to [{}]%%.'", + "Your [{}] minimum hold was set to [{}%%].'", c->GetSpellTypeNameByID(spellType), c->GetSpellMinThreshold(spellType) ).c_str() From 5a000f1259e8a9881d862f7faceb7706beaa0f47 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 23 Dec 2024 23:50:37 -0600 Subject: [PATCH 243/394] Cleanup ^copysettings command and add new commands --- zone/bot_commands/copy_settings.cpp | 270 ++++++++++++++-------------- 1 file changed, 130 insertions(+), 140 deletions(-) diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 34a5cfcd25..415dccb826 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -55,7 +55,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) std::vector options = { - "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, showhelm, followd, stopmeleelevel, enforcespellsettings, bottoggleranged, petsettype, behindmob, distanceranged, illusionblock, sitincombat, sithppercent, sitmanapercent, blockedbuffs, blockedpetbuffs" + "[all], [misc], [spellsettings], [spelltypesettings], [spellholds], [spelldelays], [spellminthresholds], [spellmaxthresholds], [spellminmanapct], [spellmaxmanapct], [spellminhppct], [spellmaxhppct], [spellidlepriority], [spellengagedpriority], [spellpursuepriority], [spellaggrochecks], [spelltargetcounts], [sithppercent], [sitmanapercent], [blockedbuffs], [blockedpetbuffs]" }; std::vector options_one = { @@ -108,6 +108,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) int ab_arg = 2; bool validOption = false; uint16 spellType = UINT16_MAX; + uint16 settingType = UINT16_MAX; std::vector options = { "all", @@ -127,49 +128,24 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) "spellpursuepriority", "spellaggrochecks", "spelltargetcounts", - "showhelm", - "followd", - "stopmeleelevel", - "enforcespellsettings", - "bottoggleranged", - "petsettype", - "behindmob", - "distanceranged", - "illusionblock", - "sitincombat", - "sithppercent", - "sitmanapercent", "blockedbuffs", "blockedpetbuffs" }; - for (int i = 0; i < options.size(); i++) { - if (sep->arg[3] == options[i]) { - if (options[i] != "all" && options[i] != "misc" && options[i] != "spellsettings") { - - if (sep->IsNumber(4) || c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX) { - if (sep->IsNumber(4)) { - spellType = atoi(sep->arg[4]); - } - - if (c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(sep->arg[4]); - } - - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + if (sep->IsNumber(4)) { + spellType = atoi(sep->arg[4]); + } + else { + spellType = c->GetSpellTypeIDByShortName(sep->arg[4]); + } - return; - } - } - } - else if ( - (options[i] == "all" || options[i] == "misc" || options[i] == "spellsettings") && - ((sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[4]) != UINT16_MAX)) - ) { - break; - } + if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + spellType = UINT16_MAX; + } + for (int i = 0; i < options.size(); i++) { + if (sep->arg[3] == options[i]) { + settingType = c->GetBotSpellCategoryIDByShortName(sep->arg[3]); validOption = true; break; } @@ -243,113 +219,127 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) std::string output = ""; - if (!strcasecmp(sep->arg[3], "misc")) { - from->CopySettings(to, BotSettingCategories::BaseSetting); - output = "Miscellaneous"; - } - else if (!strcasecmp(sep->arg[3], "holds")) { - from->CopySettings(to, BotSettingCategories::SpellHold, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellHold); - } - else if (!strcasecmp(sep->arg[3], "delays")) { - from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellDelay); - } - else if (!strcasecmp(sep->arg[3], "minthresholds")) { - from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellMinThreshold); - } - else if (!strcasecmp(sep->arg[3], "maxthresholds")) { - from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellMaxThreshold); - } - else if (!strcasecmp(sep->arg[3], "aggrochecks")) { - from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeAggroCheck); - } - else if (!strcasecmp(sep->arg[3], "minmanapct")) { - from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMinManaPct); - } - else if (!strcasecmp(sep->arg[3], "maxmanapct")) { - from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMaxManaPct); - } - else if (!strcasecmp(sep->arg[3], "minhppct")) { - from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMinHPPct); - } - else if (!strcasecmp(sep->arg[3], "maxhppct")) { - from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeMaxHPPct); - } - else if (!strcasecmp(sep->arg[3], "idlepriority")) { - from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeIdlePriority); - } - else if (!strcasecmp(sep->arg[3], "engagedpriority")) { - from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeEngagedPriority); - } - else if (!strcasecmp(sep->arg[3], "pursuepriority")) { - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypePursuePriority); - } - else if (!strcasecmp(sep->arg[3], "targetcounts")) { - from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); - output = from->GetBotSpellCategoryName(BotSettingCategories::SpellTypeAEOrGroupTargetCount); - } - else if (!strcasecmp(sep->arg[3], "spellsettings")) { - from->CopyBotSpellSettings(to); - output = "^spellsettings"; - } - else if (!strcasecmp(sep->arg[3], "spelltypesettings")) { - from->CopySettings(to, BotSettingCategories::SpellHold, spellType); - from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); - from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); - output = "spell type"; - } - else if (!strcasecmp(sep->arg[3], "all")) { - from->CopySettings(to, BotSettingCategories::BaseSetting); - from->CopySettings(to, BotSettingCategories::SpellHold, spellType); - from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); - from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); - from->CopyBotSpellSettings(to); + if (settingType != UINT16_MAX) { + if (spellType != UINT16_MAX) { + from->CopySettings(to, settingType, spellType); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + from->CopySettings(to, settingType, i); + } + } + + output = from->GetBotSpellCategoryName(settingType); } else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); + if (!strcasecmp(sep->arg[3], "misc")) { + from->CopySettings(to, BotSettingCategories::BaseSetting); + output = "Miscellaneous"; + } + else if (!strcasecmp(sep->arg[3], "spellsettings")) { + from->CopyBotSpellSettings(to); + output = "^spellsettings"; + } + else if (!strcasecmp(sep->arg[3], "spelltypesettings")) { + if (spellType != UINT16_MAX) { + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + from->CopySettings(to, BotSettingCategories::SpellHold, i); + from->CopySettings(to, BotSettingCategories::SpellDelay, i); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i); + } + } - return; + output = "spell type"; + } + else if (!strcasecmp(sep->arg[3], "blockedbuffs")) { + from->CopyBotBlockedBuffs(to); + } + else if (!strcasecmp(sep->arg[3], "blockedpetbuffs")) { + from->CopyBotBlockedPetBuffs(to); + } + else if (!strcasecmp(sep->arg[3], "all")) { + from->CopySettings(to, BotSettingCategories::BaseSetting); + + if (spellType != UINT16_MAX) { + from->CopySettings(to, BotSettingCategories::SpellHold, spellType); + from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + from->CopySettings(to, BotSettingCategories::SpellHold, i); + from->CopySettings(to, BotSettingCategories::SpellDelay, i); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i); + } + } + + from->CopyBotSpellSettings(to); + from->CopyBotBlockedBuffs(to); + from->CopyBotBlockedPetBuffs(to); + output = "spell type"; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } } + to->Save(); + c->Message( Chat::Green, fmt::format( From 8b7ffd71176904fac3ce20abfde740cf05c5a301 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 24 Dec 2024 08:07:13 -0600 Subject: [PATCH 244/394] Add pet option to ^taunt No longer has toggle, required on/off option and an optional "pet" option to control pets' taunting state --- zone/bot_commands/taunt.cpp | 211 ++++++++++++++++++++++++++---------- 1 file changed, 151 insertions(+), 60 deletions(-) diff --git a/zone/bot_commands/taunt.cpp b/zone/bot_commands/taunt.cpp index 39d1675ff5..c2c4917d6a 100644 --- a/zone/bot_commands/taunt.cpp +++ b/zone/bot_commands/taunt.cpp @@ -1,34 +1,136 @@ #include "../bot_command.h" -void bot_command_taunt(Client *c, const Seperator *sep) +void bot_command_taunt(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) { return; } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + std::vector description = + { + "Allows you to turn on/off the taunting state of your bots and/or their pets" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [on / off / pet] [optional: pet] [actionable, default: target]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To turn off taunt on all bots:", + fmt::format( + "{} off spawned", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To turn on taunt on all bots' pets:", + fmt::format( + "{} on pet spawned", + sep->arg[0] + ) + }; + std::vector examples_three = + { + "To turn on taunt for all ShadowKnights:", + fmt::format( + "{} on byclass {}", + sep->arg[0], + Class::ShadowKnight + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + return; } - const int ab_mask = ActionableBots::ABM_Type1; - std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + + bool tauntState = false; + bool petTaunt = false; + bool validOption = false; - bool taunt_state = false; - bool toggle_taunt = true; int ab_arg = 1; if (!arg1.compare("on")) { - taunt_state = true; - toggle_taunt = false; + tauntState = true; + validOption = true; ++ab_arg; } else if (!arg1.compare("off")) { - toggle_taunt = false; + validOption = true; + ++ab_arg; + } + + if (!arg2.compare("pet")) { + petTaunt = true; + validOption = true; ++ab_arg; } + if (!validOption) { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "target"; + } + std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; @@ -38,25 +140,23 @@ void bot_command_taunt(Client *c, const Seperator *sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[(ab_arg + 1)] : nullptr, class_race_check ? atoi(sep->arg[(ab_arg + 1)]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - int taunting_count = 0; - for (auto bot_iter : sbl) { - if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) { - continue; - } + int botTauntingCount = 0; + int petTauntingCount = 0; - if (toggle_taunt) { - bot_iter->SetTaunting(!bot_iter->IsTaunting()); - } else { - bot_iter->SetTaunting(taunt_state); - } + if (!petTaunt) { + for (auto bot_iter : sbl) { + if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) { + continue; + } + + bot_iter->SetTaunting(tauntState); - if (sbl.size() == 1) { Bot::BotGroupSay( bot_iter, fmt::format( @@ -64,27 +164,23 @@ void bot_command_taunt(Client *c, const Seperator *sep) bot_iter->IsTaunting() ? "now" : "no longer" ).c_str() ); - } - ++taunting_count; + ++botTauntingCount; + } } - for (auto bot_iter : sbl) { - if (!bot_iter->HasPet()) { - continue; - } + if (petTaunt) { + for (auto bot_iter : sbl) { + if (!bot_iter->HasPet()) { + continue; + } - if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) { - continue; - } + if (!bot_iter->GetPet()->GetSkill(EQ::skills::SkillTaunt)) { + continue; + } - if (toggle_taunt) { - bot_iter->GetPet()->CastToNPC()->SetTaunting(!bot_iter->GetPet()->CastToNPC()->IsTaunting()); - } else { - bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state); - } + bot_iter->GetPet()->CastToNPC()->SetTaunting(tauntState); - if (sbl.size() == 1) { Bot::BotGroupSay( bot_iter, fmt::format( @@ -92,34 +188,29 @@ void bot_command_taunt(Client *c, const Seperator *sep) bot_iter->GetPet()->CastToNPC()->IsTaunting() ? "now" : "no longer" ).c_str() ); - } - ++taunting_count; + ++petTauntingCount; + } } - if (taunting_count) { - if (toggle_taunt) { - c->Message( - Chat::White, - fmt::format( - "{} of your bots and their pets {} toggled their taunting state", - taunting_count, - taunting_count != 1 ? "have" : "has" - ).c_str() - ); - } else { - c->Message( - Chat::White, - fmt::format( - "{} of your bots and their pets {} {} taunting.", - taunting_count, - taunting_count != 1 ? "have" : "has", - taunt_state ? "started" : "stopped" - ).c_str() - ); - } + if (botTauntingCount || petTauntingCount) { + c->Message( + Chat::Green, + fmt::format( + "{} of your {} are {} taunting.", + (botTauntingCount ? botTauntingCount : petTauntingCount), + (botTauntingCount ? "bots" : "bots' pets"), + tauntState ? "now" : "no longer" + ).c_str() + ); } else { - c->Message(Chat::White, "None of your bots are capable of taunting"); + c->Message( + Chat::Yellow, + fmt::format( + "None of your {} are capable of taunting.", + !petTaunt ? "bots" : "bots' pets" + ).c_str() + ); } } From e652bc02b9703dc06a6d1ba703f82b3064a964b8 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 24 Dec 2024 08:56:23 -0600 Subject: [PATCH 245/394] Allow pet types to ^cast, prevent failure spam, add cure check --- zone/bot_commands/cast.cpp | 71 ++++++++++++++++---------------------- zone/botspellsai.cpp | 2 +- 2 files changed, 30 insertions(+), 43 deletions(-) diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 9abd8eb559..054d763e41 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -350,22 +350,9 @@ void bot_command_cast(Client* c, const Seperator* sep) subTargetType = CommandedSubTypes::AETarget; ++ab_arg; } - - if ( - spellType == BotSpellTypes::PetBuffs || - spellType == BotSpellTypes::PetCompleteHeals || - spellType == BotSpellTypes::PetFastHeals || - spellType == BotSpellTypes::PetHoTHeals || - spellType == BotSpellTypes::PetRegularHeals || - spellType == BotSpellTypes::PetVeryFastHeals - ) { - c->Message(Chat::Yellow, "Pet type heals and buffs are not supported, use the regular spell type."); - return; - } } Mob* tar = c->GetTarget(); - //LogTestDebug("{}: 'Attempting {} [{}-{}] on {}'", __LINE__, c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (tar ? tar->GetCleanName() : "NOBODY")); //deleteme if (!tar) { if ((!aaType && !bySpellID) && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { @@ -375,6 +362,19 @@ void bot_command_cast(Client* c, const Seperator* sep) } if (!aaType && !bySpellID) { + if (IsPetBotSpellType(spellType) && !tar->IsPet()) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] is an invalid target. {} requires a pet to be targeted.", + tar->GetCleanName(), + tar->GetSpellTypeNameByID(spellType) + ).c_str() + ); + + return; + } + switch (spellType) { //Target Checks case BotSpellTypes::Resurrect: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { @@ -452,6 +452,21 @@ void bot_command_cast(Client* c, const Seperator* sep) } } + if ( + (spellType == BotSpellTypes::Cure || spellType == BotSpellTypes::GroupCures || spellType == BotSpellTypes::PetCures) && + !c->CastToBot()->GetNeedsCured(tar) + ) { + c->Message( + Chat::Yellow, + fmt::format( + "[{}] doesn't have anything that needs to be cured.", + tar->GetCleanName() + ).c_str() + ); + + return; + } + const int ab_mask = ActionableBots::ABM_Type1; std::string actionableArg = sep->arg[ab_arg]; @@ -494,7 +509,6 @@ void bot_command_cast(Client* c, const Seperator* sep) Mob* newTar = tar; if (!aaType && !bySpellID) { - //LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on {}'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme if (!SpellTypeRequiresTarget(spellType)) { newTar = bot_iter; } @@ -529,7 +543,6 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - LogTestDebug("{}: {} says, 'aaID is {}'", __LINE__, bot_iter->GetCleanName(), aaID); //deleteme AA::Rank* tempRank = nullptr; AA::Rank*& rank = tempRank; uint16 spell_id = bot_iter->GetSpellByAA(aaID, rank); @@ -550,14 +563,11 @@ void bot_command_cast(Client* c, const Seperator* sep) else if (bySpellID) { SPDat_Spell_Struct spell = spells[chosenSpellID]; - LogTestDebug("Starting bySpellID checks."); //deleteme if (!bot_iter->CanUseBotSpell(chosenSpellID)) { - LogTestDebug("{} does not have {} [#{}].", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme continue; } if (!tar || (spell.target_type == ST_Self && tar != bot_iter)) { - LogTestDebug("{} set my target to myself for {} [#{}] due to !tar.", bot_iter->GetCleanName(), spell.name, chosenSpellID); //deleteme tar = bot_iter; } @@ -569,23 +579,10 @@ void bot_command_cast(Client* c, const Seperator* sep) isSuccess = true; ++successCount; } - else { - c->Message( - Chat::Red, - fmt::format( - "{} says, '{} [#{}] failed to cast on [{}]. This could be due to this to any number of things: range, mana, immune, etc.'", - bot_iter->GetCleanName(), - spell.name, - chosenSpellID, - tar->GetCleanName() - ).c_str() - ); - } continue; } else { - LogTestDebug("{}: {} says, 'Attempting {} [{}-{}] on [{}]'", __LINE__, bot_iter->GetCleanName(), c->GetSpellTypeNameByID(spellType), (subType != UINT16_MAX ? c->GetSubTypeNameByID(subType) : "Standard"), (subTargetType != UINT16_MAX ? c->GetSubTypeNameByID(subTargetType) : "Standard"), (newTar ? newTar->GetCleanName() : "NOBODY")); //deleteme bot_iter->SetCommandedSpell(true); if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { @@ -596,16 +593,6 @@ void bot_command_cast(Client* c, const Seperator* sep) isSuccess = true; ++successCount; } - else { - c->Message( - Chat::Red, - fmt::format( - "{} says, 'Ability failed to cast [{}]. This could be due to this to any number of things: range, mana, immune, etc.'", - bot_iter->GetCleanName(), - tar->GetCleanName() - ).c_str() - ); - } bot_iter->SetCommandedSpell(false); @@ -631,7 +618,7 @@ void bot_command_cast(Client* c, const Seperator* sep) c->Message( Chat::Yellow, fmt::format( - "No bots are capable of casting [{}] on {}.", + "No bots are capable of casting [{}] on {}. This could be due to this to any number of things: range, mana, immune, target type, etc.", (bySpellID ? spells[chosenSpellID].name : type), tar ? tar->GetCleanName() : "your target" ).c_str() diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 054b82ac96..963d715974 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2025,7 +2025,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) } } - if (botCaster->IsCommandedSpell() || botCaster->GetNeedsCured(m)) { + if (botCaster->GetNeedsCured(m)) { if (botCaster->CastChecks(itr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { if (m->FindType(SE_PoisonCounter)) { ++countPoisoned; From 8ed6dece34952b85b4dd90e7a2db87b8e1054f01 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 28 Dec 2024 22:58:07 -0600 Subject: [PATCH 246/394] more raid optimizations, should be final. 10 clients, 710 bots, 10 raids, ~250 pets sits around 3.5% CPU idle --- zone/bot.cpp | 29 ++++++++++++++++++++--------- zone/bot.h | 3 +++ zone/bot_raid.cpp | 2 ++ zone/entity.cpp | 20 +++++++++++++++----- zone/entity.h | 2 +- zone/raids.cpp | 5 +++-- 6 files changed, 44 insertions(+), 17 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 46d762ad16..dc7fd196e9 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -111,7 +111,9 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm bot_blocked_buffs.clear(); _spellTargetList.clear(); _groupSpellTargetList.clear(); - _storedRaid = nullptr; + SetStoredRaid(nullptr); + SetVerifiedRaid(false); + p_raid_instance = nullptr; // Calculate HitPoints Last As It Uses Base Stats current_hp = GenerateBaseHitPoints(); @@ -263,7 +265,9 @@ Bot::Bot( _spellTargetList.clear(); _groupSpellTargetList.clear(); - _storedRaid = nullptr; + SetStoredRaid(nullptr); + SetVerifiedRaid(false); + p_raid_instance = nullptr; LoadAAs(); if (database.botdb.LoadBuffs(this)) { @@ -1637,10 +1641,6 @@ bool Bot::Process() entity_list.ScanCloseMobs(this); } - //if (m_mob_check_moving_timer.Check()) { //TODO bot rewrite - is this necessary - // CheckScanCloseMobsMovingTimer(); - //} - // SpellProcess(); if (tic_timer.Check()) { @@ -2064,12 +2064,16 @@ void Bot::AI_Process() return; } - auto raid = entity_list.GetRaidByBot(this); + Raid* raid = entity_list.GetRaidByBot(this); SetStoredRaid(raid); uint32 r_group = RAID_GROUPLESS; if (raid) { - //raid->VerifyRaid(); + if (!GetVerifiedRaid()) { + raid->VerifyRaid(); + SetVerifiedRaid(true); + } + r_group = raid->GetGroup(GetName()); //if (mana_timer.Check(false)) { @@ -3568,7 +3572,7 @@ bool Bot::Spawn(Client* botCharacterOwner) { ChangeBotRangedWeapons(true); } - if (auto raid = entity_list.GetRaidByBot(this)) { + if (auto raid = entity_list.GetRaidByBotName(GetName())) { // Safety Check to confirm we have a valid raid auto owner = GetBotOwner(); if (owner && !raid->IsRaidMember(owner->GetCleanName())) { @@ -3577,6 +3581,9 @@ bool Bot::Spawn(Client* botCharacterOwner) { SetRaidGrouped(true); raid->LearnMembers(); raid->VerifyRaid(); + SetStoredRaid(raid); + p_raid_instance = raid; + SetVerifiedRaid(true); } } else if (auto group = entity_list.GetGroupByMob(this)) { @@ -7051,6 +7058,10 @@ void Bot::RemoveBotFromRaid(Bot* bot) { bot_raid->DisbandRaid(); } } + + bot->SetStoredRaid(nullptr); + bot->p_raid_instance = nullptr; + bot->SetVerifiedRaid(false); } // Handles all client zone change event diff --git a/zone/bot.h b/zone/bot.h index e0177e2574..f1c8e0482f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -467,6 +467,8 @@ class Bot : public NPC { void SetGroupSpellTargetList(std::vector spellTargetList) { _groupSpellTargetList = spellTargetList; } Raid* GetStoredRaid() { return _storedRaid; } void SetStoredRaid(Raid* storedRaid) { _storedRaid = storedRaid; } + bool GetVerifiedRaid() { return _verifiedRaid; } + void SetVerifiedRaid(bool status) { _verifiedRaid = status; } uint16 GetTempSpellType() { return _tempSpellType; } void SetTempSpellType(uint16 spellType) { _tempSpellType = spellType; } void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); @@ -1107,6 +1109,7 @@ class Bot : public NPC { std::vector _spellTargetList; std::vector _groupSpellTargetList; Raid* _storedRaid; + bool _verifiedRaid; uint16 _tempSpellType; // Private "base stats" Members diff --git a/zone/bot_raid.cpp b/zone/bot_raid.cpp index 458ae03c41..4e93b5d492 100644 --- a/zone/bot_raid.cpp +++ b/zone/bot_raid.cpp @@ -313,7 +313,9 @@ void Client::SpawnRaidBotsOnConnect(Raid* raid) { if (bot) { bot->SetRaidGrouped(true); + bot->SetStoredRaid(raid); bot->p_raid_instance = raid; + bot->SetVerifiedRaid(false); } } } diff --git a/zone/entity.cpp b/zone/entity.cpp index a756c9fdc2..a7ba7f3960 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -2244,14 +2244,24 @@ Raid* EntityList::GetRaidByBotName(const char* name) return nullptr; } -Raid* EntityList::GetRaidByBot(const Bot* bot) +Raid* EntityList::GetRaidByBot(Bot* bot) { - for (const auto& r : raid_list) { - for (const auto& m : r->members) { - if (m.is_bot && m.member->CastToBot() == bot) { - return r; + if (bot->p_raid_instance) { + return bot->p_raid_instance; + } + + std::list::iterator iterator; + iterator = raid_list.begin(); + + while (iterator != raid_list.end()) { + for (const auto& member : (*iterator)->members) { + if (member.member && member.is_bot && member.member->CastToBot() == bot) { + bot->p_raid_instance = *iterator; + return *iterator; } } + + ++iterator; } return nullptr; diff --git a/zone/entity.h b/zone/entity.h index 4b5c9b6908..d1798c6bc3 100644 --- a/zone/entity.h +++ b/zone/entity.h @@ -200,7 +200,7 @@ class EntityList Raid *GetRaidByClient(Client* client); Raid *GetRaidByID(uint32 id); Raid* GetRaidByBotName(const char* name); - Raid* GetRaidByBot(const Bot* bot); + Raid* GetRaidByBot(Bot* bot); Raid* GetRaidByName(const char* name); Corpse *GetCorpseByOwner(Client* client); diff --git a/zone/raids.cpp b/zone/raids.cpp index 4dd710e3af..7bb6ab4e50 100644 --- a/zone/raids.cpp +++ b/zone/raids.cpp @@ -240,8 +240,6 @@ void Raid::AddBot(Bot* b, uint32 group, bool raid_leader, bool group_leader, boo SendRaidAddAll(b->GetName()); b->SetRaidGrouped(true); - b->p_raid_instance = this; - auto pack = new ServerPacket(ServerOP_RaidAdd, sizeof(ServerRaidGeneralAction_Struct)); auto* rga = (ServerRaidGeneralAction_Struct*) pack->pBuffer; @@ -267,6 +265,9 @@ void Raid::RemoveMember(const char *character_name) b->SetFollowID(b->GetOwner()->CastToClient()->GetID()); b->SetTarget(nullptr); b->SetRaidGrouped(false); + b->p_raid_instance = nullptr; + b->SetStoredRaid(nullptr); + b->SetVerifiedRaid(false); } disbandCheck = true; From 1c01b716d57429b8ff583db5a1a1e68dee65597d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 29 Dec 2024 20:01:01 -0600 Subject: [PATCH 247/394] Move spell range check to proper location --- zone/bot.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index dc7fd196e9..41ab32421b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9525,6 +9525,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } + if (!AECheck && !IsValidSpellRange(spell_id, tar)) { + //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + return false; + } + if (spellType == UINT16_MAX) { //AA/Forced cast checks, return here return true; } @@ -9539,11 +9544,6 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (!AECheck && !IsValidSpellRange(spell_id, tar)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme - return false; - } - if (spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id)) { //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme return false; From a0c2abfedf13e935c6438bf56a9cbd9a13590680 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 29 Dec 2024 20:01:18 -0600 Subject: [PATCH 248/394] Implement ^discipline --- .../database_update_manifest_bots.cpp | 906 ++++++++++++++++++ common/spdat.h | 8 + common/version.h | 2 +- zone/bot.cpp | 106 +- zone/bot.h | 2 +- zone/bot_command.cpp | 2 + zone/bot_command.h | 1 + zone/bot_commands/cast.cpp | 8 +- zone/bot_commands/discipline.cpp | 296 ++++++ zone/botspellsai.cpp | 26 +- 10 files changed, 1309 insertions(+), 48 deletions(-) create mode 100644 zone/bot_commands/discipline.cpp diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 999f504783..8da924e897 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -271,6 +271,7 @@ UPDATE `bot_command_settings` SET `aliases`= 'minmana' WHERE `bot_command`='spel UPDATE `bot_command_settings` SET `aliases`= 'minthresholds' WHERE `bot_command`='spellminthresholds'; UPDATE `bot_command_settings` SET `aliases`= 'pursuepriority' WHERE `bot_command`='spellpursuepriority'; UPDATE `bot_command_settings` SET `aliases`= 'targetcount' WHERE `bot_command`='spelltargetcount'; +UPDATE `bot_command_settings` SET `aliases`= 'disc' WHERE `bot_command`='discipline'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|vc') ELSE 'vc' END WHERE `bot_command`='viewcombos' AND `aliases` NOT LIKE '%vc%'; )" }, @@ -1198,6 +1199,911 @@ CREATE TABLE `bot_blocked_buffs` ( COLLATE='latin1_swedish_ci' ENGINE=InnoDB ; +)" + }, + ManifestEntry{ + .version = 9053, + .description = "2024_12_26_discipline_inserts.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 200", + .condition = "empty", + .match = "", + .sql = R"( +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`) +VALUES +(3001, 5225, 200, 1, 254), +(3001, 25060, 200, 5, 254), +(3001, 4721, 200, 10, 62), +(3001, 4608, 200, 20, 51), +(3001, 4585, 200, 30, 254), +(3001, 4587, 200, 40, 254), +(3001, 4681, 200, 52, 55), +(3001, 4503, 200, 52, 254), +(3001, 4672, 200, 53, 254), +(3001, 4514, 200, 54, 254), +(3001, 4499, 200, 55, 64), +(3001, 8921, 200, 55, 69), +(3001, 4682, 200, 56, 62), +(3001, 4674, 200, 56, 254), +(3001, 4501, 200, 57, 254), +(3001, 4675, 200, 58, 59), +(3001, 4670, 200, 59, 254), +(3001, 4498, 200, 60, 254), +(3001, 6750, 200, 61, 68), +(3001, 4689, 200, 61, 254), +(3001, 4697, 200, 63, 64), +(3001, 4687, 200, 63, 254), +(3001, 4688, 200, 65, 71), +(3001, 5015, 200, 65, 254), +(3001, 5016, 200, 65, 254), +(3001, 6191, 200, 66, 70), +(3001, 6192, 200, 68, 73), +(3001, 8000, 200, 68, 254), +(3001, 6725, 200, 69, 73), +(3001, 6173, 200, 69, 80), +(3001, 8467, 200, 70, 74), +(3001, 8468, 200, 70, 254), +(3001, 6190, 200, 70, 254), +(3001, 10959, 200, 71, 75), +(3001, 10960, 200, 71, 75), +(3001, 10961, 200, 71, 75), +(3001, 11913, 200, 72, 76), +(3001, 11914, 200, 72, 76), +(3001, 11915, 200, 72, 76), +(3001, 10965, 200, 72, 254), +(3001, 10966, 200, 72, 254), +(3001, 10967, 200, 72, 254), +(3001, 10970, 200, 73, 254), +(3001, 10968, 200, 73, 254), +(3001, 10969, 200, 73, 254), +(3001, 10971, 200, 74, 78), +(3001, 10972, 200, 74, 78), +(3001, 10973, 200, 74, 78), +(3001, 11917, 200, 74, 254), +(3001, 11918, 200, 74, 254), +(3001, 11916, 200, 74, 254), +(3001, 10974, 200, 75, 79), +(3001, 10975, 200, 75, 79), +(3001, 10976, 200, 75, 79), +(3001, 15345, 200, 76, 80), +(3001, 15346, 200, 76, 80), +(3001, 15347, 200, 76, 80), +(3001, 14192, 200, 77, 81), +(3001, 14193, 200, 77, 81), +(3001, 14194, 200, 77, 81), +(3001, 15369, 200, 77, 254), +(3001, 15370, 200, 77, 254), +(3001, 15371, 200, 77, 254), +(3001, 15375, 200, 78, 82), +(3001, 15376, 200, 78, 82), +(3001, 15377, 200, 78, 82), +(3001, 15359, 200, 79, 83), +(3001, 15357, 200, 79, 83), +(3001, 15358, 200, 79, 83), +(3001, 15379, 200, 80, 84), +(3001, 15360, 200, 80, 84), +(3001, 15380, 200, 80, 84), +(3001, 15361, 200, 80, 84), +(3001, 15362, 200, 80, 84), +(3001, 15378, 200, 80, 84), +(3001, 19537, 200, 81, 254), +(3001, 19538, 200, 81, 254), +(3001, 19516, 200, 81, 254), +(3001, 19539, 200, 81, 254), +(3001, 19517, 200, 81, 254), +(3001, 19518, 200, 81, 254), +(3001, 18213, 200, 82, 254), +(3001, 18214, 200, 82, 254), +(3001, 18215, 200, 82, 254), +(3001, 19555, 200, 83, 254), +(3001, 19556, 200, 83, 254), +(3001, 19557, 200, 83, 254), +(3001, 19914, 200, 83, 254), +(3001, 19915, 200, 83, 254), +(3001, 19916, 200, 83, 254), +(3001, 19553, 200, 84, 254), +(3001, 19554, 200, 84, 254), +(3001, 19528, 200, 84, 254), +(3001, 19529, 200, 84, 254), +(3001, 19552, 200, 84, 254), +(3001, 19530, 200, 84, 254), +(3001, 22556, 200, 85, 254), +(3001, 19531, 200, 85, 254), +(3001, 22557, 200, 85, 254), +(3001, 19532, 200, 85, 254), +(3001, 22558, 200, 85, 254), +(3001, 19533, 200, 85, 254), +(3001, 19549, 200, 85, 254), +(3001, 19550, 200, 85, 254), +(3001, 19917, 200, 85, 254), +(3001, 19551, 200, 85, 254), +(3001, 19918, 200, 85, 254), +(3001, 19919, 200, 85, 254), +(3002, 33000, 200, 1, 254), +(3003, 4585, 200, 51, 254), +(3003, 4587, 200, 54, 254), +(3003, 4500, 200, 55, 254), +(3003, 7004, 200, 56, 60), +(3003, 4590, 200, 59, 254), +(3003, 4518, 200, 60, 254), +(3003, 6731, 200, 61, 68), +(3003, 6663, 200, 69, 72), +(3003, 11854, 200, 73, 77), +(3003, 11855, 200, 73, 77), +(3003, 11856, 200, 73, 77), +(3003, 14987, 200, 78, 82), +(3003, 14988, 200, 78, 82), +(3003, 14989, 200, 78, 82), +(3003, 19103, 200, 83, 254), +(3003, 19131, 200, 83, 254), +(3003, 19132, 200, 83, 254), +(3003, 19133, 200, 83, 254), +(3003, 22665, 200, 83, 254), +(3003, 22666, 200, 83, 254), +(3003, 22667, 200, 83, 254), +(3003, 19101, 200, 83, 254), +(3003, 19102, 200, 83, 254), +(3004, 33000, 200, 1, 254), +(3004, 4585, 200, 51, 254), +(3004, 4587, 200, 54, 254), +(3004, 4506, 200, 55, 79), +(3004, 4519, 200, 60, 254), +(3004, 8019, 200, 69, 254), +(3004, 10086, 200, 72, 76), +(3004, 10087, 200, 72, 76), +(3004, 10088, 200, 72, 76), +(3004, 15020, 200, 77, 81), +(3004, 15021, 200, 77, 81), +(3004, 15022, 200, 77, 81), +(3004, 15091, 200, 80, 84), +(3004, 15092, 200, 80, 84), +(3004, 15093, 200, 80, 84), +(3004, 19153, 200, 82, 254), +(3004, 19154, 200, 82, 254), +(3004, 19152, 200, 82, 254), +(3004, 19223, 200, 85, 254), +(3004, 19224, 200, 85, 254), +(3004, 19225, 200, 85, 254), +(3005, 4585, 200, 51, 254), +(3005, 4587, 200, 54, 254), +(3005, 4520, 200, 55, 254), +(3005, 7005, 200, 56, 60), +(3005, 4590, 200, 59, 254), +(3005, 4504, 200, 60, 254), +(3005, 6741, 200, 61, 68), +(3005, 6673, 200, 69, 72), +(3005, 11866, 200, 73, 77), +(3005, 11867, 200, 73, 77), +(3005, 11868, 200, 73, 77), +(3005, 10306, 200, 75, 77), +(3005, 10307, 200, 75, 77), +(3005, 10308, 200, 75, 77), +(3005, 15223, 200, 78, 79), +(3005, 15224, 200, 78, 79), +(3005, 15225, 200, 78, 79), +(3005, 15211, 200, 78, 82), +(3005, 15212, 200, 78, 82), +(3005, 15213, 200, 78, 82), +(3005, 15191, 200, 80, 84), +(3005, 15192, 200, 80, 84), +(3005, 15190, 200, 80, 84), +(3005, 19364, 200, 83, 254), +(3005, 19365, 200, 83, 254), +(3005, 19366, 200, 83, 254), +(3005, 22662, 200, 83, 254), +(3005, 22663, 200, 83, 254), +(3005, 19131, 200, 83, 254), +(3005, 22664, 200, 83, 254), +(3005, 19132, 200, 83, 254), +(3005, 19133, 200, 83, 254), +(3005, 19343, 200, 85, 254), +(3005, 19344, 200, 85, 254), +(3005, 19345, 200, 85, 254), +(3007, 5225, 200, 1, 254), +(3007, 25060, 200, 5, 60), +(3007, 4721, 200, 10, 62), +(3007, 4585, 200, 30, 254), +(3007, 4614, 200, 35, 49), +(3007, 4587, 200, 40, 254), +(3007, 4683, 200, 50, 56), +(3007, 4510, 200, 51, 64), +(3007, 4511, 200, 52, 59), +(3007, 4509, 200, 53, 254), +(3007, 4502, 200, 54, 254), +(3007, 8923, 200, 55, 69), +(3007, 4512, 200, 56, 78), +(3007, 4684, 200, 57, 63), +(3007, 4513, 200, 57, 254), +(3007, 4507, 200, 59, 254), +(3007, 4508, 200, 60, 73), +(3007, 4692, 200, 61, 65), +(3007, 6752, 200, 61, 68), +(3007, 4687, 200, 63, 254), +(3007, 4691, 200, 63, 254), +(3007, 4698, 200, 64, 64), +(3007, 4690, 200, 65, 71), +(3007, 5019, 200, 65, 254), +(3007, 5020, 200, 65, 254), +(3007, 6193, 200, 66, 254), +(3007, 6195, 200, 68, 254), +(3007, 8002, 200, 68, 254), +(3007, 6175, 200, 69, 70), +(3007, 6727, 200, 69, 254), +(3007, 6194, 200, 70, 254), +(3007, 8473, 200, 70, 254), +(3007, 8474, 200, 70, 254), +(3007, 10947, 200, 71, 75), +(3007, 10948, 200, 71, 75), +(3007, 10949, 200, 71, 75), +(3007, 11913, 200, 72, 76), +(3007, 11914, 200, 72, 76), +(3007, 11915, 200, 72, 76), +(3007, 10938, 200, 72, 254), +(3007, 10939, 200, 72, 254), +(3007, 10940, 200, 72, 254), +(3007, 10933, 200, 73, 77), +(3007, 10934, 200, 73, 77), +(3007, 10932, 200, 73, 77), +(3007, 10944, 200, 74, 78), +(3007, 10945, 200, 74, 78), +(3007, 10946, 200, 74, 78), +(3007, 11922, 200, 74, 254), +(3007, 11923, 200, 74, 254), +(3007, 11924, 200, 74, 254), +(3007, 10950, 200, 75, 79), +(3007, 10951, 200, 75, 79), +(3007, 10952, 200, 75, 79), +(3007, 14799, 200, 76, 80), +(3007, 14800, 200, 76, 80), +(3007, 14801, 200, 76, 80), +(3007, 14194, 200, 77, 81), +(3007, 14811, 200, 77, 81), +(3007, 14812, 200, 77, 81), +(3007, 14813, 200, 77, 81), +(3007, 14192, 200, 77, 81), +(3007, 14193, 200, 77, 81), +(3007, 14784, 200, 78, 82), +(3007, 14785, 200, 78, 82), +(3007, 14786, 200, 78, 82), +(3007, 14796, 200, 79, 80), +(3007, 14797, 200, 79, 80), +(3007, 14798, 200, 79, 80), +(3007, 14822, 200, 79, 254), +(3007, 14820, 200, 79, 254), +(3007, 14821, 200, 79, 254), +(3007, 14814, 200, 80, 84), +(3007, 14815, 200, 80, 84), +(3007, 14816, 200, 80, 84), +(3007, 14802, 200, 80, 254), +(3007, 14803, 200, 80, 254), +(3007, 14804, 200, 80, 254), +(3007, 18895, 200, 81, 83), +(3007, 18896, 200, 81, 83), +(3007, 18897, 200, 81, 83), +(3007, 18904, 200, 81, 254), +(3007, 18905, 200, 81, 254), +(3007, 18906, 200, 81, 254), +(3007, 18918, 200, 82, 254), +(3007, 18213, 200, 82, 254), +(3007, 18214, 200, 82, 254), +(3007, 18215, 200, 82, 254), +(3007, 18916, 200, 82, 254), +(3007, 18917, 200, 82, 254), +(3007, 18889, 200, 83, 254), +(3007, 18890, 200, 83, 254), +(3007, 18891, 200, 83, 254), +(3007, 19826, 200, 83, 254), +(3007, 19827, 200, 83, 254), +(3007, 19828, 200, 83, 254), +(3007, 18902, 200, 84, 254), +(3007, 18903, 200, 84, 254), +(3007, 19552, 200, 84, 254), +(3007, 19553, 200, 84, 254), +(3007, 19554, 200, 84, 254), +(3007, 18901, 200, 84, 254), +(3007, 19830, 200, 85, 254), +(3007, 18919, 200, 85, 254), +(3007, 19831, 200, 85, 254), +(3007, 18920, 200, 85, 254), +(3007, 22525, 200, 85, 254), +(3007, 18921, 200, 85, 254), +(3007, 22526, 200, 85, 254), +(3007, 22527, 200, 85, 254), +(3007, 18907, 200, 85, 254), +(3007, 18908, 200, 85, 254), +(3007, 18909, 200, 85, 254), +(3007, 19829, 200, 85, 254), +(3008, 4585, 200, 51, 254), +(3008, 4587, 200, 54, 254), +(3008, 4516, 200, 55, 254), +(3008, 4586, 200, 60, 254), +(3008, 8030, 200, 69, 254), +(3009, 5225, 200, 1, 254), +(3009, 25060, 200, 5, 82), +(3009, 4721, 200, 10, 62), +(3009, 4659, 200, 20, 51), +(3009, 4585, 200, 30, 254), +(3009, 4587, 200, 40, 254), +(3009, 4685, 200, 52, 62), +(3009, 4673, 200, 53, 254), +(3009, 4505, 200, 54, 62), +(3009, 8922, 200, 55, 69), +(3009, 4515, 200, 55, 254), +(3009, 4517, 200, 57, 254), +(3009, 4677, 200, 58, 254), +(3009, 4676, 200, 59, 74), +(3009, 6751, 200, 61, 68), +(3009, 4696, 200, 61, 254), +(3009, 4686, 200, 63, 64), +(3009, 4687, 200, 63, 254), +(3009, 4694, 200, 63, 254), +(3009, 5017, 200, 65, 68), +(3009, 5018, 200, 65, 68), +(3009, 4695, 200, 65, 254), +(3009, 6198, 200, 66, 254), +(3009, 8001, 200, 68, 254), +(3009, 6196, 200, 68, 254), +(3009, 6174, 200, 69, 69), +(3009, 6726, 200, 69, 70), +(3009, 8470, 200, 70, 79), +(3009, 6197, 200, 70, 254), +(3009, 8471, 200, 70, 254), +(3009, 10883, 200, 71, 75), +(3009, 10884, 200, 71, 75), +(3009, 10885, 200, 71, 75), +(3009, 11913, 200, 72, 76), +(3009, 11914, 200, 72, 76), +(3009, 11915, 200, 72, 76), +(3009, 10889, 200, 72, 254), +(3009, 10890, 200, 72, 254), +(3009, 10891, 200, 72, 254), +(3009, 10892, 200, 73, 77), +(3009, 10893, 200, 73, 77), +(3009, 10894, 200, 73, 77), +(3009, 11925, 200, 74, 78), +(3009, 11926, 200, 74, 78), +(3009, 11927, 200, 74, 78), +(3009, 10895, 200, 74, 254), +(3009, 10896, 200, 74, 254), +(3009, 10897, 200, 74, 254), +(3009, 10899, 200, 75, 254), +(3009, 10900, 200, 75, 254), +(3009, 10898, 200, 75, 254), +(3009, 15097, 200, 76, 80), +(3009, 15120, 200, 76, 80), +(3009, 15098, 200, 76, 80), +(3009, 15099, 200, 76, 80), +(3009, 15118, 200, 76, 80), +(3009, 15119, 200, 76, 80), +(3009, 15121, 200, 77, 81), +(3009, 15122, 200, 77, 81), +(3009, 15123, 200, 77, 81), +(3009, 14192, 200, 77, 81), +(3009, 14193, 200, 77, 81), +(3009, 14194, 200, 77, 81), +(3009, 15103, 200, 78, 82), +(3009, 15104, 200, 78, 82), +(3009, 15105, 200, 78, 82), +(3009, 15115, 200, 79, 83), +(3009, 15116, 200, 79, 83), +(3009, 15117, 200, 79, 83), +(3009, 15127, 200, 80, 84), +(3009, 15128, 200, 80, 84), +(3009, 15129, 200, 80, 84), +(3009, 15133, 200, 80, 84), +(3009, 15134, 200, 80, 84), +(3009, 15135, 200, 80, 84), +(3009, 19244, 200, 81, 254), +(3009, 19245, 200, 81, 254), +(3009, 19265, 200, 81, 254), +(3009, 19246, 200, 81, 254), +(3009, 19266, 200, 81, 254), +(3009, 19267, 200, 81, 254), +(3009, 18213, 200, 82, 254), +(3009, 18214, 200, 82, 254), +(3009, 18215, 200, 82, 254), +(3009, 19268, 200, 82, 254), +(3009, 19269, 200, 82, 254), +(3009, 19270, 200, 82, 254), +(3009, 19873, 200, 83, 84), +(3009, 19871, 200, 83, 84), +(3009, 19872, 200, 83, 84), +(3009, 19252, 200, 83, 254), +(3009, 19247, 200, 83, 254), +(3009, 19248, 200, 83, 254), +(3009, 19249, 200, 83, 254), +(3009, 19250, 200, 83, 254), +(3009, 19251, 200, 83, 254), +(3009, 19262, 200, 84, 254), +(3009, 19263, 200, 84, 254), +(3009, 19264, 200, 84, 254), +(3009, 19283, 200, 84, 254), +(3009, 19284, 200, 84, 254), +(3009, 19285, 200, 84, 254), +(3009, 19275, 200, 85, 254), +(3009, 19874, 200, 85, 254), +(3009, 19276, 200, 85, 254), +(3009, 19875, 200, 85, 254), +(3009, 19280, 200, 85, 254), +(3009, 19876, 200, 85, 254), +(3009, 19281, 200, 85, 254), +(3009, 22540, 200, 85, 254), +(3009, 19282, 200, 85, 254), +(3009, 22541, 200, 85, 254), +(3009, 22542, 200, 85, 254), +(3009, 19274, 200, 85, 254), +(3015, 4585, 200, 51, 254), +(3015, 4587, 200, 54, 254), +(3015, 4671, 200, 55, 254), +(3015, 4678, 200, 60, 68), +(3015, 8233, 200, 69, 254), +(3015, 8782, 200, 70, 79), +(3015, 14158, 200, 80, 84), +(3015, 14159, 200, 80, 84), +(3015, 14160, 200, 80, 84), +(3015, 18170, 200, 85, 254), +(3015, 18171, 200, 85, 254), +(3015, 18172, 200, 85, 254), +(3016, 5225, 200, 1, 254), +(3016, 4937, 200, 1, 4), +(3016, 4938, 200, 5, 9), +(3016, 25060, 200, 5, 254), +(3016, 4928, 200, 8, 31), +(3016, 4939, 200, 10, 14), +(3016, 4721, 200, 10, 62), +(3016, 4940, 200, 15, 19), +(3016, 4931, 200, 16, 39), +(3016, 4941, 200, 20, 24), +(3016, 4934, 200, 24, 47), +(3016, 4942, 200, 25, 29), +(3016, 4943, 200, 30, 34), +(3016, 5027, 200, 30, 49), +(3016, 4585, 200, 30, 254), +(3016, 4929, 200, 32, 53), +(3016, 4944, 200, 35, 39), +(3016, 4945, 200, 40, 44), +(3016, 4932, 200, 40, 59), +(3016, 4587, 200, 40, 254), +(3016, 4946, 200, 45, 49), +(3016, 4935, 200, 48, 64), +(3016, 4947, 200, 50, 54), +(3016, 5028, 200, 50, 56), +(3016, 5039, 200, 53, 254), +(3016, 4930, 200, 54, 66), +(3016, 5037, 200, 54, 254), +(3016, 4948, 200, 55, 59), +(3016, 8924, 200, 55, 69), +(3016, 5040, 200, 56, 254), +(3016, 5029, 200, 57, 63), +(3016, 5035, 200, 57, 254), +(3016, 5041, 200, 58, 59), +(3016, 5038, 200, 59, 254), +(3016, 4949, 200, 60, 64), +(3016, 4933, 200, 60, 67), +(3016, 5034, 200, 60, 74), +(3016, 6754, 200, 61, 68), +(3016, 5044, 200, 61, 254), +(3016, 4687, 200, 63, 254), +(3016, 5042, 200, 63, 254), +(3016, 5030, 200, 64, 64), +(3016, 4950, 200, 65, 65), +(3016, 5107, 200, 65, 65), +(3016, 4936, 200, 65, 68), +(3016, 5043, 200, 65, 254), +(3016, 5031, 200, 65, 254), +(3016, 5032, 200, 65, 254), +(3016, 6172, 200, 66, 70), +(3016, 6200, 200, 66, 254), +(3016, 6169, 200, 67, 70), +(3016, 6170, 200, 68, 72), +(3016, 6201, 200, 68, 254), +(3016, 8003, 200, 68, 254), +(3016, 6171, 200, 69, 73), +(3016, 6729, 200, 69, 73), +(3016, 8476, 200, 70, 254), +(3016, 8477, 200, 70, 254), +(3016, 6199, 200, 70, 254), +(3016, 10907, 200, 71, 75), +(3016, 10908, 200, 71, 75), +(3016, 10909, 200, 71, 75), +(3016, 10910, 200, 71, 75), +(3016, 11915, 200, 72, 76), +(3016, 10914, 200, 72, 76), +(3016, 10915, 200, 72, 76), +(3016, 11913, 200, 72, 76), +(3016, 10916, 200, 72, 76), +(3016, 11914, 200, 72, 76), +(3016, 10918, 200, 73, 77), +(3016, 10919, 200, 73, 77), +(3016, 10917, 200, 73, 77), +(3016, 11928, 200, 74, 78), +(3016, 11929, 200, 74, 78), +(3016, 10920, 200, 74, 78), +(3016, 11930, 200, 74, 78), +(3016, 10921, 200, 74, 78), +(3016, 10922, 200, 74, 78), +(3016, 10923, 200, 75, 254), +(3016, 10924, 200, 75, 254), +(3016, 10925, 200, 75, 254), +(3016, 14176, 200, 76, 80), +(3016, 14177, 200, 76, 80), +(3016, 14178, 200, 76, 80), +(3016, 14179, 200, 76, 80), +(3016, 14192, 200, 77, 81), +(3016, 14193, 200, 77, 81), +(3016, 14180, 200, 77, 81), +(3016, 14194, 200, 77, 81), +(3016, 14181, 200, 77, 81), +(3016, 14182, 200, 77, 81), +(3016, 14183, 200, 78, 82), +(3016, 14184, 200, 78, 82), +(3016, 14185, 200, 78, 82), +(3016, 14197, 200, 79, 83), +(3016, 14186, 200, 79, 83), +(3016, 14187, 200, 79, 83), +(3016, 14188, 200, 79, 83), +(3016, 14195, 200, 79, 83), +(3016, 14196, 200, 79, 83), +(3016, 14198, 200, 80, 84), +(3016, 14199, 200, 80, 84), +(3016, 14200, 200, 80, 84), +(3016, 18200, 200, 81, 254), +(3016, 16918, 200, 81, 254), +(3016, 16919, 200, 81, 254), +(3016, 16920, 200, 81, 254), +(3016, 18197, 200, 81, 254), +(3016, 18198, 200, 81, 254), +(3016, 18199, 200, 81, 254), +(3016, 18201, 200, 82, 254), +(3016, 18202, 200, 82, 254), +(3016, 18213, 200, 82, 254), +(3016, 18203, 200, 82, 254), +(3016, 18214, 200, 82, 254), +(3016, 18215, 200, 82, 254), +(3016, 18211, 200, 83, 254), +(3016, 18212, 200, 83, 254), +(3016, 19753, 200, 83, 254), +(3016, 18204, 200, 83, 254), +(3016, 19754, 200, 83, 254), +(3016, 18205, 200, 83, 254), +(3016, 19755, 200, 83, 254), +(3016, 18206, 200, 83, 254), +(3016, 18210, 200, 83, 254), +(3016, 18216, 200, 84, 254), +(3016, 18217, 200, 84, 254), +(3016, 18207, 200, 84, 254), +(3016, 18218, 200, 84, 254), +(3016, 18208, 200, 84, 254), +(3016, 18209, 200, 84, 254), +(3016, 19741, 200, 85, 254), +(3016, 19742, 200, 85, 254), +(3016, 19743, 200, 85, 254), +(3016, 22506, 200, 85, 254), +(3016, 22507, 200, 85, 254), +(3016, 18219, 200, 85, 254), +(3016, 22508, 200, 85, 254), +(3016, 18220, 200, 85, 254), +(3016, 18221, 200, 85, 254); + +DELETE +FROM bot_spells_entries +WHERE NOT EXISTS +(SELECT * +FROM spells_new +WHERE bot_spells_entries.spell_id = spells_new.id); +)" + }, + ManifestEntry{ + .version = 9054, + .description = "2024_12_29_discipline__subtypes_inserts.sql", + .check = "SELECT * FROM `bot_spells_entries` where `type` = 201", + .condition = "empty", + .match = "", + .sql = R"( +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`, `priority`) +VALUES +(3001, 4672, 201, 53, 254, 3), +(3001, 4514, 201, 54, 254, 3), +(3001, 4501, 201, 57, 254, 3), +(3001, 4675, 201, 58, 59, 3), +(3001, 4498, 201, 60, 254, 3), +(3001, 6750, 201, 61, 68, 3), +(3001, 6192, 201, 68, 73, 3), +(3001, 6725, 201, 69, 73, 3), +(3001, 10970, 201, 73, 254, 1), +(3001, 10968, 201, 73, 254, 3), +(3001, 10969, 201, 73, 254, 2), +(3001, 10971, 201, 74, 78, 3), +(3001, 10972, 201, 74, 78, 2), +(3001, 10973, 201, 74, 78, 1), +(3001, 11917, 201, 74, 254, 2), +(3001, 11918, 201, 74, 254, 1), +(3001, 11916, 201, 74, 254, 3), +(3001, 15369, 201, 77, 254, 3), +(3001, 15370, 201, 77, 254, 2), +(3001, 15371, 201, 77, 254, 1), +(3001, 15375, 201, 78, 82, 3), +(3001, 15376, 201, 78, 82, 2), +(3001, 15377, 201, 78, 82, 1), +(3001, 15359, 201, 79, 83, 1), +(3001, 15357, 201, 79, 83, 3), +(3001, 15358, 201, 79, 83, 2), +(3001, 15379, 201, 80, 84, 2), +(3001, 15380, 201, 80, 84, 1), +(3001, 15378, 201, 80, 84, 3), +(3001, 19555, 201, 83, 254, 3), +(3001, 19556, 201, 83, 254, 2), +(3001, 19557, 201, 83, 254, 1), +(3001, 19553, 201, 84, 254, 2), +(3001, 19554, 201, 84, 254, 1), +(3001, 19528, 201, 84, 254, 3), +(3001, 19529, 201, 84, 254, 2), +(3001, 19552, 201, 84, 254, 3), +(3001, 19530, 201, 84, 254, 1), +(3001, 19549, 201, 85, 254, 3), +(3001, 19550, 201, 85, 254, 2), +(3001, 19551, 201, 85, 254, 1), +(3003, 4500, 201, 55, 254, 3), +(3003, 4590, 201, 59, 254, 3), +(3004, 4506, 201, 55, 79, 3), +(3004, 8019, 201, 69, 254, 3), +(3004, 10086, 201, 72, 76, 3), +(3004, 10087, 201, 72, 76, 2), +(3004, 10088, 201, 72, 76, 1), +(3004, 15020, 201, 77, 81, 3), +(3004, 15021, 201, 77, 81, 2), +(3004, 15022, 201, 77, 81, 1), +(3004, 15091, 201, 80, 84, 3), +(3004, 15092, 201, 80, 84, 2), +(3004, 15093, 201, 80, 84, 1), +(3004, 19153, 201, 82, 254, 2), +(3004, 19154, 201, 82, 254, 1), +(3004, 19152, 201, 82, 254, 3), +(3004, 19223, 201, 85, 254, 3), +(3004, 19224, 201, 85, 254, 2), +(3004, 19225, 201, 85, 254, 1), +(3005, 4520, 201, 55, 254, 3), +(3005, 4590, 201, 59, 254, 3), +(3007, 4511, 201, 52, 59, 3), +(3007, 4512, 201, 56, 78, 3), +(3007, 4513, 201, 57, 254, 3), +(3007, 4507, 201, 59, 254, 3), +(3007, 4508, 201, 60, 73, 3), +(3007, 4691, 201, 63, 254, 3), +(3007, 6194, 201, 70, 254, 3), +(3007, 8473, 201, 70, 254, 3), +(3007, 10944, 201, 74, 78, 3), +(3007, 10945, 201, 74, 78, 2), +(3007, 10946, 201, 74, 78, 1), +(3007, 11922, 201, 74, 254, 3), +(3007, 11923, 201, 74, 254, 2), +(3007, 11924, 201, 74, 254, 1), +(3007, 14796, 201, 79, 80, 3), +(3007, 14797, 201, 79, 80, 2), +(3007, 14798, 201, 79, 80, 1), +(3007, 14822, 201, 79, 254, 1), +(3007, 14820, 201, 79, 254, 3), +(3007, 14821, 201, 79, 254, 2), +(3007, 14814, 201, 80, 84, 3), +(3007, 14815, 201, 80, 84, 2), +(3007, 14816, 201, 80, 84, 1), +(3007, 18895, 201, 81, 83, 3), +(3007, 18896, 201, 81, 83, 2), +(3007, 18897, 201, 81, 83, 1), +(3007, 18902, 201, 84, 254, 2), +(3007, 18903, 201, 84, 254, 1), +(3007, 19552, 201, 84, 254, 3), +(3007, 19553, 201, 84, 254, 2), +(3007, 19554, 201, 84, 254, 1), +(3007, 18901, 201, 84, 254, 3), +(3007, 18919, 201, 85, 254, 3), +(3007, 18920, 201, 85, 254, 2), +(3007, 22525, 201, 85, 254, 3), +(3007, 18921, 201, 85, 254, 1), +(3007, 22526, 201, 85, 254, 2), +(3007, 22527, 201, 85, 254, 1), +(3008, 4586, 201, 60, 254, 3), +(3008, 8030, 201, 69, 254, 3), +(3009, 4659, 201, 20, 51, 3), +(3009, 4685, 201, 52, 62, 3), +(3009, 4505, 201, 54, 62, 3), +(3009, 4517, 201, 57, 254, 3), +(3009, 4677, 201, 58, 254, 3), +(3009, 4676, 201, 59, 74, 3), +(3009, 4696, 201, 61, 254, 3), +(3009, 4686, 201, 63, 64, 3), +(3009, 4694, 201, 63, 254, 3), +(3009, 5017, 201, 65, 68, 3), +(3009, 5018, 201, 65, 68, 3), +(3009, 4695, 201, 65, 254, 3), +(3009, 8001, 201, 68, 254, 3), +(3009, 6196, 201, 68, 254, 3), +(3009, 6174, 201, 69, 69, 3), +(3009, 8470, 201, 70, 79, 3), +(3009, 6197, 201, 70, 254, 3), +(3009, 10892, 201, 73, 77, 3), +(3009, 10893, 201, 73, 77, 2), +(3009, 10894, 201, 73, 77, 1), +(3009, 11925, 201, 74, 78, 3), +(3009, 11926, 201, 74, 78, 2), +(3009, 11927, 201, 74, 78, 1), +(3009, 10899, 201, 75, 254, 2), +(3009, 10900, 201, 75, 254, 1), +(3009, 10898, 201, 75, 254, 3), +(3009, 15103, 201, 78, 82, 3), +(3009, 15104, 201, 78, 82, 2), +(3009, 15105, 201, 78, 82, 1), +(3009, 15115, 201, 79, 83, 3), +(3009, 15116, 201, 79, 83, 2), +(3009, 15117, 201, 79, 83, 1), +(3009, 15133, 201, 80, 84, 3), +(3009, 15134, 201, 80, 84, 2), +(3009, 15135, 201, 80, 84, 1), +(3009, 19252, 201, 83, 254, 1), +(3009, 19250, 201, 83, 254, 3), +(3009, 19251, 201, 83, 254, 2), +(3009, 19262, 201, 84, 254, 3), +(3009, 19263, 201, 84, 254, 2), +(3009, 19264, 201, 84, 254, 1), +(3009, 19280, 201, 85, 254, 3), +(3009, 19281, 201, 85, 254, 2), +(3009, 19282, 201, 85, 254, 1), +(3015, 4678, 201, 60, 68, 3), +(3015, 8233, 201, 69, 254, 3), +(3015, 8782, 201, 70, 79, 3), +(3015, 14158, 201, 80, 84, 3), +(3015, 14159, 201, 80, 84, 2), +(3015, 14160, 201, 80, 84, 1), +(3015, 18170, 201, 85, 254, 3), +(3015, 18171, 201, 85, 254, 2), +(3015, 18172, 201, 85, 254, 1), +(3016, 5027, 201, 30, 49, 3), +(3016, 5028, 201, 50, 56, 3), +(3016, 5039, 201, 53, 254, 3), +(3016, 5037, 201, 54, 254, 3), +(3016, 5029, 201, 57, 63, 3), +(3016, 5035, 201, 57, 254, 3), +(3016, 5041, 201, 58, 59, 3), +(3016, 5034, 201, 60, 74, 3), +(3016, 6754, 201, 61, 68, 3), +(3016, 5030, 201, 64, 64, 3), +(3016, 5043, 201, 65, 254, 3), +(3016, 5031, 201, 65, 254, 3), +(3016, 5032, 201, 65, 254, 3), +(3016, 8003, 201, 68, 254, 3), +(3016, 6729, 201, 69, 73, 3), +(3016, 6199, 201, 70, 254, 3), +(3016, 10914, 201, 72, 76, 3), +(3016, 10915, 201, 72, 76, 2), +(3016, 10916, 201, 72, 76, 1), +(3016, 11928, 201, 74, 78, 3), +(3016, 11929, 201, 74, 78, 2), +(3016, 11930, 201, 74, 78, 1), +(3016, 10923, 201, 75, 254, 3), +(3016, 10924, 201, 75, 254, 2), +(3016, 10925, 201, 75, 254, 1), +(3016, 14180, 201, 77, 81, 3), +(3016, 14181, 201, 77, 81, 2), +(3016, 14182, 201, 77, 81, 1), +(3016, 14197, 201, 79, 83, 1), +(3016, 14195, 201, 79, 83, 3), +(3016, 14196, 201, 79, 83, 2), +(3016, 14198, 201, 80, 84, 3), +(3016, 14199, 201, 80, 84, 2), +(3016, 14200, 201, 80, 84, 1), +(3016, 16918, 201, 81, 254, 3), +(3016, 16919, 201, 81, 254, 2), +(3016, 16920, 201, 81, 254, 1), +(3016, 18201, 201, 82, 254, 3), +(3016, 18202, 201, 82, 254, 2), +(3016, 18203, 201, 82, 254, 1), +(3016, 18216, 201, 84, 254, 3), +(3016, 18217, 201, 84, 254, 2), +(3016, 18218, 201, 84, 254, 1), +(3016, 19741, 201, 85, 254, 3), +(3016, 19742, 201, 85, 254, 2), +(3016, 19743, 201, 85, 254, 1), +(3016, 22506, 201, 85, 254, 3), +(3016, 22507, 201, 85, 254, 2), +(3016, 18219, 201, 85, 254, 3), +(3016, 22508, 201, 85, 254, 1), +(3016, 18220, 201, 85, 254, 2), +(3016, 18221, 201, 85, 254, 1); + +INSERT INTO `bot_spells_entries` (`npc_spells_id`, `spell_id`, `type`, `minlevel`, `maxlevel`, `priority`) +VALUES +(3001, 4587, 202, 40, 254, 3), +(3001, 4503, 202, 52, 254, 3), +(3001, 4499, 202, 55, 64, 3), +(3001, 4674, 202, 56, 254, 3), +(3001, 4670, 202, 59, 254, 3), +(3001, 4688, 202, 65, 71, 3), +(3001, 8000, 202, 68, 254, 3), +(3001, 6190, 202, 70, 254, 3), +(3001, 10965, 202, 72, 254, 3), +(3001, 10966, 202, 72, 254, 2), +(3001, 10967, 202, 72, 254, 1), +(3003, 4587, 202, 54, 254, 3), +(3003, 7004, 202, 56, 60, 3), +(3003, 4518, 202, 60, 254, 3), +(3003, 6731, 202, 61, 68, 3), +(3003, 6663, 202, 69, 72, 3), +(3003, 11854, 202, 73, 77, 3), +(3003, 11855, 202, 73, 77, 2), +(3003, 11856, 202, 73, 77, 1), +(3003, 14987, 202, 78, 82, 3), +(3003, 14988, 202, 78, 82, 2), +(3003, 14989, 202, 78, 82, 1), +(3003, 19103, 202, 83, 254, 1), +(3003, 19131, 202, 83, 254, 3), +(3003, 19132, 202, 83, 254, 2), +(3003, 19133, 202, 83, 254, 1), +(3003, 22665, 202, 83, 254, 3), +(3003, 22666, 202, 83, 254, 2), +(3003, 22667, 202, 83, 254, 1), +(3003, 19101, 202, 83, 254, 3), +(3003, 19102, 202, 83, 254, 2), +(3004, 4587, 202, 54, 254, 3), +(3004, 4519, 202, 60, 254, 3), +(3005, 4587, 202, 54, 254, 3), +(3005, 7005, 202, 56, 60, 3), +(3005, 4504, 202, 60, 254, 3), +(3005, 6741, 202, 61, 68, 3), +(3005, 6673, 202, 69, 72, 3), +(3005, 11866, 202, 73, 77, 3), +(3005, 11867, 202, 73, 77, 2), +(3005, 11868, 202, 73, 77, 1), +(3005, 15211, 202, 78, 82, 3), +(3005, 15212, 202, 78, 82, 2), +(3005, 15213, 202, 78, 82, 1), +(3005, 19364, 202, 83, 254, 3), +(3005, 19365, 202, 83, 254, 2), +(3005, 19366, 202, 83, 254, 1), +(3005, 22662, 202, 83, 254, 3), +(3005, 22663, 202, 83, 254, 2), +(3005, 19131, 202, 83, 254, 3), +(3005, 22664, 202, 83, 254, 1), +(3005, 19132, 202, 83, 254, 2), +(3005, 19133, 202, 83, 254, 1), +(3007, 4587, 202, 40, 254, 3), +(3007, 4510, 202, 51, 64, 3), +(3007, 4509, 202, 53, 254, 3), +(3007, 4502, 202, 54, 254, 3), +(3007, 4690, 202, 65, 71, 3), +(3007, 6195, 202, 68, 254, 3), +(3007, 10938, 202, 72, 254, 3), +(3007, 10939, 202, 72, 254, 2), +(3007, 10940, 202, 72, 254, 1), +(3007, 19830, 202, 85, 254, 2), +(3007, 19831, 202, 85, 254, 1), +(3007, 19829, 202, 85, 254, 3), +(3008, 4587, 202, 54, 254, 3), +(3008, 4516, 202, 55, 254, 3), +(3009, 4587, 202, 40, 254, 3), +(3009, 4673, 202, 53, 254, 3), +(3009, 4515, 202, 55, 254, 3), +(3009, 10889, 202, 72, 254, 3), +(3009, 10890, 202, 72, 254, 2), +(3009, 10891, 202, 72, 254, 1), +(3009, 10895, 202, 74, 254, 3), +(3009, 10896, 202, 74, 254, 2), +(3009, 10897, 202, 74, 254, 1), +(3015, 4587, 202, 54, 254, 3), +(3015, 4671, 202, 55, 254, 3), +(3016, 4587, 202, 40, 254, 3), +(3016, 5040, 202, 56, 254, 3), +(3016, 5038, 202, 59, 254, 3), +(3016, 5043, 202, 65, 254, 3), +(3016, 6200, 202, 66, 254, 3), +(3016, 6201, 202, 68, 254, 3), +(3016, 8003, 202, 68, 254, 3), +(3016, 8476, 202, 70, 254, 3); + +DELETE +FROM bot_spells_entries +WHERE NOT EXISTS +(SELECT * +FROM spells_new +WHERE bot_spells_entries.spell_id = spells_new.id); )" } // -- template; copy/paste this when you need to create a new entry diff --git a/common/spdat.h b/common/spdat.h index b518dc7250..4681a85841 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -727,11 +727,19 @@ namespace BotSpellTypes constexpr uint16 SendHome = 111; constexpr uint16 SummonCorpse = 112; constexpr uint16 AELull = 113; + + // Discipline Types + constexpr uint16 Discipline = 200; + constexpr uint16 DiscAggressive = 201; + constexpr uint16 DiscDefensive = 202; + constexpr uint16 DiscUtility = 203; constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed + constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this + constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed } const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); diff --git a/common/version.h b/common/version.h index 731ffab162..ffc83aef71 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9286 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9052 //TODO bot rewrite +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 //TODO bot rewrite #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 41ab32421b..b2e8e195f5 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10826,7 +10826,13 @@ bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { } bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { - if (!tar || spells[spell_id].target_type == ST_Self) { + if (!IsValidSpell(spell_id)) { + return false; + } + + SPDat_Spell_Struct spell = spells[spell_id]; + + if (!tar || spell.target_type == ST_Self) { tar = this; } @@ -10854,7 +10860,7 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { } if (!CastChecks(spell_id, tar, UINT16_MAX)) { - GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", GetCleanName()); + GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, target type, etc.'", GetCleanName()); return false; } @@ -10866,7 +10872,7 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { fmt::format( "Interrupting {}. I have been commanded to try to cast an AA - {} on {}.", CastingSpellID() ? spells[CastingSpellID()].name : "my spell", - spells[spell_id].name, + spell.name, tar->GetCleanName() ).c_str() ); @@ -10893,7 +10899,7 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { SetSpellRecastTimer(spell_id, timer_duration); } else { - GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", GetCleanName()); + GetBotOwner()->Message(Chat::Red, "%s says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, target type, etc.'", GetCleanName()); return false; } @@ -10914,24 +10920,26 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { return true; } -bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { +bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc) { + if (!IsValidSpell(spell_id)) { + return false; + } + SPDat_Spell_Struct spell = spells[spell_id]; - uint16 forcedSpellID = spell.id; - if (!tar || (spells[spell_id].target_type == ST_Self && tar != this)) { - LogTestDebug("{} set my target to myself for {} [#{}] due to !tar.", GetCleanName(), spell.name, forcedSpellID); //deleteme + if (!tar || (spell.target_type == ST_Self && tar != this)) { tar = this; } - if ((IsCharmSpell(forcedSpellID) || IsPetSpell(forcedSpellID) && HasPet())) { + if ((IsCharmSpell(spell_id) || IsPetSpell(spell_id) && HasPet())) { return false; } - if (IsResurrectSpell(forcedSpellID) && (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse())) { + if (IsResurrectSpell(spell_id) && (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse())) { return false; } - if (IsBeneficialSpell(forcedSpellID)) { + if (IsBeneficialSpell(spell_id)) { if ( (tar->IsNPC() && !tar->GetOwner()) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar->GetOwner())) || @@ -10949,7 +10957,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { } } - if (IsDetrimentalSpell(forcedSpellID) && (!GetBotOwner()->IsAttackAllowed(tar) || !IsAttackAllowed(tar))) { + if (IsDetrimentalSpell(spell_id) && (!GetBotOwner()->IsAttackAllowed(tar) || !IsAttackAllowed(tar))) { GetBotOwner()->Message( Chat::Yellow, fmt::format( @@ -10962,32 +10970,35 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { return false; } - if (!CheckSpellRecastTimer(forcedSpellID)) { - LogTestDebug("{} failed CheckSpellRecastTimer for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme - return false; + if (!isDisc) { + if (!CheckSpellRecastTimer(spell_id)) { + return false; + } } if ( - !RuleB(Bots, EnableBotTGB) && - IsGroupSpell(forcedSpellID) && - !IsTGBCompatibleSpell(forcedSpellID) && - !IsInGroupOrRaid(tar, true) + !IsInGroupOrRaid(tar, true) && + ( + !RuleB(Bots, EnableBotTGB) || + ( + IsGroupSpell(spell_id) && + !IsTGBCompatibleSpell(spell_id) + ) + ) ) { - LogTestDebug("{} failed TGB for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + LogTestDebug("{} failed TGB for {} [#{}].", GetCleanName(), spell.name, spell_id); //deleteme return false; } if (!DoLosChecks(this, tar)) { - LogTestDebug("{} failed LoS for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme return false; } - if (!CastChecks(forcedSpellID, tar, UINT16_MAX)) { - LogTestDebug("{} failed CastChecks for {} [#{}].", GetCleanName(), spell.name, forcedSpellID); //deleteme + if (!CastChecks(spell_id, tar, UINT16_MAX)) { GetBotOwner()->Message( Chat::Red, fmt::format( - "{} says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, etc.'", + "{} says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, target type, etc.'", GetBotOwner()->GetCleanName() ).c_str() ); @@ -11009,27 +11020,53 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id) { InterruptSpell(); } - if (CastSpell(forcedSpellID, tar->GetID())) { + if (CastSpell(spell_id, tar->GetID())) { BotGroupSay( this, fmt::format( "Casting {} on {}.", - GetSpellName(forcedSpellID), + GetSpellName(spell_id), (tar == this ? "myself" : tar->GetCleanName()) ).c_str() ); - int timer_duration = CalcBuffDuration(tar, this, forcedSpellID); + int timer_duration = 0; + + if (!isDisc) { + timer_duration = CalcBuffDuration(tar, this, spell_id); - if (timer_duration) { // negatives are perma buffs - timer_duration = GetActSpellDuration(forcedSpellID, timer_duration); + if (timer_duration) { + timer_duration = GetActSpellDuration(spell_id, timer_duration); + } + + if (timer_duration < 0) { + timer_duration = 0; + } } + else { + if (spell.recast_time > 0) { + timer_duration = spell.recast_time / 1000; + auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); - if (timer_duration < 0) { - timer_duration = 0; + if (focus > timer_duration) { + timer_duration = 0; + } + else { + timer_duration -= focus; + } + } + + if (timer_duration > 0) { + timer_duration = timer_duration * 1000; + } } - SetSpellRecastTimer(forcedSpellID, timer_duration); + if (!isDisc) { + SetSpellRecastTimer(spell_id, timer_duration); + } + else { + SetDisciplineReuseTimer(spell_id, timer_duration); + } return true; } @@ -11804,7 +11841,7 @@ uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) { } uint32 points = GetAA(ability->first_rank_id); - //if (points) { LogTestDebug("{}: {} says, '{} points for {} [#{} - {}] rank {}'", __LINE__, GetCleanName(), points, zone->GetAAName(aa_ability.first->id), aa_ability.first->id, aa_ability.second->id, points); } //deleteme + if (points > 0) { aa_ability = zone->GetAlternateAdvancementAbilityAndRank(ability->id, points); } @@ -11812,14 +11849,11 @@ uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) { rank = aa_ability.second; if (!points || !rank) { - LogTestDebug("{}: {} says, 'No {} found'", __LINE__, GetCleanName(), (!points ? "points" : "rank")); //deleteme return spell_id; } spell_id = rank->spell; - LogTestDebug("{}: {} says, 'Found {} [#{}]'", __LINE__, GetCleanName(), spells[spell_id].name, spell_id); //deleteme - return spell_id; } diff --git a/zone/bot.h b/zone/bot.h index f1c8e0482f..3f4c212376 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -401,7 +401,7 @@ class Bot : public NPC { bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); - bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id); + bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc = false); bool AttemptCloseBeneficialSpells(uint16 spellType); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 4eb2526172..56d45cac75 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1286,6 +1286,7 @@ int bot_command_init(void) bot_command_add("botupdate", "Updates a bot to reflect any level changes that you have experienced", AccountStatus::Player, bot_command_update) || bot_command_add("botwoad", "Changes the Barbarian woad of a bot", AccountStatus::Player, bot_command_woad) || bot_command_add("cast", "Tells the first found specified bot to cast the given spell type", AccountStatus::Player, bot_command_cast) || + bot_command_add("discipline", "Uses aggressive/defensive disciplines or can specify spell ID", AccountStatus::Player, bot_command_discipline) || bot_command_add("distanceranged", "Controls the range casters and ranged will try to stay away from a mob", AccountStatus::Player, bot_command_distance_ranged) || bot_command_add("classracelist", "Lists the classes and races and their appropriate IDs", AccountStatus::Player, bot_command_class_race_list) || bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || @@ -2222,6 +2223,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/default_settings.cpp" #include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" +#include "bot_commands/discipline.cpp" #include "bot_commands/distance_ranged.cpp" #include "bot_commands/find_aliases.cpp" #include "bot_commands/follow.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 6f66e87888..ca22b4d32b 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1673,6 +1673,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep); void bot_command_bot(Client *c, const Seperator *sep); void bot_command_bot_settings(Client* c, const Seperator* sep); void bot_command_cast(Client* c, const Seperator* sep); +void bot_command_discipline(Client* c, const Seperator* sep); void bot_command_distance_ranged(Client* c, const Seperator* sep); void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 054d763e41..7f1093cf25 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -184,7 +184,7 @@ void bot_command_cast(Client* c, const Seperator* sep) uint16 chosenSpellID = UINT16_MAX; if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) { - if (!RuleB(Bots, AllowForcedCastsBySpellID)) { + if (!RuleB(Bots, AllowCastAAs)) { c->Message(Chat::Yellow, "This commanded type is currently disabled."); return; } @@ -208,7 +208,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } if (!arg1.compare("spellid")) { - if (!RuleB(Bots, AllowCastAAs)) { + if (!RuleB(Bots, AllowForcedCastsBySpellID)) { c->Message(Chat::Yellow, "This commanded type is currently disabled."); return; } @@ -561,13 +561,11 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } else if (bySpellID) { - SPDat_Spell_Struct spell = spells[chosenSpellID]; - if (!bot_iter->CanUseBotSpell(chosenSpellID)) { continue; } - if (!tar || (spell.target_type == ST_Self && tar != bot_iter)) { + if (!tar || (spells[chosenSpellID].target_type == ST_Self && tar != bot_iter)) { tar = bot_iter; } diff --git a/zone/bot_commands/discipline.cpp b/zone/bot_commands/discipline.cpp new file mode 100644 index 0000000000..233eaae899 --- /dev/null +++ b/zone/bot_commands/discipline.cpp @@ -0,0 +1,296 @@ +#include "../bot_command.h" + +void bot_command_discipline(Client* c, const Seperator* sep) +{ + if (helper_command_alias_fail(c, "bot_command_discipline", sep->arg[0], "discipline")) { + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + std::vector description = + { + "Tells applicable bots to use the specified disciplines" + }; + + std::vector notes = { }; + + std::vector example_format = + { + fmt::format( + "{} [aggressive | defensive | spell ID] [actionable, default: spawned]" + , sep->arg[0] + ) + }; + std::vector examples_one = + { + "To tell all bots to use an aggressive discipline:", + fmt::format( + "{} aggressive spawned", + sep->arg[0] + ) + }; + std::vector examples_two = + { + "To tell Warrior bots to use a defensive discipline:", + fmt::format( + "{} defensive byclass {}", + sep->arg[0], + Class::Warrior + ) + }; + std::vector examples_three = + { + "To tell all bots to use their Fearless discipline:", + fmt::format( + "{} 4587 spawned", + sep->arg[0] + ) + }; + + std::vector actionables = + { + "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + }; + + std::vector options = { }; + std::vector options_one = { }; + std::vector options_two = { }; + std::vector options_three = { }; + + std::string popup_text = c->SendCommandHelpWindow( + c, + description, + notes, + example_format, + examples_one, examples_two, examples_three, + actionables, + options, + options_one, options_two, options_three + ); + + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + + return; + } + + std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool aggressive = false; + bool defensive = false; + Mob* tar = c->GetTarget(); + uint16 spell_id = UINT16_MAX; + + if (!arg1.compare("aggressive")) { + aggressive = true; + } + else if (!arg1.compare("defensive")) { + defensive = true; + } + else if (sep->IsNumber(1)) { + if (!IsValidSpell(atoi(sep->arg[1]))) { + c->Message(Chat::Yellow, "You must enter a valid spell ID."); + return; + } + + spell_id = atoi(sep->arg[1]); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string actionableArg = sep->arg[ab_arg]; + + if (actionableArg.empty()) { + actionableArg = "spawned"; + } + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::vector sbl; + + if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + bool isSuccess = false; + uint16 successCount = 0; + Bot* firstFound = nullptr; + + for (auto bot_iter : sbl) { + if (!bot_iter->IsInGroupOrRaid(c)) { + continue; + } + + if (bot_iter->GetBotStance() == Stance::Passive || bot_iter->GetHoldFlag() || bot_iter->GetAppearance() == eaDead || bot_iter->IsFeared() || bot_iter->IsSilenced() || bot_iter->IsAmnesiad() || bot_iter->GetHP() < 0) { + continue; + } + + if (spell_id == UINT16_MAX) { // Aggressive/Defensive type + std::vector botSpellList; + + if (aggressive) { + botSpellList = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscAggressive); + } + else if (defensive) { + botSpellList = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscDefensive); + } + + for (int i = botSpellList.size() - 1; i >= 0; i--) { + if (!IsValidSpell(botSpellList[i].spellid)) { + continue; + } + + if (!bot_iter->CheckDisciplineReuseTimer(botSpellList[i].spellid)) { + uint32 remaining_time = (bot_iter->GetDisciplineReuseRemainingTime(botSpellList[i].spellid) / 1000); + + bot_iter->OwnerMessage( + fmt::format( + "I can use this discipline in {}.", + Strings::SecondsToTime(remaining_time) + ) + ); + + continue; + } + + if (bot_iter->GetEndurance() < spells[botSpellList[i].spellid].endurance_cost) { + continue; + } + + if (bot_iter->DivineAura() && !IsCastNotStandingSpell(botSpellList[i].spellid)) { + continue; + } + + if (spells[botSpellList[i].spellid].buff_duration_formula != 0 && spells[botSpellList[i].spellid].target_type == ST_Self && bot_iter->HasDiscBuff()) { + continue; + } + + if (!tar || (spells[botSpellList[i].spellid].target_type == ST_Self && tar != bot_iter)) { + tar = bot_iter; + } + + if (bot_iter->AttemptForcedCastSpell(tar, botSpellList[i].spellid, true)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + spell_id = botSpellList[i].spellid; + } + } + } + else { // Direct spell ID + if (!IsValidSpell(spell_id)) { + continue; + } + + SPDat_Spell_Struct spell = spells[spell_id]; + + if (!bot_iter->CanUseBotSpell(spell_id)) { + continue; + } + + if (!bot_iter->CheckDisciplineReuseTimer(spell_id)) { + uint32 remaining_time = (bot_iter->GetDisciplineReuseRemainingTime(spell_id) / 1000); + + bot_iter->OwnerMessage( + fmt::format( + "I can use this item in {}.", + Strings::SecondsToTime(remaining_time) + ) + ); + + continue; + } + + if (bot_iter->GetEndurance() < spell.endurance_cost) { + continue; + } + + if (bot_iter->DivineAura() && !IsCastNotStandingSpell(spell_id)) { + continue; + } + + if (spell.buff_duration_formula != 0 && spell.target_type == ST_Self && bot_iter->HasDiscBuff()) { + continue; + } + + if (!tar || (spell.target_type == ST_Self && tar != bot_iter)) { + tar = bot_iter; + } + + if (bot_iter->AttemptForcedCastSpell(tar, spell_id, true)) { + if (!firstFound) { + firstFound = bot_iter; + } + + isSuccess = true; + ++successCount; + } + } + + continue; + } + + if (!isSuccess) { + c->Message(Chat::Yellow, "No bots were selected."); + } + else { + if (aggressive || defensive) { + c->Message( + Chat::Yellow, + fmt::format( + "{} {} {} {} discipline.", + ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), + ((successCount == 1 && firstFound) ? "used" : "of your bots used"), + (aggressive ? "an" : "a"), + (aggressive ? "aggressive" : "defensive") + ).c_str() + ); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "{} {} their {} [#{}] discipline.", + ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), + ((successCount == 1 && firstFound) ? "used" : "of your bots used"), + spells[spell_id].name, + spell_id + ).c_str() + ); + } + } +} diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 963d715974..55356ad554 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -649,6 +649,10 @@ bool Bot::AI_PursueCastCheck() { continue; } + if (AIBot_spells_by_type[currentCast.spellType].empty()) { + continue; + } + result = AttemptAICastSpell(currentCast.spellType, nullptr); if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { @@ -721,6 +725,10 @@ bool Bot::AI_IdleCastCheck() { continue; } + if (AIBot_spells_by_type[currentCast.spellType].empty()) { + continue; + } + result = AttemptAICastSpell(currentCast.spellType, nullptr); if (result) { @@ -778,6 +786,10 @@ bool Bot::AI_EngagedCastCheck() { continue; } + if (AIBot_spells_by_type[currentCast.spellType].empty()) { + continue; + } + result = AttemptAICastSpell(currentCast.spellType, nullptr); if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { @@ -1041,11 +1053,15 @@ std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* bot } if ( - !RuleB(Bots, EnableBotTGB) && - IsGroupSpell(botSpellList[i].spellid) && - !IsTGBCompatibleSpell(botSpellList[i].spellid) && - !botCaster->IsInGroupOrRaid(tar, true) - ) { + !botCaster->IsInGroupOrRaid(tar, true) && + ( + !RuleB(Bots, EnableBotTGB) || + ( + IsGroupSpell(botSpellList[i].spellid) && + !IsTGBCompatibleSpell(botSpellList[i].spellid) + ) + ) + ) { continue; } From 2d7d58b4efdc4fd6e627980ebff78c2c5d6acbf6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 29 Dec 2024 20:18:12 -0600 Subject: [PATCH 249/394] remove ^aggressive/^defensive --- zone/bot_command.cpp | 4 -- zone/bot_command.h | 2 - zone/bot_commands/aggressive.cpp | 85 -------------------------------- zone/bot_commands/defensive.cpp | 74 --------------------------- 4 files changed, 165 deletions(-) delete mode 100644 zone/bot_commands/aggressive.cpp delete mode 100644 zone/bot_commands/defensive.cpp diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 56d45cac75..25149e2eb9 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -1247,7 +1247,6 @@ int bot_command_init(void) if ( bot_command_add("actionable", "Lists actionable command arguments and use descriptions", AccountStatus::Player, bot_command_actionable) || - bot_command_add("aggressive", "Orders a bot to use a aggressive discipline", AccountStatus::Player, bot_command_aggressive) || bot_command_add("applypoison", "Applies cursor-held poison to a rogue bot's weapon", AccountStatus::Player, bot_command_apply_poison) || bot_command_add("attack", "Orders bots to attack a designated target", AccountStatus::Player, bot_command_attack) || bot_command_add("behindmob", "Toggles whether or not your bot tries to stay behind a mob", AccountStatus::Player, bot_command_behind_mob) || @@ -1292,7 +1291,6 @@ int bot_command_init(void) bot_command_add("clickitem", "Orders your targeted bot to click the item in the provided inventory slot.", AccountStatus::Player, bot_command_click_item) || bot_command_add("copysettings", "Copies settings from one bot to another", AccountStatus::Player, bot_command_copy_settings) || bot_command_add("defaultsettings", "Restores a bot back to default settings", AccountStatus::Player, bot_command_default_settings) || - bot_command_add("defensive", "Orders a bot to use a defensive discipline", AccountStatus::Player, bot_command_defensive) || bot_command_add("depart", "Orders a bot to open a magical doorway to a specified destination", AccountStatus::Player, bot_command_depart) || bot_command_add("enforcespellsettings", "Toggles your Bot to cast only spells in their spell settings list.", AccountStatus::Player, bot_command_enforce_spell_list) || bot_command_add("findaliases", "Find available aliases for a bot command", AccountStatus::Player, bot_command_find_aliases) || @@ -2207,7 +2205,6 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { } #include "bot_commands/actionable.cpp" -#include "bot_commands/aggressive.cpp" #include "bot_commands/appearance.cpp" #include "bot_commands/apply_poison.cpp" #include "bot_commands/apply_potion.cpp" @@ -2221,7 +2218,6 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/click_item.cpp" #include "bot_commands/copy_settings.cpp" #include "bot_commands/default_settings.cpp" -#include "bot_commands/defensive.cpp" #include "bot_commands/depart.cpp" #include "bot_commands/discipline.cpp" #include "bot_commands/distance_ranged.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index ca22b4d32b..b64dabbf96 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1663,7 +1663,6 @@ int bot_command_real_dispatch(Client *c, char const *message); // 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); void bot_command_apply_potion(Client* c, const Seperator* sep); void bot_command_attack(Client *c, const Seperator *sep); @@ -1679,7 +1678,6 @@ void bot_command_class_race_list(Client* c, const Seperator* sep); void bot_command_click_item(Client* c, const Seperator* sep); void bot_command_copy_settings(Client* c, const Seperator* sep); void bot_command_default_settings(Client* c, const Seperator* sep); -void bot_command_defensive(Client *c, const Seperator *sep); void bot_command_depart(Client *c, const Seperator *sep); void bot_command_find_aliases(Client *c, const Seperator *sep); void bot_command_follow(Client *c, const Seperator *sep); diff --git a/zone/bot_commands/aggressive.cpp b/zone/bot_commands/aggressive.cpp deleted file mode 100644 index 7f26b2a49d..0000000000 --- a/zone/bot_commands/aggressive.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "../bot_command.h" - -void bot_command_aggressive(Client* c, const Seperator* sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Stance]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Stance) || - helper_command_alias_fail(c, "bot_command_aggressive", sep->arg[0], "aggressive")) { - return; - } - - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "note: Orders a bot to use a aggressive discipline"); - helper_send_usage_required_bots(c, BCEnum::SpT_Stance); - return; - } - - const int ab_mask = ActionableBots::ABM_Type1; - int ab_arg = 1; - std::string class_race_arg = sep->arg[ab_arg]; - bool class_race_check = false; - - if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { - class_race_check = true; - } - - std::vector sbl; - - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { - return; - } - - sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - - int success_count = 0; - int candidate_count = sbl.size(); - for (auto list_iter: *local_list) { - if (sbl.empty()) { - break; - } - - auto local_entry = list_iter->SafeCastToStance(); - if (helper_spell_check_fail(local_entry)) { - continue; - } - if (local_entry->stance_type != BCEnum::StT_Aggressive) { - continue; - } - - for (auto bot_iter = sbl.begin(); bot_iter != sbl.end();) { - Bot* my_bot = *bot_iter; - if (local_entry->caster_class != my_bot->GetClass()) { - ++bot_iter; - continue; - } - if (local_entry->spell_level > my_bot->GetLevel()) { - ++bot_iter; - continue; - } - - my_bot->InterruptSpell(); - if (candidate_count == 1) { - Bot::BotGroupSay( - my_bot, - fmt::format( - "Using {}.", - spells[local_entry->spell_id].name - ).c_str() - ); - } - - my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID()); - ++success_count; - - bot_iter = sbl.erase(bot_iter); - } - } - - c->Message( - Chat::White, - "%i of %i bots have attempted to use aggressive disciplines", - success_count, - candidate_count - ); -} diff --git a/zone/bot_commands/defensive.cpp b/zone/bot_commands/defensive.cpp deleted file mode 100644 index 4fcf0fce2b..0000000000 --- a/zone/bot_commands/defensive.cpp +++ /dev/null @@ -1,74 +0,0 @@ -#include "../bot_command.h" - -void bot_command_defensive(Client *c, const Seperator *sep) -{ - bcst_list* local_list = &bot_command_spells[BCEnum::SpT_Stance]; - if (helper_spell_list_fail(c, local_list, BCEnum::SpT_Stance) || helper_command_alias_fail(c, "bot_command_defensive", sep->arg[0], "defensive")) - return; - if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - helper_send_usage_required_bots(c, BCEnum::SpT_Stance); - return; - } - const int ab_mask = ActionableBots::ABM_Type1; - - std::string arg1 = sep->arg[1]; - int ab_arg = 1; - - std::string class_race_arg = sep->arg[ab_arg]; - bool class_race_check = false; - - if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { - class_race_check = true; - } - - std::vector sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { - return; - } - - sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - - int success_count = 0; - int candidate_count = sbl.size(); - for (auto list_iter : *local_list) { - if (sbl.empty()) - break; - - auto local_entry = list_iter->SafeCastToStance(); - if (helper_spell_check_fail(local_entry)) - continue; - if (local_entry->stance_type != BCEnum::StT_Defensive) - continue; - - for (auto bot_iter = sbl.begin(); bot_iter != sbl.end(); ) { - Bot* my_bot = *bot_iter; - if (local_entry->caster_class != my_bot->GetClass()) { - ++bot_iter; - continue; - } - if (local_entry->spell_level > my_bot->GetLevel()) { - ++bot_iter; - continue; - } - - my_bot->InterruptSpell(); - if (candidate_count == 1) { - Bot::BotGroupSay( - my_bot, - fmt::format( - "Using {}.", - spells[local_entry->spell_id].name - ).c_str() - ); - } - - my_bot->UseDiscipline(local_entry->spell_id, my_bot->GetID()); - ++success_count; - - bot_iter = sbl.erase(bot_iter); - } - } - - c->Message(Chat::White, "%i of %i bots have attempted to use defensive disciplines", success_count, candidate_count); -} From d3ccff2d65a8133cc3a08eff4b8d63c0c9fc526c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 30 Dec 2024 01:16:36 -0600 Subject: [PATCH 250/394] remove this for a separate PR --- zone/exp.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/exp.cpp b/zone/exp.cpp index 70f72cacb5..be6b8af2b8 100644 --- a/zone/exp.cpp +++ b/zone/exp.cpp @@ -1004,7 +1004,6 @@ void Client::SetLevel(uint8 set_level, bool command) } } else { SetHP(CalcMaxHP()); // Why not, lets give them a free heal - SetMana(CalcMaxMana()); } if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) { From d13a48fc93bbbc0d9a7e31c852eec410a0054ef1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:02:06 -0600 Subject: [PATCH 251/394] cleanup --- zone/aggro.cpp | 9 ++- zone/bot.cpp | 137 +++++++++++++++++++----------------------- zone/bot_database.cpp | 14 ++--- zone/botspellsai.cpp | 35 ++++++----- zone/client.cpp | 6 +- zone/mob.cpp | 16 +---- 6 files changed, 95 insertions(+), 122 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 4f1d658319..74d2364f58 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1737,7 +1737,6 @@ bool Mob::CheckLosCheat(Mob* who, Mob* other) { auto who_to_other = DistanceNoZ(who->GetPosition(), other->GetPosition()); auto distance_difference = who_to_other - (who_to_door + other_to_door); if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) { - LogTestDebug("CheckLosCheat failed at Door [{}], TriggerType [{}], GetLockpick [{}], GetKeyItem [{}], GetNoKeyring [{}]", door->GetDoorID(), door->GetTriggerType(), door->GetLockpick(), door->GetKeyItem(), door->GetNoKeyring()); //deleteme return false; } } @@ -1758,7 +1757,7 @@ bool Mob::CheckLosCheat(Mob* who, Mob* other) { // // if (zone_id == 103) { // Doors* door_to_check = entity_list.FindDoor(8); - // TestDebug("Entered LoSCheat for ZoneID: [{}]", zone_id); //deleteme + // TestDebug("Entered LoSCheat for ZoneID: [{}]", zone_id); // glm::vec4 who_check; who_check.x = 1202; who_check.y = 559; who_check.z = -158.94; // glm::vec4 other_check; other_check.x = 1291; other_check.y = 559; other_check.z = -158.19; // float my_distance = DistanceNoZ(who->GetPosition(), who_check); @@ -1766,11 +1765,11 @@ bool Mob::CheckLosCheat(Mob* who, Mob* other) { // float my_range = 16; // float tar_range = 75; // if (my_distance <= my_range && tar_distance <= tar_range && !quest_manager.isdooropen(8)) { - // TestDebug("Door is NOT open"); //deleteme - // TestDebug("LoSCheat failed"); //deleteme + // TestDebug("Door is NOT open"); + // TestDebug("LoSCheat failed"); // return false; // } - // TestDebug("LoS Check for ZoneID: [{}] was [{}] units for [{}], [{}] units for [{}]", zone_id, my_distance, who->GetCleanName(), tar_distance, other->GetCleanName()); //deleteme + // TestDebug("LoS Check for ZoneID: [{}] was [{}] units for [{}], [{}] units for [{}]", zone_id, my_distance, who->GetCleanName(), tar_distance, other->GetCleanName()); // } //} return true; diff --git a/zone/bot.cpp b/zone/bot.cpp index b2e8e195f5..e3cdffe9fa 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9280,14 +9280,14 @@ uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][St bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { if (!tar) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); return false; } - //LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); if (GetUltimateSpellHold(spellType, tar)) { - //LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); return false; } @@ -9296,17 +9296,17 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { } if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); return false; } if (GetHPRatio() < GetSpellTypeMinHPLimit(spellType) || GetHPRatio() > GetSpellTypeMaxHPLimit(spellType)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); return false; } if (!GetUltimateSpellDelayCheck(spellType, tar)) { - //LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); return false; } @@ -9316,7 +9316,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return true; default: if (GetHPRatioForSpellType(spellType, tar) < GetUltimateSpellMinThreshold(spellType, tar) || GetHPRatioForSpellType(spellType, tar) > GetUltimateSpellMaxThreshold(spellType, tar)) { - //LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); return false; } } @@ -9327,7 +9327,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { if (doPrechecks) { if (!tar) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } @@ -9341,57 +9341,57 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (!PrecastChecks(tar, spellType)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); return false; } } - //LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "nobody")); //deleteme + LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "nobody")); if (!IsValidSpell(spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); return false; } if (IsFeared() || IsSilenced() || IsAmnesiad()) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } if (!CheckSpellRecastTimer(spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (!BotHasEnoughMana(spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { if (IsBeneficialSpell(spell_id)) { if (IsEngaged()) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); return false; } } @@ -9399,7 +9399,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { if (IsBeneficialSpell(spell_id)) { if (!IsEngaged()) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); return false; } } @@ -9409,33 +9409,33 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec int chance = GetFocusEffect(focusFcMute, spell_id); if (chance && zone->random.Roll(chance)) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); return(false); } } if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (SpellTypeRequiresTarget(spellType) && !tar) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } @@ -9444,32 +9444,32 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec && tar != this && (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) ) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (this == tar && IsSacrificeSpell(spell_id)) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (tar->GetSpecialAbility(SpecialAbility::MagicImmunity)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (tar->IsImmuneToBotSpell(spell_id, this)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (!tar->CheckSpellLevelRestriction(this, spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9481,7 +9481,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec ) ) { if (tar->IsBlockedBuff(spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } } @@ -9494,11 +9494,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec ) ) { if (tar->GetOwner()->IsBlockedPetBuff(spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } } - //LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); if (!CanCastSpellType(spellType, spell_id, tar)) { return false; } @@ -9508,7 +9508,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9517,7 +9517,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec && tar->CanBuffStack(spell_id, GetLevel(), true) < 0 ) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9526,7 +9526,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (!AECheck && !IsValidSpellRange(spell_id, tar)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9535,17 +9535,17 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9554,7 +9554,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { if (!spell_id || !tar) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); return false; } @@ -9581,27 +9581,27 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: if (tar == this && spells[spell_id].target_type == ST_TargetsTarget) { - //LogBotPreChecks("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); return false; } if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spell_id, SE_Illusion)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (spells[spell_id].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if ((IsGroupSpell(spell_id) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9615,7 +9615,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9625,7 +9625,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) ) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9644,7 +9644,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { if (IsLichSpell(tar->GetBuffs()[j].spellid)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } } @@ -9667,7 +9667,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9677,7 +9677,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) ) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9691,7 +9691,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { case BotSpellTypes::AELull: case BotSpellTypes::Lull: if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9700,7 +9700,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { break; } - //LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); //deleteme + //LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return true; } @@ -9800,7 +9800,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spell_id, int32 resist_limit) { default: return true; } - ////LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); //deleteme) + //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); if ((targetResist + level_mod - resist_difficulty) > resist_limit) { return false; } @@ -10047,46 +10047,32 @@ void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { int Bot::GetBotBaseSetting(uint16 botSetting) { switch (botSetting) { case BotBaseSettings::ExpansionBitmask: - //LogBotSettingsDetail("Returning current GetExpansionBitmask of [{}] for [{}]", GetExpansionBitmask(), GetCleanName()); //deleteme return GetExpansionBitmask(); case BotBaseSettings::ShowHelm: - //LogBotSettingsDetail("Returning current GetShowHelm of [{}] for [{}]", GetShowHelm(), GetCleanName()); //deleteme return GetShowHelm(); case BotBaseSettings::FollowDistance: - //LogBotSettingsDetail("Returning current GetFollowDistance of [{}] for [{}]", sqrt(GetFollowDistance()), GetCleanName()); //deleteme return sqrt(GetFollowDistance()); case BotBaseSettings::StopMeleeLevel: - //LogBotSettingsDetail("Returning current GetStopMeleeLevel of [{}] for [{}]", GetStopMeleeLevel(), GetCleanName()); //deleteme return GetStopMeleeLevel(); case BotBaseSettings::EnforceSpellSettings: - //LogBotSettingsDetail("Returning current GetBotEnforceSpellSetting of [{}] for [{}]", GetBotEnforceSpellSetting(), GetCleanName()); //deleteme return GetBotEnforceSpellSetting(); case BotBaseSettings::RangedSetting: - //LogBotSettingsDetail("Returning current IsBotRanged of [{}] for [{}]", IsBotRanged(), GetCleanName()); //deleteme return IsBotRanged(); case BotBaseSettings::PetSetTypeSetting: - //LogBotSettingsDetail("Returning current GetPetChooserID of [{}] for [{}]", GetPetChooserID(), GetCleanName()); //deleteme return GetPetChooserID(); case BotBaseSettings::BehindMob: - //LogBotSettingsDetail("Returning current GetBehindMob of [{}] for [{}]", GetBehindMob(), GetCleanName()); //deleteme return GetBehindMob(); case BotBaseSettings::DistanceRanged: - //LogBotSettingsDetail("Returning current GetBotDistanceRanged of [{}] for [{}]", GetBotDistanceRanged(), GetCleanName()); //deleteme return GetBotDistanceRanged(); case BotBaseSettings::IllusionBlock: - //LogBotSettingsDetail("Returning current GetIllusionBlock of [{}] for [{}]", GetIllusionBlock(), GetCleanName()); //deleteme return GetIllusionBlock(); case BotBaseSettings::MaxMeleeRange: - //LogBotSettingsDetail("Returning current MaxMeleeRange of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme return GetMaxMeleeRange(); case BotBaseSettings::MedInCombat: - //LogBotSettingsDetail("Returning current GetMedInCombate of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme return GetMedInCombat(); case BotBaseSettings::SitHPPct: - //LogBotSettingsDetail("Returning current GetSitHPPct of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme return GetSitHPPct(); case BotBaseSettings::SitManaPct: - //LogBotSettingsDetail("Returning current GetSitManaPct of [{}] for [{}]", GetMaxMeleeRange(), GetCleanName()); //deleteme return GetSitManaPct(); default: return true; @@ -10159,7 +10145,7 @@ void Bot::LoadDefaultBotSettings() { for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, botStance)); - //LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); //deleteme + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -10186,10 +10172,10 @@ void Bot::LoadDefaultBotSettings() { _spellSettings.push_back(t); - //LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(botStance), botStance); //deleteme - //LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, botStance), GetDefaultSpellDelay(i, botStance), GetDefaultSpellMinThreshold(i, botStance), GetDefaultSpellMaxThreshold(i, botStance)); //deleteme - //LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, botStance), GetDefaultSpellTypeMinManaLimit(i, botStance), GetDefaultSpellTypeMaxManaLimit(i, botStance), GetDefaultSpellTypeMinHPLimit(i, botStance), GetDefaultSpellTypeMaxHPLimit(i, botStance)); //deleteme - //LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), botStance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), botStance), GetDefaultSpellTypePursuePriority(i, GetClass(), botStance), GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); //deleteme + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(botStance), botStance); + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, botStance), GetDefaultSpellDelay(i, botStance), GetDefaultSpellMinThreshold(i, botStance), GetDefaultSpellMaxThreshold(i, botStance)); + LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, botStance), GetDefaultSpellTypeMinManaLimit(i, botStance), GetDefaultSpellTypeMaxManaLimit(i, botStance), GetDefaultSpellTypeMinHPLimit(i, botStance), GetDefaultSpellTypeMaxHPLimit(i, botStance)); + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), botStance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), botStance), GetDefaultSpellTypePursuePriority(i, GetClass(), botStance), GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); } } @@ -10801,7 +10787,7 @@ bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { } if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { - //LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); //deleteme + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); return result; } @@ -10986,7 +10972,6 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc) { ) ) ) { - LogTestDebug("{} failed TGB for {} [#{}].", GetCleanName(), spell.name, spell_id); //deleteme return false; } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 84fa0dfa46..53cf76225d 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2244,7 +2244,7 @@ bool BotDatabase::LoadBotSettings(Mob* m) , m->GetBotSettingCategoryName(e.setting_type) , e.setting_type , e.value - ); //deleteme + ); } else { LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}]." @@ -2254,7 +2254,7 @@ bool BotDatabase::LoadBotSettings(Mob* m) , m->GetSpellTypeNameByID(e.setting_id) , e.setting_id , e.value - ); //deleteme + ); } m->SetBotSetting(e.setting_type, e.setting_id, e.value); @@ -2313,7 +2313,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); //deleteme + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); } } @@ -2333,7 +2333,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, botStance)); //deleteme + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, botStance)); } } } @@ -2354,12 +2354,12 @@ bool BotDatabase::SaveBotSettings(Mob* m) v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->GetIllusionBlock()); //deleteme + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->GetIllusionBlock()); } for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { - LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { auto e = BotSettingsRepository::BotSettings{ .char_id = charID, @@ -2374,7 +2374,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); //deleteme + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); } } } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 55356ad554..69bc55c167 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -26,7 +26,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return false; } - //LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); if ( !AI_HasSpells() || @@ -271,7 +271,6 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return true; } - else { LogTestDebug("{} says, '{} [#{}] - [{}] FAILED AIDoSpellCast on {}.'", GetCleanName(), spells[s.SpellId].name, s.SpellId, GetSpellTypeNameByID(spellType), tar->GetCleanName()); } //deleteme } return false; @@ -624,7 +623,7 @@ bool Bot::AI_PursueCastCheck() { if (GetTarget() && AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Pursue autocast check triggered: [{}]", GetCleanName()); - //LogBotPreChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -637,7 +636,7 @@ bool Bot::AI_PursueCastCheck() { for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { - //LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); continue; } @@ -682,7 +681,7 @@ bool Bot::AI_IdleCastCheck() { if (AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Non-Engaged autocast check triggered: [{}]", GetCleanName()); - //LogBotPreChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -705,7 +704,7 @@ bool Bot::AI_IdleCastCheck() { for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { - //LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); continue; } @@ -761,7 +760,7 @@ bool Bot::AI_EngagedCastCheck() { if (GetTarget() && AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Engaged autocast check triggered: [{}]", GetCleanName()); - //LogBotPreChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); //deleteme + LogBotPreChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -774,7 +773,7 @@ bool Bot::AI_EngagedCastCheck() { for (auto& currentCast : castOrder) { if (currentCast.priority == 0) { - //LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); //deleteme + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); continue; } @@ -2828,7 +2827,7 @@ void Bot::CheckBotSpells() { for (const auto& s : spellList) { if (!IsValidSpell(s.spell_id)) { - //LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); //deleteme + LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); continue; } @@ -2836,7 +2835,7 @@ void Bot::CheckBotSpells() { spell_id = spell.id; if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] >= 255) { - //LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); //deleteme + LogBotSpellTypeChecks("{} [#{}] is not usable by a {} [#{}].", GetSpellName(spell_id), spell_id, GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX), s.npc_spells_id); } else { if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] > s.minlevel) { @@ -2847,7 +2846,7 @@ void Bot::CheckBotSpells() { , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.minlevel - ); //deleteme + ); LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] @@ -2859,7 +2858,7 @@ void Bot::CheckBotSpells() { , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id - ); //deleteme + ); } if (spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] < s.minlevel) { @@ -2870,7 +2869,7 @@ void Bot::CheckBotSpells() { , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.minlevel - ); //deleteme + ); LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `minlevel` = {} WHERE `spellid` = {} AND `npc_spells_id` = {}; -- {} [#{}] from minlevel {} to {} for {} [#{}]" , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] @@ -2882,7 +2881,7 @@ void Bot::CheckBotSpells() { , spell.classes[s.npc_spells_id - (BOT_CLASS_BASE_ID_PREFIX + 1)] , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id - ); //deleteme + ); } @@ -2894,7 +2893,7 @@ void Bot::CheckBotSpells() { , GetClassIDName(s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX) , s.npc_spells_id , s.maxlevel - ); //deleteme + ); } } @@ -2922,7 +2921,7 @@ void Bot::CheckBotSpells() { , spell_id , GetSpellTypeNameByID(s.type) , s.type - ); //deleteme + ); } else { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] and should be {} [#{}]" @@ -2932,7 +2931,7 @@ void Bot::CheckBotSpells() { , s.type , GetSpellTypeNameByID(correctType) , correctType - ); //deleteme + ); LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" , correctType , spell_id @@ -2942,7 +2941,7 @@ void Bot::CheckBotSpells() { , s.type , GetSpellTypeNameByID(correctType) , correctType - ); //deleteme + ); } } } diff --git a/zone/client.cpp b/zone/client.cpp index 6544ac33a4..8b5d57737c 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13086,7 +13086,7 @@ void Client::LoadDefaultBotSettings() { // Only illusion block supported currently SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); - LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); //deleteme + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { BotSpellSettings_Struct t; @@ -13101,8 +13101,8 @@ void Client::LoadDefaultBotSettings() { _spellSettings.push_back(t); - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); //deleteme - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); //deleteme + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); } } diff --git a/zone/mob.cpp b/zone/mob.cpp index 918326067e..c1eee931a7 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4844,7 +4844,7 @@ bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, const uint16 maxIterationsAllowed = 50; uint16 counter = 0; - //LogTestDebug("Plotting for {} - Min: [{}] - Max: [{}] - BehindMob: [{}] - Taunt [{}] -- LosReq [{}]", GetCleanName(), min_distance, max_distance, behindOnly, frontOnly, bypassLoS ? "bypassed" : CastToBot()->RequiresLoSForPositioning() ? "true" : "false"); //deleteme + while (counter < maxIterationsAllowed) { tempX = tarX + zone->random.Real(-max_distance, max_distance); tempY = tarY + zone->random.Real(-max_distance, max_distance); @@ -4858,7 +4858,6 @@ bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, tempZ = bestZ; } else { - //LogTestDebug("{} - Plot Failed GetFixedZ - Try #[{}].", GetCleanName(), (counter + 1)); //deleteme counter++; continue; } @@ -4866,30 +4865,26 @@ bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, temp_m_Position.x = tempX; temp_m_Position.y = tempY; temp_m_Position.z = tempZ; - //tar_distance = DistanceNoZ(target->GetPosition(), temp_m_Position); + tar_distance = Distance(target->GetPosition(), temp_m_Position); if (tar_distance > max_distance || tar_distance < min_distance) { - //LogTestDebug("{} - Plot Failed Distance - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); counter++; continue; } if (frontOnly && !InFrontMob(target, tempX, tempY)) { - //LogTestDebug("{} - Plot Failed frontOnly - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); counter++; continue; } else if (behindOnly && !BehindMob(target, tempX, tempY)) { - //LogTestDebug("{} - Plot Failed BehindMob - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); counter++; continue; } if (!bypassLoS && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, tempX, tempY, tempZ)) { - //LogTestDebug("{} - Plot Failed LoS - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); counter++; continue; } - //LogTestDebug("{} - Plot PASSED! - Try #[{}]. Target LOCs XYZ - [{}], [{}], [{}] - Temp LOCs [{}], [{}], [{}] - Distance between = [{}] - Melee Distance = [{}] - Difference = [{}]", GetCleanName(), (counter + 1), target->GetX(), target->GetY(), target->GetZ(), tempX, tempY, tempZ, tar_distance, max_distance, (tar_distance / max_distance)); + Result = true; break; } @@ -9753,11 +9748,6 @@ void Mob::SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue) { void Mob::SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime) { _spellSettings[spellType].recastTimer.Start(recastTime); - //LogBotDelayChecksDetail("{} says, 'My {} Delay was to {} seconds.'" - // , GetCleanName() - // , GetSpellTypeNameByID(spellType) - // , (recastTime / 1000.00) - //); //deleteme } void Mob::StartBotSpellTimers() { From f182294648a51650ceec162a1a9c6a85903c1fdb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:06:40 -0600 Subject: [PATCH 252/394] Add BotGroupSay method --- zone/lua_bot.cpp | 6 ++++++ zone/lua_bot.h | 1 + zone/perl_bot.cpp | 6 ++++++ 3 files changed, 13 insertions(+) diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index 8003e13881..323976fe03 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -659,6 +659,11 @@ void Lua_Bot::DeleteBot() { self->DeleteBot(); } +void Lua_Bot::BotGroupSay(const char* message) { + Lua_Safe_Call_Void(); + self->BotGroupSay(self, message); +} + luabind::scope lua_register_bot() { return luabind::class_("Bot") .def(luabind::constructor<>()) @@ -688,6 +693,7 @@ luabind::scope lua_register_bot() { .def("ApplySpellRaid", (void(Lua_Bot::*)(int,int,int,bool))&Lua_Bot::ApplySpellRaid) .def("ApplySpellRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::ApplySpellRaid) .def("ApplySpellRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::ApplySpellRaid) + .def("BotGroupSay", (void(Lua_Bot::*)(const char*)) & Lua_Bot::BotGroupSay) .def("Camp", (void(Lua_Bot::*)(void))&Lua_Bot::Camp) .def("Camp", (void(Lua_Bot::*)(bool))&Lua_Bot::Camp) .def("ClearDisciplineReuseTimer", (void(Lua_Bot::*)())&Lua_Bot::ClearDisciplineReuseTimer) diff --git a/zone/lua_bot.h b/zone/lua_bot.h index 7a038b7dc3..1a5fdefd9a 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -46,6 +46,7 @@ class Lua_Bot : public Lua_Mob Lua_Mob GetOwner(); int16 HasBotItem(uint32 item_id); void OwnerMessage(std::string message); + void BotGroupSay(const char* message); bool ReloadBotDataBuckets(); bool ReloadBotOwnerDataBuckets(); bool ReloadBotSpells(); diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index f24134c6a9..2bce3f6474 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -615,6 +615,11 @@ void Perl_Bot_DeleteBot(Bot* self) // @categories Script Utility self->DeleteBot(); } +void Perl_Bot_BotGroupSay(Bot* self, const char* message) // @categories Script Utility +{ + self->BotGroupSay(self, message); +} + void perl_register_bot() { perl::interpreter state(PERL_GET_THX); @@ -644,6 +649,7 @@ void perl_register_bot() package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int))&Perl_Bot_ApplySpellRaid); package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int, bool))&Perl_Bot_ApplySpellRaid); package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int, bool, bool))&Perl_Bot_ApplySpellRaid); + package.add("BotGroupSay", &Perl_Bot_BotGroupSay); package.add("Camp", (void(*)(Bot*))&Perl_Bot_Camp); package.add("Camp", (void(*)(Bot*, bool))&Perl_Bot_Camp); package.add("ClearDisciplineReuseTimer", (void(*)(Bot*))&Perl_Bot_ClearDisciplineReuseTimer); From 4d7ac703fd652fcaa75a3e432e2c8c1f97a9cb9b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:08:32 -0600 Subject: [PATCH 253/394] todo list --- common/version.h | 2 +- zone/bot.cpp | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/common/version.h b/common/version.h index ffc83aef71..c5cb5d19f5 100644 --- a/common/version.h +++ b/common/version.h @@ -43,7 +43,7 @@ */ #define CURRENT_BINARY_DATABASE_VERSION 9286 -#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 //TODO bot rewrite +#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9054 #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index e3cdffe9fa..8e26b75a16 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -29,6 +29,15 @@ #include "../common/repositories/criteria/content_filter_criteria.h" #include "../common/skill_caps.h" +/* +TODO bot rewrite: +--have help option say first usable class level by spell list. Check existing options and update logic +--remove all hardcoded spell grabbing when above is done +--add slotid option to invgive +--command cleanup (move to new help window, make more descriptive) +--Add quest methods for functions +*/ + // This constructor is used during the bot create command Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) { GiveNPCTypeData(npcTypeData); From 85dbdb7f2caea267b9dd3902c09c447d8db94625 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 30 Dec 2024 13:16:38 -0600 Subject: [PATCH 254/394] Add missing bot_blocked_buffs to schema --- common/database_schema.h | 1 + 1 file changed, 1 insertion(+) diff --git a/common/database_schema.h b/common/database_schema.h index 6bf75b6d1e..fa13534c1f 100644 --- a/common/database_schema.h +++ b/common/database_schema.h @@ -402,6 +402,7 @@ namespace DatabaseSchema { static std::vector GetBotTables() { return { + "bot_blocked_buffs", "bot_buffs", "bot_command_settings", "bot_create_combinations", From 61a3987d25582d8473fa0c233d43f48c548f165b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:54:00 -0600 Subject: [PATCH 255/394] Remove plural on ^spelltypeidsand ^spelltypenames --- zone/bot_command.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 25149e2eb9..e30503901c 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -2143,7 +2143,6 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { std::string fillerLine = "-----------"; std::string spellTypeField = "Spell Type"; - std::string pluralS = "s"; std::string idField = "ID"; std::string shortnameField = "Short Name"; @@ -2185,9 +2184,8 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { popup_text += DialogueWindow::TableRow( DialogueWindow::TableCell( fmt::format( - "{}{}", - DialogueWindow::ColorMessage(forest_green, c->GetSpellTypeNameByID(i)), - DialogueWindow::ColorMessage(forest_green, pluralS) + "{}", + DialogueWindow::ColorMessage(forest_green, c->GetSpellTypeNameByID(i)) ) ) + DialogueWindow::TableCell( From e994d14418ec696b5e86a2de98b2fd4f3fbbc0ae Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 30 Dec 2024 22:58:37 -0600 Subject: [PATCH 256/394] Move spelltype names, spell subtypes, category names and setting names to maps. --- common/spdat.h | 150 +++++++++++++ zone/bot.h | 54 ++++- zone/mob.cpp | 575 ++----------------------------------------------- zone/mob.h | 6 +- 4 files changed, 228 insertions(+), 557 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 4681a85841..985aab5188 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -742,6 +742,156 @@ namespace BotSpellTypes constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed } +static std::map spellType_names = { + { BotSpellTypes::Nuke, "Nuke" }, + { BotSpellTypes::RegularHeal, "Regular Heal" }, + { BotSpellTypes::Root, "Root" }, + { BotSpellTypes::Buff, "Buff" }, + { BotSpellTypes::Escape, "Escape" }, + { BotSpellTypes::Pet, "Pet" }, + { BotSpellTypes::Lifetap, "Lifetap" }, + { BotSpellTypes::Snare, "Snare" }, + { BotSpellTypes::DOT, "DoT" }, + { BotSpellTypes::Dispel, "Dispel" }, + { BotSpellTypes::InCombatBuff, "In-Combat Buff" }, + { BotSpellTypes::Mez, "Mez" }, + { BotSpellTypes::Charm, "Charm" }, + { BotSpellTypes::Slow, "Slow" }, + { BotSpellTypes::Debuff, "Debuff" }, + { BotSpellTypes::Cure, "Cure" }, + { BotSpellTypes::GroupCures, "Group Cure" }, + { BotSpellTypes::PetCures, "Pet Cure" }, + { BotSpellTypes::Resurrect, "Resurrect" }, + { BotSpellTypes::HateRedux, "Hate Reduction" }, + { BotSpellTypes::InCombatBuffSong, "In-Combat Buff Song" }, + { BotSpellTypes::OutOfCombatBuffSong, "Out-of-Combat Buff Song" }, + { BotSpellTypes::PreCombatBuff, "Pre-Combat Buff" }, + { BotSpellTypes::PreCombatBuffSong, "Pre-Combat Buff Song" }, + { BotSpellTypes::Fear, "Fear" }, + { BotSpellTypes::Stun, "Stun" }, + { BotSpellTypes::CompleteHeal, "Complete Heal" }, + { BotSpellTypes::FastHeals, "Fast Heal" }, + { BotSpellTypes::VeryFastHeals, "Very Fast Heal" }, + { BotSpellTypes::GroupHeals, "Group Heal" }, + { BotSpellTypes::GroupCompleteHeals, "Group Complete Heal" }, + { BotSpellTypes::GroupHoTHeals, "Group HoT Heal" }, + { BotSpellTypes::HoTHeals, "HoT Heal" }, + { BotSpellTypes::AENukes, "AE Nuke" }, + { BotSpellTypes::AERains, "AE Rain" }, + { BotSpellTypes::AEMez, "AE Mez" }, + { BotSpellTypes::AEStun, "AE Stun" }, + { BotSpellTypes::AEDebuff, "AE Debuff" }, + { BotSpellTypes::AESlow, "AE Slow" }, + { BotSpellTypes::AESnare, "AE Snare" }, + { BotSpellTypes::AEFear, "AE Fear" }, + { BotSpellTypes::AEDispel, "AE Dispel" }, + { BotSpellTypes::AERoot, "AE Root" }, + { BotSpellTypes::AEDoT, "AE DoT" }, + { BotSpellTypes::AELifetap, "AE Lifetap" }, + { BotSpellTypes::PBAENuke, "PBAE Nuke" }, + { BotSpellTypes::PetBuffs, "Pet Buff" }, + { BotSpellTypes::PetRegularHeals, "Pet Regular Heal" }, + { BotSpellTypes::PetCompleteHeals, "Pet Complete Heal" }, + { BotSpellTypes::PetFastHeals, "Pet Fast Heal" }, + { BotSpellTypes::PetVeryFastHeals, "Pet Very Fast Heal" }, + { BotSpellTypes::PetHoTHeals, "Pet HoT Heal" }, + { BotSpellTypes::DamageShields, "Damage Shield" }, + { BotSpellTypes::ResistBuffs, "Resist Buff" }, + { BotSpellTypes::PetDamageShields, "Pet Damage Shield" }, + { BotSpellTypes::PetResistBuffs, "Pet Resist Buff" }, + { BotSpellTypes::HateLine, "Hate Line" }, + { BotSpellTypes::AEHateLine, "AE Hate Line" }, + { BotSpellTypes::Lull, "Lull" }, + { BotSpellTypes::Teleport, "Teleport" }, + { BotSpellTypes::Succor, "Succor" }, + { BotSpellTypes::BindAffinity, "Bind Affinity" }, + { BotSpellTypes::Identify, "Identify" }, + { BotSpellTypes::Levitate, "Levitate" }, + { BotSpellTypes::Rune, "Rune" }, + { BotSpellTypes::WaterBreathing, "Water Breathing" }, + { BotSpellTypes::Size, "Size" }, + { BotSpellTypes::Invisibility, "Invisibility" }, + { BotSpellTypes::MovementSpeed, "Movement Speed" }, + { BotSpellTypes::SendHome, "Send Home" }, + { BotSpellTypes::SummonCorpse, "Summon Corpse" }, + { BotSpellTypes::AELull, "AE Lull" } +}; + +static std::map spellType_shortNames = { + { BotSpellTypes::Nuke, "nukes" }, + { BotSpellTypes::RegularHeal, "regularheals" }, + { BotSpellTypes::Root, "roots" }, + { BotSpellTypes::Buff, "buffs" }, + { BotSpellTypes::Escape, "escapes" }, + { BotSpellTypes::Pet, "pets" }, + { BotSpellTypes::Lifetap, "lifetaps" }, + { BotSpellTypes::Snare, "snares" }, + { BotSpellTypes::DOT, "dots" }, + { BotSpellTypes::Dispel, "dispels" }, + { BotSpellTypes::InCombatBuff, "incombatbuffs" }, + { BotSpellTypes::Mez, "mez" }, + { BotSpellTypes::Charm, "charms" }, + { BotSpellTypes::Slow, "slows" }, + { BotSpellTypes::Debuff, "debuffs" }, + { BotSpellTypes::Cure, "cures" }, + { BotSpellTypes::GroupCures, "groupcures" }, + { BotSpellTypes::PetCures, "petcure" }, + { BotSpellTypes::Resurrect, "resurrect" }, + { BotSpellTypes::HateRedux, "hateredux" }, + { BotSpellTypes::InCombatBuffSong, "incombatbuffsongs" }, + { BotSpellTypes::OutOfCombatBuffSong, "outofcombatbuffsongs" }, + { BotSpellTypes::PreCombatBuff, "precombatbuffs" }, + { BotSpellTypes::PreCombatBuffSong, "precombatbuffsongs" }, + { BotSpellTypes::Fear, "fears" }, + { BotSpellTypes::Stun, "stuns" }, + { BotSpellTypes::CompleteHeal, "completeheals" }, + { BotSpellTypes::FastHeals, "fastheals" }, + { BotSpellTypes::VeryFastHeals, "veryfastheals" }, + { BotSpellTypes::GroupHeals, "groupheals" }, + { BotSpellTypes::GroupCompleteHeals, "groupcompleteheals" }, + { BotSpellTypes::GroupHoTHeals, "grouphotheals" }, + { BotSpellTypes::HoTHeals, "hotheals" }, + { BotSpellTypes::AENukes, "aenukes" }, + { BotSpellTypes::AERains, "aerains" }, + { BotSpellTypes::AEMez, "aemez" }, + { BotSpellTypes::AEStun, "aestuns" }, + { BotSpellTypes::AEDebuff, "aedebuffs" }, + { BotSpellTypes::AESlow, "aeslows" }, + { BotSpellTypes::AESnare, "aesnares" }, + { BotSpellTypes::AEFear, "aefears" }, + { BotSpellTypes::AEDispel, "aedispels" }, + { BotSpellTypes::AERoot, "aeroots" }, + { BotSpellTypes::AEDoT, "aedots" }, + { BotSpellTypes::AELifetap, "aelifetaps" }, + { BotSpellTypes::PBAENuke, "pbaenukes" }, + { BotSpellTypes::PetBuffs, "petbuffs" }, + { BotSpellTypes::PetRegularHeals, "petregularheals" }, + { BotSpellTypes::PetCompleteHeals, "petcompleteheals" }, + { BotSpellTypes::PetFastHeals, "petfastheals" }, + { BotSpellTypes::PetVeryFastHeals, "petveryfastheals" }, + { BotSpellTypes::PetHoTHeals, "pethotheals" }, + { BotSpellTypes::DamageShields, "damageshields" }, + { BotSpellTypes::ResistBuffs, "resistbuffs" }, + { BotSpellTypes::PetDamageShields, "petdamageshields" }, + { BotSpellTypes::PetResistBuffs, "petresistbuffs" }, + { BotSpellTypes::HateLine, "hateline" }, + { BotSpellTypes::AEHateLine, "aehateline" }, + { BotSpellTypes::Lull, "lull" }, + { BotSpellTypes::Teleport, "teleport" }, + { BotSpellTypes::Succor, "succor" }, + { BotSpellTypes::BindAffinity, "bindaffinity" }, + { BotSpellTypes::Identify, "identify" }, + { BotSpellTypes::Levitate, "levitate" }, + { BotSpellTypes::Rune, "rune" }, + { BotSpellTypes::WaterBreathing, "waterbreathing" }, + { BotSpellTypes::Size, "size" }, + { BotSpellTypes::Invisibility, "invisibility" }, + { BotSpellTypes::MovementSpeed, "movementspeed" }, + { BotSpellTypes::SendHome, "sendhome" }, + { BotSpellTypes::SummonCorpse, "summoncorpse" }, + { BotSpellTypes::AELull, "aelull" } +}; + const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); diff --git a/zone/bot.h b/zone/bot.h index 3f4c212376..c7852b5117 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -114,7 +114,26 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; constexpr uint16 START_CLIENT = BotSettingCategories::SpellHold; constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; - constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; // Increment as needed + constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; + constexpr uint16 END_FULL = BotSettingCategories::SpellTypeRecastDelay; +}; + +static std::map botSpellCategory_names = { + { BotSettingCategories::BaseSetting, "BaseSetting" }, + { BotSettingCategories::SpellHold, "SpellHolds" }, + { BotSettingCategories::SpellDelay, "SpellDelays" }, + { BotSettingCategories::SpellMinThreshold, "SpellMinThresholds" }, + { BotSettingCategories::SpellMaxThreshold, "SpellMaxThresholds" }, + { BotSettingCategories::SpellTypeAggroCheck, "SpellAggroChecks" }, + { BotSettingCategories::SpellTypeMinManaPct, "SpellMinManaPct" }, + { BotSettingCategories::SpellTypeMaxManaPct, "SpellMaxManaPct" }, + { BotSettingCategories::SpellTypeMinHPPct, "SpellMinHPPct" }, + { BotSettingCategories::SpellTypeMaxHPPct, "SpellMaxHPPct" }, + { BotSettingCategories::SpellTypeIdlePriority, "SpellIdlePriority" }, + { BotSettingCategories::SpellTypeEngagedPriority, "SpellEngagedPriority" }, + { BotSettingCategories::SpellTypePursuePriority, "SpellPursuePriority" }, + { BotSettingCategories::SpellTypeAEOrGroupTargetCount, "SpellTargetCounts" }, + { BotSettingCategories::SpellTypeRecastDelay, "SpellRecastDelay" } }; namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed @@ -147,6 +166,23 @@ namespace BotBaseSettings { constexpr uint16 END = BotBaseSettings::SitManaPct; // Increment as needed }; +static std::map botBaseSettings_names = { + { BotBaseSettings::ExpansionBitmask, "ExpansionBitmask" }, + { BotBaseSettings::ShowHelm, "ShowHelm" }, + { BotBaseSettings::FollowDistance, "FollowDistance" }, + { BotBaseSettings::StopMeleeLevel, "StopMeleeLevel" }, + { BotBaseSettings::EnforceSpellSettings, "EnforceSpellSettings" }, + { BotBaseSettings::RangedSetting, "RangedSetting" }, + { BotBaseSettings::PetSetTypeSetting, "PetSetTypeSetting" }, + { BotBaseSettings::BehindMob, "BehindMob" }, + { BotBaseSettings::DistanceRanged, "DistanceRanged" }, + { BotBaseSettings::IllusionBlock, "IllusionBlock" }, + { BotBaseSettings::MaxMeleeRange, "MaxMeleeRange" }, + { BotBaseSettings::MedInCombat, "MedInCombat" }, + { BotBaseSettings::SitHPPct, "SitHPPct" }, + { BotBaseSettings::SitManaPct, "SitManaPct" } +}; + namespace CommandedSubTypes { constexpr uint16 SingleTarget = 1; constexpr uint16 GroupTarget = 2; @@ -158,8 +194,24 @@ namespace CommandedSubTypes { constexpr uint16 Shrink = 8; constexpr uint16 Grow = 9; constexpr uint16 Selo = 10; + + constexpr uint16 START = CommandedSubTypes::SingleTarget; + constexpr uint16 END = CommandedSubTypes::Selo; }; + +static std::map botSubType_names = { + { CommandedSubTypes::SingleTarget, "SingleTarget" }, + { CommandedSubTypes::GroupTarget, "GroupTarget" }, + { CommandedSubTypes::AETarget, "AETarget" }, + { CommandedSubTypes::SeeInvis, "SeeInvis" }, + { CommandedSubTypes::Invis, "Invis" }, + { CommandedSubTypes::InvisUndead, "InvisUndead" }, + { CommandedSubTypes::InvisAnimals, "InvisAnimals" }, + { CommandedSubTypes::Shrink, "Shrink" }, + { CommandedSubTypes::Grow, "Grow" }, + { CommandedSubTypes::Selo, "Selo" } +}; class Bot : public NPC { friend class Mob; public: diff --git a/zone/mob.cpp b/zone/mob.cpp index c1eee931a7..b3075d392d 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8764,43 +8764,12 @@ uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { return UINT16_MAX; } -std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { - switch (setting_type) { - case BotSettingCategories::BaseSetting: - return "BaseSetting"; - case BotSettingCategories::SpellHold: - return "SpellHolds"; - case BotSettingCategories::SpellDelay: - return "SpellDelays"; - case BotSettingCategories::SpellMinThreshold: - return "SpellMinThresholds"; - case BotSettingCategories::SpellMaxThreshold: - return "SpellMaxThresholds"; - case BotSettingCategories::SpellTypeAggroCheck: - return "SpellAggroChecks"; - case BotSettingCategories::SpellTypeMinManaPct: - return "SpellMinManaPct"; - case BotSettingCategories::SpellTypeMaxManaPct: - return "SpellMaxManaPct"; - case BotSettingCategories::SpellTypeMinHPPct: - return "SpellMinHPPct"; - case BotSettingCategories::SpellTypeMaxHPPct: - return "SpellMaxHPPct"; - case BotSettingCategories::SpellTypeIdlePriority: - return "SpellIdlePriority"; - case BotSettingCategories::SpellTypeEngagedPriority: - return "SpellEngagedPriority"; - case BotSettingCategories::SpellTypePursuePriority: - return "SpellPursuePriority"; - case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - return "SpellTargetCounts"; - case BotSettingCategories::SpellTypeRecastDelay: - return "SpellRecastDelay"; - default: - return "Null"; - } +bool Mob::IsValidBotSpellCategory(uint8 setting_type) { + return (setting_type >= BotSettingCategories::START && setting_type <= BotSettingCategories::END_FULL); +} - return "Null"; +std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { + return IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; } uint16 Mob::GetBotSpellCategoryIDByShortName(std::string settingString) { @@ -8813,41 +8782,12 @@ uint16 Mob::GetBotSpellCategoryIDByShortName(std::string settingString) { return UINT16_MAX; } -std::string Mob::GetBotSettingCategoryName(uint8 setting_type) { - switch (setting_type) { - case BotBaseSettings::ExpansionBitmask: - return "ExpansionBitmask"; - case BotBaseSettings::ShowHelm: - return "ShowHelm"; - case BotBaseSettings::FollowDistance: - return "FollowDistance"; - case BotBaseSettings::StopMeleeLevel: - return "StopMeleeLevel"; - case BotBaseSettings::EnforceSpellSettings: - return "EnforceSpellSettings"; - case BotBaseSettings::RangedSetting: - return "RangedSetting"; - case BotBaseSettings::PetSetTypeSetting: - return "PetSetTypeSetting"; - case BotBaseSettings::BehindMob: - return "BehindMob"; - case BotBaseSettings::DistanceRanged: - return "DistanceRanged"; - case BotBaseSettings::IllusionBlock: - return "IllusionBlock"; - case BotBaseSettings::MaxMeleeRange: - return "MaxMeleeRange"; - case BotBaseSettings::MedInCombat: - return "MedInCombat"; - case BotBaseSettings::SitHPPct: - return "SitHPPct"; - case BotBaseSettings::SitManaPct: - return "SitManaPct"; - default: - return "Null"; - } +bool Mob::IsValidBotBaseSetting(uint16 setting_type) { + return (setting_type >= BotBaseSettings::START_ALL && setting_type <= BotBaseSettings::END); +} - return "Null"; +std::string Mob::GetBotSettingCategoryName(uint16 setting_type) { + return IsValidBotBaseSetting(setting_type) ? botBaseSettings_names[setting_type] : "UNKNOWN SETTING"; } uint16 Mob::GetBaseSettingIDByShortName(std::string settingString) { @@ -8860,499 +8800,24 @@ uint16 Mob::GetBaseSettingIDByShortName(std::string settingString) { return UINT16_MAX; } -std::string Mob::GetSpellTypeNameByID(uint16 spellType) { - std::string spellTypeName = "null"; - - switch (spellType) { - case BotSpellTypes::Nuke: - spellTypeName = "Nuke"; - break; - case BotSpellTypes::RegularHeal: - spellTypeName = "Regular Heal"; - break; - case BotSpellTypes::Root: - spellTypeName = "Root"; - break; - case BotSpellTypes::Buff: - spellTypeName = "Buff"; - break; - case BotSpellTypes::Escape: - spellTypeName = "Escape"; - break; - case BotSpellTypes::Pet: - spellTypeName = "Pet"; - break; - case BotSpellTypes::Lifetap: - spellTypeName = "Lifetap"; - break; - case BotSpellTypes::Snare: - spellTypeName = "Snare"; - break; - case BotSpellTypes::DOT: - spellTypeName = "DoT"; - break; - case BotSpellTypes::Dispel: - spellTypeName = "Dispel"; - break; - case BotSpellTypes::InCombatBuff: - spellTypeName = "In-Combat Buff"; - break; - case BotSpellTypes::Mez: - spellTypeName = "Mez"; - break; - case BotSpellTypes::Charm: - spellTypeName = "Charm"; - break; - case BotSpellTypes::Slow: - spellTypeName = "Slow"; - break; - case BotSpellTypes::Debuff: - spellTypeName = "Debuff"; - break; - case BotSpellTypes::Cure: - spellTypeName = "Cure"; - break; - case BotSpellTypes::GroupCures: - spellTypeName = "Group Cure"; - break; - case BotSpellTypes::PetCures: - spellTypeName = "Pet Cure"; - break; - case BotSpellTypes::Resurrect: - spellTypeName = "Resurrect"; - break; - case BotSpellTypes::HateRedux: - spellTypeName = "Hate Reduction"; - break; - case BotSpellTypes::InCombatBuffSong: - spellTypeName = "In-Combat Buff Song"; - break; - case BotSpellTypes::OutOfCombatBuffSong: - spellTypeName = "Out-of-Combat Buff Song"; - break; - case BotSpellTypes::PreCombatBuff: - spellTypeName = "Pre-Combat Buff"; - break; - case BotSpellTypes::PreCombatBuffSong: - spellTypeName = "Pre-Combat Buff Song"; - break; - case BotSpellTypes::Fear: - spellTypeName = "Fear"; - break; - case BotSpellTypes::Stun: - spellTypeName = "Stun"; - break; - case BotSpellTypes::CompleteHeal: - spellTypeName = "Complete Heal"; - break; - case BotSpellTypes::FastHeals: - spellTypeName = "Fast Heal"; - break; - case BotSpellTypes::VeryFastHeals: - spellTypeName = "Very Fast Heal"; - break; - case BotSpellTypes::GroupHeals: - spellTypeName = "Group Heal"; - break; - case BotSpellTypes::GroupCompleteHeals: - spellTypeName = "Group Complete Heal"; - break; - case BotSpellTypes::GroupHoTHeals: - spellTypeName = "Group HoT Heal"; - break; - case BotSpellTypes::HoTHeals: - spellTypeName = "HoT Heal"; - break; - case BotSpellTypes::AENukes: - spellTypeName = "AE Nuke"; - break; - case BotSpellTypes::AERains: - spellTypeName = "AE Rain"; - break; - case BotSpellTypes::AEMez: - spellTypeName = "AE Mez"; - break; - case BotSpellTypes::AEStun: - spellTypeName = "AE Stun"; - break; - case BotSpellTypes::AEDebuff: - spellTypeName = "AE Debuff"; - break; - case BotSpellTypes::AESlow: - spellTypeName = "AE Slow"; - break; - case BotSpellTypes::AESnare: - spellTypeName = "AE Snare"; - break; - case BotSpellTypes::AEFear: - spellTypeName = "AE Fear"; - break; - case BotSpellTypes::AEDispel: - spellTypeName = "AE Dispel"; - break; - case BotSpellTypes::AERoot: - spellTypeName = "AE Root"; - break; - case BotSpellTypes::AEDoT: - spellTypeName = "AE DoT"; - break; - case BotSpellTypes::AELifetap: - spellTypeName = "AE Lifetap"; - break; - case BotSpellTypes::PBAENuke: - spellTypeName = "PBAE Nuke"; - break; - case BotSpellTypes::PetBuffs: - spellTypeName = "Pet Buff"; - break; - case BotSpellTypes::PetRegularHeals: - spellTypeName = "Pet Regular Heal"; - break; - case BotSpellTypes::PetCompleteHeals: - spellTypeName = "Pet Complete Heal"; - break; - case BotSpellTypes::PetFastHeals: - spellTypeName = "Pet Fast Heal"; - break; - case BotSpellTypes::PetVeryFastHeals: - spellTypeName = "Pet Very Fast Heal"; - break; - case BotSpellTypes::PetHoTHeals: - spellTypeName = "Pet HoT Heal"; - break; - case BotSpellTypes::DamageShields: - spellTypeName = "Damage Shield"; - break; - case BotSpellTypes::ResistBuffs: - spellTypeName = "Resist Buff"; - break; - case BotSpellTypes::PetDamageShields: - spellTypeName = "Pet Damage Shield"; - break; - case BotSpellTypes::PetResistBuffs: - spellTypeName = "Pet Resist Buff"; - break; - case BotSpellTypes::HateLine: - spellTypeName = "Hate Line"; - break; - case BotSpellTypes::AEHateLine: - spellTypeName = "AE Hate Line"; - break; - case BotSpellTypes::Lull: - spellTypeName = "Lull"; - break; - case BotSpellTypes::Teleport: - spellTypeName = "Teleport"; - break; - case BotSpellTypes::Succor: - spellTypeName = "Succor"; - break; - case BotSpellTypes::BindAffinity: - spellTypeName = "Bind Affinity"; - break; - case BotSpellTypes::Identify: - spellTypeName = "Identify"; - break; - case BotSpellTypes::Levitate: - spellTypeName = "Levitate"; - break; - case BotSpellTypes::Rune: - spellTypeName = "Rune"; - break; - case BotSpellTypes::WaterBreathing: - spellTypeName = "Water Breathing"; - break; - case BotSpellTypes::Size: - spellTypeName = "Size"; - break; - case BotSpellTypes::Invisibility: - spellTypeName = "Invisibility"; - break; - case BotSpellTypes::MovementSpeed: - spellTypeName = "Movement Speed"; - break; - case BotSpellTypes::SendHome: - spellTypeName = "Send Home"; - break; - case BotSpellTypes::SummonCorpse: - spellTypeName = "Summon Corpse"; - break; - case BotSpellTypes::AELull: - spellTypeName = "AE Lull"; - break; - default: - break; - } - - return spellTypeName; +bool Mob::IsValidSpellType(uint16 spellType) { + return (spellType >= BotSpellTypes::START && spellType <= BotSpellTypes::END) || (spellType >= BotSpellTypes::COMMANDED_START && spellType <= BotSpellTypes::COMMANDED_END); } std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { - std::string spellTypeName = "null"; + return IsValidSpellType(spellType) ? spellType_shortNames[spellType] : "UNKNOWN SPELLTYPE"; +} - switch (spellType) { - case BotSpellTypes::Nuke: - spellTypeName = "nukes"; - break; - case BotSpellTypes::RegularHeal: - spellTypeName = "regularheals"; - break; - case BotSpellTypes::Root: - spellTypeName = "roots"; - break; - case BotSpellTypes::Buff: - spellTypeName = "buffs"; - break; - case BotSpellTypes::Escape: - spellTypeName = "escapes"; - break; - case BotSpellTypes::Pet: - spellTypeName = "pets"; - break; - case BotSpellTypes::Lifetap: - spellTypeName = "lifetaps"; - break; - case BotSpellTypes::Snare: - spellTypeName = "snares"; - break; - case BotSpellTypes::DOT: - spellTypeName = "dots"; - break; - case BotSpellTypes::Dispel: - spellTypeName = "dispels"; - break; - case BotSpellTypes::InCombatBuff: - spellTypeName = "incombatbuffs"; - break; - case BotSpellTypes::Mez: - spellTypeName = "mez"; - break; - case BotSpellTypes::Charm: - spellTypeName = "charms"; - break; - case BotSpellTypes::Slow: - spellTypeName = "slows"; - break; - case BotSpellTypes::Debuff: - spellTypeName = "debuffs"; - break; - case BotSpellTypes::Cure: - spellTypeName = "cures"; - break; - case BotSpellTypes::GroupCures: - spellTypeName = "groupcures"; - break; - case BotSpellTypes::PetCures: - spellTypeName = "petcure"; - break; - case BotSpellTypes::Resurrect: - spellTypeName = "resurrect"; - break; - case BotSpellTypes::HateRedux: - spellTypeName = "hateredux"; - break; - case BotSpellTypes::InCombatBuffSong: - spellTypeName = "incombatbuffsongs"; - break; - case BotSpellTypes::OutOfCombatBuffSong: - spellTypeName = "outofcombatbuffsongs"; - break; - case BotSpellTypes::PreCombatBuff: - spellTypeName = "precombatbuffs"; - break; - case BotSpellTypes::PreCombatBuffSong: - spellTypeName = "precombatbuffsongs"; - break; - case BotSpellTypes::Fear: - spellTypeName = "fears"; - break; - case BotSpellTypes::Stun: - spellTypeName = "stuns"; - break; - case BotSpellTypes::CompleteHeal: - spellTypeName = "completeheals"; - break; - case BotSpellTypes::FastHeals: - spellTypeName = "fastheals"; - break; - case BotSpellTypes::VeryFastHeals: - spellTypeName = "veryfastheals"; - break; - case BotSpellTypes::GroupHeals: - spellTypeName = "groupheals"; - break; - case BotSpellTypes::GroupCompleteHeals: - spellTypeName = "groupcompleteheals"; - break; - case BotSpellTypes::GroupHoTHeals: - spellTypeName = "grouphotheals"; - break; - case BotSpellTypes::HoTHeals: - spellTypeName = "hotheals"; - break; - case BotSpellTypes::AENukes: - spellTypeName = "aenukes"; - break; - case BotSpellTypes::AERains: - spellTypeName = "aerains"; - break; - case BotSpellTypes::AEMez: - spellTypeName = "aemez"; - break; - case BotSpellTypes::AEStun: - spellTypeName = "aestuns"; - break; - case BotSpellTypes::AEDebuff: - spellTypeName = "aedebuffs"; - break; - case BotSpellTypes::AESlow: - spellTypeName = "aeslows"; - break; - case BotSpellTypes::AESnare: - spellTypeName = "aesnares"; - break; - case BotSpellTypes::AEFear: - spellTypeName = "aefears"; - break; - case BotSpellTypes::AEDispel: - spellTypeName = "aedispels"; - break; - case BotSpellTypes::AERoot: - spellTypeName = "aeroots"; - break; - case BotSpellTypes::AEDoT: - spellTypeName = "aedots"; - break; - case BotSpellTypes::AELifetap: - spellTypeName = "aelifetaps"; - break; - case BotSpellTypes::PBAENuke: - spellTypeName = "pbaenukes"; - break; - case BotSpellTypes::PetBuffs: - spellTypeName = "petbuffs"; - break; - case BotSpellTypes::PetRegularHeals: - spellTypeName = "petregularheals"; - break; - case BotSpellTypes::PetCompleteHeals: - spellTypeName = "petcompleteheals"; - break; - case BotSpellTypes::PetFastHeals: - spellTypeName = "petfastheals"; - break; - case BotSpellTypes::PetVeryFastHeals: - spellTypeName = "petveryfastheals"; - break; - case BotSpellTypes::PetHoTHeals: - spellTypeName = "pethotheals"; - break; - case BotSpellTypes::DamageShields: - spellTypeName = "damageshields"; - break; - case BotSpellTypes::ResistBuffs: - spellTypeName = "resistbuffs"; - break; - case BotSpellTypes::PetDamageShields: - spellTypeName = "petdamageshields"; - break; - case BotSpellTypes::PetResistBuffs: - spellTypeName = "petresistbuffs"; - break; - case BotSpellTypes::HateLine: - spellTypeName = "hateline"; - break; - case BotSpellTypes::AEHateLine: - spellTypeName = "aehateline"; - break; - case BotSpellTypes::Lull: - spellTypeName = "lull"; - break; - case BotSpellTypes::Teleport: - spellTypeName = "teleport"; - break; - case BotSpellTypes::Succor: - spellTypeName = "succor"; - break; - case BotSpellTypes::BindAffinity: - spellTypeName = "bindaffinity"; - break; - case BotSpellTypes::Identify: - spellTypeName = "identify"; - break; - case BotSpellTypes::Levitate: - spellTypeName = "levitate"; - break; - case BotSpellTypes::Rune: - spellTypeName = "rune"; - break; - case BotSpellTypes::WaterBreathing: - spellTypeName = "waterbreathing"; - break; - case BotSpellTypes::Size: - spellTypeName = "size"; - break; - case BotSpellTypes::Invisibility: - spellTypeName = "invisibility"; - break; - case BotSpellTypes::MovementSpeed: - spellTypeName = "movementspeed"; - break; - case BotSpellTypes::SendHome: - spellTypeName = "sendhome"; - break; - case BotSpellTypes::SummonCorpse: - spellTypeName = "summoncorpse"; - break; - case BotSpellTypes::AELull: - spellTypeName = "aelull"; - break; - default: - break; - } +std::string Mob::GetSpellTypeNameByID(uint16 spellType) { + return IsValidSpellType(spellType) ? spellType_names[spellType] : "UNKNOWN SPELLTYPE"; +} - return spellTypeName; +bool Mob::IsValidSubType(uint16 subType) { + return (subType >= CommandedSubTypes::START && subType <= CommandedSubTypes::END); } std::string Mob::GetSubTypeNameByID(uint16 subType) { - std::string subTypeName = "null"; - - switch (subType) { - case CommandedSubTypes::SingleTarget: - subTypeName = "SingleTarget"; - break; - case CommandedSubTypes::GroupTarget: - subTypeName = "GroupTarget"; - break; - case CommandedSubTypes::AETarget: - subTypeName = "AETarget"; - break; - case CommandedSubTypes::SeeInvis: - subTypeName = "SeeInvis"; - break; - case CommandedSubTypes::Invis: - subTypeName = "Invis"; - break; - case CommandedSubTypes::InvisUndead: - subTypeName = "InvisUndead"; - break; - case CommandedSubTypes::InvisAnimals: - subTypeName = "InvisAnimals"; - break; - case CommandedSubTypes::Shrink: - subTypeName = "Shrink"; - break; - case CommandedSubTypes::Grow: - subTypeName = "Grow"; - break; - case CommandedSubTypes::Selo: - subTypeName = "Selo"; - break; - default: - break; - } - - return subTypeName; + return IsValidSpellType(subType) ? botSubType_names[subType] : "UNKNOWN SUBTYPE"; } bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { diff --git a/zone/mob.h b/zone/mob.h index 3d2ab96b42..3bd97c7cea 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -436,12 +436,16 @@ class Mob : public Entity { uint16 GetSpellTypeIDByShortName(std::string spellTypeString); + bool IsValidBotSpellCategory(uint8 setting_type); std::string GetBotSpellCategoryName(uint8 setting_type); uint16 GetBotSpellCategoryIDByShortName(std::string settingString); - std::string GetBotSettingCategoryName(uint8 setting_type); + bool IsValidBotBaseSetting(uint16 setting_type); + std::string GetBotSettingCategoryName(uint16 setting_type); uint16 GetBaseSettingIDByShortName(std::string settingString); + bool IsValidSpellType(uint16 spellType); std::string GetSpellTypeNameByID(uint16 spellType); std::string GetSpellTypeShortNameByID(uint16 spellType); + bool IsValidSubType(uint16 subType); std::string GetSubTypeNameByID(uint16 subType); bool GetDefaultSpellHold(uint16 spellType, uint8 stance = Stance::Balanced); From 67d7413748e2962170ed3b36413ef4db1f7a8215 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:45:04 -0600 Subject: [PATCH 257/394] move los checks to mob.cpp --- zone/aggro.cpp | 86 -------------------------------------------------- zone/mob.cpp | 58 ++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 86 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 74d2364f58..6fb2f45ce6 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1708,89 +1708,3 @@ void Mob::RogueEvade(Mob *other) return; } - -bool Mob::DoLosChecks(Mob* who, Mob* other) { - if (!who->CheckLosFN(other) || !who->CheckWaterLoS(other)) { - if (who->CheckLosCheatExempt(who, other)) { - return true; - } - - if (CheckLosCheat(who, other)) { - return true; - } - - return false; - } - - return true; -} - -bool Mob::CheckLosCheat(Mob* who, Mob* other) { - if (RuleB(Map, CheckForLoSCheat)) { - auto& door_list = entity_list.GetDoorsList(); - for (auto itr : door_list) { - Doors* door = itr.second; - if (door && !door->IsDoorOpen() && (door->GetTriggerType() == 255 || door->GetLockpick() != 0 || door->GetKeyItem() != 0 || door->GetNoKeyring() != 0)) { - if (DistanceNoZ(who->GetPosition(), door->GetPosition()) <= 50) { - auto who_to_door = DistanceNoZ(who->GetPosition(), door->GetPosition()); - auto other_to_door = DistanceNoZ(other->GetPosition(), door->GetPosition()); - auto who_to_other = DistanceNoZ(who->GetPosition(), other->GetPosition()); - auto distance_difference = who_to_other - (who_to_door + other_to_door); - if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) { - return false; - } - } - } - } - } - - //if (RuleB(Map, CheckForLoSCheat)) { - // uint8 zone_id = zone->GetZoneID(); - // // ZoneID, target XYZ, my range from target - // //float zone_basic_checks[] = { 6, 36 }; - // //float zone_basic_x_coord[] = { -295, -179.908 }; - // //float zone_basic_y_coord[] = { -18, -630.708 }; - // //float zone_basic_y_coord[] = { 50.97, -69.971 }; - // //float zone_basic_range_check[] = { 21, 10 }; - // //if door and target infront, fail - // //if door and target behind, fail - // - // if (zone_id == 103) { - // Doors* door_to_check = entity_list.FindDoor(8); - // TestDebug("Entered LoSCheat for ZoneID: [{}]", zone_id); - // glm::vec4 who_check; who_check.x = 1202; who_check.y = 559; who_check.z = -158.94; - // glm::vec4 other_check; other_check.x = 1291; other_check.y = 559; other_check.z = -158.19; - // float my_distance = DistanceNoZ(who->GetPosition(), who_check); - // float tar_distance = DistanceNoZ(other->GetPosition(), other_check); - // float my_range = 16; - // float tar_range = 75; - // if (my_distance <= my_range && tar_distance <= tar_range && !quest_manager.isdooropen(8)) { - // TestDebug("Door is NOT open"); - // TestDebug("LoSCheat failed"); - // return false; - // } - // TestDebug("LoS Check for ZoneID: [{}] was [{}] units for [{}], [{}] units for [{}]", zone_id, my_distance, who->GetCleanName(), tar_distance, other->GetCleanName()); - // } - //} - return true; -} - -bool Mob::CheckLosCheatExempt(Mob* who, Mob* other) { - if (RuleB(Map, EnableLoSCheatExemptions)) { - glm::vec4 exempt_check_who; - glm::vec4 exempt_check_other; - /* This is an exmaple of how to configure exemptions for LoS checks. - if (zone->GetZoneID() == 222) { //PoEarthB - exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns - //check to be sure the player and the target are in the pit to PoEarthB - //if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) - //otherwise they can pass LoS checks even if they don't have true LoS - if (who->GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(who->GetPosition(), exempt_check_who) <= 800) { - return true; - } - } - */ - } - - return false; -} diff --git a/zone/mob.cpp b/zone/mob.cpp index b3075d392d..314a3a6013 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9477,3 +9477,61 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { return false; } + +bool Mob::DoLosChecks(Mob* who, Mob* other) { + if (!who->CheckLosFN(other) || !who->CheckWaterLoS(other)) { + if (who->CheckLosCheatExempt(who, other)) { + return true; + } + + if (CheckLosCheat(who, other)) { + return true; + } + + return false; + } + + return true; +} + +bool Mob::CheckLosCheat(Mob* who, Mob* other) { + if (RuleB(Map, CheckForLoSCheat)) { + for (auto itr : entity_list.GetDoorsList()) { + Doors* d = itr.second; + if (d && !d->IsDoorOpen() && (d->GetTriggerType() == 255 || d->GetLockpick() != 0 || d->GetKeyItem() != 0 || d->GetNoKeyring() != 0)) { + if (DistanceNoZ(who->GetPosition(), d->GetPosition()) <= 50) { + auto who_to_door = DistanceNoZ(who->GetPosition(), d->GetPosition()); + auto other_to_door = DistanceNoZ(other->GetPosition(), d->GetPosition()); + auto who_to_other = DistanceNoZ(who->GetPosition(), other->GetPosition()); + auto distance_difference = who_to_other - (who_to_door + other_to_door); + + if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) { + return false; + } + } + } + } + } + + return true; +} + +bool Mob::CheckLosCheatExempt(Mob* who, Mob* other) { + if (RuleB(Map, EnableLoSCheatExemptions)) { + glm::vec4 exempt_check_who; + glm::vec4 exempt_check_other; + /* This is an exmaple of how to configure exemptions for LoS checks. + if (zone->GetZoneID() == 222) { //PoEarthB + exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns + //check to be sure the player and the target are in the pit to PoEarthB + //if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) + //otherwise they can pass LoS checks even if they don't have true LoS + if (who->GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(who->GetPosition(), exempt_check_who) <= 800) { + return true; + } + } + */ + } + + return false; +} From 0c62210f90304aa7b57057b3869fcb02fada38b1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 7 Jan 2025 23:59:02 -0600 Subject: [PATCH 258/394] Bot CampAll fix --- zone/client_process.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/client_process.cpp b/zone/client_process.cpp index aff3399dd0..83f638a2fd 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -194,7 +194,7 @@ bool Client::Process() { } if (bot_camp_timer.Check()) { - Bot::BotOrderCampAll(this); + CampAllBots(); } if (camp_timer.Check()) { From 85bd0319928a8ceb02b1eab9dfd1ca212cdd64c8 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 8 Jan 2025 00:05:36 -0600 Subject: [PATCH 259/394] Bots special_attacks.cpp fix --- zone/special_attacks.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index e7c5350be5..db4c7bf47c 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -25,6 +25,7 @@ #include "string_ids.h" #include "lua_parser.h" #include "npc.h" +#include "bot.h" #include From de053ce8f3ad1a8a48ea8260bc471d430110c7fb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 8 Jan 2025 00:28:33 -0600 Subject: [PATCH 260/394] Add zero check for bot spawn limits If the spawn limit rule is set to 0 and spawn limit is set by bucket, if no class buckets are set, it defaults to the rule of 0 and renders the player unable to spawn bots. This adds a check where if the rule and class bucket are 0, it will check for the spawn limit bucket --- zone/bot_commands/bot.cpp | 12 +++++++++--- zone/client_bot.cpp | 15 +++++++++++++++ 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 6678cb5211..704e62863b 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -983,6 +983,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) } auto bot_character_level = c->GetBotRequiredLevel(); + if ( bot_character_level >= 0 && c->GetLevel() < bot_character_level && @@ -1009,8 +1010,9 @@ void bot_command_spawn(Client *c, const Seperator *sep) bot_spawn_limit >= 0 && spawned_bot_count >= bot_spawn_limit && !c->GetGM() - ) { + ) { std::string message; + if (bot_spawn_limit) { message = fmt::format( "You cannot have more than {} spawned bot{}.", @@ -1034,6 +1036,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) uint32 bot_id = 0; uint8 bot_class = Class::None; + if (!database.botdb.LoadBotID(bot_name, bot_id, bot_class)) { c->Message( Chat::White, @@ -1052,7 +1055,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) bot_spawn_limit_class >= 0 && spawned_bot_count_class >= bot_spawn_limit_class && !c->GetGM() - ) { + ) { std::string message; if (bot_spawn_limit_class) { @@ -1074,11 +1077,12 @@ void bot_command_spawn(Client *c, const Seperator *sep) } auto bot_character_level_class = c->GetBotRequiredLevel(bot_class); + if ( bot_character_level_class >= 0 && c->GetLevel() < bot_character_level_class && !c->GetGM() - ) { + ) { c->Message( Chat::White, fmt::format( @@ -1113,6 +1117,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) } auto my_bot = Bot::LoadBot(bot_id); + if (!my_bot) { c->Message( Chat::White, @@ -1134,6 +1139,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) bot_id ).c_str() ); + safe_delete(my_bot); return; } diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 9d796cef10..2a4c5c0a8c 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -88,8 +88,22 @@ int Client::GetBotSpawnLimit(uint8 class_id) ); auto bucket_value = GetBucket(bucket_name); + + if (class_id && !bot_spawn_limit && bucket_value.empty()) { + const auto new_bucket_name = "bot_spawn_limit"; + + bucket_value = GetBucket(new_bucket_name); + + if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) { + bot_spawn_limit = Strings::ToInt(bucket_value); + + return bot_spawn_limit; + } + } + if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) { bot_spawn_limit = Strings::ToInt(bucket_value); + return bot_spawn_limit; } @@ -101,6 +115,7 @@ int Client::GetBotSpawnLimit(uint8 class_id) ); auto results = database.QueryDatabase(query); // use 'database' for non-bot table calls + if (!results.Success() || !results.RowCount()) { return bot_spawn_limit; } From 2fa32d18747ff1e96d79bac2b8a4dae9709daff0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 8 Jan 2025 17:15:04 -0600 Subject: [PATCH 261/394] Add HasSkill checks to bot special abilities (kick/bash/etc) --- zone/bot.cpp | 70 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 8e26b75a16..4e36611b36 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -5172,7 +5172,6 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { // Bots without this skill shouldn't be 'checking' on this timer..let's just disable it and avoid the extra IsAttackAllowed() checks // Note: this is done here instead of NPC::ctor() because taunt skill can be acquired during level ups (the timer is re-enabled in CalcBotStats()) if (!GetSkill(EQ::skills::SkillTaunt)) { - taunt_timer.Disable(); return; } @@ -5266,53 +5265,76 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { int bot_level = GetLevel(); int reuse = (TauntReuseTime * 1000); // Same as Bash and Kick bool did_attack = false; + switch (GetClass()) { case Class::Warrior: - if (bot_level >= RuleI(Combat, NPCBashKickLevel)) { - bool canBash = false; - if ((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) || (m_inv.GetItem(EQ::invslot::slotSecondary) && m_inv.GetItem(EQ::invslot::slotSecondary)->GetItem()->ItemType == EQ::item::ItemTypeShield) || (m_inv.GetItem(EQ::invslot::slotPrimary) && m_inv.GetItem(EQ::invslot::slotPrimary)->GetItem()->IsType2HWeapon() && GetAA(aa2HandBash) >= 1)) - canBash = true; + case Class::Cleric: + case Class::ShadowKnight: + case Class::Paladin: + if ( + (GetBaseRace() == OGRE || GetBaseRace() == TROLL || GetBaseRace() == BARBARIAN) || + ( + GetSkill(EQ::skills::SkillBash) && + ( + ( + m_inv.GetItem(EQ::invslot::slotSecondary) && + m_inv.GetItem(EQ::invslot::slotSecondary)->GetItem()->ItemType == EQ::item::ItemTypeShield + ) + || + ( + m_inv.GetItem(EQ::invslot::slotPrimary) && + m_inv.GetItem(EQ::invslot::slotPrimary)->GetItem()->IsType2HWeapon() && + GetAA(aa2HandBash) >= 1 + ) + ) + ) + ) { + skill_to_use = EQ::skills::SkillBash; - if (!canBash || zone->random.Int(0, 100) > 25) + break; + } + + if (GetSkill(EQ::skills::SkillKick)) { skill_to_use = EQ::skills::SkillKick; - else - skill_to_use = EQ::skills::SkillBash; - } + } + + break; case Class::Ranger: case Class::Beastlord: - skill_to_use = EQ::skills::SkillKick; + if (GetSkill(EQ::skills::SkillKick)) { + skill_to_use = EQ::skills::SkillKick; + } + break; case Class::Berserker: - skill_to_use = EQ::skills::SkillFrenzy; - break; - case Class::Cleric: - case Class::ShadowKnight: - case Class::Paladin: - if (bot_level >= RuleI(Combat, NPCBashKickLevel)) { - if ((GetRace() == OGRE || GetRace() == TROLL || GetRace() == BARBARIAN) || (m_inv.GetItem(EQ::invslot::slotSecondary) && m_inv.GetItem(EQ::invslot::slotSecondary)->GetItem()->ItemType == EQ::item::ItemTypeShield) || (m_inv.GetItem(EQ::invslot::slotPrimary) && m_inv.GetItem(EQ::invslot::slotPrimary)->GetItem()->IsType2HWeapon() && GetAA(aa2HandBash) >= 1)) - skill_to_use = EQ::skills::SkillBash; + if (GetSkill(EQ::skills::SkillFrenzy)) { + skill_to_use = EQ::skills::SkillFrenzy; } + break; case Class::Monk: - if (GetLevel() >= 30) { + if (GetSkill(EQ::skills::SkillFlyingKick)) { skill_to_use = EQ::skills::SkillFlyingKick; } - else if (GetLevel() >= 25) { + else if (GetSkill(EQ::skills::SkillDragonPunch)) { skill_to_use = EQ::skills::SkillDragonPunch; } - else if (GetLevel() >= 20) { + else if (GetSkill(EQ::skills::SkillEagleStrike)) { skill_to_use = EQ::skills::SkillEagleStrike; } - else if (GetLevel() >= 5) { + else if (GetSkill(EQ::skills::SkillRoundKick)) { skill_to_use = EQ::skills::SkillRoundKick; } - else { + else if (GetSkill(EQ::skills::SkillKick)) { skill_to_use = EQ::skills::SkillKick; } break; case Class::Rogue: - skill_to_use = EQ::skills::SkillBackstab; + if (GetSkill(EQ::skills::SkillBackstab)) { + skill_to_use = EQ::skills::SkillBackstab; + } + break; } From 40b676c3203d0066de99514380d855590acf2e87 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 9 Jan 2025 23:01:48 -0600 Subject: [PATCH 262/394] code cleanup 1 --- zone/bot.cpp | 69 +++++--- zone/bot_command.cpp | 45 +---- zone/bot_commands/bot_settings.cpp | 71 ++++---- zone/bot_database.cpp | 24 +-- zone/client.cpp | 8 +- zone/mob.cpp | 262 +++++++++++++++-------------- zone/mob.h | 78 ++++----- zone/spells.cpp | 8 +- 8 files changed, 286 insertions(+), 279 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4e36611b36..76268c03fc 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9441,7 +9441,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec if (chance && zone->random.Roll(chance)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); - return(false); + return false; } } @@ -11389,11 +11389,7 @@ void Bot::DoCombatPositioning( bool Bot::CheckDoubleRangedAttack() { int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; - if (chance && zone->random.Roll(chance)) { - return true; - } - - return false; + return (chance && zone->random.Roll(chance)); } bool Bot::RequiresLoSForPositioning() { @@ -11775,7 +11771,10 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell switch (subType) { case CommandedSubTypes::SingleTarget: - if (!IsAnyAESpell(spell_id) && !IsGroupSpell(spell_id)) { + if ( + !IsAnyAESpell(spell_id) && + !IsGroupSpell(spell_id) + ) { return true; } @@ -11787,7 +11786,10 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell break; case CommandedSubTypes::AETarget: - if (IsAnyAESpell(spell_id) && !IsGroupSpell(spell_id)) { + if ( + IsAnyAESpell(spell_id) && + !IsGroupSpell(spell_id) + ) { return true; } @@ -11799,43 +11801,70 @@ bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell break; case CommandedSubTypes::Invis: - if (IsEffectInSpell(spell_id, SE_Invisibility) || IsEffectInSpell(spell_id, SE_Invisibility2)) { + if ( + IsEffectInSpell(spell_id, SE_Invisibility) || + IsEffectInSpell(spell_id, SE_Invisibility2) + ) { return true; } break; case CommandedSubTypes::InvisUndead: - if (IsEffectInSpell(spell_id, SE_InvisVsUndead) || IsEffectInSpell(spell_id, SE_InvisVsUndead2)) { + if ( + IsEffectInSpell(spell_id, SE_InvisVsUndead) || + IsEffectInSpell(spell_id, SE_InvisVsUndead2) + ) { return true; } - -break; + + break; case CommandedSubTypes::InvisAnimals: - if (IsEffectInSpell(spell_id, SE_InvisVsAnimals) || IsEffectInSpell(spell_id, SE_ImprovedInvisAnimals)) { + if ( + IsEffectInSpell(spell_id, SE_InvisVsAnimals) || + IsEffectInSpell(spell_id, SE_ImprovedInvisAnimals) + ) { return true; } break; case CommandedSubTypes::Shrink: if ( - (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) < 100) || - (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) < 100) - ) { + ( + IsEffectInSpell(spell_id, SE_ModelSize) && + CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) < 100 + ) + || + ( + IsEffectInSpell(spell_id, SE_ChangeHeight) && + CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) < 100 + ) + ) { return true; } break; case CommandedSubTypes::Grow: if ( - (IsEffectInSpell(spell_id, SE_ModelSize) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) > 100) || - (IsEffectInSpell(spell_id, SE_ChangeHeight) && CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) > 100) - ) { + ( + IsEffectInSpell(spell_id, SE_ModelSize) && + CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ModelSize), GetLevel()) > 100 + ) + || + ( + IsEffectInSpell(spell_id, SE_ChangeHeight) && + CalcSpellEffectValue(spell_id, GetSpellEffectIndex(spell_id, SE_ChangeHeight), GetLevel()) > 100 + ) + ) { return true; } break; case CommandedSubTypes::Selo: - if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed) && IsBardSong(spell_id)) { + if ( + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_MovementSpeed) && + IsBardSong(spell_id) + ) { return true; } diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index e30503901c..33b40ce790 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -2147,33 +2147,15 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { std::string shortnameField = "Short Name"; std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}", - DialogueWindow::ColorMessage(goldenrod, spellTypeField) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField)) - ) - ) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(goldenrod, spellTypeField)) + + + DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField))) ); popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}", - DialogueWindow::ColorMessage(gold, fillerLine) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - DialogueWindow::ColorMessage(gold, fillerLine) - ) - ) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(gold, fillerLine)) + + + DialogueWindow::TableCell(DialogueWindow::ColorMessage(gold, fillerLine)) ); for (int i = minCount; i <= maxCount; ++i) { @@ -2182,18 +2164,9 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { } popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}", - DialogueWindow::ColorMessage(forest_green, c->GetSpellTypeNameByID(i)) - ) - ) + - DialogueWindow::TableCell( - fmt::format( - "{}", - (!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i))) - ) - ) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(forest_green, c->GetSpellTypeNameByID(i))) + + + DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i)))) ); } diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp index d42df0b077..758916ca03 100644 --- a/zone/bot_commands/bot_settings.cpp +++ b/zone/bot_commands/bot_settings.cpp @@ -2,41 +2,42 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) { - std::list subcommand_list; - subcommand_list.push_back("behindmob"); - subcommand_list.push_back("blockedbuffs"); - subcommand_list.push_back("blockedpetbuffs"); - subcommand_list.push_back("distanceranged"); - subcommand_list.push_back("copysettings"); - subcommand_list.push_back("defaultsettings"); - subcommand_list.push_back("enforcespelllist"); - subcommand_list.push_back("follow"); - subcommand_list.push_back("followdistance"); - subcommand_list.push_back("illusionblock"); - subcommand_list.push_back("maxmeleerange"); - subcommand_list.push_back("owneroption"); - subcommand_list.push_back("petsettype"); - subcommand_list.push_back("sithppercent"); - subcommand_list.push_back("sitincombat"); - subcommand_list.push_back("sitmanapercent"); - subcommand_list.push_back("sithppercent"); - subcommand_list.push_back("spellaggrocheck"); - subcommand_list.push_back("spelldelays"); - subcommand_list.push_back("spellengagedpriority"); - subcommand_list.push_back("spellholds"); - subcommand_list.push_back("spellidlepriority"); - subcommand_list.push_back("spellmaxhppct"); - subcommand_list.push_back("spellmaxmanapct"); - subcommand_list.push_back("spellmaxthresholds"); - subcommand_list.push_back("spellminhppct"); - subcommand_list.push_back("spellminmanapct"); - subcommand_list.push_back("spellminthresholds"); - subcommand_list.push_back("spellpursuepriority"); - subcommand_list.push_back("spelltargetcount"); - subcommand_list.push_back("spelllist"); - subcommand_list.push_back("stance"); - subcommand_list.push_back("togglehelm"); - subcommand_list.push_back("bottoggleranged"); + std::vector subcommand_list = { + "behindmob", + "blockedbuffs", + "blockedpetbuffs", + "distanceranged", + "copysettings", + "defaultsettings", + "enforcespelllist", + "follow", + "followdistance", + "illusionblock", + "maxmeleerange", + "owneroption", + "petsettype", + "sithppercent", + "sitincombat", + "sitmanapercent", + "sithppercent", + "spellaggrocheck", + "spelldelays", + "spellengagedpriority", + "spellholds", + "spellidlepriority", + "spellmaxhppct", + "spellmaxmanapct", + "spellmaxthresholds", + "spellminhppct", + "spellminmanapct", + "spellminthresholds", + "spellpursuepriority", + "spelltargetcount", + "spelllist", + "stance", + "togglehelm", + "bottoggleranged" + }; if (helper_command_alias_fail(c, "bot_command_bot_settings", sep->arg[0], "botsettings")) return; diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 53cf76225d..3ca0f0e45b 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2239,21 +2239,21 @@ bool BotDatabase::LoadBotSettings(Mob* m) for (const auto& e : l) { if (e.setting_type == BotSettingCategories::BaseSetting) { - LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}]." - , m->GetCleanName() - , m->GetBotSettingCategoryName(e.setting_type) - , e.setting_type - , e.value + LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}].", + m->GetCleanName(), + m->GetBotSettingCategoryName(e.setting_type), + e.setting_type, + e.value ); } else { - LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}]." - , m->GetCleanName() - , m->GetBotSpellCategoryName(e.setting_type) - , e.setting_type - , m->GetSpellTypeNameByID(e.setting_id) - , e.setting_id - , e.value + LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}].", + m->GetCleanName(), + m->GetBotSpellCategoryName(e.setting_type), + e.setting_type, + m->GetSpellTypeNameByID(e.setting_id), + e.setting_id, + e.value ); } diff --git a/zone/client.cpp b/zone/client.cpp index 31ac137c9b..27480804bc 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13308,14 +13308,8 @@ std::string Client::SplitCommandHelpText(std::vector msg, std::stri for (const auto& s : msg_split) { returnText += DialogueWindow::TableRow( - DialogueWindow::TableCell( - fmt::format( - "{}", - DialogueWindow::ColorMessage(((secondColor && i == 0) ? color : secondaryColor), s) - ) - ) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(((secondColor && i == 0) ? color : secondaryColor), s)) ); - } } diff --git a/zone/mob.cpp b/zone/mob.cpp index 314a3a6013..3f8426d080 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4822,7 +4822,7 @@ bool Mob::PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, fl return Result; } -bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behindOnly, bool frontOnly, bool bypassLoS) { +bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) { bool Result = false; if (target) { @@ -4830,41 +4830,41 @@ bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, min_distance = min_distance; max_distance = max_distance; - float tempX = 0; - float tempY = 0; - float tempZ = target->GetZ(); - float bestZ = 0; + float temp_x = 0; + float temp_y = 0; + float temp_z = target->GetZ(); + float best_z = 0; auto offset = GetZOffset(); - const float tarX = target->GetX(); - const float tarY = target->GetY(); + const float tar_x = target->GetX(); + const float tar_y = target->GetY(); float tar_distance = 0; glm::vec3 temp_z_Position; glm::vec4 temp_m_Position; - const uint16 maxIterationsAllowed = 50; + const uint16 max_iterations_allowed = 50; uint16 counter = 0; - while (counter < maxIterationsAllowed) { - tempX = tarX + zone->random.Real(-max_distance, max_distance); - tempY = tarY + zone->random.Real(-max_distance, max_distance); + while (counter < max_iterations_allowed) { + temp_x = tar_x + zone->random.Real(-max_distance, max_distance); + temp_y = tar_y + zone->random.Real(-max_distance, max_distance); - temp_z_Position.x = tempX; - temp_z_Position.y = tempY; - temp_z_Position.z = tempZ; - bestZ = GetFixedZ(temp_z_Position); + temp_z_Position.x = temp_z; + temp_z_Position.y = temp_y; + temp_z_Position.z = temp_z; + best_z = GetFixedZ(temp_z_Position); - if (bestZ != BEST_Z_INVALID) { - tempZ = bestZ; + if (best_z != BEST_Z_INVALID) { + temp_z = best_z; } else { counter++; continue; } - temp_m_Position.x = tempX; - temp_m_Position.y = tempY; - temp_m_Position.z = tempZ; + temp_m_Position.x = temp_x; + temp_m_Position.y = temp_y; + temp_m_Position.z = temp_z; tar_distance = Distance(target->GetPosition(), temp_m_Position); @@ -4872,15 +4872,15 @@ bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, counter++; continue; } - if (frontOnly && !InFrontMob(target, tempX, tempY)) { + if (front_only && !InFrontMob(target, temp_x, temp_y)) { counter++; continue; } - else if (behindOnly && !BehindMob(target, tempX, tempY)) { + else if (behind_only && !BehindMob(target, temp_x, temp_y)) { counter++; continue; } - if (!bypassLoS && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, tempX, tempY, tempZ)) { + if (!bypass_los && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, temp_x, temp_y, temp_z)) { counter++; continue; } @@ -4890,9 +4890,9 @@ bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, } if (Result) { - x_dest = tempX; - y_dest = tempY; - z_dest = tempZ; + x_dest = temp_x; + y_dest = temp_y; + z_dest = temp_z; } } @@ -8690,7 +8690,7 @@ void Mob::CheckScanCloseMobsMovingTimer() } } -std::vector Mob::GatherSpellTargets(bool entireRaid, Mob* target, bool noClients, bool noBots, bool noPets) { +std::vector Mob::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots, bool no_pets) { std::vector valid_spell_targets; if (IsRaidGrouped()) { @@ -8704,25 +8704,32 @@ std::vector Mob::GatherSpellTargets(bool entireRaid, Mob* target, bool noC } if (raid) { - if (entireRaid) { + if (entire_raid) { for (const auto& m : raid->members) { - if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !no_clients) || (m.member->IsBot() && !no_bots))) { valid_spell_targets.emplace_back(m.member); } } } else { - std::vector raidGroup; + std::vector raid_group; if (target) { - raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(target->GetName())); + raid_group = raid->GetRaidGroupMembers(raid->GetGroup(target->GetName())); } else { - raidGroup = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); + raid_group = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); } - for (const auto& m : raidGroup) { - if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !noClients) || (m.member->IsBot() && !noBots))) { + for (const auto& m : raid_group) { + if ( + m.member && + m.group_number != RAID_GROUPLESS && + ( + (m.member->IsClient() && !no_clients) || + (m.member->IsBot() && !no_bots) + ) + ) { valid_spell_targets.emplace_back(m.member); } } @@ -8734,7 +8741,7 @@ std::vector Mob::GatherSpellTargets(bool entireRaid, Mob* target, bool noC if (group) { for (const auto& m : group->members) { - if (m && ((m->IsClient() && !noClients) || (m->IsBot() && !noBots))) { + if (m && ((m->IsClient() && !no_clients) || (m->IsBot() && !no_bots))) { valid_spell_targets.emplace_back(m); } } @@ -8747,16 +8754,16 @@ std::vector Mob::GatherSpellTargets(bool entireRaid, Mob* target, bool noC return valid_spell_targets; } -uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { +uint16 Mob::GetSpellTypeIDByShortName(std::string spell_type_string) { for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!Strings::ToLower(spellTypeString).compare(GetSpellTypeShortNameByID(i))) { + if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { return i; } } for (int i = BotSpellTypes::COMMANDED_START; i <= BotSpellTypes::COMMANDED_END; ++i) { - if (!Strings::ToLower(spellTypeString).compare(GetSpellTypeShortNameByID(i))) { + if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { return i; } } @@ -8765,16 +8772,16 @@ uint16 Mob::GetSpellTypeIDByShortName(std::string spellTypeString) { } bool Mob::IsValidBotSpellCategory(uint8 setting_type) { - return (setting_type >= BotSettingCategories::START && setting_type <= BotSettingCategories::END_FULL); + return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END_FULL); } std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { return IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; } -uint16 Mob::GetBotSpellCategoryIDByShortName(std::string settingString) { +uint16 Mob::GetBotSpellCategoryIDByShortName(std::string setting_string) { for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { - if (!Strings::ToLower(settingString).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) { + if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) { return i; } } @@ -8783,16 +8790,16 @@ uint16 Mob::GetBotSpellCategoryIDByShortName(std::string settingString) { } bool Mob::IsValidBotBaseSetting(uint16 setting_type) { - return (setting_type >= BotBaseSettings::START_ALL && setting_type <= BotBaseSettings::END); + return EQ::ValueWithin(setting_type, BotBaseSettings::START_ALL, BotBaseSettings::END); } std::string Mob::GetBotSettingCategoryName(uint16 setting_type) { return IsValidBotBaseSetting(setting_type) ? botBaseSettings_names[setting_type] : "UNKNOWN SETTING"; } -uint16 Mob::GetBaseSettingIDByShortName(std::string settingString) { +uint16 Mob::GetBaseSettingIDByShortName(std::string setting_string) { for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { - if (!Strings::ToLower(settingString).compare(Strings::ToLower(GetBotSettingCategoryName(i)))) { + if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSettingCategoryName(i)))) { return i; } } @@ -8800,30 +8807,33 @@ uint16 Mob::GetBaseSettingIDByShortName(std::string settingString) { return UINT16_MAX; } -bool Mob::IsValidSpellType(uint16 spellType) { - return (spellType >= BotSpellTypes::START && spellType <= BotSpellTypes::END) || (spellType >= BotSpellTypes::COMMANDED_START && spellType <= BotSpellTypes::COMMANDED_END); +bool Mob::IsValidSpellType(uint16 spell_type) { + return ( + EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END) || + EQ::ValueWithin(spell_type, BotSpellTypes::COMMANDED_START, BotSpellTypes::COMMANDED_END) + ); } -std::string Mob::GetSpellTypeShortNameByID(uint16 spellType) { - return IsValidSpellType(spellType) ? spellType_shortNames[spellType] : "UNKNOWN SPELLTYPE"; +std::string Mob::GetSpellTypeShortNameByID(uint16 spell_type) { + return IsValidSpellType(spell_type) ? spellType_shortNames[spell_type] : "UNKNOWN SPELLTYPE"; } -std::string Mob::GetSpellTypeNameByID(uint16 spellType) { - return IsValidSpellType(spellType) ? spellType_names[spellType] : "UNKNOWN SPELLTYPE"; +std::string Mob::GetSpellTypeNameByID(uint16 spell_type) { + return IsValidSpellType(spell_type) ? spellType_names[spell_type] : "UNKNOWN SPELLTYPE"; } -bool Mob::IsValidSubType(uint16 subType) { - return (subType >= CommandedSubTypes::START && subType <= CommandedSubTypes::END); +bool Mob::IsValidSubType(uint16 sub_type) { + return EQ::ValueWithin(sub_type, CommandedSubTypes::START, CommandedSubTypes::END); } -std::string Mob::GetSubTypeNameByID(uint16 subType) { - return IsValidSpellType(subType) ? botSubType_names[subType] : "UNKNOWN SUBTYPE"; +std::string Mob::GetSubTypeNameByID(uint16 sub_type) { + return IsValidSpellType(sub_type) ? botSubType_names[sub_type] : "UNKNOWN SUBTYPE"; } -bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { - uint8 botClass = GetClass(); +bool Mob::GetDefaultSpellHold(uint16 spell_type, uint8 stance) { + uint8 bot_class = GetClass(); - switch (spellType) { + switch (spell_type) { case BotSpellTypes::FastHeals: case BotSpellTypes::VeryFastHeals: case BotSpellTypes::Pet: @@ -8866,7 +8876,7 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { case BotSpellTypes::InCombatBuffSong: case BotSpellTypes::OutOfCombatBuffSong: case BotSpellTypes::PreCombatBuffSong: - if (botClass == Class::Bard) { + if (bot_class == Class::Bard) { return false; } else { @@ -8917,7 +8927,7 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { return false; } case BotSpellTypes::HateLine: - if (botClass == Class::ShadowKnight || botClass == Class::Paladin) { + if (bot_class == Class::ShadowKnight || bot_class == Class::Paladin) { switch (stance) { case Stance::Aggressive: return false; @@ -8948,8 +8958,8 @@ bool Mob::GetDefaultSpellHold(uint16 spellType, uint8 stance) { } } -uint16 Mob::GetDefaultSpellDelay(uint16 spellType, uint8 stance) { - switch (spellType) { +uint16 Mob::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { + switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: return 1500; @@ -9015,8 +9025,8 @@ uint16 Mob::GetDefaultSpellDelay(uint16 spellType, uint8 stance) { } } -uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance) { - switch (spellType) { +uint8 Mob::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { + switch (spell_type) { case BotSpellTypes::AEDebuff: case BotSpellTypes::Debuff: case BotSpellTypes::AEDispel: @@ -9063,10 +9073,10 @@ uint8 Mob::GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance) { } } -uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { - uint8 botClass = GetClass(); +uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { + uint8 bot_class = GetClass(); - switch (spellType) { + switch (spell_type) { case BotSpellTypes::Escape: case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: @@ -9153,7 +9163,7 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: - if (botClass == Class::Necromancer || botClass == Class::Shaman) { + if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { return 60; } else { @@ -9195,24 +9205,24 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance) { } } -void Mob::SetSpellHold(uint16 spellType, bool holdStatus) { - _spellSettings[spellType].hold = holdStatus; +void Mob::SetSpellHold(uint16 spell_type, bool hold_status) { + _spellSettings[spell_type].hold = hold_status; } -void Mob::SetSpellDelay(uint16 spellType, uint16 delayValue) { - _spellSettings[spellType].delay = delayValue; +void Mob::SetSpellDelay(uint16 spell_type, uint16 delay_value) { + _spellSettings[spell_type].delay = delay_value; } -void Mob::SetSpellMinThreshold(uint16 spellType, uint8 thresholdValue) { - _spellSettings[spellType].minThreshold = thresholdValue; +void Mob::SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { + _spellSettings[spell_type].minThreshold = threshold_value; } -void Mob::SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue) { - _spellSettings[spellType].maxThreshold = thresholdValue; +void Mob::SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { + _spellSettings[spell_type].maxThreshold = threshold_value; } -void Mob::SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime) { - _spellSettings[spellType].recastTimer.Start(recastTime); +void Mob::SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { + _spellSettings[spell_type].recastTimer.Start(recast_time); } void Mob::StartBotSpellTimers() { @@ -9227,84 +9237,84 @@ void Mob::DisableBotSpellTimers() { } } -bool Mob::GetUltimateSpellHold(uint16 spellType, Mob* tar) { +bool Mob::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellHold(spellType); + return GetSpellHold(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellHold(GetPetSpellType(spellType)); + return tar->GetOwner()->GetSpellHold(GetPetSpellType(spell_type)); } - return GetSpellHold(spellType); + return GetSpellHold(spell_type); } -uint16 Mob::GetUltimateSpellDelay(uint16 spellType, Mob* tar) { +uint16 Mob::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellDelay(spellType); + return GetSpellDelay(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spellType)); + return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { - return tar->GetSpellDelay(spellType); + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->GetSpellDelay(spell_type); } - return GetSpellDelay(spellType); + return GetSpellDelay(spell_type); } -bool Mob::GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar) { +bool Mob::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { if (!tar) { - return SpellTypeRecastCheck(spellType); + return SpellTypeRecastCheck(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spellType)); + return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { - return tar->SpellTypeRecastCheck(spellType); + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->SpellTypeRecastCheck(spell_type); } - return SpellTypeRecastCheck(spellType); + return SpellTypeRecastCheck(spell_type); } -uint8 Mob::GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar) { +uint8 Mob::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellMinThreshold(spellType); + return GetSpellMinThreshold(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spellType)); + return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { - return tar->GetSpellMinThreshold(spellType); + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->GetSpellMinThreshold(spell_type); } - return GetSpellMinThreshold(spellType); + return GetSpellMinThreshold(spell_type); } -uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar) { +uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellMaxThreshold(spellType); + return GetSpellMaxThreshold(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spellType)); + return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spell_type)); } - if (IsBotSpellTypeOtherBeneficial(spellType) && tar->IsOfClientBot()) { - return tar->GetSpellMaxThreshold(spellType); + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->GetSpellMaxThreshold(spell_type); } - return GetSpellMaxThreshold(spellType); + return GetSpellMaxThreshold(spell_type); } -uint16 Mob::GetPetSpellType(uint16 spellType) { - switch (spellType) { +uint16 Mob::GetPetSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::VeryFastHeals: return BotSpellTypes::PetVeryFastHeals; case BotSpellTypes::FastHeals: @@ -9327,11 +9337,11 @@ uint16 Mob::GetPetSpellType(uint16 spellType) { break; } - return spellType; + return spell_type; } -uint8 Mob::GetHPRatioForSpellType(uint16 spellType, Mob* tar) { - switch (spellType) { +uint8 Mob::GetHPRatioForSpellType(uint16 spell_type, Mob* tar) { + switch (spell_type) { case BotSpellTypes::Escape: case BotSpellTypes::HateRedux: case BotSpellTypes::InCombatBuff: @@ -9350,28 +9360,28 @@ uint8 Mob::GetHPRatioForSpellType(uint16 spellType, Mob* tar) { return tar->GetHPRatio(); } -void Mob::SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue) { +void Mob::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value) { if (!IsOfClientBot()) { return; } if (IsClient()) { - CastToClient()->SetBotSetting(settingType, botSetting, settingValue); + CastToClient()->SetBotSetting(setting_type, bot_setting, setting_value); return; } if (IsBot()) { - CastToBot()->SetBotSetting(settingType, botSetting, settingValue); + CastToBot()->SetBotSetting(setting_type, bot_setting, setting_value); return; } return; } -void Mob::SetBaseSetting(uint16 baseSetting, int settingValue) { - switch (baseSetting) { +void Mob::SetBaseSetting(uint16 base_setting, int setting_value) { + switch (base_setting) { case BotBaseSettings::IllusionBlock: - SetIllusionBlock(settingValue); + SetIllusionBlock(setting_value); break; default: break; @@ -9409,7 +9419,7 @@ void Mob::ClearDataBucketCache() } } -bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { +bool Mob::IsInGroupOrRaid(Mob *other, bool same_raid_group) { if (!other || !IsOfClientBotMerc() || !other->IsOfClientBotMerc()) { return false; } @@ -9440,23 +9450,23 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { return false; } - Raid* otherRaid = nullptr; + Raid* other_raid = nullptr; if (other->IsBot()) { - otherRaid = other->CastToBot()->GetStoredRaid(); + other_raid = other->CastToBot()->GetStoredRaid(); } else { - otherRaid = other->GetRaid(); + other_raid = other->GetRaid(); } - if (!otherRaid) { + if (!other_raid) { return false; } - auto rGroup = raid->GetGroup(GetCleanName()); - auto rOGroup = otherRaid->GetGroup(other->GetCleanName()); + auto raid_group = raid->GetGroup(GetCleanName()); + auto raid_other_group = other_raid->GetGroup(other->GetCleanName()); - if (rGroup == RAID_GROUPLESS || rOGroup == RAID_GROUPLESS || (sameRaidGroup && rGroup != rOGroup)) { + if (raid_group == RAID_GROUPLESS || raid_other_group == RAID_GROUPLESS || (same_raid_group && raid_group != raid_other_group)) { return false; } @@ -9464,10 +9474,10 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool sameRaidGroup) { } else { auto* group = GetGroup(); - auto* groupOther = other->GetGroup(); + auto* group_other = other->GetGroup(); if (group) { - if (!groupOther || group != groupOther) { + if (!group_other || group != group_other) { return false; } diff --git a/zone/mob.h b/zone/mob.h index 3bd97c7cea..ae121c772e 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -432,53 +432,53 @@ class Mob : public Entity { inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient() ? true : _spellSettings[spellType].recastTimer.GetRemainingTime() > 0 ? false : true); } - std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool noClients = false, bool noBots = false, bool noPets = false); + std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); - uint16 GetSpellTypeIDByShortName(std::string spellTypeString); + uint16 GetSpellTypeIDByShortName(std::string spellType_string); bool IsValidBotSpellCategory(uint8 setting_type); std::string GetBotSpellCategoryName(uint8 setting_type); - uint16 GetBotSpellCategoryIDByShortName(std::string settingString); + uint16 GetBotSpellCategoryIDByShortName(std::string setting_string); bool IsValidBotBaseSetting(uint16 setting_type); std::string GetBotSettingCategoryName(uint16 setting_type); - uint16 GetBaseSettingIDByShortName(std::string settingString); - bool IsValidSpellType(uint16 spellType); - std::string GetSpellTypeNameByID(uint16 spellType); - std::string GetSpellTypeShortNameByID(uint16 spellType); - bool IsValidSubType(uint16 subType); - std::string GetSubTypeNameByID(uint16 subType); - - bool GetDefaultSpellHold(uint16 spellType, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellDelay(uint16 spellType, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMinThreshold(uint16 spellType, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMaxThreshold(uint16 spellType, uint8 stance = Stance::Balanced); - - inline bool GetSpellHold(uint16 spellType) const { return _spellSettings[spellType].hold; } - void SetSpellHold(uint16 spellType, bool holdStatus); - inline uint16 GetSpellDelay(uint16 spellType) const { return _spellSettings[spellType].delay; } - void SetSpellDelay(uint16 spellType, uint16 delayValue); - inline uint8 GetSpellMinThreshold(uint16 spellType) const { return _spellSettings[spellType].minThreshold; } - void SetSpellMinThreshold(uint16 spellType, uint8 thresholdValue); - inline uint8 GetSpellMaxThreshold(uint16 spellType) const { return _spellSettings[spellType].maxThreshold; } - void SetSpellMaxThreshold(uint16 spellType, uint8 thresholdValue); - - inline uint16 GetSpellTypeRecastTimer(uint16 spellType) { return _spellSettings[spellType].recastTimer.GetRemainingTime(); } - void SetSpellTypeRecastTimer(uint16 spellType, uint32 recastTime); - - uint8 GetHPRatioForSpellType(uint16 spellType, Mob* tar); - bool GetUltimateSpellHold(uint16 spellType, Mob* tar); - uint16 GetUltimateSpellDelay(uint16 spellType, Mob* tar); - bool GetUltimateSpellDelayCheck(uint16 spellType, Mob* tar); - uint8 GetUltimateSpellMinThreshold(uint16 spellType, Mob* tar); - uint8 GetUltimateSpellMaxThreshold(uint16 spellType, Mob* tar); - - uint16 GetPetSpellType(uint16 spellType); + uint16 GetBaseSettingIDByShortName(std::string setting_string); + bool IsValidSpellType(uint16 spell_type); + std::string GetSpellTypeNameByID(uint16 spell_type); + std::string GetSpellTypeShortNameByID(uint16 spell_type); + bool IsValidSubType(uint16 sub_type); + std::string GetSubTypeNameByID(uint16 sub_type); + + bool GetDefaultSpellHold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + + inline bool GetSpellHold(uint16 spell_type) const { return _spellSettings[spell_type].hold; } + void SetSpellHold(uint16 spell_type, bool hold_status); + inline uint16 GetSpellDelay(uint16 spell_type) const { return _spellSettings[spell_type].delay; } + void SetSpellDelay(uint16 spell_type, uint16 delay_value); + inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return _spellSettings[spell_type].minThreshold; } + void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value); + inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return _spellSettings[spell_type].maxThreshold; } + void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value); + + inline uint16 GetSpellTypeRecastTimer(uint16 spell_type) { return _spellSettings[spell_type].recastTimer.GetRemainingTime(); } + void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time); + + uint8 GetHPRatioForSpellType(uint16 spell_type, Mob* tar); + bool GetUltimateSpellHold(uint16 spell_type, Mob* tar); + uint16 GetUltimateSpellDelay(uint16 spell_type, Mob* tar); + bool GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar); + uint8 GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar); + uint8 GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar); + + uint16 GetPetSpellType(uint16 spell_type); void DisableBotSpellTimers(); void StartBotSpellTimers(); - void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); - void SetBaseSetting(uint16 baseSetting, int settingValue); + void SetBotSetting(uint8 spell_type, uint16 bot_setting, int setting_value); + void SetBaseSetting(uint16 base_setting, int setting_value); void SetIllusionBlock(bool value) { _illusionBlock = value; } bool GetIllusionBlock() const { return _illusionBlock; } @@ -785,7 +785,7 @@ class Mob : public Entity { virtual bool HasGroup() = 0; virtual Raid* GetRaid() = 0; virtual Group* GetGroup() = 0; - bool IsInGroupOrRaid(Mob* other, bool sameRaidGroup = false); + bool IsInGroupOrRaid(Mob* other, bool same_raid_group = false); //Faction virtual inline int32 GetPrimaryFaction() const { return 0; } @@ -940,7 +940,7 @@ class Mob : public Entity { void ShowStats(Client* client); void ShowBuffs(Client* c); bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true); - bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behindOnly = false, bool frontOnly = false, bool bypassLoS = false); + bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); virtual int GetKillExpMod() const { return 100; } // aura functions diff --git a/zone/spells.cpp b/zone/spells.cpp index 8d427aeb11..919706d422 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2180,14 +2180,14 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce IsClient() && CastToClient()->TGB() && IsTGBCompatibleSpell(spell_id) && (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)) - ) { + ) { if ( !target || target->IsCorpse() || ( target->IsNPC() && !(target->GetOwner() && target->GetOwner()->IsClient()) - ) + ) ) { spell_target = this; } @@ -7549,8 +7549,8 @@ bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) { int effect_index; - if (caster == nullptr) { - return(false); + if (!caster) { + return false; } //TODO: this function loops through the effect list for From 95c1bb6883f0afc462de1d122f2f63d07f276140 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 10 Jan 2025 11:45:00 -0600 Subject: [PATCH 263/394] code cleanup 2 --- common/spdat.cpp | 258 ++++---- common/spdat.h | 38 +- zone/bot.cpp | 893 +++++++++++++-------------- zone/bot.h | 220 +++---- zone/bot_command.cpp | 52 +- zone/bot_commands/bot.cpp | 48 +- zone/bot_database.cpp | 58 +- zone/botspellsai.cpp | 1199 +++++++++++++++++++------------------ zone/client.cpp | 144 +++-- zone/client.h | 6 +- 10 files changed, 1460 insertions(+), 1456 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 8990b46733..898c06bc25 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2791,8 +2791,8 @@ bool IsLichSpell(uint16 spell_id) return false; } -bool IsBotSpellTypeDetrimental(uint16 spellType) { - switch (spellType) { +bool IsBotSpellTypeDetrimental(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::Nuke: case BotSpellTypes::Root: case BotSpellTypes::Lifetap: @@ -2831,8 +2831,8 @@ bool IsBotSpellTypeDetrimental(uint16 spellType) { return false; } -bool IsBotSpellTypeBeneficial(uint16 spellType) { - switch (spellType) { +bool IsBotSpellTypeBeneficial(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: @@ -2881,8 +2881,8 @@ bool IsBotSpellTypeBeneficial(uint16 spellType) { return false; } -bool IsBotSpellTypeOtherBeneficial(uint16 spellType) { - switch (spellType) { +bool IsBotSpellTypeOtherBeneficial(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: @@ -2925,8 +2925,8 @@ bool IsBotSpellTypeOtherBeneficial(uint16 spellType) { return false; } -bool IsBotSpellTypeInnate(uint16 spellType) { - switch (spellType) { +bool IsBotSpellTypeInnate(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::PBAENuke: @@ -2960,8 +2960,8 @@ bool IsBotSpellTypeInnate(uint16 spellType) { return false; } -bool IsAEBotSpellType(uint16 spellType) { - switch (spellType) { +bool IsAEBotSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::AEDebuff: case BotSpellTypes::AEFear: case BotSpellTypes::AEMez: @@ -2985,8 +2985,8 @@ bool IsAEBotSpellType(uint16 spellType) { return false; } -bool IsGroupBotSpellType(uint16 spellType) { - switch (spellType) { +bool IsGroupBotSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::GroupCures: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::GroupHeals: @@ -2999,8 +2999,8 @@ bool IsGroupBotSpellType(uint16 spellType) { return false; } -bool IsGroupTargetOnlyBotSpellType(uint16 spellType) { - switch (spellType) { +bool IsGroupTargetOnlyBotSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::GroupCures: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::GroupHeals: @@ -3012,8 +3012,8 @@ bool IsGroupTargetOnlyBotSpellType(uint16 spellType) { return false; } -bool IsPetBotSpellType(uint16 spellType) { - switch (spellType) { +bool IsPetBotSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::PetBuffs: case BotSpellTypes::PetRegularHeals: case BotSpellTypes::PetCompleteHeals: @@ -3031,8 +3031,8 @@ bool IsPetBotSpellType(uint16 spellType) { return false; } -bool IsClientBotSpellType(uint16 spellType) { - switch (spellType) { +bool IsClientBotSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::RegularHeal: case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: @@ -3063,8 +3063,8 @@ bool IsClientBotSpellType(uint16 spellType) { return false; } -bool IsHealBotSpellType(uint16 spellType) { - switch (spellType) { +bool IsHealBotSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::FastHeals: case BotSpellTypes::RegularHeal: @@ -3086,12 +3086,12 @@ bool IsHealBotSpellType(uint16 spellType) { return false; } -bool SpellTypeRequiresLoS(uint16 spellType) { - if (IsAEBotSpellType(spellType)) { // These gather their own targets later +bool SpellTypeRequiresLoS(uint16 spell_type) { + if (IsAEBotSpellType(spell_type)) { // These gather their own targets later return false; } - switch (spellType) { + switch (spell_type) { case BotSpellTypes::RegularHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::CompleteHeal: @@ -3114,8 +3114,8 @@ bool SpellTypeRequiresLoS(uint16 spellType) { return true; } -bool SpellTypeRequiresTarget(uint16 spellType) { - switch (spellType) { +bool SpellTypeRequiresTarget(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::Pet: case BotSpellTypes::Succor: return false; @@ -3126,8 +3126,8 @@ bool SpellTypeRequiresTarget(uint16 spellType) { return true; } -bool SpellTypeRequiresAEChecks(uint16 spellType) { - switch (spellType) { +bool SpellTypeRequiresAEChecks(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::AEMez: return false; default: @@ -3137,12 +3137,12 @@ bool SpellTypeRequiresAEChecks(uint16 spellType) { return true; } -bool IsValidSpellAndLoS(uint32 spell_id, bool hasLoS) { +bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) { if (!IsValidSpell(spell_id)) { return false; } - if (!hasLoS && IsTargetRequiredForSpell(spell_id)) { + if (!has_los && IsTargetRequiredForSpell(spell_id)) { return false; } @@ -3162,8 +3162,8 @@ bool IsResurrectSpell(uint16 spell_id) return IsEffectInSpell(spell_id, SE_Revive); } -bool RequiresStackCheck(uint16 spellType) { - switch (spellType) { +bool RequiresStackCheck(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: case BotSpellTypes::FastHeals: @@ -3244,8 +3244,8 @@ bool IsHateSpell(uint16 spell_id) { return false; } -bool IsCommandedSpellType(uint16 spellType) { - switch (spellType) { +bool IsCommandedSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::Charm: case BotSpellTypes::AEFear: case BotSpellTypes::Fear: @@ -3278,8 +3278,8 @@ bool IsCommandedSpellType(uint16 spellType) { return false; } -bool IsPullingSpellType(uint16 spellType) { - switch (spellType) { +bool IsPullingSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::Nuke: case BotSpellTypes::Lifetap: case BotSpellTypes::Snare: @@ -3297,101 +3297,101 @@ bool IsPullingSpellType(uint16 spellType) { return false; } -uint16 GetCorrectSpellType(uint16 spellType, uint16 spell_id) { - uint16 correctType = UINT16_MAX; +uint16 GetCorrectSpellType(uint16 spell_type, uint16 spell_id) { + uint16 correct_type = UINT16_MAX; SPDat_Spell_Struct spell = spells[spell_id]; - std::string teleportZone = spell.teleport_zone; + std::string teleport_zone = spell.teleport_zone; if (IsCharmSpell(spell_id)) { - correctType = BotSpellTypes::Charm; + correct_type = BotSpellTypes::Charm; } else if (IsFearSpell(spell_id)) { - correctType = BotSpellTypes::Fear; + correct_type = BotSpellTypes::Fear; } else if (IsEffectInSpell(spell_id, SE_Revive)) { - correctType = BotSpellTypes::Resurrect; + correct_type = BotSpellTypes::Resurrect; } else if (IsHarmonySpell(spell_id)) { - correctType = BotSpellTypes::Lull; + correct_type = BotSpellTypes::Lull; } - else if (teleportZone.compare("") && !IsEffectInSpell(spell_id, SE_GateToHomeCity) && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - correctType = BotSpellTypes::Teleport; + else if (teleport_zone.compare("") && !IsEffectInSpell(spell_id, SE_GateToHomeCity) && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { + correct_type = BotSpellTypes::Teleport; } else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - correctType = BotSpellTypes::Succor; + correct_type = BotSpellTypes::Succor; } else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - correctType = BotSpellTypes::BindAffinity; + correct_type = BotSpellTypes::BindAffinity; } else if (IsEffectInSpell(spell_id, SE_Identify)) { - correctType = BotSpellTypes::Identify; + correct_type = BotSpellTypes::Identify; } - else if (spellType == BotSpellTypes::Levitate && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - correctType = BotSpellTypes::Levitate; + else if (spell_type == BotSpellTypes::Levitate && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { + correct_type = BotSpellTypes::Levitate; } - else if (spellType == BotSpellTypes::Rune && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))) { - correctType = BotSpellTypes::Rune; + else if (spell_type == BotSpellTypes::Rune && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))) { + correct_type = BotSpellTypes::Rune; } - else if (spellType == BotSpellTypes::WaterBreathing && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { - correctType = BotSpellTypes::WaterBreathing; + else if (spell_type == BotSpellTypes::WaterBreathing && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { + correct_type = BotSpellTypes::WaterBreathing; } - else if (spellType == BotSpellTypes::Size && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - correctType = BotSpellTypes::Size; + else if (spell_type == BotSpellTypes::Size && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { + correct_type = BotSpellTypes::Size; } - else if (spellType == BotSpellTypes::Invisibility && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))) { - correctType = BotSpellTypes::Invisibility; + else if (spell_type == BotSpellTypes::Invisibility && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))) { + correct_type = BotSpellTypes::Invisibility; } - else if (spellType == BotSpellTypes::MovementSpeed && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - correctType = BotSpellTypes::MovementSpeed; + else if (spell_type == BotSpellTypes::MovementSpeed && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { + correct_type = BotSpellTypes::MovementSpeed; } - else if (!teleportZone.compare("") && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))) { - correctType = BotSpellTypes::SendHome; + else if (!teleport_zone.compare("") && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))) { + correct_type = BotSpellTypes::SendHome; } else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - correctType = BotSpellTypes::SummonCorpse; + correct_type = BotSpellTypes::SummonCorpse; } - if (correctType == UINT16_MAX) { + if (correct_type == UINT16_MAX) { if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { - correctType = BotSpellTypes::Pet; + correct_type = BotSpellTypes::Pet; } else if (IsMesmerizeSpell(spell_id)) { - correctType = BotSpellTypes::Mez; + correct_type = BotSpellTypes::Mez; } else if (IsEscapeSpell(spell_id)) { - correctType = BotSpellTypes::Escape; + correct_type = BotSpellTypes::Escape; } else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_Root)) { if (IsAnyAESpell(spell_id)) { - correctType = BotSpellTypes::AERoot; + correct_type = BotSpellTypes::AERoot; } else { - correctType = BotSpellTypes::Root; + correct_type = BotSpellTypes::Root; } } else if (IsDetrimentalSpell(spell_id) && IsLifetapSpell(spell_id)) { - correctType = BotSpellTypes::Lifetap; + correct_type = BotSpellTypes::Lifetap; } else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - correctType = BotSpellTypes::Snare; + correct_type = BotSpellTypes::Snare; } else if (IsDetrimentalSpell(spell_id) && (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))) { - correctType = BotSpellTypes::DOT; + correct_type = BotSpellTypes::DOT; } else if (IsDispelSpell(spell_id)) { - correctType = BotSpellTypes::Dispel; + correct_type = BotSpellTypes::Dispel; } else if (IsDetrimentalSpell(spell_id) && IsSlowSpell(spell_id)) { - correctType = BotSpellTypes::Slow; + correct_type = BotSpellTypes::Slow; } else if (IsDebuffSpell(spell_id) && !IsHateReduxSpell(spell_id) && !IsHateSpell(spell_id)) { - correctType = BotSpellTypes::Debuff; + correct_type = BotSpellTypes::Debuff; } else if (IsHateReduxSpell(spell_id)) { - correctType = BotSpellTypes::HateRedux; + correct_type = BotSpellTypes::HateRedux; } else if (IsDetrimentalSpell(spell_id) && IsHateSpell(spell_id)) { - correctType = BotSpellTypes::HateLine; + correct_type = BotSpellTypes::HateLine; } else if ( IsBuffSpell(spell_id) && @@ -3399,133 +3399,133 @@ uint16 GetCorrectSpellType(uint16 spellType, uint16 spell_id) { IsBardSong(spell_id) ) { if ( - spellType == BotSpellTypes::InCombatBuffSong || - spellType == BotSpellTypes::OutOfCombatBuffSong || - spellType == BotSpellTypes::PreCombatBuffSong + spell_type == BotSpellTypes::InCombatBuffSong || + spell_type == BotSpellTypes::OutOfCombatBuffSong || + spell_type == BotSpellTypes::PreCombatBuffSong ) { - correctType = spellType; + correct_type = spell_type; } else { - correctType = BotSpellTypes::OutOfCombatBuffSong; + correct_type = BotSpellTypes::OutOfCombatBuffSong; } } else if ( !IsBardSong(spell_id) && ( (IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) || - (spellType == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) + (spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) ) ) { - correctType = BotSpellTypes::InCombatBuff; + correct_type = BotSpellTypes::InCombatBuff; } else if ( - spellType == BotSpellTypes::PreCombatBuff && + spell_type == BotSpellTypes::PreCombatBuff && IsAnyBuffSpell(spell_id) && !IsBardSong(spell_id) ) { - correctType = BotSpellTypes::PreCombatBuff; + correct_type = BotSpellTypes::PreCombatBuff; } else if ( - (IsCureSpell(spell_id) && spellType == BotSpellTypes::Cure) || + (IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) || (IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id)) ) { - correctType = BotSpellTypes::Cure; + correct_type = BotSpellTypes::Cure; } else if (IsAnyNukeOrStunSpell(spell_id)) { if (IsAnyAESpell(spell_id)) { if (IsAERainSpell(spell_id)) { - correctType = BotSpellTypes::AERains; + correct_type = BotSpellTypes::AERains; } else if (IsPBAENukeSpell(spell_id)) { - correctType = BotSpellTypes::PBAENuke; + correct_type = BotSpellTypes::PBAENuke; } else if (IsStunSpell(spell_id)) { - correctType = BotSpellTypes::AEStun; + correct_type = BotSpellTypes::AEStun; } else { - correctType = BotSpellTypes::AENukes; + correct_type = BotSpellTypes::AENukes; } } else if (IsStunSpell(spell_id)) { - correctType = BotSpellTypes::Stun; + correct_type = BotSpellTypes::Stun; } else { - correctType = BotSpellTypes::Nuke; + correct_type = BotSpellTypes::Nuke; } } else if (IsAnyHealSpell(spell_id)) { if (IsGroupSpell(spell_id)) { if (IsGroupCompleteHealSpell(spell_id)) { - correctType = BotSpellTypes::GroupCompleteHeals; + correct_type = BotSpellTypes::GroupCompleteHeals; } else if (IsGroupHealOverTimeSpell(spell_id)) { - correctType = BotSpellTypes::GroupHoTHeals; + correct_type = BotSpellTypes::GroupHoTHeals; } else if (IsRegularGroupHealSpell(spell_id)) { - correctType = BotSpellTypes::GroupHeals; + correct_type = BotSpellTypes::GroupHeals; } } else { if (IsVeryFastHealSpell(spell_id)) { - correctType = BotSpellTypes::VeryFastHeals; + correct_type = BotSpellTypes::VeryFastHeals; } else if (IsFastHealSpell(spell_id)) { - correctType = BotSpellTypes::FastHeals; + correct_type = BotSpellTypes::FastHeals; } else if (IsCompleteHealSpell(spell_id)) { - correctType = BotSpellTypes::CompleteHeal; + correct_type = BotSpellTypes::CompleteHeal; } else if (IsHealOverTimeSpell(spell_id)) { - correctType = BotSpellTypes::HoTHeals; + correct_type = BotSpellTypes::HoTHeals; } else if (IsRegularSingleTargetHealSpell(spell_id)) { - correctType = BotSpellTypes::RegularHeal; + correct_type = BotSpellTypes::RegularHeal; } else if (IsRegularPetHealSpell(spell_id)) { - correctType = BotSpellTypes::RegularHeal; + correct_type = BotSpellTypes::RegularHeal; } } } else if (IsAnyBuffSpell(spell_id)) { if (IsResistanceOnlySpell(spell_id)) { - correctType = BotSpellTypes::ResistBuffs; + correct_type = BotSpellTypes::ResistBuffs; } else if (IsDamageShieldOnlySpell(spell_id)) { - correctType = BotSpellTypes::DamageShields; + correct_type = BotSpellTypes::DamageShields; } else { - correctType = BotSpellTypes::Buff; + correct_type = BotSpellTypes::Buff; } } } - return correctType; + return correct_type; } -uint16 GetPetSpellType(uint16 spellType) { - switch (spellType) { - case BotSpellTypes::Buff: - return BotSpellTypes::PetBuffs; - case BotSpellTypes::RegularHeal: - return BotSpellTypes::PetRegularHeals; - case BotSpellTypes::CompleteHeal: - return BotSpellTypes::PetCompleteHeals; - case BotSpellTypes::FastHeals: - return BotSpellTypes::PetFastHeals; - case BotSpellTypes::VeryFastHeals: - return BotSpellTypes::PetVeryFastHeals; - case BotSpellTypes::HoTHeals: - return BotSpellTypes::PetHoTHeals; - case BotSpellTypes::Cure: - return BotSpellTypes::PetCures; - case BotSpellTypes::DamageShields: - return BotSpellTypes::PetDamageShields; - case BotSpellTypes::ResistBuffs: - return BotSpellTypes::PetResistBuffs; - default: - return spellType; +uint16 GetPetSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::Cure: + return BotSpellTypes::PetCures; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; + default: + return spell_type; } - return spellType; + return spell_type; } diff --git a/common/spdat.h b/common/spdat.h index 985aab5188..69662eee08 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -896,23 +896,23 @@ const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellT const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); -bool IsBotSpellTypeDetrimental (uint16 spellType); -bool IsBotSpellTypeBeneficial (uint16 spellType); -bool IsBotSpellTypeOtherBeneficial(uint16 spellType); -bool IsBotSpellTypeInnate (uint16 spellType); -bool IsAEBotSpellType(uint16 spellType); -bool IsGroupBotSpellType(uint16 spellType); -bool IsGroupTargetOnlyBotSpellType(uint16 spellType); -bool IsPetBotSpellType(uint16 spellType); -bool IsClientBotSpellType(uint16 spellType); -bool IsHealBotSpellType(uint16 spellType); -bool SpellTypeRequiresLoS(uint16 spellType); -bool SpellTypeRequiresTarget(uint16 spellType); -bool SpellTypeRequiresAEChecks(uint16 spellType); -bool IsCommandedSpellType(uint16 spellType); -bool IsPullingSpellType(uint16 spellType); -uint16 GetCorrectSpellType(uint16 spellType, uint16 spell_id); -uint16 GetPetSpellType(uint16 spellType); +bool IsBotSpellTypeDetrimental (uint16 spell_type); +bool IsBotSpellTypeBeneficial (uint16 spell_type); +bool IsBotSpellTypeOtherBeneficial(uint16 spell_type); +bool IsBotSpellTypeInnate (uint16 spell_type); +bool IsAEBotSpellType(uint16 spell_type); +bool IsGroupBotSpellType(uint16 spell_type); +bool IsGroupTargetOnlyBotSpellType(uint16 spell_type); +bool IsPetBotSpellType(uint16 spell_type); +bool IsClientBotSpellType(uint16 spell_type); +bool IsHealBotSpellType(uint16 spell_type); +bool SpellTypeRequiresLoS(uint16 spell_type); +bool SpellTypeRequiresTarget(uint16 spell_type); +bool SpellTypeRequiresAEChecks(uint16 spell_type); +bool IsCommandedSpellType(uint16 spell_type); +bool IsPullingSpellType(uint16 spell_type); +uint16 GetCorrectSpellType(uint16 spell_type, uint16 spell_id); +uint16 GetPetSpellType(uint16 spell_type); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only @@ -1810,7 +1810,7 @@ bool IsEffectInSpell(uint16 spell_id, int effect_id); uint16 GetSpellTriggerSpellID(uint16 spell_id, int effect_id); bool IsBlankSpellEffect(uint16 spell_id, int effect_index); bool IsValidSpell(uint32 spell_id); -bool IsValidSpellAndLoS(uint32 spell_id, bool hasLoS = true); +bool IsValidSpellAndLoS(uint32 spell_id, bool has_los = true); bool IsSummonSpell(uint16 spell_id); bool IsDamageSpell(uint16 spell_id); bool IsAnyDamageSpell(uint16 spell_id); @@ -1906,7 +1906,7 @@ int8 SpellEffectsCount(uint16 spell_id); bool IsLichSpell(uint16 spell_id); bool IsInstantHealSpell(uint32 spell_id); bool IsResurrectSpell(uint16 spell_id); -bool RequiresStackCheck(uint16 spellType); +bool RequiresStackCheck(uint16 spell_type); bool IsResistanceOnlySpell(uint16 spell_id); bool IsDamageShieldOnlySpell(uint16 spell_id); bool IsHateSpell(uint16 spell_id); diff --git a/zone/bot.cpp b/zone/bot.cpp index 76268c03fc..3f99c4cdbf 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1803,20 +1803,20 @@ void Bot::SpellProcess() { } } -void Bot::BotMeditate(bool isSitting) { +void Bot::BotMeditate(bool is_sitting) { if (GetManaRatio() < GetSitManaPct() || (GetHPRatio() < GetSitHPPct() && GetLevel() < GetStopMeleeLevel())) { - if ((!IsEngaged() || (IsEngaged() && GetMedInCombat() && !HasTargetReflection())) && !isSitting) { + if ((!IsEngaged() || (IsEngaged() && GetMedInCombat() && !HasTargetReflection())) && !is_sitting) { Sit(); } } else { - if (isSitting) { + if (is_sitting) { Stand(); } } } -bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { +bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { if (!other || !IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { return false; } @@ -1831,48 +1831,48 @@ bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { return false; } - if (!CanDoubleAttack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { + if (!can_double_attack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { LogCombatDetail("Bot ranged attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return false; } - const auto rangedItem = GetBotItem(EQ::invslot::slotRange); - const EQ::ItemData* RangeWeapon = nullptr; + const auto ranged_item = GetBotItem(EQ::invslot::slotRange); + const EQ::ItemData* ranged_weapon = nullptr; - if (rangedItem) { - RangeWeapon = rangedItem->GetItem(); + if (ranged_item) { + ranged_weapon = ranged_item->GetItem(); } - if (!RangeWeapon) { + if (!ranged_weapon) { return false; } - const auto ammoItem = GetBotItem(EQ::invslot::slotAmmo); - const EQ::ItemData* Ammo = nullptr; + const auto ammo_item = GetBotItem(EQ::invslot::slotAmmo); + const EQ::ItemData* ammo = nullptr; - if (ammoItem) { - Ammo = ammoItem->GetItem(); + if (ammo_item) { + ammo = ammo_item->GetItem(); } // Bow requires arrows if ( - !Ammo || - (RangeWeapon && + !ammo || + (ranged_weapon && ( - (RangeWeapon->ItemType != EQ::item::ItemTypeBow && RangeWeapon->ItemType != EQ::item::ItemTypeSmallThrowing && RangeWeapon->ItemType != EQ::item::ItemTypeLargeThrowing) || - (RangeWeapon->ItemType == EQ::item::ItemTypeBow && (Ammo->ItemType != EQ::item::ItemTypeArrow)) || + (ranged_weapon->ItemType != EQ::item::ItemTypeBow && ranged_weapon->ItemType != EQ::item::ItemTypeSmallThrowing && ranged_weapon->ItemType != EQ::item::ItemTypeLargeThrowing) || + (ranged_weapon->ItemType == EQ::item::ItemTypeBow && (ammo->ItemType != EQ::item::ItemTypeArrow)) || ( - (RangeWeapon->ItemType == EQ::item::ItemTypeSmallThrowing || RangeWeapon->ItemType == EQ::item::ItemTypeLargeThrowing) && - ammoItem->GetCharges() < 1 || + (ranged_weapon->ItemType == EQ::item::ItemTypeSmallThrowing || ranged_weapon->ItemType == EQ::item::ItemTypeLargeThrowing) && + ammo_item->GetCharges() < 1 || ( - (RuleI(Bots, StackSizeMin) != -1 && rangedItem->GetCharges() != RangeWeapon->StackSize) || - rangedItem->GetCharges() < RuleI(Bots, StackSizeMin) + (RuleI(Bots, StackSizeMin) != -1 && ranged_item->GetCharges() != ranged_weapon->StackSize) || + ranged_item->GetCharges() < RuleI(Bots, StackSizeMin) ) ) ) ) ) { - if (!Ammo || ammoItem->GetCharges() < 1) { + if (!ammo || ammo_item->GetCharges() < 1) { if (!GetCombatRoundForAlerts()) { SetCombatRoundForAlerts(); BotGroupSay(this, "I do not have enough any ammo!"); @@ -1884,17 +1884,17 @@ bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { LogCombatDetail("Ranged attacking [{}] with {} [{}] ([{}]){}{}{}", other->GetCleanName(), - (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? "bow" : "throwing"), - RangeWeapon->Name, - RangeWeapon->ID, - (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? " and arrow " : ""), - (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? Ammo->Name : ""), - (Ammo && Ammo->ItemType == EQ::item::ItemTypeArrow ? std::to_string(Ammo->ID) : "") + (ranged_weapon->ItemType == EQ::item::ItemTypeBow ? "bow" : "throwing"), + ranged_weapon->Name, + ranged_weapon->ID, + (ammo && ammo->ItemType == EQ::item::ItemTypeArrow ? " and arrow " : ""), + (ammo && ammo->ItemType == EQ::item::ItemTypeArrow ? ammo->Name : ""), + (ammo && ammo->ItemType == EQ::item::ItemTypeArrow ? std::to_string(ammo->ID) : "") ); - SendItemAnimation(other, Ammo, (RangeWeapon->ItemType == EQ::item::ItemTypeBow ? EQ::skills::SkillArchery : EQ::skills::SkillThrowing)); - if (RangeWeapon->ItemType == EQ::item::ItemTypeBow) { - DoArcheryAttackDmg(other, rangedItem, ammoItem); // watch + SendItemAnimation(other, ammo, (ranged_weapon->ItemType == EQ::item::ItemTypeBow ? EQ::skills::SkillArchery : EQ::skills::SkillThrowing)); + if (ranged_weapon->ItemType == EQ::item::ItemTypeBow) { + DoArcheryAttackDmg(other, ranged_item, ammo_item); // watch //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; @@ -1903,15 +1903,15 @@ bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { if ( consumes_ammo && ( - RangeWeapon->ExpendableArrow || + ranged_weapon->ExpendableArrow || !ChanceAvoidConsume || (ChanceAvoidConsume < 100 && zone->random.Int(0, 99) > ChanceAvoidConsume) ) ) { - ammoItem->SetCharges((ammoItem->GetCharges() - 1)); + ammo_item->SetCharges((ammo_item->GetCharges() - 1)); LogCombatDetail("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); - if (ammoItem->GetCharges() < 1) { + if (ammo_item->GetCharges() < 1) { RemoveBotItemBySlot(EQ::invslot::slotAmmo); BotRemoveEquipItem(EQ::invslot::slotAmmo); } @@ -1924,13 +1924,13 @@ bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { } } else { - DoThrowingAttackDmg(other, rangedItem); // watch + DoThrowingAttackDmg(other, ranged_item); // watch // Consume Ammo, unless Ammo Consumption is disabled if (RuleB(Bots, BotThrowingConsumesAmmo)) { - ammoItem->SetCharges((ammoItem->GetCharges() - 1)); + ammo_item->SetCharges((ammo_item->GetCharges() - 1)); LogCombatDetail("Consumed Throwing Ammo from slot {}.", EQ::invslot::slotAmmo); - if (ammoItem->GetCharges() < 1) { + if (ammo_item->GetCharges() < 1) { RemoveBotItemBySlot(EQ::invslot::slotAmmo); BotRemoveEquipItem(EQ::invslot::slotAmmo); } @@ -1945,7 +1945,7 @@ bool Bot::BotRangedAttack(Mob* other, bool CanDoubleAttack) { return true; } -bool Bot::CheckBotDoubleAttack(bool tripleAttack) { +bool Bot::CheckBotDoubleAttack(bool triple_attack) { //Check for bonuses that give you a double attack chance regardless of skill (ie Bestial Frenzy/Harmonious Attack AA) uint32 bonus_give_double_attack = aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack; @@ -2146,10 +2146,10 @@ void Bot::AI_Process() return; } - std::vector spellTargetList = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); - SetSpellTargetList(spellTargetList); - std::vector groupSpellTargetList = GatherSpellTargets(); - SetGroupSpellTargetList(groupSpellTargetList); + std::vector spell_target_list = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); + SetSpellTargetList(spell_target_list); + std::vector group_spell_target_list = GatherSpellTargets(); + SetGroupSpellTargetList(group_spell_target_list); SetTempSpellType(UINT16_MAX); // HEAL ROTATION CASTING CHECKS @@ -2235,10 +2235,10 @@ void Bot::AI_Process() // COMBAT RANGE CALCS - bool atCombatRange = false; - bool behindMob = BehindMob(tar, GetX(), GetY()); - bool frontMob = InFrontMob(tar, GetX(), GetY()); - uint8 stopMeleeLevel = GetStopMeleeLevel(); + bool at_combat_range = false; + bool behind_mob = BehindMob(tar, GetX(), GetY()); + bool front_mob = InFrontMob(tar, GetX(), GetY()); + uint8 stop_melee_level = GetStopMeleeLevel(); const EQ::ItemInstance* p_item; const EQ::ItemInstance* s_item; float melee_distance_min = 0.0f; @@ -2246,7 +2246,7 @@ void Bot::AI_Process() float melee_distance = 0.0f; tar_distance = sqrt(tar_distance); - CheckCombatRange(tar, tar_distance, atCombatRange, behindMob, p_item, s_item, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); + CheckCombatRange(tar, tar_distance, at_combat_range, behind_mob, p_item, s_item, melee_distance_min, melee_distance, melee_distance_max, stop_melee_level); // PULLING FLAG (ACTIONABLE RANGE) @@ -2257,7 +2257,7 @@ void Bot::AI_Process() return; } - if (atCombatRange) { + if (at_combat_range) { if (!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); @@ -2280,12 +2280,12 @@ void Bot::AI_Process() } if (RuleB(Bots, UseSpellPulling)) { - uint16 pullSpell = RuleI(Bots, PullSpellID); + uint16 spell_id = RuleI(Bots, PullSpellID); - if (tar_distance <= spells[pullSpell].range) { + if (tar_distance <= spells[spell_id].range) { StopMoving(); SetPullingSpell(true); - CastSpell(pullSpell, tar->GetID()); + CastSpell(spell_id, tar->GetID()); SetPullingSpell(false); return; @@ -2300,11 +2300,11 @@ void Bot::AI_Process() // ENGAGED AT COMBAT RANGE // We can fight - if (atCombatRange) { - bool jitterCooldown = false; + if (at_combat_range) { + bool jitter_cooldown = false; if (m_combat_jitter_timer.GetRemainingTime() > 1 && m_combat_jitter_timer.Enabled()) { - jitterCooldown = true; + jitter_cooldown = true; } if (IsMoving() || GetCombatJitterFlag() || GetCombatOutOfRangeJitterFlag()) { @@ -2315,8 +2315,8 @@ void Bot::AI_Process() return; } - if (!jitterCooldown && AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { - DoCombatPositioning(tar, Goal, stopMeleeLevel, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behindMob, frontMob); + if (!jitter_cooldown && AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { + DoCombatPositioning(tar, Goal, stop_melee_level, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behind_mob, front_mob); return; } else { @@ -2342,7 +2342,7 @@ void Bot::AI_Process() ranged_timer.Start(); } - else if (!IsBotRanged() && GetLevel() < stopMeleeLevel) { + else if (!IsBotRanged() && GetLevel() < stop_melee_level) { if (!GetMaxMeleeRange() || !RuleB(Bots, DisableSpecialAbilitiesAtMaxMelee)) { DoClassAttacks(tar); } @@ -2509,17 +2509,17 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { NOT_HOLDING && NOT_PASSIVE ) { - XTargetAutoHaters* tempHaters; - std::vector assisteeHaters; - std::vector assisteeMembers; + XTargetAutoHaters* temp_haters; + std::vector assistee_haters; + std::vector assistee_members; bool found = false; if (bot_owner->GetAggroCount()) { - tempHaters = bot_owner->GetXTargetAutoMgr(); + temp_haters = bot_owner->GetXTargetAutoMgr(); - if (tempHaters && !tempHaters->empty()) { - assisteeHaters.emplace_back(tempHaters); - assisteeMembers.emplace_back(bot_owner); + if (temp_haters && !temp_haters->empty()) { + assistee_haters.emplace_back(temp_haters); + assistee_members.emplace_back(bot_owner); } } @@ -2528,7 +2528,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { RuleB(Bots, AllowCrossGroupRaidAssist) ) { XTargetAutoHaters* temp_xhaters = bot_owner->GetXTargetAutoMgr(); - bool assisteeFound = false; + bool assistee_found = false; if (IsRaidGrouped()) { Raid* raid = GetStoredRaid(); @@ -2546,8 +2546,8 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { continue; } - assisteeHaters.emplace_back(temp_xhaters); - assisteeMembers.emplace_back(m.member); + assistee_haters.emplace_back(temp_xhaters); + assistee_members.emplace_back(m.member); } } } @@ -2568,8 +2568,8 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { continue; } - assisteeHaters.emplace_back(temp_xhaters); - assisteeMembers.emplace_back(m->CastToClient()); + assistee_haters.emplace_back(temp_xhaters); + assistee_members.emplace_back(m->CastToClient()); } } } @@ -2583,32 +2583,32 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { Client* c = entity_list.GetClientByCharID(bot_owner->GetAssistee()); if (bot_owner->IsInGroupOrRaid(c) && c->GetAggroCount()) { - tempHaters = bot_owner->GetXTargetAutoMgr(); + temp_haters = bot_owner->GetXTargetAutoMgr(); - if (tempHaters && !tempHaters->empty()) { - assisteeHaters.emplace_back(tempHaters); - assisteeMembers.emplace_back(c); + if (temp_haters && !temp_haters->empty()) { + assistee_haters.emplace_back(temp_haters); + assistee_members.emplace_back(c); } } } } - if (!assisteeHaters.empty()) { - for (XTargetAutoHaters* xHaters : assisteeHaters) { - if (!xHaters->empty()) { - for (auto hater_iter : xHaters->get_list()) { + if (!assistee_haters.empty()) { + for (XTargetAutoHaters* x_haters : assistee_haters) { + if (!x_haters->empty()) { + for (auto hater_iter : x_haters->get_list()) { if (!hater_iter.spawn_id) { continue; } Mob* hater = nullptr; - for (Client* xMember : assisteeMembers) { + for (Client* x_member : assistee_members) { if ( - xMember && - xMember->GetBotPulling() && - xMember->GetTarget() && - (hater_iter.spawn_id == xMember->GetTarget()->GetID()) + x_member && + x_member->GetBotPulling() && + x_member->GetTarget() && + (hater_iter.spawn_id == x_member->GetTarget()->GetID()) ) { continue; } @@ -2619,7 +2619,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { hater && !hater->IsMezzed() && (DistanceSquared(hater->GetPosition(), bot_owner->GetPosition()) <= leash_distance) && - hater->CastToNPC()->IsOnHatelist(xMember) + hater->CastToNPC()->IsOnHatelist(x_member) ) { break; } @@ -2915,8 +2915,8 @@ bool Bot::TryEvade(Mob* tar) { return false; } -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bool behindMob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { - atCombatRange= false; +void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& at_combat_range, bool behind_mob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stop_melee_level) { + at_combat_range = false; p_item = GetBotItem(EQ::invslot::slotPrimary); s_item = GetBotItem(EQ::invslot::slotSecondary); @@ -2930,14 +2930,14 @@ void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& atCombatRange, bo } // Calculate melee distances - CalcMeleeDistances(tar, p_item, s_item, backstab_weapon, behindMob, melee_distance_min, melee_distance, melee_distance_max, stopMeleeLevel); + CalcMeleeDistances(tar, p_item, s_item, backstab_weapon, behind_mob, melee_distance_min, melee_distance, melee_distance_max, stop_melee_level); if (tar_distance <= melee_distance) { - atCombatRange = true; + at_combat_range = true; } } -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool backstab_weapon, bool behindMob, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stopMeleeLevel) { +void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool backstab_weapon, bool behind_mob, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stop_melee_level) { float size_mod = GetSize(); float other_size_mod = tar->GetSize(); @@ -3017,7 +3017,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it } break; case Class::Rogue: - if (behindMob && backstab_weapon) { + if (behind_mob && backstab_weapon) { if (p_item->GetItem()->IsType2HWeapon()) { melee_distance = melee_distance_max * 0.30f; } @@ -3057,25 +3057,25 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it melee_distance = melee_distance * RuleR(Bots, TauntNormalMeleeRangeDistance); } - bool isStopMeleeLevel = GetLevel() >= stopMeleeLevel; + bool is_stop_melee_level = GetLevel() >= stop_melee_level; - if (!IsTaunting() && !IsBotRanged() && !isStopMeleeLevel && GetMaxMeleeRange()) { + if (!IsTaunting() && !IsBotRanged() && !is_stop_melee_level && GetMaxMeleeRange()) { melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); } - if (isStopMeleeLevel && !IsBotRanged()) { - float desiredRange = GetBotDistanceRanged(); - melee_distance_min = std::max(melee_distance, (desiredRange / 2)); - melee_distance = std::max((melee_distance + 1), desiredRange); + if (is_stop_melee_level && !IsBotRanged()) { + float desired_range = GetBotDistanceRanged(); + melee_distance_min = std::max(melee_distance, (desired_range / 2)); + melee_distance = std::max((melee_distance + 1), desired_range); } if (IsBotRanged()) { - float minDistance = RuleI(Combat, MinRangedAttackDist); - float maxDistance = GetBotRangedValue(); - float desiredRange = GetBotDistanceRanged(); - melee_distance_min = std::max(minDistance, (desiredRange / 2)); - melee_distance = std::min(maxDistance, desiredRange); + float min_distance = RuleI(Combat, MinRangedAttackDist); + float max_distance = GetBotRangedValue(); + float desired_range = GetBotDistanceRanged(); + melee_distance_min = std::max(min_distance, (desired_range / 2)); + melee_distance = std::min(max_distance, desired_range); } } @@ -5888,14 +5888,17 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) { bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot) { bool Result = false; - SpellTargetType targetType = spells[spell_id].target_type; - if (targetType == ST_GroupClientAndPet) { + SpellTargetType target_type = spells[spell_id].target_type; + + if (target_type == ST_GroupClientAndPet) { if ((spell_id == 1768 && zone->GetZoneID() == 202) || (!IsDetrimentalSpell(spell_id))) { CastAction = SingleTarget; return true; } } + Result = Mob::DetermineSpellTargets(spell_id, spell_target, ae_center, CastAction, slot); + return Result; } @@ -6854,10 +6857,13 @@ bool Bot::IsAtRange(Mob *target) { range *= range; float targetDistance = DistanceSquaredNoZ(m_Position, target->GetPosition()); float minRuleDistance = (RuleI(Combat, MinRangedAttackDist) * RuleI(Combat, MinRangedAttackDist)); - if ((targetDistance > range) || (targetDistance < minRuleDistance)) + + if ((targetDistance > range) || (targetDistance < minRuleDistance)) { result = false; - else + } + else { result = true; + } } return result; } @@ -7266,7 +7272,7 @@ bool Bot::CheckLoreConflict(const EQ::ItemData* item) { return (m_inv.HasItemByLoreGroup(item->LoreGroup, invWhereWorn) != INVALID_INDEX); } -bool Bot::AttemptCloseBeneficialSpells(uint16 spellType) { +bool Bot::AttemptCloseBeneficialSpells(uint16 spell_type) { bool result = false; Mob* tar = nullptr; @@ -7277,14 +7283,14 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spellType) { continue; } - if (IsGroupTargetOnlyBotSpellType(spellType)) { + if (IsGroupTargetOnlyBotSpellType(spell_type)) { Raid* raid = GetStoredRaid(); if (raid && (raid->GetGroup(GetName()) == raid->GetGroup(tar->GetName()))) { continue; } } - result = AttemptAICastSpell(spellType, tar); + result = AttemptAICastSpell(spell_type, tar); if (!result) { if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { @@ -7298,7 +7304,7 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spellType) { continue; } - result = AttemptAICastSpell(spellType, tar); + result = AttemptAICastSpell(spell_type, tar); } } @@ -7575,7 +7581,7 @@ void EntityList::ScanCloseClientMobs(std::unordered_map& close_mob LogAIScanCloseDetail("Close Client Mob List Size [{}] for mob [{}]", close_mobs.size(), scanning_mob->GetCleanName()); } -uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid) { +uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool include_pets, Raid* raid) { uint8 need_healed = 0; if (HasGroup()) { @@ -7590,14 +7596,14 @@ uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid need_healed++; } - if (includePets && member->GetPet() && !member->GetPet()->IsFamiliar() && member->GetPet()->GetHPRatio() <= hpr) { + if (include_pets && member->GetPet() && !member->GetPet()->IsFamiliar() && member->GetPet()->GetHPRatio() <= hpr) { need_healed++; } } } } } - return GetNumberNeedingHealedInRaidGroup(need_healed, hpr, includePets, raid); + return GetNumberNeedingHealedInRaidGroup(need_healed, hpr, include_pets, raid); } int Bot::GetRawACNoShield(int &shield_ac) { @@ -7719,7 +7725,7 @@ int Bot::GroupLeadershipAAOffenseEnhancement() { } bool Bot::GetNeedsCured(Mob *tar) { - bool needCured = false; + bool need_cured = false; if (tar) { if (tar->FindType(SE_PoisonCounter) || tar->FindType(SE_DiseaseCounter) || tar->FindType(SE_CurseCounter) || tar->FindType(SE_CorruptionCounter)) { @@ -7732,14 +7738,14 @@ bool Bot::GetNeedsCured(Mob *tar) { continue; } - needCured = true; + need_cured = true; } } } } } - return needCured; + return need_cured; } bool Bot::GetNeedsHateRedux(Mob *tar) { @@ -7757,38 +7763,38 @@ bool Bot::GetNeedsHateRedux(Mob *tar) { } bool Bot::HasOrMayGetAggro(bool SitAggro, uint32 spell_id) { - bool mayGetAggro = false; + bool may_get_aggro = false; if (GetTarget() && GetTarget()->GetHateTop()) { - Mob* topHate = GetTarget()->GetHateTop(); - if (topHate == this) { - mayGetAggro = true; + Mob* top_hate = GetTarget()->GetHateTop(); + if (top_hate == this) { + may_get_aggro = true; } else { - uint32 myHateAmt = GetTarget()->GetHateAmount(this); - uint32 topHateAmt = GetTarget()->GetHateAmount(topHate); + uint32 my_hate_amt = GetTarget()->GetHateAmount(this); + uint32 top_hate_amt = GetTarget()->GetHateAmount(top_hate); if (SitAggro && !spell_id) { - myHateAmt *= (1 + (RuleI(Aggro, SittingAggroMod) / 100)); + my_hate_amt *= (1 + (RuleI(Aggro, SittingAggroMod) / 100)); } if (spell_id && IsValidSpell(spell_id) && GetTarget()) { - myHateAmt += CheckAggroAmount(spell_id, GetTarget()); + my_hate_amt += CheckAggroAmount(spell_id, GetTarget()); } if ( - topHateAmt < 1 || + top_hate_amt < 1 || ( - myHateAmt > 0 && - (uint8)((myHateAmt / topHateAmt) * 100) > RuleI(Bots, HasOrMayGetAggroThreshold) + my_hate_amt > 0 && + (uint8)((my_hate_amt / top_hate_amt) * 100) > RuleI(Bots, HasOrMayGetAggroThreshold) ) ) { - mayGetAggro = true; + may_get_aggro = true; } } } - return mayGetAggro; + return may_get_aggro; } void Bot::SetDefaultBotStance() { @@ -9309,45 +9315,45 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND] = { 0 }; -bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { +bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { if (!tar) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); return false; } - LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); + LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); - if (GetUltimateSpellHold(spellType, tar)) { - LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); + if (GetUltimateSpellHold(spell_type, tar)) { + LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } - if (IsPullingSpell() && IsPullingSpellType(spellType)) { //Skip remaining checks for commanded + if (IsPullingSpell() && IsPullingSpellType(spell_type)) { //Skip remaining checks for commanded return true; } - if (GetManaRatio() < GetSpellTypeMinManaLimit(spellType) || GetManaRatio() > GetSpellTypeMaxManaLimit(spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); + if (GetManaRatio() < GetSpellTypeMinManaLimit(spell_type) || GetManaRatio() > GetSpellTypeMaxManaLimit(spell_type)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } - if (GetHPRatio() < GetSpellTypeMinHPLimit(spellType) || GetHPRatio() > GetSpellTypeMaxHPLimit(spellType)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); + if (GetHPRatio() < GetSpellTypeMinHPLimit(spell_type) || GetHPRatio() > GetSpellTypeMaxHPLimit(spell_type)) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } - if (!GetUltimateSpellDelayCheck(spellType, tar)) { - LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); + if (!GetUltimateSpellDelayCheck(spell_type, tar)) { + LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } - switch (spellType) { //This will skip Threshold Checks during Precast for specific SpellTypes that are checked when acquiring new targets + switch (spell_type) { //This will skip Threshold Checks during Precast for specific SpellTypes that are checked when acquiring new targets case BotSpellTypes::Mez: case BotSpellTypes::AEMez: return true; default: - if (GetHPRatioForSpellType(spellType, tar) < GetUltimateSpellMinThreshold(spellType, tar) || GetHPRatioForSpellType(spellType, tar) > GetUltimateSpellMaxThreshold(spellType, tar)) { - LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); + if (GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellMinThreshold(spell_type, tar) || GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellMaxThreshold(spell_type, tar)) { + LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } } @@ -9355,8 +9361,8 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spellType) { return true; } -bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks, bool AECheck) { - if (doPrechecks) { +bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks, bool ae_check) { + if (prechecks) { if (!tar) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; @@ -9371,13 +9377,13 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } } - if (!PrecastChecks(tar, spellType)) { + if (!PrecastChecks(tar, spell_type)) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); return false; } } - LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spellType), (tar ? tar->GetCleanName() : "nobody")); + LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), (tar ? tar->GetCleanName() : "nobody")); if (!IsValidSpell(spell_id)) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); @@ -9465,7 +9471,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (SpellTypeRequiresTarget(spellType) && !tar) { + if (SpellTypeRequiresTarget(spell_type) && !tar) { LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } @@ -9473,7 +9479,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec if ( spells[spell_id].target_type == ST_Self && tar != this && - (spellType != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) + (spell_type != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) ) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; @@ -9530,7 +9536,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } } //LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); - if (!CanCastSpellType(spellType, spell_id, tar)) { + if (!CanCastSpellType(spell_type, spell_id, tar)) { return false; } @@ -9544,7 +9550,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec } if ( - (RequiresStackCheck(spellType) || (!RequiresStackCheck(spellType) && CalcBuffDuration(this, tar, spell_id) != 0)) + (RequiresStackCheck(spell_type) || (!RequiresStackCheck(spell_type) && CalcBuffDuration(this, tar, spell_id) != 0)) && tar->CanBuffStack(spell_id, GetLevel(), true) < 0 ) { @@ -9561,16 +9567,16 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return false; } - if (spellType == UINT16_MAX) { //AA/Forced cast checks, return here + if (spell_type == UINT16_MAX) { //AA/Forced cast checks, return here return true; } - if (!IsTaunting() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + if (!IsTaunting() && GetSpellTypeAggroCheck(spell_type) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } - if (!DoResistCheckBySpellType(tar, spell_id, spellType)) { + if (!DoResistCheckBySpellType(tar, spell_id, spell_type)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9583,15 +9589,15 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechec return true; } -bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { +bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { if (!spell_id || !tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spellType ? GetSpellTypeNameByID(spellType) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); + LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spell_type ? GetSpellTypeNameByID(spell_type) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); return false; } - uint8 botClass = GetClass(); + uint8 bot_class = GetClass(); - switch (spellType) { + switch (spell_type) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: case BotSpellTypes::PreCombatBuff: @@ -9616,7 +9622,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { return false; } - if ((spellType != BotSpellTypes::Teleport && spellType != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { + if ((spell_type != BotSpellTypes::Teleport && spell_type != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9667,7 +9673,7 @@ bool Bot::CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar) { } // Differences for each type - if (spellType != BotSpellTypes::InCombatBuff) { + if (spell_type != BotSpellTypes::InCombatBuff) { if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { for (int i = 0; i < tar->GetMaxTotalSlots(); i++) { uint32 buff_count = tar->GetMaxTotalSlots(); @@ -9740,10 +9746,9 @@ bool Bot::BotHasEnoughMana(uint16 spell_id) { return false; } - //int32 manaCost = GetActSpellCost(spell_id, spells[spell_id].mana); - int32 manaCost = spells[spell_id].mana; + int32 mana_cost = spells[spell_id].mana; - if (GetMana() < manaCost) { + if (GetMana() < mana_cost) { return false; } @@ -9756,7 +9761,7 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { } std::vector v; - uint16 targetID = tar->GetID(); + uint16 target_id = tar->GetID(); if (RuleB(Bots, CrossRaidBuffingAndHealing)) { v = GetSpellTargetList(); @@ -9782,7 +9787,7 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { } } else { - if (m->CastToBot()->casting_spell_targetid == targetID) { + if (m->CastToBot()->casting_spell_targetid == target_id) { return true; } } @@ -9805,90 +9810,90 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spell_id, int32 resist_limit) { level_mod = -level_mod; } - int32 targetResist = 0; + int32 target_resist = 0; switch (GetSpellResistType(spell_id)) { case RESIST_NONE: return true; case RESIST_MAGIC: - targetResist = tar->GetMR(); + target_resist = tar->GetMR(); break; case RESIST_COLD: - targetResist = tar->GetCR(); + target_resist = tar->GetCR(); break; case RESIST_FIRE: - targetResist = tar->GetFR(); + target_resist = tar->GetFR(); break; case RESIST_POISON: - targetResist = tar->GetPR(); + target_resist = tar->GetPR(); break; case RESIST_DISEASE: - targetResist = tar->GetDR(); + target_resist = tar->GetDR(); break; case RESIST_CORRUPTION: - targetResist = tar->GetCorrup(); + target_resist = tar->GetCorrup(); break; default: return true; } - //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), targetResist, level_mod, resist_difficulty, (targetResist + level_mod - resist_difficulty), resist_limit); - if ((targetResist + level_mod - resist_difficulty) > resist_limit) { + //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), target_resist, level_mod, resist_difficulty, (target_resist + level_mod - resist_difficulty), resist_limit); + if ((target_resist + level_mod - resist_difficulty) > resist_limit) { return false; } return true; } -bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType) { +bool Bot::DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spell_type) { if (!tar || !IsValidSpell(spell_id)) { return false; } - if (GetSpellTypeResistLimit(spellType) == 0) { + if (GetSpellTypeResistLimit(spell_type) == 0) { return true; } - return DoResistCheck(tar, spell_id, GetSpellTypeResistLimit(spellType)); + return DoResistCheck(tar, spell_id, GetSpellTypeResistLimit(spell_type)); } -bool Bot::IsValidTargetType(uint16 spell_id, int targetType, uint8 bodyType) { +bool Bot::IsValidTargetType(uint16 spell_id, int target_type, uint8 body_type) { if (!spell_id) { return false; } - switch (targetType) { + switch (target_type) { case ST_Undead: - if (bodyType == BodyType::Undead || bodyType == BodyType::SummonedUndead || bodyType == BodyType::Vampire) { + if (body_type == BodyType::Undead || body_type == BodyType::SummonedUndead || body_type == BodyType::Vampire) { return true; } break; case ST_Summoned: - if (bodyType == BodyType::Summoned || bodyType == BodyType::Summoned2 || bodyType == BodyType::Summoned3) { + if (body_type == BodyType::Summoned || body_type == BodyType::Summoned2 || body_type == BodyType::Summoned3) { return true; } break; case ST_Animal: - if (bodyType == BodyType::Animal) { + if (body_type == BodyType::Animal) { return true; } break; case ST_Plant: - if (bodyType == BodyType::Plant) { + if (body_type == BodyType::Plant) { return true; } break; case ST_Giant: - if (bodyType == BodyType::Giant || bodyType == BodyType::RaidGiant) { + if (body_type == BodyType::Giant || body_type == BodyType::RaidGiant) { return true; } break; case ST_Dragon: - if (bodyType == BodyType::Dragon || bodyType == BodyType::VeliousDragon) { + if (body_type == BodyType::Dragon || body_type == BodyType::VeliousDragon) { return true; } @@ -9979,104 +9984,104 @@ bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id) { return true; } -void Bot::SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue) { - switch (settingType) { +void Bot::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value) { + switch (setting_type) { case BotSettingCategories::BaseSetting: - SetBotBaseSetting(botSetting, settingValue); + SetBotBaseSetting(bot_setting, setting_value); break; case BotSettingCategories::SpellHold: - SetSpellHold(botSetting, settingValue); + SetSpellHold(bot_setting, setting_value); break; case BotSettingCategories::SpellDelay: - SetSpellDelay(botSetting, settingValue); + SetSpellDelay(bot_setting, setting_value); break; case BotSettingCategories::SpellMinThreshold: - SetSpellMinThreshold(botSetting, settingValue); + SetSpellMinThreshold(bot_setting, setting_value); break; case BotSettingCategories::SpellMaxThreshold: - SetSpellMaxThreshold(botSetting, settingValue); + SetSpellMaxThreshold(bot_setting, setting_value); break; case BotSettingCategories::SpellTypeAggroCheck: - SetSpellTypeAggroCheck(botSetting, settingValue); + SetSpellTypeAggroCheck(bot_setting, setting_value); break; case BotSettingCategories::SpellTypeMinManaPct: - SetSpellTypeMinManaLimit(botSetting, settingValue); + SetSpellTypeMinManaLimit(bot_setting, setting_value); break; case BotSettingCategories::SpellTypeMaxManaPct: - SetSpellTypeMaxManaLimit(botSetting, settingValue); + SetSpellTypeMaxManaLimit(bot_setting, setting_value); break; case BotSettingCategories::SpellTypeMinHPPct: - SetSpellTypeMinHPLimit(botSetting, settingValue); + SetSpellTypeMinHPLimit(bot_setting, setting_value); break; case BotSettingCategories::SpellTypeMaxHPPct: - SetSpellTypeMaxHPLimit(botSetting, settingValue); + SetSpellTypeMaxHPLimit(bot_setting, setting_value); break; case BotSettingCategories::SpellTypeIdlePriority: - SetSpellTypePriority(botSetting, BotPriorityCategories::Idle, settingValue); + SetSpellTypePriority(bot_setting, BotPriorityCategories::Idle, setting_value); break; case BotSettingCategories::SpellTypeEngagedPriority: - SetSpellTypePriority(botSetting, BotPriorityCategories::Engaged, settingValue); + SetSpellTypePriority(bot_setting, BotPriorityCategories::Engaged, setting_value); break; case BotSettingCategories::SpellTypePursuePriority: - SetSpellTypePriority(botSetting, BotPriorityCategories::Pursue, settingValue); + SetSpellTypePriority(bot_setting, BotPriorityCategories::Pursue, setting_value); break; case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - SetSpellTypeAEOrGroupTargetCount(botSetting, settingValue); + SetSpellTypeAEOrGroupTargetCount(bot_setting, setting_value); break; } } -void Bot::SetBotBaseSetting(uint16 botSetting, int settingValue) { - switch (botSetting) { +void Bot::SetBotBaseSetting(uint16 bot_setting, int setting_value) { + switch (bot_setting) { case BotBaseSettings::ExpansionBitmask: - SetExpansionBitmask(settingValue); + SetExpansionBitmask(setting_value); break; case BotBaseSettings::ShowHelm: - SetShowHelm(settingValue); + SetShowHelm(setting_value); break; case BotBaseSettings::FollowDistance: - SetFollowDistance(EQ::Clamp(static_cast(settingValue * settingValue), static_cast(1), static_cast((RuleI(Bots, MaxFollowDistance) * RuleI(Bots, MaxFollowDistance))))); + SetFollowDistance(EQ::Clamp(static_cast(setting_value * setting_value), static_cast(1), static_cast((RuleI(Bots, MaxFollowDistance) * RuleI(Bots, MaxFollowDistance))))); break; case BotBaseSettings::StopMeleeLevel: - SetStopMeleeLevel(settingValue); + SetStopMeleeLevel(setting_value); break; case BotBaseSettings::EnforceSpellSettings: - SetBotEnforceSpellSetting(settingValue); + SetBotEnforceSpellSetting(setting_value); break; case BotBaseSettings::RangedSetting: - SetBotRangedSetting(settingValue); + SetBotRangedSetting(setting_value); break; case BotBaseSettings::PetSetTypeSetting: - SetPetChooserID(settingValue); + SetPetChooserID(setting_value); break; case BotBaseSettings::BehindMob: - SetBehindMob(settingValue); + SetBehindMob(setting_value); break; case BotBaseSettings::DistanceRanged: - SetBotDistanceRanged(settingValue); + SetBotDistanceRanged(setting_value); break; case BotBaseSettings::IllusionBlock: - SetIllusionBlock(settingValue); + SetIllusionBlock(setting_value); break; case BotBaseSettings::MaxMeleeRange: - SetMaxMeleeRange(settingValue); + SetMaxMeleeRange(setting_value); break; case BotBaseSettings::MedInCombat: - SetMedInCombat(settingValue); + SetMedInCombat(setting_value); break; case BotBaseSettings::SitHPPct: - SetSitHPPct(settingValue); + SetSitHPPct(setting_value); break; case BotBaseSettings::SitManaPct: - SetSitManaPct(settingValue); + SetSitManaPct(setting_value); break; default: break; } } -int Bot::GetBotBaseSetting(uint16 botSetting) { - switch (botSetting) { +int Bot::GetBotBaseSetting(uint16 bot_setting) { + switch (bot_setting) { case BotBaseSettings::ExpansionBitmask: return GetExpansionBitmask(); case BotBaseSettings::ShowHelm: @@ -10112,8 +10117,8 @@ int Bot::GetBotBaseSetting(uint16 botSetting) { return true; } -int Bot::GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance) { - switch (botSetting) { +int Bot::GetDefaultBotBaseSetting(uint16 bot_setting, uint8 stance) { + switch (bot_setting) { case BotBaseSettings::ExpansionBitmask: return RuleI(Bots, BotExpansionSettings); case BotBaseSettings::ShowHelm: @@ -10172,11 +10177,11 @@ int Bot::GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance) { void Bot::LoadDefaultBotSettings() { _spellSettings.clear(); - uint8 botStance = GetBotStance(); + uint8 bot_stance = GetBotStance(); for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { - SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, botStance)); - LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, botStance)); + SetBotBaseSetting(i, GetDefaultSetting(BotSettingCategories::BaseSetting, i, bot_stance)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), GetBotSettingCategoryName(i), i, GetDefaultBotBaseSetting(i, bot_stance)); } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -10185,192 +10190,192 @@ void Bot::LoadDefaultBotSettings() { t.spellType = i; t.shortName = GetSpellTypeShortNameByID(i); t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i, botStance); - t.delay = GetDefaultSpellDelay(i, botStance); - t.minThreshold = GetDefaultSpellMinThreshold(i, botStance); - t.maxThreshold = GetDefaultSpellMaxThreshold(i, botStance); - t.resistLimit = GetDefaultSpellTypeResistLimit(i, botStance); - t.aggroCheck = GetDefaultSpellTypeAggroCheck(i, botStance); - t.minManaPct = GetDefaultSpellTypeMinManaLimit(i, botStance); - t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i, botStance); - t.minHPPct = GetDefaultSpellTypeMinHPLimit(i, botStance); - t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i, botStance); - t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass(), botStance); - t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass(), botStance); - t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass(), botStance); - t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance); + t.hold = GetDefaultSpellHold(i, bot_stance); + t.delay = GetDefaultSpellDelay(i, bot_stance); + t.minThreshold = GetDefaultSpellMinThreshold(i, bot_stance); + t.maxThreshold = GetDefaultSpellMaxThreshold(i, bot_stance); + t.resistLimit = GetDefaultSpellTypeResistLimit(i, bot_stance); + t.aggroCheck = GetDefaultSpellTypeAggroCheck(i, bot_stance); + t.minManaPct = GetDefaultSpellTypeMinManaLimit(i, bot_stance); + t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i, bot_stance); + t.minHPPct = GetDefaultSpellTypeMinHPLimit(i, bot_stance); + t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i, bot_stance); + t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass(), bot_stance); + t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass(), bot_stance); + t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass(), bot_stance); + t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); t.recastTimer.Start(); _spellSettings.push_back(t); - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(botStance), botStance); - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, botStance), GetDefaultSpellDelay(i, botStance), GetDefaultSpellMinThreshold(i, botStance), GetDefaultSpellMaxThreshold(i, botStance)); - LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, botStance), GetDefaultSpellTypeMinManaLimit(i, botStance), GetDefaultSpellTypeMaxManaLimit(i, botStance), GetDefaultSpellTypeMinHPLimit(i, botStance), GetDefaultSpellTypeMaxHPLimit(i, botStance)); - LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), botStance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), botStance), GetDefaultSpellTypePursuePriority(i, GetClass(), botStance), GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(bot_stance), bot_stance); + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, bot_stance), GetDefaultSpellDelay(i, bot_stance), GetDefaultSpellMinThreshold(i, bot_stance), GetDefaultSpellMaxThreshold(i, bot_stance)); + LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, bot_stance), GetDefaultSpellTypeMinManaLimit(i, bot_stance), GetDefaultSpellTypeMaxManaLimit(i, bot_stance), GetDefaultSpellTypeMinHPLimit(i, bot_stance), GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); } } -void Bot::SetBotSpellRecastTimer(uint16 spellType, Mob* tar, bool preCast) { +void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) { if (!tar) { return; } - if (!preCast && IsBotSpellTypeOtherBeneficial(spellType)) { + if (!precast && IsBotSpellTypeOtherBeneficial(spell_type)) { return; } - uint32 addedDelay = 0; + uint32 added_delay = 0; - switch (spellType) { //Additional delays + switch (spell_type) { //Additional delays case BotSpellTypes::Mez: - addedDelay = RuleI(Bots, MezSuccessDelay); + added_delay = RuleI(Bots, MezSuccessDelay); break; case BotSpellTypes::AEMez: - addedDelay = RuleI(Bots, AEMezSuccessDelay); + added_delay = RuleI(Bots, AEMezSuccessDelay); break; } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + return tar->GetOwner()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); } - else if (IsBotSpellTypeOtherBeneficial(spellType)) { - tar->SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + else if (IsBotSpellTypeOtherBeneficial(spell_type)) { + tar->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); } else { - SetSpellTypeRecastTimer(spellType, (GetUltimateSpellDelay(spellType, tar) + addedDelay)); + SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); } } -BotSpell Bot::GetSpellByHealType(uint16 spellType, Mob* tar) { - switch (spellType) { +BotSpell Bot::GetSpellByHealType(uint16 spell_type, Mob* tar) { + switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: - return GetBestBotSpellForVeryFastHeal(this, tar, spellType); + return GetBestBotSpellForVeryFastHeal(this, tar, spell_type); case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: - return GetBestBotSpellForFastHeal(this, tar, spellType); + return GetBestBotSpellForFastHeal(this, tar, spell_type); case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: - return GetBestBotSpellForRegularSingleTargetHeal(this, tar, spellType); + return GetBestBotSpellForRegularSingleTargetHeal(this, tar, spell_type); case BotSpellTypes::GroupHeals: - return GetBestBotSpellForGroupHeal(this, tar, spellType); + return GetBestBotSpellForGroupHeal(this, tar, spell_type); case BotSpellTypes::CompleteHeal: case BotSpellTypes::PetCompleteHeals: - return GetBestBotSpellForPercentageHeal(this, tar, spellType); + return GetBestBotSpellForPercentageHeal(this, tar, spell_type); case BotSpellTypes::GroupCompleteHeals: - return GetBestBotSpellForGroupCompleteHeal(this, tar, spellType); + return GetBestBotSpellForGroupCompleteHeal(this, tar, spell_type); case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: - return GetBestBotSpellForHealOverTime(this, tar, spellType); + return GetBestBotSpellForHealOverTime(this, tar, spell_type); case BotSpellTypes::GroupHoTHeals: - return GetBestBotSpellForGroupHealOverTime(this, tar, spellType); + return GetBestBotSpellForGroupHealOverTime(this, tar, spell_type); } } -uint16 Bot::GetSpellTypePriority(uint16 spellType, uint8 priorityType) { - switch (priorityType) { +uint16 Bot::GetSpellTypePriority(uint16 spell_type, uint8 priority_type) { + switch (priority_type) { case BotPriorityCategories::Idle: - return _spellSettings[spellType].idlePriority; + return _spellSettings[spell_type].idlePriority; case BotPriorityCategories::Engaged: - return _spellSettings[spellType].engagedPriority; + return _spellSettings[spell_type].engagedPriority; case BotPriorityCategories::Pursue: - return _spellSettings[spellType].pursuePriority; + return _spellSettings[spell_type].pursuePriority; default: return 0; } } -int Bot::GetDefaultSetting(uint16 settingCategory, uint16 settingType, uint8 stance) { - switch (settingCategory) { +int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 stance) { + switch (setting_category) { case BotSettingCategories::BaseSetting: - return GetDefaultBotBaseSetting(settingType, stance); + return GetDefaultBotBaseSetting(setting_type, stance); case BotSettingCategories::SpellHold: - return GetDefaultSpellHold(settingType, stance); + return GetDefaultSpellHold(setting_type, stance); case BotSettingCategories::SpellDelay: - return GetDefaultSpellDelay(settingType, stance); + return GetDefaultSpellDelay(setting_type, stance); case BotSettingCategories::SpellMinThreshold: - return GetDefaultSpellMinThreshold(settingType, stance); + return GetDefaultSpellMinThreshold(setting_type, stance); case BotSettingCategories::SpellMaxThreshold: - return GetDefaultSpellMaxThreshold(settingType, stance); + return GetDefaultSpellMaxThreshold(setting_type, stance); case BotSettingCategories::SpellTypeAggroCheck: - return GetDefaultSpellTypeAggroCheck(settingType, stance); + return GetDefaultSpellTypeAggroCheck(setting_type, stance); case BotSettingCategories::SpellTypeMinManaPct: - return GetDefaultSpellTypeMinManaLimit(settingType, stance); + return GetDefaultSpellTypeMinManaLimit(setting_type, stance); case BotSettingCategories::SpellTypeMaxManaPct: - return GetDefaultSpellTypeMaxManaLimit(settingType, stance); + return GetDefaultSpellTypeMaxManaLimit(setting_type, stance); case BotSettingCategories::SpellTypeMinHPPct: - return GetDefaultSpellTypeMinHPLimit(settingType, stance); + return GetDefaultSpellTypeMinHPLimit(setting_type, stance); case BotSettingCategories::SpellTypeMaxHPPct: - return GetDefaultSpellTypeMaxHPLimit(settingType, stance); + return GetDefaultSpellTypeMaxHPLimit(setting_type, stance); case BotSettingCategories::SpellTypeIdlePriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Idle, GetClass(), stance); + return GetDefaultSpellTypePriority(setting_type, BotPriorityCategories::Idle, GetClass(), stance); case BotSettingCategories::SpellTypeEngagedPriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Engaged, GetClass(), stance); + return GetDefaultSpellTypePriority(setting_type, BotPriorityCategories::Engaged, GetClass(), stance); case BotSettingCategories::SpellTypePursuePriority: - return GetDefaultSpellTypePriority(settingType, BotPriorityCategories::Pursue, GetClass(), stance); + return GetDefaultSpellTypePriority(setting_type, BotPriorityCategories::Pursue, GetClass(), stance); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - return GetDefaultSpellTypeAEOrGroupTargetCount(settingType, stance); + return GetDefaultSpellTypeAEOrGroupTargetCount(setting_type, stance); default: break; } } -int Bot::GetSetting(uint16 settingCategory, uint16 settingType) { - switch (settingCategory) { +int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { + switch (setting_category) { case BotSettingCategories::BaseSetting: - return GetBotBaseSetting(settingType); + return GetBotBaseSetting(setting_type); case BotSettingCategories::SpellHold: - return GetSpellHold(settingType); + return GetSpellHold(setting_type); case BotSettingCategories::SpellDelay: - return GetSpellDelay(settingType); + return GetSpellDelay(setting_type); case BotSettingCategories::SpellMinThreshold: - return GetSpellMinThreshold(settingType); + return GetSpellMinThreshold(setting_type); case BotSettingCategories::SpellMaxThreshold: - return GetSpellMaxThreshold(settingType); + return GetSpellMaxThreshold(setting_type); case BotSettingCategories::SpellTypeAggroCheck: - return GetSpellTypeAggroCheck(settingType); + return GetSpellTypeAggroCheck(setting_type); case BotSettingCategories::SpellTypeMinManaPct: - return GetSpellTypeMinManaLimit(settingType); + return GetSpellTypeMinManaLimit(setting_type); case BotSettingCategories::SpellTypeMaxManaPct: - return GetSpellTypeMaxManaLimit(settingType); + return GetSpellTypeMaxManaLimit(setting_type); case BotSettingCategories::SpellTypeMinHPPct: - return GetSpellTypeMinHPLimit(settingType); + return GetSpellTypeMinHPLimit(setting_type); case BotSettingCategories::SpellTypeMaxHPPct: - return GetSpellTypeMaxHPLimit(settingType); + return GetSpellTypeMaxHPLimit(setting_type); case BotSettingCategories::SpellTypeIdlePriority: - return GetSpellTypePriority(settingType, BotPriorityCategories::Idle); + return GetSpellTypePriority(setting_type, BotPriorityCategories::Idle); case BotSettingCategories::SpellTypeEngagedPriority: - return GetSpellTypePriority(settingType, BotPriorityCategories::Engaged); + return GetSpellTypePriority(setting_type, BotPriorityCategories::Engaged); case BotSettingCategories::SpellTypePursuePriority: - return GetSpellTypePriority(settingType, BotPriorityCategories::Pursue); + return GetSpellTypePriority(setting_type, BotPriorityCategories::Pursue); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - return GetSpellTypeAEOrGroupTargetCount(settingType); + return GetSpellTypeAEOrGroupTargetCount(setting_type); default: break; } } -uint16 Bot::GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass, uint8 stance) { - switch (priorityType) { +uint16 Bot::GetDefaultSpellTypePriority(uint16 spell_type, uint8 priority_type, uint8 bot_class, uint8 stance) { + switch (priority_type) { case BotPriorityCategories::Idle: - return GetDefaultSpellTypeIdlePriority(spellType, botClass, stance); + return GetDefaultSpellTypeIdlePriority(spell_type, bot_class, stance); case BotPriorityCategories::Engaged: - return GetDefaultSpellTypeEngagedPriority(spellType, botClass, stance); + return GetDefaultSpellTypeEngagedPriority(spell_type, bot_class, stance); case BotPriorityCategories::Pursue: - return GetDefaultSpellTypePursuePriority(spellType, botClass, stance); + return GetDefaultSpellTypePursuePriority(spell_type, bot_class, stance); default: return 0; } } -uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance) { - if (!IsBotSpellTypeBeneficial(spellType)) { +uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spell_type, uint8 bot_class, uint8 stance) { + if (!IsBotSpellTypeBeneficial(spell_type)) { return 0; } uint16 priority = 0; - switch (spellType) { + switch (spell_type) { case BotSpellTypes::VeryFastHeals: priority = 1; @@ -10484,8 +10489,8 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, ui return priority; } -uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, uint8 stance) { - switch (spellType) { +uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spell_type, uint8 bot_class, uint8 stance) { + switch (spell_type) { case BotSpellTypes::Escape: return 1; case BotSpellTypes::VeryFastHeals: @@ -10579,8 +10584,8 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, } } -uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint8 stance) { - switch (spellType) { +uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spell_type, uint8 bot_class, uint8 stance) { + switch (spell_type) { case BotSpellTypes::Escape: return 1; case BotSpellTypes::VeryFastHeals: @@ -10632,9 +10637,9 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, } } -uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance) { +uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spell_type, uint8 stance) { - if (!IsBotSpellTypeBeneficial(spellType)) { + if (!IsBotSpellTypeBeneficial(spell_type)) { return RuleI(Bots, SpellResistLimit); } else { @@ -10642,7 +10647,7 @@ uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance) { } } -bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance) { +bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spell_type, uint8 stance) { switch (stance) { case Stance::AEBurn: case Stance::Burn: @@ -10651,7 +10656,7 @@ bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance) { break; } - switch (spellType) { + switch (spell_type) { case BotSpellTypes::Nuke: case BotSpellTypes::Root: case BotSpellTypes::Snare: @@ -10677,12 +10682,12 @@ bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance) { } } -uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spellType, uint8 stance) { +uint8 Bot::GetDefaultSpellTypeMinManaLimit(uint16 spell_type, uint8 stance) { return 0; } -uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType, uint8 stance) { - switch (spellType) { +uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spell_type, uint8 stance) { + switch (spell_type) { case BotSpellTypes::InCombatBuff: if (GetClass() == Class::Shaman) { return 75; @@ -10696,8 +10701,8 @@ uint8 Bot::GetDefaultSpellTypeMaxManaLimit(uint16 spellType, uint8 stance) { return 100; } -uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType, uint8 stance) { - switch (spellType) { +uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spell_type, uint8 stance) { + switch (spell_type) { case BotSpellTypes::InCombatBuff: if (GetClass() == Class::Shaman) { return 40; @@ -10711,99 +10716,99 @@ uint8 Bot::GetDefaultSpellTypeMinHPLimit(uint16 spellType, uint8 stance) { return 0; } -uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spellType, uint8 stance) { +uint8 Bot::GetDefaultSpellTypeMaxHPLimit(uint16 spell_type, uint8 stance) { return 100; } -uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType, uint8 stance) { - if (IsAEBotSpellType(spellType)) { +uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 stance) { + if (IsAEBotSpellType(spell_type)) { return RuleI(Bots, MinTargetsForAESpell); } - if (IsGroupBotSpellType(spellType)) { + if (IsGroupBotSpellType(spell_type)) { return RuleI(Bots, MinTargetsForGroupSpell); } return 0; } -void Bot::SetSpellTypePriority(uint16 spellType, uint8 priorityType, uint16 priority) { - switch (priorityType) { +void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority) { + switch (priority_type) { case BotPriorityCategories::Idle: - _spellSettings[spellType].idlePriority = priority; + _spellSettings[spell_type].idlePriority = priority; break; case BotPriorityCategories::Engaged: - _spellSettings[spellType].engagedPriority = priority; + _spellSettings[spell_type].engagedPriority = priority; break; case BotPriorityCategories::Pursue: - _spellSettings[spellType].pursuePriority = priority; + _spellSettings[spell_type].pursuePriority = priority; break; default: return; } } -void Bot::SetSpellTypeResistLimit(uint16 spellType, uint16 resistLimit) { - _spellSettings[spellType].resistLimit = resistLimit; +void Bot::SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit) { + _spellSettings[spell_type].resistLimit = resist_limit; } -void Bot::SetSpellTypeAggroCheck(uint16 spellType, bool aggroCheck) { - _spellSettings[spellType].aggroCheck = aggroCheck; +void Bot::SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check) { + _spellSettings[spell_type].aggroCheck = aggro_check; } -void Bot::SetSpellTypeMinManaLimit(uint16 spellType, uint8 manaLimit) { - _spellSettings[spellType].minManaPct = manaLimit; +void Bot::SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit) { + _spellSettings[spell_type].minManaPct = mana_limit; } -void Bot::SetSpellTypeMaxManaLimit(uint16 spellType, uint8 manaLimit) { - _spellSettings[spellType].maxManaPct = manaLimit; +void Bot::SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit) { + _spellSettings[spell_type].maxManaPct = mana_limit; } -void Bot::SetSpellTypeMinHPLimit(uint16 spellType, uint8 hpLimit) { - _spellSettings[spellType].minHPPct = hpLimit; +void Bot::SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit) { + _spellSettings[spell_type].minHPPct = hp_limit; } -void Bot::SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit) { - _spellSettings[spellType].maxHPPct = hpLimit; +void Bot::SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit) { + _spellSettings[spell_type].maxHPPct = hp_limit; } -void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount) { - _spellSettings[spellType].AEOrGroupTargetCount = targetCount; +void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count) { + _spellSettings[spell_type].AEOrGroupTargetCount = target_count; } -std::list Bot::GetSpellTypesPrioritized(uint8 priorityType) { - std::list castOrder; - std::list tempCastOrder; +std::list Bot::GetSpellTypesPrioritized(uint8 priority_type) { + std::list cast_order; + std::list temp_cast_order; for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; i++) { BotSpellTypeOrder typeSettings = { .spellType = i, - .priority = GetSpellTypePriority(i, priorityType) + .priority = GetSpellTypePriority(i, priority_type) }; - castOrder.emplace_back(typeSettings); + cast_order.emplace_back(typeSettings); } - for (auto& currentType : castOrder) { + for (auto& currentType : cast_order) { if (currentType.priority != 0) { - tempCastOrder.emplace_back(currentType); + temp_cast_order.emplace_back(currentType); } } - castOrder = tempCastOrder; + cast_order = temp_cast_order; - if (castOrder.size() > 1) { - castOrder.sort( + if (cast_order.size() > 1) { + cast_order.sort( [](BotSpellTypeOrder const& l, BotSpellTypeOrder const& r) { return l.priority < r.priority; } ); } - return castOrder; + return cast_order; } -bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { +bool Bot::AttemptAICastSpell(uint16 spell_type, Mob* tar) { bool result = false; if (!tar) { @@ -10811,28 +10816,28 @@ bool Bot::AttemptAICastSpell(uint16 spellType, Mob* tar) { tar = GetTarget(); } else { - if (!IsBotSpellTypeBeneficial(spellType)) { + if (!IsBotSpellTypeBeneficial(spell_type)) { return result; } } } - if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spellType) && HasOrMayGetAggro(IsSitting())) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spellType)); + if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spell_type) && HasOrMayGetAggro(IsSitting())) { + LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spell_type)); return result; } - if (IsBotSpellTypeBeneficial(spellType)) { + if (IsBotSpellTypeBeneficial(spell_type)) { if (GetClass() == Class::Bard) { tar = this; } - if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!PrecastChecks(tar, spell_type) || !AICastSpell(tar, GetChanceToCastBySpellType(spell_type), spell_type)) { return result; } } else { - if (!PrecastChecks(tar, spellType) || !AICastSpell(tar, GetChanceToCastBySpellType(spellType), spellType)) { + if (!PrecastChecks(tar, spell_type) || !AICastSpell(tar, GetChanceToCastBySpellType(spell_type), spell_type)) { return result; } } @@ -10937,7 +10942,7 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { return true; } -bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc) { +bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { if (!IsValidSpell(spell_id)) { return false; } @@ -10987,7 +10992,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc) { return false; } - if (!isDisc) { + if (!is_disc) { if (!CheckSpellRecastTimer(spell_id)) { return false; } @@ -11048,7 +11053,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc) { int timer_duration = 0; - if (!isDisc) { + if (!is_disc) { timer_duration = CalcBuffDuration(tar, this, spell_id); if (timer_duration) { @@ -11077,7 +11082,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc) { } } - if (!isDisc) { + if (!is_disc) { SetSpellRecastTimer(spell_id, timer_duration); } else { @@ -11090,8 +11095,8 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc) { return false; } -uint16 Bot::GetParentSpellType(uint16 spellType) { - switch (spellType) { +uint16 Bot::GetParentSpellType(uint16 spell_type) { + switch (spell_type) { case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::AEStun: @@ -11175,22 +11180,22 @@ uint16 Bot::GetParentSpellType(uint16 spellType) { case BotSpellTypes::PreCombatBuffSong: case BotSpellTypes::Resurrect: default: - return spellType; + return spell_type; } - return spellType; + return spell_type; } -bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { - if (IsAEBotSpellType(spellType) && !IsAnyAESpell(spell_id)) { +bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { + if (IsAEBotSpellType(spell_type) && !IsAnyAESpell(spell_id)) { return false; } - if (IsGroupBotSpellType(spellType) && !IsGroupSpell(spell_id)) { + if (IsGroupBotSpellType(spell_type) && !IsGroupSpell(spell_id)) { return false; } - switch (spellType) { + switch (spell_type) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id)) { @@ -11244,8 +11249,8 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id) { return true; } -void Bot::SetCastedSpellType(uint16 spellType) { - _castedSpellType = spellType; +void Bot::SetCastedSpellType(uint16 spell_type) { + _castedSpellType = spell_type; } void Bot::DoFaceCheckWithJitter(Mob* tar) { @@ -11305,13 +11310,13 @@ void Bot::SetCombatJitter() { void Bot::DoCombatPositioning( Mob* tar, glm::vec3 Goal, - bool stopMeleeLevel, + bool stop_melee_level, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, - bool behindMob, - bool frontMob + bool behind_mob, + bool front_mob ) { if (HasTargetReflection()) { if (!IsTaunting() && !tar->IsFeared() && !tar->IsStunned()) { @@ -11328,7 +11333,7 @@ void Bot::DoCombatPositioning( } } } - else if (IsTaunting() && ((tar_distance < melee_distance_min) || !frontMob)) { // Back up any bots that are too close + else if (IsTaunting() && ((tar_distance < melee_distance_min) || !front_mob)) { // Back up any bots that are too close if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, false, IsTaunting())) { RunToGoalWithJitter(Goal); @@ -11339,39 +11344,39 @@ void Bot::DoCombatPositioning( else { if (!tar->IsFeared()) { if (IsTaunting()) { // Taunting adjustments - Mob* mobTar = tar->GetTarget(); + Mob* mob_tar = tar->GetTarget(); - if (!mobTar || mobTar == nullptr) { + if (!mob_tar || mob_tar == nullptr) { DoFaceCheckNoJitter(tar); return; } if (RuleB(Bots, TauntingBotsFollowTopHate)) { // If enabled, taunting bots will stick to top hate - if (Distance(m_Position, mobTar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate)) { - Goal = mobTar->GetPosition(); + if (Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate)) { + Goal = mob_tar->GetPosition(); RunToGoalWithJitter(Goal); return; } } else { // Otherwise, stick to any other bots that are taunting - if (mobTar->IsBot() && mobTar->CastToBot()->IsTaunting() && (Distance(m_Position, mobTar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))) { - Goal = mobTar->GetPosition(); + if (mob_tar->IsBot() && mob_tar->CastToBot()->IsTaunting() && (Distance(m_Position, mob_tar->GetPosition()) > RuleI(Bots, DistanceTauntingBotsStickMainHate))) { + Goal = mob_tar->GetPosition(); RunToGoalWithJitter(Goal); return; } } } - else if (tar_distance < melee_distance_min || (GetBehindMob() && !behindMob) || (IsTaunting() && !frontMob)|| !HasRequiredLoSForPositioning(tar)) { // Regular adjustment + else if (tar_distance < melee_distance_min || (GetBehindMob() && !behind_mob) || (IsTaunting() && !front_mob)|| !HasRequiredLoSForPositioning(tar)) { // Regular adjustment if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, GetBehindMob(), IsTaunting())) { RunToGoalWithJitter(Goal); return; } } - else if (tar->IsEnraged() && !IsTaunting() && !stopMeleeLevel && !behindMob) { // Move non-taunting melee bots behind target during enrage + else if (tar->IsEnraged() && !IsTaunting() && !stop_melee_level && !behind_mob) { // Move non-taunting melee bots behind target during enrage if (PlotBotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z, melee_distance_min, melee_distance, true)) { RunToGoalWithJitter(Goal); @@ -11418,19 +11423,19 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } -bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar) { - int spellRange = botCaster->GetActSpellRange(spell_id, spells[spell_id].range); - int spellAERange = botCaster->GetActSpellRange(spell_id, spells[spell_id].aoe_range); - int targetCount = 0; +bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar) { + int spell_range = caster->GetActSpellRange(spell_id, spells[spell_id].range); + int spell_ae_range = caster->GetActSpellRange(spell_id, spells[spell_id].aoe_range); + int target_count = 0; - for (auto& close_mob : botCaster->m_close_mobs) { + for (auto& close_mob : caster->m_close_mobs) { Mob* m = close_mob.second; if (tar == m) { continue; } - switch (spellType) { + switch (spell_type) { case BotSpellTypes::AEDispel: if (m->GetSpecialAbility(SpecialAbility::DispellImmunity)) { continue; @@ -11465,7 +11470,7 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo break; } - if (!m->IsNPC() || (!IsCommandedSpell() && !m->CastToNPC()->IsOnHatelist(botCaster->GetOwner()))) { + if (!m->IsNPC() || (!IsCommandedSpell() && !m->CastToNPC()->IsOnHatelist(caster->GetOwner()))) { continue; } @@ -11475,27 +11480,27 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo if (IsPBAESpell(spell_id)) { if ( - spellAERange >= Distance(botCaster->GetPosition(), m->GetPosition()) && - botCaster->CastChecks(spell_id, m, spellType, true, true) + spell_ae_range >= Distance(caster->GetPosition(), m->GetPosition()) && + caster->CastChecks(spell_id, m, spell_type, true, true) ) { - ++targetCount; + ++target_count; } } else { - if (!tar || spellRange < Distance(botCaster->GetPosition(), tar->GetPosition()) || !DoLosChecks(this, m)) { + if (!tar || spell_range < Distance(caster->GetPosition(), tar->GetPosition()) || !DoLosChecks(this, m)) { continue; } if ( - spellAERange >= Distance(tar->GetPosition(), m->GetPosition()) && - botCaster->CastChecks(spell_id, m, spellType, true, true) + spell_ae_range >= Distance(tar->GetPosition(), m->GetPosition()) && + caster->CastChecks(spell_id, m, spell_type, true, true) ) { - ++targetCount; + ++target_count; } } } - if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { return false; } @@ -11504,8 +11509,8 @@ bool Bot::HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mo return true; } -void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { - switch (settingType) { +void Bot::CopySettings(Bot* to, uint8 setting_type, uint16 spell_type) { + switch (setting_type) { case BotSettingCategories::BaseSetting: for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { to->SetBotBaseSetting(i, GetBotBaseSetting(i)); @@ -11513,8 +11518,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellHold: - if (spellType != UINT16_MAX) { - to->SetSpellHold(spellType, GetSpellHold(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellHold(spell_type, GetSpellHold(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11524,8 +11529,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellDelay: - if (spellType != UINT16_MAX) { - to->SetSpellDelay(spellType, GetSpellDelay(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellDelay(spell_type, GetSpellDelay(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11535,8 +11540,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellMinThreshold: - if (spellType != UINT16_MAX) { - to->SetSpellMinThreshold(spellType, GetSpellMinThreshold(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellMinThreshold(spell_type, GetSpellMinThreshold(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11546,8 +11551,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellMaxThreshold: - if (spellType != UINT16_MAX) { - to->SetSpellMaxThreshold(spellType, GetSpellMaxThreshold(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellMaxThreshold(spell_type, GetSpellMaxThreshold(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11557,8 +11562,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeAggroCheck: - if (spellType != UINT16_MAX) { - to->SetSpellTypeAggroCheck(spellType, GetSpellTypeAggroCheck(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypeAggroCheck(spell_type, GetSpellTypeAggroCheck(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11568,8 +11573,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeMinManaPct: - if (spellType != UINT16_MAX) { - to->SetSpellTypeMinManaLimit(spellType, GetSpellTypeMinManaLimit(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypeMinManaLimit(spell_type, GetSpellTypeMinManaLimit(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11579,8 +11584,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeMaxManaPct: - if (spellType != UINT16_MAX) { - to->SetSpellTypeMaxManaLimit(spellType, GetSpellTypeMaxManaLimit(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypeMaxManaLimit(spell_type, GetSpellTypeMaxManaLimit(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11590,8 +11595,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeMinHPPct: - if (spellType != UINT16_MAX) { - to->SetSpellTypeMinHPLimit(spellType, GetSpellTypeMinHPLimit(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypeMinHPLimit(spell_type, GetSpellTypeMinHPLimit(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11601,8 +11606,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeMaxHPPct: - if (spellType != UINT16_MAX) { - to->SetSpellTypeMaxHPLimit(spellType, GetSpellTypeMaxHPLimit(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypeMaxHPLimit(spell_type, GetSpellTypeMaxHPLimit(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11612,8 +11617,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeIdlePriority: - if (spellType != UINT16_MAX) { - to->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, GetSpellTypePriority(spellType, BotPriorityCategories::Idle)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, GetSpellTypePriority(spell_type, BotPriorityCategories::Idle)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11623,8 +11628,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeEngagedPriority: - if (spellType != UINT16_MAX) { - to->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, GetSpellTypePriority(spellType, BotPriorityCategories::Engaged)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11634,8 +11639,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypePursuePriority: - if (spellType != UINT16_MAX) { - to->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, GetSpellTypePriority(spellType, BotPriorityCategories::Pursue)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11645,8 +11650,8 @@ void Bot::CopySettings(Bot* to, uint8 settingType, uint16 spellType) { break; case BotSettingCategories::SpellTypeAEOrGroupTargetCount: - if (spellType != UINT16_MAX) { - to->SetSpellTypeAEOrGroupTargetCount(spellType, GetSpellTypeAEOrGroupTargetCount(spellType)); + if (spell_type != UINT16_MAX) { + to->SetSpellTypeAEOrGroupTargetCount(spell_type, GetSpellTypeAEOrGroupTargetCount(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -11721,10 +11726,10 @@ void Bot::CopyBotBlockedBuffs(Bot* to) { to->ClearBotBlockedBuffs(); - std::vector blockedBuffs = GetBotBlockedBuffs(); + std::vector blocked_buffs = GetBotBlockedBuffs(); - if (!blockedBuffs.empty()) { - for (auto& blocked_buff : blockedBuffs) { + if (!blocked_buffs.empty()) { + for (auto& blocked_buff : blocked_buffs) { to->SetBotBlockedBuff(blocked_buff.spell_id, blocked_buff.blocked); } } @@ -11737,10 +11742,10 @@ void Bot::CopyBotBlockedPetBuffs(Bot* to) { to->ClearBotBlockedBuffs(); - std::vector blockedBuffs = GetBotBlockedBuffs(); + std::vector blocked_buffs = GetBotBlockedBuffs(); - if (!blockedBuffs.empty()) { - for (auto& blocked_buff : blockedBuffs) { + if (!blocked_buffs.empty()) { + for (auto& blocked_buff : blocked_buffs) { to->SetBotBlockedPetBuff(blocked_buff.spell_id, blocked_buff.blocked_pet); } } @@ -11764,12 +11769,12 @@ bool Bot::BotPassiveCheck() { return false; } -bool Bot::IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell_id) { - if (subType == UINT16_MAX) { +bool Bot::IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id) { + if (sub_type == UINT16_MAX) { return true; } - switch (subType) { + switch (sub_type) { case CommandedSubTypes::SingleTarget: if ( !IsAnyAESpell(spell_id) && @@ -12052,12 +12057,12 @@ void Bot::CleanBotBlockedBuffs() } } -std::vector Bot::BotGetSpellsByType(uint16 spellType) { - if (AIBot_spells_by_type[spellType].empty()) { - spellType = GetParentSpellType(spellType); +std::vector Bot::BotGetSpellsByType(uint16 spell_type) { + if (AIBot_spells_by_type[spell_type].empty()) { + spell_type = GetParentSpellType(spell_type); } - return AIBot_spells_by_type[spellType]; + return AIBot_spells_by_type[spell_type]; } void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type) { @@ -12070,7 +12075,7 @@ void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, st continue; } - BotSpells_Struct_wIndex spellWithIndex{ + BotSpells_Struct_wIndex spell_with_index{ static_cast(i), spell.type, spell.spellid, @@ -12088,6 +12093,6 @@ void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, st spell.bucket_comparison }; - AIBot_spells_by_type[spell.type].emplace_back(spellWithIndex); + AIBot_spells_by_type[spell.type].emplace_back(spell_with_index); } } diff --git a/zone/bot.h b/zone/bot.h index c7852b5117..d558b1a752 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -288,7 +288,7 @@ class Bot : public NPC { uint16 BotGetSpells(int spellslot) { return AIBot_spells[spellslot].spellid; } uint32 BotGetSpellType(int spellslot) { return AIBot_spells[spellslot].type; } uint16 BotGetSpellPriority(int spellslot) { return AIBot_spells[spellslot].priority; } - std::vector BotGetSpellsByType(uint16 spellType); + std::vector BotGetSpellsByType(uint16 spell_type); float GetProcChances(float ProcBonus, uint16 hand) override; int GetHandToHandDamage(void) override; bool TryFinishingBlow(Mob *defender, int64 &damage) override; @@ -322,7 +322,7 @@ class Bot : public NPC { void SetTarget(Mob* mob) override; void Zone(); bool IsAtRange(Mob* target); - void ChangeBotRangedWeapons(bool isRanged); + void ChangeBotRangedWeapons(bool is_ranged); void Sit(); void Stand(); bool IsSitting() const override; @@ -347,8 +347,8 @@ class Bot : public NPC { bool GetIsUsingItemClick() { return is_using_item_click; } void SetIsUsingItemClick(bool flag = true) { is_using_item_click = flag; } bool UseDiscipline(uint32 spell_id, uint32 target); - uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets, Raid* raid); - uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid); + uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool include_pets, Raid* raid); + uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool include_pets, Raid* raid); bool GetNeedsCured(Mob *tar); bool GetNeedsHateRedux(Mob *tar); bool HasOrMayGetAggro(bool SitAggro, uint32 spell_id = 0); @@ -450,11 +450,11 @@ class Bot : public NPC { void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot); // AI Methods - bool AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); - bool AttemptAICastSpell(uint16 spellType, Mob* tar = nullptr); + bool AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_target_type = UINT16_MAX, uint16 sub_type = UINT16_MAX); + bool AttemptAICastSpell(uint16 spell_type, Mob* tar = nullptr); bool AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank); - bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool isDisc = false); - bool AttemptCloseBeneficialSpells(uint16 spellType); + bool AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc = false); + bool AttemptCloseBeneficialSpells(uint16 spell_type); bool AI_EngagedCastCheck() override; bool AI_PursueCastCheck() override; bool AI_IdleCastCheck() override; @@ -509,37 +509,37 @@ class Bot : public NPC { { return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); } void DoAttackRounds(Mob* target, int hand); - bool PrecastChecks(Mob* tar, uint16 spellType); - bool CastChecks(uint16 spell_id, Mob* tar, uint16 spellType, bool doPrechecks = false, bool AECheck = false); - bool CanCastSpellType(uint16 spellType, uint16 spell_id, Mob* tar); + bool PrecastChecks(Mob* tar, uint16 spell_type); + bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false); + bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar); bool BotHasEnoughMana(uint16 spell_id); std::vector GetSpellTargetList() { return _spellTargetList; } - void SetSpellTargetList(std::vector spellTargetList) { _spellTargetList = spellTargetList; } + void SetSpellTargetList(std::vector spell_target_list) { _spellTargetList = spell_target_list; } std::vector GetGroupSpellTargetList() { return _groupSpellTargetList; } - void SetGroupSpellTargetList(std::vector spellTargetList) { _groupSpellTargetList = spellTargetList; } + void SetGroupSpellTargetList(std::vector spell_target_list) { _groupSpellTargetList = spell_target_list; } Raid* GetStoredRaid() { return _storedRaid; } - void SetStoredRaid(Raid* storedRaid) { _storedRaid = storedRaid; } + void SetStoredRaid(Raid* stored_raid) { _storedRaid = stored_raid; } bool GetVerifiedRaid() { return _verifiedRaid; } void SetVerifiedRaid(bool status) { _verifiedRaid = status; } uint16 GetTempSpellType() { return _tempSpellType; } - void SetTempSpellType(uint16 spellType) { _tempSpellType = spellType; } + void SetTempSpellType(uint16 spell_type) { _tempSpellType = spell_type; } void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); - bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spellType); - bool IsValidTargetType(uint16 spell_id, int targetType, uint8 bodyType); + bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spell_type); + bool IsValidTargetType(uint16 spell_id, int target_type, uint8 body_type); bool IsMobEngagedByAnyone(Mob* tar); - void SetBotSetting(uint8 settingType, uint16 botSetting, int settingValue); - void CopySettings(Bot* to, uint8 settingType, uint16 spellType = UINT16_MAX); + void SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value); + void CopySettings(Bot* to, uint8 setting_type, uint16 spell_type = UINT16_MAX); void CopyBotSpellSettings(Bot* to); void ResetBotSpellSettings(); void CopyBotBlockedBuffs(Bot* to); void CopyBotBlockedPetBuffs(Bot* to); - int GetBotBaseSetting(uint16 botSetting); - int GetDefaultBotBaseSetting(uint16 botSetting, uint8 stance = Stance::Balanced); - void SetBotBaseSetting(uint16 botSetting, int settingValue); + int GetBotBaseSetting(uint16 bot_setting); + int GetDefaultBotBaseSetting(uint16 bot_setting, uint8 stance = Stance::Balanced); + void SetBotBaseSetting(uint16 bot_setting, int setting_value); void LoadDefaultBotSettings(); - void SetBotSpellRecastTimer(uint16 spellType, Mob* spelltar, bool preCast = false); + void SetBotSpellRecastTimer(uint16 spell_type, Mob* spelltar, bool pre_cast = false); uint16 GetSpellByAA(int id, AA::Rank* &rank); void CleanBotBlockedBuffs(); void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); } @@ -548,40 +548,40 @@ class Bot : public NPC { void SetBotBlockedBuff(uint16 spell_id, bool block); void SetBotBlockedPetBuff(uint16 spell_id, bool block); - int GetDefaultSetting(uint16 settingCategory, uint16 settingType, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellTypePriority(uint16 spellType, uint8 priorityType, uint8 botClass, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellTypeIdlePriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellTypeEngagedPriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellTypePursuePriority(uint16 spellType, uint8 botClass, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellTypeResistLimit(uint16 spellType, uint8 stance = Stance::Balanced); - bool GetDefaultSpellTypeAggroCheck(uint16 spellType, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellTypeMinManaLimit(uint16 spellType, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spellType, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellTypeMinHPLimit(uint16 spellType, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spellType, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spellType, uint8 stance = Stance::Balanced); - - int GetSetting(uint16 settingCategory, uint16 settingType); - uint16 GetSpellTypePriority(uint16 spellType, uint8 priorityType); - void SetSpellTypePriority(uint16 spellType, uint8 priorityType, uint16 priority); - inline uint16 GetSpellTypeResistLimit(uint16 spellType) const { return _spellSettings[spellType].resistLimit; } - void SetSpellTypeResistLimit(uint16 spellType, uint16 resistLimit); - inline bool GetSpellTypeAggroCheck(uint16 spellType) const { return _spellSettings[spellType].aggroCheck; } - void SetSpellTypeAggroCheck(uint16 spellType, bool AggroCheck); - inline uint8 GetSpellTypeMinManaLimit(uint16 spellType) const { return _spellSettings[spellType].minManaPct; } - inline uint8 GetSpellTypeMaxManaLimit(uint16 spellType) const { return _spellSettings[spellType].maxManaPct; } - void SetSpellTypeMinManaLimit(uint16 spellType, uint8 manaLimit); - void SetSpellTypeMaxManaLimit(uint16 spellType, uint8 manaLimit); - inline uint8 GetSpellTypeMinHPLimit(uint16 spellType) const { return _spellSettings[spellType].minHPPct; } - inline uint8 GetSpellTypeMaxHPLimit(uint16 spellType) const { return _spellSettings[spellType].maxHPPct; } - void SetSpellTypeMinHPLimit(uint16 spellType, uint8 hpLimit); - void SetSpellTypeMaxHPLimit(uint16 spellType, uint8 hpLimit); - inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spellType) const { return _spellSettings[spellType].AEOrGroupTargetCount; } - void SetSpellTypeAEOrGroupTargetCount(uint16 spellType, uint16 targetCount); + int GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypePriority(uint16 spell_type, uint8 priority_type, uint8 bot_class, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeIdlePriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeEngagedPriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypePursuePriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeResistLimit(uint16 spell_type, uint8 stance = Stance::Balanced); + bool GetDefaultSpellTypeAggroCheck(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMinManaLimit(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMinHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 stance = Stance::Balanced); + + int GetSetting(uint16 setting_category, uint16 setting_type); + uint16 GetSpellTypePriority(uint16 spell_type, uint8 priority_type); + void SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority); + inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return _spellSettings[spell_type].resistLimit; } + void SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit); + inline bool GetSpellTypeAggroCheck(uint16 spell_type) const { return _spellSettings[spell_type].aggroCheck; } + void SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check); + inline uint8 GetSpellTypeMinManaLimit(uint16 spell_type) const { return _spellSettings[spell_type].minManaPct; } + inline uint8 GetSpellTypeMaxManaLimit(uint16 spell_type) const { return _spellSettings[spell_type].maxManaPct; } + void SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit); + void SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit); + inline uint8 GetSpellTypeMinHPLimit(uint16 spell_type) const { return _spellSettings[spell_type].minHPPct; } + inline uint8 GetSpellTypeMaxHPLimit(uint16 spell_type) const { return _spellSettings[spell_type].maxHPPct; } + void SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit); + void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit); + inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return _spellSettings[spell_type].AEOrGroupTargetCount; } + void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count); bool BotPassiveCheck(); bool GetShowHelm() const { return _showHelm; } - void SetShowHelm(bool showHelm) { _showHelm = showHelm; } + void SetShowHelm(bool show_helm) { _showHelm = show_helm; } bool GetBehindMob() const { return _behindMobStatus; } void SetBehindMob(bool value) { _behindMobStatus = value; } bool GetMaxMeleeRange() const { return _maxMeleeRangeStatus; } @@ -589,24 +589,24 @@ class Bot : public NPC { uint8 GetStopMeleeLevel() const { return _stopMeleeLevel; } void SetStopMeleeLevel(uint8 level) { _stopMeleeLevel = level; } uint32 GetBotDistanceRanged() const { return _distanceRanged; } - void SetBotDistanceRanged(uint32 distanceRanged) { _distanceRanged = distanceRanged; } + void SetBotDistanceRanged(uint32 distance) { _distanceRanged = distance; } bool GetMedInCombat() const { return _medInCombat; } void SetMedInCombat(bool value) { _medInCombat = value; } uint8 GetSitHPPct() const { return _SitHPPct; } void SetSitHPPct(uint8 value) { _SitHPPct = value; } uint8 GetSitManaPct() const { return _SitManaPct; } void SetSitManaPct(uint8 value) { _SitManaPct = value; } - void SetHasLoS(bool hasLoS) { _hasLoS = hasLoS; } + void SetHasLoS(bool has_los) { _hasLoS = has_los; } bool HasLoS() const { return _hasLoS; } - std::list GetSpellTypesPrioritized(uint8 priorityType); - uint16 GetParentSpellType(uint16 spellType); - bool IsValidSpellTypeBySpellID(uint16 spellType, uint16 spell_id); + std::list GetSpellTypesPrioritized(uint8 priority_type); + uint16 GetParentSpellType(uint16 spell_type); + bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } - void SetCastedSpellType(uint16 spellType); - bool IsValidSpellTypeSubType(uint16 spellType, uint16 subType, uint16 spell_id); + void SetCastedSpellType(uint16 spell_type); + bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id); - bool HasValidAETarget(Bot* botCaster, uint16 spell_id, uint16 spellType, Mob* tar); + bool HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar); void CheckBotSpells(); @@ -655,37 +655,37 @@ class Bot : public NPC { ProcessBotGroupAdd(Group* group, Raid* raid, Client* client = nullptr, bool new_raid = false, bool initial = false); - static std::list GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect); - static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType); - static std::list GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType); - static std::vector GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE = false, uint16 subTargetType = UINT16_MAX, uint16 subType = UINT16_MAX); - - static BotSpell GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType); - BotSpell GetSpellByHealType(uint16 spellType, Mob* tar); - static BotSpell GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetBestBotSpellForFastHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetBestBotSpellForPercentageHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - static BotSpell GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType = BotSpellTypes::RegularHeal); - - static Mob* GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE); + static std::list GetBotSpellsForSpellEffect(Bot* caster, uint16 spell_type, int spell_effect); + static std::list GetBotSpellsForSpellEffectAndTargetType(Bot* caster, uint16 spell_type, int spell_effect, SpellTargetType target_type); + static std::list GetBotSpellsBySpellType(Bot* caster, uint16 spell_type); + static std::vector GetPrioritizedBotSpellsBySpellType(Bot* caster, uint16 spell_type, Mob* tar, bool AE = false, uint16 sub_target_type = UINT16_MAX, uint16 sub_type = UINT16_MAX); + + static BotSpell GetFirstBotSpellBySpellType(Bot* caster, uint16 spell_type); + BotSpell GetSpellByHealType(uint16 spell_type, Mob* tar); + static BotSpell GetBestBotSpellForVeryFastHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForFastHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForHealOverTime(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForPercentageHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForRegularSingleTargetHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetFirstBotSpellForSingleTargetHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + static BotSpell GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); + + static Mob* GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_type, bool AE); bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); - static BotSpell GetBestBotSpellForMez(Bot* botCaster, uint16 spellType = BotSpellTypes::Mez); - static BotSpell GetBestBotMagicianPetSpell(Bot* botCaster, uint16 spellType = BotSpellTypes::Pet); - static std::string GetBotMagicianPetType(Bot* botCaster); - static BotSpell GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); - static BotSpell GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE = false, Mob* tar = nullptr); - static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target, uint16 spellType); - static BotSpell GetDebuffBotSpell(Bot* botCaster, Mob* target, uint16 spellType); - static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target, uint16 spellType); - static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target, uint16 spellType); - static BotSpell GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE = false, Mob* tar = nullptr); - static BotSpell GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellType); - static BotSpell GetBestBotSpellForCharm(Bot* botCaster, Mob* target, uint16 spellType); + static BotSpell GetBestBotSpellForMez(Bot* caster, uint16 spell_type = BotSpellTypes::Mez); + static BotSpell GetBestBotMagicianPetSpell(Bot* caster, uint16 spell_type = BotSpellTypes::Pet); + static std::string GetBotMagicianPetType(Bot* caster); + static BotSpell GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType target_type, uint16 spell_type, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotSpellForStunByTargetType(Bot* caster, SpellTargetType target_type, uint16 spell_type, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target, uint16 spell_type); + static BotSpell GetDebuffBotSpell(Bot* caster, Mob* target, uint16 spell_type); + static BotSpell GetBestBotSpellForCure(Bot* caster, Mob* target, uint16 spell_type); + static BotSpell GetBestBotSpellForResistDebuff(Bot* caster, Mob* target, uint16 spell_type); + static BotSpell GetBestBotSpellForNukeByBodyType(Bot* caster, uint8 body_type, uint16 spell_type, bool AE = false, Mob* tar = nullptr); + static BotSpell GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type); + static BotSpell GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_type); static NPCType *CreateDefaultNPCTypeStructForBot( const std::string& botName, @@ -720,7 +720,7 @@ class Bot : public NPC { bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; } uint8 GetBotStance() { return _botStance; } - uint8 GetChanceToCastBySpellType(uint16 spellType); + uint8 GetChanceToCastBySpellType(uint16 spell_type); bool IsGroupHealer() const { return m_CastingRoles.GroupHealer; } bool IsGroupSlower() const { return m_CastingRoles.GroupSlower; } bool IsGroupNuker() const { return m_CastingRoles.GroupNuker; } @@ -832,7 +832,7 @@ class Bot : public NPC { void SetBotSpellID(uint32 newSpellID); void SetSpawnStatus(bool spawnStatus) { _spawnStatus = spawnStatus; } void SetPetChooserID(uint8 id) { _petChooserID = id; } - void SetBotRangedSetting(bool botRangedSetting) { _botRangedSetting = botRangedSetting; } + void SetBotRangedSetting(bool value) { _botRangedSetting = value; } void SetBotCharmer(bool c) { _botCharmer = c; } void SetBotOwner(Mob* botOwner) { this->_botOwner = botOwner; } void SetRangerAutoWeaponSelect(bool enable) { GetClass() == Class::Ranger ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; } @@ -950,7 +950,7 @@ class Bot : public NPC { std::vector GetBotTimers() { return bot_timers; } void SetBotTimers(std::vector timers) { bot_timers = timers; } std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } - void SetBotBlockedBuffs(std::vector blockedBuff) { bot_blocked_buffs = blockedBuff; } + void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } uint32 GetLastZoneID() const { return _lastZoneId; } int32 GetBaseAC() const { return _baseAC; } int32 GetBaseATK() const { return _baseATK; } @@ -977,11 +977,11 @@ class Bot : public NPC { static uint8 spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][Stance::AEBurn][cntHSND]; - bool BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); - bool BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); - bool BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); - bool BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); - bool BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType); + bool BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type); + bool BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type); + bool BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type); + bool BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type); + bool BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type); bool CheckIfIncapacitated(); bool IsAIProcessValid(const Client* bot_owner, const Group* bot_group, const Raid* raid); @@ -1010,12 +1010,12 @@ class Bot : public NPC { const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, - bool behindMob, + bool behind_mob, bool backstab_weapon, float& melee_distance_min, float& melee_distance, float& melee_distance_max, - uint8 stopMeleeLevel + uint8 stop_melee_level ); // Combat Checks @@ -1025,14 +1025,14 @@ class Bot : public NPC { void CheckCombatRange( Mob* tar, float tar_distance, - bool& atCombatRange, - bool behindMob, + bool& at_combat_range, + bool behind_mob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, - uint8 stopMeleeLevel + uint8 stop_melee_level ); bool GetCombatJitterFlag() { return m_combat_jitter_flag; } void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; } @@ -1040,7 +1040,7 @@ class Bot : public NPC { void SetCombatOutOfRangeJitterFlag(bool flag = true) { m_combat_out_of_range_jitter_flag = flag; } void SetCombatJitter(); void SetCombatOutOfRangeJitter(); - void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stopMeleeLevel, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behindMob, bool frontMob); + void DoCombatPositioning(Mob* tar, glm::vec3 Goal, bool stop_melee_level, float tar_distance, float melee_distance_min, float melee_distance, float melee_distance_max, bool behind_mob, bool front_mob); void DoFaceCheckWithJitter(Mob* tar); void DoFaceCheckNoJitter(Mob* tar); void RunToGoalWithJitter(glm::vec3 Goal); @@ -1056,15 +1056,15 @@ class Bot : public NPC { bool TryIdleChecks(float fm_distance); bool TryNonCombatMovementChecks(Client* bot_owner, const Mob* follow_mob, glm::vec3& Goal); bool TryBardMovementCasts(); - bool BotRangedAttack(Mob* other, bool CanDoubleAttack = false); + bool BotRangedAttack(Mob* other, bool can_double_attack = false); bool CheckDoubleRangedAttack(); // Public "Refactor" Methods static bool CheckCampSpawnConditions(Client* c); protected: - void BotMeditate(bool isSitting); - bool CheckBotDoubleAttack(bool Triple = false); + void BotMeditate(bool is_sitting); + bool CheckBotDoubleAttack(bool triple_attack = false); bool CheckTripleAttack(); void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client); bool AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = nullptr) override; @@ -1210,6 +1210,6 @@ class Bot : public NPC { int32 CalcItemATKCap() final; }; -bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID); +bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 spell_id); #endif // BOT_H diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 33b40ce790..706a8c2085 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -2100,34 +2100,34 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { std::string arg0 = sep->arg[0]; std::string arg1 = sep->arg[1]; - uint8 minCount = 0; - uint8 maxCount = 0; - bool clientOnly = false; + uint8 min_count = 0; + uint8 max_count = 0; + bool client_only = false; if (BotSpellTypes::END <= 19) { - minCount = BotSpellTypes::START; - maxCount = BotSpellTypes::END; + min_count = BotSpellTypes::START; + max_count = BotSpellTypes::END; } else if (!arg1.compare("0-19")) { - minCount = BotSpellTypes::START; - maxCount = 19; + min_count = BotSpellTypes::START; + max_count = 19; } else if (!arg1.compare("20-39")) { - minCount = std::min(static_cast(20), static_cast(BotSpellTypes::END)); - maxCount = std::min(static_cast(39), static_cast(BotSpellTypes::END)); + min_count = std::min(static_cast(20), static_cast(BotSpellTypes::END)); + max_count = std::min(static_cast(39), static_cast(BotSpellTypes::END)); } else if (!arg1.compare("40+")) { - minCount = std::min(static_cast(40), static_cast(BotSpellTypes::END)); - maxCount = BotSpellTypes::END; + min_count = std::min(static_cast(40), static_cast(BotSpellTypes::END)); + max_count = BotSpellTypes::END; } else if (!arg1.compare("commanded")) { - minCount = BotSpellTypes::COMMANDED_START; - maxCount = BotSpellTypes::COMMANDED_END; + min_count = BotSpellTypes::COMMANDED_START; + max_count = BotSpellTypes::COMMANDED_END; } else if (!arg1.compare("client")) { - minCount = BotSpellTypes::START; - maxCount = BotSpellTypes::END; - clientOnly = true; + min_count = BotSpellTypes::START; + max_count = BotSpellTypes::END; + client_only = true; } else { c->Message(Chat::Yellow, "You must choose a valid range option"); @@ -2141,25 +2141,25 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { const std::string& forest_green = "forest_green"; const std::string& goldenrod = "goldenrod"; - std::string fillerLine = "-----------"; - std::string spellTypeField = "Spell Type"; - std::string idField = "ID"; - std::string shortnameField = "Short Name"; + std::string filler_line = "-----------"; + std::string spell_type_field = "Spell Type"; + std::string id_field = "ID"; + std::string shortname_field = "Short Name"; std::string popup_text = DialogueWindow::TableRow( - DialogueWindow::TableCell(DialogueWindow::ColorMessage(goldenrod, spellTypeField)) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(goldenrod, spell_type_field)) + - DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, idField) : DialogueWindow::ColorMessage(goldenrod, shortnameField))) + DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(goldenrod, id_field) : DialogueWindow::ColorMessage(goldenrod, shortname_field))) ); popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell(DialogueWindow::ColorMessage(gold, fillerLine)) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(gold, filler_line)) + - DialogueWindow::TableCell(DialogueWindow::ColorMessage(gold, fillerLine)) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(gold, filler_line)) ); - for (int i = minCount; i <= maxCount; ++i) { - if (clientOnly && !IsClientBotSpellType(i)) { + for (int i = min_count; i <= max_count; ++i) { + if (client_only && !IsClientBotSpellType(i)) { continue; } diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 704e62863b..8654f5e69b 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -56,15 +56,15 @@ void bot_command_camp(Client *c, const Seperator *sep) return; } - uint16 campCount; + uint16 camp_count; for (auto bot_iter : sbl) { bot_iter->Camp(); - ++campCount; + ++camp_count; } - if (campCount) { - c->Message(Chat::White, "%i of your bots have been camped.", campCount); + if (camp_count) { + c->Message(Chat::White, "%i of your bots have been camped.", camp_count); } } @@ -559,7 +559,7 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) uint32 bfd = RuleI(Bots, DefaultFollowDistance); bool set_flag = false; - bool currentCheck = false; + bool current_check = false; int ab_arg = 2; std::string arg1 = sep->arg[1]; @@ -589,7 +589,7 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) ++ab_arg; } else if (!arg1.compare("current")) { - currentCheck = true; + current_check = true; } else if (arg1.compare("reset")) { c->Message( @@ -619,13 +619,13 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - int botCount = 0; + int bot_count = 0; for (auto bot_iter : sbl) { if (!bot_iter) { continue; } - if (currentCheck) { + if (current_check) { Mob* follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); c->Message( @@ -640,15 +640,15 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) } else { bot_iter->SetFollowDistance(bfd * bfd); - ++botCount; + ++bot_count; } } - if (currentCheck) { + if (current_check) { return; } - if (botCount == 1) { + if (bot_count == 1) { Mob* follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); c->Message( @@ -666,7 +666,7 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) Chat::Green, fmt::format( "{} of your bots are now following at a distance of {}.", - botCount, + bot_count, bfd ).c_str() ); @@ -1170,16 +1170,16 @@ void bot_command_spawn(Client *c, const Seperator *sep) } std::string silent_confirm = sep->arg[2]; - bool silentTell = false; + bool silent_tell = false; if (!silent_confirm.compare("silent")) { - silentTell = true; + silent_tell = true; } - if (!silentTell && c->GetBotOption(Client::booSpawnMessageSay)) { + if (!silent_tell && c->GetBotOption(Client::booSpawnMessageSay)) { Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str()); } - else if (!silentTell && c->GetBotOption(Client::booSpawnMessageTell)) { + else if (!silent_tell && c->GetBotOption(Client::booSpawnMessageTell)) { my_bot->OwnerMessage(bot_spawn_message[message_index]); } } @@ -1330,7 +1330,7 @@ void bot_command_stance(Client *c, const Seperator *sep) const int ab_mask = ActionableBots::ABM_Type1; - bool currentCheck = false; + bool current_check = false; int ab_arg = 1; uint32 value = 0; @@ -1360,7 +1360,7 @@ void bot_command_stance(Client *c, const Seperator *sep) } else if (!arg1.compare("current")) { ++ab_arg; - currentCheck = true; + current_check = true; } else { c->Message( @@ -1395,7 +1395,7 @@ void bot_command_stance(Client *c, const Seperator *sep) first_found = bot_iter; } - if (currentCheck) { + if (current_check) { c->Message( Chat::Green, fmt::format( @@ -1436,7 +1436,7 @@ void bot_command_stance(Client *c, const Seperator *sep) ++success_count; } - if (currentCheck) { + if (current_check) { return; } @@ -1482,7 +1482,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) uint8 sml = RuleI(Bots, CasterStopMeleeLevel); bool sync_sml = false; bool reset_sml = false; - bool currentCheck = false; + bool current_check = false; if (sep->IsNumber(1)) { ab_arg = 2; @@ -1498,7 +1498,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) } else if (!arg1.compare("current")) { ab_arg = 2; - currentCheck = true; + current_check = true; } else if (!strcasecmp(sep->arg[1], "reset")) { ab_arg = 2; @@ -1553,7 +1553,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) sml = my_bot->GetDefaultBotBaseSetting(BotBaseSettings::StopMeleeLevel); } - if (currentCheck) { + if (current_check) { c->Message( Chat::White, fmt::format( @@ -1570,7 +1570,7 @@ void bot_command_stop_melee_level(Client* c, const Seperator* sep) } } - if (!currentCheck) { + if (!current_check) { if (success_count == 1 && first_found) { c->Message( Chat::White, diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 3ca0f0e45b..5573de2b16 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -1161,8 +1161,8 @@ bool BotDatabase::SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, c if (all_flag) { where_clause = fmt::format( "IN ({}, {}, {}, {}, {}, {}, {})", - EQ::invslot::slotHead, - EQ::invslot::slotArms, + EQ::invslot::SlotHead, + EQ::invslot::SlotArms, EQ::invslot::slotWrist1, EQ::invslot::slotHands, EQ::invslot::slotChest, @@ -2214,19 +2214,19 @@ bool BotDatabase::LoadBotSettings(Mob* m) return false; } - uint32 mobID = (m->IsClient() ? m->CastToClient()->CharacterID() : m->CastToBot()->GetBotID()); - uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); + uint32 mob_id = (m->IsClient() ? m->CastToClient()->CharacterID() : m->CastToBot()->GetBotID()); + uint8 stance_id = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {} AND `stance` = {}", mobID, stanceID); + query = fmt::format("`char_id` = {} AND `stance` = {}", mob_id, stance_id); } else { - query = fmt::format("`bot_id` = {} AND `stance` = {}", mobID, stanceID); + query = fmt::format("`bot_id` = {} AND `stance` = {}", mob_id, stance_id); } - if (stanceID == Stance::Passive) { + if (stance_id == Stance::Passive) { LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive); return true; } @@ -2273,11 +2273,11 @@ bool BotDatabase::SaveBotSettings(Mob* m) return false; } - uint32 botID = (m->IsBot() ? m->CastToBot()->GetBotID() : 0); - uint32 charID = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); - uint8 stanceID = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); + uint32 bot_id = (m->IsBot() ? m->CastToBot()->GetBotID() : 0); + uint32 char_id = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); + uint8 stance_id = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); - if (stanceID == Stance::Passive) { + if (stance_id == Stance::Passive) { LogBotSettings("{} is currently set to {} [#{}]. No saving or loading required.", m->GetCleanName(), Stance::GetName(Stance::Passive), Stance::Passive); return true; } @@ -2285,10 +2285,10 @@ bool BotDatabase::SaveBotSettings(Mob* m) std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {} AND `stance` = {}", charID, stanceID); + query = fmt::format("`char_id` = {} AND `stance` = {}", char_id, stance_id); } else { - query = fmt::format("`bot_id` = {} AND `stance` = {}", botID, stanceID); + query = fmt::format("`bot_id` = {} AND `stance` = {}", bot_id, stance_id); } BotSettingsRepository::DeleteWhere(database, query); @@ -2296,14 +2296,14 @@ bool BotDatabase::SaveBotSettings(Mob* m) std::vector v; if (m->IsBot()) { - uint8 botStance = m->CastToBot()->GetBotStance(); + uint8 bot_stance = m->CastToBot()->GetBotStance(); for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { - if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, botStance)) { + if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, bot_stance)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = charID, - .bot_id = botID, - .stance = stanceID, + .char_id = char_id, + .bot_id = bot_id, + .stance = stance_id, .setting_id = static_cast(i), .setting_type = static_cast(BotSettingCategories::BaseSetting), .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), @@ -2319,11 +2319,11 @@ bool BotDatabase::SaveBotSettings(Mob* m) for (uint16 i = BotSettingCategories::START_NO_BASE; i <= BotSettingCategories::END; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { - if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x, botStance)) { + if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x, bot_stance)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = charID, - .bot_id = botID, - .stance = stanceID, + .char_id = char_id, + .bot_id = bot_id, + .stance = stance_id, .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToBot()->GetSetting(i, x), @@ -2333,7 +2333,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, botStance)); + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, bot_stance)); } } } @@ -2342,9 +2342,9 @@ bool BotDatabase::SaveBotSettings(Mob* m) if (m->IsClient()) { if (m->CastToClient()->GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock) != m->GetIllusionBlock()) { // Only illusion block supported auto e = BotSettingsRepository::BotSettings{ - .char_id = charID, - .bot_id = botID, - .stance = stanceID, + .char_id = char_id, + .bot_id = bot_id, + .stance = stance_id, .setting_id = static_cast(BotBaseSettings::IllusionBlock), .setting_type = static_cast(BotSettingCategories::BaseSetting), .value = m->GetIllusionBlock(), @@ -2362,9 +2362,9 @@ bool BotDatabase::SaveBotSettings(Mob* m) LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = charID, - .bot_id = botID, - .stance = stanceID, + .char_id = char_id, + .bot_id = bot_id, + .stance = stance_id, .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToClient()->GetBotSetting(i, x), diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 69bc55c167..78d6ed135f 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -21,57 +21,57 @@ #include "../common/repositories/bot_spells_entries_repository.h" #include "../common/repositories/npc_spells_repository.h" -bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTargetType, uint16 subType) { +bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_target_type, uint16 sub_type) { if (!tar) { return false; } - LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spellType), tar->GetCleanName()); + LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); if ( !AI_HasSpells() || - (spellType == BotSpellTypes::Pet && tar != this) || - (IsPetBotSpellType(spellType) && !tar->IsPet()) || - (spellType == BotSpellTypes::Buff && tar->IsPet()) || - (spellType == BotSpellTypes::InCombatBuffSong && tar->IsPet()) || - (spellType == BotSpellTypes::OutOfCombatBuffSong && tar->IsPet()) || - (spellType == BotSpellTypes::PreCombatBuffSong && tar->IsPet()) || + (spell_type == BotSpellTypes::Pet && tar != this) || + (IsPetBotSpellType(spell_type) && !tar->IsPet()) || + (spell_type == BotSpellTypes::Buff && tar->IsPet()) || + (spell_type == BotSpellTypes::InCombatBuffSong && tar->IsPet()) || + (spell_type == BotSpellTypes::OutOfCombatBuffSong && tar->IsPet()) || + (spell_type == BotSpellTypes::PreCombatBuffSong && tar->IsPet()) || (!RuleB(Bots, AllowBuffingHealingFamiliars) && tar->IsFamiliar()) || - (tar->IsPet() && tar->IsCharmed() && spellType == BotSpellTypes::PetBuffs && !RuleB(Bots, AllowCharmedPetBuffs)) || - (tar->IsPet() && tar->IsCharmed() && spellType == BotSpellTypes::PetCures && !RuleB(Bots, AllowCharmedPetCures)) || - (tar->IsPet() && tar->IsCharmed() && IsHealBotSpellType(spellType) && !RuleB(Bots, AllowCharmedPetHeals)) + (tar->IsPet() && tar->IsCharmed() && spell_type == BotSpellTypes::PetBuffs && !RuleB(Bots, AllowCharmedPetBuffs)) || + (tar->IsPet() && tar->IsCharmed() && spell_type == BotSpellTypes::PetCures && !RuleB(Bots, AllowCharmedPetCures)) || + (tar->IsPet() && tar->IsCharmed() && IsHealBotSpellType(spell_type) && !RuleB(Bots, AllowCharmedPetHeals)) ) { return false; } - if (iChance < 100 && zone->random.Int(0, 100) > iChance) { + if (chance < 100 && zone->random.Int(0, 100) > chance) { return false; } - if ((spellType != BotSpellTypes::Resurrect && spellType != BotSpellTypes::SummonCorpse) && tar->GetAppearance() == eaDead) { + if ((spell_type != BotSpellTypes::Resurrect && spell_type != BotSpellTypes::SummonCorpse) && tar->GetAppearance() == eaDead) { if (!((tar->IsClient() && tar->CastToClient()->GetFeigned()) || tar->IsBot())) { return false; } } - uint8 botClass = GetClass(); + uint8 bot_class = GetClass(); SetCastedSpellType(UINT16_MAX); // this is for recast timers - SetTempSpellType(spellType); // this is for spell checks + SetTempSpellType(spell_type); // this is for spell checks - BotSpell botSpell; - botSpell.SpellId = 0; - botSpell.SpellIndex = 0; - botSpell.ManaCost = 0; + BotSpell bot_spell; + bot_spell.SpellId = 0; + bot_spell.SpellIndex = 0; + bot_spell.ManaCost = 0; - if (SpellTypeRequiresLoS(spellType) && tar != this) { + if (SpellTypeRequiresLoS(spell_type) && tar != this) { SetHasLoS(DoLosChecks(this, tar)); } else { SetHasLoS(true); } - switch (spellType) { + switch (spell_type) { case BotSpellTypes::Slow: if (tar->GetSpecialAbility(SpecialAbility::SlowImmunity)) { return false; @@ -118,7 +118,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; case BotSpellTypes::InCombatBuff: - if (!IsCommandedSpell() && GetClass() != Class::Shaman && spellType == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { + if (!IsCommandedSpell() && GetClass() != Class::Shaman && spell_type == BotSpellTypes::InCombatBuff && IsCasterClass(GetClass()) && GetLevel() >= GetStopMeleeLevel()) { return false; } @@ -162,14 +162,14 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; case BotSpellTypes::AEMez: case BotSpellTypes::Mez: - return BotCastMez(tar, botClass, botSpell, spellType); + return BotCastMez(tar, bot_class, bot_spell, spell_type); case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::PBAENuke: case BotSpellTypes::Nuke: case BotSpellTypes::AEStun: case BotSpellTypes::Stun: - return BotCastNuke(tar, botClass, botSpell, spellType); + return BotCastNuke(tar, bot_class, bot_spell, spell_type); case BotSpellTypes::RegularHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::CompleteHeal: @@ -187,7 +187,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return false; } - return BotCastHeal(tar, botClass, botSpell, spellType); + return BotCastHeal(tar, bot_class, bot_spell, spell_type); case BotSpellTypes::GroupCures: case BotSpellTypes::Cure: case BotSpellTypes::PetCures: @@ -195,13 +195,13 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return false; } - return BotCastCure(tar, botClass, botSpell, spellType); + return BotCastCure(tar, bot_class, bot_spell, spell_type); case BotSpellTypes::Pet: if (HasPet() || IsBotCharmer()) { return false; } - return BotCastPet(tar, botClass, botSpell, spellType); + return BotCastPet(tar, bot_class, bot_spell, spell_type); case BotSpellTypes::Resurrect: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { return false; @@ -218,9 +218,9 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge break; } - std::vector botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, (IsAEBotSpellType(spellType) || subTargetType == CommandedSubTypes::AETarget), subTargetType, subType); + std::vector bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, (IsAEBotSpellType(spell_type) || sub_target_type == CommandedSubTypes::AETarget), sub_target_type, sub_type); - for (const auto& s : botSpellList) { + for (const auto& s : bot_spell_list) { if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { continue; @@ -236,7 +236,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge fmt::format( "Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.", CastingSpellID() ? spells[CastingSpellID()].name : "my spell", - GetSpellTypeNameByID(spellType), + GetSpellTypeNameByID(spell_type), spells[s.SpellId].name, tar->GetCleanName() ).c_str() @@ -246,24 +246,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (IsBotSpellTypeOtherBeneficial(spellType)) { + if (IsBotSpellTypeOtherBeneficial(spell_type)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spellType, tar, true); + SetBotSpellRecastTimer(spell_type, tar, true); } } else { - SetCastedSpellType(spellType); + SetCastedSpellType(spell_type); } - if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { BotGroupSay( this, fmt::format( "Casting {} [{}] on {}.", GetSpellName(s.SpellId), - GetSpellTypeNameByID(spellType), + GetSpellTypeNameByID(spell_type), (tar == this ? "myself" : tar->GetCleanName()) ).c_str() ); @@ -276,34 +276,34 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint16 spellType, uint16 subTarge return false; } -bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { - std::vector botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); +bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) { + std::vector bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type)); - for (const auto& s : botSpellList) { + for (const auto& s : bot_spell_list) { if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { continue; } if (!IsCommandedSpell()) { - Mob* addMob = GetFirstIncomingMobToMez(this, s.SpellId, spellType, IsAEBotSpellType(spellType)); + Mob* add_mob = GetFirstIncomingMobToMez(this, s.SpellId, spell_type, IsAEBotSpellType(spell_type)); - if (!addMob) { + if (!add_mob) { return false; } - tar = addMob; + tar = add_mob; } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - if (IsBotSpellTypeOtherBeneficial(spellType)) { + if (IsBotSpellTypeOtherBeneficial(spell_type)) { SetCastedSpellType(UINT16_MAX); if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spellType, tar, true); + SetBotSpellRecastTimer(spell_type, tar, true); } } else { - SetCastedSpellType(spellType); + SetCastedSpellType(spell_type); } BotGroupSay( @@ -311,7 +311,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellT fmt::format( "Casting {} [{}] on {}.", GetSpellName(s.SpellId), - GetSpellTypeNameByID(spellType), + GetSpellTypeNameByID(spell_type), (tar == this ? "myself" : tar->GetCleanName()) ).c_str() ); @@ -323,30 +323,30 @@ bool Bot::BotCastMez(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellT return false; } -bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { - uint32 currentTime = Timer::GetCurrentTime(); - uint32 nextCureTime = tar->DontCureMeBefore(); +bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) { + uint32 current_time = Timer::GetCurrentTime(); + uint32 next_cure_time = tar->DontCureMeBefore(); if (!IsCommandedSpell()) { - if ((nextCureTime > currentTime) || !GetNeedsCured(tar)) { + if ((next_cure_time > current_time) || !GetNeedsCured(tar)) { return false; } } - botSpell = GetBestBotSpellForCure(this, tar, spellType); + bot_spell = GetBestBotSpellForCure(this, tar, spell_type); - if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) { return false; } - if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { - if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - if (IsGroupSpell(botSpell.SpellId)) { + if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { + if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (IsGroupSpell(bot_spell.SpellId)) { BotGroupSay( this, fmt::format( "Curing the group with {}.", - GetSpellName(botSpell.SpellId) + GetSpellName(bot_spell.SpellId) ).c_str() ); @@ -354,7 +354,7 @@ bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell if (!IsCommandedSpell()) { for (Mob* m : v) { - SetBotSpellRecastTimer(spellType, m, true); + SetBotSpellRecastTimer(spell_type, m, true); } } } @@ -364,12 +364,12 @@ bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell fmt::format( "Curing {} with {}.", (tar == this ? "myself" : tar->GetCleanName()), - GetSpellName(botSpell.SpellId) + GetSpellName(bot_spell.SpellId) ).c_str() ); if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spellType, tar, true); + SetBotSpellRecastTimer(spell_type, tar, true); } return true; @@ -380,8 +380,8 @@ bool Bot::BotCastCure(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell return false; } -bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { - if (botClass == Class::Wizard) { +bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) { + if (bot_class == Class::Wizard) { auto buffs_max = GetMaxBuffSlots(); auto my_buffs = GetBuffs(); int familiar_buff_slot = -1; @@ -402,26 +402,26 @@ bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellT return false; } - botSpell = GetFirstBotSpellBySpellType(this, spellType); + bot_spell = GetFirstBotSpellBySpellType(this, spell_type); } - else if (botClass == Class::Magician) { - botSpell = GetBestBotMagicianPetSpell(this, spellType); + else if (bot_class == Class::Magician) { + bot_spell = GetBestBotMagicianPetSpell(this, spell_type); } else { - botSpell = GetFirstBotSpellBySpellType(this, spellType); + bot_spell = GetFirstBotSpellBySpellType(this, spell_type); } - if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) { return false; } - if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { - SetCastedSpellType(spellType); + if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { + SetCastedSpellType(spell_type); BotGroupSay( this, fmt::format( "Summoning a pet [{}].", - GetSpellName(botSpell.SpellId) + GetSpellName(bot_spell.SpellId) ).c_str() ); @@ -431,55 +431,55 @@ bool Bot::BotCastPet(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellT return false; } -bool Bot::BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { - if (spellType == BotSpellTypes::Stun || spellType == BotSpellTypes::AEStun) { - uint8 stunChance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal)); +bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) { + if (spell_type == BotSpellTypes::Stun || spell_type == BotSpellTypes::AEStun) { + uint8 stun_chance = (tar->IsCasting() ? RuleI(Bots, StunCastChanceIfCasting) : RuleI(Bots, StunCastChanceNormal)); - if (botClass == Class::Paladin) { - stunChance = RuleI(Bots, StunCastChancePaladins); + if (bot_class == Class::Paladin) { + stun_chance = RuleI(Bots, StunCastChancePaladins); } if ( !tar->GetSpecialAbility(SpecialAbility::StunImmunity) && ( IsCommandedSpell() || - (!tar->IsStunned() && (zone->random.Int(1, 100) <= stunChance)) + (!tar->IsStunned() && (zone->random.Int(1, 100) <= stun_chance)) ) ) { - botSpell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spellType, IsAEBotSpellType(spellType), tar); + bot_spell = GetBestBotSpellForStunByTargetType(this, ST_TargetOptional, spell_type, IsAEBotSpellType(spell_type), tar); } - if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { + if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) { return false; } } - if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { - botSpell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spellType, IsAEBotSpellType(spellType), tar); + if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) { + bot_spell = GetBestBotSpellForNukeByBodyType(this, tar->GetBodyType(), spell_type, IsAEBotSpellType(spell_type), tar); } - if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS()) && spellType == BotSpellTypes::Nuke && botClass == Class::Wizard) { - botSpell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spellType); + if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS()) && spell_type == BotSpellTypes::Nuke && bot_class == Class::Wizard) { + bot_spell = GetBestBotWizardNukeSpellByTargetResists(this, tar, spell_type); } - if (!IsValidSpellAndLoS(botSpell.SpellId, HasLoS())) { - std::vector botSpellList = GetPrioritizedBotSpellsBySpellType(this, spellType, tar, IsAEBotSpellType(spellType)); + if (!IsValidSpellAndLoS(bot_spell.SpellId, HasLoS())) { + std::vector bot_spell_list = GetPrioritizedBotSpellsBySpellType(this, spell_type, tar, IsAEBotSpellType(spell_type)); - for (const auto& s : botSpellList) { + for (const auto& s : bot_spell_list) { if (!IsValidSpellAndLoS(s.SpellId, HasLoS())) { continue; } if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { - SetCastedSpellType(spellType); + SetCastedSpellType(spell_type); - if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { BotGroupSay( this, fmt::format( "Casting {} [{}] on {}.", GetSpellName(s.SpellId), - GetSpellTypeNameByID(spellType), + GetSpellTypeNameByID(spell_type), tar->GetCleanName() ).c_str() ); @@ -490,16 +490,16 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell } } else { - if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { - SetCastedSpellType(spellType); + if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { + SetCastedSpellType(spell_type); - if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { BotGroupSay( this, fmt::format( "Casting {} [{}] on {}.", - GetSpellName(botSpell.SpellId), - GetSpellTypeNameByID(spellType), + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type), tar->GetCleanName() ).c_str() ); @@ -512,31 +512,31 @@ bool Bot::BotCastNuke(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell return false; } -bool Bot::BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spellType) { - botSpell = GetSpellByHealType(spellType, tar); +bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spell_type) { + bot_spell = GetSpellByHealType(spell_type, tar); - if (!IsValidSpell(botSpell.SpellId)) { + if (!IsValidSpell(bot_spell.SpellId)) { return false; } - if (AIDoSpellCast(botSpell.SpellIndex, tar, botSpell.ManaCost)) { - if (botClass != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - if (IsGroupSpell(botSpell.SpellId)) { + if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { + if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { + if (IsGroupSpell(bot_spell.SpellId)) { BotGroupSay( this, fmt::format( "Healing the group with {} [{}].", - GetSpellName(botSpell.SpellId), - GetSpellTypeNameByID(spellType) + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type) ).c_str() ); - if (botClass != Class::Bard) { + if (bot_class != Class::Bard) { const std::vector v = GatherSpellTargets(false, tar); if (!IsCommandedSpell()) { for (Mob* m : v) { - SetBotSpellRecastTimer(spellType, m, true); + SetBotSpellRecastTimer(spell_type, m, true); } } } @@ -548,14 +548,14 @@ bool Bot::BotCastHeal(Mob* tar, uint8 botClass, BotSpell& botSpell, uint16 spell fmt::format( "Healing {} with {} [{}].", (tar == this ? "myself" : tar->GetCleanName()), - GetSpellName(botSpell.SpellId), - GetSpellTypeNameByID(spellType) + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type) ).c_str() ); - if (botClass != Class::Bard) { + if (bot_class != Class::Bard) { if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spellType, tar, true); + SetBotSpellRecastTimer(spell_type, tar, true); } } } @@ -631,31 +631,31 @@ bool Bot::AI_PursueCastCheck() { return false; } - auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Pursue); + auto cast_order = GetSpellTypesPrioritized(BotPriorityCategories::Pursue); Mob* tar = nullptr; - for (auto& currentCast : castOrder) { - if (currentCast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); + for (auto& current_cast : cast_order) { + if (current_cast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } - if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (current_cast.spellType == BotSpellTypes::AEMez || current_cast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. + if (IsCommandedSpellType(current_cast.spellType)) { // Unsupported by AI currently. continue; } - if (AIBot_spells_by_type[currentCast.spellType].empty()) { + if (AIBot_spells_by_type[current_cast.spellType].empty()) { continue; } - result = AttemptAICastSpell(currentCast.spellType, nullptr); + result = AttemptAICastSpell(current_cast.spellType, nullptr); - if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { - result = AttemptCloseBeneficialSpells(currentCast.spellType); + if (!result && IsBotSpellTypeBeneficial(current_cast.spellType)) { + result = AttemptCloseBeneficialSpells(current_cast.spellType); } if (result) { @@ -685,7 +685,7 @@ bool Bot::AI_IdleCastCheck() { AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. - bool preCombat = false; + bool pre_combat = false; Client* test_against = nullptr; if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient()) { @@ -696,45 +696,45 @@ bool Bot::AI_IdleCastCheck() { } if (test_against) { - preCombat = test_against->GetBotPrecombat(); + pre_combat = test_against->GetBotPrecombat(); } - auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Idle); + auto cast_order = GetSpellTypesPrioritized(BotPriorityCategories::Idle); Mob* tar = nullptr; - for (auto& currentCast : castOrder) { - if (currentCast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); + for (auto& current_cast : cast_order) { + if (current_cast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } - if (!preCombat && (currentCast.spellType == BotSpellTypes::PreCombatBuff || currentCast.spellType == BotSpellTypes::PreCombatBuffSong)) { + if (!pre_combat && (current_cast.spellType == BotSpellTypes::PreCombatBuff || current_cast.spellType == BotSpellTypes::PreCombatBuffSong)) { continue; } - if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (current_cast.spellType == BotSpellTypes::AEMez || current_cast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. + if (IsCommandedSpellType(current_cast.spellType)) { // Unsupported by AI currently. continue; } - if (!IsBotSpellTypeBeneficial(currentCast.spellType)) { + if (!IsBotSpellTypeBeneficial(current_cast.spellType)) { continue; } - if (AIBot_spells_by_type[currentCast.spellType].empty()) { + if (AIBot_spells_by_type[current_cast.spellType].empty()) { continue; } - result = AttemptAICastSpell(currentCast.spellType, nullptr); + result = AttemptAICastSpell(current_cast.spellType, nullptr); if (result) { break; } - result = AttemptCloseBeneficialSpells(currentCast.spellType); + result = AttemptCloseBeneficialSpells(current_cast.spellType); if (result) { break; @@ -755,7 +755,6 @@ bool Bot::AI_EngagedCastCheck() { } bool result = false; - bool failedToCast = false; if (GetTarget() && AIautocastspell_timer->Check(false)) { @@ -768,31 +767,31 @@ bool Bot::AI_EngagedCastCheck() { return false; } - auto castOrder = GetSpellTypesPrioritized(BotPriorityCategories::Engaged); + auto cast_order = GetSpellTypesPrioritized(BotPriorityCategories::Engaged); Mob* tar = nullptr; - for (auto& currentCast : castOrder) { - if (currentCast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(currentCast.spellType)); + for (auto& current_cast : cast_order) { + if (current_cast.priority == 0) { + LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } - if (!RuleB(Bots, AllowAIMez) && (currentCast.spellType == BotSpellTypes::AEMez || currentCast.spellType == BotSpellTypes::Mez)) { + if (!RuleB(Bots, AllowAIMez) && (current_cast.spellType == BotSpellTypes::AEMez || current_cast.spellType == BotSpellTypes::Mez)) { continue; } - if (IsCommandedSpellType(currentCast.spellType)) { // Unsupported by AI currently. + if (IsCommandedSpellType(current_cast.spellType)) { // Unsupported by AI currently. continue; } - if (AIBot_spells_by_type[currentCast.spellType].empty()) { + if (AIBot_spells_by_type[current_cast.spellType].empty()) { continue; } - result = AttemptAICastSpell(currentCast.spellType, nullptr); + result = AttemptAICastSpell(current_cast.spellType, nullptr); - if (!result && IsBotSpellTypeBeneficial(currentCast.spellType)) { - result = AttemptCloseBeneficialSpells(currentCast.spellType); + if (!result && IsBotSpellTypeBeneficial(current_cast.spellType)) { + result = AttemptCloseBeneficialSpells(current_cast.spellType); } if (result) { @@ -896,37 +895,37 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { return castedSpell; } -std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spellType, int spellEffect) { +std::list Bot::GetBotSpellsForSpellEffect(Bot* caster, uint16 spell_type, int spell_effect) { std::list result; - if (!botCaster) { + if (!caster) { return result; } - if (auto bot_owner = botCaster->GetBotOwner(); !bot_owner) { + if (auto bot_owner = caster->GetBotOwner(); !bot_owner) { return result; } - if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); + if (caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { continue; } if ( - botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && - botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && - (IsEffectInSpell(botSpellList[i].spellid, spellEffect) || GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect)) + caster->CheckSpellRecastTimer(bot_spell_list[i].spellid) && + (bot_spell_list[i].type == spell_type || bot_spell_list[i].type == caster->GetParentSpellType(spell_type)) && + caster->IsValidSpellTypeBySpellID(spell_type, bot_spell_list[i].spellid) && + (IsEffectInSpell(bot_spell_list[i].spellid, spell_effect) || GetSpellTriggerSpellID(bot_spell_list[i].spellid, spell_effect)) ) { - BotSpell botSpell; - botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = botSpellList[i].index; - botSpell.ManaCost = botSpellList[i].manacost; + BotSpell bot_spell; + bot_spell.SpellId = bot_spell_list[i].spellid; + bot_spell.SpellIndex = bot_spell_list[i].index; + bot_spell.ManaCost = bot_spell_list[i].manacost; - result.push_back(botSpell); + result.push_back(bot_spell); } } } @@ -934,40 +933,40 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* botCaster, uint16 spell return result; } -std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, uint16 spellType, int spellEffect, SpellTargetType targetType) { +std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* caster, uint16 spell_type, int spell_effect, SpellTargetType target_type) { std::list result; - if (!botCaster) { + if (!caster) { return result; } - if (auto bot_owner = botCaster->GetBotOwner(); !bot_owner) { + if (auto bot_owner = caster->GetBotOwner(); !bot_owner) { return result; } - if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); + if (caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { continue; } if ( - botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && - botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && + caster->CheckSpellRecastTimer(bot_spell_list[i].spellid) && + (bot_spell_list[i].type == spell_type || bot_spell_list[i].type == caster->GetParentSpellType(spell_type)) && + caster->IsValidSpellTypeBySpellID(spell_type, bot_spell_list[i].spellid) && ( - IsEffectInSpell(botSpellList[i].spellid, spellEffect) || - GetSpellTriggerSpellID(botSpellList[i].spellid, spellEffect) + IsEffectInSpell(bot_spell_list[i].spellid, spell_effect) || + GetSpellTriggerSpellID(bot_spell_list[i].spellid, spell_effect) ) && - (targetType == ST_TargetOptional || spells[botSpellList[i].spellid].target_type == targetType) + (target_type == ST_TargetOptional || spells[bot_spell_list[i].spellid].target_type == target_type) ) { - BotSpell botSpell; - botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = botSpellList[i].index; - botSpell.ManaCost = botSpellList[i].manacost; - result.push_back(botSpell); + BotSpell bot_spell; + bot_spell.SpellId = bot_spell_list[i].spellid; + bot_spell.SpellIndex = bot_spell_list[i].index; + bot_spell.ManaCost = bot_spell_list[i].manacost; + result.push_back(bot_spell); } } } @@ -975,36 +974,36 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, return result; } -std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellType) { +std::list Bot::GetBotSpellsBySpellType(Bot* caster, uint16 spell_type) { std::list result; - if (!botCaster) { + if (!caster) { return result; } - if (auto bot_owner = botCaster->GetBotOwner(); !bot_owner) { + if (auto bot_owner = caster->GetBotOwner(); !bot_owner) { return result; } - if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); + if (caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { continue; } if ( - botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && - botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + caster->CheckSpellRecastTimer(bot_spell_list[i].spellid) && + (bot_spell_list[i].type == spell_type || bot_spell_list[i].type == caster->GetParentSpellType(spell_type)) && + caster->IsValidSpellTypeBySpellID(spell_type, bot_spell_list[i].spellid) ) { - BotSpell botSpell; - botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = botSpellList[i].index; - botSpell.ManaCost = botSpellList[i].manacost; + BotSpell bot_spell; + bot_spell.SpellId = bot_spell_list[i].spellid; + bot_spell.SpellIndex = bot_spell_list[i].index; + bot_spell.ManaCost = bot_spell_list[i].manacost; - result.push_back(botSpell); + result.push_back(bot_spell); } } } @@ -1012,77 +1011,77 @@ std::list Bot::GetBotSpellsBySpellType(Bot* botCaster, uint16 spellTyp return result; } -std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCaster, uint16 spellType, Mob* tar, bool AE, uint16 subTargetType, uint16 subType) { +std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* caster, uint16 spell_type, Mob* tar, bool AE, uint16 sub_target_type, uint16 sub_type) { std::vector result; - if (botCaster && botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); + if (caster && caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { continue; } - if (spellType == BotSpellTypes::HateRedux && botCaster->GetClass() == Class::Bard) { - if (spells[botSpellList[i].spellid].target_type != ST_Target) { + if (spell_type == BotSpellTypes::HateRedux && caster->GetClass() == Class::Bard) { + if (spells[bot_spell_list[i].spellid].target_type != ST_Target) { continue; } } if ( - botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && - botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + caster->CheckSpellRecastTimer(bot_spell_list[i].spellid) && + (bot_spell_list[i].type == spell_type || bot_spell_list[i].type == caster->GetParentSpellType(spell_type)) && + caster->IsValidSpellTypeBySpellID(spell_type, bot_spell_list[i].spellid) ) { if ( - botCaster->IsCommandedSpell() && + caster->IsCommandedSpell() && ( - !botCaster->IsValidSpellTypeSubType(spellType, subTargetType, botSpellList[i].spellid) || - !botCaster->IsValidSpellTypeSubType(spellType, subType, botSpellList[i].spellid) + !caster->IsValidSpellTypeSubType(spell_type, sub_target_type, bot_spell_list[i].spellid) || + !caster->IsValidSpellTypeSubType(spell_type, sub_type, bot_spell_list[i].spellid) ) ) { continue; } - if (!AE && IsAnyAESpell(botSpellList[i].spellid) && !IsGroupSpell(botSpellList[i].spellid)) { + if (!AE && IsAnyAESpell(bot_spell_list[i].spellid) && !IsGroupSpell(bot_spell_list[i].spellid)) { continue; } - else if (AE && !IsAnyAESpell(botSpellList[i].spellid)) { + else if (AE && !IsAnyAESpell(bot_spell_list[i].spellid)) { continue; } if ( - !botCaster->IsInGroupOrRaid(tar, true) && + !caster->IsInGroupOrRaid(tar, true) && ( !RuleB(Bots, EnableBotTGB) || ( - IsGroupSpell(botSpellList[i].spellid) && - !IsTGBCompatibleSpell(botSpellList[i].spellid) + IsGroupSpell(bot_spell_list[i].spellid) && + !IsTGBCompatibleSpell(bot_spell_list[i].spellid) ) ) ) { continue; } - if (!IsPBAESpell(botSpellList[i].spellid) && !botCaster->CastChecks(botSpellList[i].spellid, tar, spellType, false, IsAEBotSpellType(spellType))) { + if (!IsPBAESpell(bot_spell_list[i].spellid) && !caster->CastChecks(bot_spell_list[i].spellid, tar, spell_type, false, IsAEBotSpellType(spell_type))) { continue; } if ( - botCaster->IsCommandedSpell() || + caster->IsCommandedSpell() || !AE || ( - SpellTypeRequiresAEChecks(spellType) && - botCaster->HasValidAETarget(botCaster, botSpellList[i].spellid, spellType, tar) + SpellTypeRequiresAEChecks(spell_type) && + caster->HasValidAETarget(caster, bot_spell_list[i].spellid, spell_type, tar) ) ) { - BotSpell_wPriority botSpell; - botSpell.SpellId = botSpellList[i].spellid; - botSpell.SpellIndex = botSpellList[i].index; - botSpell.ManaCost = botSpellList[i].manacost; - botSpell.Priority = botSpellList[i].priority; + BotSpell_wPriority bot_spell; + bot_spell.SpellId = bot_spell_list[i].spellid; + bot_spell.SpellIndex = bot_spell_list[i].index; + bot_spell.ManaCost = bot_spell_list[i].manacost; + bot_spell.Priority = bot_spell_list[i].priority; - result.emplace_back(botSpell); + result.emplace_back(bot_spell); } } } @@ -1097,29 +1096,29 @@ std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* bot return result; } -BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { +BotSpell Bot::GetFirstBotSpellBySpellType(Bot* caster, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster && botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); + if (caster && caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { continue; } if ( - botCaster->CheckSpellRecastTimer(botSpellList[i].spellid) && - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && - botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) + caster->CheckSpellRecastTimer(bot_spell_list[i].spellid) && + (bot_spell_list[i].type == spell_type || bot_spell_list[i].type == caster->GetParentSpellType(spell_type)) && + caster->IsValidSpellTypeBySpellID(spell_type, bot_spell_list[i].spellid) ) { - result.SpellId = botSpellList[i].spellid; - result.SpellIndex = botSpellList[i].index; - result.ManaCost = botSpellList[i].manacost; + result.SpellId = bot_spell_list[i].spellid; + result.SpellIndex = bot_spell_list[i].index; + result.ManaCost = bot_spell_list[i].manacost; break; } @@ -1129,23 +1128,23 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint16 spellType) { return result; } -BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP); - for (auto botSpellListItr : botSpellList) { + for (auto bot_spell_list_itr : bot_spell_list) { // Assuming all the spells have been loaded into this list by level and in descending order if ( - IsVeryFastHealSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { - result.SpellId = botSpellListItr.SpellId; - result.SpellIndex = botSpellListItr.SpellIndex; - result.ManaCost = botSpellListItr.ManaCost; + IsVeryFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) { + result.SpellId = bot_spell_list_itr.SpellId; + result.SpellIndex = bot_spell_list_itr.SpellIndex; + result.ManaCost = bot_spell_list_itr.ManaCost; break; } @@ -1155,22 +1154,22 @@ BotSpell Bot::GetBestBotSpellForVeryFastHeal(Bot* botCaster, Mob* tar, uint16 sp return result; } -BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForFastHeal(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP); - for (auto botSpellListItr : botSpellList) { + for (auto bot_spell_list_itr : bot_spell_list) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsFastHealSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { - result.SpellId = botSpellListItr.SpellId; - result.SpellIndex = botSpellListItr.SpellIndex; - result.ManaCost = botSpellListItr.ManaCost; + if (IsFastHealSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) { + result.SpellId = bot_spell_list_itr.SpellId; + result.SpellIndex = bot_spell_list_itr.SpellIndex; + result.ManaCost = bot_spell_list_itr.ManaCost; break; } @@ -1180,22 +1179,22 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster, Mob* tar, uint16 spellT return result; } -BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime); - for (auto botSpellListItr : botSpellList) { + for (auto bot_spell_list_itr : bot_spell_list) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsHealOverTimeSpell(botSpellListItr.SpellId) && botCaster->CastChecks(botSpellListItr.SpellId, tar, spellType)) { - result.SpellId = botSpellListItr.SpellId; - result.SpellIndex = botSpellListItr.SpellIndex; - result.ManaCost = botSpellListItr.ManaCost; + if (IsHealOverTimeSpell(bot_spell_list_itr.SpellId) && caster->CastChecks(bot_spell_list_itr.SpellId, tar, spell_type)) { + result.SpellId = bot_spell_list_itr.SpellId; + result.SpellIndex = bot_spell_list_itr.SpellIndex; + result.ManaCost = bot_spell_list_itr.ManaCost; break; } @@ -1205,29 +1204,29 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster, Mob* tar, uint16 sp return result; } -BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster && botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { + if (caster && caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpell(bot_spell_list[i].spellid)) { continue; } if ( - (botSpellList[i].type == spellType || botSpellList[i].type == botCaster->GetParentSpellType(spellType)) && - botCaster->IsValidSpellTypeBySpellID(spellType, botSpellList[i].spellid) && - IsCompleteHealSpell(botSpellList[i].spellid) && - botCaster->CastChecks(botSpellList[i].spellid, tar, spellType) + (bot_spell_list[i].type == spell_type || bot_spell_list[i].type == caster->GetParentSpellType(spell_type)) && + caster->IsValidSpellTypeBySpellID(spell_type, bot_spell_list[i].spellid) && + IsCompleteHealSpell(bot_spell_list[i].spellid) && + caster->CastChecks(bot_spell_list[i].spellid, tar, spell_type) ) { - result.SpellId = botSpellList[i].spellid; - result.SpellIndex = botSpellList[i].index; - result.ManaCost = botSpellList[i].manacost; + result.SpellId = bot_spell_list[i].spellid; + result.SpellIndex = bot_spell_list[i].index; + result.ManaCost = bot_spell_list[i].manacost; break; } @@ -1237,22 +1236,22 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster, Mob* tar, uint16 return result; } -BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP); - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType)) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) { + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1262,22 +1261,22 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster, Mob* tar return result; } -BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP); - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType)) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (IsRegularSingleTargetHealSpell(bot_spell_list_itr->SpellId) && caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type)) { + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1287,38 +1286,38 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster, Mob* tar, uint return result; } -BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CurrentHP); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CurrentHP); - int targetCount = 0; + int target_count = 0; - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsRegularGroupHealSpell(botSpellListItr->SpellId)) { - if (!botCaster->IsCommandedSpell()) { - targetCount = 0; + if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) { + if (!caster->IsCommandedSpell()) { + target_count = 0; - for (Mob* m : botCaster->GetSpellTargetList()) { - if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { - ++targetCount; + for (Mob* m : caster->GetSpellTargetList()) { + if (caster->IsValidSpellRange(bot_spell_list_itr->SpellId, m) && caster->CastChecks(bot_spell_list_itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { + ++target_count; } } - if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { continue; } } - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1328,38 +1327,38 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster, Mob* tar, uint16 spell return result; } -BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_HealOverTime); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_HealOverTime); - int targetCount = 0; + int target_count = 0; - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) { - if (!botCaster->IsCommandedSpell()) { - targetCount = 0; + if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) { + if (!caster->IsCommandedSpell()) { + target_count = 0; - for (Mob* m : botCaster->GetSpellTargetList()) { - if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { - ++targetCount; + for (Mob* m : caster->GetSpellTargetList()) { + if (caster->IsValidSpellRange(bot_spell_list_itr->SpellId, m) && caster->CastChecks(bot_spell_list_itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { + ++target_count; } } - if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { continue; } } - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1369,38 +1368,38 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster, Mob* tar, uint return result; } -BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_CompleteHeal); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_CompleteHeal); - int targetCount = 0; + int target_count = 0; - for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for(std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsGroupCompleteHealSpell(botSpellListItr->SpellId)) { - if (!botCaster->IsCommandedSpell()) { - targetCount = 0; + if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) { + if (!caster->IsCommandedSpell()) { + target_count = 0; - for (Mob* m : botCaster->GetSpellTargetList()) { - if (botCaster->IsValidSpellRange(botSpellListItr->SpellId, m) && botCaster->CastChecks(botSpellListItr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { - ++targetCount; + for (Mob* m : caster->GetSpellTargetList()) { + if (caster->IsValidSpellRange(bot_spell_list_itr->SpellId, m) && caster->CastChecks(bot_spell_list_itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { + ++target_count; } } - if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { continue; } } - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1410,25 +1409,25 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster, Mob* tar, uint return result; } -BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForMez(Bot* caster, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Mez); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Mez); - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order if ( - IsMesmerizeSpell(botSpellListItr->SpellId) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) + IsMesmerizeSpell(bot_spell_list_itr->SpellId) && + caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1438,15 +1437,15 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster, uint16 spellType) { return result; } -Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellType, bool AE) { +Mob* Bot::GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_type, bool AE) { Mob* result = nullptr; - if (botCaster && botCaster->GetOwner()) { - int spellRange = (!AE ? botCaster->GetActSpellRange(spell_id, spells[spell_id].range) : botCaster->GetActSpellRange(spell_id, spells[spell_id].aoe_range)); + if (caster && caster->GetOwner()) { + int spell_range = (!AE ? caster->GetActSpellRange(spell_id, spells[spell_id].range) : caster->GetActSpellRange(spell_id, spells[spell_id].aoe_range)); int buff_count = 0; NPC* npc = nullptr; - for (auto& close_mob : botCaster->m_close_mobs) { + for (auto& close_mob : caster->m_close_mobs) { buff_count = 0; npc = close_mob.second->CastToNPC(); @@ -1454,66 +1453,66 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellT continue; } - if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), npc, spell_id)) { + if (!caster->IsValidMezTarget(caster->GetOwner(), npc, spell_id)) { continue; } if (AE) { - int targetCount = 0; + int target_count = 0; - for (auto& close_mob : botCaster->m_close_mobs) { + for (auto& close_mob : caster->m_close_mobs) { Mob* m = close_mob.second; if (npc == m) { continue; } - if (!botCaster->IsValidMezTarget(botCaster->GetOwner(), m, spell_id)) { + if (!caster->IsValidMezTarget(caster->GetOwner(), m, spell_id)) { continue; } if (IsPBAESpell(spell_id)) { - if (spellRange < Distance(botCaster->GetPosition(), m->GetPosition())) { + if (spell_range < Distance(caster->GetPosition(), m->GetPosition())) { continue; } } else { - if (spellRange < Distance(m->GetPosition(), npc->GetPosition())) { + if (spell_range < Distance(m->GetPosition(), npc->GetPosition())) { continue; } } - if (botCaster->CastChecks(spell_id, m, spellType, true, true)) { - ++targetCount; + if (caster->CastChecks(spell_id, m, spell_type, true, true)) { + ++target_count; } - if (targetCount >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + if (target_count >= caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { break; } } - if (targetCount < botCaster->GetSpellTypeAEOrGroupTargetCount(spellType)) { + if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { continue; } if (zone->random.Int(1, 100) < RuleI(Bots, AEMezChance)) { - botCaster->SetSpellTypeRecastTimer(spellType, RuleI(Bots, MezFailDelay)); + caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezFailDelay)); return result; } result = npc; } else { - if (spellRange < Distance(botCaster->GetPosition(), npc->GetPosition())) { + if (spell_range < Distance(caster->GetPosition(), npc->GetPosition())) { continue; } - if (!botCaster->CastChecks(spell_id, npc, spellType, true)) { + if (!caster->CastChecks(spell_id, npc, spell_type, true)) { continue; } if (zone->random.Int(1, 100) < RuleI(Bots, MezChance)) { - botCaster->SetSpellTypeRecastTimer(spellType, RuleI(Bots, MezAEFailDelay)); + caster->SetSpellTypeRecastTimer(spell_type, RuleI(Bots, MezAEFailDelay)); return result; } @@ -1522,7 +1521,7 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellT } if (result) { - botCaster->SetHasLoS(true); + caster->SetHasLoS(true); return result; } @@ -1532,27 +1531,27 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, int16 spell_id, uint16 spellT return result; } -BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster, uint16 spellType) { +BotSpell Bot::GetBestBotMagicianPetSpell(Bot* caster, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_SummonPet); - std::string petType = GetBotMagicianPetType(botCaster); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_SummonPet); + std::string pet_type = GetBotMagicianPetType(caster); - for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for(std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order if ( - IsSummonPetSpell(botSpellListItr->SpellId) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) && - !strncmp(spells[botSpellListItr->SpellId].teleport_zone, petType.c_str(), petType.length()) + IsSummonPetSpell(bot_spell_list_itr->SpellId) && + caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) && + !strncmp(spells[bot_spell_list_itr->SpellId].teleport_zone, pet_type.c_str(), pet_type.length()) ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1562,36 +1561,36 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster, uint16 spellType) { return result; } -std::string Bot::GetBotMagicianPetType(Bot* botCaster) { +std::string Bot::GetBotMagicianPetType(Bot* caster) { std::string result; - if (botCaster) { - uint8 petType = botCaster->GetPetChooserID(); - uint8 botLevel = botCaster->GetLevel(); - bool epicAllowed = false; - std::string epicSpellName = RuleS(Bots, EpicPetSpellName); + if (caster) { + uint8 pet_type = caster->GetPetChooserID(); + uint8 bot_level = caster->GetLevel(); + bool epic_allowed = false; + std::string epic_spell_name = RuleS(Bots, EpicPetSpellName); - if (epicSpellName.empty()) { - epicSpellName = "SumMageMultiElement"; + if (epic_spell_name.empty()) { + epic_spell_name = "SumMageMultiElement"; } if (RuleB(Bots, AllowMagicianEpicPet)) { - if (botLevel >= RuleI(Bots, AllowMagicianEpicPetLevel)) { + if (bot_level >= RuleI(Bots, AllowMagicianEpicPetLevel)) { if (!RuleI(Bots, RequiredMagicianEpicPetItemID)) { - epicAllowed = true; + epic_allowed = true; } else { - bool has_item = botCaster->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX; + bool has_item = caster->HasBotItem(RuleI(Bots, RequiredMagicianEpicPetItemID)) != INVALID_INDEX; if (has_item) { - epicAllowed = true; + epic_allowed = true; } } } } - if (petType > 0) { - switch (petType) { + if (pet_type > 0) { + switch (pet_type) { case SumWater: result = std::string("SumWater"); break; @@ -1608,25 +1607,25 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { result = std::string("MonsterSum"); break; case SumMageMultiElement: - if (epicAllowed) { - result = epicSpellName; + if (epic_allowed) { + result = epic_spell_name; } else { - botCaster->SetPetChooserID(0); + caster->SetPetChooserID(0); } break; } } else { - uint8 airMinLevel = 255; - uint8 fireMinLevel = 255; - uint8 waterMinLevel = 255; - uint8 earthMinLevel = 255; - uint8 monsterMinLevel = 255; - uint8 epicMinLevel = 255; - std::list botSpellList = botCaster->GetBotSpellsBySpellType(botCaster, BotSpellTypes::Pet); - - for (const auto& s : botSpellList) { + uint8 air_min_level = 255; + uint8 fire_min_level = 255; + uint8 water_min_level = 255; + uint8 earth_min_level = 255; + uint8 monster_min_level = 255; + uint8 epic_min_level = 255; + std::list bot_spell_list = caster->GetBotSpellsBySpellType(caster, BotSpellTypes::Pet); + + for (const auto& s : bot_spell_list) { if (!IsValidSpell(s.SpellId)) { continue; } @@ -1637,31 +1636,31 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { auto spell = spells[s.SpellId]; - if (!strncmp(spell.teleport_zone, "SumWater", 8) && spell.classes[Class::Magician - 1] < waterMinLevel) { - waterMinLevel = spell.classes[Class::Magician - 1]; + if (!strncmp(spell.teleport_zone, "SumWater", 8) && spell.classes[Class::Magician - 1] < water_min_level) { + water_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "SumFire", 7) && spell.classes[Class::Magician - 1] < fireMinLevel) { - fireMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "SumFire", 7) && spell.classes[Class::Magician - 1] < fire_min_level) { + fire_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "SumAir", 6) && spell.classes[Class::Magician - 1] < airMinLevel) { - airMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "SumAir", 6) && spell.classes[Class::Magician - 1] < air_min_level) { + air_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "SumEarth", 8) && spell.classes[Class::Magician - 1] < earthMinLevel) { - earthMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "SumEarth", 8) && spell.classes[Class::Magician - 1] < earth_min_level) { + earth_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "MonsterSum", 10) && spell.classes[Class::Magician - 1] < monsterMinLevel) { - monsterMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "MonsterSum", 10) && spell.classes[Class::Magician - 1] < monster_min_level) { + monster_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, epicSpellName.c_str(), epicSpellName.length()) && spell.classes[Class::Magician - 1] < epicMinLevel) { - epicMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, epic_spell_name.c_str(), epic_spell_name.length()) && spell.classes[Class::Magician - 1] < epic_min_level) { + epic_min_level = spell.classes[Class::Magician - 1]; } } - if (epicAllowed) { - epicMinLevel = std::max(int(epicMinLevel), RuleI(Bots, AllowMagicianEpicPetLevel)); + if (epic_allowed) { + epic_min_level = std::max(int(epic_min_level), RuleI(Bots, AllowMagicianEpicPetLevel)); - if (botLevel >= epicMinLevel) { - result = epicSpellName; + if (bot_level >= epic_min_level) { + result = epic_spell_name; } } else { @@ -1673,28 +1672,28 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { switch (counter) { case SumWater: - if (botLevel >= waterMinLevel) { + if (bot_level >= water_min_level) { result = std::string("SumWater"); } found = true; break; case SumFire: - if (botLevel >= fireMinLevel) { + if (bot_level >= fire_min_level) { result = std::string("SumFire"); } found = true; break; case SumAir: - if (botLevel >= airMinLevel) { + if (bot_level >= air_min_level) { result = std::string("SumAir"); } found = true; break; case SumEarth: - if (botLevel >= earthMinLevel) { + if (bot_level >= earth_min_level) { result = std::string("SumEarth"); } @@ -1711,7 +1710,7 @@ std::string Bot::GetBotMagicianPetType(Bot* botCaster) { return result; } -BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE, Mob* tar) { +BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* caster, SpellTargetType target_type, uint16 spell_type, bool AE, Mob* tar) { BotSpell result; result.SpellId = 0; @@ -1719,35 +1718,35 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType result.ManaCost = 0; if (tar == nullptr) { - tar = botCaster->GetTarget(); + tar = caster->GetTarget(); } - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_CurrentHP, targetType); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_CurrentHP, target_type); - for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for(std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsPureNukeSpell(botSpellListItr->SpellId) || IsDamageSpell(botSpellListItr->SpellId)) { - if (!AE && IsAnyAESpell(botSpellListItr->SpellId) && !IsGroupSpell(botSpellListItr->SpellId)) { + if (IsPureNukeSpell(bot_spell_list_itr->SpellId) || IsDamageSpell(bot_spell_list_itr->SpellId)) { + if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) { continue; } - else if (AE && !IsAnyAESpell(botSpellListItr->SpellId)) { + else if (AE && !IsAnyAESpell(bot_spell_list_itr->SpellId)) { continue; } - if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) { + if (!IsPBAESpell(bot_spell_list_itr->SpellId) && !caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type, false, IsAEBotSpellType(spell_type))) { continue; } if ( - botCaster->IsCommandedSpell() || + caster->IsCommandedSpell() || !AE || - (AE && botCaster->HasValidAETarget(botCaster, botSpellListItr->SpellId, spellType, tar)) + (AE && caster->HasValidAETarget(caster, bot_spell_list_itr->SpellId, spell_type, tar)) ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1758,7 +1757,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType return result; } -BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType targetType, uint16 spellType, bool AE, Mob* tar) +BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* caster, SpellTargetType target_type, uint16 spell_type, bool AE, Mob* tar) { BotSpell result; @@ -1767,36 +1766,36 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType result.ManaCost = 0; if (tar == nullptr) { - tar = botCaster->GetTarget(); + tar = caster->GetTarget(); } - if (botCaster) + if (caster) { - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_Stun, targetType); + std::list bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_Stun, target_type); - for(std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) + for(std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsStunSpell(botSpellListItr->SpellId)) { - if (!AE && IsAnyAESpell(botSpellListItr->SpellId) && !IsGroupSpell(botSpellListItr->SpellId)) { + if (IsStunSpell(bot_spell_list_itr->SpellId)) { + if (!AE && IsAnyAESpell(bot_spell_list_itr->SpellId) && !IsGroupSpell(bot_spell_list_itr->SpellId)) { continue; } - else if (AE && !IsAnyAESpell(botSpellListItr->SpellId)) { + else if (AE && !IsAnyAESpell(bot_spell_list_itr->SpellId)) { continue; } - if (!IsPBAESpell(botSpellListItr->SpellId) && !botCaster->CastChecks(botSpellListItr->SpellId, tar, spellType, false, IsAEBotSpellType(spellType))) { + if (!IsPBAESpell(bot_spell_list_itr->SpellId) && !caster->CastChecks(bot_spell_list_itr->SpellId, tar, spell_type, false, IsAEBotSpellType(spell_type))) { continue; } if ( - botCaster->IsCommandedSpell() || + caster->IsCommandedSpell() || !AE || - (AE && botCaster->HasValidAETarget(botCaster, botSpellListItr->SpellId, spellType, tar)) + (AE && caster->HasValidAETarget(caster, bot_spell_list_itr->SpellId, spell_type, tar)) ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1807,107 +1806,107 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType return result; } -BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* target, uint16 spellType) { +BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* caster, Mob* target, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster && target) { + if (caster && target) { - const int lureResisValue = -100; + const int lure_resis_value = -100; - int32 level_mod = (target->GetLevel() - botCaster->GetLevel()) * (target->GetLevel() - botCaster->GetLevel()) / 2; + int32 level_mod = (target->GetLevel() - caster->GetLevel()) * (target->GetLevel() - caster->GetLevel()) / 2; - if (target->GetLevel() - botCaster->GetLevel() < 0) { + if (target->GetLevel() - caster->GetLevel() < 0) { level_mod = -level_mod; } - const int maxTargetResistValue = botCaster->GetSpellTypeResistLimit(spellType); - bool selectLureNuke = false; + const int max_target_resist_value = caster->GetSpellTypeResistLimit(spell_type); + bool select_lure_nuke = false; - if (((target->GetMR() + level_mod) > maxTargetResistValue) && ((target->GetCR() + level_mod) > maxTargetResistValue) && ((target->GetFR() + level_mod) > maxTargetResistValue)) { - selectLureNuke = true; + if (((target->GetMR() + level_mod) > max_target_resist_value) && ((target->GetCR() + level_mod) > max_target_resist_value) && ((target->GetFR() + level_mod) > max_target_resist_value)) { + select_lure_nuke = true; } - std::list botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, spellType, SE_CurrentHP, ST_Target); + std::list bot_spell_list = GetBotSpellsForSpellEffectAndTargetType(caster, spell_type, SE_CurrentHP, ST_Target); - BotSpell firstWizardMagicNukeSpellFound; - firstWizardMagicNukeSpellFound.SpellId = 0; - firstWizardMagicNukeSpellFound.SpellIndex = 0; - firstWizardMagicNukeSpellFound.ManaCost = 0; - bool spellSelected = false; + BotSpell first_wizard_magic_nuke_spell_found; + first_wizard_magic_nuke_spell_found.SpellId = 0; + first_wizard_magic_nuke_spell_found.SpellIndex = 0; + first_wizard_magic_nuke_spell_found.ManaCost = 0; + bool spell_selected = false; - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (!botCaster->IsValidSpellRange(botSpellListItr->SpellId, target)) { + if (!caster->IsValidSpellRange(bot_spell_list_itr->SpellId, target)) { continue; } - if (selectLureNuke && (spells[botSpellListItr->SpellId].resist_difficulty < lureResisValue)) { - if (botCaster->CastChecks(botSpellListItr->SpellId, target, spellType)) { - spellSelected = true; + if (select_lure_nuke && (spells[bot_spell_list_itr->SpellId].resist_difficulty < lure_resis_value)) { + if (caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)) { + spell_selected = true; } } - else if (!selectLureNuke && IsPureNukeSpell(botSpellListItr->SpellId)) { + else if (!select_lure_nuke && IsPureNukeSpell(bot_spell_list_itr->SpellId)) { if ( ((target->GetMR() < target->GetCR()) || (target->GetMR() < target->GetFR())) && - (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && - (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && - botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + (GetSpellResistType(bot_spell_list_itr->SpellId) == RESIST_MAGIC) && + (spells[bot_spell_list_itr->SpellId].resist_difficulty > lure_resis_value) && + caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type) ) { - spellSelected = true; + spell_selected = true; } else if ( ((target->GetCR() < target->GetMR()) || (target->GetCR() < target->GetFR())) && - (GetSpellResistType(botSpellListItr->SpellId) == RESIST_COLD) && - (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && - botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + (GetSpellResistType(bot_spell_list_itr->SpellId) == RESIST_COLD) && + (spells[bot_spell_list_itr->SpellId].resist_difficulty > lure_resis_value) && + caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type) ) { - spellSelected = true; + spell_selected = true; } else if ( ((target->GetFR() < target->GetCR()) || (target->GetFR() < target->GetMR())) && - (GetSpellResistType(botSpellListItr->SpellId) == RESIST_FIRE) && - (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && - botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + (GetSpellResistType(bot_spell_list_itr->SpellId) == RESIST_FIRE) && + (spells[bot_spell_list_itr->SpellId].resist_difficulty > lure_resis_value) && + caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type) ) { - spellSelected = true; + spell_selected = true; } else if ( - (GetSpellResistType(botSpellListItr->SpellId) == RESIST_MAGIC) && - (spells[botSpellListItr->SpellId].resist_difficulty > lureResisValue) && - botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + (GetSpellResistType(bot_spell_list_itr->SpellId) == RESIST_MAGIC) && + (spells[bot_spell_list_itr->SpellId].resist_difficulty > lure_resis_value) && + caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type) ) { - firstWizardMagicNukeSpellFound.SpellId = botSpellListItr->SpellId; - firstWizardMagicNukeSpellFound.SpellIndex = botSpellListItr->SpellIndex; - firstWizardMagicNukeSpellFound.ManaCost = botSpellListItr->ManaCost; + first_wizard_magic_nuke_spell_found.SpellId = bot_spell_list_itr->SpellId; + first_wizard_magic_nuke_spell_found.SpellIndex = bot_spell_list_itr->SpellIndex; + first_wizard_magic_nuke_spell_found.ManaCost = bot_spell_list_itr->ManaCost; } } - if (spellSelected) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (spell_selected) { + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } } - if (!spellSelected) { - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + if (!spell_selected) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId)) { - if (botCaster->CastChecks(botSpellListItr->SpellId, target, spellType)) { - spellSelected = true; + if (caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId)) { + if (caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type)) { + spell_selected = true; } } - if (spellSelected) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + if (spell_selected) { + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -1915,38 +1914,38 @@ BotSpell Bot::GetBestBotWizardNukeSpellByTargetResists(Bot* botCaster, Mob* targ } if (result.SpellId == 0) { - result = firstWizardMagicNukeSpellFound; + result = first_wizard_magic_nuke_spell_found; } } return result; } -BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar, uint16 spellType) { +BotSpell Bot::GetDebuffBotSpell(Bot* caster, Mob *tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (!tar || !botCaster) + if (!tar || !caster) return result; - if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); + if (caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { continue; } - if (((botSpellList[i].type == BotSpellTypes::Debuff) || IsDebuffSpell(botSpellList[i].spellid)) - && (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster) - && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) - && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { - result.SpellId = botSpellList[i].spellid; - result.SpellIndex = botSpellList[i].index; - result.ManaCost = botSpellList[i].manacost; + if (((bot_spell_list[i].type == BotSpellTypes::Debuff) || IsDebuffSpell(bot_spell_list[i].spellid)) + && (!tar->IsImmuneToSpell(bot_spell_list[i].spellid, caster) + && tar->CanBuffStack(bot_spell_list[i].spellid, caster->GetLevel(), true) >= 0) + && caster->CheckSpellRecastTimer(bot_spell_list[i].spellid)) { + result.SpellId = bot_spell_list[i].spellid; + result.SpellIndex = bot_spell_list[i].index; + result.ManaCost = bot_spell_list[i].manacost; break; } @@ -1956,48 +1955,52 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar, uint16 spellType) { return result; } -BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* caster, Mob *tar, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (!tar || !botCaster) { + if (!tar || !caster) { return result; } - int level_mod = (tar->GetLevel() - botCaster->GetLevel())* (tar->GetLevel() - botCaster->GetLevel()) / 2; - if (tar->GetLevel() - botCaster->GetLevel() < 0) { + int level_mod = (tar->GetLevel() - caster->GetLevel())* (tar->GetLevel() - caster->GetLevel()) / 2; + if (tar->GetLevel() - caster->GetLevel() < 0) { level_mod = -level_mod; } - bool needsMagicResistDebuff = (tar->GetMR() + level_mod) > 100; - bool needsColdResistDebuff = (tar->GetCR() + level_mod) > 100; - bool needsFireResistDebuff = (tar->GetFR() + level_mod) > 100; - bool needsPoisonResistDebuff = (tar->GetPR() + level_mod) > 100; - bool needsDiseaseResistDebuff = (tar->GetDR() + level_mod) > 100; + bool needs_magic_resist_debuff = (tar->GetMR() + level_mod) > 100; + bool needs_cold_resist_debuff = (tar->GetCR() + level_mod) > 100; + bool needs_fire_resist_debuff = (tar->GetFR() + level_mod) > 100; + bool needs_poison_resist_debuff = (tar->GetPR() + level_mod) > 100; + bool needs_disease_resist_debuff = (tar->GetDR() + level_mod) > 100; - if (botCaster->AI_HasSpells()) { - std::vector botSpellList = botCaster->BotGetSpellsByType(spellType); + if (caster->AI_HasSpells()) { + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpellAndLoS(botSpellList[i].spellid, botCaster->HasLoS())) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { continue; } - if (((botSpellList[i].type == BotSpellTypes::Debuff) || IsResistDebuffSpell(botSpellList[i].spellid)) - && ((needsMagicResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistMagic)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) - || (needsColdResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistCold)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) - || (needsFireResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistFire)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) - || (needsPoisonResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistPoison)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll)) - || (needsDiseaseResistDebuff && (IsEffectInSpell(botSpellList[i].spellid, SE_ResistDisease)) || IsEffectInSpell(botSpellList[i].spellid, SE_ResistAll))) - && (!tar->IsImmuneToSpell(botSpellList[i].spellid, botCaster) - && tar->CanBuffStack(botSpellList[i].spellid, botCaster->GetLevel(), true) >= 0) - && botCaster->CheckSpellRecastTimer(botSpellList[i].spellid)) { - result.SpellId = botSpellList[i].spellid; - result.SpellIndex = botSpellList[i].index; - result.ManaCost = botSpellList[i].manacost; + if ( + (bot_spell_list[i].type == BotSpellTypes::Debuff || IsResistDebuffSpell(bot_spell_list[i].spellid)) && + ( + (needs_magic_resist_debuff && (IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistMagic) || IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistAll))) || + (needs_cold_resist_debuff && (IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistCold) || IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistAll))) || + (needs_fire_resist_debuff && (IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistFire) || IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistAll))) || + (needs_poison_resist_debuff && (IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistPoison) || IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistAll))) || + (needs_disease_resist_debuff && (IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistDisease) || IsEffectInSpell(bot_spell_list[i].spellid, SE_ResistAll))) + ) && + !tar->IsImmuneToSpell(bot_spell_list[i].spellid, caster) && + tar->CanBuffStack(bot_spell_list[i].spellid, caster->GetLevel(), true) >= 0 && + caster->CheckSpellRecastTimer(bot_spell_list[i].spellid) + ) { + result.SpellId = bot_spell_list[i].spellid; + result.SpellIndex = bot_spell_list[i].index; + result.ManaCost = bot_spell_list[i].manacost; break; } @@ -2007,7 +2010,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar, uint16 sp return result; } -BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForCure(Bot* caster, Mob* tar, uint16 spell_type) { BotSpell_wPriority result; result.SpellId = 0; @@ -2018,51 +2021,51 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) return result; } - if (botCaster) { - std::vector botSpellListItr = GetPrioritizedBotSpellsBySpellType(botCaster, spellType, tar); + if (caster) { + std::vector bot_spell_list_itr = GetPrioritizedBotSpellsBySpellType(caster, spell_type, tar); - if (IsGroupBotSpellType(spellType)) { - int countNeedsCured = 0; - uint16 countPoisoned = 0; - uint16 countDiseased = 0; - uint16 countCursed = 0; - uint16 countCorrupted = 0; - - for (std::vector::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + if (IsGroupBotSpellType(spell_type)) { + int count_needs_cured = 0; + uint16 count_poisoned = 0; + uint16 count_diseased = 0; + uint16 count_cursed = 0; + uint16 count_corrupted = 0; + + for (std::vector::iterator itr = bot_spell_list_itr.begin(); itr != bot_spell_list_itr.end(); ++itr) { if (!IsValidSpell(itr->SpellId) || !IsGroupSpell(itr->SpellId)) { continue; } - for (Mob* m : botCaster->GetSpellTargetList()) { - if (IsGroupBotSpellType(spellType)) { - if (!botCaster->IsInGroupOrRaid(m, true)) { + for (Mob* m : caster->GetSpellTargetList()) { + if (IsGroupBotSpellType(spell_type)) { + if (!caster->IsInGroupOrRaid(m, true)) { continue; } } - if (botCaster->GetNeedsCured(m)) { - if (botCaster->CastChecks(itr->SpellId, m, spellType, true, IsGroupBotSpellType(spellType))) { + if (caster->GetNeedsCured(m)) { + if (caster->CastChecks(itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { if (m->FindType(SE_PoisonCounter)) { - ++countPoisoned; + ++count_poisoned; } if (m->FindType(SE_DiseaseCounter)) { - ++countDiseased; + ++count_diseased; } if (m->FindType(SE_CurseCounter)) { - ++countCursed; + ++count_cursed; } if (m->FindType(SE_CorruptionCounter)) { - ++countCorrupted; + ++count_corrupted; } } } } if ( - (countPoisoned >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) || - (countDiseased >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) || - (countCursed >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) || - (countCorrupted >= botCaster->GetSpellTypeAEOrGroupTargetCount(spellType) && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) + (count_poisoned >= caster->GetSpellTypeAEOrGroupTargetCount(spell_type) && IsEffectInSpell(itr->SpellId, SE_PoisonCounter)) || + (count_diseased >= caster->GetSpellTypeAEOrGroupTargetCount(spell_type) && IsEffectInSpell(itr->SpellId, SE_DiseaseCounter)) || + (count_cursed >= caster->GetSpellTypeAEOrGroupTargetCount(spell_type) && IsEffectInSpell(itr->SpellId, SE_CurseCounter)) || + (count_corrupted >= caster->GetSpellTypeAEOrGroupTargetCount(spell_type) && IsEffectInSpell(itr->SpellId, SE_CorruptionCounter)) ) { result.SpellId = itr->SpellId; result.SpellIndex = itr->SpellIndex; @@ -2073,7 +2076,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) } } else { - for (std::vector::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + for (std::vector::iterator itr = bot_spell_list_itr.begin(); itr != bot_spell_list_itr.end(); ++itr) { if (!IsValidSpell(itr->SpellId) || IsGroupSpell(itr->SpellId)) { continue; } @@ -2096,9 +2099,9 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob* tar, uint16 spellType) return result; } -uint8 Bot::GetChanceToCastBySpellType(uint16 spellType) +uint8 Bot::GetChanceToCastBySpellType(uint16 spell_type) { - switch (spellType) { + switch (spell_type) { case BotSpellTypes::AENukes: case BotSpellTypes::AERains: case BotSpellTypes::AEStun: @@ -2773,43 +2776,43 @@ bool Bot::IsValidSpellRange(uint16 spell_id, Mob* tar) { return true; } -BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, uint16 spellType, bool AE, Mob* tar) { +BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* caster, uint8 body_type, uint16 spell_type, bool AE, Mob* tar) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (!botCaster || !bodyType) { + if (!caster || !body_type) { return result; } if (tar == nullptr) { - tar = botCaster->GetTarget(); + tar = caster->GetTarget(); } - switch (bodyType) { + switch (body_type) { case BodyType::Undead: case BodyType::SummonedUndead: case BodyType::Vampire: - result = GetBestBotSpellForNukeByTargetType(botCaster, (!AE ? ST_Undead : ST_UndeadAE), spellType, AE, tar); + result = GetBestBotSpellForNukeByTargetType(caster, (!AE ? ST_Undead : ST_UndeadAE), spell_type, AE, tar); break; case BodyType::Summoned: case BodyType::Summoned2: case BodyType::Summoned3: - result = GetBestBotSpellForNukeByTargetType(botCaster, (!AE ? ST_Summoned : ST_SummonedAE), spellType, AE, tar); + result = GetBestBotSpellForNukeByTargetType(caster, (!AE ? ST_Summoned : ST_SummonedAE), spell_type, AE, tar); break; case BodyType::Animal: - result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Animal, spellType, AE, tar); + result = GetBestBotSpellForNukeByTargetType(caster, ST_Animal, spell_type, AE, tar); break; case BodyType::Plant: - result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Plant, spellType, AE, tar); + result = GetBestBotSpellForNukeByTargetType(caster, ST_Plant, spell_type, AE, tar); break; case BodyType::Giant: - result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Giant, spellType, AE, tar); + result = GetBestBotSpellForNukeByTargetType(caster, ST_Giant, spell_type, AE, tar); break; case BodyType::Dragon: - result = GetBestBotSpellForNukeByTargetType(botCaster, ST_Dragon, spellType, AE, tar); + result = GetBestBotSpellForNukeByTargetType(caster, ST_Dragon, spell_type, AE, tar); break; default: break; @@ -2819,13 +2822,13 @@ BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* botCaster, uint8 bodyType, u } void Bot::CheckBotSpells() { - auto spellList = BotSpellsEntriesRepository::All(content_db); + auto spell_list = BotSpellsEntriesRepository::All(content_db); uint16 spell_id; SPDat_Spell_Struct spell; - uint16 correctType; - uint16 parentType; + uint16 correct_type; + uint16 parent_type; - for (const auto& s : spellList) { + for (const auto& s : spell_list) { if (!IsValidSpell(s.spell_id)) { LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); continue; @@ -2897,25 +2900,25 @@ void Bot::CheckBotSpells() { } } - correctType = GetCorrectSpellType(s.type, spell_id); - parentType = GetParentSpellType(correctType); + correct_type = GetCorrectSpellType(s.type, spell_id); + parent_type = GetParentSpellType(correct_type); if (RuleB(Bots, UseParentSpellTypeForChecks)) { - if (s.type == parentType || s.type == correctType) { + if (s.type == parent_type || s.type == correct_type) { continue; } } else { if (IsPetBotSpellType(s.type)) { - correctType = GetPetSpellType(correctType); + correct_type = GetPetSpellType(correct_type); } } - if (correctType == s.type) { + if (correct_type == s.type) { continue; } - if (correctType == UINT16_MAX) { + if (correct_type == UINT16_MAX) { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown." , GetSpellName(spell_id) , spell_id @@ -2929,42 +2932,42 @@ void Bot::CheckBotSpells() { , spell_id , GetSpellTypeNameByID(s.type) , s.type - , GetSpellTypeNameByID(correctType) - , correctType + , GetSpellTypeNameByID(correct_type) + , correct_type ); LogBotSpellTypeChecksDetail("UPDATE bot_spells_entries SET `type` = {} WHERE `spell_id` = {}; -- {} [#{}] from {} [#{}] to {} [#{}]" - , correctType + , correct_type , spell_id , GetSpellName(spell_id) , spell_id , GetSpellTypeNameByID(s.type) , s.type - , GetSpellTypeNameByID(correctType) - , correctType + , GetSpellTypeNameByID(correct_type) + , correct_type ); } } } -BotSpell Bot::GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Revive); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive); - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order if ( - IsResurrectSpell(botSpellListItr->SpellId) && - botCaster->CheckSpellRecastTimer(botSpellListItr->SpellId) + IsResurrectSpell(bot_spell_list_itr->SpellId) && + caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } @@ -2974,25 +2977,25 @@ BotSpell Bot::GetBestBotSpellForRez(Bot* botCaster, Mob* target, uint16 spellTyp return result; } -BotSpell Bot::GetBestBotSpellForCharm(Bot* botCaster, Mob* target, uint16 spellType) { +BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_type) { BotSpell result; result.SpellId = 0; result.SpellIndex = 0; result.ManaCost = 0; - if (botCaster) { - std::list botSpellList = GetBotSpellsForSpellEffect(botCaster, spellType, SE_Charm); + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm); - for (std::list::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order if ( - IsCharmSpell(botSpellListItr->SpellId) && - botCaster->CastChecks(botSpellListItr->SpellId, target, spellType) + IsCharmSpell(bot_spell_list_itr->SpellId) && + caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type) ) { - result.SpellId = botSpellListItr->SpellId; - result.SpellIndex = botSpellListItr->SpellIndex; - result.ManaCost = botSpellListItr->ManaCost; + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; break; } diff --git a/zone/client.cpp b/zone/client.cpp index 27480804bc..d5ea6c79a0 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13089,50 +13089,50 @@ void Client::LoadDefaultBotSettings() { } } -int Client::GetDefaultBotSettings(uint8 settingType, uint16 botSetting) { - switch (settingType) { +int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) { + switch (setting_type) { case BotSettingCategories::BaseSetting: return false; case BotSettingCategories::SpellHold: - return GetDefaultSpellHold(botSetting); + return GetDefaultSpellHold(bot_setting); case BotSettingCategories::SpellDelay: - return GetDefaultSpellDelay(botSetting); + return GetDefaultSpellDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: - return GetDefaultSpellMinThreshold(botSetting); + return GetDefaultSpellMinThreshold(bot_setting); case BotSettingCategories::SpellMaxThreshold: - return GetDefaultSpellMaxThreshold(botSetting); + return GetDefaultSpellMaxThreshold(bot_setting); } } -int Client::GetBotSetting(uint8 settingType, uint16 botSetting) { - switch (settingType) { +int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) { + switch (setting_type) { case BotSettingCategories::SpellHold: - return GetSpellHold(botSetting); + return GetSpellHold(bot_setting); case BotSettingCategories::SpellDelay: - return GetSpellDelay(botSetting); + return GetSpellDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: - return GetSpellMinThreshold(botSetting); + return GetSpellMinThreshold(bot_setting); case BotSettingCategories::SpellMaxThreshold: - return GetSpellMaxThreshold(botSetting); + return GetSpellMaxThreshold(bot_setting); } } -void Client::SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue) { - switch (settingType) { +void Client::SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value) { + switch (setting_type) { case BotSettingCategories::BaseSetting: - SetBaseSetting(botSetting, settingValue); + SetBaseSetting(bot_setting, setting_value); break; case BotSettingCategories::SpellHold: - SetSpellHold(botSetting, settingValue); + SetSpellHold(bot_setting, setting_value); break; case BotSettingCategories::SpellDelay: - SetSpellDelay(botSetting, settingValue); + SetSpellDelay(bot_setting, setting_value); break; case BotSettingCategories::SpellMinThreshold: - SetSpellMinThreshold(botSetting, settingValue); + SetSpellMinThreshold(bot_setting, setting_value); break; case BotSettingCategories::SpellMaxThreshold: - SetSpellMaxThreshold(botSetting, settingValue); + SetSpellMaxThreshold(bot_setting, setting_value); break; } } @@ -13148,9 +13148,9 @@ std::string Client::SendCommandHelpWindow( std::vector options_one, std::vector options_two, std::vector options_three ) { - unsigned stringLength = 0; - unsigned currentPlace = 0; - uint16 maxLength = RuleI(Command, MaxHelpLineLength); //character length of a line before splitting in to multiple lines + unsigned string_length = 0; + unsigned current_place = 0; + uint16 max_length = RuleI(Command, MaxHelpLineLength); //character length of a line before splitting in to multiple lines const std::string& description_color = RuleS(Command, DescriptionColor); const std::string& description_header_color = RuleS(Command, DescriptionHeaderColor); const std::string& alt_description_color = RuleS(Command, AltDescriptionColor); @@ -13177,78 +13177,74 @@ std::string Client::SendCommandHelpWindow( - std::string fillerLine = "--------------------------------------------------------------------"; - std::string fillerDia = DialogueWindow::TableRow(DialogueWindow::TableCell(fmt::format("{}", DialogueWindow::ColorMessage(filler_line_color, fillerLine)))); - std::string breakLine = DialogueWindow::Break(); + std::string filler_line = "--------------------------------------------------------------------"; + std::string filler_dia = DialogueWindow::TableRow(DialogueWindow::TableCell(fmt::format("{}", DialogueWindow::ColorMessage(filler_line_color, filler_line)))); + std::string break_line = DialogueWindow::Break(); std::string indent = "        "; std::string bullet = "- "; std::string popup_text = ""; /* - maxLength is how long you want lines to be before splitting them. This will look for the last space before the count and split there so words are not split mid sentence - Any SplitCommandHelpText can be be have the first string from a vector differ in color from the next strings by setting secondColor to true and assigning a color. - ex: SplitCommandHelpText(examples_one, example_color, maxLength, true, alt_example_color) - - This will make the first string from examples_one vector be the color of example_color and all following strings the color of alt_example_color - ex: SplitCommandHelpText(examples_one, example_color, maxLength) - - This will apply the color example_color to everything in examples_one vector + max_length is how long you want lines to be before splitting them. This will look for the last space before the count and split there so words are not split mid sentence + Any SplitCommandHelpText can have the first string from a vector differ in color from the next strings by setting an alternate color for that type in the rule. */ if (!description.empty()) { popup_text += GetCommandHelpHeader(description_header_color, "[Description]"); - popup_text += SplitCommandHelpText(description, description_color, maxLength, true, alt_description_color); + popup_text += SplitCommandHelpText(description, description_color, max_length, !alt_description_color.empty(), alt_description_color); } if (!notes.empty()) { - popup_text += breakLine; - popup_text += breakLine; + popup_text += break_line; + popup_text += break_line; popup_text += GetCommandHelpHeader(note_header_color, "[Notes]"); - popup_text += SplitCommandHelpText(notes, note_color, maxLength, true, alt_note_color); + popup_text += SplitCommandHelpText(notes, note_color, max_length, !alt_note_color.empty(), alt_note_color); } if (!example_format.empty()) { - popup_text += fillerDia; + popup_text += filler_dia; popup_text += GetCommandHelpHeader(example_header_color, "[Examples]"); - popup_text += SplitCommandHelpText(example_format, example_color, maxLength, true, alt_example_color); + popup_text += SplitCommandHelpText(example_format, example_color, max_length, !alt_example_color.empty(), alt_example_color); } if (!examples_one.empty()) { - popup_text += breakLine; - popup_text += breakLine; - popup_text += SplitCommandHelpText(examples_one, sub_example_color, maxLength, true, sub_alt_example_color); + popup_text += break_line; + popup_text += break_line; + popup_text += SplitCommandHelpText(examples_one, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); } if (!examples_two.empty()) { - popup_text += SplitCommandHelpText(examples_two, sub_example_color, maxLength, true, sub_alt_example_color); + popup_text += SplitCommandHelpText(examples_two, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); } if (!examples_three.empty()) { - popup_text += SplitCommandHelpText(examples_three, sub_example_color, maxLength, true, sub_alt_example_color); + popup_text += SplitCommandHelpText(examples_three, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); } if (!options.empty()) { - popup_text += fillerDia; + popup_text += filler_dia; popup_text += GetCommandHelpHeader(option_header_color, "[Options]"); - popup_text += SplitCommandHelpText(options, option_color, maxLength, true, alt_option_color); + popup_text += SplitCommandHelpText(options, option_color, max_length, !alt_option_color.empty(), alt_option_color); } if (!options_one.empty()) { - popup_text += breakLine; - popup_text += breakLine; - popup_text += SplitCommandHelpText(options_one, sub_option_color, maxLength, true, sub_alt_option_color); + popup_text += break_line; + popup_text += break_line; + popup_text += SplitCommandHelpText(options_one, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); } if (!options_two.empty()) { - popup_text += SplitCommandHelpText(options_two, sub_option_color, maxLength, true, sub_alt_option_color); + popup_text += SplitCommandHelpText(options_two, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); } if (!options_three.empty()) { - popup_text += SplitCommandHelpText(options_three, secondary_header_color, maxLength, true, sub_alt_option_color); + popup_text += SplitCommandHelpText(options_three, secondary_header_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); } if (!actionables.empty()) { - popup_text += fillerDia; + popup_text += filler_dia; popup_text += GetCommandHelpHeader(actionable_header_color, "[Actionables]"); - popup_text += SplitCommandHelpText(actionables, actionable_color, maxLength, true, alt_actionable_color); + popup_text += SplitCommandHelpText(actionables, actionable_color, max_length, !alt_actionable_color.empty(), alt_actionable_color); } popup_text = DialogueWindow::Table(popup_text); @@ -13257,7 +13253,7 @@ std::string Client::SendCommandHelpWindow( } std::string Client::GetCommandHelpHeader(std::string color, std::string header) { - std::string returnText = DialogueWindow::TableRow( + std::string return_text = DialogueWindow::TableRow( DialogueWindow::TableCell( fmt::format( "{}", @@ -13266,58 +13262,58 @@ std::string Client::GetCommandHelpHeader(std::string color, std::string header) ) ); - return returnText; + return return_text; } -std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor, std::string secondaryColor) { - std::string returnText; +std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 max_length, bool second_color, std::string secondary_color) { + std::string return_text; for (int i = 0; i < msg.size(); i++) { std::vector msg_split; - int stringLength = msg[i].length() + 1; - int endCount = 0; - int newCount = 0; - int splitCount = 0; + int string_length = msg[i].length() + 1; + int end_count = 0; + int new_count = 0; + int split_count = 0; - for (int x = 0; x < stringLength; x = endCount) { - endCount = std::min(int(stringLength), (int(x) + std::min(int(stringLength), int(maxLength)))); + for (int x = 0; x < string_length; x = end_count) { + end_count = std::min(int(string_length), (int(x) + std::min(int(string_length), int(max_length)))); - if ((stringLength - (x + 1)) > maxLength) { - for (int y = endCount; y >= x; --y) { + if ((string_length - (x + 1)) > max_length) { + for (int y = end_count; y >= x; --y) { if (msg[i][y] == ' ') { - splitCount = y - x; - msg_split.emplace_back(msg[i].substr(x, splitCount)); - endCount = y + 1; + split_count = y - x; + msg_split.emplace_back(msg[i].substr(x, split_count)); + end_count = y + 1; break; } if (y == x) { - msg_split.emplace_back(msg[i].substr(x, maxLength)); + msg_split.emplace_back(msg[i].substr(x, max_length)); break; } } } else { - msg_split.emplace_back(msg[i].substr(x, (stringLength - 1) - x)); + msg_split.emplace_back(msg[i].substr(x, (string_length - 1) - x)); break; } } for (const auto& s : msg_split) { - returnText += DialogueWindow::TableRow( - DialogueWindow::TableCell(DialogueWindow::ColorMessage(((secondColor && i == 0) ? color : secondaryColor), s)) + return_text += DialogueWindow::TableRow( + DialogueWindow::TableCell(DialogueWindow::ColorMessage(((second_color && i == 0) ? color : secondary_color), s)) ); } } - return returnText; + return return_text; } -void Client::SendSpellTypePrompts(bool commandedTypes, bool clientOnlyTypes) { - if (clientOnlyTypes) { +void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) { + if (client_only_types) { Message( Chat::Yellow, fmt::format( @@ -13358,7 +13354,7 @@ void Client::SendSpellTypePrompts(bool commandedTypes, bool clientOnlyTypes) { ); } - if (commandedTypes) { + if (commanded_types) { Message( Chat::Yellow, fmt::format( diff --git a/zone/client.h b/zone/client.h index d7d01cda3d..9da15a9b03 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2239,9 +2239,9 @@ class Client : public Mob void SpawnRaidBotsOnConnect(Raid* raid); void LoadDefaultBotSettings(); - int GetDefaultBotSettings(uint8 settingType, uint16 botSetting); - int GetBotSetting(uint8 settingType, uint16 botSetting); - void SetBotSetting(uint8 settingType, uint16 botSetting, uint32 settingValue); + int GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting); + int GetBotSetting(uint8 setting_type, uint16 bot_setting); + void SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value); private: bool bot_owner_options[_booCount]; From 90e95572fd863155d27832e770935bfefa636af1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:10:12 -0600 Subject: [PATCH 264/394] code cleanup 3 --- zone/bot_commands/behind_mob.cpp | 10 +- zone/bot_commands/blocked_buffs.cpp | 68 +++---- zone/bot_commands/cast.cpp | 196 +++++++++--------- zone/bot_commands/copy_settings.cpp | 90 ++++----- zone/bot_commands/default_settings.cpp | 200 +++++++++---------- zone/bot_commands/depart.cpp | 40 ++-- zone/bot_commands/discipline.cpp | 66 +++--- zone/bot_commands/follow.cpp | 38 ++-- zone/bot_commands/illusion_block.cpp | 10 +- zone/bot_commands/item_use.cpp | 8 +- zone/bot_commands/max_melee_range.cpp | 10 +- zone/bot_commands/pet.cpp | 127 ++++++------ zone/bot_commands/pull.cpp | 8 +- zone/bot_commands/sit_hp_percent.cpp | 10 +- zone/bot_commands/sit_in_combat.cpp | 10 +- zone/bot_commands/sit_mana_percent.cpp | 10 +- zone/bot_commands/spell_aggro_checks.cpp | 28 +-- zone/bot_commands/spell_delays.cpp | 28 +-- zone/bot_commands/spell_engaged_priority.cpp | 28 +-- zone/bot_commands/spell_holds.cpp | 28 +-- zone/bot_commands/spell_idle_priority.cpp | 28 +-- zone/bot_commands/spell_max_hp_pct.cpp | 28 +-- zone/bot_commands/spell_max_mana_pct.cpp | 28 +-- zone/bot_commands/spell_max_thresholds.cpp | 28 +-- zone/bot_commands/spell_min_hp_pct.cpp | 28 +-- zone/bot_commands/spell_min_mana_pct.cpp | 28 +-- zone/bot_commands/spell_min_thresholds.cpp | 28 +-- zone/bot_commands/spell_pursue_priority.cpp | 28 +-- zone/bot_commands/spell_target_count.cpp | 28 +-- zone/bot_commands/taunt.cpp | 52 ++--- zone/bot_raid.cpp | 4 +- zone/gm_commands/illusion_block.cpp | 8 +- zone/gm_commands/spell_delays.cpp | 26 +-- zone/gm_commands/spell_holds.cpp | 26 +-- zone/gm_commands/spell_max_thresholds.cpp | 26 +-- zone/gm_commands/spell_min_thresholds.cpp | 26 +-- zone/groups.cpp | 12 +- zone/groups.h | 2 +- 38 files changed, 718 insertions(+), 729 deletions(-) diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 0385ad5b20..6bbfdd1687 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -82,12 +82,12 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) int ab_arg = 1; bool current_check = false; - uint32 typeValue = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - typeValue = atoi(sep->arg[1]); + type_value = atoi(sep->arg[1]); ++ab_arg; - if (typeValue < 0 || typeValue > 1) { + if (type_value < 0 || type_value > 1) { c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); return; @@ -147,7 +147,7 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) ); } else { - my_bot->SetBehindMob(typeValue); + my_bot->SetBehindMob(type_value); ++success_count; } } @@ -168,7 +168,7 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) fmt::format( "{} of your bots {} stay behind mobs.", success_count, - typeValue ? "will now" : "will no longer" + type_value ? "will now" : "will no longer" ).c_str() ); } diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index d41dd54bc5..ebe50f5ac6 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -149,10 +149,10 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "target"; + if (actionable_arg.empty()) { + actionable_arg = "target"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -164,23 +164,23 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - bool isSuccess = false; - uint16 successCount = 0; - Bot* firstFound = nullptr; + bool is_success = false; + uint16 success_count = 0; + Bot* first_found = nullptr; for (auto bot_iter : sbl) { if (!bot_iter->IsInGroupOrRaid(c)) { continue; } - if (!firstFound) { - firstFound = bot_iter; + if (!first_found) { + first_found = bot_iter; } if (add) { @@ -190,11 +190,11 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) bot_iter->SetBotBlockedBuff(spell_id, false); } else if (list) { - std::vector blockedBuffs = bot_iter->GetBotBlockedBuffs(); + std::vector blocked_buffs = bot_iter->GetBotBlockedBuffs(); bool found = false; - if (!blockedBuffs.empty()) { - for (auto& blocked_buff : blockedBuffs) { + if (!blocked_buffs.empty()) { + for (auto& blocked_buff : blocked_buffs) { if (blocked_buff.blocked == 1 && IsValidSpell(blocked_buff.spell_id)) { found = true; c->Message( @@ -232,11 +232,11 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) ); } - isSuccess = true; - ++successCount; + is_success = true; + ++success_count; } - if (!isSuccess) { + if (!is_success) { c->Message(Chat::Yellow, "No bots were selected."); } else { @@ -245,8 +245,8 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "{} {} {} blocking {} [#{}]", - ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), - ((successCount == 1 && firstFound) ? "is" : "of your bots"), + ((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())), + ((success_count == 1 && first_found) ? "is" : "of your bots"), (add ? "now" : "no longer"), spells[spell_id].name, spell_id @@ -408,10 +408,10 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "target"; + if (actionable_arg.empty()) { + actionable_arg = "target"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -423,23 +423,23 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - bool isSuccess = false; - uint16 successCount = 0; - Bot* firstFound = nullptr; + bool is_success = false; + uint16 success_count = 0; + Bot* first_found = nullptr; for (auto bot_iter : sbl) { if (!bot_iter->IsInGroupOrRaid(c)) { continue; } - if (!firstFound) { - firstFound = bot_iter; + if (!first_found) { + first_found = bot_iter; } if (add) { @@ -449,11 +449,11 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) bot_iter->SetBotBlockedPetBuff(spell_id, false); } else if (list) { - std::vector blockedBuffs = bot_iter->GetBotBlockedBuffs(); + std::vector blocked_buffs = bot_iter->GetBotBlockedBuffs(); bool found = false; - if (!blockedBuffs.empty()) { - for (auto& blocked_buff : blockedBuffs) { + if (!blocked_buffs.empty()) { + for (auto& blocked_buff : blocked_buffs) { if (blocked_buff.blocked_pet == 1 && IsValidSpell(blocked_buff.spell_id)) { found = true; c->Message( @@ -491,11 +491,11 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) ); } - isSuccess = true; - ++successCount; + is_success = true; + ++success_count; } - if (!isSuccess) { + if (!is_success) { c->Message(Chat::Yellow, "No bots were selected."); } else { @@ -504,8 +504,8 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "{} {} {} blocking {} [#{}] on their pet.", - ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), - ((successCount == 1 && firstFound) ? "is" : "of your bots"), + ((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())), + ((success_count == 1 && first_found) ? "is" : "of your bots"), (add ? "now" : "no longer"), spells[spell_id].name, spell_id diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 7f1093cf25..2d453c6f34 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -175,13 +175,13 @@ void bot_command_cast(Client* c, const Seperator* sep) } int ab_arg = 2; - uint16 spellType = UINT16_MAX; - uint16 subType = UINT16_MAX; - uint16 subTargetType = UINT16_MAX; - bool aaType = false; - int aaID = 0; - bool bySpellID = false; - uint16 chosenSpellID = UINT16_MAX; + uint16 spell_type = UINT16_MAX; + uint16 sub_type = UINT16_MAX; + uint16 sub_target_type = UINT16_MAX; + bool aa_type = false; + int aa_id = 0; + bool by_spell_id = false; + uint16 chosen_spell_id = UINT16_MAX; if (!arg1.compare("aa") || !arg1.compare("harmtouch") || !arg1.compare("layonhands")) { if (!RuleB(Bots, AllowCastAAs)) { @@ -190,10 +190,10 @@ void bot_command_cast(Client* c, const Seperator* sep) } if (!arg1.compare("harmtouch")) { - aaID = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id; + aa_id = zone->GetAlternateAdvancementAbilityByRank(aaHarmTouch)->id; } else if (!arg1.compare("layonhands")) { - aaID = zone->GetAlternateAdvancementAbilityByRank(aaLayonHands)->id; + aa_id = zone->GetAlternateAdvancementAbilityByRank(aaLayonHands)->id; } else if (!sep->IsNumber(2) || !zone->GetAlternateAdvancementAbility(Strings::ToInt(arg2))) { c->Message(Chat::Yellow, "You must enter an AA ID."); @@ -201,10 +201,10 @@ void bot_command_cast(Client* c, const Seperator* sep) } else { ++ab_arg; - aaID = Strings::ToInt(arg2); + aa_id = Strings::ToInt(arg2); } - aaType = true; + aa_type = true; } if (!arg1.compare("spellid")) { @@ -215,8 +215,8 @@ void bot_command_cast(Client* c, const Seperator* sep) if (sep->IsNumber(2) && IsValidSpell(atoi(sep->arg[2]))) { ++ab_arg; - chosenSpellID = atoi(sep->arg[2]); - bySpellID = true; + chosen_spell_id = atoi(sep->arg[2]); + by_spell_id = true; } else { c->Message(Chat::Yellow, "You must enter a valid spell ID."); @@ -225,11 +225,11 @@ void bot_command_cast(Client* c, const Seperator* sep) } } - if (!aaType && !bySpellID) { + if (!aa_type && !by_spell_id) { if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || (spellType > BotSpellTypes::END && spellType < BotSpellTypes::COMMANDED_START) || spellType > BotSpellTypes::COMMANDED_END) { + if (spell_type < BotSpellTypes::START || (spell_type > BotSpellTypes::END && spell_type < BotSpellTypes::COMMANDED_START) || spell_type > BotSpellTypes::COMMANDED_END) { c->Message( Chat::Yellow, fmt::format( @@ -245,7 +245,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -262,7 +262,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } } - switch (spellType) { //Allowed command checks + switch (spell_type) { //Allowed command checks case BotSpellTypes::Charm: if (!RuleB(Bots, AllowCommandedCharm)) { c->Message(Chat::Yellow, "This commanded type is currently disabled."); @@ -304,50 +304,50 @@ void bot_command_cast(Client* c, const Seperator* sep) break; } - std::string argString = sep->arg[ab_arg]; + std::string arg_string = sep->arg[ab_arg]; - if (!argString.compare("shrink")) { - subType = CommandedSubTypes::Shrink; + if (!arg_string.compare("shrink")) { + sub_type = CommandedSubTypes::Shrink; ++ab_arg; } - else if (!argString.compare("grow")) { - subType = CommandedSubTypes::Grow; + else if (!arg_string.compare("grow")) { + sub_type = CommandedSubTypes::Grow; ++ab_arg; } - else if (!argString.compare("see")) { - subType = CommandedSubTypes::SeeInvis; + else if (!arg_string.compare("see")) { + sub_type = CommandedSubTypes::SeeInvis; ++ab_arg; } - else if (!argString.compare("invis")) { - subType = CommandedSubTypes::Invis; + else if (!arg_string.compare("invis")) { + sub_type = CommandedSubTypes::Invis; ++ab_arg; } - else if (!argString.compare("undead")) { - subType = CommandedSubTypes::InvisUndead; + else if (!arg_string.compare("undead")) { + sub_type = CommandedSubTypes::InvisUndead; ++ab_arg; } - else if (!argString.compare("animals")) { - subType = CommandedSubTypes::InvisAnimals; + else if (!arg_string.compare("animals")) { + sub_type = CommandedSubTypes::InvisAnimals; ++ab_arg; } - else if (!argString.compare("selo")) { - subType = CommandedSubTypes::Selo; + else if (!arg_string.compare("selo")) { + sub_type = CommandedSubTypes::Selo; ++ab_arg; } - argString = sep->arg[ab_arg]; + arg_string = sep->arg[ab_arg]; - if (!argString.compare("single")) { - subTargetType = CommandedSubTypes::SingleTarget; + if (!arg_string.compare("single")) { + sub_target_type = CommandedSubTypes::SingleTarget; ++ab_arg; } - else if (!argString.compare("group")) { - subTargetType = CommandedSubTypes::GroupTarget; + else if (!arg_string.compare("group")) { + sub_target_type = CommandedSubTypes::GroupTarget; ++ab_arg; } - else if (!argString.compare("ae")) { - subTargetType = CommandedSubTypes::AETarget; + else if (!arg_string.compare("ae")) { + sub_target_type = CommandedSubTypes::AETarget; ++ab_arg; } } @@ -355,27 +355,27 @@ void bot_command_cast(Client* c, const Seperator* sep) Mob* tar = c->GetTarget(); if (!tar) { - if ((!aaType && !bySpellID) && spellType != BotSpellTypes::Escape && spellType != BotSpellTypes::Pet) { + if ((!aa_type && !by_spell_id) && spell_type != BotSpellTypes::Escape && spell_type != BotSpellTypes::Pet) { c->Message(Chat::Yellow, "You need a target for that."); return; } } - if (!aaType && !bySpellID) { - if (IsPetBotSpellType(spellType) && !tar->IsPet()) { + if (!aa_type && !by_spell_id) { + if (IsPetBotSpellType(spell_type) && !tar->IsPet()) { c->Message( Chat::Yellow, fmt::format( "[{}] is an invalid target. {} requires a pet to be targeted.", tar->GetCleanName(), - tar->GetSpellTypeNameByID(spellType) + tar->GetSpellTypeNameByID(spell_type) ).c_str() ); return; } - switch (spellType) { //Target Checks + switch (spell_type) { //Target Checks case BotSpellTypes::Resurrect: if (!tar->IsCorpse() || !tar->CastToCorpse()->IsPlayerCorpse()) { c->Message( @@ -409,9 +409,9 @@ void bot_command_cast(Client* c, const Seperator* sep) break; default: if ( - (IsBotSpellTypeDetrimental(spellType) && !c->IsAttackAllowed(tar)) || + (IsBotSpellTypeDetrimental(spell_type) && !c->IsAttackAllowed(tar)) || ( - spellType == BotSpellTypes::Charm && + spell_type == BotSpellTypes::Charm && ( tar->IsClient() || tar->IsCorpse() || @@ -430,7 +430,7 @@ void bot_command_cast(Client* c, const Seperator* sep) return; } - if (IsBotSpellTypeBeneficial(spellType)) { + if (IsBotSpellTypeBeneficial(spell_type)) { if ( (tar->IsNPC() && !tar->GetOwner()) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !c->IsInGroupOrRaid(tar->GetOwner())) || @@ -453,7 +453,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } if ( - (spellType == BotSpellTypes::Cure || spellType == BotSpellTypes::GroupCures || spellType == BotSpellTypes::PetCures) && + (spell_type == BotSpellTypes::Cure || spell_type == BotSpellTypes::GroupCures || spell_type == BotSpellTypes::PetCures) && !c->CastToBot()->GetNeedsCured(tar) ) { c->Message( @@ -468,10 +468,10 @@ void bot_command_cast(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "spawned"; + if (actionable_arg.empty()) { + actionable_arg = "spawned"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -483,19 +483,19 @@ void bot_command_cast(Client* c, const Seperator* sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - BotSpell botSpell; - botSpell.SpellId = 0; - botSpell.SpellIndex = 0; - botSpell.ManaCost = 0; - bool isSuccess = false; - uint16 successCount = 0; - Bot* firstFound = nullptr; + BotSpell bot_spell; + bot_spell.SpellId = 0; + bot_spell.SpellIndex = 0; + bot_spell.ManaCost = 0; + bool is_success = false; + uint16 success_count = 0; + Bot* first_found = nullptr; for (auto bot_iter : sbl) { if (!bot_iter->IsInGroupOrRaid(c)) { @@ -506,31 +506,31 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - Mob* newTar = tar; + Mob* new_tar = tar; - if (!aaType && !bySpellID) { - if (!SpellTypeRequiresTarget(spellType)) { - newTar = bot_iter; + if (!aa_type && !by_spell_id) { + if (!SpellTypeRequiresTarget(spell_type)) { + new_tar = bot_iter; } - if (!newTar) { + if (!new_tar) { continue; } if ( - IsBotSpellTypeBeneficial(spellType) && + IsBotSpellTypeBeneficial(spell_type) && !RuleB(Bots, CrossRaidBuffingAndHealing) && - !bot_iter->IsInGroupOrRaid(newTar, true) + !bot_iter->IsInGroupOrRaid(new_tar, true) ) { continue; } - if (IsBotSpellTypeDetrimental(spellType) && !bot_iter->IsAttackAllowed(newTar)) { + if (IsBotSpellTypeDetrimental(spell_type) && !bot_iter->IsAttackAllowed(new_tar)) { bot_iter->BotGroupSay( bot_iter, fmt::format( "I cannot attack [{}].", - newTar->GetCleanName() + new_tar->GetCleanName() ).c_str() ); @@ -538,14 +538,14 @@ void bot_command_cast(Client* c, const Seperator* sep) } } - if (aaType) { - if (!bot_iter->GetAA(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id)) { + if (aa_type) { + if (!bot_iter->GetAA(zone->GetAlternateAdvancementAbility(aa_id)->first_rank_id)) { continue; } - AA::Rank* tempRank = nullptr; - AA::Rank*& rank = tempRank; - uint16 spell_id = bot_iter->GetSpellByAA(aaID, rank); + AA::Rank* temp_rank = nullptr; + AA::Rank*& rank = temp_rank; + uint16 spell_id = bot_iter->GetSpellByAA(aa_id, rank); if (!IsValidSpell(spell_id)) { continue; @@ -555,27 +555,27 @@ void bot_command_cast(Client* c, const Seperator* sep) continue; } - isSuccess = true; - ++successCount; + is_success = true; + ++success_count; continue; } - else if (bySpellID) { - if (!bot_iter->CanUseBotSpell(chosenSpellID)) { + else if (by_spell_id) { + if (!bot_iter->CanUseBotSpell(chosen_spell_id)) { continue; } - if (!tar || (spells[chosenSpellID].target_type == ST_Self && tar != bot_iter)) { + if (!tar || (spells[chosen_spell_id].target_type == ST_Self && tar != bot_iter)) { tar = bot_iter; } - if (bot_iter->AttemptForcedCastSpell(tar, chosenSpellID)) { - if (!firstFound) { - firstFound = bot_iter; + if (bot_iter->AttemptForcedCastSpell(tar, chosen_spell_id)) { + if (!first_found) { + first_found = bot_iter; } - isSuccess = true; - ++successCount; + is_success = true; + ++success_count; } continue; @@ -583,13 +583,13 @@ void bot_command_cast(Client* c, const Seperator* sep) else { bot_iter->SetCommandedSpell(true); - if (bot_iter->AICastSpell(newTar, 100, spellType, subTargetType, subType)) { - if (!firstFound) { - firstFound = bot_iter; + if (bot_iter->AICastSpell(new_tar, 100, spell_type, sub_target_type, sub_type)) { + if (!first_found) { + first_found = bot_iter; } - isSuccess = true; - ++successCount; + is_success = true; + ++success_count; } bot_iter->SetCommandedSpell(false); @@ -602,22 +602,22 @@ void bot_command_cast(Client* c, const Seperator* sep) std::string type = ""; - if (aaType) { - type = zone->GetAAName(zone->GetAlternateAdvancementAbility(aaID)->first_rank_id); + if (aa_type) { + type = zone->GetAAName(zone->GetAlternateAdvancementAbility(aa_id)->first_rank_id); } - else if (bySpellID) { + else if (by_spell_id) { type = "Forced"; } else { - type = c->GetSpellTypeNameByID(spellType); + type = c->GetSpellTypeNameByID(spell_type); } - if (!isSuccess) { + if (!is_success) { c->Message( Chat::Yellow, fmt::format( "No bots are capable of casting [{}] on {}. This could be due to this to any number of things: range, mana, immune, target type, etc.", - (bySpellID ? spells[chosenSpellID].name : type), + (by_spell_id ? spells[chosen_spell_id].name : type), tar ? tar->GetCleanName() : "your target" ).c_str() ); @@ -627,9 +627,9 @@ void bot_command_cast(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "{} {} [{}]{}", - ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), - ((successCount == 1 && firstFound) ? "casted" : "of your bots casted"), - (bySpellID ? spells[chosenSpellID].name : type), + ((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())), + ((success_count == 1 && first_found) ? "casted" : "of your bots casted"), + (by_spell_id ? spells[chosen_spell_id].name : type), tar ? (fmt::format(" on {}.", tar->GetCleanName()).c_str()) : "." ).c_str() ); diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 415dccb826..1b250b80bc 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -106,9 +106,9 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; int ab_arg = 2; - bool validOption = false; - uint16 spellType = UINT16_MAX; - uint16 settingType = UINT16_MAX; + bool valid_option = false; + uint16 spell_type = UINT16_MAX; + uint16 setting_type = UINT16_MAX; std::vector options = { "all", @@ -133,25 +133,25 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) }; if (sep->IsNumber(4)) { - spellType = atoi(sep->arg[4]); + spell_type = atoi(sep->arg[4]); } else { - spellType = c->GetSpellTypeIDByShortName(sep->arg[4]); + spell_type = c->GetSpellTypeIDByShortName(sep->arg[4]); } - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { - spellType = UINT16_MAX; + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + spell_type = UINT16_MAX; } for (int i = 0; i < options.size(); i++) { if (sep->arg[3] == options[i]) { - settingType = c->GetBotSpellCategoryIDByShortName(sep->arg[3]); - validOption = true; + setting_type = c->GetBotSpellCategoryIDByShortName(sep->arg[3]); + valid_option = true; break; } } - if (!validOption) { + if (!valid_option) { c->Message( Chat::Yellow, fmt::format( @@ -219,17 +219,17 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) std::string output = ""; - if (settingType != UINT16_MAX) { - if (spellType != UINT16_MAX) { - from->CopySettings(to, settingType, spellType); + if (setting_type != UINT16_MAX) { + if (spell_type != UINT16_MAX) { + from->CopySettings(to, setting_type, spell_type); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - from->CopySettings(to, settingType, i); + from->CopySettings(to, setting_type, i); } } - output = from->GetBotSpellCategoryName(settingType); + output = from->GetBotSpellCategoryName(setting_type); } else { if (!strcasecmp(sep->arg[3], "misc")) { @@ -241,20 +241,20 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) output = "^spellsettings"; } else if (!strcasecmp(sep->arg[3], "spelltypesettings")) { - if (spellType != UINT16_MAX) { - from->CopySettings(to, BotSettingCategories::SpellHold, spellType); - from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); - from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + if (spell_type != UINT16_MAX) { + from->CopySettings(to, BotSettingCategories::SpellHold, spell_type); + from->CopySettings(to, BotSettingCategories::SpellDelay, spell_type); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -285,20 +285,20 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) else if (!strcasecmp(sep->arg[3], "all")) { from->CopySettings(to, BotSettingCategories::BaseSetting); - if (spellType != UINT16_MAX) { - from->CopySettings(to, BotSettingCategories::SpellHold, spellType); - from->CopySettings(to, BotSettingCategories::SpellDelay, spellType); - from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spellType); - from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spellType); + if (spell_type != UINT16_MAX) { + from->CopySettings(to, BotSettingCategories::SpellHold, spell_type); + from->CopySettings(to, BotSettingCategories::SpellDelay, spell_type); + from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type); + from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -346,9 +346,9 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) "{}'s{}{} settings were copied to {}.", from->GetCleanName(), ( - spellType != UINT16_MAX ? + spell_type != UINT16_MAX ? fmt::format(" [{}] ", - c->GetSpellTypeNameByID(spellType) + c->GetSpellTypeNameByID(spell_type) ) : " " ), diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 0843e9a8fd..9ac4c94179 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -109,8 +109,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; int ab_arg = 2; - bool validOption = false; - uint16 spellType = UINT16_MAX; + bool valid_option = false; + uint16 spell_type = UINT16_MAX; std::vector options = { "all", @@ -138,14 +138,14 @@ void bot_command_default_settings(Client* c, const Seperator* sep) if (sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { if (sep->IsNumber(2)) { - spellType = atoi(sep->arg[2]); + spell_type = atoi(sep->arg[2]); } if (c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(sep->arg[2]); + spell_type = c->GetSpellTypeIDByShortName(sep->arg[2]); } - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -161,12 +161,12 @@ void bot_command_default_settings(Client* c, const Seperator* sep) break; } - validOption = true; + valid_option = true; break; } } - if (!validOption) { + if (!valid_option) { c->Message( Chat::Yellow, fmt::format( @@ -197,212 +197,212 @@ void bot_command_default_settings(Client* c, const Seperator* sep) Bot* first_found = nullptr; int success_count = 0; std::string output = ""; - uint8 botStance = 2; + uint8 bot_stance = 2; - for (auto myBot : sbl) { + for (auto my_bot : sbl) { if (!first_found) { - first_found = myBot; + first_found = my_bot; } - botStance = myBot->GetBotStance(); + bot_stance = my_bot->GetBotStance(); if (!strcasecmp(sep->arg[1], "misc")) { for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - myBot->SetBotBaseSetting(i, myBot->GetDefaultBotBaseSetting(i, botStance)); + my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance)); output = "miscellanous settings"; } } else if (!strcasecmp(sep->arg[1], "holds")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellHold(spellType, myBot->GetDefaultSpellHold(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); } } output = "hold settings"; } else if (!strcasecmp(sep->arg[1], "delays")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellDelay(spell_type, my_bot->GetDefaultSpellDelay(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); } } output = "delay settings"; } else if (!strcasecmp(sep->arg[1], "minthresholds")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellMinThreshold(spellType, myBot->GetDefaultSpellMinThreshold(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); } } output = "minimum threshold settings"; } else if (!strcasecmp(sep->arg[1], "maxthresholds")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellMaxThreshold(spellType, myBot->GetDefaultSpellMaxThreshold(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); } } output = "maximum threshold settings"; } else if (!strcasecmp(sep->arg[1], "aggrochecks")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypeAggroCheck(spellType, myBot->GetDefaultSpellTypeAggroCheck(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); } } output = "aggro check settings"; } else if (!strcasecmp(sep->arg[1], "minmanapct")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypeMinManaLimit(spellType, myBot->GetDefaultSpellTypeMinManaLimit(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); } } output = "min mana settings"; } else if (!strcasecmp(sep->arg[1], "maxmanapct")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypeMaxManaLimit(spellType, myBot->GetDefaultSpellTypeMaxManaLimit(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); } } output = "max mana settings"; } else if (!strcasecmp(sep->arg[1], "minhppct")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypeMinHPLimit(spellType, myBot->GetDefaultSpellTypeMinHPLimit(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); } } output = "min hp settings"; } else if (!strcasecmp(sep->arg[1], "maxhppct")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypeMaxHPLimit(spellType, myBot->GetDefaultSpellTypeMaxHPLimit(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); } } output = "max hp settings"; } else if (!strcasecmp(sep->arg[1], "idlepriority")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); } } output = "idle priority settings"; } else if (!strcasecmp(sep->arg[1], "engagedpriority")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); } } output = "engaged priority settings"; } else if (!strcasecmp(sep->arg[1], "pursuepriority")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); } } output = "pursue priority settings"; } else if (!strcasecmp(sep->arg[1], "targetcounts")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellTypeAEOrGroupTargetCount(spellType, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); } } output = "target count settings"; } else if (!strcasecmp(sep->arg[1], "spellsettings")) { - myBot->ResetBotSpellSettings(); + my_bot->ResetBotSpellSettings(); output = "^spellsettings"; } else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { - if (spellType != UINT16_MAX) { - myBot->SetSpellHold(spellType, myBot->GetDefaultSpellHold(spellType, botStance)); - myBot->SetSpellDelay(spellType, myBot->GetDefaultSpellDelay(spellType, botStance)); - myBot->SetSpellMinThreshold(spellType, myBot->GetDefaultSpellMinThreshold(spellType, botStance)); - myBot->SetSpellMaxThreshold(spellType, myBot->GetDefaultSpellMaxThreshold(spellType, botStance)); - myBot->SetSpellTypeAggroCheck(spellType, myBot->GetDefaultSpellTypeAggroCheck(spellType, botStance)); - myBot->SetSpellTypeMinManaLimit(spellType, myBot->GetDefaultSpellTypeMinManaLimit(spellType, botStance)); - myBot->SetSpellTypeMaxManaLimit(spellType, myBot->GetDefaultSpellTypeMaxManaLimit(spellType, botStance)); - myBot->SetSpellTypeMinHPLimit(spellType, myBot->GetDefaultSpellTypeMinHPLimit(spellType, botStance)); - myBot->SetSpellTypeMaxHPLimit(spellType, myBot->GetDefaultSpellTypeMaxHPLimit(spellType, botStance)); - myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); - myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); - myBot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(spellType, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); - myBot->SetSpellTypeAEOrGroupTargetCount(spellType, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(spellType, botStance)); + if (spell_type != UINT16_MAX) { + my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(SpellType, bot_stance)); + my_bot->SetSpellDelay(spell_type, my_bot->GetDefaultSpellDelay(spell_type, bot_stance)); + my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); + my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); + my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance)); + my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); + my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance)); + my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance)); + my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance)); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); - myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); - myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); - myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); - myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); - myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); - myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); - myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); - myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); - myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); - myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); - myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); - myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); } } @@ -410,29 +410,29 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "all")) { for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - myBot->SetBotBaseSetting(i, myBot->GetDefaultBotBaseSetting(i, botStance)); + my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance)); } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - myBot->SetSpellHold(i, myBot->GetDefaultSpellHold(i, botStance)); - myBot->SetSpellDelay(i, myBot->GetDefaultSpellDelay(i, botStance)); - myBot->SetSpellMinThreshold(i, myBot->GetDefaultSpellMinThreshold(i, botStance)); - myBot->SetSpellMaxThreshold(i, myBot->GetDefaultSpellMaxThreshold(i, botStance)); - myBot->SetSpellTypeAggroCheck(i, myBot->GetDefaultSpellTypeAggroCheck(i, botStance)); - myBot->SetSpellTypeMinManaLimit(i, myBot->GetDefaultSpellTypeMinManaLimit(i, botStance)); - myBot->SetSpellTypeMaxManaLimit(i, myBot->GetDefaultSpellTypeMaxManaLimit(i, botStance)); - myBot->SetSpellTypeMinHPLimit(i, myBot->GetDefaultSpellTypeMinHPLimit(i, botStance)); - myBot->SetSpellTypeMaxHPLimit(i, myBot->GetDefaultSpellTypeMaxHPLimit(i, botStance)); - myBot->SetSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, myBot->GetClass(), botStance)); - myBot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, myBot->GetClass(), botStance)); - myBot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, myBot->GetClass(), botStance)); - myBot->SetSpellTypeAEOrGroupTargetCount(i, myBot->GetDefaultSpellTypeAEOrGroupTargetCount(i, botStance)); + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); }; - myBot->ResetBotSpellSettings(); - myBot->ClearBotBlockedBuffs(); + my_bot->ResetBotSpellSettings(); + my_bot->ClearBotBlockedBuffs(); - myBot->Save(); + my_bot->Save(); output = "settings"; @@ -448,9 +448,9 @@ void bot_command_default_settings(Client* c, const Seperator* sep) "{} says, '{}{} were restored.'", first_found->GetCleanName(), ( - spellType != UINT16_MAX ? + spell_type != UINT16_MAX ? fmt::format("My [{}] ", - c->GetSpellTypeNameByID(spellType) + c->GetSpellTypeNameByID(spell_type) ) : "My " ), @@ -465,9 +465,9 @@ void bot_command_default_settings(Client* c, const Seperator* sep) "{} of your bot's{}{} were restored.", success_count, ( - spellType != UINT16_MAX ? + spell_type != UINT16_MAX ? fmt::format(" [{}] ", - c->GetSpellTypeNameByID(spellType) + c->GetSpellTypeNameByID(spell_type) ) : " " ), diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index c82acd1baa..06dc59894c 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -131,13 +131,13 @@ void bot_command_depart(Client* c, const Seperator* sep) tar = c; } - std::string argString = sep->arg[ab_arg]; + std::string arg_string = sep->arg[ab_arg]; const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "spawned"; + if (actionable_arg.empty()) { + actionable_arg = "spawned"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -149,19 +149,19 @@ void bot_command_depart(Client* c, const Seperator* sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - BotSpell botSpell; - botSpell.SpellId = 0; - botSpell.SpellIndex = 0; - botSpell.ManaCost = 0; + BotSpell bot_spell; + bot_spell.SpellId = 0; + bot_spell.SpellIndex = 0; + bot_spell.ManaCost = 0; - bool isSuccess = false; - std::map> listZones; + bool is_success = false; + std::map> list_zones; for (auto bot_iter : sbl) { if (!bot_iter->IsInGroupOrRaid(tar, !single)) { @@ -172,9 +172,9 @@ void bot_command_depart(Client* c, const Seperator* sep) continue; } - std::vector botSpellListItr = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Teleport, tar); + std::vector bot_spell_list_itr = bot_iter->GetPrioritizedBotSpellsBySpellType(bot_iter, BotSpellTypes::Teleport, tar); - for (std::vector::iterator itr = botSpellListItr.begin(); itr != botSpellListItr.end(); ++itr) { + for (std::vector::iterator itr = bot_spell_list_itr.begin(); itr != bot_spell_list_itr.end(); ++itr) { if (!IsValidSpell(itr->SpellId)) { continue; } @@ -188,9 +188,9 @@ void bot_command_depart(Client* c, const Seperator* sep) } if (list) { - auto it = listZones.find(spells[itr->SpellId].teleport_zone); + auto it = list_zones.find(spells[itr->SpellId].teleport_zone); - if (it != listZones.end()) { + if (it != list_zones.end()) { const auto& [val1, val2] = it->second; if (val1 == spells[itr->SpellId].target_type && val2 == bot_iter->GetClass()) { @@ -214,7 +214,7 @@ void bot_command_depart(Client* c, const Seperator* sep) ).c_str() ); - listZones.insert({ spells[itr->SpellId].teleport_zone, {spells[itr->SpellId].target_type, bot_iter->GetClass()} }); + list_zones.insert({ spells[itr->SpellId].teleport_zone, {spells[itr->SpellId].target_type, bot_iter->GetClass()} }); continue; } @@ -268,12 +268,12 @@ void bot_command_depart(Client* c, const Seperator* sep) ); } - isSuccess = true; + is_success = true; } bot_iter->SetCommandedSpell(false); - if (isSuccess) { + if (is_success) { return; } else { @@ -283,8 +283,8 @@ void bot_command_depart(Client* c, const Seperator* sep) } if ( - (list && listZones.empty()) || - (!list && !isSuccess) + (list && list_zones.empty()) || + (!list && !is_success) ) { c->Message( Chat::Yellow, diff --git a/zone/bot_commands/discipline.cpp b/zone/bot_commands/discipline.cpp index 233eaae899..0a7f8ad491 100644 --- a/zone/bot_commands/discipline.cpp +++ b/zone/bot_commands/discipline.cpp @@ -122,10 +122,10 @@ void bot_command_discipline(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "spawned"; + if (actionable_arg.empty()) { + actionable_arg = "spawned"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -137,15 +137,15 @@ void bot_command_discipline(Client* c, const Seperator* sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - bool isSuccess = false; - uint16 successCount = 0; - Bot* firstFound = nullptr; + bool is_success = false; + uint16 success_count = 0; + Bot* first_found = nullptr; for (auto bot_iter : sbl) { if (!bot_iter->IsInGroupOrRaid(c)) { @@ -157,22 +157,22 @@ void bot_command_discipline(Client* c, const Seperator* sep) } if (spell_id == UINT16_MAX) { // Aggressive/Defensive type - std::vector botSpellList; + std::vector bot_spell_list; if (aggressive) { - botSpellList = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscAggressive); + bot_spell_list = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscAggressive); } else if (defensive) { - botSpellList = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscDefensive); + bot_spell_list = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscDefensive); } - for (int i = botSpellList.size() - 1; i >= 0; i--) { - if (!IsValidSpell(botSpellList[i].spellid)) { + for (int i = bot_spell_list.size() - 1; i >= 0; i--) { + if (!IsValidSpell(bot_spell_list[i].spellid)) { continue; } - if (!bot_iter->CheckDisciplineReuseTimer(botSpellList[i].spellid)) { - uint32 remaining_time = (bot_iter->GetDisciplineReuseRemainingTime(botSpellList[i].spellid) / 1000); + if (!bot_iter->CheckDisciplineReuseTimer(bot_spell_list[i].spellid)) { + uint32 remaining_time = (bot_iter->GetDisciplineReuseRemainingTime(bot_spell_list[i].spellid) / 1000); bot_iter->OwnerMessage( fmt::format( @@ -184,30 +184,30 @@ void bot_command_discipline(Client* c, const Seperator* sep) continue; } - if (bot_iter->GetEndurance() < spells[botSpellList[i].spellid].endurance_cost) { + if (bot_iter->GetEndurance() < spells[bot_spell_list[i].spellid].endurance_cost) { continue; } - if (bot_iter->DivineAura() && !IsCastNotStandingSpell(botSpellList[i].spellid)) { + if (bot_iter->DivineAura() && !IsCastNotStandingSpell(bot_spell_list[i].spellid)) { continue; } - if (spells[botSpellList[i].spellid].buff_duration_formula != 0 && spells[botSpellList[i].spellid].target_type == ST_Self && bot_iter->HasDiscBuff()) { + if (spells[bot_spell_list[i].spellid].buff_duration_formula != 0 && spells[bot_spell_list[i].spellid].target_type == ST_Self && bot_iter->HasDiscBuff()) { continue; } - if (!tar || (spells[botSpellList[i].spellid].target_type == ST_Self && tar != bot_iter)) { + if (!tar || (spells[bot_spell_list[i].spellid].target_type == ST_Self && tar != bot_iter)) { tar = bot_iter; } - if (bot_iter->AttemptForcedCastSpell(tar, botSpellList[i].spellid, true)) { - if (!firstFound) { - firstFound = bot_iter; + if (bot_iter->AttemptForcedCastSpell(tar, bot_spell_list[i].spellid, true)) { + if (!first_found) { + first_found = bot_iter; } - isSuccess = true; - ++successCount; - spell_id = botSpellList[i].spellid; + is_success = true; + ++success_count; + spell_id = bot_spell_list[i].spellid; } } } @@ -252,19 +252,19 @@ void bot_command_discipline(Client* c, const Seperator* sep) } if (bot_iter->AttemptForcedCastSpell(tar, spell_id, true)) { - if (!firstFound) { - firstFound = bot_iter; + if (!first_found) { + first_found = bot_iter; } - isSuccess = true; - ++successCount; + is_success = true; + ++success_count; } } continue; } - if (!isSuccess) { + if (!is_success) { c->Message(Chat::Yellow, "No bots were selected."); } else { @@ -273,8 +273,8 @@ void bot_command_discipline(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "{} {} {} {} discipline.", - ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), - ((successCount == 1 && firstFound) ? "used" : "of your bots used"), + ((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())), + ((success_count == 1 && first_found) ? "used" : "of your bots used"), (aggressive ? "an" : "a"), (aggressive ? "aggressive" : "defensive") ).c_str() @@ -285,8 +285,8 @@ void bot_command_discipline(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "{} {} their {} [#{}] discipline.", - ((successCount == 1 && firstFound) ? firstFound->GetCleanName() : (fmt::format("{}", successCount).c_str())), - ((successCount == 1 && firstFound) ? "used" : "of your bots used"), + ((success_count == 1 && first_found) ? first_found->GetCleanName() : (fmt::format("{}", success_count).c_str())), + ((success_count == 1 && first_found) ? "used" : "of your bots used"), spells[spell_id].name, spell_id ).c_str() diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 34e16b7f36..5996706d64 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -82,7 +82,7 @@ void bot_command_follow(Client* c, const Seperator* sep) bool chain = false; bool reset = false; - bool currentCheck = false; + bool current_check = false; int ab_arg = 1; Mob* target_mob = nullptr; @@ -94,7 +94,7 @@ void bot_command_follow(Client* c, const Seperator* sep) ++ab_arg; } else if (!optional_arg.compare("current")) { - currentCheck = true; + current_check = true; ++ab_arg; } else { @@ -124,13 +124,13 @@ void bot_command_follow(Client* c, const Seperator* sep) sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - auto botCount = sbl.size(); + auto bot_count = sbl.size(); Mob* follow_mob = nullptr; - std::list chainList; - std::list::const_iterator it = chainList.begin(); + std::list chain_list; + std::list::const_iterator it = chain_list.begin(); uint16 count = 0; for (auto bot_iter : sbl) { - if (currentCheck) { + if (current_check) { follow_mob = entity_list.GetMob(bot_iter->GetFollowID()); c->Message( @@ -150,7 +150,7 @@ void bot_command_follow(Client* c, const Seperator* sep) } if (bot_iter == target_mob) { - if (botCount == 1) { + if (bot_count == 1) { c->Message( Chat::Yellow, fmt::format( @@ -167,13 +167,13 @@ void bot_command_follow(Client* c, const Seperator* sep) } bot_iter->WipeHateList(); - --botCount; + --bot_count; continue; } if (!bot_iter->IsInGroupOrRaid(target_mob)) { - --botCount; + --bot_count; continue; } @@ -191,20 +191,20 @@ void bot_command_follow(Client* c, const Seperator* sep) } else { if (chain) { - Mob* nextTar = target_mob; + Mob* next_tar = target_mob; if (count > 0) { - nextTar = *it; + next_tar = *it; - if (!nextTar) { - nextTar = target_mob; + if (!next_tar) { + next_tar = target_mob; } } - chainList.push_back(bot_iter); + chain_list.push_back(bot_iter); ++it; ++count; - bot_iter->SetFollowID(nextTar->GetID()); + bot_iter->SetFollowID(next_tar->GetID()); } else { bot_iter->SetFollowID(target_mob->GetID()); @@ -222,13 +222,13 @@ void bot_command_follow(Client* c, const Seperator* sep) bot_iter->GetPet()->SetFollowID(bot_iter->GetID()); } - if (currentCheck || !botCount) { + if (current_check || !bot_count) { return; } follow_mob = target_mob; - if (botCount == 1) { + if (bot_count == 1) { follow_mob = entity_list.GetMob(sbl.front()->GetFollowID()); c->Message( @@ -246,7 +246,7 @@ void bot_command_follow(Client* c, const Seperator* sep) Chat::Green, fmt::format( "{} of your bots are following you.", - botCount + bot_count ).c_str() ); } @@ -255,7 +255,7 @@ void bot_command_follow(Client* c, const Seperator* sep) Chat::Green, fmt::format( "{} of your bots are {} {}.", - botCount, + bot_count, chain ? "chain following" : "following", follow_mob ? follow_mob->GetCleanName() : "you" ).c_str() diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index 1ad5e8465e..cb87313d25 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -82,12 +82,12 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) int ab_arg = 1; bool current_check = false; - uint32 typeValue = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - typeValue = atoi(sep->arg[1]); + type_value = atoi(sep->arg[1]); ++ab_arg; - if (typeValue < 0 || typeValue > 1) { + if (type_value < 0 || type_value > 1) { c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); return; @@ -147,7 +147,7 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) ); } else { - my_bot->SetIllusionBlock(typeValue); + my_bot->SetIllusionBlock(type_value); ++success_count; } } @@ -168,7 +168,7 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) fmt::format( "{} of your bots {} block illusions.", success_count, - typeValue ? "will now" : "will no longer" + type_value ? "will now" : "will no longer" ).c_str() ); } diff --git a/zone/bot_commands/item_use.cpp b/zone/bot_commands/item_use.cpp index f76c5e48b2..b9cf6a803a 100644 --- a/zone/bot_commands/item_use.cpp +++ b/zone/bot_commands/item_use.cpp @@ -123,10 +123,10 @@ void bot_command_item_use(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "spawned"; + if (actionable_arg.empty()) { + actionable_arg = "spawned"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -138,7 +138,7 @@ void bot_command_item_use(Client* c, const Seperator* sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp index a8df8eeccb..adb00f37d5 100644 --- a/zone/bot_commands/max_melee_range.cpp +++ b/zone/bot_commands/max_melee_range.cpp @@ -81,12 +81,12 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) int ab_arg = 1; bool current_check = false; - uint32 typeValue = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - typeValue = atoi(sep->arg[1]); + type_value = atoi(sep->arg[1]); ++ab_arg; - if (typeValue < 0 || typeValue > 1) { + if (type_value < 0 || type_value > 1) { c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); return; @@ -146,7 +146,7 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) ); } else { - my_bot->SetMaxMeleeRange(typeValue); + my_bot->SetMaxMeleeRange(type_value); ++success_count; } } @@ -167,7 +167,7 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) fmt::format( "{} of your bots {} stay at max melee range.", success_count, - typeValue ? "will now" : "will no longer" + type_value ? "will now" : "will no longer" ).c_str() ); } diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index ce01596ad8..d6ba9a3837 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -228,10 +228,10 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "target"; + if (actionable_arg.empty()) { + actionable_arg = "target"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -243,17 +243,17 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - std::string currentType; + std::string current_type; uint16 reclaim_energy_id = RuleI(Bots, ReclaimEnergySpellID); - bool isSuccess = false; - uint16 successCount = 0; - Bot* firstFound = nullptr; + bool is_success = false; + uint16 success_count = 0; + Bot* first_found = nullptr; for (auto bot_iter : sbl) { if (bot_iter->GetClass() != Class::Magician) { @@ -284,28 +284,28 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) if (current_check) { switch (bot_iter->GetPetChooserID()) { case 0: - currentType = "auto"; + current_type = "auto"; break; case SumWater: - currentType = "water"; + current_type = "water"; break; case SumFire: - currentType = "fire"; + current_type = "fire"; break; case SumAir: - currentType = "air"; + current_type = "air"; break; case SumEarth: - currentType = "earth"; + current_type = "earth"; break; case MonsterSum: - currentType = "monster"; + current_type = "monster"; break; case SumMageMultiElement: - currentType = "epic"; + current_type = "epic"; break; default: - currentType = "null"; + current_type = "null"; break; } @@ -314,22 +314,22 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) fmt::format( "{} says, 'I'm currently summoning {} pets.'", bot_iter->GetCleanName(), - currentType + current_type ).c_str() ); continue; } - uint8 airMinLevel = 255; - uint8 fireMinLevel = 255; - uint8 waterMinLevel = 255; - uint8 earthMinLevel = 255; - uint8 monsterMinLevel = 255; - uint8 epicMinLevel = 255; - std::list botSpellList = bot_iter->GetBotSpellsBySpellType(bot_iter, BotSpellTypes::Pet); + uint8 air_min_level = 255; + uint8 fire_min_level = 255; + uint8 water_min_level = 255; + uint8 earth_min_level = 255; + uint8 monster_min_level = 255; + uint8 epic_min_level = 255; + std::list bot_spell_list = bot_iter->GetBotSpellsBySpellType(bot_iter, BotSpellTypes::Pet); - for (const auto& s : botSpellList) { + for (const auto& s : bot_spell_list) { if (!IsValidSpell(s.SpellId)) { continue; } @@ -340,65 +340,54 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) auto spell = spells[s.SpellId]; - if (!strncmp(spell.teleport_zone, "SumWater", 8) && spell.classes[Class::Magician - 1] < waterMinLevel) { - waterMinLevel = spell.classes[Class::Magician - 1]; + if (!strncmp(spell.teleport_zone, "SumWater", 8) && spell.classes[Class::Magician - 1] < water_min_level) { + water_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "SumFire", 7) && spell.classes[Class::Magician - 1] < fireMinLevel) { - fireMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "SumFire", 7) && spell.classes[Class::Magician - 1] < fire_min_level) { + fire_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "SumAir", 6) && spell.classes[Class::Magician - 1] < airMinLevel) { - airMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "SumAir", 6) && spell.classes[Class::Magician - 1] < air_min_level) { + air_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "SumEarth", 8) && spell.classes[Class::Magician - 1] < earthMinLevel) { - earthMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "SumEarth", 8) && spell.classes[Class::Magician - 1] < earth_min_level) { + earth_min_level = spell.classes[Class::Magician - 1]; } - else if (!strncmp(spell.teleport_zone, "MonsterSum", 10) && spell.classes[Class::Magician - 1] < monsterMinLevel) { - monsterMinLevel = spell.classes[Class::Magician - 1]; + else if (!strncmp(spell.teleport_zone, "MonsterSum", 10) && spell.classes[Class::Magician - 1] < monster_min_level) { + monster_min_level = spell.classes[Class::Magician - 1]; } } - uint8 minLevel = std::min({ - waterMinLevel, - fireMinLevel, - airMinLevel, - earthMinLevel, - monsterMinLevel + uint8 min_level = std::min({ + water_min_level, + fire_min_level, + air_min_level, + earth_min_level, + monster_min_level }); - epicMinLevel = RuleI(Bots, AllowMagicianEpicPetLevel); - - LogTestDebug("{} says, 'minLevel = {} | waterMinLevel = {} | fireMinLevel = {} | airMinLevel = {} | earthMinLevel = {} | monsterMinLevel = {} | epicMinLevel = {}'", - bot_iter->GetCleanName(), - minLevel, - waterMinLevel, - fireMinLevel, - airMinLevel, - earthMinLevel, - monsterMinLevel, - epicMinLevel - ); //deleteme + epic_min_level = RuleI(Bots, AllowMagicianEpicPetLevel); switch (pet_type) { case 0: - level_req = minLevel; + level_req = min_level; break; case SumWater: - level_req = waterMinLevel; + level_req = water_min_level; break; case SumFire: - level_req = fireMinLevel; + level_req = fire_min_level; break; case SumAir: - level_req = airMinLevel; + level_req = air_min_level; break; case SumEarth: - level_req = earthMinLevel; + level_req = earth_min_level; break; case MonsterSum: - level_req = monsterMinLevel; + level_req = monster_min_level; break; case SumMageMultiElement: - level_req = epicMinLevel; + level_req = epic_min_level; break; default: break; @@ -416,31 +405,31 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) bot_iter->CastSpell(reclaim_energy_id, pet_id); } - if (!firstFound) { - firstFound = bot_iter; + if (!first_found) { + first_found = bot_iter; } - isSuccess = true; - ++successCount; + is_success = true; + ++success_count; } if (current_check) { return; } - if (!isSuccess) { + if (!is_success) { c->Message(Chat::Yellow, "No bots were selected."); return; } - if (successCount == 1 && firstFound) { + if (success_count == 1 && first_found) { c->Message( Chat::Green, fmt::format( "{} says, 'I will now summon {} pets.'", - firstFound->GetCleanName(), - currentType + first_found->GetCleanName(), + current_type ).c_str() ); } @@ -449,7 +438,7 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) Chat::Green, fmt::format( "{} of your bots will now summon {} pets.", - successCount, + success_count, arg1 ).c_str() ); diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index 9d60408bc9..eeceb6ce9d 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -15,10 +15,10 @@ void bot_command_pull(Client *c, const Seperator *sep) std::string arg1 = sep->arg[1]; int ab_arg = 1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "spawned"; + if (actionable_arg.empty()) { + actionable_arg = "spawned"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -30,7 +30,7 @@ void bot_command_pull(Client *c, const Seperator *sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index e374ac5e28..a147057f5f 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -81,12 +81,12 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) int ab_arg = 1; bool current_check = false; - uint32 typeValue = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - typeValue = atoi(sep->arg[1]); + type_value = atoi(sep->arg[1]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); return; @@ -146,7 +146,7 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) ); } else { - my_bot->SetSitHPPct(typeValue); + my_bot->SetSitHPPct(type_value); ++success_count; } } @@ -167,7 +167,7 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) fmt::format( "{} of your bots will now sit in combat whem at or below [{}%%] HP.'", success_count, - typeValue + type_value ).c_str() ); } diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index e40c79dc26..86d8896b02 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -81,12 +81,12 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) int ab_arg = 1; bool current_check = false; - uint32 typeValue = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - typeValue = atoi(sep->arg[1]); + type_value = atoi(sep->arg[1]); ++ab_arg; - if (typeValue < 0 || typeValue > 1) { + if (type_value < 0 || type_value > 1) { c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); return; @@ -146,7 +146,7 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) ); } else { - my_bot->SetMedInCombat(typeValue); + my_bot->SetMedInCombat(type_value); ++success_count; } } @@ -167,7 +167,7 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) fmt::format( "{} of your bots {} sit in combat.", success_count, - typeValue ? "will now" : "will no longer" + type_value ? "will now" : "will no longer" ).c_str() ); } diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 6c2f2fd43d..9aae5fd9fc 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -81,12 +81,12 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) int ab_arg = 1; bool current_check = false; - uint32 typeValue = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - typeValue = atoi(sep->arg[1]); + type_value = atoi(sep->arg[1]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); return; @@ -146,7 +146,7 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) ); } else { - my_bot->SetSitManaPct(typeValue); + my_bot->SetSitManaPct(type_value); ++success_count; } } @@ -167,7 +167,7 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) fmt::format( "{} of your bots will now sit in combat whem at or below [{}%%] mana.'", success_count, - typeValue + TypeValue ).c_str() ); } diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index e4c64a24c7..3cddc2bbd0 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -111,14 +111,14 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -126,7 +126,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -144,9 +144,9 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 1) { + if (type_value < 0 || type_value > 1) { c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); return; @@ -201,13 +201,13 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] aggro check is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypeAggroCheck(spellType) ? "enabled" : "disabled" + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeAggroCheck(spell_type) ? "enabled" : "disabled" ).c_str() ); } else { - my_bot->SetSpellTypeAggroCheck(spellType, typeValue); + my_bot->SetSpellTypeAggroCheck(spell_type, type_value); ++success_count; } } @@ -218,8 +218,8 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] aggro check was [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypeAggroCheck(spellType) ? "enabled" : "disabled" + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeAggroCheck(spell_type) ? "enabled" : "disabled" ).c_str() ); } @@ -229,8 +229,8 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} of your bots [{}] their [{}] aggro check.", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue ? "enabled" : "disabled" + c->GetSpellTypeNameByID(spell_type), + type_value ? "enabled" : "disabled" ).c_str() ); } diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index ea7250e8c4..0701ca2f09 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -117,14 +117,14 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -132,7 +132,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -150,9 +150,9 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 1 || typeValue > 60000) { + if (type_value < 1 || type_value > 60000) { c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); return; @@ -207,13 +207,13 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] spell delay is currently [{}] seconds.'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellDelay(spellType) / 1000.00 + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); } else { - my_bot->SetSpellDelay(spellType, typeValue); + my_bot->SetSpellDelay(spell_type, type_value); ++success_count; } } @@ -224,8 +224,8 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] spell delay was set to [{}] seconds.'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellDelay(spellType) / 1000.00 + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); } @@ -235,8 +235,8 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] spell delay to [{}] seconds.", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue / 1000.00 + c->GetSpellTypeNameByID(spell_type), + type_value / 1000.00 ).c_str() ); } diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index b934d5ccb9..271ce848b0 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -115,14 +115,14 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -130,7 +130,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -148,9 +148,9 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100."); return; @@ -205,13 +205,13 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] engaged cast priority is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Engaged) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged) ).c_str() ); } else { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Engaged, typeValue); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, type_value); ++success_count; } } @@ -222,8 +222,8 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] engaged cast priority was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Engaged) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged) ).c_str() ); } @@ -233,8 +233,8 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] engaged cast priority to [{}].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index 4f14129c43..703da4df73 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -101,14 +101,14 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -116,7 +116,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -134,9 +134,9 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 1) { + if (type_value < 0 || type_value > 1) { c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); return; @@ -186,13 +186,13 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] spell hold is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellHold(spellType) ? "enabled" : "disabled" + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellHold(spell_type) ? "enabled" : "disabled" ).c_str() ); } else { - my_bot->SetSpellHold(spellType, typeValue); + my_bot->SetSpellHold(spell_type, type_value); ++success_count; } } @@ -203,8 +203,8 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] spell hold was [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellHold(spellType) ? "enabled" : "disabled" + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellHold(spell_type) ? "enabled" : "disabled" ).c_str() ); } @@ -214,8 +214,8 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) fmt::format( "{} of your bots [{}] their [{}] spell hold.", success_count, - typeValue ? "enabled" : "disabled", - c->GetSpellTypeNameByID(spellType) + type_value ? "enabled" : "disabled", + c->GetSpellTypeNameByID(spell_type) ).c_str() ); } diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 82676db3c8..6b990b343e 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -115,14 +115,14 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -130,7 +130,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -148,9 +148,9 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100."); return; @@ -205,13 +205,13 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] idle cast priority is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Idle) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Idle) ).c_str() ); } else { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Idle, typeValue); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, type_value); ++success_count; } } @@ -222,8 +222,8 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] idle cast priority was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Idle) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Idle) ).c_str() ); } @@ -233,8 +233,8 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] idle cast priority to [{}].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 50c83e6dd3..a9e7e3f6d2 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -111,14 +111,14 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -126,7 +126,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -144,9 +144,9 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); return; @@ -201,13 +201,13 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum HP is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypeMaxHPLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeMaxHPLimit(spell_type) ).c_str() ); } else { - my_bot->SetSpellTypeMaxHPLimit(spellType, typeValue); + my_bot->SetSpellTypeMaxHPLimit(spell_type, type_value); ++success_count; } } @@ -218,8 +218,8 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum HP was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypeMaxHPLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeMaxHPLimit(spell_type) ).c_str() ); } @@ -229,8 +229,8 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] maximum HP to [{}%%].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 2bd4aa3230..68dde0c6dd 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -111,14 +111,14 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -126,7 +126,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -144,9 +144,9 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); return; @@ -201,13 +201,13 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum mana is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypeMaxManaLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeMaxManaLimit(spell_type) ).c_str() ); } else { - my_bot->SetSpellTypeMaxManaLimit(spellType, typeValue); + my_bot->SetSpellTypeMaxManaLimit(spell_type, type_value); ++success_count; } } @@ -218,8 +218,8 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum mana was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypeMaxManaLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeMaxManaLimit(spell_type) ).c_str() ); } @@ -229,8 +229,8 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] maximum mana to [{}%%].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 432e506fd4..709e8bd8a0 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -117,14 +117,14 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -132,7 +132,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -150,9 +150,9 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); return; @@ -207,13 +207,13 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum threshold is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellMaxThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellMaxThreshold(spell_type) ).c_str() ); } else { - my_bot->SetSpellMaxThreshold(spellType, typeValue); + my_bot->SetSpellMaxThreshold(spell_type, type_value); ++success_count; } } @@ -224,8 +224,8 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum threshold was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellMaxThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellMaxThreshold(spell_type) ).c_str() ); } @@ -235,8 +235,8 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] maximum threshold to [{}%%].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 359ca1d4fe..c8124b9a0d 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -111,14 +111,14 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -126,7 +126,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -144,9 +144,9 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); return; @@ -201,13 +201,13 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum HP is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypeMinHPLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeMinHPLimit(spell_type) ).c_str() ); } else { - my_bot->SetSpellTypeMinHPLimit(spellType, typeValue); + my_bot->SetSpellTypeMinHPLimit(spell_type, type_value); ++success_count; } } @@ -218,8 +218,8 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum HP was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypeMinHPLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeMinHPLimit(spell_type) ).c_str() ); } @@ -229,8 +229,8 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] minimum HP to [{}%%].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index 7963f14a12..411d6ec18c 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -111,14 +111,14 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -126,7 +126,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -144,9 +144,9 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of mana)."); return; @@ -201,13 +201,13 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum mana is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypeMinManaLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeMinManaLimit(spell_type) ).c_str() ); } else { - my_bot->SetSpellTypeMinManaLimit(spellType, typeValue); + my_bot->SetSpellTypeMinManaLimit(spell_type, type_value); ++success_count; } } @@ -218,8 +218,8 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum mana was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypeMinManaLimit(spellType) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeMinManaLimit(spell_type) ).c_str() ); } @@ -229,8 +229,8 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] minimum mana to [{}%%].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index 2c0257ccbe..c1c5a62276 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -119,14 +119,14 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -134,7 +134,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -152,9 +152,9 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of health)."); return; @@ -209,13 +209,13 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum threshold is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellMinThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellMinThreshold(spell_type) ).c_str() ); } else { - my_bot->SetSpellMinThreshold(spellType, typeValue); + my_bot->SetSpellMinThreshold(spell_type, type_value); ++success_count; } } @@ -226,8 +226,8 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum threshold was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellMinThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellMinThreshold(spell_type) ).c_str() ); } @@ -237,8 +237,8 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] minimum threshold to [{}%%].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index fb90ccd1ef..47fd620288 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -115,14 +115,14 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -130,7 +130,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -148,9 +148,9 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100."); return; @@ -205,13 +205,13 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] pursue cast priority is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypePriority(spellType, BotPriorityCategories::Pursue) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue) ).c_str() ); } else { - my_bot->SetSpellTypePriority(spellType, BotPriorityCategories::Pursue, typeValue); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, type_value); ++success_count; } } @@ -222,8 +222,8 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] pursue cast priority was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypePriority(spellType, BotPriorityCategories::Pursue) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue) ).c_str() ); } @@ -233,8 +233,8 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] pursue cast priority to [{}].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index ff9ea75873..aeb1931fe4 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -111,14 +111,14 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; // String/Int type checks if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (spellType < BotSpellTypes::START || spellType > BotSpellTypes::END) { + if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; @@ -126,7 +126,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -144,9 +144,9 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) } if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 1 || typeValue > 100) { + if (type_value < 1 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 1-100."); return; @@ -201,13 +201,13 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] target count is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - my_bot->GetSpellTypeAEOrGroupTargetCount(spellType) + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeAEOrGroupTargetCount(spell_type) ).c_str() ); } else { - my_bot->SetSpellTypeAEOrGroupTargetCount(spellType, typeValue); + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, type_value); ++success_count; } } @@ -218,8 +218,8 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] target count was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spellType), - first_found->GetSpellTypeAEOrGroupTargetCount(spellType) + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeAEOrGroupTargetCount(spell_type) ).c_str() ); } @@ -229,8 +229,8 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] target count to [{}].", success_count, - c->GetSpellTypeNameByID(spellType), - typeValue + c->GetSpellTypeNameByID(spell_type), + type_value ).c_str() ); } diff --git a/zone/bot_commands/taunt.cpp b/zone/bot_commands/taunt.cpp index c2c4917d6a..2b1eea20b3 100644 --- a/zone/bot_commands/taunt.cpp +++ b/zone/bot_commands/taunt.cpp @@ -88,29 +88,29 @@ void bot_command_taunt(Client* c, const Seperator* sep) std::string arg1 = sep->arg[1]; std::string arg2 = sep->arg[2]; - bool tauntState = false; - bool petTaunt = false; - bool validOption = false; + bool taunt_state = false; + bool pet_taunt = false; + bool valid_option = false; int ab_arg = 1; if (!arg1.compare("on")) { - tauntState = true; - validOption = true; + taunt_state = true; + valid_option = true; ++ab_arg; } else if (!arg1.compare("off")) { - validOption = true; + valid_option = true; ++ab_arg; } if (!arg2.compare("pet")) { - petTaunt = true; - validOption = true; + pet_taunt = true; + valid_option = true; ++ab_arg; } - if (!validOption) { + if (!valid_option) { c->Message( Chat::Yellow, fmt::format( @@ -125,10 +125,10 @@ void bot_command_taunt(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string actionableArg = sep->arg[ab_arg]; + std::string actionable_arg = sep->arg[ab_arg]; - if (actionableArg.empty()) { - actionableArg = "target"; + if (actionable_arg.empty()) { + actionable_arg = "target"; } std::string class_race_arg = sep->arg[ab_arg]; @@ -140,22 +140,22 @@ void bot_command_taunt(Client* c, const Seperator* sep) std::vector sbl; - if (ActionableBots::PopulateSBL(c, actionableArg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - int botTauntingCount = 0; - int petTauntingCount = 0; + int bot_taunting_count = 0; + int pet_taunting_count = 0; - if (!petTaunt) { + if (!pet_taunt) { for (auto bot_iter : sbl) { if (!bot_iter->GetSkill(EQ::skills::SkillTaunt)) { continue; } - bot_iter->SetTaunting(tauntState); + bot_iter->SetTaunting(taunt_state); Bot::BotGroupSay( bot_iter, @@ -165,11 +165,11 @@ void bot_command_taunt(Client* c, const Seperator* sep) ).c_str() ); - ++botTauntingCount; + ++bot_taunting_count; } } - if (petTaunt) { + if (pet_taunt) { for (auto bot_iter : sbl) { if (!bot_iter->HasPet()) { continue; @@ -179,7 +179,7 @@ void bot_command_taunt(Client* c, const Seperator* sep) continue; } - bot_iter->GetPet()->CastToNPC()->SetTaunting(tauntState); + bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state); Bot::BotGroupSay( bot_iter, @@ -189,18 +189,18 @@ void bot_command_taunt(Client* c, const Seperator* sep) ).c_str() ); - ++petTauntingCount; + ++pet_taunting_count; } } - if (botTauntingCount || petTauntingCount) { + if (bot_taunting_count || pet_taunting_count) { c->Message( Chat::Green, fmt::format( "{} of your {} are {} taunting.", - (botTauntingCount ? botTauntingCount : petTauntingCount), - (botTauntingCount ? "bots" : "bots' pets"), - tauntState ? "now" : "no longer" + (bot_taunting_count ? bot_taunting_count : pet_taunting_count), + (bot_taunting_count ? "bots" : "bots' pets"), + taunt_state ? "now" : "no longer" ).c_str() ); } @@ -209,7 +209,7 @@ void bot_command_taunt(Client* c, const Seperator* sep) Chat::Yellow, fmt::format( "None of your {} are capable of taunting.", - !petTaunt ? "bots" : "bots' pets" + !pet_taunt ? "bots" : "bots' pets" ).c_str() ); } diff --git a/zone/bot_raid.cpp b/zone/bot_raid.cpp index 4e93b5d492..3f7cc5e4c9 100644 --- a/zone/bot_raid.cpp +++ b/zone/bot_raid.cpp @@ -142,7 +142,7 @@ void Raid::HandleOfflineBots(uint32 owner) { } } -uint8 Bot::GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool includePets, Raid* raid) { +uint8 Bot::GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool include_pets, Raid* raid) { if (raid) { uint32 r_group = raid->GetGroup(GetName()); @@ -153,7 +153,7 @@ uint8 Bot::GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool need_healed++; } - if (includePets && m.member->GetPet() && m.member->GetPet()->GetHPRatio() <= hpr) { + if (include_pets && m.member->GetPet() && m.member->GetPet()->GetHPRatio() <= hpr) { need_healed++; } } diff --git a/zone/gm_commands/illusion_block.cpp b/zone/gm_commands/illusion_block.cpp index cd05b965ad..b0457d7da5 100644 --- a/zone/gm_commands/illusion_block.cpp +++ b/zone/gm_commands/illusion_block.cpp @@ -12,10 +12,10 @@ void command_illusion_block(Client* c, const Seperator* sep) } if (sep->IsNumber(1)) { - int setStatus = atoi(sep->arg[1]); - if (setStatus == 0 || setStatus == 1) { - c->SetIllusionBlock(setStatus); - c->Message(Chat::White, "Your Illusion Block has been %s.", (setStatus ? "enabled" : "disabled")); + int set_status = atoi(sep->arg[1]); + if (set_status == 0 || set_status == 1) { + c->SetIllusionBlock(set_status); + c->Message(Chat::White, "Your Illusion Block has been %s.", (set_status ? "enabled" : "disabled")); } else { c->Message(Chat::White, "You must enter 0 for disabled or 1 for enabled."); diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp index 1a8a6c40c4..0c3c8c3920 100644 --- a/zone/gm_commands/spell_delays.cpp +++ b/zone/gm_commands/spell_delays.cpp @@ -89,13 +89,13 @@ void command_spell_delays(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); @@ -104,9 +104,9 @@ void command_spell_delays(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); } @@ -128,9 +128,9 @@ void command_spell_delays(Client* c, const Seperator* sep) // Enable/Disable/Current checks if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 60000) { + if (type_value < 0 || type_value > 60000) { c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); return; @@ -159,19 +159,19 @@ void command_spell_delays(Client* c, const Seperator* sep) Chat::Green, fmt::format( "Your [{}] delay is currently {} seconds.'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellDelay(spellType) / 1000.00 + c->GetSpellTypeNameByID(spell_type), + c->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); } else { - c->SetSpellDelay(spellType, typeValue); + c->SetSpellDelay(spell_type, type_value); c->Message( Chat::Green, fmt::format( "Your [{}] delay was set to {} seconds.'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellDelay(spellType) / 1000.00 + c->GetSpellTypeNameByID(spell_type), + c->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); } diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp index c9c3641d75..dcb0639dea 100644 --- a/zone/gm_commands/spell_holds.cpp +++ b/zone/gm_commands/spell_holds.cpp @@ -93,13 +93,13 @@ void command_spell_holds(Client *c, const Seperator *sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); @@ -108,9 +108,9 @@ void command_spell_holds(Client *c, const Seperator *sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); } @@ -132,9 +132,9 @@ void command_spell_holds(Client *c, const Seperator *sep) // Enable/Disable/Current checks if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 1) { + if (type_value < 0 || type_value > 1) { c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); return; @@ -163,19 +163,19 @@ void command_spell_holds(Client *c, const Seperator *sep) Chat::Green, fmt::format( "Your [{}] spell hold is currently [{}].'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellHold(spellType) ? "enabled" : "disabled" + c->GetSpellTypeNameByID(spell_type), + c->GetSpellHold(spell_type) ? "enabled" : "disabled" ).c_str() ); } else { - c->SetSpellHold(spellType, typeValue); + c->SetSpellHold(spell_type, type_value); c->Message( Chat::Green, fmt::format( "Your [{}] spell hold was [{}].'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellHold(spellType) ? "enabled" : "disabled" + c->GetSpellTypeNameByID(spell_type), + c->GetSpellHold(spell_type) ? "enabled" : "disabled" ).c_str() ); } diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp index f4aa137f8c..c6452d76f5 100644 --- a/zone/gm_commands/spell_max_thresholds.cpp +++ b/zone/gm_commands/spell_max_thresholds.cpp @@ -89,13 +89,13 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); @@ -104,9 +104,9 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); } @@ -128,9 +128,9 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) // Enable/Disable/Current checks if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); return; @@ -159,19 +159,19 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) Chat::Green, fmt::format( "Your [{}] maximum hold is currently [{}%%].'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellMaxThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + c->GetSpellMaxThreshold(spell_type) ).c_str() ); } else { - c->SetSpellMaxThreshold(spellType, typeValue); + c->SetSpellMaxThreshold(spell_type, type_value); c->Message( Chat::Green, fmt::format( "Your [{}] maximum hold was set to [{}%%].'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellMaxThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + c->GetSpellMaxThreshold(spell_type) ).c_str() ); } diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp index 79eabc5959..3bff9dbb2f 100644 --- a/zone/gm_commands/spell_min_thresholds.cpp +++ b/zone/gm_commands/spell_min_thresholds.cpp @@ -89,13 +89,13 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; - uint16 spellType = 0; - uint32 typeValue = 0; + uint16 spell_type = 0; + uint32 type_value = 0; if (sep->IsNumber(1)) { - spellType = atoi(sep->arg[1]); + spell_type = atoi(sep->arg[1]); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); @@ -104,9 +104,9 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) } else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spellType = c->GetSpellTypeIDByShortName(arg1); + spell_type = c->GetSpellTypeIDByShortName(arg1); - if (!IsClientBotSpellType(spellType)) { + if (!IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type."); c->SendSpellTypePrompts(false, true); } @@ -128,9 +128,9 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) // Enable/Disable/Current checks if (sep->IsNumber(2)) { - typeValue = atoi(sep->arg[2]); + type_value = atoi(sep->arg[2]); ++ab_arg; - if (typeValue < 0 || typeValue > 100) { + if (type_value < 0 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); return; @@ -159,19 +159,19 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) Chat::Green, fmt::format( "Your [{}] minimum hold is currently [{}%%].'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellMinThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + c->GetSpellMinThreshold(spell_type) ).c_str() ); } else { - c->SetSpellMinThreshold(spellType, typeValue); + c->SetSpellMinThreshold(spell_type, type_value); c->Message( Chat::Green, fmt::format( "Your [{}] minimum hold was set to [{}%%].'", - c->GetSpellTypeNameByID(spellType), - c->GetSpellMinThreshold(spellType) + c->GetSpellTypeNameByID(spell_type), + c->GetSpellMinThreshold(spell_type) ).c_str() ); } diff --git a/zone/groups.cpp b/zone/groups.cpp index 512cccfc39..f040e8d917 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -2262,24 +2262,24 @@ void Group::ClearAllNPCMarks() } -int8 Group::GetNumberNeedingHealedInGroup(int8 hpr, bool includePets) { - int8 needHealed = 0; +int8 Group::GetNumberNeedingHealedInGroup(int8 hpr, bool include_pets) { + int8 need_healed = 0; for( int i = 0; iqglobal) { if(members[i]->GetHPRatio() <= hpr) - needHealed++; + need_healed++; - if(includePets) { + if(include_pets) { if(members[i]->GetPet() && members[i]->GetPet()->GetHPRatio() <= hpr) { - needHealed++; + need_healed++; } } } } - return needHealed; + return need_healed; } void Group::UpdateGroupAAs() diff --git a/zone/groups.h b/zone/groups.h index f5e2b2baaf..9a49b7d3cd 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -107,7 +107,7 @@ class Group : public GroupIDConsumer { void UpdateGroupAAs(); void SaveGroupLeaderAA(); void MarkNPC(Mob* Target, int Number); - int8 GetNumberNeedingHealedInGroup(int8 hpr, bool includePets); + int8 GetNumberNeedingHealedInGroup(int8 hpr, bool include_pets); void DelegateMainTank(const char *NewMainAssistName, uint8 toggle = 0); void DelegateMainAssist(const char *NewMainAssistName, uint8 toggle = 0); void DelegatePuller(const char *NewMainAssistName, uint8 toggle = 0); From f34a9470a83832036c0f5c711b9f08b14085fbc1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 10 Jan 2025 12:23:40 -0600 Subject: [PATCH 265/394] code cleanup 4 --- zone/bot.cpp | 2 +- zone/bot_command.cpp | 2 +- zone/bot_command.h | 2 +- zone/bot_commands/appearance.cpp | 26 +++++++-------- zone/bot_commands/bot.cpp | 38 +++++++++++----------- zone/bot_commands/default_settings.cpp | 2 +- zone/bot_commands/heal_rotation.cpp | 44 +++++++++++++------------- zone/bot_commands/inventory.cpp | 12 +++---- zone/bot_commands/pet.cpp | 10 +++--- zone/bot_commands/sit_mana_percent.cpp | 2 +- zone/bot_database.cpp | 4 +-- 11 files changed, 72 insertions(+), 72 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3f99c4cdbf..3594adb4af 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9562,7 +9562,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (!AECheck && !IsValidSpellRange(spell_id, tar)) { + if (!ae_check && !IsValidSpellRange(spell_id, tar)) { LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 706a8c2085..2ceb520bd9 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -2037,7 +2037,7 @@ bool helper_no_available_bots(Client *bot_owner, Bot *my_bot) return false; } -void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, const std::list& subcommand_list) +void helper_send_available_subcommands(Client* bot_owner, const char* command_simile, std::vector subcommand_list) { bot_owner->Message(Chat::White, "Available %s management subcommands:", command_simile); diff --git a/zone/bot_command.h b/zone/bot_command.h index b64dabbf96..1482bcc61e 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1802,7 +1802,7 @@ bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, c void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag = false); bool helper_is_help_or_usage(const char* arg); bool helper_no_available_bots(Client *bot_owner, Bot *my_bot = nullptr); -void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, const std::list& subcommand_list); +void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, std::vector subcommand_list); void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = Class::None); bool helper_spell_check_fail(STBaseEntry* local_entry); bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type); diff --git a/zone/bot_commands/appearance.cpp b/zone/bot_commands/appearance.cpp index 9ca820e60e..5b8d883cc5 100644 --- a/zone/bot_commands/appearance.cpp +++ b/zone/bot_commands/appearance.cpp @@ -2,19 +2,19 @@ void bot_command_appearance(Client *c, const Seperator *sep) { - - std::list subcommand_list; - subcommand_list.push_back("botbeardcolor"); - subcommand_list.push_back("botbeardstyle"); - subcommand_list.push_back("botdetails"); - subcommand_list.push_back("botdyearmor"); - subcommand_list.push_back("boteyes"); - subcommand_list.push_back("botface"); - subcommand_list.push_back("bothaircolor"); - subcommand_list.push_back("bothairstyle"); - subcommand_list.push_back("botheritage"); - subcommand_list.push_back("bottattoo"); - subcommand_list.push_back("botwoad"); + std::vector subcommand_list = { + "botbeardcolor", + "botbeardstyle", + "botdetails", + "botdyearmor", + "boteyes", + "botface", + "bothaircolor", + "bothairstyle", + "botheritage", + "bottattoo", + "botwoad" + }; if (helper_command_alias_fail(c, "bot_command_appearance", sep->arg[0], "botappearance")) return; diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 8654f5e69b..ab5e036d3e 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -2,25 +2,25 @@ void bot_command_bot(Client *c, const Seperator *sep) { - - std::list subcommand_list; - subcommand_list.push_back("botappearance"); - subcommand_list.push_back("botcamp"); - subcommand_list.push_back("botclone"); - subcommand_list.push_back("botcreate"); - subcommand_list.push_back("botdelete"); - subcommand_list.push_back("botfollowdistance"); - subcommand_list.push_back("botinspectmessage"); - subcommand_list.push_back("botlist"); - subcommand_list.push_back("botoutofcombat"); - subcommand_list.push_back("botreport"); - subcommand_list.push_back("botspawn"); - subcommand_list.push_back("botstance"); - subcommand_list.push_back("botstopmeleelevel"); - subcommand_list.push_back("botsummon"); - subcommand_list.push_back("bottoggleranged"); - subcommand_list.push_back("bottogglehelm"); - subcommand_list.push_back("botupdate"); + std::vector subcommand_list = { + "botappearance", + "botcamp", + "botclone", + "botcreate", + "botdelete", + "botfollowdistance", + "botinspectmessage", + "botlist", + "botoutofcombat", + "botreport", + "botspawn", + "botstance", + "botstopmeleelevel", + "botsummon", + "bottoggleranged", + "bottogglehelm", + "botupdate" + }; if (helper_command_alias_fail(c, "bot_command_bot", sep->arg[0], "bot")) return; diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 9ac4c94179..e5cd77b00e 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -374,7 +374,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { if (spell_type != UINT16_MAX) { - my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(SpellType, bot_stance)); + my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(spell_type, bot_stance)); my_bot->SetSpellDelay(spell_type, my_bot->GetDefaultSpellDelay(spell_type, bot_stance)); my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); diff --git a/zone/bot_commands/heal_rotation.cpp b/zone/bot_commands/heal_rotation.cpp index 0dd57eb377..2ffd6df23b 100644 --- a/zone/bot_commands/heal_rotation.cpp +++ b/zone/bot_commands/heal_rotation.cpp @@ -2,28 +2,28 @@ void bot_command_heal_rotation(Client *c, const Seperator *sep) { - - std::list subcommand_list; - subcommand_list.push_back("healrotationadaptivetargeting"); - subcommand_list.push_back("healrotationaddmember"); - subcommand_list.push_back("healrotationaddtarget"); - subcommand_list.push_back("healrotationadjustcritical"); - subcommand_list.push_back("healrotationadjustsafe"); - subcommand_list.push_back("healrotationcastoverride"); - subcommand_list.push_back("healrotationchangeinterval"); - subcommand_list.push_back("healrotationclearhot"); - subcommand_list.push_back("healrotationcleartargets"); - subcommand_list.push_back("healrotationcreate"); - subcommand_list.push_back("healrotationdelete"); - subcommand_list.push_back("healrotationfastheals"); - subcommand_list.push_back("healrotationlist"); - subcommand_list.push_back("healrotationremovemember"); - subcommand_list.push_back("healrotationremovetarget"); - subcommand_list.push_back("healrotationresetlimits"); - subcommand_list.push_back("healrotationsave"); - subcommand_list.push_back("healrotationsethot"); - subcommand_list.push_back("healrotationstart"); - subcommand_list.push_back("healrotationstop"); + std::vector subcommand_list = { + "healrotationadaptivetargeting", + "healrotationaddmember", + "healrotationaddtarget", + "healrotationadjustcritical", + "healrotationadjustsafe", + "healrotationcastoverride", + "healrotationchangeinterval", + "healrotationclearhot", + "healrotationcleartargets", + "healrotationcreate", + "healrotationdelete", + "healrotationfastheals", + "healrotationlist", + "healrotationremovemember", + "healrotationremovetarget", + "healrotationresetlimits", + "healrotationsave", + "healrotationsethot", + "healrotationstart", + "healrotationstop" + }; if (helper_command_alias_fail(c, "bot_command_heal_rotation", sep->arg[0], "healrotation")) return; diff --git a/zone/bot_commands/inventory.cpp b/zone/bot_commands/inventory.cpp index b83ce228c0..48e5f48798 100644 --- a/zone/bot_commands/inventory.cpp +++ b/zone/bot_commands/inventory.cpp @@ -2,12 +2,12 @@ void bot_command_inventory(Client *c, const Seperator *sep) { - - std::list subcommand_list; - subcommand_list.push_back("inventorygive"); - subcommand_list.push_back("inventorylist"); - subcommand_list.push_back("inventoryremove"); - subcommand_list.push_back("inventorywindow"); + std::vector subcommand_list = { + "inventorygive", + "inventorylist", + "inventoryremove", + "inventorywindow" + }; if (helper_command_alias_fail(c, "bot_command_inventory", sep->arg[0], "inventory")) return; diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index d6ba9a3837..5406d71321 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -3,11 +3,11 @@ void bot_command_pet(Client *c, const Seperator *sep) { - - std::list subcommand_list; - subcommand_list.push_back("petgetlost"); - subcommand_list.push_back("petremove"); - subcommand_list.push_back("petsettype"); + std::vector subcommand_list = { + "petgetlost", + "petremove", + "petsettype" + }; if (helper_command_alias_fail(c, "bot_command_pet", sep->arg[0], "pet")) return; diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 9aae5fd9fc..14e37fa369 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -167,7 +167,7 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) fmt::format( "{} of your bots will now sit in combat whem at or below [{}%%] mana.'", success_count, - TypeValue + type_value ).c_str() ); } diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 5573de2b16..760108ad92 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -1161,8 +1161,8 @@ bool BotDatabase::SaveEquipmentColor(const uint32 bot_id, const int16 slot_id, c if (all_flag) { where_clause = fmt::format( "IN ({}, {}, {}, {}, {}, {}, {})", - EQ::invslot::SlotHead, - EQ::invslot::SlotArms, + EQ::invslot::slotHead, + EQ::invslot::slotArms, EQ::invslot::slotWrist1, EQ::invslot::slotHands, EQ::invslot::slotChest, From fc527b8077cf87f431b27d1bdac1d6b46e4e800d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 11 Jan 2025 01:31:17 -0600 Subject: [PATCH 266/394] fix ^cast wirh commanded types --- zone/bot.cpp | 122 +++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 119 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3594adb4af..124cb6bb1b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11198,7 +11198,10 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { switch (spell_type) { case BotSpellTypes::Buff: case BotSpellTypes::PetBuffs: - if (IsResistanceOnlySpell(spell_id) || IsDamageShieldOnlySpell(spell_id)) { + if ( + IsResistanceOnlySpell(spell_id) || + IsDamageShieldOnlySpell(spell_id) + ) { return false; } @@ -11218,13 +11221,19 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { return false; case BotSpellTypes::PBAENuke: - if (IsPBAENukeSpell(spell_id) && !IsStunSpell(spell_id)) { + if ( + IsPBAENukeSpell(spell_id) && + !IsStunSpell(spell_id) + ) { return true; } return false; case BotSpellTypes::AERains: - if (IsAERainNukeSpell(spell_id) && !IsStunSpell(spell_id)) { + if ( + IsAERainNukeSpell(spell_id) && + !IsStunSpell(spell_id) + ) { return true; } return false; @@ -11241,6 +11250,113 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { return true; } + return false; + case BotSpellTypes::Lull: + if (IsHarmonySpell(spell_id)) { + return true; + } + + return false; + case BotSpellTypes::Teleport: + if ( + IsBeneficialSpell(spell_id) && + ( + IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate) + ) + ) { + return true; + } + + return false; + case BotSpellTypes::Succor: + if ( + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_Succor) + ) { + return true; + } + + return false; + case BotSpellTypes::BindAffinity: + if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + return true; + } + + return false; + case BotSpellTypes::Identify: + if (IsEffectInSpell(spell_id, SE_Identify)) { + return true; + } + + return false; + case BotSpellTypes::Levitate: + if ( + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_Levitate) + ) { + return true; + } + + return false; + case BotSpellTypes::Rune: + if ( + IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || + IsEffectInSpell(spell_id, SE_Rune) + ) { + return true; + } + + return false; + case BotSpellTypes::WaterBreathing: + if (IsEffectInSpell(spell_id, SE_WaterBreathing)) { + return true; + } + + return false; + case BotSpellTypes::Size: + if ( + IsBeneficialSpell(spell_id) && + ( + IsEffectInSpell(spell_id, SE_ModelSize) || + IsEffectInSpell(spell_id, SE_ChangeHeight) + ) + ) { + return true; + } + + return false; + case BotSpellTypes::Invisibility: + if ( + IsEffectInSpell(spell_id, SE_SeeInvis) || + IsInvisibleSpell(spell_id) + ) { + return true; + } + + return false; + case BotSpellTypes::MovementSpeed: + if ( + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) { + return true; + } + + return false; + case BotSpellTypes::SendHome: + if ( + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_GateToHomeCity) + ) { + return true; + } + + return false; + case BotSpellTypes::SummonCorpse: + if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + return true; + } + return false; default: return true; From 01afd68230f67b55e047ce8fcdf1dd82a7d12e92 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 11 Jan 2025 01:40:32 -0600 Subject: [PATCH 267/394] Remove bcspells, fix helper_send_usage_required_bots --- zone/bot.cpp | 2 + zone/bot.h | 5 + zone/bot_command.cpp | 1372 ++---------------------------- zone/bot_command.h | 646 +------------- zone/bot_commands/appearance.cpp | 52 +- zone/bot_commands/cast.cpp | 6 +- zone/bot_commands/pull.cpp | 9 +- zone/bot_structs.h | 7 + zone/botspellsai.cpp | 142 +++- 9 files changed, 218 insertions(+), 2023 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 124cb6bb1b..dd7d7e9bce 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3607,6 +3607,8 @@ bool Bot::Spawn(Client* botCharacterOwner) { } } + MapSpellTypeLevels(); + if (RuleB(Bots, RunSpellTypeChecksOnSpawn)) { OwnerMessage("Running SpellType checks. There may be some spells that are mislabeled as incorrect. Use this as a loose guideline."); CheckBotSpells(); //This runs through a serious of checks and outputs any spells that are set to the wrong spell type in the database diff --git a/zone/bot.h b/zone/bot.h index d558b1a752..6cd4079b78 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -609,6 +609,7 @@ class Bot : public NPC { bool HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar); void CheckBotSpells(); + void MapSpellTypeLevels(); [[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; } [[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; } @@ -951,6 +952,7 @@ class Bot : public NPC { void SetBotTimers(std::vector timers) { bot_timers = timers; } std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } + const CommandedSpellTypesMinLevelMap& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } uint32 GetLastZoneID() const { return _lastZoneId; } int32 GetBaseAC() const { return _baseAC; } int32 GetBaseATK() const { return _baseATK; } @@ -1079,6 +1081,9 @@ class Bot : public NPC { std::vector AIBot_spells; std::vector AIBot_spells_enforced; std::unordered_map> AIBot_spells_by_type; + + CommandedSpellTypesMinLevelMap commanded_spells_min_level; + std::vector bot_timers; std::vector bot_blocked_buffs; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 2ceb520bd9..2c05b748f9 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -64,1168 +64,6 @@ extern QueryServ* QServ; extern WorldServer worldserver; extern TaskManager *task_manager; -bcst_map bot_command_spells; -bcst_required_bot_classes_map required_bots_map; -bcst_required_bot_classes_map_by_class required_bots_map_by_class; - -class BCSpells -{ -public: - static void Load() { - bot_command_spells.clear(); - bcst_levels_map bot_levels_map; - - for (int i = BCEnum::SpellTypeFirst; i <= BCEnum::SpellTypeLast; ++i) { - bot_command_spells[static_cast(i)]; - bot_levels_map[static_cast(i)]; - } - - for (int spell_id = 2; spell_id < SPDAT_RECORDS; ++spell_id) { - if (!IsValidSpell(spell_id)) { - continue; - } - - if (spells[spell_id].player_1[0] == '\0') { - continue; - } - - if ( - spells[spell_id].target_type != ST_Target && - spells[spell_id].cast_restriction != 0 - ) { - continue; - } - - auto target_type = BCEnum::TT_None; - switch (spells[spell_id].target_type) { - case ST_GroupTeleport: - target_type = BCEnum::TT_GroupV1; - break; - case ST_AECaster: - // Disabled until bot code works correctly - //target_type = BCEnum::TT_AECaster; - break; - case ST_AEBard: - // Disabled until bot code works correctly - //target_type = BCEnum::TT_AEBard; - break; - case ST_Target: - switch (spells[spell_id].cast_restriction) { - case 0: - target_type = BCEnum::TT_Single; - break; - case 104: - target_type = BCEnum::TT_Animal; - break; - case 105: - target_type = BCEnum::TT_Plant; - break; - case 118: - target_type = BCEnum::TT_Summoned; - break; - case 120: - target_type = BCEnum::TT_Undead; - break; - default: - break; - } - break; - case ST_Self: - target_type = BCEnum::TT_Self; - break; - case ST_AETarget: - // Disabled until bot code works correctly - //target_type = BCEnum::TT_AETarget; - break; - case ST_Animal: - target_type = BCEnum::TT_Animal; - break; - case ST_Undead: - target_type = BCEnum::TT_Undead; - break; - case ST_Summoned: - target_type = BCEnum::TT_Summoned; - break; - case ST_Corpse: - target_type = BCEnum::TT_Corpse; - break; - case ST_Plant: - target_type = BCEnum::TT_Plant; - break; - case ST_Group: - target_type = BCEnum::TT_GroupV2; - break; - default: - break; - } - if (target_type == BCEnum::TT_None) - continue; - - uint8 class_levels[16] = {0}; - bool player_spell = false; - for (int class_type = Class::Warrior; class_type <= Class::Berserker; ++class_type) { - int class_index = CLASSIDTOINDEX(class_type); - if (spells[spell_id].classes[class_index] == 0 || - spells[spell_id].classes[class_index] > HARD_LEVEL_CAP) { - continue; - } - - class_levels[class_index] = spells[spell_id].classes[class_index]; - player_spell = true; - } - if (!player_spell) - continue; - - STBaseEntry* entry_prototype = nullptr; - while (true) { - switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(1)]) { - case SE_BindAffinity: - entry_prototype = new STBaseEntry(BCEnum::SpT_BindAffinity); - break; - case SE_Charm: - if (spells[spell_id].spell_affect_index != 12) - break; - entry_prototype = new STCharmEntry(); - if (spells[spell_id].resist_difficulty <= -1000) - entry_prototype->SafeCastToCharm()->dire = true; - break; - case SE_Teleport: - entry_prototype = new STDepartEntry; - entry_prototype->SafeCastToDepart()->single = !BCSpells::IsGroupType(target_type); - break; - case SE_Succor: - if (!strcmp(spells[spell_id].teleport_zone, "same")) { - entry_prototype = new STEscapeEntry; - } else { - entry_prototype = new STDepartEntry; - entry_prototype->SafeCastToDepart()->single = !BCSpells::IsGroupType(target_type); - } - break; - case SE_Translocate: - if (spells[spell_id].teleport_zone[0] == '\0') { - entry_prototype = new STSendHomeEntry(); - entry_prototype->SafeCastToSendHome()->group = BCSpells::IsGroupType(target_type); - } else { - entry_prototype = new STDepartEntry; - entry_prototype->SafeCastToDepart()->single = !BCSpells::IsGroupType(target_type); - } - break; - case SE_ModelSize: - if (spells[spell_id].base_value[EFFECTIDTOINDEX(1)] > 100) { - entry_prototype = new STSizeEntry; - entry_prototype->SafeCastToSize()->size_type = BCEnum::SzT_Enlarge; - } else if (spells[spell_id].base_value[EFFECTIDTOINDEX(1)] > 0 && - spells[spell_id].base_value[EFFECTIDTOINDEX(1)] < 100) { - entry_prototype = new STSizeEntry; - entry_prototype->SafeCastToSize()->size_type = BCEnum::SzT_Reduce; - } - break; - case SE_Identify: - entry_prototype = new STBaseEntry(BCEnum::SpT_Identify); - break; - case SE_Invisibility: - if (spells[spell_id].spell_affect_index != 9) - break; - entry_prototype = new STInvisibilityEntry; - entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Living; - break; - case SE_SeeInvis: - if (spells[spell_id].spell_affect_index != 5) - break; - entry_prototype = new STInvisibilityEntry; - entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_See; - break; - case SE_InvisVsUndead: - if (spells[spell_id].spell_affect_index != 9) - break; - entry_prototype = new STInvisibilityEntry; - entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Undead; - break; - case SE_InvisVsAnimals: - if (spells[spell_id].spell_affect_index != 9) - break; - entry_prototype = new STInvisibilityEntry; - entry_prototype->SafeCastToInvisibility()->invis_type = BCEnum::IT_Animal; - break; - case SE_Mez: - if (spells[spell_id].effect_id[EFFECTIDTOINDEX(1)] != 31) - break; - entry_prototype = new STBaseEntry(BCEnum::SpT_Mesmerize); - break; - case SE_Revive: - if (spells[spell_id].spell_affect_index != 1) - break; - entry_prototype = new STResurrectEntry(); - entry_prototype->SafeCastToResurrect()->aoe = BCSpells::IsCasterCentered(target_type); - break; - case SE_Rune: - if (spells[spell_id].spell_affect_index != 2) - break; - entry_prototype = new STBaseEntry(BCEnum::SpT_Rune); - break; - case SE_SummonCorpse: - entry_prototype = new STBaseEntry(BCEnum::SpT_SummonCorpse); - break; - case SE_WaterBreathing: - entry_prototype = new STBaseEntry(BCEnum::SpT_WaterBreathing); - break; - default: - break; - } - if (entry_prototype) - break; - - switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(2)]) { - case SE_Succor: - entry_prototype = new STEscapeEntry; - std::string is_lesser = spells[spell_id].name; - if (is_lesser.find("Lesser") != std::string::npos) - entry_prototype->SafeCastToEscape()->lesser = true; - break; - } - if (entry_prototype) - break; - - switch (spells[spell_id].effect_id[EFFECTIDTOINDEX(3)]) { - case SE_Lull: - entry_prototype = new STBaseEntry(BCEnum::SpT_Lull); - break; - case SE_Levitate: // needs more criteria - entry_prototype = new STBaseEntry(BCEnum::SpT_Levitation); - break; - default: - break; - } - if (entry_prototype) - break; - - while (spells[spell_id].type_description_id == 27) { - if (!spells[spell_id].good_effect) - break; - if (spells[spell_id].skill != EQ::skills::SkillOffense && - spells[spell_id].skill != EQ::skills::SkillDefense) - break; - - entry_prototype = new STStanceEntry(); - if (spells[spell_id].skill == EQ::skills::SkillOffense) - entry_prototype->SafeCastToStance()->stance_type = BCEnum::StT_Aggressive; - else - entry_prototype->SafeCastToStance()->stance_type = BCEnum::StT_Defensive; - - break; - } - if (entry_prototype) - break; - - switch (spells[spell_id].spell_affect_index) { - case 1: { - bool valid_spell = false; - entry_prototype = new STCureEntry; - - for (int i = EffectIDFirst; i <= EffectIDLast; ++i) { - int effect_index = EFFECTIDTOINDEX(i); - if (spells[spell_id].effect_id[effect_index] != SE_Blind && - spells[spell_id].base_value[effect_index] >= 0) - continue; - else if (spells[spell_id].effect_id[effect_index] == SE_Blind && - !spells[spell_id].good_effect) - continue; - - switch (spells[spell_id].effect_id[effect_index]) { - case SE_Blind: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX( - BCEnum::AT_Blindness)] += spells[spell_id].base_value[effect_index]; - break; - case SE_DiseaseCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX( - BCEnum::AT_Disease)] += spells[spell_id].base_value[effect_index]; - break; - case SE_PoisonCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX( - BCEnum::AT_Poison)] += spells[spell_id].base_value[effect_index]; - break; - case SE_CurseCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX( - BCEnum::AT_Curse)] += spells[spell_id].base_value[effect_index]; - break; - case SE_CorruptionCounter: - entry_prototype->SafeCastToCure()->cure_value[AILMENTIDTOINDEX( - BCEnum::AT_Corruption)] += spells[spell_id].base_value[effect_index]; - break; - default: - continue; - } - entry_prototype->SafeCastToCure()->cure_total += spells[spell_id].base_value[effect_index]; - valid_spell = true; - } - if (!valid_spell) { - safe_delete(entry_prototype); - entry_prototype = nullptr; - } - - break; - } - case 2: { - bool valid_spell = false; - entry_prototype = new STResistanceEntry; - - for (int i = EffectIDFirst; i <= EffectIDLast; ++i) { - int effect_index = EFFECTIDTOINDEX(i); - if (spells[spell_id].max_value[effect_index] <= 0) - continue; - - switch (spells[spell_id].effect_id[effect_index]) { - case SE_ResistFire: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX( - BCEnum::RT_Fire)] += spells[spell_id].max_value[effect_index]; - break; - case SE_ResistCold: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX( - BCEnum::RT_Cold)] += spells[spell_id].max_value[effect_index]; - break; - case SE_ResistPoison: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX( - BCEnum::RT_Poison)] += spells[spell_id].max_value[effect_index]; - break; - case SE_ResistDisease: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX( - BCEnum::RT_Disease)] += spells[spell_id].max_value[effect_index]; - break; - case SE_ResistMagic: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX( - BCEnum::RT_Magic)] += spells[spell_id].max_value[effect_index]; - break; - case SE_ResistCorruption: - entry_prototype->SafeCastToResistance()->resist_value[RESISTANCEIDTOINDEX( - BCEnum::RT_Corruption)] += spells[spell_id].max_value[effect_index]; - break; - default: - continue; - } - entry_prototype->SafeCastToResistance()->resist_total += spells[spell_id].max_value[effect_index]; - valid_spell = true; - } - if (!valid_spell) { - safe_delete(entry_prototype); - entry_prototype = nullptr; - } - - break; - } - case 7: - case 10: - if (spells[spell_id].effect_description_id != 65) - break; - if (IsEffectInSpell(spell_id, SE_NegateIfCombat)) - break; - entry_prototype = new STMovementSpeedEntry(); - entry_prototype->SafeCastToMovementSpeed()->group = BCSpells::IsGroupType(target_type); - break; - default: - break; - } - if (entry_prototype) - break; - - break; - } - if (!entry_prototype) - continue; - - if (target_type == BCEnum::TT_Self && (entry_prototype->BCST() != BCEnum::SpT_Stance && - entry_prototype->BCST() != BCEnum::SpT_SummonCorpse)) { -#ifdef BCSTSPELLDUMP - LogError("DELETING entry_prototype (primary clause) - name: [{}], target_type: [{}], BCST: [{}]", - spells[spell_id].name, BCEnum::TargetTypeEnumToString(target_type).c_str(), BCEnum::SpellTypeEnumToString(entry_prototype->BCST()).c_str()); -#endif - safe_delete(entry_prototype); - continue; - } - if (entry_prototype->BCST() == BCEnum::SpT_Stance && target_type != BCEnum::TT_Self) { -#ifdef BCSTSPELLDUMP - LogError("DELETING entry_prototype (secondary clause) - name: [{}], BCST: [{}], target_type: [{}]", - spells[spell_id].name, BCEnum::SpellTypeEnumToString(entry_prototype->BCST()).c_str(), BCEnum::TargetTypeEnumToString(target_type).c_str()); -#endif - safe_delete(entry_prototype); - continue; - } - - assert(entry_prototype->BCST() != BCEnum::SpT_None); - - entry_prototype->spell_id = spell_id; - entry_prototype->target_type = target_type; - - bcst_levels& bot_levels = bot_levels_map[entry_prototype->BCST()]; - for (int class_type = Class::Warrior; class_type <= Class::Berserker; ++class_type) { - int class_index = CLASSIDTOINDEX(class_type); - if (!class_levels[class_index]) - continue; - - STBaseEntry* spell_entry = nullptr; - switch (entry_prototype->BCST()) { - case BCEnum::SpT_Charm: - if (entry_prototype->IsCharm()) - spell_entry = new STCharmEntry(entry_prototype->SafeCastToCharm()); - break; - case BCEnum::SpT_Cure: - if (entry_prototype->IsCure()) - spell_entry = new STCureEntry(entry_prototype->SafeCastToCure()); - break; - case BCEnum::SpT_Depart: - if (entry_prototype->IsDepart()) - spell_entry = new STDepartEntry(entry_prototype->SafeCastToDepart()); - break; - case BCEnum::SpT_Escape: - if (entry_prototype->IsEscape()) - spell_entry = new STEscapeEntry(entry_prototype->SafeCastToEscape()); - break; - case BCEnum::SpT_Invisibility: - if (entry_prototype->IsInvisibility()) - spell_entry = new STInvisibilityEntry(entry_prototype->SafeCastToInvisibility()); - break; - case BCEnum::SpT_MovementSpeed: - if (entry_prototype->IsMovementSpeed()) - spell_entry = new STMovementSpeedEntry(entry_prototype->SafeCastToMovementSpeed()); - break; - case BCEnum::SpT_Resistance: - if (entry_prototype->IsResistance()) - spell_entry = new STResistanceEntry(entry_prototype->SafeCastToResistance()); - break; - case BCEnum::SpT_Resurrect: - if (entry_prototype->IsResurrect()) - spell_entry = new STResurrectEntry(entry_prototype->SafeCastToResurrect()); - break; - case BCEnum::SpT_SendHome: - if (entry_prototype->IsSendHome()) - spell_entry = new STSendHomeEntry(entry_prototype->SafeCastToSendHome()); - break; - case BCEnum::SpT_Size: - if (entry_prototype->IsSize()) - spell_entry = new STSizeEntry(entry_prototype->SafeCastToSize()); - break; - case BCEnum::SpT_Stance: - if (entry_prototype->IsStance()) - spell_entry = new STStanceEntry(entry_prototype->SafeCastToStance()); - break; - default: - spell_entry = new STBaseEntry(entry_prototype); - break; - } - - assert(spell_entry); - - spell_entry->caster_class = class_type; - spell_entry->spell_level = class_levels[class_index]; - - bot_command_spells[spell_entry->BCST()].push_back(spell_entry); - - if (bot_levels.find(class_type) == bot_levels.end() || - bot_levels[class_type] > class_levels[class_index]) - bot_levels[class_type] = class_levels[class_index]; - } - - delete(entry_prototype); - } - - remove_inactive(); - order_all(); - load_teleport_zone_names(); - build_strings(bot_levels_map); - status_report(); - -#ifdef BCSTSPELLDUMP - spell_dump(); -#endif - } - - static void Unload() { - for (auto map_iter : bot_command_spells) { - if (map_iter.second.empty()) - continue; - for (auto list_iter: map_iter.second) { - safe_delete(list_iter); - } - map_iter.second.clear(); - } - bot_command_spells.clear(); - required_bots_map.clear(); - required_bots_map_by_class.clear(); - } - - static bool IsCasterCentered(BCEnum::TType target_type) { - switch (target_type) { - case BCEnum::TT_AECaster: - case BCEnum::TT_AEBard: - return true; - default: - return false; - } - } - - static bool IsGroupType(BCEnum::TType target_type) { - switch (target_type) { - case BCEnum::TT_GroupV1: - case BCEnum::TT_GroupV2: - return true; - default: - return false; - } - } -private: - static void remove_inactive() { - if (bot_command_spells.empty()) - return; - - for (auto map_iter = bot_command_spells.begin(); map_iter != bot_command_spells.end(); ++map_iter) { - if (map_iter->second.empty()) - continue; - - bcst_list* spells_list = &map_iter->second; - bcst_list* removed_spells_list = new bcst_list; - - spells_list->remove(nullptr); - spells_list->remove_if([removed_spells_list](STBaseEntry* l) { - if (l->spell_id < 2 || l->spell_id >= SPDAT_RECORDS || strlen(spells[l->spell_id].name) < 3) { - removed_spells_list->push_back(l); - return true; - } - else { - return false; - } - }); - - for (auto del_iter: *removed_spells_list) - { - safe_delete(del_iter); - } - removed_spells_list->clear(); - - if (RuleI(Bots, CommandSpellRank) == 1) { - spells_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (spells[l->spell_id].spell_group < spells[r->spell_id].spell_group) - return true; - if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class < r->caster_class) - return true; - if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) - return true; - - return false; - }); - spells_list->unique([removed_spells_list](STBaseEntry* l, STBaseEntry* r) { - std::string r_name = spells[r->spell_id].name; - if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank < spells[r->spell_id].rank) { - removed_spells_list->push_back(r); - return true; - } - - return false; - }); - - for (auto del_iter: *removed_spells_list) { - safe_delete(del_iter); - } - removed_spells_list->clear(); - } - - if (RuleI(Bots, CommandSpellRank) == 2) { - spells_list->remove_if([removed_spells_list](STBaseEntry* l) { - std::string l_name = spells[l->spell_id].name; - if (spells[l->spell_id].rank == 10) { - removed_spells_list->push_back(l); - return true; - } - if (l_name.find("III") == (l_name.size() - 3)) { - removed_spells_list->push_back(l); - return true; - } - if (l_name.find("III ") == (l_name.size() - 4)) { - removed_spells_list->push_back(l); - return true; - } - - return false; - }); - - for (auto del_iter: *removed_spells_list) { - safe_delete(del_iter); - } - removed_spells_list->clear(); - } - - // needs rework - if (RuleI(Bots, CommandSpellRank) == 2 || RuleI(Bots, CommandSpellRank) == 3) { - spells_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (spells[l->spell_id].spell_group < spells[r->spell_id].spell_group) - return true; - if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class < r->caster_class) - return true; - if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) - return true; - - return false; - }); - spells_list->unique([removed_spells_list](STBaseEntry* l, STBaseEntry* r) { - std::string l_name = spells[l->spell_id].name; - if (spells[l->spell_id].spell_group == spells[r->spell_id].spell_group && l->caster_class == r->caster_class && spells[l->spell_id].rank > spells[r->spell_id].rank) { - removed_spells_list->push_back(r); - return true; - } - - return false; - }); - - for (auto del_iter: *removed_spells_list) { - safe_delete(del_iter); - } - removed_spells_list->clear(); - } - - safe_delete(removed_spells_list); - } - } - - static void order_all() { - // Example of a macro'd lambda using anonymous property dereference: - // #define XXX(p) ([](const <_Ty>* l, const <_Ty>* r) { return (l->p < r->p); }) - - -#define LT_STBASE(l, r, p) (l->p < r->p) -#define LT_STCHARM(l, r, p) (l->SafeCastToCharm()->p < r->SafeCastToCharm()->p) -#define LT_STCURE(l, r, p) (l->SafeCastToCure()->p < r->SafeCastToCure()->p) -#define LT_STCURE_VAL_ID(l, r, p, ctid) (l->SafeCastToCure()->p[AILMENTIDTOINDEX(ctid)] < r->SafeCastToCure()->p[AILMENTIDTOINDEX(ctid)]) -#define LT_STDEPART(l, r, p) (l->SafeCastToDepart()->p < r->SafeCastToDepart()->p) -#define LT_STESCAPE(l, r, p) (l->SafeCastToEscape()->p < r->SafeCastToEscape()->p) -#define LT_STINVISIBILITY(l, r, p) (l->SafeCastToInvisibility()->p < r->SafeCastToInvisibility()->p) -#define LT_STRESISTANCE(l, r, p) (l->SafeCastToResistance()->p < r->SafeCastToResistance()->p) -#define LT_STRESISTANCE_VAL_ID(l, r, p, rtid) (l->SafeCastToResistance()->p[RESISTANCEIDTOINDEX(rtid)] < r->SafeCastToResistance()->p[RESISTANCEIDTOINDEX(rtid)) -#define LT_STSTANCE(l, r, p) (l->SafeCastToStance()->p < r->SafeCastToStance()->p) -#define LT_SPELLS(l, r, p) (spells[l->spell_id].p < spells[r->spell_id].p) -#define LT_SPELLS_EFFECT_ID(l, r, p, eid) (spells[l->spell_id].p[EFFECTIDTOINDEX(eid)] < spells[r->spell_id].p[EFFECTIDTOINDEX(eid)]) -#define LT_SPELLS_STR(l, r, s) (strcasecmp(spells[l->spell_id].s, spells[r->spell_id].s) < 0) - -#define EQ_STBASE(l, r, p) (l->p == r->p) -#define EQ_STCHARM(l, r, p) (l->SafeCastToCharm()->p == r->SafeCastToCharm()->p) -#define EQ_STCURE(l, r, p) (l->SafeCastToCure()->p == r->SafeCastToCure()->p) -#define EQ_STCURE_VAL_ID(l, r, p, ctid) (l->SafeCastToCure()->p[AILMENTIDTOINDEX(ctid)] == r->SafeCastToCure()->p[AILMENTIDTOINDEX(ctid)]) -#define EQ_STDEPART(l, r, p) (l->SafeCastToDepart()->p == r->SafeCastToDepart()->p) -#define EQ_STESCAPE(l, r, p) (l->SafeCastToEscape()->p == r->SafeCastToEscape()->p) -#define EQ_STINVISIBILITY(l, r, p) (l->SafeCastToInvisibility()->p == r->SafeCastToInvisibility()->p) -#define EQ_STRESISTANCE(l, r, p) (l->SafeCastToResistance()->p == r->SafeCastToResistance()->p) -#define EQ_STRESISTANCE_VAL_ID(l, r, p, rtid) (l->SafeCastToResistance()->p[RESISTANCEIDTOINDEX(rtid)] == r->SafeCastToResistance()->p[RESISTANCEIDTOINDEX(rtid)) -#define EQ_STSTANCE(l, r, p) (l->SafeCastToStance()->p == r->SafeCastToStance()->p) -#define EQ_SPELLS(l, r, p) (spells[l->spell_id].p == spells[r->spell_id].p) -#define EQ_SPELLS_EFFECT_ID(l, r, p, eid) (spells[l->spell_id].p[EFFECTIDTOINDEX(eid)] == spells[r->spell_id].p[EFFECTIDTOINDEX(eid)]) -#define EQ_SPELLS_STR(l, r, s) (strcasecmp(spells[l->spell_id].s, spells[r->spell_id].s) == 0) - -#define GT_STBASE(l, r, p) (l->p > r->p) -#define GT_STCHARM(l, r, p) (l->SafeCastToCharm()->p > r->SafeCastToCharm()->p) -#define GT_STCURE(l, r, p) (l->SafeCastToCure()->p > r->SafeCastToCure()->p) -#define GT_STCURE_VAL_ID(l, r, p, ctid) (l->SafeCastToCure()->p[AILMENTIDTOINDEX(ctid)] > r->SafeCastToCure()->p[AILMENTIDTOINDEX(ctid)]) -#define GT_STDEPART(l, r, p) (l->SafeCastToDepart()->p > r->SafeCastToDepart()->p) -#define GT_STESCAPE(l, r, p) (l->SafeCastToEscape()->p > r->SafeCastToEscape()->p) -#define GT_STINVISIBILITY(l, r, p) (l->SafeCastToInvisibility()->p > r->SafeCastToInvisibility()->p) -#define GT_STRESISTANCE(l, r, p) (l->SafeCastToResistance()->p > r->SafeCastToResistance()->p) -#define GT_STRESISTANCE_VAL_ID(l, r, p, rtid) (l->SafeCastToResistance()->p[RESISTANCEIDTOINDEX(rtid)] > r->SafeCastToResistance()->p[RESISTANCEIDTOINDEX(rtid)) -#define GT_STSTANCE(l, r, p) (l->SafeCastToStance()->p > r->SafeCastToStance()->p) -#define GT_SPELLS(l, r, p) (spells[l->spell_id].p > spells[r->spell_id].p) -#define GT_SPELLS_EFFECT_ID(l, r, p, eid) (spells[l->spell_id].p[EFFECTIDTOINDEX(eid)] > spells[r->spell_id].p[EFFECTIDTOINDEX(eid)]) -#define GT_SPELLS_STR(l, r, s) (strcasecmp(spells[l->spell_id].s, spells[r->spell_id].s) > 0) - - - for (auto map_iter = bot_command_spells.begin(); map_iter != bot_command_spells.end(); ++map_iter) { - if (map_iter->second.size() < 2) - continue; - - auto spell_type = map_iter->first; - bcst_list* spell_list = &map_iter->second; - switch (spell_type) { - case BCEnum::SpT_BindAffinity: - if (RuleB(Bots, PreferNoManaCommandSpells)) { - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_SPELLS(l, r, mana)) - return true; - if (EQ_SPELLS(l, r, mana) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_SPELLS(l, r, mana) && EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_SPELLS(l, r, mana) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - } - else { - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - } - continue; - case BCEnum::SpT_Charm: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_SPELLS(l, r, resist_difficulty)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Cure: // per-use sorting in command handler - spell_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (l->spell_id < r->spell_id) - return true; - if (l->spell_id == r->spell_id && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Depart: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, caster_class)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, caster_class) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, caster_class) && EQ_STBASE(l, r, spell_level) && LT_SPELLS_STR(l, r, name)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Escape: - spell_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (LT_STESCAPE(l, r, lesser)) - return true; - if (EQ_STESCAPE(l, r, lesser) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_STESCAPE(l, r, lesser) && EQ_STBASE(l, r, target_type) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_STESCAPE(l, r, lesser) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Identify: - if (RuleB(Bots, PreferNoManaCommandSpells)) { - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_SPELLS(l, r, mana)) - return true; - if (EQ_SPELLS(l, r, mana) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_SPELLS(l, r, mana) && EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_SPELLS(l, r, mana) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - } - else { - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - } - continue; - case BCEnum::SpT_Invisibility: - spell_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (LT_STINVISIBILITY(l, r, invis_type)) - return true; - if (EQ_STINVISIBILITY(l, r, invis_type) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_STINVISIBILITY(l, r, invis_type) && EQ_STBASE(l, r, target_type) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_STINVISIBILITY(l, r, invis_type) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - return false; - }); - continue; - case BCEnum::SpT_Levitation: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && LT_SPELLS(l, r, zone_type)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zone_type) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS(l, r, zone_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Lull: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_SPELLS(l, r, resist_difficulty)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 3)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 3) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 3) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Mesmerize: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (GT_SPELLS(l, r, resist_difficulty)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_SPELLS(l, r, resist_difficulty) && EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_MovementSpeed: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base_value, 2)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 2) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 2) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Resistance: // per-use sorting in command handler - spell_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (l->spell_id < r->spell_id) - return true; - if (l->spell_id == r->spell_id && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Resurrect: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (GT_SPELLS_EFFECT_ID(l, r, base_value, 1)) - return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && LT_STBASE(l, r, target_type)) - return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, target_type) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Rune: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, max_value, 1)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, max_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_SendHome: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_Size: - spell_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - - auto l_size_type = l->SafeCastToSize()->size_type; - auto r_size_type = r->SafeCastToSize()->size_type; - if (l_size_type < r_size_type) - return true; - if (l_size_type == BCEnum::SzT_Enlarge && r_size_type == BCEnum::SzT_Enlarge) { - if (EQ_STBASE(l, r, target_type) && GT_SPELLS_EFFECT_ID(l, r, base_value, 1)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - } - if (l_size_type == BCEnum::SzT_Reduce && r_size_type == BCEnum::SzT_Reduce) { - if (EQ_STBASE(l, r, target_type) && LT_SPELLS_EFFECT_ID(l, r, base_value, 1)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - } - - return false; - }); - continue; - case BCEnum::SpT_Stance: - spell_list->sort([](STBaseEntry* l, STBaseEntry* r) { - if (LT_STSTANCE(l, r, stance_type)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_SummonCorpse: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (GT_SPELLS_EFFECT_ID(l, r, base_value, 1)) - return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && LT_STBASE(l, r, spell_level)) - return true; - if (EQ_SPELLS_EFFECT_ID(l, r, base_value, 1) && EQ_STBASE(l, r, spell_level) && EQ_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - case BCEnum::SpT_WaterBreathing: - spell_list->sort([](const STBaseEntry* l, const STBaseEntry* r) { - if (LT_STBASE(l, r, target_type)) - return true; - if (EQ_STBASE(l, r, target_type) && GT_STBASE(l, r, spell_level)) - return true; - if (EQ_STBASE(l, r, target_type) && EQ_STBASE(l, r, spell_level) && LT_STBASE(l, r, caster_class)) - return true; - - return false; - }); - continue; - default: - continue; - } - } - } - - static void load_teleport_zone_names() { - auto depart_list = &bot_command_spells[BCEnum::SpT_Depart]; - if (depart_list->empty()) - return; - - std::string query = "SELECT `short_name`, `long_name` FROM `zone` WHERE '' NOT IN (`short_name`, `long_name`)"; - auto results = content_db.QueryDatabase(query); - if (!results.Success()) { - LogError("load_teleport_zone_names() - Error in zone names query: [{}]", results.ErrorMessage().c_str()); - return; - } - - std::map zone_names; - for (auto row = results.begin(); row != results.end(); ++row) - zone_names[row[0]] = row[1]; - - for (auto list_iter = depart_list->begin(); list_iter != depart_list->end();) { - auto test_iter = zone_names.find(spells[(*list_iter)->spell_id].teleport_zone); - if (test_iter == zone_names.end()) { - list_iter = depart_list->erase(list_iter); - continue; - } - - (*list_iter)->SafeCastToDepart()->long_name = test_iter->second; - ++list_iter; - } - - } - - static void build_strings(bcst_levels_map& bot_levels_map) { - for (int i = BCEnum::SpellTypeFirst; i <= BCEnum::SpellTypeLast; ++i) - helper_bots_string(static_cast(i), bot_levels_map[static_cast(i)]); - } - - static void status_report() { - LogCommands("load_bot_command_spells(): - 'RuleI(Bots, CommandSpellRank)' set to [{}]", RuleI(Bots, CommandSpellRank)); - if (bot_command_spells.empty()) { - LogError("load_bot_command_spells() - 'bot_command_spells' is empty"); - return; - } - - for (int i = BCEnum::SpellTypeFirst; i <= BCEnum::SpellTypeLast; ++i) - LogCommands("load_bot_command_spells(): - [{}] returned [{}] spell entries", - BCEnum::SpellTypeEnumToString(static_cast(i)).c_str(), bot_command_spells[static_cast(i)].size()); - } - - static void helper_bots_string(BCEnum::SpType type_index, bcst_levels& bot_levels) { - for (int i = Class::Warrior; i <= Class::Berserker; ++i) - required_bots_map_by_class[type_index][i] = "Unavailable..."; - - if (bot_levels.empty()) { - required_bots_map[type_index] = "This command is currently unavailable..."; - return; - } - - required_bots_map[type_index] = ""; - - auto map_size = bot_levels.size(); - while (bot_levels.size()) { - bcst_levels::iterator test_iter = bot_levels.begin(); - for (bcst_levels::iterator levels_iter = bot_levels.begin(); levels_iter != bot_levels.end(); ++levels_iter) { - if (levels_iter->second < test_iter->second) - test_iter = levels_iter; - if (strcasecmp(GetClassIDName(levels_iter->first), GetClassIDName(test_iter->first)) < 0 && levels_iter->second <= test_iter->second) - test_iter = levels_iter; - } - - std::string bot_segment; - if (bot_levels.size() == map_size) - bot_segment = "%s(%u)"; - else if (bot_levels.size() > 1) - bot_segment = ", %s(%u)"; - else - bot_segment = " or %s(%u)"; - - 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)", GetClassIDName(test_iter->first), test_iter->second); - bot_levels.erase(test_iter); - } - } - -#ifdef BCSTSPELLDUMP - static void spell_dump() { - std::ofstream spell_dump; - spell_dump.open(StringFormat("bcs_dump/spell_dump_%i.txt", getpid()), std::ios_base::app | std::ios_base::out); - - if (bot_command_spells.empty()) { - spell_dump << "BCSpells::spell_dump() - 'bot_command_spells' map is empty.\n"; - spell_dump.close(); - return; - } - - int entry_count = 0; - for (int i = BCEnum::SpellTypeFirst; i <= BCEnum::SpellTypeLast; ++i) { - auto bcst_id = static_cast(i); - spell_dump << StringFormat("BCSpells::spell_dump(): - '%s' returned %u spells:\n", - BCEnum::SpellTypeEnumToString(bcst_id).c_str(), bot_command_spells[bcst_id].size()); - - bcst_list& map_entry = bot_command_spells[bcst_id]; - for (auto list_iter = map_entry.begin(); list_iter != map_entry.end(); ++list_iter) { - STBaseEntry* list_entry = *list_iter; - int spell_id = list_entry->spell_id; - spell_dump << StringFormat("\"%20s\" tt:%02u/cc:%02u/cl:%03u", - ((strlen(spells[spell_id].name) > 20) ? (std::string(spells[spell_id].name).substr(0, 20).c_str()) : (spells[spell_id].name)), - list_entry->target_type, - list_entry->caster_class, - list_entry->spell_level - ); - - spell_dump << StringFormat(" /mn:%05u/RD:%06i/zt:%02i/d#:%06i/td#:%05i/ed#:%05i/SAI:%03u", - spells[spell_id].mana, - spells[spell_id].resist_difficulty, - spells[spell_id].zone_type, - spells[spell_id].description_id, - spells[spell_id].type_description_id, - spells[spell_id].effect_description_id, - spells[spell_id].spell_affect_index - ); - - for (int i = EffectIDFirst; i <= 3/*EffectIDLast*/; ++i) { - int effect_index = EFFECTIDTOINDEX(i); - spell_dump << StringFormat(" /e%02i:%04i/b%02i:%06i/m%02i:%06i", - i, spells[spell_id].effect_id[effect_index], i, spells[spell_id].base_value[effect_index], i, spells[spell_id].max_value[effect_index]); - } - - switch (list_entry->BCST()) { - case BCEnum::SpT_Charm: - spell_dump << StringFormat(" /d:%c", ((list_entry->SafeCastToCharm()->dire) ? ('T') : ('F'))); - break; - case BCEnum::SpT_Cure: - spell_dump << ' '; - for (int i = 0; i < BCEnum::AilmentTypeCount; ++i) { - spell_dump << StringFormat("/cv%02i:%03i", i, list_entry->SafeCastToCure()->cure_value[i]); - } - break; - case BCEnum::SpT_Depart: { - std::string long_name = list_entry->SafeCastToDepart()->long_name.c_str(); - spell_dump << StringFormat(" /ln:%20s", ((long_name.size() > 20) ? (long_name.substr(0, 20).c_str()) : (long_name.c_str()))); - break; - } - case BCEnum::SpT_Escape: - spell_dump << StringFormat(" /l:%c", ((list_entry->SafeCastToEscape()->lesser) ? ('T') : ('F'))); - break; - case BCEnum::SpT_Invisibility: - spell_dump << StringFormat(" /it:%02i", list_entry->SafeCastToInvisibility()->invis_type); - break; - case BCEnum::SpT_MovementSpeed: - spell_dump << StringFormat(" /g:%c", ((list_entry->SafeCastToMovementSpeed()->group) ? ('T') : ('F'))); - break; - case BCEnum::SpT_Resistance: - spell_dump << ' '; - for (int i = 0; i < BCEnum::ResistanceTypeCount; ++i) { - spell_dump << StringFormat("/rv%02i:%03i", i, list_entry->SafeCastToResistance()->resist_value[i]); - } - break; - case BCEnum::SpT_Resurrect: - spell_dump << StringFormat(" /aoe:%c", ((list_entry->SafeCastToResurrect()->aoe) ? ('T') : ('F'))); - break; - case BCEnum::SpT_SendHome: - spell_dump << StringFormat(" /g:%c", ((list_entry->SafeCastToSendHome()->group) ? ('T') : ('F'))); - break; - case BCEnum::SpT_Size: - spell_dump << StringFormat(" /st:%02i", list_entry->SafeCastToSize()->size_type); - break; - case BCEnum::SpT_Stance: - spell_dump << StringFormat(" /st:%02i", list_entry->SafeCastToStance()->stance_type); - break; - default: - break; - } - - spell_dump << "\n"; - ++entry_count; - } - - spell_dump << StringFormat("required_bots_map[%s] = \"%s\"\n", - BCEnum::SpellTypeEnumToString(static_cast(i)).c_str(), required_bots_map[static_cast(i)].c_str()); - - spell_dump << "\n"; - } - - spell_dump << StringFormat("Total bcs entry count: %i\n", entry_count); - spell_dump.close(); - } -#endif -}; - int bot_command_count; int (*bot_command_dispatch)(Client *,char const *) = bot_command_not_avail; @@ -1465,8 +303,6 @@ int bot_command_init(void) bot_command_dispatch = bot_command_real_dispatch; - BCSpells::Load(); - return bot_command_count; } @@ -1477,8 +313,6 @@ void bot_command_deinit(void) bot_command_dispatch = bot_command_not_avail; bot_command_count = 0; - - BCSpells::Unload(); } int bot_command_add(std::string bot_command_name, const char *desc, int access, BotCmdFuncPtr function) @@ -1552,18 +386,18 @@ int bot_command_real_dispatch(Client *c, const char *message) } -bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, BCEnum::AFType fail_type, const char* type_desc) +bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, uint8 fail_type, const char* type_desc) { switch (fail_type) { - case BCEnum::AFT_Value: - bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid value for this command", type_desc, my_bot->GetCleanName()); - return true; - case BCEnum::AFT_GenderRace: - bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot gender and/or race for this command", type_desc, my_bot->GetCleanName()); - return true; - case BCEnum::AFT_Race: - bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot race for this command", type_desc, my_bot->GetCleanName()); - return true; + case AFT_Value: + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid value for this command", type_desc, my_bot->GetCleanName()); + return true; + case AFT_GenderRace: + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot gender and/or race for this command", type_desc, my_bot->GetCleanName()); + return true; + case AFT_Race: + bot_owner->Message(Chat::Yellow, "Failed to change '%s' for %s due to invalid bot race for this command", type_desc, my_bot->GetCleanName()); + return true; default: return false; } @@ -1876,26 +710,6 @@ int helper_bot_follow_option_chain(Client* bot_owner) return chain_follow_count; } -bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast, uint32* dont_root_before) -{ - if (!casting_bot || !target_mob) - return false; - - casting_bot->InterruptSpell(); - if (annouce_cast) { - Bot::BotGroupSay( - casting_bot, - fmt::format( - "Attempting to cast {} on {}.", - spells[spell_id].name, - target_mob->GetCleanName() - ).c_str() - ); - } - - return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQ::spells::CastingSlot::Gem2, -1, -1, dont_root_before); -} - bool helper_command_disabled(Client* bot_owner, bool rule_value, const char* command) { if (!rule_value) { @@ -1917,104 +731,6 @@ bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, c return false; } -void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag) -{ - if (!bot_owner) { - return; - } - - if (!MyBots::IsMyBot(bot_owner, druid_bot)) { - druid_bot = nullptr; - } - - if (!MyBots::IsMyBot(bot_owner, wizard_bot)) { - wizard_bot = nullptr; - } - - if (!druid_bot && !wizard_bot) { - bot_owner->Message(Chat::Yellow, "No bots are capable of performing this action"); - return; - } - - if (!local_list) { - bot_owner->Message(Chat::Yellow, "There are no destinations you can be taken to."); - return; - } - - auto destination_count = 0; - auto destination_number = 1; - for (auto list_iter : *local_list) { - auto local_entry = list_iter->SafeCastToDepart(); - if (!local_entry) { - continue; - } - - if ( - druid_bot && - druid_bot->GetClass() == local_entry->caster_class && - druid_bot->GetLevel() >= local_entry->spell_level - ) { - if (local_entry->single != single_flag) { - continue; - } - - druid_bot->OwnerMessage( - fmt::format( - "Destination {} | {} | {}", - destination_number, - local_entry->long_name, - Saylink::Silent( - fmt::format( - "^circle {}{}", - spells[local_entry->spell_id].teleport_zone, - single_flag ? " single" : "" - ), - "Goto" - ) - ) - ); - - destination_count++; - destination_number++; - continue; - } - - if ( - wizard_bot && - wizard_bot->GetClass() == local_entry->caster_class && - wizard_bot->GetLevel() >= local_entry->spell_level - ) { - if (local_entry->single != single_flag) { - continue; - } - - wizard_bot->OwnerMessage( - fmt::format( - "Destination {} | {} | {}", - destination_number, - local_entry->long_name, - Saylink::Silent( - fmt::format( - "^portal {}{}", - spells[local_entry->spell_id].teleport_zone, - single_flag ? " single" : "" - ), - "Goto" - ) - ) - ); - - destination_count++; - destination_number++; - continue; - } - } - - if (!destination_count) { - bot_owner->Message(Chat::Yellow, "There are no destinations you can be taken to."); - } -} - bool helper_is_help_or_usage(const char* arg) { if (!arg) @@ -2067,33 +783,59 @@ void helper_send_available_subcommands(Client* bot_owner, const char* command_si bot_owner->Message(Chat::White, "%d bot subcommand%s listed.", bot_subcommands_shown, bot_subcommands_shown != 1 ? "s" : ""); } -void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class) +void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type) { - bot_owner->Message(Chat::White, "requires one of the following bot classes:"); - if (bot_class) - bot_owner->Message(Chat::White, "%s", required_bots_map_by_class[spell_type][bot_class].c_str()); - else - bot_owner->Message(Chat::White, "%s", required_bots_map[spell_type].c_str()); -} + if (!bot_owner) { + return; + } -bool helper_spell_check_fail(STBaseEntry* local_entry) -{ - if (!local_entry) - return true; - if (spells[local_entry->spell_id].zone_type && zone->GetZoneType() && !(spells[local_entry->spell_id].zone_type & zone->GetZoneType())) - return true; + auto sbl = entity_list.GetBotListByCharacterID(bot_owner->CharacterID()); + Bot* bot = nullptr; - return false; -} + for (const auto& b : sbl) { + if (b) { + bot = b; -bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type) -{ - if (!spell_list || spell_list->empty()) { - bot_owner->Message(Chat::Yellow, "%s", required_bots_map[spell_type].c_str()); - return true; + break; + } } - return false; + auto& spell_map = bot->GetCommandedSpellTypesMinLevels(); + + if (spell_map.empty()) { + bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type"); //deleteme + return; + } + + bool found = false; + std::string description; + + for (int i = Class::Warrior; i <= Class::Berserker; ++i) { + auto spell_type_itr = spell_map.find(spell_type); + auto class_itr = spell_type_itr->second.find(i); + const auto& spell_info = class_itr->second; + + if (spell_info.min_level < UINT8_MAX) { + found = true; + + if (!description.empty()) { + description.append(", "); + } + else { + bot_owner->Message(Chat::Yellow, "Required bots to cast: Class [Class ID]: [Level]"); + } + + description.append(spell_info.description); + } + } + + if (!found || description.empty()) { + bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type"); + + return; + } + + bot_owner->Message(Chat::Green, "%s", description.c_str()); } void SendSpellTypeWindow(Client* c, const Seperator* sep) { diff --git a/zone/bot_command.h b/zone/bot_command.h index 1482bcc61e..553f098c41 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -27,194 +27,6 @@ class Seperator; #include "bot.h" #include "dialogue_window.h" -class BCEnum -{ -public: - typedef enum SpellType { - SpT_None = 0, - SpT_BindAffinity, - SpT_Charm, - SpT_Cure, - SpT_Depart, - SpT_Escape, - SpT_Identify, - SpT_Invisibility, - SpT_Levitation, - SpT_Lull, - SpT_Mesmerize, - SpT_MovementSpeed, - SpT_Resistance, - SpT_Resurrect, - SpT_Rune, - SpT_SendHome, - SpT_Size, - SpT_Stance, - SpT_SummonCorpse, - SpT_WaterBreathing - } SpType; - static const int SpellTypeFirst = SpT_BindAffinity; - static const int SpellTypeLast = SpT_WaterBreathing; - - typedef enum TargetType { - TT_None = 0, - TT_Corpse, - TT_Self, - TT_Animal, - TT_Undead, - TT_Summoned, - TT_Plant, - TT_Single, - TT_GroupV1, - TT_GroupV2, - TT_AECaster, - TT_AEBard, - TT_AETarget - } TType; - static const int TargetTypeFirst = TT_Corpse; - static const int TargetTypeLast = TT_AETarget; - static const int TargetTypeCount = 13; - - typedef enum TargetMask { - TM_None = 0, - TM_Corpse = 1, - TM_Self = 2, - TM_Animal = 4, - TM_Undead = 8, - TM_Summoned = 16, - TM_Plant = 32, - TM_Single = 124, // currently, 2^6 + 2^{2..5}) -or- (64+32+16+8+4) - TM_GroupV1 = 128, - TM_GroupV2 = 256, - TM_AECaster = 512, - TM_AEBard = 1024, - TM_AETarget = 2048 - } TMask; - - typedef enum AppearanceFailType { - AFT_None = 0, - AFT_Value, - AFT_GenderRace, - AFT_Race - } AFType; - - typedef enum AilmentType { - AT_None = 0, - AT_Blindness, // SE: 20 - AT_Disease, // SE: 35 - AT_Poison, // SE: 36 - AT_Curse, // SE: 116 - AT_Corruption // SE: 369 - } AType; - static const int AilmentTypeCount = 5; - - typedef enum InvisType { - IT_None = 0, - IT_Animal, - IT_Undead, - IT_Living, - IT_See - } IType; - - typedef enum ResistanceType { - RT_None = 0, - RT_Fire, // SE: 46 - RT_Cold, // SE: 47 - RT_Poison, // SE: 48 - RT_Disease, // SE: 49 - RT_Magic, // SE: 50 - RT_Corruption // SE: 370 - } RType; - static const int ResistanceTypeCount = 6; - - typedef enum SizeType { - SzT_None = 0, - SzT_Enlarge, - SzT_Reduce - } SzType; - - typedef enum StanceType { - StT_None = 0, - StT_Aggressive, - StT_Defensive - } StType; - - static std::string SpellTypeEnumToString(BCEnum::SpType spell_type) { - switch (spell_type) { - case SpT_BindAffinity: - return "SpT_BindAffinity"; - case SpT_Charm: - return "SpT_Charm"; - case SpT_Cure: - return "SpT_Cure"; - case SpT_Depart: - return "SpT_Depart"; - case SpT_Escape: - return "SpT_Escape"; - case SpT_Identify: - return "SpT_Identify"; - case SpT_Invisibility: - return "SpT_Invisibility"; - case SpT_Levitation: - return "SpT_Levitation"; - case SpT_Lull: - return "SpT_Lull"; - case SpT_Mesmerize: - return "SpT_Mesmerize"; - case SpT_MovementSpeed: - return "SpT_MovementSpeed"; - case SpT_Resistance: - return "SpT_Resistance"; - case SpT_Resurrect: - return "SpT_Resurrect"; - case SpT_Rune: - return "SpT_Rune"; - case SpT_SendHome: - return "SpT_SendHome"; - case SpT_Size: - return "SpT_Size"; - case SpT_Stance: - return "SpT_Stance"; - case SpT_SummonCorpse: - return "SpT_SummonCorpse"; - case SpT_WaterBreathing: - return "SpT_WaterBreathing"; - default: - return "SpT_None"; - } - } - - static std::string TargetTypeEnumToString(BCEnum::TType target_type) { - switch (target_type) { - case TT_Self: - return "TT_Self"; - case TT_Animal: - return "TT_Animal"; - case TT_Undead: - return "TT_Undead"; - case TT_Summoned: - return "TT_Summoned"; - case TT_Plant: - return "TT_Plant"; - case TT_Single: - return "TT_Single"; - case TT_GroupV1: - return "TT_GroupV1"; - case TT_GroupV2: - return "TT_GroupV2"; - case TT_AECaster: - return "TT_AECaster"; - case TT_AEBard: - return "TT_AEBard"; - case TT_AETarget: - return "TT_AETarget"; - case TT_Corpse: - return "TT_Corpse"; - default: - return "TT_None"; - } - } -}; - namespace { #define HP_RATIO_DELTA 5.0f @@ -222,14 +34,17 @@ namespace enum { EffectIDFirst = 1, EffectIDLast = 12 }; #define VALIDATECLASSID(x) ((x >= Class::Warrior && x <= Class::Berserker) ? (x) : (0)) -#define CLASSIDTOINDEX(x) ((x >= Class::Warrior && x <= Class::Berserker) ? (x - 1) : (0)) -#define EFFECTIDTOINDEX(x) ((x >= EffectIDFirst && x <= EffectIDLast) ? (x - 1) : (0)) -#define AILMENTIDTOINDEX(x) ((x >= BCEnum::AT_Blindness && x <= BCEnum::AT_Corruption) ? (x - 1) : (0)) -#define RESISTANCEIDTOINDEX(x) ((x >= BCEnum::RT_Fire && x <= BCEnum::RT_Corruption) ? (x - 1) : (0)) // ActionableTarget action_type #define FRIENDLY true #define ENEMY false + + enum { + AFT_None = 0, + AFT_Value, + AFT_GenderRace, + AFT_Race + }; } namespace MyBots @@ -759,128 +574,6 @@ namespace ActionableTarget return false; } - - static Mob* VerifyFriendly(Client* bot_owner, BCEnum::TType target_type, bool return_me_on_null_target = true) { - if (IsAttackable(bot_owner, bot_owner->GetTarget()) || target_type == BCEnum::TT_None) - return nullptr; - - auto target_mob = bot_owner->GetTarget(); - Mob* verified_friendly = nullptr; - switch (target_type) { - case BCEnum::TT_Single: - case BCEnum::TT_GroupV1: - case BCEnum::TT_GroupV2: - case BCEnum::TT_AETarget: - verified_friendly = target_mob; - break; - case BCEnum::TT_Animal: - if (target_mob && target_mob->GetBodyType() == BodyType::Animal) - verified_friendly = target_mob; - break; - case BCEnum::TT_Undead: - if (target_mob && target_mob->GetBodyType() == BodyType::Undead) - verified_friendly = target_mob; - break; - case BCEnum::TT_Summoned: - if (target_mob && target_mob->GetBodyType() == BodyType::Summoned) - verified_friendly = target_mob; - break; - case BCEnum::TT_Plant: - if (target_mob && target_mob->GetBodyType() == BodyType::Plant) - verified_friendly = target_mob; - break; - case BCEnum::TT_Corpse: - if (target_mob && target_mob->IsCorpse()) - verified_friendly = target_mob; - break; - default: - return nullptr; - } - - if (return_me_on_null_target && !target_mob && !verified_friendly) { - switch (target_type) { - case BCEnum::TT_Single: - case BCEnum::TT_GroupV1: - case BCEnum::TT_GroupV2: - case BCEnum::TT_AETarget: - verified_friendly = bot_owner; - break; - default: - break; - } - } - - return verified_friendly; - } - - static Mob* VerifyEnemy(Client* bot_owner, BCEnum::TType target_type) { - if (!IsAttackable(bot_owner, bot_owner->GetTarget()) || target_type == BCEnum::TT_None) - return nullptr; - - auto target_mob = bot_owner->GetTarget(); - Mob* verified_enemy = nullptr; - switch (target_type) { - case BCEnum::TT_Animal: - if (target_mob->GetBodyType() == BodyType::Animal) - verified_enemy = target_mob; - break; - case BCEnum::TT_Undead: - if (target_mob->GetBodyType() == BodyType::Undead) - verified_enemy = target_mob; - break; - case BCEnum::TT_Summoned: - if (target_mob->GetBodyType() == BodyType::Summoned) - verified_enemy = target_mob; - break; - case BCEnum::TT_Plant: - if (target_mob->GetBodyType() == BodyType::Plant) - verified_enemy = target_mob; - break; - case BCEnum::TT_Single: - case BCEnum::TT_GroupV1: - case BCEnum::TT_GroupV2: - case BCEnum::TT_AETarget: - verified_enemy = target_mob; - break; - case BCEnum::TT_Corpse: - if (target_mob->IsCorpse()) - verified_enemy = target_mob; - break; - default: - return nullptr; - } - - return verified_enemy; - } - - class Types { - Mob* target[BCEnum::TargetTypeCount]; - bool target_set[BCEnum::TargetTypeCount]; - - public: - Types() { Clear(); } - - void Clear() { - for (int i = BCEnum::TT_None; i <= BCEnum::TargetTypeLast; ++i) { - target[i] = nullptr; - target_set[i] = false; - } - target_set[BCEnum::TT_None] = true; - } - - Mob* Select(Client* bot_owner, BCEnum::TType target_type, bool action_type, bool return_me_on_null_target = true) { - if (target_set[target_type]) - return target[target_type]; - - if (action_type == FRIENDLY) - target[target_type] = VerifyFriendly(bot_owner, target_type, return_me_on_null_target); - else - target[target_type] = VerifyEnemy(bot_owner, target_type); - target_set[target_type] = true; - - return target[target_type]; - } - }; } namespace ActionableBots @@ -1228,7 +921,7 @@ namespace ActionableBots return nullptr; } - static Bot* Select_ByClass(Client* bot_owner, BCEnum::TType target_type, std::vector& sbl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { + static Bot* Select_ByClass(Client* bot_owner, int target_type, std::vector& sbl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { if (!bot_owner || sbl.empty()) return nullptr; @@ -1239,7 +932,7 @@ namespace ActionableBots continue; if (petless && bot_iter->GetPet()) continue; - if (target_type == BCEnum::TT_GroupV1) { + if (target_type == ST_GroupTeleport) { if (!target_mob) return nullptr; else if (bot_iter->GetGroup() != target_mob->GetGroup()) @@ -1252,7 +945,7 @@ namespace ActionableBots return nullptr; } - static Bot* Select_ByMinLevelAndClass(Client* bot_owner, BCEnum::TType target_type, std::vector& sbl, uint8 minlvl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { + static Bot* Select_ByMinLevelAndClass(Client* bot_owner, int target_type, std::vector& sbl, uint8 minlvl, uint8 cls, Mob* target_mob = nullptr, bool petless = false) { if (!bot_owner || sbl.empty()) return nullptr; @@ -1263,7 +956,7 @@ namespace ActionableBots continue; if (petless && bot_iter->GetPet()) continue; - if (target_type == BCEnum::TT_GroupV1) { + if (target_type == ST_GroupTeleport) { if (!target_mob) return nullptr; else if (bot_iter->GetGroup() != target_mob->GetGroup()) @@ -1332,315 +1025,6 @@ namespace ActionableBots } } - -class STBaseEntry; -class STCharmEntry; -class STCureEntry; -class STDepartEntry; -class STEscapeEntry; -class STInvisibilityEntry; -class STMovementSpeedEntry; -class STResistanceEntry; -class STResurrectEntry; -class STSendHomeEntry; -class STSizeEntry; -class STStanceEntry; - -class STBaseEntry -{ -protected: - BCEnum::SpType m_bcst; - -public: - int spell_id; - uint8 spell_level; - uint8 caster_class; - BCEnum::TType target_type; - - // A non-polymorphic constructor requires an appropriate, non-'ST_None' BCEnum::SType - STBaseEntry(BCEnum::SpType init_bcst = BCEnum::SpT_None) { - spell_id = 0; - spell_level = 255; - caster_class = 255; - target_type = BCEnum::TT_None; - m_bcst = init_bcst; - } - STBaseEntry(STBaseEntry* prototype) { - spell_id = prototype->spell_id; - spell_level = 255; - caster_class = 255; - target_type = prototype->target_type; - m_bcst = prototype->BCST(); - } - virtual ~STBaseEntry() { return; }; - - BCEnum::SpType BCST() { return m_bcst; } - - virtual bool IsDerived() { return false; } - - bool IsCharm() const { return (m_bcst == BCEnum::SpT_Charm); } - bool IsCure() const { return (m_bcst == BCEnum::SpT_Cure); } - bool IsDepart() const { return (m_bcst == BCEnum::SpT_Depart); } - bool IsEscape() const { return (m_bcst == BCEnum::SpT_Escape); } - bool IsInvisibility() const { return (m_bcst == BCEnum::SpT_Invisibility); } - bool IsMovementSpeed() const { return (m_bcst == BCEnum::SpT_MovementSpeed); } - bool IsResistance() const { return (m_bcst == BCEnum::SpT_Resistance); } - bool IsResurrect() const { return (m_bcst == BCEnum::SpT_Resurrect); } - bool IsSendHome() const { return (m_bcst == BCEnum::SpT_SendHome); } - bool IsSize() const { return (m_bcst == BCEnum::SpT_Size); } - bool IsStance() const { return (m_bcst == BCEnum::SpT_Stance); } - - virtual STCharmEntry* SafeCastToCharm() { return nullptr; } - virtual STCureEntry* SafeCastToCure() { return nullptr; } - virtual STDepartEntry* SafeCastToDepart() { return nullptr; } - virtual STEscapeEntry* SafeCastToEscape() { return nullptr; } - virtual STInvisibilityEntry* SafeCastToInvisibility() { return nullptr; } - virtual STMovementSpeedEntry* SafeCastToMovementSpeed() { return nullptr; } - virtual STResistanceEntry* SafeCastToResistance() { return nullptr; } - virtual STResurrectEntry* SafeCastToResurrect() { return nullptr; } - virtual STSendHomeEntry* SafeCastToSendHome() { return nullptr; } - virtual STSizeEntry* SafeCastToSize() { return nullptr; } - virtual STStanceEntry* SafeCastToStance() { return nullptr; } -}; - -class STCharmEntry : public STBaseEntry -{ -public: - bool dire; - - STCharmEntry() { - m_bcst = BCEnum::SpT_Charm; - dire = false; - } - STCharmEntry(STCharmEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Charm; - dire = prototype->dire; - } - virtual ~STCharmEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STCharmEntry* SafeCastToCharm() { return ((m_bcst == BCEnum::SpT_Charm) ? (static_cast(this)) : (nullptr)); } -}; - -class STCureEntry : public STBaseEntry -{ -public: - int cure_value[BCEnum::AilmentTypeCount]; - int cure_total; - - STCureEntry() { - m_bcst = BCEnum::SpT_Cure; - memset(&cure_value, 0, (sizeof(int) * BCEnum::AilmentTypeCount)); - cure_total = 0; - } - STCureEntry(STCureEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Cure; - memcpy(&cure_value, prototype->cure_value, (sizeof(int) * BCEnum::AilmentTypeCount)); - cure_total = prototype->cure_total; - } - virtual ~STCureEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STCureEntry* SafeCastToCure() { return ((m_bcst == BCEnum::SpT_Cure) ? (static_cast(this)) : (nullptr)); } -}; - -class STDepartEntry : public STBaseEntry -{ -public: - bool single; - std::string long_name; - - STDepartEntry() { - m_bcst = BCEnum::SpT_Depart; - single = false; - long_name.clear(); - } - STDepartEntry(STDepartEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Depart; - single = prototype->single; - long_name = prototype->long_name; - } - virtual ~STDepartEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STDepartEntry* SafeCastToDepart() { return ((m_bcst == BCEnum::SpT_Depart) ? (static_cast(this)) : (nullptr)); } -}; - -class STEscapeEntry : public STBaseEntry -{ -public: - bool lesser; - - STEscapeEntry() { - m_bcst = BCEnum::SpT_Escape; - lesser = false; - } - STEscapeEntry(STEscapeEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Escape; - lesser = prototype->lesser; - } - virtual ~STEscapeEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STEscapeEntry* SafeCastToEscape() { return ((m_bcst == BCEnum::SpT_Escape) ? (static_cast(this)) : (nullptr)); } -}; - -class STInvisibilityEntry : public STBaseEntry -{ -public: - BCEnum::IType invis_type; - - STInvisibilityEntry() { - m_bcst = BCEnum::SpT_Invisibility; - invis_type = BCEnum::IT_None; - } - STInvisibilityEntry(STInvisibilityEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Invisibility; - invis_type = prototype->invis_type; - } - virtual ~STInvisibilityEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STInvisibilityEntry* SafeCastToInvisibility() { return ((m_bcst == BCEnum::SpT_Invisibility) ? (static_cast(this)) : (nullptr)); } -}; - -class STMovementSpeedEntry : public STBaseEntry -{ -public: - bool group; - - STMovementSpeedEntry() { - m_bcst = BCEnum::SpT_MovementSpeed; - group = false; - } - STMovementSpeedEntry(STMovementSpeedEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_MovementSpeed; - group = prototype->group; - } - virtual ~STMovementSpeedEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STMovementSpeedEntry* SafeCastToMovementSpeed() { return ((m_bcst == BCEnum::SpT_MovementSpeed) ? (static_cast(this)) : (nullptr)); } -}; - -class STResistanceEntry : public STBaseEntry -{ -public: - int resist_value[BCEnum::ResistanceTypeCount]; - int resist_total; - - STResistanceEntry() { - m_bcst = BCEnum::SpT_Resistance; - memset(&resist_value, 0, (sizeof(int) * BCEnum::ResistanceTypeCount)); - resist_total = 0; - } - STResistanceEntry(STResistanceEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Resistance; - memcpy(&resist_value, prototype->resist_value, (sizeof(int) * BCEnum::ResistanceTypeCount)); - resist_total = prototype->resist_total; - } - virtual ~STResistanceEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STResistanceEntry* SafeCastToResistance() { return ((m_bcst == BCEnum::SpT_Resistance) ? (static_cast(this)) : (nullptr)); } -}; - -class STResurrectEntry : public STBaseEntry -{ -public: - bool aoe; - - STResurrectEntry() { - m_bcst = BCEnum::SpT_Resurrect; - aoe = false; - } - STResurrectEntry(STResurrectEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Resurrect; - aoe = prototype->aoe; - } - virtual ~STResurrectEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STResurrectEntry* SafeCastToResurrect() { return ((m_bcst == BCEnum::SpT_Resurrect) ? (static_cast(this)) : (nullptr)); } -}; - -class STSendHomeEntry : public STBaseEntry -{ -public: - bool group; - - STSendHomeEntry() { - m_bcst = BCEnum::SpT_SendHome; - group = false; - } - STSendHomeEntry(STSendHomeEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_SendHome; - group = prototype->group; - } - virtual ~STSendHomeEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STSendHomeEntry* SafeCastToSendHome() { return ((m_bcst == BCEnum::SpT_SendHome) ? (static_cast(this)) : (nullptr)); } -}; - -class STSizeEntry : public STBaseEntry -{ -public: - BCEnum::SzType size_type; - - STSizeEntry() { - m_bcst = BCEnum::SpT_Size; - size_type = BCEnum::SzT_None; - } - STSizeEntry(STSizeEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Size; - size_type = prototype->size_type; - } - virtual ~STSizeEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STSizeEntry* SafeCastToSize() { return ((m_bcst == BCEnum::SpT_Size) ? (static_cast(this)) : (nullptr)); } -}; - -class STStanceEntry : public STBaseEntry { -public: - BCEnum::StType stance_type; - - STStanceEntry() { - m_bcst = BCEnum::SpT_Stance; - stance_type = BCEnum::StT_None; - } - STStanceEntry(STStanceEntry* prototype) : STBaseEntry(prototype) { - m_bcst = BCEnum::SpT_Stance; - stance_type = prototype->stance_type; - } - virtual ~STStanceEntry() { return; }; - - virtual bool IsDerived() { return true; } - - virtual STStanceEntry* SafeCastToStance() { return ((m_bcst == BCEnum::SpT_Stance) ? (static_cast(this)) : (nullptr)); } -}; - - -typedef std::list bcst_list; -typedef std::map bcst_map; - -typedef std::map bcst_required_bot_classes_map; -typedef std::map> bcst_required_bot_classes_map_by_class; - -typedef std::map bcst_levels; -typedef std::map bcst_levels_map; - #define BOT_COMMAND_CHAR '^' typedef void (*BotCmdFuncPtr)(Client *,const Seperator *); @@ -1791,21 +1175,17 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep); // bot command helpers -bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, BCEnum::AFType fail_type, const char* type_desc); +bool helper_bot_appearance_fail(Client *bot_owner, Bot *my_bot, uint8 fail_type, const char* type_desc); void helper_bot_appearance_form_final(Client *bot_owner, Bot *my_bot); void helper_bot_appearance_form_update(Bot *my_bot); uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_class, uint16 bot_race, uint8 bot_gender); int helper_bot_follow_option_chain(Client *bot_owner); -bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id, bool annouce_cast = true, uint32* dont_root_before = nullptr); bool helper_command_disabled(Client *bot_owner, bool rule_value, const char *command); bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command); -void helper_command_depart_list(Client* bot_owner, Bot* druid_bot, Bot* wizard_bot, bcst_list* local_list, bool single_flag = false); bool helper_is_help_or_usage(const char* arg); bool helper_no_available_bots(Client *bot_owner, Bot *my_bot = nullptr); void helper_send_available_subcommands(Client *bot_owner, const char* command_simile, std::vector subcommand_list); -void helper_send_usage_required_bots(Client *bot_owner, BCEnum::SpType spell_type, uint8 bot_class = Class::None); -bool helper_spell_check_fail(STBaseEntry* local_entry); -bool helper_spell_list_fail(Client *bot_owner, bcst_list* spell_list, BCEnum::SpType spell_type); +void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type); void SendSpellTypeWindow(Client* c, const Seperator* sep); #endif diff --git a/zone/bot_commands/appearance.cpp b/zone/bot_commands/appearance.cpp index 5b8d883cc5..5758846c9e 100644 --- a/zone/bot_commands/appearance.cpp +++ b/zone/bot_commands/appearance.cpp @@ -45,11 +45,11 @@ void bot_command_beard_color(Client *c, const Seperator *sep) uint8 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF) - fail_type = BCEnum::AFT_GenderRace; + fail_type = AFT_GenderRace; else if (!PlayerAppearance::IsValidBeardColor(my_bot->GetRace(), my_bot->GetGender(), uvalue)) - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; else my_bot->SetBeardColor(uvalue); @@ -82,11 +82,11 @@ void bot_command_beard_style(Client *c, const Seperator *sep) uint8 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (my_bot->GetGender() != Gender::Male && my_bot->GetRace() != DWARF) - fail_type = BCEnum::AFT_GenderRace; + fail_type = AFT_GenderRace; else if (!PlayerAppearance::IsValidBeard(my_bot->GetRace(), my_bot->GetGender(), uvalue)) - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; else my_bot->SetBeard(uvalue); @@ -121,11 +121,11 @@ void bot_command_details(Client *c, const Seperator *sep) uint32 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (my_bot->GetRace() != DRAKKIN) - fail_type = BCEnum::AFT_Race; + fail_type = AFT_Race; else if (!PlayerAppearance::IsValidDetail(my_bot->GetRace(), my_bot->GetGender(), uvalue)) - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; else my_bot->SetDrakkinDetails(uvalue); @@ -280,9 +280,9 @@ void bot_command_eyes(Client *c, const Seperator *sep) //else if (!arg2.compare("right")) // eye_bias = 2; - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (!PlayerAppearance::IsValidEyeColor(my_bot->GetRace(), my_bot->GetGender(), uvalue)) { - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; } else { //if (eye_bias == 1) { @@ -327,9 +327,9 @@ void bot_command_face(Client *c, const Seperator *sep) uint8 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (!PlayerAppearance::IsValidFace(my_bot->GetRace(), my_bot->GetGender(), uvalue)) { - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; } else { uint8 old_woad = 0; @@ -367,9 +367,9 @@ void bot_command_hair_color(Client *c, const Seperator *sep) uint8 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (!PlayerAppearance::IsValidHairColor(my_bot->GetRace(), my_bot->GetGender(), uvalue)) - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; else my_bot->SetHairColor(uvalue); @@ -402,9 +402,9 @@ void bot_command_hairstyle(Client *c, const Seperator *sep) uint8 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (!PlayerAppearance::IsValidHair(my_bot->GetRace(), my_bot->GetGender(), uvalue)) - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; else my_bot->SetHairStyle(uvalue); @@ -439,11 +439,11 @@ void bot_command_heritage(Client *c, const Seperator *sep) uint32 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (my_bot->GetRace() != DRAKKIN) - fail_type = BCEnum::AFT_Race; + fail_type = AFT_Race; else if (!PlayerAppearance::IsValidHeritage(my_bot->GetRace(), my_bot->GetGender(), uvalue)) - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; else my_bot->SetDrakkinHeritage(uvalue); @@ -478,11 +478,11 @@ void bot_command_tattoo(Client *c, const Seperator *sep) uint32 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (my_bot->GetRace() != DRAKKIN) - fail_type = BCEnum::AFT_Race; + fail_type = AFT_Race; else if (!PlayerAppearance::IsValidTattoo(my_bot->GetRace(), my_bot->GetGender(), uvalue)) - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; else my_bot->SetDrakkinTattoo(uvalue); @@ -515,12 +515,12 @@ void bot_command_woad(Client *c, const Seperator *sep) uint8 uvalue = Strings::ToInt(sep->arg[1]); - auto fail_type = BCEnum::AFT_None; + auto fail_type = AFT_None; if (my_bot->GetRace() != BARBARIAN) { - fail_type = BCEnum::AFT_Race; + fail_type = AFT_Race; } else if (!PlayerAppearance::IsValidWoad(my_bot->GetRace(), my_bot->GetGender(), uvalue)) { - fail_type = BCEnum::AFT_Value; + fail_type = AFT_Value; } else { uint8 old_face = (my_bot->GetLuclinFace() % 10); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 2d453c6f34..5d62102aaf 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -229,7 +229,7 @@ void bot_command_cast(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || (spell_type > BotSpellTypes::END && spell_type < BotSpellTypes::COMMANDED_START) || spell_type > BotSpellTypes::COMMANDED_END) { + if (!c->IsValidSpellType(spell_type)) { c->Message( Chat::Yellow, fmt::format( @@ -621,6 +621,10 @@ void bot_command_cast(Client* c, const Seperator* sep) tar ? tar->GetCleanName() : "your target" ).c_str() ); + + if (!aa_type && !by_spell_id) { + helper_send_usage_required_bots(c, spell_type); + } } else { c->Message( diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index eeceb6ce9d..e082e05f01 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -36,10 +36,15 @@ void bot_command_pull(Client *c, const Seperator *sep) sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - auto target_mob = ActionableTarget::VerifyEnemy(c, BCEnum::TT_Single); - if (!target_mob) { + auto target_mob = c->GetTarget(); + if ( + !target_mob || + target_mob == c || + !c->IsAttackAllowed(target_mob) + ) { c->Message(Chat::White, "Your current target is not attackable!"); + return; } diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 22d33957ef..69e1ce6721 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -122,4 +122,11 @@ struct BotBlockedBuffs_Struct { uint8_t blocked_pet; }; +struct BotSpellTypesByClass_Struct { + uint8_t min_level = 255; + std::string description; +}; + +using CommandedSpellTypesMinLevelMap = std::map>; + #endif // BOT_STRUCTS diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 78d6ed135f..9be4ad2823 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -2821,6 +2821,63 @@ BotSpell Bot::GetBestBotSpellForNukeByBodyType(Bot* caster, uint8 body_type, uin return result; } +BotSpell Bot::GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive); + + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsResurrectSpell(bot_spell_list_itr->SpellId) && + caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) + ) { + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; + + break; + } + } + } + + return result; +} + +BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_type) { + BotSpell result; + + result.SpellId = 0; + result.SpellIndex = 0; + result.ManaCost = 0; + + if (caster) { + std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm); + + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { + // Assuming all the spells have been loaded into this list by level and in descending order + if ( + IsCharmSpell(bot_spell_list_itr->SpellId) && + caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type) + ) { + result.SpellId = bot_spell_list_itr->SpellId; + result.SpellIndex = bot_spell_list_itr->SpellIndex; + result.ManaCost = bot_spell_list_itr->ManaCost; + + break; + } + } + } + + return result; +} + + void Bot::CheckBotSpells() { auto spell_list = BotSpellsEntriesRepository::All(content_db); uint16 spell_id; @@ -2902,13 +2959,13 @@ void Bot::CheckBotSpells() { correct_type = GetCorrectSpellType(s.type, spell_id); parent_type = GetParentSpellType(correct_type); - + if (RuleB(Bots, UseParentSpellTypeForChecks)) { if (s.type == parent_type || s.type == correct_type) { continue; } } - else { + else { if (IsPetBotSpellType(s.type)) { correct_type = GetPetSpellType(correct_type); } @@ -2917,7 +2974,7 @@ void Bot::CheckBotSpells() { if (correct_type == s.type) { continue; } - + if (correct_type == UINT16_MAX) { LogBotSpellTypeChecks("{} [#{}] is incorrect. It is currently set as {} [#{}] but the correct type is unknown." , GetSpellName(spell_id) @@ -2949,58 +3006,51 @@ void Bot::CheckBotSpells() { } } -BotSpell Bot::GetBestBotSpellForRez(Bot* caster, Mob* target, uint16 spell_type) { - BotSpell result; +void Bot::MapSpellTypeLevels() { + commanded_spells_min_level.clear(); - result.SpellId = 0; - result.SpellIndex = 0; - result.ManaCost = 0; + auto start = std::min({ BotSpellTypes::START, BotSpellTypes::COMMANDED_START, BotSpellTypes::DISCIPLINE_START }); + auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END }); - if (caster) { - std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Revive); - - for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsResurrectSpell(bot_spell_list_itr->SpellId) && - caster->CheckSpellRecastTimer(bot_spell_list_itr->SpellId) - ) { - result.SpellId = bot_spell_list_itr->SpellId; - result.SpellIndex = bot_spell_list_itr->SpellIndex; - result.ManaCost = bot_spell_list_itr->ManaCost; + for (int i = start; i <= end; ++i) { + if (!Bot::IsValidSpellType(i)) { + continue; + } - break; - } + for (int x = Class::Warrior; x <= Class::Berserker; ++x) { + commanded_spells_min_level[i][x] = { UINT8_MAX, "" }; } } - return result; -} - -BotSpell Bot::GetBestBotSpellForCharm(Bot* caster, Mob* target, uint16 spell_type) { - BotSpell result; - - result.SpellId = 0; - result.SpellIndex = 0; - result.ManaCost = 0; + auto spell_list = BotSpellsEntriesRepository::All(content_db); - if (caster) { - std::list bot_spell_list = GetBotSpellsForSpellEffect(caster, spell_type, SE_Charm); + for (const auto& s : spell_list) { + if (!IsValidSpell(s.spell_id)) { + LogBotSpellTypeChecks("{} is an invalid spell", s.spell_id); + continue; + } - for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { - // Assuming all the spells have been loaded into this list by level and in descending order - if ( - IsCharmSpell(bot_spell_list_itr->SpellId) && - caster->CastChecks(bot_spell_list_itr->SpellId, target, spell_type) - ) { - result.SpellId = bot_spell_list_itr->SpellId; - result.SpellIndex = bot_spell_list_itr->SpellIndex; - result.ManaCost = bot_spell_list_itr->ManaCost; + uint16_t spell_type = s.type; + int32_t bot_class = s.npc_spells_id - BOT_CLASS_BASE_ID_PREFIX; + uint8_t min_level = s.minlevel; - break; - } + if ( + !EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) || + !Bot::IsValidSpellType(spell_type) + ) { + continue; + } + + auto& spell_info = commanded_spells_min_level[spell_type][bot_class]; + + if (min_level < spell_info.min_level) { + spell_info.min_level = min_level; + spell_info.description = StringFormat( + "%s [#%u]: Level %u", + GetClassIDName(bot_class), + bot_class, + min_level + ); } } - - return result; } From bff1705ba224205a2f6aace73b56d3c0d1d589ee Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 11 Jan 2025 08:15:33 -0600 Subject: [PATCH 268/394] linux build fix --- zone/bot.h | 4 ++-- zone/bot_structs.h | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/zone/bot.h b/zone/bot.h index 6cd4079b78..bebef8d5df 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -952,7 +952,7 @@ class Bot : public NPC { void SetBotTimers(std::vector timers) { bot_timers = timers; } std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } - const CommandedSpellTypesMinLevelMap& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } + const std::map>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } uint32 GetLastZoneID() const { return _lastZoneId; } int32 GetBaseAC() const { return _baseAC; } int32 GetBaseATK() const { return _baseATK; } @@ -1082,7 +1082,7 @@ class Bot : public NPC { std::vector AIBot_spells_enforced; std::unordered_map> AIBot_spells_by_type; - CommandedSpellTypesMinLevelMap commanded_spells_min_level; + std::map> commanded_spells_min_level; std::vector bot_timers; std::vector bot_blocked_buffs; diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 69e1ce6721..a9d336a213 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -127,6 +127,4 @@ struct BotSpellTypesByClass_Struct { std::string description; }; -using CommandedSpellTypesMinLevelMap = std::map>; - #endif // BOT_STRUCTS From e792c97d67d66c0280a725d3d3a022b0851a1822 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 11 Jan 2025 16:19:33 -0600 Subject: [PATCH 269/394] remove completed todo --- zone/bot.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index dd7d7e9bce..976af64cdb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,8 +31,6 @@ /* TODO bot rewrite: ---have help option say first usable class level by spell list. Check existing options and update logic ---remove all hardcoded spell grabbing when above is done --add slotid option to invgive --command cleanup (move to new help window, make more descriptive) --Add quest methods for functions From 4134a30f59dc796b094663615e3e09e6bcfdec81 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:38:00 -0600 Subject: [PATCH 270/394] Allow inventory give to specific ID slots --- zone/bot.cpp | 24 ++++++++++++----------- zone/bot.h | 4 ++-- zone/bot_commands/inventory.cpp | 34 +++++++++++++++++++++++++++++---- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 976af64cdb..e2ffd9e5b4 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -4142,7 +4142,7 @@ bool Bot::AddBotToGroup(Bot* bot, Group* group) { } // Completes a trade with a client bot owner -void Bot::FinishTrade(Client* client, BotTradeType trade_type) +void Bot::FinishTrade(Client* client, BotTradeType trade_type, int16 chosen_slot) { if ( !client || @@ -4166,7 +4166,7 @@ void Bot::FinishTrade(Client* client, BotTradeType trade_type) else if (trade_type == BotTradeClientNoDropNoTrade) { // Items being traded are found on the Client's cursor slot, slot id 30. This item can be either a single item or it can be a bag. // If it is a bag, then we have to search for items in slots 331 thru 340 - PerformTradeWithClient(EQ::invslot::slotCursor, EQ::invslot::slotCursor, client); + PerformTradeWithClient(EQ::invslot::slotCursor, EQ::invslot::slotCursor, client, chosen_slot); // TODO: Add logic here to test if the item in SLOT_CURSOR is a container type, if it is then we need to call the following: // PerformTradeWithClient(331, 340, client); @@ -4174,7 +4174,7 @@ void Bot::FinishTrade(Client* client, BotTradeType trade_type) } // Perfoms the actual trade action with a client bot owner -void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client) +void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client, int16 chosen_slot) { using namespace EQ; @@ -4194,14 +4194,16 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* ClientReturn(ItemInstance* item, int16 from) : return_item_instance(item), from_bot_slot(from) { } }; - static const int16 bot_equip_order[invslot::EQUIPMENT_COUNT] = { - invslot::slotCharm, invslot::slotEar1, invslot::slotHead, invslot::slotFace, - invslot::slotEar2, invslot::slotNeck, invslot::slotShoulders, invslot::slotArms, - invslot::slotBack, invslot::slotWrist1, invslot::slotWrist2, invslot::slotRange, - invslot::slotHands, invslot::slotPrimary, invslot::slotSecondary, invslot::slotFinger1, - invslot::slotFinger2, invslot::slotChest, invslot::slotLegs, invslot::slotFeet, - invslot::slotWaist, invslot::slotPowerSource, invslot::slotAmmo - }; + std::vector bot_equip_order; + + if (chosen_slot != INVALID_INDEX) { + bot_equip_order.emplace_back(chosen_slot); + } + else { + for (int16 i = 0; i < invslot::EQUIPMENT_COUNT; ++i) { + bot_equip_order.push_back(i); + } + } enum { stageStackable = 0, stageEmpty, stageReplaceable }; diff --git a/zone/bot.h b/zone/bot.h index bebef8d5df..cb03f44038 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -281,7 +281,7 @@ class Bot : public NPC { void SetLevel(uint8 in_level, bool command = false) override; void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho) override; bool Process() override; - void FinishTrade(Client* client, BotTradeType trade_type); + void FinishTrade(Client* client, BotTradeType trade_type, int16 chosen_slot = INVALID_INDEX); bool Save() override; void Depop(); void CalcBotStats(bool showtext = true); @@ -1068,7 +1068,7 @@ class Bot : public NPC { void BotMeditate(bool is_sitting); bool CheckBotDoubleAttack(bool triple_attack = false); bool CheckTripleAttack(); - void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client); + void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client, int16 chosen_slot = INVALID_INDEX); bool AIDoSpellCast(int32 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = nullptr) override; BotCastingRoles& GetCastingRoles() { return m_CastingRoles; } diff --git a/zone/bot_commands/inventory.cpp b/zone/bot_commands/inventory.cpp index 48e5f48798..c9b2de3f03 100644 --- a/zone/bot_commands/inventory.cpp +++ b/zone/bot_commands/inventory.cpp @@ -25,7 +25,7 @@ void bot_command_inventory_give(Client* c, const Seperator* sep) c->Message( Chat::White, fmt::format( - "Usage: {} ([actionable: target | byname] ([actionable_name]))", + "Usage: {} ([actionable: target | byname] ([actionable_name])) [optional: slot ID]", sep->arg[0] ).c_str() ); @@ -33,19 +33,45 @@ void bot_command_inventory_give(Client* c, const Seperator* sep) } int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName); + int ab_arg = 1; + int slot_arg = 1; + int16 chosen_slot = INVALID_INDEX; + bool byname = false; + + std::string byname_arg = sep->arg[ab_arg]; + + if (!byname_arg.compare("byname")) { + byname = true; + slot_arg = ab_arg + 2; + } + + if (sep->IsNumber(slot_arg)) { + chosen_slot = atoi(sep->arg[slot_arg]); + + if (!byname) { + ++ab_arg; + } + + if (!EQ::ValueWithin(chosen_slot, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + c->Message(Chat::Yellow, "Please enter a valid inventory slot."); + + return; + } + } std::vector sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[1], sbl, ab_mask, sep->arg[2]) == ActionableBots::ABT_None) { + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[ab_arg + 1]) == ActionableBots::ABT_None) { return; } auto my_bot = sbl.front(); if (!my_bot) { - c->Message(Chat::White, "ActionableBots returned 'nullptr'"); + c->Message(Chat::Yellow, "ActionableBots returned 'nullptr'"); + return; } - my_bot->FinishTrade(c, Bot::BotTradeClientNoDropNoTrade); + my_bot->FinishTrade(c, Bot::BotTradeClientNoDropNoTrade, chosen_slot); } void bot_command_inventory_list(Client* c, const Seperator* sep) From 8dce4cd9bf5a3f40916652230791985c0dfb33ea Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:38:43 -0600 Subject: [PATCH 271/394] Update TODO --- zone/bot.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index e2ffd9e5b4..4251795780 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,7 +31,6 @@ /* TODO bot rewrite: ---add slotid option to invgive --command cleanup (move to new help window, make more descriptive) --Add quest methods for functions */ From 394d8e33921a8cda521765243279ef89a992461a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 12 Jan 2025 15:42:41 -0600 Subject: [PATCH 272/394] Correct slot ranges for inventorygive --- zone/bot_commands/inventory.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/zone/bot_commands/inventory.cpp b/zone/bot_commands/inventory.cpp index c9b2de3f03..9d8511cf9d 100644 --- a/zone/bot_commands/inventory.cpp +++ b/zone/bot_commands/inventory.cpp @@ -48,15 +48,15 @@ void bot_command_inventory_give(Client* c, const Seperator* sep) if (sep->IsNumber(slot_arg)) { chosen_slot = atoi(sep->arg[slot_arg]); - if (!byname) { - ++ab_arg; - } - - if (!EQ::ValueWithin(chosen_slot, EQ::invslot::POSSESSIONS_BEGIN, EQ::invslot::POSSESSIONS_END)) { + if (!EQ::ValueWithin(chosen_slot, EQ::invslot::EQUIPMENT_BEGIN, EQ::invslot::EQUIPMENT_END)) { c->Message(Chat::Yellow, "Please enter a valid inventory slot."); return; - } + } + + if (!byname) { + ++ab_arg; + } } std::vector sbl; From 7e05a6fa6c6f8800dd1d7db785eb49ef29926488 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 19 Jan 2025 14:46:16 -0600 Subject: [PATCH 273/394] Add zone specific spawn limits and zone specific forced spawn limits --- common/ruletypes.h | 4 ++++ zone/client_bot.cpp | 48 +++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 50 insertions(+), 2 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 60a9bfa0e3..99ff386a7a 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -895,6 +895,10 @@ RULE_INT(Bots, CampTimer, 25, "Number of seconds after /camp has begun before bo RULE_BOOL(Bots, SendClassRaceOnHelp, true, "If enabled a reminder of how to check class/race IDs will be sent when using compatible commands.") RULE_BOOL(Bots, AllowCrossGroupRaidAssist, true, "If enabled bots will autodefend group or raid members set as main assist.") RULE_BOOL(Bots, AllowBotBlockedBuffs, true, "If enabled, you can create blocked buffs for each bot and for their pets.") +RULE_STRING(Bots, ZonesWithSpawnLimits, "", "Comma-delimited list of zones where different bot spawn limits apply. This is the max a zone allows.") +RULE_STRING(Bots, ZoneSpawnLimits, "", "Comma-delimited list of spawn limits for zones.") +RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones where bot spawn limits are forced. This will take priority over any other type of spawn limits.") +RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 2a4c5c0a8c..7b0bfd7bb9 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -103,8 +103,6 @@ int Client::GetBotSpawnLimit(uint8 class_id) if (!bucket_value.empty() && Strings::IsNumber(bucket_value)) { bot_spawn_limit = Strings::ToInt(bucket_value); - - return bot_spawn_limit; } if (RuleB(Bots, QuestableSpawnLimit)) { @@ -124,6 +122,52 @@ int Client::GetBotSpawnLimit(uint8 class_id) bot_spawn_limit = Strings::ToInt(row[0]); } + const auto& zones_list = Strings::Split(RuleS(Bots, ZonesWithSpawnLimits), ","); + const auto& zones_limits_list = Strings::Split(RuleS(Bots, ZoneSpawnLimits), ","); + int i = 0; + + for (const auto& result : zones_list) { + try { + if ( + std::stoul(result) == zone->GetZoneID() && + std::stoul(zones_limits_list[i]) < bot_spawn_limit + ) { + bot_spawn_limit = std::stoul(zones_limits_list[i]); + + break; + } + + ++i; + } + + catch (const std::exception& e) { + LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what()); + } + } + + const auto& zones_forced_list = Strings::Split(RuleS(Bots, ZonesWithForcedSpawnLimits), ","); + const auto& zones_forced_limits_list = Strings::Split(RuleS(Bots, ZoneForcedSpawnLimits), ","); + i = 0; + + for (const auto& result : zones_forced_list) { + try { + if ( + std::stoul(result) == zone->GetZoneID() && + std::stoul(zones_forced_limits_list[i]) != bot_spawn_limit + ) { + bot_spawn_limit = std::stoul(zones_forced_limits_list[i]); + + break; + } + + ++i; + } + + catch (const std::exception& e) { + LogInfo("Invalid entry in Rule VegasScaling:SpecialScalingZones or SpecialScalingZonesVersions: [{}]", e.what()); + } + } + return bot_spawn_limit; } From 9e7c1d841a1ae4da591fae5f14be91822590ebc4 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 00:06:50 -0600 Subject: [PATCH 274/394] remove bd. from update queries where it doesn't exist --- common/database/database_update_manifest_bots.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 8da924e897..748ca5962e 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -190,11 +190,11 @@ JOIN rule_values rv WHERE rv.rule_name LIKE 'Bots:BotExpansionSettings' AND bd.expansion_bitmask != rv.rule_value; -INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; -INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = `bot_id`) AS stance_id, 1, 0, `show_helm`, 'BaseSetting', 'ShowHelm' FROM bot_data WHERE `show_helm` != 1; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = `bot_id`) AS stance_id, 2, 0, sqrt(`follow_distance`), 'BaseSetting', 'FollowDistance' FROM bot_data WHERE `follow_distance` != 184; INSERT INTO bot_settings -SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' +SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = `bot_id`) AS stance_id, 3, 0, `stop_melee_level`, 'BaseSetting', 'StopMeleeLevel' FROM ( SELECT `bot_id`, (CASE @@ -206,11 +206,11 @@ FROM ( ) AS `subquery` WHERE `sml` != `stop_melee_level`; -INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; -INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = `bot_id`) AS stance_id, 4, 0, `enforce_spell_settings`, 'BaseSetting', 'EnforceSpellSettings' FROM bot_data WHERE `enforce_spell_settings` != 0; +INSERT INTO bot_settings SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = `bot_id`) AS stance_id, 5, 0, `archery_setting`, 'BaseSetting', 'RangedSetting' FROM bot_data WHERE `archery_setting` != 0; INSERT INTO bot_settings -SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = bd.`bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'DistanceRanged' +SELECT 0, `bot_id`, (SELECT bs.`stance_id` FROM bot_stances bs WHERE bs.`bot_id` = `bot_id`) AS stance_id, 8, 0, `caster_range`, 'BaseSetting', 'DistanceRanged' FROM ( SELECT `bot_id`, (CASE From dff3af1193a0b05f7e586ca88989c01efd4f41d0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 10:44:22 -0600 Subject: [PATCH 275/394] Rename _spellSettings to m_bot_spell_settings --- zone/bot.cpp | 30 +++++++++++++++--------------- zone/bot.h | 14 +++++++------- zone/client.cpp | 4 ++-- zone/mob.cpp | 14 +++++++------- zone/mob.h | 14 +++++++------- 5 files changed, 38 insertions(+), 38 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4251795780..c7e93c6ab7 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10176,7 +10176,7 @@ int Bot::GetDefaultBotBaseSetting(uint16 bot_setting, uint8 stance) { void Bot::LoadDefaultBotSettings() { - _spellSettings.clear(); + m_bot_spell_settings.clear(); uint8 bot_stance = GetBotStance(); @@ -10207,7 +10207,7 @@ void Bot::LoadDefaultBotSettings() { t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); t.recastTimer.Start(); - _spellSettings.push_back(t); + m_bot_spell_settings.push_back(t); LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(bot_stance), bot_stance); LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, bot_stance), GetDefaultSpellDelay(i, bot_stance), GetDefaultSpellMinThreshold(i, bot_stance), GetDefaultSpellMaxThreshold(i, bot_stance)); @@ -10276,11 +10276,11 @@ BotSpell Bot::GetSpellByHealType(uint16 spell_type, Mob* tar) { uint16 Bot::GetSpellTypePriority(uint16 spell_type, uint8 priority_type) { switch (priority_type) { case BotPriorityCategories::Idle: - return _spellSettings[spell_type].idlePriority; + return m_bot_spell_settings[spell_type].idlePriority; case BotPriorityCategories::Engaged: - return _spellSettings[spell_type].engagedPriority; + return m_bot_spell_settings[spell_type].engagedPriority; case BotPriorityCategories::Pursue: - return _spellSettings[spell_type].pursuePriority; + return m_bot_spell_settings[spell_type].pursuePriority; default: return 0; } @@ -10736,13 +10736,13 @@ uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 sta void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority) { switch (priority_type) { case BotPriorityCategories::Idle: - _spellSettings[spell_type].idlePriority = priority; + m_bot_spell_settings[spell_type].idlePriority = priority; break; case BotPriorityCategories::Engaged: - _spellSettings[spell_type].engagedPriority = priority; + m_bot_spell_settings[spell_type].engagedPriority = priority; break; case BotPriorityCategories::Pursue: - _spellSettings[spell_type].pursuePriority = priority; + m_bot_spell_settings[spell_type].pursuePriority = priority; break; default: return; @@ -10750,31 +10750,31 @@ void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 pr } void Bot::SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit) { - _spellSettings[spell_type].resistLimit = resist_limit; + m_bot_spell_settings[spell_type].resistLimit = resist_limit; } void Bot::SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check) { - _spellSettings[spell_type].aggroCheck = aggro_check; + m_bot_spell_settings[spell_type].aggroCheck = aggro_check; } void Bot::SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit) { - _spellSettings[spell_type].minManaPct = mana_limit; + m_bot_spell_settings[spell_type].minManaPct = mana_limit; } void Bot::SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit) { - _spellSettings[spell_type].maxManaPct = mana_limit; + m_bot_spell_settings[spell_type].maxManaPct = mana_limit; } void Bot::SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit) { - _spellSettings[spell_type].minHPPct = hp_limit; + m_bot_spell_settings[spell_type].minHPPct = hp_limit; } void Bot::SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit) { - _spellSettings[spell_type].maxHPPct = hp_limit; + m_bot_spell_settings[spell_type].maxHPPct = hp_limit; } void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count) { - _spellSettings[spell_type].AEOrGroupTargetCount = target_count; + m_bot_spell_settings[spell_type].AEOrGroupTargetCount = target_count; } std::list Bot::GetSpellTypesPrioritized(uint8 priority_type) { diff --git a/zone/bot.h b/zone/bot.h index cb03f44038..1265ed5262 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -564,19 +564,19 @@ class Bot : public NPC { int GetSetting(uint16 setting_category, uint16 setting_type); uint16 GetSpellTypePriority(uint16 spell_type, uint8 priority_type); void SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority); - inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return _spellSettings[spell_type].resistLimit; } + inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].resistLimit; } void SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit); - inline bool GetSpellTypeAggroCheck(uint16 spell_type) const { return _spellSettings[spell_type].aggroCheck; } + inline bool GetSpellTypeAggroCheck(uint16 spell_type) const { return m_bot_spell_settings[spell_type].aggroCheck; } void SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check); - inline uint8 GetSpellTypeMinManaLimit(uint16 spell_type) const { return _spellSettings[spell_type].minManaPct; } - inline uint8 GetSpellTypeMaxManaLimit(uint16 spell_type) const { return _spellSettings[spell_type].maxManaPct; } + inline uint8 GetSpellTypeMinManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].minManaPct; } + inline uint8 GetSpellTypeMaxManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].maxManaPct; } void SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit); void SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit); - inline uint8 GetSpellTypeMinHPLimit(uint16 spell_type) const { return _spellSettings[spell_type].minHPPct; } - inline uint8 GetSpellTypeMaxHPLimit(uint16 spell_type) const { return _spellSettings[spell_type].maxHPPct; } + inline uint8 GetSpellTypeMinHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].minHPPct; } + inline uint8 GetSpellTypeMaxHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].maxHPPct; } void SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit); void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit); - inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return _spellSettings[spell_type].AEOrGroupTargetCount; } + inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return m_bot_spell_settings[spell_type].AEOrGroupTargetCount; } void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count); bool BotPassiveCheck(); diff --git a/zone/client.cpp b/zone/client.cpp index a3cad1cf2d..7fd23510a9 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13113,7 +13113,7 @@ void Client::ShowZoneShardMenu() } void Client::LoadDefaultBotSettings() { - _spellSettings.clear(); + m_bot_spell_settings.clear(); // Only illusion block supported currently SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); @@ -13130,7 +13130,7 @@ void Client::LoadDefaultBotSettings() { t.minThreshold = GetDefaultSpellMinThreshold(i); t.maxThreshold = GetDefaultSpellMaxThreshold(i); - _spellSettings.push_back(t); + m_bot_spell_settings.push_back(t); LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); diff --git a/zone/mob.cpp b/zone/mob.cpp index 3f8426d080..c625248927 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9206,34 +9206,34 @@ uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { } void Mob::SetSpellHold(uint16 spell_type, bool hold_status) { - _spellSettings[spell_type].hold = hold_status; + m_bot_spell_settings[spell_type].hold = hold_status; } void Mob::SetSpellDelay(uint16 spell_type, uint16 delay_value) { - _spellSettings[spell_type].delay = delay_value; + m_bot_spell_settings[spell_type].delay = delay_value; } void Mob::SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { - _spellSettings[spell_type].minThreshold = threshold_value; + m_bot_spell_settings[spell_type].minThreshold = threshold_value; } void Mob::SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { - _spellSettings[spell_type].maxThreshold = threshold_value; + m_bot_spell_settings[spell_type].maxThreshold = threshold_value; } void Mob::SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { - _spellSettings[spell_type].recastTimer.Start(recast_time); + m_bot_spell_settings[spell_type].recastTimer.Start(recast_time); } void Mob::StartBotSpellTimers() { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - _spellSettings[i].recastTimer.Start(); + m_bot_spell_settings[i].recastTimer.Start(); } } void Mob::DisableBotSpellTimers() { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - _spellSettings[i].recastTimer.Disable(); + m_bot_spell_settings[i].recastTimer.Disable(); } } diff --git a/zone/mob.h b/zone/mob.h index ae121c772e..5737b5ff86 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -230,7 +230,7 @@ class Mob : public Entity { // Bot attack flag Timer bot_attack_flag_timer; - std::vector _spellSettings; + std::vector m_bot_spell_settings; //Somewhat sorted: needs documenting! @@ -430,7 +430,7 @@ class Mob : public Entity { virtual bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); - inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient() ? true : _spellSettings[spellType].recastTimer.GetRemainingTime() > 0 ? false : true); } + inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient() ? true : m_bot_spell_settings[spellType].recastTimer.GetRemainingTime() > 0 ? false : true); } std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); @@ -453,16 +453,16 @@ class Mob : public Entity { uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); - inline bool GetSpellHold(uint16 spell_type) const { return _spellSettings[spell_type].hold; } + inline bool GetSpellHold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].hold; } void SetSpellHold(uint16 spell_type, bool hold_status); - inline uint16 GetSpellDelay(uint16 spell_type) const { return _spellSettings[spell_type].delay; } + inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } void SetSpellDelay(uint16 spell_type, uint16 delay_value); - inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return _spellSettings[spell_type].minThreshold; } + inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].minThreshold; } void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value); - inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return _spellSettings[spell_type].maxThreshold; } + inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].maxThreshold; } void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value); - inline uint16 GetSpellTypeRecastTimer(uint16 spell_type) { return _spellSettings[spell_type].recastTimer.GetRemainingTime(); } + inline uint16 GetSpellTypeRecastTimer(uint16 spell_type) { return m_bot_spell_settings[spell_type].recastTimer.GetRemainingTime(); } void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time); uint8 GetHPRatioForSpellType(uint16 spell_type, Mob* tar); From 32a885d1773a25bd4cdf2b26ecf7f9bd7c3313ab Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:44:21 -0600 Subject: [PATCH 276/394] Add IsPetOwnerOfClientBot(), add Lua and Perl methods --- zone/attack.cpp | 2 +- zone/lua_mob.cpp | 9 ++++++++- zone/lua_mob.h | 3 ++- zone/mob.h | 1 + zone/perl_mob.cpp | 6 ++++++ 5 files changed, 18 insertions(+), 3 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 41016be136..5dd9e521dd 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2641,7 +2641,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy } } - if (give_exp && give_exp->IsTempPet() && (give_exp->IsPetOwnerClient() || give_exp->IsPetOwnerBot())) { + if (give_exp && give_exp->IsTempPet() && give_exp->IsPetOwnerOfClientBot()) { if (give_exp->IsNPC() && give_exp->CastToNPC()->GetSwarmOwner()) { Mob* temp_owner = entity_list.GetMobID(give_exp->CastToNPC()->GetSwarmOwner()); if (temp_owner) { diff --git a/zone/lua_mob.cpp b/zone/lua_mob.cpp index 0e644f4f2f..27ab7bbd86 100644 --- a/zone/lua_mob.cpp +++ b/zone/lua_mob.cpp @@ -3268,6 +3268,12 @@ bool Lua_Mob::IsPetOwnerNPC() return self->IsPetOwnerNPC(); } +bool Lua_Mob::IsPetOwnerOfClientBot() +{ + Lua_Safe_Call_Bool(); + return self->IsPetOwnerOfClientBot(); +} + bool Lua_Mob::IsDestructibleObject() { Lua_Safe_Call_Bool(); @@ -3902,8 +3908,9 @@ luabind::scope lua_register_mob() { .def("IsPausedTimer", &Lua_Mob::IsPausedTimer) .def("IsPet", (bool(Lua_Mob::*)(void))&Lua_Mob::IsPet) .def("IsPetOwnerBot", &Lua_Mob::IsPetOwnerBot) - .def("IsPetOwnerClient", &Lua_Mob::IsPetOwnerClient) + .def("IsPetOwnerClient", &Lua_Mob::IsPetOwnerClient) .def("IsPetOwnerNPC", &Lua_Mob::IsPetOwnerNPC) + .def("IsPetOwnerOfClientBot", &Lua_Mob::IsPetOwnerOfClientBot) .def("IsPureMeleeClass", &Lua_Mob::IsPureMeleeClass) .def("IsRoamer", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRoamer) .def("IsRooted", (bool(Lua_Mob::*)(void))&Lua_Mob::IsRooted) diff --git a/zone/lua_mob.h b/zone/lua_mob.h index dfe646cc01..0f3d4f6c4e 100644 --- a/zone/lua_mob.h +++ b/zone/lua_mob.h @@ -574,8 +574,9 @@ class Lua_Mob : public Lua_Entity bool IsFamiliar(); bool IsTargetLockPet(); bool IsPetOwnerBot(); - bool IsPetOwnerClient(); + bool IsPetOwnerClient(); bool IsPetOwnerNPC(); + bool IsPetOwnerOfClientBot(); bool IsDestructibleObject(); bool IsBoat(); bool IsControllableBoat(); diff --git a/zone/mob.h b/zone/mob.h index 5737b5ff86..b092e3f547 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1179,6 +1179,7 @@ class Mob : public Entity { inline void SetPetOwnerClient(bool b) { pet_owner_client = b; } inline bool IsPetOwnerNPC() const { return pet_owner_npc; } inline void SetPetOwnerNPC(bool b) { pet_owner_npc = b; } + inline bool IsPetOwnerOfClientBot() const { return pet_owner_bot || pet_owner_client; } inline bool IsTempPet() const { return _IsTempPet; } inline void SetTempPet(bool value) { _IsTempPet = value; } inline bool IsHorse() { return is_horse; } diff --git a/zone/perl_mob.cpp b/zone/perl_mob.cpp index a4042dc4b8..af564d3b7b 100644 --- a/zone/perl_mob.cpp +++ b/zone/perl_mob.cpp @@ -3400,6 +3400,11 @@ bool Perl_Mob_IsPetOwnerNPC(Mob* self) return self->IsPetOwnerNPC(); } +bool Perl_Mob_IsPetOwnerOfClientBot(Mob* self) +{ + return self->IsPetOwnerOfClientBot(); +} + bool Perl_Mob_IsDestructibleObject(Mob* self) { return self->IsDestructibleObject(); @@ -3999,6 +4004,7 @@ void perl_register_mob() package.add("IsPetOwnerBot", &Perl_Mob_IsPetOwnerBot); package.add("IsPetOwnerClient", &Perl_Mob_IsPetOwnerClient); package.add("IsPetOwnerNPC", &Perl_Mob_IsPetOwnerNPC); + package.add("IsPetOwnerOfClientBot", &Perl_Mob_IsPetOwnerOfClientBot); package.add("IsPlayerCorpse", &Perl_Mob_IsPlayerCorpse); package.add("IsPureMeleeClass", &Perl_Mob_IsPureMeleeClass); package.add("IsRoamer", &Perl_Mob_IsRoamer); From 744a1d23c030e4fce0a80b4e02d040f54b6d44cf Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 11:56:27 -0600 Subject: [PATCH 277/394] Make botOwnerCharacterID snakecase --- zone/bot.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c7e93c6ab7..4ec245000f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7410,17 +7410,17 @@ void EntityList::AddBot(Bot *new_bot, bool send_spawn_packet, bool dont_queue) { } } -std::vector EntityList::GetBotsByBotOwnerCharacterID(uint32 botOwnerCharacterID) { +std::vector EntityList::GetBotsByBotOwnerCharacterID(uint32 bot_owner_character_id) { std::vector client_bot_list; - if (botOwnerCharacterID <= 0) { + if (bot_owner_character_id <= 0) { return client_bot_list; } auto it = bot_list.begin(); while (it != bot_list.end()) { - if (it->second->GetOwner() && it->second->GetBotOwnerCharacterID() == botOwnerCharacterID) { + if (it->second->GetOwner() && it->second->GetBotOwnerCharacterID() == bot_owner_character_id) { client_bot_list.push_back(it->second); } ++it; From 28fbdc68777058b09e2d55601dd4987bb994adda Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 12:09:30 -0600 Subject: [PATCH 278/394] Throw bot_camp_timer behind Bots:Enabled rule --- zone/client_packet.cpp | 7 ++++++- zone/client_process.cpp | 6 ++++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 3dfb24fa27..6cfb9b174e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -4292,8 +4292,13 @@ void Client::Handle_OP_Camp(const EQApplicationPacket *app) OnDisconnect(true); return; } + camp_timer.Start(29000, true); - bot_camp_timer.Start((RuleI(Bots, CampTimer) * 1000), true); + + if (RuleB(Bots, Enabled)) { + bot_camp_timer.Start((RuleI(Bots, CampTimer) * 1000), true); + } + return; } diff --git a/zone/client_process.cpp b/zone/client_process.cpp index 83f638a2fd..6326efeec0 100644 --- a/zone/client_process.cpp +++ b/zone/client_process.cpp @@ -193,8 +193,10 @@ bool Client::Process() { return false; //delete client } - if (bot_camp_timer.Check()) { - CampAllBots(); + if (RuleB(Bots, Enabled)) { + if (bot_camp_timer.Check()) { + CampAllBots(); + } } if (camp_timer.Check()) { From 3040d3bc97acaea386c3332ebf553d5e9a4d8953 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:03:58 -0600 Subject: [PATCH 279/394] Move various Bot<>Checks logging to BotSpellChecks --- common/eqemu_logsys.h | 5 +- common/eqemu_logsys_log_aliases.h | 42 ++--------- zone/bot.cpp | 112 +++++++++++++++--------------- zone/botspellsai.cpp | 14 ++-- 4 files changed, 70 insertions(+), 103 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 7e39f3c447..7d960aed7b 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -144,10 +144,7 @@ namespace Logs { XTargets, EvolveItem, BotSettings, - BotPreChecks, - BotHoldChecks, - BotDelayChecks, - BotThresholdChecks, + BotSpellChecks, BotSpellTypeChecks, TestDebug, MaxCategoryID /* Don't Remove this */ diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 616ee203a0..bee8a8f1e4 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -864,44 +864,14 @@ OutF(LogSys, Logs::Detail, Logs::BotSettings, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) -#define LogBotPreChecks(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::General, Logs::BotPreChecks))\ - OutF(LogSys, Logs::General, Logs::BotPreChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +#define LogBotSpellChecks(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::General, Logs::BotSpellChecks))\ + OutF(LogSys, Logs::General, Logs::BotSpellChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) -#define LogBotPreChecksDetail(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotPreChecks))\ - OutF(LogSys, Logs::Detail, Logs::BotPreChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - -#define LogBotHoldChecks(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::General, Logs::BotHoldChecks))\ - OutF(LogSys, Logs::General, Logs::BotHoldChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - -#define LogBotHoldChecksDetail(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotHoldChecks))\ - OutF(LogSys, Logs::Detail, Logs::BotHoldChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - -#define LogBotDelayChecks(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::General, Logs::BotDelayChecks))\ - OutF(LogSys, Logs::General, Logs::BotDelayChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - -#define LogBotDelayChecksDetail(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotDelayChecks))\ - OutF(LogSys, Logs::Detail, Logs::BotDelayChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - -#define LogBotThresholdChecks(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::General, Logs::BotThresholdChecks))\ - OutF(LogSys, Logs::General, Logs::BotThresholdChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - -#define LogBotThresholdChecksDetail(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotThresholdChecks))\ - OutF(LogSys, Logs::Detail, Logs::BotThresholdChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ +#define LogBotSpellChecksDetail(message, ...) do {\ + if (LogSys.IsLogEnabled(Logs::Detail, Logs::BotSpellChecks))\ + OutF(LogSys, Logs::Detail, Logs::BotSpellChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) #define LogBotSpellTypeChecks(message, ...) do {\ diff --git a/zone/bot.cpp b/zone/bot.cpp index 4ec245000f..87b765862c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9318,14 +9318,14 @@ uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][St bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { if (!tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast due to PrecastChecks !tar.'", GetCleanName()); return false; } - LogBotPreChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); if (GetUltimateSpellHold(spell_type, tar)) { - LogBotHoldChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9334,17 +9334,17 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { } if (GetManaRatio() < GetSpellTypeMinManaLimit(spell_type) || GetManaRatio() > GetSpellTypeMaxManaLimit(spell_type)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } if (GetHPRatio() < GetSpellTypeMinHPLimit(spell_type) || GetHPRatio() > GetSpellTypeMaxHPLimit(spell_type)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } if (!GetUltimateSpellDelayCheck(spell_type, tar)) { - LogBotDelayChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9354,7 +9354,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { return true; default: if (GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellMinThreshold(spell_type, tar) || GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellMaxThreshold(spell_type, tar)) { - LogBotThresholdChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } } @@ -9365,7 +9365,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks, bool ae_check) { if (prechecks) { if (!tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } @@ -9379,57 +9379,57 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck } if (!PrecastChecks(tar, spell_type)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast due to !PrecastChecks.'", GetCleanName()); return false; } } - LogBotPreChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), (tar ? tar->GetCleanName() : "nobody")); + LogBotSpellChecksDetail("{} says, 'Running [{}] CastChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), (tar ? tar->GetCleanName() : "nobody")); if (!IsValidSpell(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast due to !IsValidSpell.'", GetCleanName()); return false; } if (IsFeared() || IsSilenced() || IsAmnesiad()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } if (!CheckSpellRecastTimer(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !CheckSpellRecastTimer.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (!BotHasEnoughMana(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !BotHasEnoughMana.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to IsSpellBlocked.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { if (IsBeneficialSpell(spell_id)) { if (IsEngaged()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); return false; } } @@ -9437,7 +9437,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { if (IsBeneficialSpell(spell_id)) { if (!IsEngaged()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); return false; } } @@ -9447,33 +9447,33 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck int chance = GetFocusEffect(focusFcMute, spell_id); if (chance && zone->random.Roll(chance)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); return false; } } if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (SpellTypeRequiresTarget(spell_type) && !tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } @@ -9482,32 +9482,32 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck && tar != this && (spell_type != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (this == tar && IsSacrificeSpell(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); return false; } if (tar->GetSpecialAbility(SpecialAbility::MagicImmunity)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to MagicImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (tar->IsImmuneToBotSpell(spell_id, this)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (!tar->CheckSpellLevelRestriction(this, spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to CheckSpellLevelRestriction.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9519,7 +9519,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck ) ) { if (tar->IsBlockedBuff(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } } @@ -9532,11 +9532,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck ) ) { if (tar->GetOwner()->IsBlockedPetBuff(spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsBlockedPetBuff.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } } - //LogBotPreChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + //LogBotSpellChecksDetail("{} says, 'Doing CanCastSpellType checks of {} on {}.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); if (!CanCastSpellType(spell_type, spell_id, tar)) { return false; } @@ -9546,7 +9546,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck } if (!IsValidTargetType(spell_id, GetSpellTargetType(spell_id), tar->GetBodyType())) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidTargetType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9555,7 +9555,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck && tar->CanBuffStack(spell_id, GetLevel(), true) < 0 ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanBuffStack.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9564,7 +9564,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck } if (!ae_check && !IsValidSpellRange(spell_id, tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9573,17 +9573,17 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck } if (!IsTaunting() && GetSpellTypeAggroCheck(spell_type) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (!DoResistCheckBySpellType(tar, spell_id, spell_type)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to DoResistCheckBySpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9592,7 +9592,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { if (!spell_id || !tar) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spell_type ? GetSpellTypeNameByID(spell_type) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to failsafe checks.'", GetCleanName(), (spell_id ? GetSpellName(spell_id) : (spell_type ? GetSpellTypeNameByID(spell_type) : "Unknown")), (tar ? tar->GetCleanName() : "Unknown")); return false; } @@ -9619,27 +9619,27 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: if (tar == this && spells[spell_id].target_type == ST_TargetsTarget) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); return false; } if ((spell_type != BotSpellTypes::Teleport && spell_type != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spell_id, SE_Illusion)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if (spells[spell_id].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } if ((IsGroupSpell(spell_id) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9653,7 +9653,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9663,7 +9663,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9682,7 +9682,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { for (unsigned int j = 0; j < buff_count; j++) { if (IsValidSpell(tar->GetBuffs()[j].spellid)) { if (IsLichSpell(tar->GetBuffs()[j].spellid)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsLichSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } } @@ -9705,7 +9705,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) ) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9715,7 +9715,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) ) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } break; @@ -9729,7 +9729,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { case BotSpellTypes::AELull: case BotSpellTypes::Lull: if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9738,7 +9738,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { break; } - //LogBotPreChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + //LogBotSpellChecksDetail("{} says, {} on {} passed CanCastSpellType.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return true; } @@ -9837,7 +9837,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spell_id, int32 resist_limit) { default: return true; } - //LogBotPreChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), target_resist, level_mod, resist_difficulty, (target_resist + level_mod - resist_difficulty), resist_limit); + //LogBotSpellChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), target_resist, level_mod, resist_difficulty, (target_resist + level_mod - resist_difficulty), resist_limit); if ((target_resist + level_mod - resist_difficulty) > resist_limit) { return false; } @@ -10824,7 +10824,7 @@ bool Bot::AttemptAICastSpell(uint16 spell_type, Mob* tar) { } if (!IsTaunting() && !IsCommandedSpell() && GetSpellTypeAggroCheck(spell_type) && HasOrMayGetAggro(IsSitting())) { - LogBotPreChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spell_type)); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] due to GetSpellTypeAggroCheck and HasOrMayGetAggro.'", GetCleanName(), GetSpellTypeNameByID(spell_type)); return result; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 9be4ad2823..bea80d337b 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -26,7 +26,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ return false; } - LogBotPreChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Attempting {} AICastSpell on {}.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); if ( !AI_HasSpells() || @@ -623,7 +623,7 @@ bool Bot::AI_PursueCastCheck() { if (GetTarget() && AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Pursue autocast check triggered: [{}]", GetCleanName()); - LogBotPreChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'AI_PursueCastCheck started.'", GetCleanName()); AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -636,7 +636,7 @@ bool Bot::AI_PursueCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); + LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -681,7 +681,7 @@ bool Bot::AI_IdleCastCheck() { if (AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Non-Engaged autocast check triggered: [{}]", GetCleanName()); - LogBotPreChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'AI_IdleCastCheck started.'", GetCleanName()); AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -704,7 +704,7 @@ bool Bot::AI_IdleCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); + LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -759,7 +759,7 @@ bool Bot::AI_EngagedCastCheck() { if (GetTarget() && AIautocastspell_timer->Check(false)) { LogAIDetail("Bot Engaged autocast check triggered: [{}]", GetCleanName()); - LogBotPreChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); + LogBotSpellChecksDetail("{} says, 'AI_EngagedCastCheck started.'", GetCleanName()); AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting. @@ -772,7 +772,7 @@ bool Bot::AI_EngagedCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { - LogBotPreChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); + LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } From 17284ea9560ff224dd72c2203e5d88d4ce045cb5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:05:35 -0600 Subject: [PATCH 280/394] Remove from LogCategoryName --- common/eqemu_logsys.h | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 7d960aed7b..c157e2d3b9 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -250,11 +250,7 @@ namespace Logs { "XTargets", "EvolveItem" "Bot Settings", - "Bot Pre Checks", - "Bot Hold Checks", - "Bot Delay Checks", - "Bot Threshold Checks", - "Bot Spell Type Checks", + "Bot Spell Checks", "Test Debug" }; } From f296ff5a501ce71b6f4f8c5789cb0431a6a2db2a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 13:52:28 -0600 Subject: [PATCH 281/394] Consolidate IsInGroupOrRaid --- zone/mob.cpp | 51 ++++++++++++--------------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index c625248927..134e51e450 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -9419,7 +9419,7 @@ void Mob::ClearDataBucketCache() } } -bool Mob::IsInGroupOrRaid(Mob *other, bool same_raid_group) { +bool Mob::IsInGroupOrRaid(Mob* other, bool same_raid_group) { if (!other || !IsOfClientBotMerc() || !other->IsOfClientBotMerc()) { return false; } @@ -9428,64 +9428,37 @@ bool Mob::IsInGroupOrRaid(Mob *other, bool same_raid_group) { return true; } - Raid* raid = nullptr; - - if (IsBot()) { - raid = CastToBot()->GetStoredRaid(); - } - else { - if (IsRaidGrouped()) { - raid = GetRaid(); - } - } - - if (IsRaidGrouped()) { - if (!raid) { - return false; - } - } + Raid* raid = IsBot() ? CastToBot()->GetStoredRaid() : (IsRaidGrouped() ? GetRaid() : nullptr); if (raid) { if (!other->IsRaidGrouped()) { return false; } - Raid* other_raid = nullptr; - - if (other->IsBot()) { - other_raid = other->CastToBot()->GetStoredRaid(); - } - else { - other_raid = other->GetRaid(); - } + Raid* other_raid = other->IsBot() ? other->CastToBot()->GetStoredRaid() : other->GetRaid(); if (!other_raid) { return false; } auto raid_group = raid->GetGroup(GetCleanName()); - auto raid_other_group = other_raid->GetGroup(other->GetCleanName()); + auto other_raid_group = other_raid->GetGroup(other->GetCleanName()); - if (raid_group == RAID_GROUPLESS || raid_other_group == RAID_GROUPLESS || (same_raid_group && raid_group != raid_other_group)) { + if ( + raid_group == RAID_GROUPLESS || + other_raid_group == RAID_GROUPLESS || + (same_raid_group && raid_group != other_raid_group) + ) { return false; } return true; } - else { - auto* group = GetGroup(); - auto* group_other = other->GetGroup(); - if (group) { - if (!group_other || group != group_other) { - return false; - } - - return true; - } - } + Group* group = GetGroup(); + Group* other_group = other->GetGroup(); - return false; + return group && group == other_group; } bool Mob::DoLosChecks(Mob* who, Mob* other) { From 1c2293e1aa043708c9f0a51d31ee910da1a3c10c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 14:33:01 -0600 Subject: [PATCH 282/394] Consolidate GatherSpellTargets --- zone/mob.cpp | 37 +++++++++++-------------------------- 1 file changed, 11 insertions(+), 26 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 134e51e450..0cf06fbbc7 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8693,43 +8693,28 @@ void Mob::CheckScanCloseMobsMovingTimer() std::vector Mob::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots, bool no_pets) { std::vector valid_spell_targets; - if (IsRaidGrouped()) { - Raid* raid = nullptr; + auto is_valid_target = [no_clients, no_bots](Mob* member) { + return member && + ((member->IsClient() && !no_clients) || (member->IsBot() && !no_bots)); + }; - if (IsBot()) { - raid = CastToBot()->GetStoredRaid(); - } - else { - raid = GetRaid(); - } + if (IsRaidGrouped()) { + Raid* raid = IsBot() ? CastToBot()->GetStoredRaid() : GetRaid(); if (raid) { if (entire_raid) { for (const auto& m : raid->members) { - if (m.member && m.group_number != RAID_GROUPLESS && ((m.member->IsClient() && !no_clients) || (m.member->IsBot() && !no_bots))) { + if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { valid_spell_targets.emplace_back(m.member); } } } else { - std::vector raid_group; - - if (target) { - raid_group = raid->GetRaidGroupMembers(raid->GetGroup(target->GetName())); - } - else { - raid_group = raid->GetRaidGroupMembers(raid->GetGroup(GetName())); - } + auto group_name = target ? raid->GetGroup(target->GetName()) : raid->GetGroup(GetName()); + auto raid_group = raid->GetRaidGroupMembers(group_name); for (const auto& m : raid_group) { - if ( - m.member && - m.group_number != RAID_GROUPLESS && - ( - (m.member->IsClient() && !no_clients) || - (m.member->IsBot() && !no_bots) - ) - ) { + if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { valid_spell_targets.emplace_back(m.member); } } @@ -8741,7 +8726,7 @@ std::vector Mob::GatherSpellTargets(bool entire_raid, Mob* target, bool no if (group) { for (const auto& m : group->members) { - if (m && ((m->IsClient() && !no_clients) || (m->IsBot() && !no_bots))) { + if (is_valid_target(m)) { valid_spell_targets.emplace_back(m); } } From 65f3f9081955399155ea01337e38f9c4b7f39893 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 15:59:16 -0600 Subject: [PATCH 283/394] Add missing Bot Spell Type Checks to log --- common/eqemu_logsys.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index c157e2d3b9..c6bc26d141 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -248,9 +248,10 @@ namespace Logs { "EqTime", "Corpses", "XTargets", - "EvolveItem" + "EvolveItem", "Bot Settings", "Bot Spell Checks", + "Bot Spell Type Checks", "Test Debug" }; } From 5de72999fe97dc5eb42f88a3e04ad4618f600782 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:29:57 -0600 Subject: [PATCH 284/394] Add GetParentSpellType when checking spelltypes for idle, engaged, pursue CastChecks. --- zone/botspellsai.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index bea80d337b..53d2511357 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -648,7 +648,7 @@ bool Bot::AI_PursueCastCheck() { continue; } - if (AIBot_spells_by_type[current_cast.spellType].empty()) { + if (AIBot_spells_by_type[current_cast.spellType].empty() && AIBot_spells_by_type[GetParentSpellType(current_cast.spellType)].empty()) { continue; } @@ -724,7 +724,7 @@ bool Bot::AI_IdleCastCheck() { continue; } - if (AIBot_spells_by_type[current_cast.spellType].empty()) { + if (AIBot_spells_by_type[current_cast.spellType].empty() && AIBot_spells_by_type[GetParentSpellType(current_cast.spellType)].empty()) { continue; } @@ -784,7 +784,7 @@ bool Bot::AI_EngagedCastCheck() { continue; } - if (AIBot_spells_by_type[current_cast.spellType].empty()) { + if (AIBot_spells_by_type[current_cast.spellType].empty() && AIBot_spells_by_type[GetParentSpellType(current_cast.spellType)].empty()) { continue; } From 46a45679b2fffa71b7f971b511fdf8f74291eadc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 19:30:26 -0600 Subject: [PATCH 285/394] Consolidate AttemptForcedCastSpell --- zone/bot.cpp | 119 +++++++++++++++------------------------------------ 1 file changed, 35 insertions(+), 84 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 87b765862c..0092248473 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10954,7 +10954,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { tar = this; } - if ((IsCharmSpell(spell_id) || IsPetSpell(spell_id) && HasPet())) { + if ((IsCharmSpell(spell_id) || (IsPetSpell(spell_id) && HasPet()))) { return false; } @@ -10963,19 +10963,16 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { } if (IsBeneficialSpell(spell_id)) { - if ( + bool invalid_beneficial_target = (tar->IsNPC() && !tar->GetOwner()) || (tar->GetOwner() && tar->GetOwner()->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar->GetOwner())) || - (tar->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar)) - ) { + (tar->IsOfClientBot() && !GetBotOwner()->IsInGroupOrRaid(tar)); + + if (invalid_beneficial_target) { GetBotOwner()->Message( - Chat::Yellow, - fmt::format( - "[{}] is an invalid target. Only players or their pet in your group or raid are eligible targets." - , tar->GetCleanName() - ).c_str() + Chat::Yellow, + fmt::format("[{}] is an invalid target. Only players or their pet in your group or raid are eligible targets.", tar->GetCleanName()).c_str() ); - return false; } } @@ -10983,32 +10980,17 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { if (IsDetrimentalSpell(spell_id) && (!GetBotOwner()->IsAttackAllowed(tar) || !IsAttackAllowed(tar))) { GetBotOwner()->Message( Chat::Yellow, - fmt::format( - "{} says, 'I cannot attack [{}]'.", - GetCleanName(), - tar->GetCleanName() - ).c_str() + fmt::format("{} says, 'I cannot attack [{}]'.", GetCleanName(), tar->GetCleanName()).c_str() ); - return false; } - if (!is_disc) { - if (!CheckSpellRecastTimer(spell_id)) { - return false; - } + if (!is_disc && !CheckSpellRecastTimer(spell_id)) { + return false; } - if ( - !IsInGroupOrRaid(tar, true) && - ( - !RuleB(Bots, EnableBotTGB) || - ( - IsGroupSpell(spell_id) && - !IsTGBCompatibleSpell(spell_id) - ) - ) - ) { + if (!IsInGroupOrRaid(tar, true) && + (!RuleB(Bots, EnableBotTGB) || (IsGroupSpell(spell_id) && !IsTGBCompatibleSpell(spell_id)))) { return false; } @@ -11018,13 +11000,9 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { if (!CastChecks(spell_id, tar, UINT16_MAX)) { GetBotOwner()->Message( - Chat::Red, - fmt::format( - "{} says, 'Ability failed to cast. This could be due to this to any number of things: range, mana, immune, target type, etc.'", - GetBotOwner()->GetCleanName() - ).c_str() + Chat::Red, + fmt::format("{} says, 'Ability failed to cast. This could be due to range, mana, immune, target type, etc.'", GetBotOwner()->GetCleanName()).c_str() ); - return false; } @@ -11038,62 +11016,35 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { tar->GetCleanName() ).c_str() ); - InterruptSpell(); } - if (CastSpell(spell_id, tar->GetID())) { - BotGroupSay( - this, - fmt::format( - "Casting {} on {}.", - GetSpellName(spell_id), - (tar == this ? "myself" : tar->GetCleanName()) - ).c_str() - ); - - int timer_duration = 0; - - if (!is_disc) { - timer_duration = CalcBuffDuration(tar, this, spell_id); - - if (timer_duration) { - timer_duration = GetActSpellDuration(spell_id, timer_duration); - } - - if (timer_duration < 0) { - timer_duration = 0; - } - } - else { - if (spell.recast_time > 0) { - timer_duration = spell.recast_time / 1000; - auto focus = GetFocusEffect(focusReduceRecastTime, spell_id); - - if (focus > timer_duration) { - timer_duration = 0; - } - else { - timer_duration -= focus; - } - } + if (!CastSpell(spell_id, tar->GetID())) { + return false; + } - if (timer_duration > 0) { - timer_duration = timer_duration * 1000; - } - } + BotGroupSay( + this, + fmt::format( + "Casting {} on {}.", + GetSpellName(spell_id), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); - if (!is_disc) { - SetSpellRecastTimer(spell_id, timer_duration); - } - else { - SetDisciplineReuseTimer(spell_id, timer_duration); - } + int timer_duration = 0; - return true; + if (!is_disc) { + timer_duration = CalcBuffDuration(tar, this, spell_id); + timer_duration = std::max(0, GetActSpellDuration(spell_id, timer_duration)); + SetSpellRecastTimer(spell_id, timer_duration); + } + else if (spell.recast_time > 0) { + timer_duration = std::max(0, int(spell.recast_time / 1000 - GetFocusEffect(focusReduceRecastTime, spell_id))); + SetDisciplineReuseTimer(spell_id, timer_duration * 1000); } - return false; + return true; } uint16 Bot::GetParentSpellType(uint16 spell_type) { From 3a25d510956c6811e36d9329676a205288d1ba39 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 20:17:38 -0600 Subject: [PATCH 286/394] Consolidate SetBotBlockedBuff/SetBotBlockedPetBuff --- zone/bot.cpp | 73 +++++++++++++--------------------------------------- 1 file changed, 18 insertions(+), 55 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0092248473..45f2d899fb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11975,42 +11975,23 @@ uint16 Bot::GetSpellByAA(int id, AA::Rank*& rank) { return spell_id; } -void Bot::SetBotBlockedBuff(uint16 spell_id, bool block) { +void Bot::SetBotBlockedBuff(uint16 spell_id, bool block) +{ if (!IsValidSpell(spell_id)) { OwnerMessage("Failed to set blocked buff."); - return; } - if (!bot_blocked_buffs.empty()) { - bool found = false; - - for (int i = 0; i < bot_blocked_buffs.size(); i++) { - if (bot_blocked_buffs[i].spell_id != spell_id) { - continue; - } - - bot_blocked_buffs[i].blocked = block; - found = true; - } - - if (!found) { - BotBlockedBuffs_Struct t; - - t.spell_id = spell_id; - t.blocked = block; + auto it = std::find_if( + bot_blocked_buffs.begin(), bot_blocked_buffs.end(), + [spell_id](const BotBlockedBuffs_Struct& buff) { return buff.spell_id == spell_id; } + ); - bot_blocked_buffs.push_back(t); - } + if (it != bot_blocked_buffs.end()) { + it->blocked = block; } else { - - BotBlockedBuffs_Struct t; - - t.spell_id = spell_id; - t.blocked = block; - - bot_blocked_buffs.push_back(t); + bot_blocked_buffs.push_back({ GetBotID(), spell_id, block }); } CleanBotBlockedBuffs(); @@ -12041,41 +12022,23 @@ bool Bot::IsBlockedBuff(int32 spell_id) return result; } -void Bot::SetBotBlockedPetBuff(uint16 spell_id, bool block) { +void Bot::SetBotBlockedPetBuff(uint16 spell_id, bool block) +{ if (!IsValidSpell(spell_id)) { OwnerMessage("Failed to set blocked pet buff."); - return; } - if (!bot_blocked_buffs.empty()) { - bool found = false; - - for (int i = 0; i < bot_blocked_buffs.size(); i++) { - if (bot_blocked_buffs[i].spell_id != spell_id) { - continue; - } - - bot_blocked_buffs[i].blocked_pet = block; - found = true; - } - - if (!found) { - BotBlockedBuffs_Struct t; - - t.spell_id = spell_id; - t.blocked_pet = block; + auto it = std::find_if( + bot_blocked_buffs.begin(), bot_blocked_buffs.end(), + [spell_id](const BotBlockedBuffs_Struct& buff) { return buff.spell_id == spell_id; } + ); - bot_blocked_buffs.push_back(t); - } + if (it != bot_blocked_buffs.end()) { + it->blocked_pet = block; } else { - BotBlockedBuffs_Struct t; - - t.spell_id = spell_id; - t.blocked_pet = block; - - bot_blocked_buffs.push_back(t); + bot_blocked_buffs.push_back({ GetBotID(), spell_id, false, block }); } CleanBotBlockedBuffs(); From e763e0e9a95065cca8c087e91c592dfb3b20ac68 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 20 Jan 2025 20:22:37 -0600 Subject: [PATCH 287/394] Add list option to ^spellpriority commands. --- zone/bot_commands/spell_engaged_priority.cpp | 36 ++++++++++++++++++-- zone/bot_commands/spell_idle_priority.cpp | 36 ++++++++++++++++++-- zone/bot_commands/spell_pursue_priority.cpp | 36 ++++++++++++++++++-- 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 271ce848b0..5ac3983347 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -115,6 +115,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; + bool list_check = false; uint16 spell_type = 0; uint32 type_value = 0; @@ -132,6 +133,10 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { spell_type = c->GetSpellTypeIDByShortName(arg1); } + else if (!arg1.compare("list")) { + ++ab_arg; + list_check = true; + } else { c->Message( Chat::Yellow, @@ -150,8 +155,8 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { type_value = atoi(sep->arg[2]); ++ab_arg; - if (type_value < 0 || type_value > 100) { - c->Message(Chat::Yellow, "You must enter a value between 0-100."); + if (EQ::ValueWithin(type_value, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message(Chat::Yellow, "You must enter a value between {} and {}.", BotSpellTypes::START, BotSpellTypes::END); return; } @@ -160,7 +165,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) ++ab_arg; current_check = true; } - else { + else if (!list_check) { c->Message( Chat::Yellow, fmt::format( @@ -210,6 +215,31 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) ).c_str() ); } + else if (list_check) { + auto cast_order = my_bot->GetSpellTypesPrioritized(BotPriorityCategories::Engaged); + + for (auto& current_cast : cast_order) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] engaged cast priority for is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(current_cast.spellType), + (current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority)) + ).c_str() + ); + } + + c->Message( + Chat::Green, + fmt::format( + "{} says, 'Anything not listed is currently disabled (0).'", + my_bot->GetCleanName() + ).c_str() + ); + + return; + } else { my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, type_value); ++success_count; diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 6b990b343e..0d24a148dc 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -115,6 +115,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; + bool list_check = false; uint16 spell_type = 0; uint32 type_value = 0; @@ -132,6 +133,10 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { spell_type = c->GetSpellTypeIDByShortName(arg1); } + else if (!arg1.compare("list")) { + ++ab_arg; + list_check = true; + } else { c->Message( Chat::Yellow, @@ -150,8 +155,8 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { type_value = atoi(sep->arg[2]); ++ab_arg; - if (type_value < 0 || type_value > 100) { - c->Message(Chat::Yellow, "You must enter a value between 0-100."); + if (EQ::ValueWithin(type_value, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message(Chat::Yellow, "You must enter a value between {} and {}.", BotSpellTypes::START, BotSpellTypes::END); return; } @@ -160,7 +165,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) ++ab_arg; current_check = true; } - else { + else if (!list_check) { c->Message( Chat::Yellow, fmt::format( @@ -210,6 +215,31 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) ).c_str() ); } + else if (list_check) { + auto cast_order = my_bot->GetSpellTypesPrioritized(BotPriorityCategories::Idle); + + for (auto& current_cast : cast_order) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] idle cast priority for is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(current_cast.spellType), + (current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority)) + ).c_str() + ); + } + + c->Message( + Chat::Green, + fmt::format( + "{} says, 'Anything not listed is currently disabled (0).'", + my_bot->GetCleanName() + ).c_str() + ); + + return; + } else { my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, type_value); ++success_count; diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 47fd620288..e9430ea4d6 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -115,6 +115,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) std::string arg2 = sep->arg[2]; int ab_arg = 2; bool current_check = false; + bool list_check = false; uint16 spell_type = 0; uint32 type_value = 0; @@ -132,6 +133,10 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { spell_type = c->GetSpellTypeIDByShortName(arg1); } + else if (!arg1.compare("list")) { + ++ab_arg; + list_check = true; + } else { c->Message( Chat::Yellow, @@ -150,8 +155,8 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { type_value = atoi(sep->arg[2]); ++ab_arg; - if (type_value < 0 || type_value > 100) { - c->Message(Chat::Yellow, "You must enter a value between 0-100."); + if (EQ::ValueWithin(type_value, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message(Chat::Yellow, "You must enter a value between {} and {}.", BotSpellTypes::START, BotSpellTypes::END); return; } @@ -160,7 +165,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) ++ab_arg; current_check = true; } - else { + else if (!list_check) { c->Message( Chat::Yellow, fmt::format( @@ -210,6 +215,31 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) ).c_str() ); } + else if (list_check) { + auto cast_order = my_bot->GetSpellTypesPrioritized(BotPriorityCategories::Pursue); + + for (auto& current_cast : cast_order) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] pursue cast priority for is currently [{}].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(current_cast.spellType), + (current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority)) + ).c_str() + ); + } + + c->Message( + Chat::Green, + fmt::format( + "{} says, 'Anything not listed is currently disabled (0).'", + my_bot->GetCleanName() + ).c_str() + ); + + return; + } else { my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, type_value); ++success_count; From 87bd37429fcf10af2bd0048e464a1c28281293b9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 21 Jan 2025 07:30:49 -0600 Subject: [PATCH 288/394] Move client functions to client_bot --- zone/client.cpp | 133 -------------------------------------- zone/client_bot.cpp | 154 ++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 140 insertions(+), 147 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 7fd23510a9..c8e76b2e29 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13112,79 +13112,6 @@ void Client::ShowZoneShardMenu() } } -void Client::LoadDefaultBotSettings() { - m_bot_spell_settings.clear(); - - // Only illusion block supported currently - SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); - LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); - - for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - BotSpellSettings_Struct t; - - t.spellType = i; - t.shortName = GetSpellTypeShortNameByID(i); - t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i); - t.delay = GetDefaultSpellDelay(i); - t.minThreshold = GetDefaultSpellMinThreshold(i); - t.maxThreshold = GetDefaultSpellMaxThreshold(i); - - m_bot_spell_settings.push_back(t); - - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); - } -} - -int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) { - switch (setting_type) { - case BotSettingCategories::BaseSetting: - return false; - case BotSettingCategories::SpellHold: - return GetDefaultSpellHold(bot_setting); - case BotSettingCategories::SpellDelay: - return GetDefaultSpellDelay(bot_setting); - case BotSettingCategories::SpellMinThreshold: - return GetDefaultSpellMinThreshold(bot_setting); - case BotSettingCategories::SpellMaxThreshold: - return GetDefaultSpellMaxThreshold(bot_setting); - } -} - -int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) { - switch (setting_type) { - case BotSettingCategories::SpellHold: - return GetSpellHold(bot_setting); - case BotSettingCategories::SpellDelay: - return GetSpellDelay(bot_setting); - case BotSettingCategories::SpellMinThreshold: - return GetSpellMinThreshold(bot_setting); - case BotSettingCategories::SpellMaxThreshold: - return GetSpellMaxThreshold(bot_setting); - } -} - -void Client::SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value) { - switch (setting_type) { - case BotSettingCategories::BaseSetting: - SetBaseSetting(bot_setting, setting_value); - break; - case BotSettingCategories::SpellHold: - SetSpellHold(bot_setting, setting_value); - break; - case BotSettingCategories::SpellDelay: - SetSpellDelay(bot_setting, setting_value); - break; - case BotSettingCategories::SpellMinThreshold: - SetSpellMinThreshold(bot_setting, setting_value); - break; - case BotSettingCategories::SpellMaxThreshold: - SetSpellMaxThreshold(bot_setting, setting_value); - break; - } -} - std::string Client::SendCommandHelpWindow( Client* c, std::vector description, @@ -13360,66 +13287,6 @@ std::string Client::SplitCommandHelpText(std::vector msg, std::stri return return_text; } -void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) { - if (client_only_types) { - Message( - Chat::Yellow, - fmt::format( - "You can view spell types by {} or {}.", - Saylink::Silent( - fmt::format("^spelltypeids client"), "ID" - ), - Saylink::Silent( - fmt::format("^spelltypenames client"), "Shortname" - ) - ).c_str() - ); - } - else { - Message( - Chat::Yellow, - fmt::format( - "You can view spell types by {}, {}, {} or by {}, {}, {}.", - Saylink::Silent( - fmt::format("^spelltypeids 0-19"), "ID 0-19" - ), - Saylink::Silent( - fmt::format("^spelltypeids 20-39"), "20-39" - ), - Saylink::Silent( - fmt::format("^spelltypeids 40+"), "40+" - ), - Saylink::Silent( - fmt::format("^spelltypenames 0-19"), "Shortname 0-19" - ), - Saylink::Silent( - fmt::format("^spelltypenames 20-39"), "20-39" - ), - Saylink::Silent( - fmt::format("^spelltypenames 40+"), "40+" - ) - ).c_str() - ); - } - - if (commanded_types) { - Message( - Chat::Yellow, - fmt::format( - "You can view commanded spell types by {} or {}.", - Saylink::Silent( - fmt::format("^spelltypeids commanded"), "ID" - ), - Saylink::Silent( - fmt::format("^spelltypenames commanded"), "Shortname" - ) - ).c_str() - ); - } - - return; -} - void Client::SetAAEXPPercentage(uint8 percentage) { const uint32 before_percentage = m_epp.perAA; diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 7b0bfd7bb9..374d645476 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -15,8 +15,7 @@ void Client::SetBotOption(BotOwnerOption boo, bool flag) { } } -uint32 Client::GetBotCreationLimit(uint8 class_id) -{ +uint32 Client::GetBotCreationLimit(uint8 class_id) { uint32 bot_creation_limit = RuleI(Bots, CreationLimit); if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { @@ -43,8 +42,7 @@ uint32 Client::GetBotCreationLimit(uint8 class_id) return bot_creation_limit; } -int Client::GetBotRequiredLevel(uint8 class_id) -{ +int Client::GetBotRequiredLevel(uint8 class_id) { int bot_character_level = RuleI(Bots, BotCharacterLevel); const auto bucket_name = fmt::format( @@ -67,8 +65,7 @@ int Client::GetBotRequiredLevel(uint8 class_id) return bot_character_level; } -int Client::GetBotSpawnLimit(uint8 class_id) -{ +int Client::GetBotSpawnLimit(uint8 class_id) { int bot_spawn_limit = RuleI(Bots, SpawnLimit); if (Admin() >= RuleI(Bots, MinStatusToBypassSpawnLimit)) { @@ -171,8 +168,7 @@ int Client::GetBotSpawnLimit(uint8 class_id) return bot_spawn_limit; } -void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id) -{ +void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id) { const auto bucket_name = fmt::format( "bot_creation_limit{}", ( @@ -188,8 +184,7 @@ void Client::SetBotCreationLimit(uint32 new_creation_limit, uint8 class_id) SetBucket(bucket_name, std::to_string(new_creation_limit)); } -void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id) -{ +void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id) { const auto bucket_name = fmt::format( "bot_required_level{}", ( @@ -205,8 +200,7 @@ void Client::SetBotRequiredLevel(int new_required_level, uint8 class_id) SetBucket(bucket_name, std::to_string(new_required_level)); } -void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id) -{ +void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id) { const auto bucket_name = fmt::format( "bot_spawn_limit{}", ( @@ -222,7 +216,139 @@ void Client::SetBotSpawnLimit(int new_spawn_limit, uint8 class_id) SetBucket(bucket_name, std::to_string(new_spawn_limit)); } -void Client::CampAllBots(uint8 class_id) -{ +void Client::CampAllBots(uint8 class_id) { Bot::BotOrderCampAll(this, class_id); } + +void Client::LoadDefaultBotSettings() { + m_bot_spell_settings.clear(); + + // Only illusion block supported currently + SetBotSetting(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); + LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + BotSpellSettings_Struct t; + + t.spellType = i; + t.shortName = GetSpellTypeShortNameByID(i); + t.name = GetSpellTypeNameByID(i); + t.hold = GetDefaultSpellHold(i); + t.delay = GetDefaultSpellDelay(i); + t.minThreshold = GetDefaultSpellMinThreshold(i); + t.maxThreshold = GetDefaultSpellMaxThreshold(i); + + m_bot_spell_settings.push_back(t); + + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); + } +} + +int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) { + switch (setting_type) { + case BotSettingCategories::BaseSetting: + return false; + case BotSettingCategories::SpellHold: + return GetDefaultSpellHold(bot_setting); + case BotSettingCategories::SpellDelay: + return GetDefaultSpellDelay(bot_setting); + case BotSettingCategories::SpellMinThreshold: + return GetDefaultSpellMinThreshold(bot_setting); + case BotSettingCategories::SpellMaxThreshold: + return GetDefaultSpellMaxThreshold(bot_setting); + } +} + +int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) { + switch (setting_type) { + case BotSettingCategories::SpellHold: + return GetSpellHold(bot_setting); + case BotSettingCategories::SpellDelay: + return GetSpellDelay(bot_setting); + case BotSettingCategories::SpellMinThreshold: + return GetSpellMinThreshold(bot_setting); + case BotSettingCategories::SpellMaxThreshold: + return GetSpellMaxThreshold(bot_setting); + } +} + +void Client::SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value) { + switch (setting_type) { + case BotSettingCategories::BaseSetting: + SetBaseSetting(bot_setting, setting_value); + break; + case BotSettingCategories::SpellHold: + SetSpellHold(bot_setting, setting_value); + break; + case BotSettingCategories::SpellDelay: + SetSpellDelay(bot_setting, setting_value); + break; + case BotSettingCategories::SpellMinThreshold: + SetSpellMinThreshold(bot_setting, setting_value); + break; + case BotSettingCategories::SpellMaxThreshold: + SetSpellMaxThreshold(bot_setting, setting_value); + break; + } +} + +void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) { + if (client_only_types) { + Message( + Chat::Yellow, + fmt::format( + "You can view spell types by {} or {}.", + Saylink::Silent( + fmt::format("^spelltypeids client"), "ID" + ), + Saylink::Silent( + fmt::format("^spelltypenames client"), "Shortname" + ) + ).c_str() + ); + } + else { + Message( + Chat::Yellow, + fmt::format( + "You can view spell types by {}, {}, {} or by {}, {}, {}.", + Saylink::Silent( + fmt::format("^spelltypeids 0-19"), "ID 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypeids 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypeids 40+"), "40+" + ), + Saylink::Silent( + fmt::format("^spelltypenames 0-19"), "Shortname 0-19" + ), + Saylink::Silent( + fmt::format("^spelltypenames 20-39"), "20-39" + ), + Saylink::Silent( + fmt::format("^spelltypenames 40+"), "40+" + ) + ).c_str() + ); + } + + if (commanded_types) { + Message( + Chat::Yellow, + fmt::format( + "You can view commanded spell types by {} or {}.", + Saylink::Silent( + fmt::format("^spelltypeids commanded"), "ID" + ), + Saylink::Silent( + fmt::format("^spelltypenames commanded"), "Shortname" + ) + ).c_str() + ); + } + + return; +} From 37910cf3dc5222adc4f5d171d9f332ed479ac041 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 21 Jan 2025 12:25:49 -0600 Subject: [PATCH 289/394] Move mob functions to mob_bot --- zone/CMakeLists.txt | 1 + zone/mob.cpp | 768 ------------------------------------------- zone/mob.h | 10 +- zone/mob_bot.cpp | 772 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 777 insertions(+), 774 deletions(-) create mode 100644 zone/mob_bot.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 798114399c..83963f5208 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -98,6 +98,7 @@ SET(zone_sources mob.cpp mob_ai.cpp mob_appearance.cpp + mob_bot.cpp mob_movement_manager.cpp mob_info.cpp npc.cpp diff --git a/zone/mob.cpp b/zone/mob.cpp index 0cf06fbbc7..e461fe6c74 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4822,83 +4822,6 @@ bool Mob::PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, fl return Result; } -bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) { - bool Result = false; - - if (target) { - float look_heading = 0; - - min_distance = min_distance; - max_distance = max_distance; - float temp_x = 0; - float temp_y = 0; - float temp_z = target->GetZ(); - float best_z = 0; - auto offset = GetZOffset(); - const float tar_x = target->GetX(); - const float tar_y = target->GetY(); - float tar_distance = 0; - - glm::vec3 temp_z_Position; - glm::vec4 temp_m_Position; - - const uint16 max_iterations_allowed = 50; - uint16 counter = 0; - - while (counter < max_iterations_allowed) { - temp_x = tar_x + zone->random.Real(-max_distance, max_distance); - temp_y = tar_y + zone->random.Real(-max_distance, max_distance); - - temp_z_Position.x = temp_z; - temp_z_Position.y = temp_y; - temp_z_Position.z = temp_z; - best_z = GetFixedZ(temp_z_Position); - - if (best_z != BEST_Z_INVALID) { - temp_z = best_z; - } - else { - counter++; - continue; - } - - temp_m_Position.x = temp_x; - temp_m_Position.y = temp_y; - temp_m_Position.z = temp_z; - - tar_distance = Distance(target->GetPosition(), temp_m_Position); - - if (tar_distance > max_distance || tar_distance < min_distance) { - counter++; - continue; - } - if (front_only && !InFrontMob(target, temp_x, temp_y)) { - counter++; - continue; - } - else if (behind_only && !BehindMob(target, temp_x, temp_y)) { - counter++; - continue; - } - if (!bypass_los && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, temp_x, temp_y, temp_z)) { - counter++; - continue; - } - - Result = true; - break; - } - - if (Result) { - x_dest = temp_x; - y_dest = temp_y; - z_dest = temp_z; - } - } - - return Result; -} - bool Mob::HateSummon() { // check if mob has ability to summon // 97% is the offical % that summoning starts on live, not 94 @@ -8690,697 +8613,6 @@ void Mob::CheckScanCloseMobsMovingTimer() } } -std::vector Mob::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots, bool no_pets) { - std::vector valid_spell_targets; - - auto is_valid_target = [no_clients, no_bots](Mob* member) { - return member && - ((member->IsClient() && !no_clients) || (member->IsBot() && !no_bots)); - }; - - if (IsRaidGrouped()) { - Raid* raid = IsBot() ? CastToBot()->GetStoredRaid() : GetRaid(); - - if (raid) { - if (entire_raid) { - for (const auto& m : raid->members) { - if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { - valid_spell_targets.emplace_back(m.member); - } - } - } - else { - auto group_name = target ? raid->GetGroup(target->GetName()) : raid->GetGroup(GetName()); - auto raid_group = raid->GetRaidGroupMembers(group_name); - - for (const auto& m : raid_group) { - if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { - valid_spell_targets.emplace_back(m.member); - } - } - } - } - } - else if (IsGrouped()) { - Group* group = GetGroup(); - - if (group) { - for (const auto& m : group->members) { - if (is_valid_target(m)) { - valid_spell_targets.emplace_back(m); - } - } - } - } - else { - valid_spell_targets.emplace_back(this); - } - - return valid_spell_targets; -} - -uint16 Mob::GetSpellTypeIDByShortName(std::string spell_type_string) { - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { - return i; - } - } - - for (int i = BotSpellTypes::COMMANDED_START; i <= BotSpellTypes::COMMANDED_END; ++i) { - if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { - return i; - } - } - - return UINT16_MAX; -} - -bool Mob::IsValidBotSpellCategory(uint8 setting_type) { - return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END_FULL); -} - -std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { - return IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; -} - -uint16 Mob::GetBotSpellCategoryIDByShortName(std::string setting_string) { - for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { - if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) { - return i; - } - } - - return UINT16_MAX; -} - -bool Mob::IsValidBotBaseSetting(uint16 setting_type) { - return EQ::ValueWithin(setting_type, BotBaseSettings::START_ALL, BotBaseSettings::END); -} - -std::string Mob::GetBotSettingCategoryName(uint16 setting_type) { - return IsValidBotBaseSetting(setting_type) ? botBaseSettings_names[setting_type] : "UNKNOWN SETTING"; -} - -uint16 Mob::GetBaseSettingIDByShortName(std::string setting_string) { - for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { - if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSettingCategoryName(i)))) { - return i; - } - } - - return UINT16_MAX; -} - -bool Mob::IsValidSpellType(uint16 spell_type) { - return ( - EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END) || - EQ::ValueWithin(spell_type, BotSpellTypes::COMMANDED_START, BotSpellTypes::COMMANDED_END) - ); -} - -std::string Mob::GetSpellTypeShortNameByID(uint16 spell_type) { - return IsValidSpellType(spell_type) ? spellType_shortNames[spell_type] : "UNKNOWN SPELLTYPE"; -} - -std::string Mob::GetSpellTypeNameByID(uint16 spell_type) { - return IsValidSpellType(spell_type) ? spellType_names[spell_type] : "UNKNOWN SPELLTYPE"; -} - -bool Mob::IsValidSubType(uint16 sub_type) { - return EQ::ValueWithin(sub_type, CommandedSubTypes::START, CommandedSubTypes::END); -} - -std::string Mob::GetSubTypeNameByID(uint16 sub_type) { - return IsValidSpellType(sub_type) ? botSubType_names[sub_type] : "UNKNOWN SUBTYPE"; -} - -bool Mob::GetDefaultSpellHold(uint16 spell_type, uint8 stance) { - uint8 bot_class = GetClass(); - - switch (spell_type) { - case BotSpellTypes::FastHeals: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::Pet: - case BotSpellTypes::Escape: - case BotSpellTypes::Lifetap: - case BotSpellTypes::Buff: - case BotSpellTypes::PetBuffs: - case BotSpellTypes::InCombatBuff: - case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::DamageShields: - case BotSpellTypes::ResistBuffs: - return false; - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::RegularHeal: - switch (stance) { - case Stance::Aggressive: - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - switch (stance) { - case Stance::Aggressive: - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::PreCombatBuffSong: - if (bot_class == Class::Bard) { - return false; - } - else { - return true; - } - case BotSpellTypes::Nuke: - case BotSpellTypes::DOT: - case BotSpellTypes::Stun: - switch (stance) { - case Stance::Assist: - return true; - default: - return false; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AEStun: - case BotSpellTypes::AEDoT: - case BotSpellTypes::AELifetap: - case BotSpellTypes::PBAENuke: - switch (stance) { - case Stance::AEBurn: - return false; - default: - return true; - } - case BotSpellTypes::Mez: - case BotSpellTypes::AEMez: - case BotSpellTypes::Debuff: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Slow: - case BotSpellTypes::AESlow: - case BotSpellTypes::HateRedux: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::Snare: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Assist: - return true; - default: - return false; - } - case BotSpellTypes::HateLine: - if (bot_class == Class::ShadowKnight || bot_class == Class::Paladin) { - switch (stance) { - case Stance::Aggressive: - return false; - default: - return true; - } - } - else { - return true; - } - case BotSpellTypes::Charm: - case BotSpellTypes::Resurrect: - case BotSpellTypes::AESnare: - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AEFear: - case BotSpellTypes::Fear: - case BotSpellTypes::AEHateLine: - case BotSpellTypes::PetCures: - case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetResistBuffs: - default: - return true; - } -} - -uint16 Mob::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { - switch (spell_type) { - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::PetVeryFastHeals: - return 1500; - case BotSpellTypes::FastHeals: - case BotSpellTypes::PetFastHeals: - return 2500; - case BotSpellTypes::GroupHeals: - case BotSpellTypes::RegularHeal: - case BotSpellTypes::PetRegularHeals: - return 4000; - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::PetCompleteHeals: - return 8000; - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetHoTHeals: - return 22000; - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return 1; - case Stance::Aggressive: - return 2000; - case Stance::Efficient: - return 8000; - default: - return 4000; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: - case BotSpellTypes::AESnare: - case BotSpellTypes::Snare: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: - case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: - case BotSpellTypes::AEStun: - case BotSpellTypes::Stun: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return 1; - case Stance::Aggressive: - return 3000; - case Stance::Efficient: - return 10000; - default: - return 6000; - } - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - return 8000; - case BotSpellTypes::Fear: - case BotSpellTypes::AEFear: - return 15000; - default: - return 1; - } -} - -uint8 Mob::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { - switch (spell_type) { - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 0; - default: - return 20; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 0; - default: - return 5; - } - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 0; - case Stance::Efficient: - return 40; - default: - return 25; - } - case BotSpellTypes::Mez: - case BotSpellTypes::AEMez: - return 85; - default: - return 0; - } -} - -uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { - uint8 bot_class = GetClass(); - - switch (spell_type) { - case BotSpellTypes::Escape: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::PetVeryFastHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 40; - case Stance::Efficient: - default: - return 25; - } - case BotSpellTypes::AELifetap: - case BotSpellTypes::Lifetap: - case BotSpellTypes::FastHeals: - case BotSpellTypes::PetFastHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 55; - case Stance::Efficient: - return 35; - default: - return 40; - } - case BotSpellTypes::GroupHeals: - case BotSpellTypes::RegularHeal: - case BotSpellTypes::PetRegularHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 70; - case Stance::Efficient: - return 50; - default: - return 60; - } - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::PetCompleteHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 90; - case Stance::Efficient: - return 65; - default: - return 80; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::AEStun: - case BotSpellTypes::Nuke: - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: - case BotSpellTypes::AESnare: - case BotSpellTypes::Snare: - case BotSpellTypes::AEFear: - case BotSpellTypes::Fear: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: - case BotSpellTypes::Stun: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return 100; - case Stance::Aggressive: - return 100; - case Stance::Efficient: - return 90; - default: - return 99; - } - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetHoTHeals: - if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { - return 60; - } - else { - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 95; - case Stance::Efficient: - return 80; - default: - return 90; - } - } - case BotSpellTypes::Buff: - case BotSpellTypes::Charm: - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - case BotSpellTypes::PetCures: - case BotSpellTypes::DamageShields: - case BotSpellTypes::HateRedux: - case BotSpellTypes::InCombatBuff: - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::Mez: - case BotSpellTypes::AEMez: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::Pet: - case BotSpellTypes::PetBuffs: - case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::PreCombatBuffSong: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetResistBuffs: - case BotSpellTypes::ResistBuffs: - case BotSpellTypes::Resurrect: - case BotSpellTypes::HateLine: - case BotSpellTypes::AEHateLine: - default: - return 100; - } -} - -void Mob::SetSpellHold(uint16 spell_type, bool hold_status) { - m_bot_spell_settings[spell_type].hold = hold_status; -} - -void Mob::SetSpellDelay(uint16 spell_type, uint16 delay_value) { - m_bot_spell_settings[spell_type].delay = delay_value; -} - -void Mob::SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { - m_bot_spell_settings[spell_type].minThreshold = threshold_value; -} - -void Mob::SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { - m_bot_spell_settings[spell_type].maxThreshold = threshold_value; -} - -void Mob::SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { - m_bot_spell_settings[spell_type].recastTimer.Start(recast_time); -} - -void Mob::StartBotSpellTimers() { - for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - m_bot_spell_settings[i].recastTimer.Start(); - } -} - -void Mob::DisableBotSpellTimers() { - for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - m_bot_spell_settings[i].recastTimer.Disable(); - } -} - -bool Mob::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellHold(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellHold(GetPetSpellType(spell_type)); - } - - return GetSpellHold(spell_type); -} - -uint16 Mob::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellDelay(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->GetSpellDelay(spell_type); - } - - return GetSpellDelay(spell_type); -} - -bool Mob::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { - if (!tar) { - return SpellTypeRecastCheck(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->SpellTypeRecastCheck(spell_type); - } - - return SpellTypeRecastCheck(spell_type); -} - -uint8 Mob::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellMinThreshold(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->GetSpellMinThreshold(spell_type); - } - - return GetSpellMinThreshold(spell_type); -} - -uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellMaxThreshold(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->GetSpellMaxThreshold(spell_type); - } - - return GetSpellMaxThreshold(spell_type); -} - -uint16 Mob::GetPetSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::VeryFastHeals: - return BotSpellTypes::PetVeryFastHeals; - case BotSpellTypes::FastHeals: - return BotSpellTypes::PetFastHeals; - case BotSpellTypes::RegularHeal: - return BotSpellTypes::PetRegularHeals; - case BotSpellTypes::CompleteHeal: - return BotSpellTypes::PetCompleteHeals; - case BotSpellTypes::HoTHeals: - return BotSpellTypes::PetHoTHeals; - case BotSpellTypes::Buff: - return BotSpellTypes::PetBuffs; - case BotSpellTypes::Cure: - return BotSpellTypes::PetCures; - case BotSpellTypes::DamageShields: - return BotSpellTypes::PetDamageShields; - case BotSpellTypes::ResistBuffs: - return BotSpellTypes::PetResistBuffs; - default: - break; - } - - return spell_type; -} - -uint8 Mob::GetHPRatioForSpellType(uint16 spell_type, Mob* tar) { - switch (spell_type) { - case BotSpellTypes::Escape: - case BotSpellTypes::HateRedux: - case BotSpellTypes::InCombatBuff: - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::AELifetap: - case BotSpellTypes::Lifetap: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::Pet: - case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::PreCombatBuffSong: - return GetHPRatio(); - default: - return tar->GetHPRatio(); - } - - return tar->GetHPRatio(); -} - -void Mob::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value) { - if (!IsOfClientBot()) { - return; - } - - if (IsClient()) { - CastToClient()->SetBotSetting(setting_type, bot_setting, setting_value); - return; - } - - if (IsBot()) { - CastToBot()->SetBotSetting(setting_type, bot_setting, setting_value); - return; - } - - return; -} - -void Mob::SetBaseSetting(uint16 base_setting, int setting_value) { - switch (base_setting) { - case BotBaseSettings::IllusionBlock: - SetIllusionBlock(setting_value); - break; - default: - break; - } -} - -bool Mob::TargetValidation(Mob* other) { - if (!other || GetAppearance() == eaDead) { - return false; - } - - return true; -} - std::unordered_map& Mob::GetCloseMobList(float distance) { return entity_list.GetCloseMobList(this, distance); diff --git a/zone/mob.h b/zone/mob.h index b092e3f547..ed358e79ec 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -428,14 +428,12 @@ class Mob : public Entity { virtual bool CheckSpellLevelRestriction(Mob *caster, uint16 spell_id); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); + // Bot functions + bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); virtual bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); - - inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient() ? true : m_bot_spell_settings[spellType].recastTimer.GetRemainingTime() > 0 ? false : true); } - std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); - + inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient () ? true : m_bot_spell_settings [spellType].recastTimer.GetRemainingTime () > 0 ? false : true); } uint16 GetSpellTypeIDByShortName(std::string spellType_string); - bool IsValidBotSpellCategory(uint8 setting_type); std::string GetBotSpellCategoryName(uint8 setting_type); uint16 GetBotSpellCategoryIDByShortName(std::string setting_string); @@ -940,7 +938,7 @@ class Mob : public Entity { void ShowStats(Client* client); void ShowBuffs(Client* c); bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true); - bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); + virtual int GetKillExpMod() const { return 100; } // aura functions diff --git a/zone/mob_bot.cpp b/zone/mob_bot.cpp new file mode 100644 index 0000000000..4dafd49350 --- /dev/null +++ b/zone/mob_bot.cpp @@ -0,0 +1,772 @@ +#include "bot.h" +#include "mob.h" + +bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) { + bool Result = false; + + if (target) { + float look_heading = 0; + + min_distance = min_distance; + max_distance = max_distance; + float temp_x = 0; + float temp_y = 0; + float temp_z = target->GetZ(); + float best_z = 0; + auto offset = GetZOffset(); + const float tar_x = target->GetX(); + const float tar_y = target->GetY(); + float tar_distance = 0; + + glm::vec3 temp_z_Position; + glm::vec4 temp_m_Position; + + const uint16 max_iterations_allowed = 50; + uint16 counter = 0; + + while (counter < max_iterations_allowed) { + temp_x = tar_x + zone->random.Real(-max_distance, max_distance); + temp_y = tar_y + zone->random.Real(-max_distance, max_distance); + + temp_z_Position.x = temp_z; + temp_z_Position.y = temp_y; + temp_z_Position.z = temp_z; + best_z = GetFixedZ(temp_z_Position); + + if (best_z != BEST_Z_INVALID) { + temp_z = best_z; + } + else { + counter++; + continue; + } + + temp_m_Position.x = temp_x; + temp_m_Position.y = temp_y; + temp_m_Position.z = temp_z; + + tar_distance = Distance(target->GetPosition(), temp_m_Position); + + if (tar_distance > max_distance || tar_distance < min_distance) { + counter++; + continue; + } + + if (front_only && !InFrontMob(target, temp_x, temp_y)) { + counter++; + continue; + } + else if (behind_only && !BehindMob(target, temp_x, temp_y)) { + counter++; + continue; + } + + if (!bypass_los && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, temp_x, temp_y, temp_z)) { + counter++; + continue; + } + + Result = true; + break; + } + + if (Result) { + x_dest = temp_x; + y_dest = temp_y; + z_dest = temp_z; + } + } + + return Result; +} + +std::vector Mob::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots, bool no_pets) { + std::vector valid_spell_targets; + + auto is_valid_target = [no_clients, no_bots](Mob* member) { + return member && + ((member->IsClient() && !no_clients) || (member->IsBot() && !no_bots)); + }; + + if (IsRaidGrouped()) { + Raid* raid = IsBot() ? CastToBot()->GetStoredRaid() : GetRaid(); + + if (raid) { + if (entire_raid) { + for (const auto& m : raid->members) { + if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { + valid_spell_targets.emplace_back(m.member); + } + } + } + else { + auto group_name = target ? raid->GetGroup(target->GetName()) : raid->GetGroup(GetName()); + auto raid_group = raid->GetRaidGroupMembers(group_name); + + for (const auto& m : raid_group) { + if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { + valid_spell_targets.emplace_back(m.member); + } + } + } + } + } + else if (IsGrouped()) { + Group* group = GetGroup(); + + if (group) { + for (const auto& m : group->members) { + if (is_valid_target(m)) { + valid_spell_targets.emplace_back(m); + } + } + } + } + else { + valid_spell_targets.emplace_back(this); + } + + return valid_spell_targets; +} + +uint16 Mob::GetSpellTypeIDByShortName(std::string spell_type_string) { + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + + for (int i = BotSpellTypes::COMMANDED_START; i <= BotSpellTypes::COMMANDED_END; ++i) { + if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + + return UINT16_MAX; +} + +bool Mob::IsValidBotSpellCategory(uint8 setting_type) { + return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END_FULL); +} + +std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { + return IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; +} + +uint16 Mob::GetBotSpellCategoryIDByShortName(std::string setting_string) { + for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { + if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) { + return i; + } + } + + return UINT16_MAX; +} + +bool Mob::IsValidBotBaseSetting(uint16 setting_type) { + return EQ::ValueWithin(setting_type, BotBaseSettings::START_ALL, BotBaseSettings::END); +} + +std::string Mob::GetBotSettingCategoryName(uint16 setting_type) { + return IsValidBotBaseSetting(setting_type) ? botBaseSettings_names[setting_type] : "UNKNOWN SETTING"; +} + +uint16 Mob::GetBaseSettingIDByShortName(std::string setting_string) { + for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { + if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSettingCategoryName(i)))) { + return i; + } + } + + return UINT16_MAX; +} + +bool Mob::IsValidSpellType(uint16 spell_type) { + return ( + EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END) || + EQ::ValueWithin(spell_type, BotSpellTypes::COMMANDED_START, BotSpellTypes::COMMANDED_END) + ); +} + +std::string Mob::GetSpellTypeShortNameByID(uint16 spell_type) { + return IsValidSpellType(spell_type) ? spellType_shortNames[spell_type] : "UNKNOWN SPELLTYPE"; +} + +std::string Mob::GetSpellTypeNameByID(uint16 spell_type) { + return IsValidSpellType(spell_type) ? spellType_names[spell_type] : "UNKNOWN SPELLTYPE"; +} + +bool Mob::IsValidSubType(uint16 sub_type) { + return EQ::ValueWithin(sub_type, CommandedSubTypes::START, CommandedSubTypes::END); +} + +std::string Mob::GetSubTypeNameByID(uint16 sub_type) { + return IsValidSpellType(sub_type) ? botSubType_names[sub_type] : "UNKNOWN SUBTYPE"; +} + +bool Mob::GetDefaultSpellHold(uint16 spell_type, uint8 stance) { + uint8 bot_class = GetClass(); + + switch (spell_type) { + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::Pet: + case BotSpellTypes::Escape: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + return false; + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::RegularHeal: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: + if (bot_class == Class::Bard) { + return false; + } + else { + return true; + } + case BotSpellTypes::Nuke: + case BotSpellTypes::DOT: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::Assist: + return true; + default: + return false; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + switch (stance) { + case Stance::AEBurn: + return false; + default: + return true; + } + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Slow: + case BotSpellTypes::AESlow: + case BotSpellTypes::HateRedux: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::Snare: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Assist: + return true; + default: + return false; + } + case BotSpellTypes::HateLine: + if (bot_class == Class::ShadowKnight || bot_class == Class::Paladin) { + switch (stance) { + case Stance::Aggressive: + return false; + default: + return true; + } + } + else { + return true; + } + case BotSpellTypes::Charm: + case BotSpellTypes::Resurrect: + case BotSpellTypes::AESnare: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::AEHateLine: + case BotSpellTypes::PetCures: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + default: + return true; + } +} + +uint16 Mob::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return 1500; + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return 2500; + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return 4000; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 8000; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return 22000; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 2000; + case Stance::Efficient: + return 8000; + default: + return 4000; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 3000; + case Stance::Efficient: + return 10000; + default: + return 6000; + } + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + return 8000; + case BotSpellTypes::Fear: + case BotSpellTypes::AEFear: + return 15000; + default: + return 1; + } +} + +uint8 Mob::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { + switch (spell_type) { + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 20; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 5; + } + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + case Stance::Efficient: + return 40; + default: + return 25; + } + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + return 85; + default: + return 0; + } +} + +uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { + uint8 bot_class = GetClass(); + + switch (spell_type) { + case BotSpellTypes::Escape: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 40; + case Stance::Efficient: + default: + return 25; + } + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 55; + case Stance::Efficient: + return 35; + default: + return 40; + } + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 70; + case Stance::Efficient: + return 50; + default: + return 60; + } + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 90; + case Stance::Efficient: + return 65; + default: + return 80; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 100; + case Stance::Aggressive: + return 100; + case Stance::Efficient: + return 90; + default: + return 99; + } + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { + return 60; + } + else { + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 95; + case Stance::Efficient: + return 80; + default: + return 90; + } + } + case BotSpellTypes::Buff: + case BotSpellTypes::Charm: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::Resurrect: + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: + default: + return 100; + } +} + +void Mob::SetSpellHold(uint16 spell_type, bool hold_status) { + m_bot_spell_settings[spell_type].hold = hold_status; +} + +void Mob::SetSpellDelay(uint16 spell_type, uint16 delay_value) { + m_bot_spell_settings[spell_type].delay = delay_value; +} + +void Mob::SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { + m_bot_spell_settings[spell_type].minThreshold = threshold_value; +} + +void Mob::SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { + m_bot_spell_settings[spell_type].maxThreshold = threshold_value; +} + +void Mob::SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { + m_bot_spell_settings[spell_type].recastTimer.Start(recast_time); +} + +void Mob::StartBotSpellTimers() { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + m_bot_spell_settings[i].recastTimer.Start(); + } +} + +void Mob::DisableBotSpellTimers() { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + m_bot_spell_settings[i].recastTimer.Disable(); + } +} + +bool Mob::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellHold(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellHold(GetPetSpellType(spell_type)); + } + + return GetSpellHold(spell_type); +} + +uint16 Mob::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellDelay(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->GetSpellDelay(spell_type); + } + + return GetSpellDelay(spell_type); +} + +bool Mob::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { + if (!tar) { + return SpellTypeRecastCheck(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->SpellTypeRecastCheck(spell_type); + } + + return SpellTypeRecastCheck(spell_type); +} + +uint8 Mob::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellMinThreshold(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->GetSpellMinThreshold(spell_type); + } + + return GetSpellMinThreshold(spell_type); +} + +uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellMaxThreshold(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->GetSpellMaxThreshold(spell_type); + } + + return GetSpellMaxThreshold(spell_type); +} + +uint16 Mob::GetPetSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + case BotSpellTypes::Cure: + return BotSpellTypes::PetCures; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; + default: + break; + } + + return spell_type; +} + +uint8 Mob::GetHPRatioForSpellType(uint16 spell_type, Mob* tar) { + switch (spell_type) { + case BotSpellTypes::Escape: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + return GetHPRatio(); + default: + return tar->GetHPRatio(); + } + + return tar->GetHPRatio(); +} + +void Mob::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value) { + if (!IsOfClientBot()) { + return; + } + + if (IsClient()) { + CastToClient()->SetBotSetting(setting_type, bot_setting, setting_value); + return; + } + + if (IsBot()) { + CastToBot()->SetBotSetting(setting_type, bot_setting, setting_value); + return; + } + + return; +} + +void Mob::SetBaseSetting(uint16 base_setting, int setting_value) { + switch (base_setting) { + case BotBaseSettings::IllusionBlock: + SetIllusionBlock(setting_value); + break; + default: + break; + } +} + +bool Mob::TargetValidation(Mob* other) { + if (!other || GetAppearance() == eaDead) { + return false; + } + + return true; +} From 2301b91e77d2494301d0cd9c63775b8e538b7155 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 21 Jan 2025 13:13:30 -0600 Subject: [PATCH 290/394] Move bot spdat functions to spdat_bot --- common/CMakeLists.txt | 1 + common/spdat.cpp | 651 --------------------------------- common/spdat.h | 15 +- common/spdat_bot.cpp | 713 +++++++++++++++++++++++++++++++++++++ zone/bot.cpp | 4 +- zone/bot_commands/cast.cpp | 2 +- zone/botspellsai.cpp | 14 +- zone/mob.h | 2 +- zone/mob_bot.cpp | 12 +- 9 files changed, 739 insertions(+), 675 deletions(-) create mode 100644 common/spdat_bot.cpp diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index ff130adb0a..bc3dd52bd9 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -89,6 +89,7 @@ SET(common_sources skills.cpp skill_caps.cpp spdat.cpp + spdat_bot.cpp strings.cpp struct_strategy.cpp textures.cpp diff --git a/common/spdat.cpp b/common/spdat.cpp index 898c06bc25..b38bc2ad72 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2791,352 +2791,6 @@ bool IsLichSpell(uint16 spell_id) return false; } -bool IsBotSpellTypeDetrimental(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::Nuke: - case BotSpellTypes::Root: - case BotSpellTypes::Lifetap: - case BotSpellTypes::Snare: - case BotSpellTypes::DOT: - case BotSpellTypes::Dispel: - case BotSpellTypes::Mez: - case BotSpellTypes::Charm: - case BotSpellTypes::Slow: - case BotSpellTypes::Debuff: - case BotSpellTypes::HateRedux: - case BotSpellTypes::Fear: - case BotSpellTypes::Stun: - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AEMez: - case BotSpellTypes::AEStun: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: - case BotSpellTypes::AEFear: - case BotSpellTypes::AEDispel: - case BotSpellTypes::AERoot: - case BotSpellTypes::AEDoT: - case BotSpellTypes::AELifetap: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Lull: - case BotSpellTypes::AELull: - case BotSpellTypes::HateLine: - case BotSpellTypes::AEHateLine: - return true; - default: - return false; - } - - return false; -} - -bool IsBotSpellTypeBeneficial(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::RegularHeal: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::FastHeals: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::Buff: - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - case BotSpellTypes::PetCures: - case BotSpellTypes::DamageShields: - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::Pet: - case BotSpellTypes::PetBuffs: - case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::PreCombatBuffSong: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetResistBuffs: - case BotSpellTypes::ResistBuffs: - case BotSpellTypes::Resurrect: - case BotSpellTypes::Teleport: - case BotSpellTypes::Succor: - case BotSpellTypes::BindAffinity: - case BotSpellTypes::Identify: - case BotSpellTypes::Levitate: - case BotSpellTypes::Rune: - case BotSpellTypes::WaterBreathing: - case BotSpellTypes::Size: - case BotSpellTypes::Invisibility: - case BotSpellTypes::MovementSpeed: - case BotSpellTypes::SendHome: - case BotSpellTypes::SummonCorpse: - return true; - default: - return false; - } - - return false; -} - -bool IsBotSpellTypeOtherBeneficial(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::RegularHeal: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::FastHeals: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::Buff: - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - case BotSpellTypes::PetCures: - case BotSpellTypes::DamageShields: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetBuffs: - case BotSpellTypes::ResistBuffs: - case BotSpellTypes::PetResistBuffs: - case BotSpellTypes::Teleport: - case BotSpellTypes::Succor: - case BotSpellTypes::BindAffinity: - case BotSpellTypes::Identify: - case BotSpellTypes::Levitate: - case BotSpellTypes::Rune: - case BotSpellTypes::WaterBreathing: - case BotSpellTypes::Size: - case BotSpellTypes::Invisibility: - case BotSpellTypes::MovementSpeed: - case BotSpellTypes::SendHome: - case BotSpellTypes::SummonCorpse: - return true; - default: - return false; - } - - return false; -} - -bool IsBotSpellTypeInnate(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: - case BotSpellTypes::Charm: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - case BotSpellTypes::AELifetap: - case BotSpellTypes::Lifetap: - case BotSpellTypes::AEStun: - case BotSpellTypes::Stun: - case BotSpellTypes::AEMez: - case BotSpellTypes::Mez: - case BotSpellTypes::Lull: - case BotSpellTypes::AELull: - case BotSpellTypes::HateLine: - case BotSpellTypes::AEHateLine: - return true; - default: - return false; - } - - return false; -} - -bool IsAEBotSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::AEDebuff: - case BotSpellTypes::AEFear: - case BotSpellTypes::AEMez: - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AESlow: - case BotSpellTypes::AESnare: - case BotSpellTypes::AEStun: - case BotSpellTypes::AEDispel: - case BotSpellTypes::AEDoT: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::AELifetap: - case BotSpellTypes::AERoot: - case BotSpellTypes::AEHateLine: - case BotSpellTypes::AELull: - return true; - default: - return false; - } - - return false; -} - -bool IsGroupBotSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::GroupCures: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - return true; - default: - return false; - } - - return false; -} - -bool IsGroupTargetOnlyBotSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::GroupCures: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHeals: - return true; - default: - return false; - } - - return false; -} - -bool IsPetBotSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::PetBuffs: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetResistBuffs: - case BotSpellTypes::PetCures: - return true; - default: - return false; - } - - return false; -} - -bool IsClientBotSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::RegularHeal: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::FastHeals: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::Buff: - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - case BotSpellTypes::PetCures: - case BotSpellTypes::DamageShields: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetBuffs: - case BotSpellTypes::ResistBuffs: - case BotSpellTypes::PetResistBuffs: - return true; - default: - return false; - } - - return false; -} - -bool IsHealBotSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::FastHeals: - case BotSpellTypes::RegularHeal: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::PetHoTHeals: - return true; - default: - return false; - } - - return false; -} - -bool SpellTypeRequiresLoS(uint16 spell_type) { - if (IsAEBotSpellType(spell_type)) { // These gather their own targets later - return false; - } - - switch (spell_type) { - case BotSpellTypes::RegularHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::FastHeals: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::InCombatBuff: - return false; - default: - return true; - } - - return true; -} - -bool SpellTypeRequiresTarget(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::Pet: - case BotSpellTypes::Succor: - return false; - default: - return true; - } - - return true; -} - -bool SpellTypeRequiresAEChecks(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::AEMez: - return false; - default: - return true; - } - - return true; -} - bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) { if (!IsValidSpell(spell_id)) { return false; @@ -3162,25 +2816,6 @@ bool IsResurrectSpell(uint16 spell_id) return IsEffectInSpell(spell_id, SE_Revive); } -bool RequiresStackCheck(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::FastHeals: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::RegularHeal: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::GroupCompleteHeals: - return false; - default: - return true; - } - - return true; -} - bool IsResistanceOnlySpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) { return false; @@ -3243,289 +2878,3 @@ bool IsHateSpell(uint16 spell_id) { return false; } - -bool IsCommandedSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::Charm: - case BotSpellTypes::AEFear: - case BotSpellTypes::Fear: - case BotSpellTypes::Resurrect: - case BotSpellTypes::AELull: - case BotSpellTypes::Lull: - case BotSpellTypes::Teleport: - case BotSpellTypes::Succor: - case BotSpellTypes::BindAffinity: - case BotSpellTypes::Identify: - case BotSpellTypes::Levitate: - case BotSpellTypes::Rune: - case BotSpellTypes::WaterBreathing: - case BotSpellTypes::Size: - case BotSpellTypes::Invisibility: - case BotSpellTypes::MovementSpeed: - case BotSpellTypes::SendHome: - case BotSpellTypes::SummonCorpse: - //case BotSpellTypes::Cure: - //case BotSpellTypes::GroupCures: - //case BotSpellTypes::DamageShields: - //case BotSpellTypes::PetDamageShields: - //case BotSpellTypes::ResistBuffs: - //case BotSpellTypes::PetResistBuffs: - return true; - default: - return false; - } - - return false; -} - -bool IsPullingSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::Nuke: - case BotSpellTypes::Lifetap: - case BotSpellTypes::Snare: - case BotSpellTypes::DOT: - case BotSpellTypes::Dispel: - case BotSpellTypes::Slow: - case BotSpellTypes::Debuff: - case BotSpellTypes::Stun: - case BotSpellTypes::HateLine: - return true; - default: - return false; - } - - return false; -} - -uint16 GetCorrectSpellType(uint16 spell_type, uint16 spell_id) { - uint16 correct_type = UINT16_MAX; - SPDat_Spell_Struct spell = spells[spell_id]; - std::string teleport_zone = spell.teleport_zone; - - if (IsCharmSpell(spell_id)) { - correct_type = BotSpellTypes::Charm; - } - else if (IsFearSpell(spell_id)) { - correct_type = BotSpellTypes::Fear; - } - else if (IsEffectInSpell(spell_id, SE_Revive)) { - correct_type = BotSpellTypes::Resurrect; - } - else if (IsHarmonySpell(spell_id)) { - correct_type = BotSpellTypes::Lull; - } - else if (teleport_zone.compare("") && !IsEffectInSpell(spell_id, SE_GateToHomeCity) && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate))) { - correct_type = BotSpellTypes::Teleport; - } - else if (IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_Succor)) { - correct_type = BotSpellTypes::Succor; - } - else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { - correct_type = BotSpellTypes::BindAffinity; - } - else if (IsEffectInSpell(spell_id, SE_Identify)) { - correct_type = BotSpellTypes::Identify; - } - else if (spell_type == BotSpellTypes::Levitate && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Levitate))) { - correct_type = BotSpellTypes::Levitate; - } - else if (spell_type == BotSpellTypes::Rune && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune))) { - correct_type = BotSpellTypes::Rune; - } - else if (spell_type == BotSpellTypes::WaterBreathing && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_WaterBreathing)) { - correct_type = BotSpellTypes::WaterBreathing; - } - else if (spell_type == BotSpellTypes::Size && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight))) { - correct_type = BotSpellTypes::Size; - } - else if (spell_type == BotSpellTypes::Invisibility && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id))) { - correct_type = BotSpellTypes::Invisibility; - } - else if (spell_type == BotSpellTypes::MovementSpeed && IsBeneficialSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - correct_type = BotSpellTypes::MovementSpeed; - } - else if (!teleport_zone.compare("") && IsBeneficialSpell(spell_id) && (IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity))) { - correct_type = BotSpellTypes::SendHome; - } - else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { - correct_type = BotSpellTypes::SummonCorpse; - } - - if (correct_type == UINT16_MAX) { - if (IsSummonPetSpell(spell_id) || IsEffectInSpell(spell_id, SE_TemporaryPets)) { - correct_type = BotSpellTypes::Pet; - } - else if (IsMesmerizeSpell(spell_id)) { - correct_type = BotSpellTypes::Mez; - } - else if (IsEscapeSpell(spell_id)) { - correct_type = BotSpellTypes::Escape; - } - else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_Root)) { - if (IsAnyAESpell(spell_id)) { - correct_type = BotSpellTypes::AERoot; - } - else { - correct_type = BotSpellTypes::Root; - } - } - else if (IsDetrimentalSpell(spell_id) && IsLifetapSpell(spell_id)) { - correct_type = BotSpellTypes::Lifetap; - } - else if (IsDetrimentalSpell(spell_id) && IsEffectInSpell(spell_id, SE_MovementSpeed)) { - correct_type = BotSpellTypes::Snare; - } - else if (IsDetrimentalSpell(spell_id) && (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id))) { - correct_type = BotSpellTypes::DOT; - } - else if (IsDispelSpell(spell_id)) { - correct_type = BotSpellTypes::Dispel; - } - else if (IsDetrimentalSpell(spell_id) && IsSlowSpell(spell_id)) { - correct_type = BotSpellTypes::Slow; - } - else if (IsDebuffSpell(spell_id) && !IsHateReduxSpell(spell_id) && !IsHateSpell(spell_id)) { - correct_type = BotSpellTypes::Debuff; - } - else if (IsHateReduxSpell(spell_id)) { - correct_type = BotSpellTypes::HateRedux; - } - else if (IsDetrimentalSpell(spell_id) && IsHateSpell(spell_id)) { - correct_type = BotSpellTypes::HateLine; - } - else if ( - IsBuffSpell(spell_id) && - IsBeneficialSpell(spell_id) && - IsBardSong(spell_id) - ) { - if ( - spell_type == BotSpellTypes::InCombatBuffSong || - spell_type == BotSpellTypes::OutOfCombatBuffSong || - spell_type == BotSpellTypes::PreCombatBuffSong - ) { - correct_type = spell_type; - } - else { - correct_type = BotSpellTypes::OutOfCombatBuffSong; - } - } - else if ( - !IsBardSong(spell_id) && - ( - (IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) || - (spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) - ) - ) { - correct_type = BotSpellTypes::InCombatBuff; - } - else if ( - spell_type == BotSpellTypes::PreCombatBuff && - IsAnyBuffSpell(spell_id) && - !IsBardSong(spell_id) - ) { - correct_type = BotSpellTypes::PreCombatBuff; - } - else if ( - (IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) || - (IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id)) - ) { - correct_type = BotSpellTypes::Cure; - } - else if (IsAnyNukeOrStunSpell(spell_id)) { - if (IsAnyAESpell(spell_id)) { - if (IsAERainSpell(spell_id)) { - correct_type = BotSpellTypes::AERains; - } - else if (IsPBAENukeSpell(spell_id)) { - correct_type = BotSpellTypes::PBAENuke; - } - else if (IsStunSpell(spell_id)) { - correct_type = BotSpellTypes::AEStun; - } - else { - correct_type = BotSpellTypes::AENukes; - } - } - else if (IsStunSpell(spell_id)) { - correct_type = BotSpellTypes::Stun; - } - else { - correct_type = BotSpellTypes::Nuke; - } - } - else if (IsAnyHealSpell(spell_id)) { - if (IsGroupSpell(spell_id)) { - if (IsGroupCompleteHealSpell(spell_id)) { - correct_type = BotSpellTypes::GroupCompleteHeals; - } - else if (IsGroupHealOverTimeSpell(spell_id)) { - correct_type = BotSpellTypes::GroupHoTHeals; - } - else if (IsRegularGroupHealSpell(spell_id)) { - correct_type = BotSpellTypes::GroupHeals; - } - } - else { - if (IsVeryFastHealSpell(spell_id)) { - correct_type = BotSpellTypes::VeryFastHeals; - } - else if (IsFastHealSpell(spell_id)) { - correct_type = BotSpellTypes::FastHeals; - } - else if (IsCompleteHealSpell(spell_id)) { - correct_type = BotSpellTypes::CompleteHeal; - } - else if (IsHealOverTimeSpell(spell_id)) { - correct_type = BotSpellTypes::HoTHeals; - } - else if (IsRegularSingleTargetHealSpell(spell_id)) { - correct_type = BotSpellTypes::RegularHeal; - } - else if (IsRegularPetHealSpell(spell_id)) { - correct_type = BotSpellTypes::RegularHeal; - } - } - } - else if (IsAnyBuffSpell(spell_id)) { - if (IsResistanceOnlySpell(spell_id)) { - correct_type = BotSpellTypes::ResistBuffs; - } - else if (IsDamageShieldOnlySpell(spell_id)) { - correct_type = BotSpellTypes::DamageShields; - } - else { - correct_type = BotSpellTypes::Buff; - } - } - } - - - return correct_type; -} - -uint16 GetPetSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::Buff: - return BotSpellTypes::PetBuffs; - case BotSpellTypes::RegularHeal: - return BotSpellTypes::PetRegularHeals; - case BotSpellTypes::CompleteHeal: - return BotSpellTypes::PetCompleteHeals; - case BotSpellTypes::FastHeals: - return BotSpellTypes::PetFastHeals; - case BotSpellTypes::VeryFastHeals: - return BotSpellTypes::PetVeryFastHeals; - case BotSpellTypes::HoTHeals: - return BotSpellTypes::PetHoTHeals; - case BotSpellTypes::Cure: - return BotSpellTypes::PetCures; - case BotSpellTypes::DamageShields: - return BotSpellTypes::PetDamageShields; - case BotSpellTypes::ResistBuffs: - return BotSpellTypes::PetResistBuffs; - default: - return spell_type; - } - - return spell_type; -} diff --git a/common/spdat.h b/common/spdat.h index 69662eee08..7ee45e7105 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -896,6 +896,7 @@ const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellT const uint32 SPELL_TYPES_BENEFICIAL = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong); const uint32 SPELL_TYPES_INNATE = (SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root); +// Bot related functions bool IsBotSpellTypeDetrimental (uint16 spell_type); bool IsBotSpellTypeBeneficial (uint16 spell_type); bool IsBotSpellTypeOtherBeneficial(uint16 spell_type); @@ -906,13 +907,13 @@ bool IsGroupTargetOnlyBotSpellType(uint16 spell_type); bool IsPetBotSpellType(uint16 spell_type); bool IsClientBotSpellType(uint16 spell_type); bool IsHealBotSpellType(uint16 spell_type); -bool SpellTypeRequiresLoS(uint16 spell_type); -bool SpellTypeRequiresTarget(uint16 spell_type); -bool SpellTypeRequiresAEChecks(uint16 spell_type); -bool IsCommandedSpellType(uint16 spell_type); -bool IsPullingSpellType(uint16 spell_type); -uint16 GetCorrectSpellType(uint16 spell_type, uint16 spell_id); -uint16 GetPetSpellType(uint16 spell_type); +bool BotSpellTypeRequiresLoS(uint16 spell_type); +bool BotSpellTypeRequiresTarget(uint16 spell_type); +bool BotSpellTypeRequiresAEChecks(uint16 spell_type); +bool IsCommandedBotSpellType(uint16 spell_type); +bool IsPullingBotSpellType(uint16 spell_type); +uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id); +uint16 GetPetBotSpellType(uint16 spell_type); // These should not be used to determine spell category.. // They are a graphical affects (effects?) index only diff --git a/common/spdat_bot.cpp b/common/spdat_bot.cpp new file mode 100644 index 0000000000..f02a823b8d --- /dev/null +++ b/common/spdat_bot.cpp @@ -0,0 +1,713 @@ +#include "spdat.h" + +bool IsBotSpellTypeDetrimental(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Root: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Mez: + case BotSpellTypes::Charm: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::HateRedux: + case BotSpellTypes::Fear: + case BotSpellTypes::Stun: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEMez: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Lull: + case BotSpellTypes::AELull: + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: + return true; + default: + return false; + } + + return false; +} + +bool IsBotSpellTypeBeneficial(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::Resurrect: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: + return true; + default: + return false; + } + + return false; +} + +bool IsBotSpellTypeOtherBeneficial(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: + return true; + default: + return false; + } + + return false; +} + +bool IsBotSpellTypeInnate(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::Charm: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + case BotSpellTypes::AEMez: + case BotSpellTypes::Mez: + case BotSpellTypes::Lull: + case BotSpellTypes::AELull: + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: + return true; + default: + return false; + } + + return false; +} + +bool IsAEBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::AEDebuff: + case BotSpellTypes::AEFear: + case BotSpellTypes::AEMez: + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AESlow: + case BotSpellTypes::AESnare: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDispel: + case BotSpellTypes::AEDoT: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AELifetap: + case BotSpellTypes::AERoot: + case BotSpellTypes::AEHateLine: + case BotSpellTypes::AELull: + return true; + default: + return false; + } + + return false; +} + +bool IsGroupBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::GroupCures: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsGroupTargetOnlyBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::GroupCures: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + return true; + default: + return false; + } + + return false; +} + +bool IsPetBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::PetCures: + return true; + default: + return false; + } + + return false; +} + +bool IsClientBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::PetResistBuffs: + return true; + default: + return false; + } + + return false; +} + +bool IsHealBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + return true; + default: + return false; + } + + return false; +} + +bool BotSpellTypeRequiresLoS(uint16 spell_type) { + if (IsAEBotSpellType(spell_type)) { // These gather their own targets later + return false; + } + + switch (spell_type) { + case BotSpellTypes::RegularHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::InCombatBuff: + return false; + default: + return true; + } + + return true; +} + +bool BotSpellTypeRequiresTarget(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::Pet: + case BotSpellTypes::Succor: + return false; + default: + return true; + } + + return true; +} + +bool BotSpellTypeRequiresAEChecks(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::AEMez: + return false; + default: + return true; + } + + return true; +} + +bool RequiresStackCheck(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::GroupCompleteHeals: + return false; + default: + return true; + } + + return true; +} + +bool IsCommandedBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::Charm: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::Resurrect: + case BotSpellTypes::AELull: + case BotSpellTypes::Lull: + case BotSpellTypes::Teleport: + case BotSpellTypes::Succor: + case BotSpellTypes::BindAffinity: + case BotSpellTypes::Identify: + case BotSpellTypes::Levitate: + case BotSpellTypes::Rune: + case BotSpellTypes::WaterBreathing: + case BotSpellTypes::Size: + case BotSpellTypes::Invisibility: + case BotSpellTypes::MovementSpeed: + case BotSpellTypes::SendHome: + case BotSpellTypes::SummonCorpse: + //case BotSpellTypes::Cure: + //case BotSpellTypes::GroupCures: + //case BotSpellTypes::DamageShields: + //case BotSpellTypes::PetDamageShields: + //case BotSpellTypes::ResistBuffs: + //case BotSpellTypes::PetResistBuffs: + return true; + default: + return false; + } + + return false; +} + +bool IsPullingBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::Nuke: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Snare: + case BotSpellTypes::DOT: + case BotSpellTypes::Dispel: + case BotSpellTypes::Slow: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + case BotSpellTypes::HateLine: + return true; + default: + return false; + } + + return false; +} + +uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) { + uint16 correct_type = UINT16_MAX; + SPDat_Spell_Struct spell = spells[spell_id]; + std::string teleport_zone = spell.teleport_zone; + + if (IsCharmSpell(spell_id)) { + correct_type = BotSpellTypes::Charm; + } + else if (IsFearSpell(spell_id)) { + correct_type = BotSpellTypes::Fear; + } + else if (IsEffectInSpell(spell_id, SE_Revive)) { + correct_type = BotSpellTypes::Resurrect; + } + else if (IsHarmonySpell(spell_id)) { + correct_type = BotSpellTypes::Lull; + } + else if ( + teleport_zone.compare("") && + !IsEffectInSpell(spell_id, SE_GateToHomeCity) && + IsBeneficialSpell(spell_id) && + (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Translocate)) + ) { + correct_type = BotSpellTypes::Teleport; + } + else if ( + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_Succor) + ) { + correct_type = BotSpellTypes::Succor; + } + else if (IsEffectInSpell(spell_id, SE_BindAffinity)) { + correct_type = BotSpellTypes::BindAffinity; + } + else if (IsEffectInSpell(spell_id, SE_Identify)) { + correct_type = BotSpellTypes::Identify; + } + else if ( + spell_type == BotSpellTypes::Levitate && + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_Levitate) + ) { + correct_type = BotSpellTypes::Levitate; + } + else if ( + spell_type == BotSpellTypes::Rune && + IsBeneficialSpell(spell_id) && + (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) + ) { + correct_type = BotSpellTypes::Rune; + } + else if ( + spell_type == BotSpellTypes::WaterBreathing && + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_WaterBreathing) + ) { + correct_type = BotSpellTypes::WaterBreathing; + } + else if ( + spell_type == BotSpellTypes::Size && + IsBeneficialSpell(spell_id) && + (IsEffectInSpell(spell_id, SE_ModelSize) || IsEffectInSpell(spell_id, SE_ChangeHeight)) + ) { + correct_type = BotSpellTypes::Size; + } + else if ( + spell_type == BotSpellTypes::Invisibility && + IsBeneficialSpell(spell_id) && + (IsEffectInSpell(spell_id, SE_SeeInvis) || IsInvisibleSpell(spell_id)) + ) { + correct_type = BotSpellTypes::Invisibility; + } + else if ( + spell_type == BotSpellTypes::MovementSpeed && + IsBeneficialSpell(spell_id) && + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) { + correct_type = BotSpellTypes::MovementSpeed; + } + else if ( + !teleport_zone.compare("") && + IsBeneficialSpell(spell_id) && + (IsEffectInSpell(spell_id, SE_Translocate) || IsEffectInSpell(spell_id, SE_GateToHomeCity)) + ) { + correct_type = BotSpellTypes::SendHome; + } + else if (IsEffectInSpell(spell_id, SE_SummonCorpse)) { + correct_type = BotSpellTypes::SummonCorpse; + } + + if (correct_type == UINT16_MAX) { + if ( + IsSummonPetSpell(spell_id) || + IsEffectInSpell(spell_id, SE_TemporaryPets) + ) { + correct_type = BotSpellTypes::Pet; + } + else if (IsMesmerizeSpell(spell_id)) { + correct_type = BotSpellTypes::Mez; + } + else if (IsEscapeSpell(spell_id)) { + correct_type = BotSpellTypes::Escape; + } + else if ( + IsDetrimentalSpell(spell_id) && + IsEffectInSpell(spell_id, SE_Root) + ) { + if (IsAnyAESpell(spell_id)) { + correct_type = BotSpellTypes::AERoot; + } + else { + correct_type = BotSpellTypes::Root; + } + } + else if ( + IsDetrimentalSpell(spell_id) && + IsLifetapSpell(spell_id) + ) { + correct_type = BotSpellTypes::Lifetap; + } + else if ( + IsDetrimentalSpell(spell_id) && + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) { + correct_type = BotSpellTypes::Snare; + } + else if ( + IsDetrimentalSpell(spell_id) && + (IsStackableDOT(spell_id) || IsDamageOverTimeSpell(spell_id)) + ) { + correct_type = BotSpellTypes::DOT; + } + else if (IsDispelSpell(spell_id)) { + correct_type = BotSpellTypes::Dispel; + } + else if ( + IsDetrimentalSpell(spell_id) && + IsSlowSpell(spell_id) + ) { + correct_type = BotSpellTypes::Slow; + } + else if ( + IsDebuffSpell(spell_id) && + !IsHateReduxSpell(spell_id) && + !IsHateSpell(spell_id) + ) { + correct_type = BotSpellTypes::Debuff; + } + else if (IsHateReduxSpell(spell_id)) { + correct_type = BotSpellTypes::HateRedux; + } + else if ( + IsDetrimentalSpell(spell_id) && + IsHateSpell(spell_id) + ) { + correct_type = BotSpellTypes::HateLine; + } + else if ( + IsBuffSpell(spell_id) && + IsBeneficialSpell(spell_id) && + IsBardSong(spell_id) + ) { + if ( + spell_type == BotSpellTypes::InCombatBuffSong || + spell_type == BotSpellTypes::OutOfCombatBuffSong || + spell_type == BotSpellTypes::PreCombatBuffSong + ) { + correct_type = spell_type; + } + else { + correct_type = BotSpellTypes::OutOfCombatBuffSong; + } + } + else if ( + !IsBardSong(spell_id) && + ( + (IsSelfConversionSpell(spell_id) && spell.buff_duration < 1) || + (spell_type == BotSpellTypes::InCombatBuff && IsAnyBuffSpell(spell_id)) + ) + ) { + correct_type = BotSpellTypes::InCombatBuff; + } + else if ( + spell_type == BotSpellTypes::PreCombatBuff && + IsAnyBuffSpell(spell_id) && + !IsBardSong(spell_id) + ) { + correct_type = BotSpellTypes::PreCombatBuff; + } + else if ( + (IsCureSpell(spell_id) && spell_type == BotSpellTypes::Cure) || + (IsCureSpell(spell_id) && !IsAnyHealSpell(spell_id)) + ) { + correct_type = BotSpellTypes::Cure; + } + else if (IsAnyNukeOrStunSpell(spell_id)) { + if (IsAnyAESpell(spell_id)) { + if (IsAERainSpell(spell_id)) { + correct_type = BotSpellTypes::AERains; + } + else if (IsPBAENukeSpell(spell_id)) { + correct_type = BotSpellTypes::PBAENuke; + } + else if (IsStunSpell(spell_id)) { + correct_type = BotSpellTypes::AEStun; + } + else { + correct_type = BotSpellTypes::AENukes; + } + } + else if (IsStunSpell(spell_id)) { + correct_type = BotSpellTypes::Stun; + } + else { + correct_type = BotSpellTypes::Nuke; + } + } + else if (IsAnyHealSpell(spell_id)) { + if (IsGroupSpell(spell_id)) { + if (IsGroupCompleteHealSpell(spell_id)) { + correct_type = BotSpellTypes::GroupCompleteHeals; + } + else if (IsGroupHealOverTimeSpell(spell_id)) { + correct_type = BotSpellTypes::GroupHoTHeals; + } + else if (IsRegularGroupHealSpell(spell_id)) { + correct_type = BotSpellTypes::GroupHeals; + } + } + else { + if (IsVeryFastHealSpell(spell_id)) { + correct_type = BotSpellTypes::VeryFastHeals; + } + else if (IsFastHealSpell(spell_id)) { + correct_type = BotSpellTypes::FastHeals; + } + else if (IsCompleteHealSpell(spell_id)) { + correct_type = BotSpellTypes::CompleteHeal; + } + else if (IsHealOverTimeSpell(spell_id)) { + correct_type = BotSpellTypes::HoTHeals; + } + else if (IsRegularSingleTargetHealSpell(spell_id)) { + correct_type = BotSpellTypes::RegularHeal; + } + else if (IsRegularPetHealSpell(spell_id)) { + correct_type = BotSpellTypes::RegularHeal; + } + } + } + else if (IsAnyBuffSpell(spell_id)) { + if (IsResistanceOnlySpell(spell_id)) { + correct_type = BotSpellTypes::ResistBuffs; + } + else if (IsDamageShieldOnlySpell(spell_id)) { + correct_type = BotSpellTypes::DamageShields; + } + else { + correct_type = BotSpellTypes::Buff; + } + } + } + + + return correct_type; +} + +uint16 GetPetBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::Cure: + return BotSpellTypes::PetCures; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; + default: + return spell_type; + } + + return spell_type; +} diff --git a/zone/bot.cpp b/zone/bot.cpp index 45f2d899fb..7269db225f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9329,7 +9329,7 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { return false; } - if (IsPullingSpell() && IsPullingSpellType(spell_type)) { //Skip remaining checks for commanded + if (IsPullingSpell() && IsPullingBotSpellType(spell_type)) { //Skip remaining checks for commanded return true; } @@ -9472,7 +9472,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (SpellTypeRequiresTarget(spell_type) && !tar) { + if (BotSpellTypeRequiresTarget(spell_type) && !tar) { LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 5d62102aaf..f6fd0f0ebd 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -509,7 +509,7 @@ void bot_command_cast(Client* c, const Seperator* sep) Mob* new_tar = tar; if (!aa_type && !by_spell_id) { - if (!SpellTypeRequiresTarget(spell_type)) { + if (!BotSpellTypeRequiresTarget(spell_type)) { new_tar = bot_iter; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 53d2511357..7cffb07c9b 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -64,7 +64,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ bot_spell.SpellIndex = 0; bot_spell.ManaCost = 0; - if (SpellTypeRequiresLoS(spell_type) && tar != this) { + if (BotSpellTypeRequiresLoS(spell_type) && tar != this) { SetHasLoS(DoLosChecks(this, tar)); } else { @@ -644,7 +644,7 @@ bool Bot::AI_PursueCastCheck() { continue; } - if (IsCommandedSpellType(current_cast.spellType)) { // Unsupported by AI currently. + if (IsCommandedBotSpellType(current_cast.spellType)) { // Unsupported by AI currently. continue; } @@ -716,7 +716,7 @@ bool Bot::AI_IdleCastCheck() { continue; } - if (IsCommandedSpellType(current_cast.spellType)) { // Unsupported by AI currently. + if (IsCommandedBotSpellType(current_cast.spellType)) { // Unsupported by AI currently. continue; } @@ -780,7 +780,7 @@ bool Bot::AI_EngagedCastCheck() { continue; } - if (IsCommandedSpellType(current_cast.spellType)) { // Unsupported by AI currently. + if (IsCommandedBotSpellType(current_cast.spellType)) { // Unsupported by AI currently. continue; } @@ -1071,7 +1071,7 @@ std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* cas caster->IsCommandedSpell() || !AE || ( - SpellTypeRequiresAEChecks(spell_type) && + BotSpellTypeRequiresAEChecks(spell_type) && caster->HasValidAETarget(caster, bot_spell_list[i].spellid, spell_type, tar) ) ) { @@ -2957,7 +2957,7 @@ void Bot::CheckBotSpells() { } } - correct_type = GetCorrectSpellType(s.type, spell_id); + correct_type = GetCorrectBotSpellType(s.type, spell_id); parent_type = GetParentSpellType(correct_type); if (RuleB(Bots, UseParentSpellTypeForChecks)) { @@ -2967,7 +2967,7 @@ void Bot::CheckBotSpells() { } else { if (IsPetBotSpellType(s.type)) { - correct_type = GetPetSpellType(correct_type); + correct_type = GetPetBotSpellType(correct_type); } } diff --git a/zone/mob.h b/zone/mob.h index ed358e79ec..b9b72d63e1 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -470,7 +470,7 @@ class Mob : public Entity { uint8 GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar); uint8 GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar); - uint16 GetPetSpellType(uint16 spell_type); + uint16 GetPetBotSpellType(uint16 spell_type); void DisableBotSpellTimers(); void StartBotSpellTimers(); diff --git a/zone/mob_bot.cpp b/zone/mob_bot.cpp index 4dafd49350..a71fa2ebfa 100644 --- a/zone/mob_bot.cpp +++ b/zone/mob_bot.cpp @@ -618,7 +618,7 @@ bool Mob::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellHold(GetPetSpellType(spell_type)); + return tar->GetOwner()->GetSpellHold(GetPetBotSpellType(spell_type)); } return GetSpellHold(spell_type); @@ -630,7 +630,7 @@ uint16 Mob::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellDelay(GetPetSpellType(spell_type)); + return tar->GetOwner()->GetSpellDelay(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { @@ -646,7 +646,7 @@ bool Mob::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->SpellTypeRecastCheck(GetPetSpellType(spell_type)); + return tar->GetOwner()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { @@ -662,7 +662,7 @@ uint8 Mob::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMinThreshold(GetPetSpellType(spell_type)); + return tar->GetOwner()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { @@ -678,7 +678,7 @@ uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMaxThreshold(GetPetSpellType(spell_type)); + return tar->GetOwner()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { @@ -688,7 +688,7 @@ uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { return GetSpellMaxThreshold(spell_type); } -uint16 Mob::GetPetSpellType(uint16 spell_type) { +uint16 Mob::GetPetBotSpellType(uint16 spell_type) { switch (spell_type) { case BotSpellTypes::VeryFastHeals: return BotSpellTypes::PetVeryFastHeals; From e9d809301ccdc7d8d5872ef9b6c8109e64ccc284 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:11:13 -0600 Subject: [PATCH 291/394] Move SendCommandHelpWindow to SendBotCommandHelpWindow and simplify --- zone/bot_commands/actionable.cpp | 34 +--- zone/bot_commands/behind_mob.cpp | 67 ++----- zone/bot_commands/blocked_buffs.cpp | 129 ++++-------- zone/bot_commands/bot.cpp | 194 ++++++------------- zone/bot_commands/bot_settings.cpp | 1 - zone/bot_commands/cast.cpp | 62 +++--- zone/bot_commands/copy_settings.cpp | 53 ++--- zone/bot_commands/default_settings.cpp | 60 ++---- zone/bot_commands/depart.cpp | 52 ++--- zone/bot_commands/discipline.cpp | 48 ++--- zone/bot_commands/distance_ranged.cpp | 56 +++++- zone/bot_commands/follow.cpp | 51 ++--- zone/bot_commands/illusion_block.cpp | 52 ++--- zone/bot_commands/max_melee_range.cpp | 52 ++--- zone/bot_commands/pet.cpp | 47 +---- zone/bot_commands/set_assistee.cpp | 41 ++-- zone/bot_commands/sit_hp_percent.cpp | 52 ++--- zone/bot_commands/sit_in_combat.cpp | 50 ++--- zone/bot_commands/sit_mana_percent.cpp | 52 ++--- zone/bot_commands/spell_aggro_checks.cpp | 40 +--- zone/bot_commands/spell_delays.cpp | 42 ++-- zone/bot_commands/spell_engaged_priority.cpp | 42 ++-- zone/bot_commands/spell_holds.cpp | 44 +---- zone/bot_commands/spell_idle_priority.cpp | 42 ++-- zone/bot_commands/spell_max_hp_pct.cpp | 45 ++--- zone/bot_commands/spell_max_mana_pct.cpp | 49 ++--- zone/bot_commands/spell_max_thresholds.cpp | 42 ++-- zone/bot_commands/spell_min_hp_pct.cpp | 43 ++-- zone/bot_commands/spell_min_mana_pct.cpp | 43 ++-- zone/bot_commands/spell_min_thresholds.cpp | 44 ++--- zone/bot_commands/spell_pursue_priority.cpp | 42 ++-- zone/bot_commands/spell_target_count.cpp | 41 +--- zone/bot_commands/taunt.cpp | 47 +---- zone/client.cpp | 81 +++----- zone/client.h | 24 ++- zone/gm_commands/spell_delays.cpp | 35 +--- zone/gm_commands/spell_holds.cpp | 35 +--- zone/gm_commands/spell_max_thresholds.cpp | 35 +--- zone/gm_commands/spell_min_thresholds.cpp | 35 +--- 39 files changed, 598 insertions(+), 1406 deletions(-) diff --git a/zone/bot_commands/actionable.cpp b/zone/bot_commands/actionable.cpp index f66a5b60a2..94ed0b0c3b 100644 --- a/zone/bot_commands/actionable.cpp +++ b/zone/bot_commands/actionable.cpp @@ -4,16 +4,14 @@ void bot_command_actionable(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_actionable", sep->arg[0], "actionable")) { c->Message(Chat::White, "note: Lists actionable command arguments and use descriptions"); + return; } - std::vector description = - { - "Lists actionable command arguments and use descriptions." - }; + BotCommandHelpParams p; - std::vector notes = - { + p.description = { "Lists actionable command arguments and use descriptions." }; + p.notes = { "[target] - uses the command on the target. Some commands will default to target if no actionable is selected.", "[byname] [name] - selects a bot by name their name.", "[ownergroup] - selects all bots in the owner's group.", @@ -32,29 +30,7 @@ void bot_command_actionable(Client* c, const Seperator* sep) "You may only select your own bots." }; - std::vector example_format = { }; - std::vector examples_one = { }; - std::vector examples_two = { }; - std::vector examples_three = { }; - - std::vector actionables = { }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 6bbfdd1687..5de7571f1b 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -3,67 +3,36 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_behind_mob", sep->arg[0], "behindmob")) { + c->Message(Chat::White, "note: Toggles whether or not bots will stay behind the mob during combat."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "-Toggles whether or not bots will stay behind the mob during combat." - }; - - std::vector notes = { }; + BotCommandHelpParams p; - std::vector example_format = - { - fmt::format( - "{} [value] [actionable]" - , sep->arg[0] - ) + p.description = { "Toggles whether or not bots will stay behind the mob during combat." }; + p.example_format = { + fmt::format("{} [value] [actionable]", sep->arg[0]) }; - - std::vector examples_one = - { - "To set Monks to stay behind the mob:", - fmt::format( - "{} 1 byclass 7", - sep->arg[0] - ) + p.examples_one = { + "To set Monks to stay behind the mob:", + fmt::format("{} 1 byclass 7", sep->arg[0]) }; - std::vector examples_two = { }; - std::vector examples_three = - { - "To check the behind mob status for all bots:", - fmt::format( - "{} current spawned", - sep->arg[0] - ) + p.examples_two = { + "To force all bots to stay behind mobs:", + fmt::format("{} 1 spawned", sep->arg[0]) }; - - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + p.examples_three = { + "To check the behind mob status of all bots:", + fmt::format("{} current spawned", sep->arg[0]) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); - c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index ebe50f5ac6..8cb5396044 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -8,72 +8,35 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) return; } + if (helper_command_alias_fail(c, "bot_command_blocked_buffs", sep->arg[0], "blockedbuffs")) { + c->Message(Chat::White, "note: Allows you to set, view and wipe blocked buffs for the selected bots."); + + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Allows you to set, view and wipe blocked buffs for the selected bots" - }; + BotCommandHelpParams p; - std::vector notes = - { - "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list." + p.description = { "Allows you to set, view and wipe blocked buffs for the selected bots." }; + p.notes = { "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list." }; + p.example_format = { + fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) }; - - std::vector example_format = - { - fmt::format( - "{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]" - , sep->arg[0] - ) + p.examples_one = { + "To add Courage(Spell ID #202) to the targeted bot's blocked list:", + fmt::format("{} add 202", sep->arg[0]) }; - std::vector examples_one = - { - "To add Courage (Spell ID #202) to the targeted bot's blocked list:", - fmt::format( - "{} add 202", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) - ) + p.examples_two = { + "To view the targeted bot's blocked buff list:", + fmt::format("{} list", sep->arg[0]) }; - std::vector examples_two = - { - "To view the targeted bot's blocked buff list:", - fmt::format( - "{} list", - sep->arg[0] - ) + p.examples_three = { + "To wipe all Warriors bots' blocked buff list:", + fmt::format( "{} wipe byclass {}", sep->arg[0], Class::Warrior) }; - std::vector examples_three = - { - "To wipe all Warriors bots' blocked buff list:", - fmt::format( - "{} wipe byclass {}", - sep->arg[0], - Class::Warrior - ) - }; - - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); @@ -265,26 +228,29 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) return; } + if (helper_command_alias_fail(c, "bot_command_blocked_pet_buffs", sep->arg[0], "blockedpetbuffs")) { + c->Message(Chat::White, "note: Allows you to set, view and wipe blocked pet buffs for the selected bots."); + + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Allows you to set, view and wipe blocked pet buffs for the selected bots" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Allows you to set, view and wipe blocked pet buffs for the selected bots." }; + p.notes = { "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list.", - "- This controls whether or not any pet the selected bot(s) own will prevent certain buffs from being cast." + "- This controls whether or not any pet the selected bot(s) own will prevent certain beneficial buffs from landing on them." }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]" , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To add Courage (Spell ID #202) to the targeted bot's blocked list:", fmt::format( @@ -293,7 +259,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ) }; - std::vector examples_two = + p.examples_two = { "To view the targeted bot's blocked buff list:", fmt::format( @@ -301,7 +267,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_three = + p.examples_three = { "To wipe all Warriors bots' blocked buff list:", fmt::format( @@ -310,28 +276,9 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) Class::Warrior ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index ab5e036d3e..e91c5eb324 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -472,82 +472,43 @@ void bot_command_delete(Client *c, const Seperator *sep) void bot_command_follow_distance(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_command_follow_distance", sep->arg[0], "botfollowdistance")) { + c->Message(Chat::White, "note: Sets or resets the follow distance of the selected bots."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Sets or resets the follow distance of the selected bots." - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Sets or resets the follow distance of the selected bots." }; + p.notes = { - fmt::format( - "[Default]: {}", - RuleI(Bots, MaxFollowDistance) - ), - - fmt::format( - "- You must use a value between 1 and {}.", - RuleI(Bots, MaxFollowDistance) - ) + fmt::format("[Default]: {}", RuleI(Bots, MaxFollowDistance)), + fmt::format("- You must use a value between 1 and {}.", RuleI(Bots, MaxFollowDistance)) }; - - std::vector example_format = - { - fmt::format( - "{} [reset]/[set [value]] [actionable]" - , sep->arg[0] - ) + p.example_format = { + fmt::format("{} [reset]/[set [value]] [actionable]", sep->arg[0]) }; - std::vector examples_one = - { - "To set all bots to follow at a distance of 25:", - fmt::format( - "{} set 25 spawned", - sep->arg[0] - ) + p.examples_one = { + "To set all bots to follow at a distance of 25:", + fmt::format("{} set 25 spawned", sep->arg[0]) }; - std::vector examples_two = - { - "To check the curret following distance of all bots:", - fmt::format( - "{} current spawned", - sep->arg[0] - ) + p.examples_two = { + "To check the curret following distance of all bots:", + fmt::format("{} current spawned", sep->arg[0]) }; - std::vector examples_three = + p.examples_three = { "To reset the following distance of all Wizards:", fmt::format( "{} reset byclass {}", sep->arg[0], Class::Wizard - ) - }; - - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" + ) }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); @@ -1187,17 +1148,16 @@ void bot_command_spawn(Client *c, const Seperator *sep) void bot_command_stance(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_command_stance", sep->arg[0], "botstance")) { + c->Message(Chat::White, "note: Change a bot's stance to control the way it behaves."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Change a bot's stance to control the way it behaves." - }; + BotCommandHelpParams p; - std::vector notes = - { + p.description = { "Change a bot's stance to control the way it behaves." }; + p.notes = { "- Changing a stance will reset all settings to match that stance type.", "- Any changes made will only save to that stance for future use.", fmt::format( @@ -1225,102 +1185,74 @@ void bot_command_stance(Client *c, const Seperator *sep) Stance::AEBurn ), "
", - fmt::format( - "- {} (#{}) [Default] - Overall balance and casts most spell types by default.", - Stance::GetName(Stance::Balanced), - Stance::Balanced + fmt::format( + "- {} (#{}) [Default] - Overall balance and casts most spell types by default.", + Stance::GetName(Stance::Balanced), + Stance::Balanced ), - fmt::format( - "- {} (#{}) - Idle. Does not cast or engage in combat.", - Stance::GetName(Stance::Passive), - Stance::Passive + fmt::format( + "- {} (#{}) - Idle. Does not cast or engage in combat.", + Stance::GetName(Stance::Passive), + Stance::Passive ), - fmt::format( - "- {} (#{}) - More mana and aggro efficient (SKs will still cast hate line). Longer delays between detrimental spells, thresholds adjusted to cast less often.", - Stance::GetName(Stance::Efficient), - Stance::Efficient + fmt::format( + "- {} (#{}) - More mana and aggro efficient (SKs will still cast hate line). Longer delays between detrimental spells, thresholds adjusted to cast less often.", + Stance::GetName(Stance::Efficient), + Stance::Efficient ), - fmt::format( - "- {} (#{}) - Much more aggressive in their cast times and thresholds. More DPS, debuffs and slow but a higher risk of snagging aggro.", - Stance::GetName(Stance::Aggressive), - Stance::Aggressive + fmt::format( + "- {} (#{}) - Much more aggressive in their cast times and thresholds. More DPS, debuffs and slow but a higher risk of snagging aggro.", + Stance::GetName(Stance::Aggressive), + Stance::Aggressive ), fmt::format( - "- {} (#{}) - Support role. Most offensive spell types are disabled. Focused on heals, cures, CC, debuffs and slows.", - Stance::GetName(Stance::Assist), - Stance::Assist + "- {} (#{}) - Support role. Most offensive spell types are disabled. Focused on heals, cures, CC, debuffs and slows.", + Stance::GetName(Stance::Assist), + Stance::Assist ), fmt::format( - "- {} (#{}) - Murder. Doesn't care about aggro, just wants to kill. DPS Machine.", - Stance::GetName(Stance::Burn), - Stance::Burn + "- {} (#{}) - Murder. Doesn't care about aggro, just wants to kill. DPS Machine.", + Stance::GetName(Stance::Burn), + Stance::Burn ), fmt::format( - "- {} (#{}) - Murder EVERYTHING. Doesn't care about aggro, casts AEs. Everything must die ASAP.", - Stance::GetName(Stance::AEBurn), - Stance::AEBurn + "- {} (#{}) - Murder EVERYTHING. Doesn't care about aggro, casts AEs. Everything must die ASAP.", + Stance::GetName(Stance::AEBurn), + Stance::AEBurn ) }; - std::vector example_format = - { - fmt::format( - "{} [current | value: {}-{}]", + p.example_format = { + fmt::format( "{} [current | value: {}-{}]", sep->arg[0], Stance::Passive, Stance::AEBurn ) }; - std::vector examples_one = - { - "To set all bots to BurnAE:", - fmt::format( - "{} {} spawned {}", + p.examples_one = { + "To set all bots to BurnAE:", + fmt::format("{} {} spawned {}", sep->arg[0], Stance::Aggressive, Class::ShadowKnight ) }; - std::vector examples_two = - { - "To set all Shadowknights to Aggressive:", - fmt::format( - "{} {} byclass {}", + p.examples_two = { + "To set all Shadowknights to Aggressive:", + fmt::format("{} {} byclass {}", sep->arg[0], Stance::Aggressive, Class::ShadowKnight ) }; - std::vector examples_three = - { - "To check the current stances of all bots:", - fmt::format( - "{} current spawned", - sep->arg[0] - ) + p.examples_three = { + "To check the current stances of all bots:", + fmt::format("{} current spawned", sep->arg[0]) }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp index 758916ca03..8552afc36f 100644 --- a/zone/bot_commands/bot_settings.cpp +++ b/zone/bot_commands/bot_settings.cpp @@ -19,7 +19,6 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) "sithppercent", "sitincombat", "sitmanapercent", - "sithppercent", "spellaggrocheck", "spelldelays", "spellengagedpriority", diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index f6fd0f0ebd..74176788ac 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -2,13 +2,17 @@ void bot_command_cast(Client* c, const Seperator* sep) { + if (helper_command_alias_fail(c, "bot_command_cast", sep->arg[0], "cast")) { + c->Message(Chat::White, "note: Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc)."); + + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc)" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Commands bots to force cast a specific spell type, ignoring all settings (holds, delays, thresholds, etc)." }; + p.notes = { "- This will interrupt any spell currently being cast by bots told to use the command", "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells", @@ -19,19 +23,12 @@ void bot_command_cast(Client* c, const Seperator* sep) , sep->arg[0] ) }; - - std::vector example_format = + p.example_format = { - fmt::format( - "{} [Type Shortname] [actionable, default: spawned]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [actionable, default: spawned]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [actionable, default: spawned]", sep->arg[0]), + fmt::format("{} [Type ID] [actionable, default: spawned]", sep->arg[0]) }; - std::vector examples_one = + p.examples_one = { "To tell everyone to Nuke the target:", fmt::format( @@ -45,7 +42,7 @@ void bot_command_cast(Client* c, const Seperator* sep) BotSpellTypes::Nuke ) }; - std::vector examples_two = + p.examples_two = { "To tell Skbot to Harm Touch the target:", fmt::format( @@ -57,7 +54,7 @@ void bot_command_cast(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_three = + p.examples_three = { "To tell all bots to try to cast spell #93 (Burst of Flame)", fmt::format( @@ -65,28 +62,10 @@ void bot_command_cast(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); @@ -609,7 +588,12 @@ void bot_command_cast(Client* c, const Seperator* sep) type = "Forced"; } else { - type = c->GetSpellTypeNameByID(spell_type); + if (sub_type == UINT16_MAX) { + type = c->GetSpellTypeNameByID(spell_type); + } + else { + type = c->GetSubTypeNameByID(sub_type); + } } if (!is_success) { diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 1b250b80bc..058385c3b5 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -3,28 +3,30 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_copy_settings", sep->arg[0], "copysettings")) { + c->Message(Chat::White, "note: Copies settings from one bot to another."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = + BotCommandHelpParams p; + + p.description = { - "Copies settings from one bot to another bot" + "Copies settings from one bot to another." }; - - std::vector notes = + p.notes = { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [from] [to] [option]" , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To copy all settings from BotA to BotB:", fmt::format( @@ -32,7 +34,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_two = + p.examples_two = { "To copy only Nuke spelltypesettings from BotA to BotB:", fmt::format( @@ -46,45 +48,22 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), }; - std::vector examples_three = { }; - - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = - { - "[all], [misc], [spellsettings], [spelltypesettings], [spellholds], [spelldelays], [spellminthresholds], [spellmaxthresholds], [spellminmanapct], [spellmaxmanapct], [spellminhppct], [spellmaxhppct], [spellidlepriority], [spellengagedpriority], [spellpursuepriority], [spellaggrochecks], [spelltargetcounts], [sithppercent], [sitmanapercent], [blockedbuffs], [blockedpetbuffs]" - }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + p.options = { "[all], [misc], [spellsettings], [spelltypesettings], [spellholds], [spelldelays], [spellminthresholds], [spellmaxthresholds], [spellminmanapct], [spellmaxmanapct], [spellminhppct], [spellmaxhppct], [spellidlepriority], [spellengagedpriority], [spellpursuepriority], [spellaggrochecks], [spelltargetcounts], [sithppercent], [sitmanapercent], [blockedbuffs], [blockedpetbuffs]" }; std::vector options_one = { "[spellsettings] will copy ^spellsettings options", "[spelltypesettings] copies all spell type settings", - "[all] copies all settings" + "[all] copies all settings" }; std::vector options_two = { "[misc] copies all miscellaneous options such as:", - "- ^showhelm, ^followd, ^stopmeleelevel, ^enforcespellsettings, ^bottoggleranged, ^petsettype, ^behindmob, ^distanceranged, ^illusionblock, ^sitincombat, ^sithppercent, ^sitmanapercent, ^blockedbuffs, ^blockedpetbuffs", - + "- ^showhelm, ^followd, ^stopmeleelevel, ^enforcespellsettings, ^bottoggleranged, ^petsettype, ^behindmob, ^distanceranged, ^illusionblock, ^sitincombat, ^sithppercent, ^sitmanapercent, ^blockedbuffs, ^blockedpetbuffs" }; - std::vector options_three = - { - "The remaining options copy that specific type" - }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + std::vector options_three = { "The remaining options copy that specific type" }; + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index e5cd77b00e..de09a2ee2c 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -3,28 +3,21 @@ void bot_command_default_settings(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_default_settings", sep->arg[0], "defaultsettings")) { + c->Message(Chat::White, "note: Restores a bot's setting(s) to defaults."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Restores a bot's setting(s) to defaults" - }; - - std::vector notes = - { - "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" - }; + BotCommandHelpParams p; - std::vector example_format = + p.description = { "Restores a bot's setting(s) to defaults" }; + p.notes = { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only"}; + p.example_format = { - fmt::format( - "{} [option] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [option] [actionable]", sep->arg[0]) }; - std::vector examples_one = + p.examples_one = { "To restore delays for Clerics:", fmt::format( @@ -32,7 +25,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_two = + p.examples_two = { "To restore only Snare delays for BotA:", fmt::format( @@ -46,48 +39,25 @@ void bot_command_default_settings(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_three = { }; - - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = - { - "all, misc, spellsettings, spelltypesettings, holds, delays, minthresholds, maxthresholds minmanapct, maxmanapct, minhppct, maxhppct, idlepriority, engagedpriority, pursuepriority, aggrocheck, targetcounts" - }; - std::vector options_one = + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + p.options = { "all, misc, spellsettings, spelltypesettings, holds, delays, minthresholds, maxthresholds minmanapct, maxmanapct, minhppct, maxhppct, idlepriority, engagedpriority, pursuepriority, aggrocheck, targetcounts" }; + p.options_one = { "[spellsettings] will restore ^spellsettings options", "[spelltypesettings] restores all spell type settings", "[all] restores all settings" }; - std::vector options_two = + p.options_two = { "[misc] restores all miscellaneous options such as:", "- ^showhelm, ^followd, ^stopmeleelevel", "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", "- ^behindmob, ^distanceranged, ^illusionblock", "- ^sitincombat, ^sithppercent and ^sitmanapercent", - - }; - std::vector options_three = - { - "The remaining options restore that specific type" }; + p.options_three = { "The remaining options restore that specific type" }; - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 06dc59894c..39cb1bffdd 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -2,26 +2,23 @@ void bot_command_depart(Client* c, const Seperator* sep) { + if (helper_command_alias_fail(c, "bot_command_depart", sep->arg[0], "depart")) { + c->Message(Chat::White, "note: Tells bots to list their port locations or port to a specific location."); + + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Tells bots to list their port locations or port to a specific location" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Tells bots to list their port locations or port to a specific location." }; + p.notes = { "- This will interrupt any spell currently being cast by bots told to use the command.", "- Bots will still check to see if they have the spell in their spell list, whether the target is immune, spell is allowed and all other sanity checks for spells" }; - - std::vector example_format = - { - fmt::format( - "{} [list | zone shortname] [optional: single | group | ae] [actionable, default: spawned]" - , sep->arg[0] - ) - }; - std::vector examples_one = + p.example_format = { fmt::format("{} [list | zone shortname] [optional: single | group | ae] [actionable, default: spawned]", sep->arg[0]) }; + p.examples_one = { "To tell everyone to list their portable locations:", fmt::format( @@ -29,7 +26,7 @@ void bot_command_depart(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_two = + p.examples_two = { "To tell all bots to port to Nexus:", fmt::format( @@ -37,7 +34,7 @@ void bot_command_depart(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_three = + p.examples_three = { "To tell Druidbot to single target port to Butcher:", fmt::format( @@ -45,28 +42,9 @@ void bot_command_depart(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/discipline.cpp b/zone/bot_commands/discipline.cpp index 0a7f8ad491..0d372bf063 100644 --- a/zone/bot_commands/discipline.cpp +++ b/zone/bot_commands/discipline.cpp @@ -3,25 +3,24 @@ void bot_command_discipline(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_discipline", sep->arg[0], "discipline")) { + c->Message(Chat::White, "note: Tells applicable bots to use the specified disciplines."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = + BotCommandHelpParams p; + + p.description = { - "Tells applicable bots to use the specified disciplines" + "Tells applicable bots to use the specified disciplines." }; - - std::vector notes = { }; - - std::vector example_format = + p.notes = { "Aside from Lay On Hands and Harm Touch, you will need to know the spell ID of the discipline to tell a bot to attempt to use it." }; + p.example_format = { - fmt::format( - "{} [aggressive | defensive | spell ID] [actionable, default: spawned]" - , sep->arg[0] - ) + fmt::format("{} [aggressive | defensive | spell ID] [actionable, default: spawned]", sep->arg[0]) }; - std::vector examples_one = + p.examples_one = { "To tell all bots to use an aggressive discipline:", fmt::format( @@ -29,7 +28,7 @@ void bot_command_discipline(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_two = + p.examples_two = { "To tell Warrior bots to use a defensive discipline:", fmt::format( @@ -38,7 +37,7 @@ void bot_command_discipline(Client* c, const Seperator* sep) Class::Warrior ) }; - std::vector examples_three = + p.examples_three = { "To tell all bots to use their Fearless discipline:", fmt::format( @@ -46,28 +45,9 @@ void bot_command_discipline(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/distance_ranged.cpp b/zone/bot_commands/distance_ranged.cpp index 35db3ac759..7a3d2108b8 100644 --- a/zone/bot_commands/distance_ranged.cpp +++ b/zone/bot_commands/distance_ranged.cpp @@ -3,16 +3,70 @@ void bot_command_distance_ranged(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_distance_ranged", sep->arg[0], "distanceranged")) { + c->Message(Chat::White, "note: Sets the distance bots will attempt to stay away from their target to cast or use ranged items."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { + BotCommandHelpParams p; + + p.description = { "Sets the distance bots will attempt to stay away from their target to cast or use ranged items." }; + p.notes = + { + "- Bots will stay between half the value of the setting and the current value. IE, if set to 60, bots will stay between 30 and 60.", + "- Casters will never go closer than their maximum melee range.", + "- Throwing bots will never get closer than the minimum value for ranged to work, or beyond the range of their items." + }; + p.example_format = { + fmt::format("{} [value] [actionable]", sep->arg[0]) + }; + p.examples_one = { + "To set Wizards to a range of 100:", + fmt::format( + "{} 100 byclass {}", + sep->arg[0], + Class::Wizard + ) + }; + p.examples_two = { + "To set Rangers to a range of 175:", + fmt::format( + "{} 175 byclass {}", + sep->arg[0], + Class::Ranger + ) + }; + p.examples_three = { + "To view the current setting of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + + return; c->Message(Chat::White, "usage: %s [current | value: 0 - 300] ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); c->Message(Chat::White, "note: Use [current] to check the current setting."); c->Message(Chat::White, "note: Set the value to the minimum distance you want your bot to try to remain from its target."); c->Message(Chat::White, "note: If they are too far for a spell, it will be skipped."); c->Message(Chat::White, "note: This is set to (90) units by default."); - return; } const int ab_mask = ActionableBots::ABM_Type1; diff --git a/zone/bot_commands/follow.cpp b/zone/bot_commands/follow.cpp index 5996706d64..fe780d32eb 100644 --- a/zone/bot_commands/follow.cpp +++ b/zone/bot_commands/follow.cpp @@ -3,28 +3,18 @@ void bot_command_follow(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_follow", sep->arg[0], "follow")) { + c->Message(Chat::White, "note: Sets bots of your choosing to follow your target, view their current following state or reset their following state."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Sets bots of your choosing to follow your target, view their current following state or reset their following state." - }; - - std::vector notes = - { - "- You can only follow players, bots or mercenaries belonging to your group or raid." - }; + BotCommandHelpParams p; - std::vector example_format = - { - fmt::format( - "{} [optional] [actionable]" - , sep->arg[0] - ) - }; - std::vector examples_one = + p.description = { "Sets bots of your choosing to follow your target, view their current following state or reset their following state." }; + p.notes = { "- You can only follow players, bots or mercenaries belonging to your group or raid." }; + p.example_format = { fmt::format("{} [optional] [actionable]", sep->arg[0]) }; + p.examples_one = { "To set all Clerics to follow your target:", fmt::format( @@ -33,7 +23,7 @@ void bot_command_follow(Client* c, const Seperator* sep) Class::Cleric ) }; - std::vector examples_two = + p.examples_two = { "To check the current state of all bots:", fmt::format( @@ -41,7 +31,7 @@ void bot_command_follow(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_three = + p.examples_three = { "To reset all bots:", fmt::format( @@ -49,28 +39,9 @@ void bot_command_follow(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index cb87313d25..f38b72d92a 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -3,34 +3,33 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_illusion_block", sep->arg[0], "illusionblock")) { + c->Message(Chat::White, "note: Toggles whether or not bots will block the illusion effects of spells cast by players or bots."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Toggles whether or not bots will block the illusion effects of spells cast by players or bots." - }; - - std::vector notes = { }; + BotCommandHelpParams p; - std::vector example_format = + p.description = { "Toggles whether or not bots will block the illusion effects of spells cast by players or bots." }; + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = { + "To set BotA to block illusions:", fmt::format( - "{} [value] [actionable]" - , sep->arg[0] + "{} 1 byname BotA", + sep->arg[0] ) }; - std::vector examples_one = - { - "To set BotA to block illusions:", + p.examples_two = + { + "To set all bots to block illusions:", fmt::format( - "{} 1 byname BotA", + "{} 1 spawned", sep->arg[0] ) }; - std::vector examples_two = { }; - std::vector examples_three = + p.examples_three = { "To check the illusion block status for all bots:", fmt::format( @@ -38,28 +37,9 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/max_melee_range.cpp b/zone/bot_commands/max_melee_range.cpp index adb00f37d5..54749c47ca 100644 --- a/zone/bot_commands/max_melee_range.cpp +++ b/zone/bot_commands/max_melee_range.cpp @@ -3,34 +3,33 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_max_melee_range", sep->arg[0], "maxmeleerange")) { + c->Message(Chat::White, "note: Toggles whether or not bots will stay at max melee range during combat."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Toggles whether or not bots will stay at max melee range during combat." - }; - - std::vector notes = { }; - - std::vector example_format = + BotCommandHelpParams p; + + p.description = { "Toggles whether or not bots will stay at max melee range during combat."}; + p.example_format ={ fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = { + "To set BotA to stay at max melee range:", fmt::format( - "{} [value] [actionable]" - , sep->arg[0] + "{} 1 byname BotA", + sep->arg[0] ) }; - std::vector examples_one = + p.examples_two = { - "To set BotA to stay at max melee range:", + "To set all bots to stay at max melee range:", fmt::format( - "{} 1 byname BotA", + "{} 1 spawned", sep->arg[0] ) }; - std::vector examples_two = { }; - std::vector examples_three = + p.examples_three = { "To check the max melee range status for all bots:", fmt::format( @@ -38,28 +37,9 @@ void bot_command_max_melee_range(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index 5406d71321..04889f1cfd 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -97,25 +97,17 @@ void bot_command_pet_remove(Client *c, const Seperator *sep) void bot_command_pet_set_type(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_command_pet_set_type", sep->arg[0], "petsettype")) { + c->Message(Chat::White, "note: Allows you to change the type of pet Magician bots will cast."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Allows you to change the type of pet Magician bots will cast" - }; - - std::vector notes = {}; + BotCommandHelpParams p; - std::vector example_format = - { - fmt::format( - "{} [current | water | fire | air | earth | monster | epic] [actionable, default: target]" - , sep->arg[0] - ) - }; - std::vector examples_one = + p.description = { "Allows you to change the type of pet Magician bots will cast." }; + p.example_format = { fmt::format("{} [current | water | fire | air | earth | monster | epic] [actionable, default: target]", sep->arg[0]) }; + p.examples_one = { "To set all spawned bots to use Water pets:", fmt::format( @@ -123,7 +115,7 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) sep->arg[0] ) }; - std::vector examples_two = + p.examples_two = { "To set Magelulz to use Fire pets:", fmt::format( @@ -131,7 +123,7 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) sep->arg[0] ) }; - std::vector examples_three = + p.examples_three = { "To check the current pet type for all bots:", fmt::format( @@ -139,28 +131,9 @@ void bot_command_pet_set_type(Client *c, const Seperator *sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/set_assistee.cpp b/zone/bot_commands/set_assistee.cpp index d2be953e27..f5a1845fea 100644 --- a/zone/bot_commands/set_assistee.cpp +++ b/zone/bot_commands/set_assistee.cpp @@ -2,41 +2,24 @@ void bot_command_set_assistee(Client* c, const Seperator* sep) { + if (helper_command_alias_fail(c, "bot_command_set_assistee", sep->arg[0], "setassistee")) { + c->Message(Chat::White, "note: Sets your bots to assist your target in addition to yourself."); + + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Sets your bots to assist your target in addition to yourself" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Sets your bots to assist your target in addition to yourself." }; + p.notes = { "- Your target must be another player in your group or raid.", - "- This needs to be set on every zone/camp you do." + "- This needs to be set on every zone/camp you do.", + "- If a Raid or Group assist is set and you do not want your bots to auto assist that person, set yourself as the assistee." }; - std::vector example_format = { }; - std::vector examples_one = { }; - std::vector examples_two = { }; - std::vector examples_three = { }; - - std::vector actionables = { }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index a147057f5f..0c42081913 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -3,34 +3,33 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_sit_hp_percent", sep->arg[0], "sithppercent")) { + c->Message(Chat::White, "note: HP % threshold when bots will sit in combat if allowed."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "HP % threshold when bots will sit in combat if allowed." - }; - - std::vector notes = { }; + BotCommandHelpParams p; - std::vector example_format = + p.description = { "HP % threshold when bots will sit in combat if allowed." }; + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = { + "To set Clerics to sit at 45% HP:", fmt::format( - "{} [value] [actionable]" - , sep->arg[0] + "{} 45 byclass 2", + sep->arg[0] ) }; - std::vector examples_one = - { - "To set Clerics to sit at 45% HP:", + p.examples_two = + { + "To set all bots to sit at 50% HP:", fmt::format( - "{} 45 byclass 2", + "{} 50 spawned", sep->arg[0] ) }; - std::vector examples_two = { }; - std::vector examples_three = + p.examples_three = { "To check the HP threshold for all bots:", fmt::format( @@ -38,28 +37,9 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index 86d8896b02..cfe868662a 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -3,34 +3,33 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_sit_in_combat", sep->arg[0], "sitincombat")) { + c->Message(Chat::White, "note: Toggles whether or not bots will sit in combat to heal or med."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Toggles whether or not bots will sit in combat to heal or med." - }; - - std::vector notes = { }; + BotCommandHelpParams p; - std::vector example_format = + p.description = { "Toggles whether or not bots will sit in combat to heal or med." }; + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = { + "To set Clerics to sit in combat:", fmt::format( - "{} [value] [actionable]" - , sep->arg[0] + "{} 1 byclass 2", + sep->arg[0] ) }; - std::vector examples_one = + p.examples_two = { - "To set Clerics to sit in combat:", + "To set all bots to sit/med in combat:", fmt::format( - "{} 1 byclass 2", + "{} 1 spawned", sep->arg[0] ) }; - std::vector examples_two = { }; - std::vector examples_three = + p.examples_three = { "To check the sit in combat state for all bots:", fmt::format( @@ -38,28 +37,9 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 14e37fa369..26e43a3be2 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -3,63 +3,43 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_sit_mana_percent", sep->arg[0], "sitmanapercent")) { + c->Message(Chat::White, "note: Mana % threshold when bots will sit in combat if allowed."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Mana % threshold when bots will sit in combat if allowed." - }; + BotCommandHelpParams p; - std::vector notes = { }; - - std::vector example_format = + p.description = { "Mana % threshold when bots will sit in combat if allowed." }; + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = { + "To set Clerics to sit at 45% Mana:", fmt::format( - "{} [value] [actionable]" - , sep->arg[0] + "{} 45 byclass 2", + sep->arg[0] ) }; - std::vector examples_one = + p.examples_two = { - "To set Clerics to sit at 45% mana:", + "To set all bots to sit at 50% Mana:", fmt::format( - "{} 45 byclass 2", + "{} 50 spawned", sep->arg[0] ) }; - std::vector examples_two = { }; - std::vector examples_three = + p.examples_three = { - "To check the mana threshold for all bots:", + "To check the Mana threshold for all bots:", fmt::format( "{} current spawned", sep->arg[0] ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index 3cddc2bbd0..274718e5a2 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -3,18 +3,16 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_aggro_checks", sep->arg[0], "spellaggrochecks")) { + c->Message(Chat::White, "note: Toggles whether or not bots will cast a spell type if they think it will get them aggro."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Toggles whether or not bots will cast a spell type if they think it will get them aggro" - }; - - std::vector notes = { }; + BotCommandHelpParams p; - std::vector example_format = + p.description = { "Toggles whether or not bots will cast a spell type if they think it will get them aggro." }; + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -25,7 +23,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to check aggro on nukes:", fmt::format( @@ -39,7 +37,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) BotSpellTypes::Nuke ) }; - std::vector examples_two = + p.examples_two = { "To set Shadowknights to ignore aggro checks on snares:", fmt::format( @@ -53,7 +51,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_three = + p.examples_three = { "To check the current DoT aggro check on all bots:", fmt::format( @@ -68,27 +66,9 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) ) }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 0701ca2f09..ea2ecfee46 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -3,24 +3,23 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_delays", sep->arg[0], "spelldelays")) { + c->Message(Chat::White, "note: Controls how long a bot will wait between casts of different spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Controls how long a bot will wait between casts of different spell types" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Controls how long a bot will wait between casts of different spell types." }; + p.notes = { "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", "- e.g., BotA is healing BotB using BotB's settings", }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -31,7 +30,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all Necromancers to an 8s DoT delay:", fmt::format( @@ -45,7 +44,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) BotSpellTypes::DOT ) }; - std::vector examples_two = + p.examples_two = { "To set all Warriors to receive Fast Heals every 2.5s:", fmt::format( @@ -59,7 +58,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; - std::vector examples_three = + p.examples_three = { "To check the current Nuke delay on all bots:", fmt::format( @@ -73,28 +72,9 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) BotSpellTypes::Nuke ) }; + p.actionables = { "target, byname, ownergroup, ownerraid targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 5ac3983347..c87e18acdc 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -3,22 +3,21 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_engaged_priority", sep->arg[0], "spellengagedpriority")) { + c->Message(Chat::White, "note: Sets the order of spell casts when engaged in combat by spell type."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Sets the order of spell casts when engaged in combat by spell type" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Sets the order of spell casts when engaged in combat by spell type." }; + p.notes = { "- Setting a spell type to 0 will prevent that type from being cast.", "- If 2 or more are set to the same priority they will sort by spell type ID." }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -29,7 +28,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all Shaman to cast slows first:", fmt::format( @@ -43,7 +42,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) BotSpellTypes::Slow ) }; - std::vector examples_two = + p.examples_two = { "To set all bots to not cast snares:", fmt::format( @@ -57,7 +56,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_three = + p.examples_three = { "To check the current engaged priority of dispels on all bots:", fmt::format( @@ -71,28 +70,9 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) BotSpellTypes::Dispel ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index 703da4df73..ad374163ba 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -3,21 +3,17 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_holds", sep->arg[0], "spellholds")) { + c->Message(Chat::White, "note: Toggles whether or not bots can cast certain spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Toggles whether or not bots can cast certain spell types" - }; - - std::vector notes = - { - "- All pet types are based off the pet owner's setting when a pet is the target" - }; + BotCommandHelpParams p; - std::vector example_format = + p.description = { "Toggles whether or not bots can cast certain spell types." }; + p.notes = { "- All pet types are based off the pet owner's setting when a pet is the target" }; + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -28,7 +24,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to hold DoTs:", fmt::format( @@ -42,7 +38,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) BotSpellTypes::DOT ) }; - std::vector examples_two = + p.examples_two = { "To check the current DoT settings on all bots:", fmt::format( @@ -56,29 +52,9 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) BotSpellTypes::DOT ) }; - std::vector examples_three = { }; - - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 0d24a148dc..5310d189ec 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -3,22 +3,21 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_idle_priority", sep->arg[0], "spellidlepriority")) { + c->Message(Chat::White, "note: Sets the order of spell casts when not in combat by spell type."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Sets the order of spell casts when not in combat by spell type" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Sets the order of spell casts when not in combat by spell type." }; + p.notes = { "- Setting a spell type to 0 will prevent that type from being cast.", "- If 2 or more are set to the same priority they will sort by spell type ID." }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -29,7 +28,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all Clerics to cast fast heals third:", fmt::format( @@ -43,7 +42,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; - std::vector examples_two = + p.examples_two = { "To set all bots to not cast cures:", fmt::format( @@ -57,7 +56,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) BotSpellTypes::Cure ) }; - std::vector examples_three = + p.examples_three = { "To check the current idle priority of buffs on all bots:", fmt::format( @@ -71,28 +70,9 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) BotSpellTypes::Buff ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index a9e7e3f6d2..450030dad4 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -3,18 +3,16 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_max_hp_pct", sep->arg[0], "spellmaxhppct")) { + c->Message(Chat::White, "note: Controls at what health percentage a bot will start casting different spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Controls at what health percentage a bot will start casting different spell types" - }; + BotCommandHelpParams p; - std::vector notes = { }; - - std::vector example_format = + p.description = { "Controls at what health percentage a bot will start casting different spell types." }; + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -25,9 +23,9 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { - "To set all bots to start snaring when their health is at or below 100% HP:", + "To set all bots to allow snaring when their health is at or below 100% HP:", fmt::format( "{} {} 100 spawned", sep->arg[0], @@ -39,9 +37,9 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_two = + p.examples_two = { - "To set BotA to start casting fast heals at 30% HP:", + "To set BotA to allow casting of fast heals at 30% HP:", fmt::format( "{} {} 30 byname BotA", sep->arg[0], @@ -53,7 +51,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; - std::vector examples_three = + p.examples_three = { "To check the current Stun max HP percent on all bots:", fmt::format( @@ -67,28 +65,9 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) BotSpellTypes::Stun ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 68dde0c6dd..1d554fc1b2 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -3,18 +3,16 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_max_mana_pct", sep->arg[0], "spellmaxmanapct")) { + c->Message(Chat::White, "note: Controls at what mana percentage a bot will stop casting different spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Controls at what mana percentage a bot will stop casting different spell types" - }; + BotCommandHelpParams p; - std::vector notes = { }; - - std::vector example_format = + p.description = { "Controls at what mana percentage a bot will stop casting different spell types." }; + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -25,9 +23,9 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { - "To set all bots to start snaring when their mana is at or below 100% HP:", + "To set all bots to allow snaring when their mana is at or below 100% HP:", fmt::format( "{} {} 10 spawned", sep->arg[0], @@ -39,21 +37,21 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_two = + p.examples_two = { - "To set BotA to start casting fast heals at 30% mana:", + "To set BotA to allow casting of fast heals at 90% mana:", fmt::format( - "{} {} 30 byname BotA", + "{} {} 90 byname BotA", sep->arg[0], c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) ), fmt::format( - "{} {} 30 byname BotA", + "{} {} 90 byname BotA", sep->arg[0], BotSpellTypes::FastHeals ) }; - std::vector examples_three = + p.examples_three = { "To check the current Stun max mana percent on all bots:", fmt::format( @@ -67,28 +65,9 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) BotSpellTypes::Stun ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 709e8bd8a0..13cd6e635d 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -3,24 +3,23 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_max_thresholds", sep->arg[0], "spellmaxthresholds")) { + c->Message(Chat::White, "note: Controls at what target HP % the bot will start casting different spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Controls at what target HP % the bot will start casting different spell types" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Controls at what target HP % the bot will start casting different spell types." }; + p.notes = { "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", "- e.g., BotA is healing BotB using BotB's settings", }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -31,7 +30,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to start snaring at 99%:", fmt::format( @@ -45,7 +44,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_two = + p.examples_two = { "To set bot Enchbot to start casting Debuffs at 99%:", fmt::format( @@ -59,7 +58,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) BotSpellTypes::Debuff ) }; - std::vector examples_three = + p.examples_three = { "To check the current Nuke max threshold on all bots:", fmt::format( @@ -73,28 +72,9 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) BotSpellTypes::Nuke ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index c8124b9a0d..5ee69b996e 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -3,18 +3,16 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_min_hp_pct", sep->arg[0], "spellminhppct")) { + c->Message(Chat::White, "note: Controls at what health percentage a bot will stop casting different spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Controls at what health percentage a bot will stop casting different spell types" - }; - - std::vector notes = { }; - - std::vector example_format = + BotCommandHelpParams p; + + p.description = { "Controls at what health percentage a bot will stop casting different spell types." }; + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -25,7 +23,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to stop snaring when their health is below 10% HP:", fmt::format( @@ -39,7 +37,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_two = + p.examples_two = { "To set BotA to stop casting fast heals at 30% HP:", fmt::format( @@ -53,7 +51,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; - std::vector examples_three = + p.examples_three = { "To check the current Stun min HP percent on all bots:", fmt::format( @@ -67,28 +65,9 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) BotSpellTypes::Stun ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index 411d6ec18c..3c760d82e3 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -3,18 +3,16 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_min_mana_pct", sep->arg[0], "spellminmanapct")) { + c->Message(Chat::White, "note: Controls at what mana percentage a bot will stop casting different spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Controls at what mana percentage a bot will stop casting different spell types" - }; - - std::vector notes = { }; - - std::vector example_format = + BotCommandHelpParams p; + + p.description = { "Controls at what mana percentage a bot will stop casting different spell types." }; + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -25,7 +23,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to stop snaring when their mana is below 10% HP:", fmt::format( @@ -39,7 +37,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) BotSpellTypes::Snare ) }; - std::vector examples_two = + p.examples_two = { "To set BotA to stop casting fast heals at 30% mana:", fmt::format( @@ -53,7 +51,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; - std::vector examples_three = + p.examples_three = { "To check the current Stun min mana percent on all bots:", fmt::format( @@ -67,28 +65,9 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) BotSpellTypes::Stun ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index c1c5a62276..606fe5b1bf 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -3,24 +3,23 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_min_thresholds", sep->arg[0], "spellminthresholds")) { + c->Message(Chat::White, "note: Controls at what target HP % the bot will stop casting different spell types."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Controls at what target HP % the bot will stop casting different spell types" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Controls at what target HP % the bot will stop casting different spell types." }; + p.notes = { "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", "- e.g., BotA is healing BotB using BotB's settings", }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -31,7 +30,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to stop debuffing at 10%:", fmt::format( @@ -45,7 +44,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) BotSpellTypes::Debuff ) }; - std::vector examples_two = + p.examples_two = { "To set all Druids to stop casting DoTs at 15%:", fmt::format( @@ -59,7 +58,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) BotSpellTypes::DOT ) }; - std::vector examples_three = + p.examples_three = { "To check the current Fast Heal min threshold on all bots:", fmt::format( @@ -73,30 +72,9 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid", - "targetgroup, namesgroup, healrotationtargets", - "mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index e9430ea4d6..51725ba947 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -3,22 +3,21 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_pursue_priority", sep->arg[0], "spellpursuepriority")) { + c->Message(Chat::White, "note: Sets the order of spell casts when the mob is fleeing in combat by spell type."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Sets the order of spell casts when the mob is fleeing in combat by spell type" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Sets the order of spell casts when the mob is fleeing in combat by spell type." }; + p.notes = { "- Setting a spell type to 0 will prevent that type from being cast.", "- If 2 or more are set to the same priority they will sort by spell type ID." }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -29,7 +28,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to cast nukes first:", fmt::format( @@ -43,7 +42,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; - std::vector examples_two = + p.examples_two = { "To set all Shaman to not cast cures:", fmt::format( @@ -57,7 +56,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) BotSpellTypes::Cure ) }; - std::vector examples_three = + p.examples_three = { "To check the current pursue priority of buffs on all bots:", fmt::format( @@ -71,28 +70,9 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) BotSpellTypes::Buff ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index aeb1931fe4..536719d6e4 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -3,18 +3,16 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_target_count", sep->arg[0], "spelltargetcount")) { + c->Message(Chat::White, "note: Decides how many eligible targets are required for an AE or group spell to cast by spell type."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Decides how many eligible targets are required for an AE or group spell to cast by spell type" - }; + BotCommandHelpParams p; - std::vector notes = { }; - - std::vector example_format = + p.description = { "Decides how many eligible targets are required for an AE or group spell to cast by spell type." }; + p.example_format = { fmt::format( "{} [Type Shortname] [value] [actionable]" @@ -25,7 +23,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set all bots to AEMez with 5 or more targets:", fmt::format( @@ -39,7 +37,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) BotSpellTypes::AEMez ) }; - std::vector examples_two = + p.examples_two = { "To set Wizards to require 5 targets for AENukes:", fmt::format( @@ -53,7 +51,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) BotSpellTypes::AENukes ) }; - std::vector examples_three = + p.examples_three = { "To check the current AESlow count on all bots:", fmt::format( @@ -67,28 +65,9 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) BotSpellTypes::AESlow ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/bot_commands/taunt.cpp b/zone/bot_commands/taunt.cpp index 2b1eea20b3..69b49b42e9 100644 --- a/zone/bot_commands/taunt.cpp +++ b/zone/bot_commands/taunt.cpp @@ -3,25 +3,17 @@ void bot_command_taunt(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_taunt", sep->arg[0], "taunt")) { + c->Message(Chat::White, "note: TAllows you to turn on/off the taunting state of your bots and/or their pets."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - std::vector description = - { - "Allows you to turn on/off the taunting state of your bots and/or their pets" - }; - - std::vector notes = { }; + BotCommandHelpParams p; - std::vector example_format = - { - fmt::format( - "{} [on / off / pet] [optional: pet] [actionable, default: target]" - , sep->arg[0] - ) - }; - std::vector examples_one = + p.description = { "Allows you to turn on/off the taunting state of your bots and/or their pets." }; + p.example_format = { fmt::format("{} [on / off / pet] [optional: pet] [actionable, default: target]", sep->arg[0]) }; + p.examples_one = { "To turn off taunt on all bots:", fmt::format( @@ -29,7 +21,7 @@ void bot_command_taunt(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_two = + p.examples_two = { "To turn on taunt on all bots' pets:", fmt::format( @@ -37,7 +29,7 @@ void bot_command_taunt(Client* c, const Seperator* sep) sep->arg[0] ) }; - std::vector examples_three = + p.examples_three = { "To turn on taunt for all ShadowKnights:", fmt::format( @@ -46,28 +38,9 @@ void bot_command_taunt(Client* c, const Seperator* sep) Class::ShadowKnight ) }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - std::vector actionables = - { - "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" - }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); - + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/client.cpp b/zone/client.cpp index c8e76b2e29..c0ec607e26 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13112,20 +13112,10 @@ void Client::ShowZoneShardMenu() } } -std::string Client::SendCommandHelpWindow( - Client* c, - std::vector description, - std::vector notes, - std::vector example_format, - std::vector examples_one, std::vector examples_two, std::vector examples_three, - std::vector actionables, - std::vector options, - std::vector options_one, std::vector options_two, std::vector options_three -) { - +std::string Client::SendBotCommandHelpWindow(const BotCommandHelpParams& params) { unsigned string_length = 0; unsigned current_place = 0; - uint16 max_length = RuleI(Command, MaxHelpLineLength); //character length of a line before splitting in to multiple lines + uint16 max_length = RuleI(Command, MaxHelpLineLength); // Line length before splitting const std::string& description_color = RuleS(Command, DescriptionColor); const std::string& description_header_color = RuleS(Command, DescriptionHeaderColor); const std::string& alt_description_color = RuleS(Command, AltDescriptionColor); @@ -13145,81 +13135,66 @@ std::string Client::SendCommandHelpWindow( const std::string& actionable_color = RuleS(Command, ActionableColor); const std::string& actionable_header_color = RuleS(Command, ActionableHeaderColor); const std::string& alt_actionable_color = RuleS(Command, AltActionableColor); - const std::string& header_color = RuleS(Command, HeaderColor); - const std::string& secondary_header_color = RuleS(Command, SecondaryHeaderColor); - const std::string& alt_header_color = RuleS(Command, AltHeaderColor); const std::string& filler_line_color = RuleS(Command, FillerLineColor); - - std::string filler_line = "--------------------------------------------------------------------"; std::string filler_dia = DialogueWindow::TableRow(DialogueWindow::TableCell(fmt::format("{}", DialogueWindow::ColorMessage(filler_line_color, filler_line)))); std::string break_line = DialogueWindow::Break(); - std::string indent = "        "; - std::string bullet = "- "; - std::string popup_text = ""; - - /* - max_length is how long you want lines to be before splitting them. This will look for the last space before the count and split there so words are not split mid sentence - Any SplitCommandHelpText can have the first string from a vector differ in color from the next strings by setting an alternate color for that type in the rule. - */ + std::string popup_text; - if (!description.empty()) { + if (!params.description.empty()) { popup_text += GetCommandHelpHeader(description_header_color, "[Description]"); - popup_text += SplitCommandHelpText(description, description_color, max_length, !alt_description_color.empty(), alt_description_color); + popup_text += SplitCommandHelpText(params.description, description_color, max_length, !alt_description_color.empty(), alt_description_color); } - if (!notes.empty()) { - popup_text += break_line; - popup_text += break_line; + if (!params.notes.empty()) { + popup_text += break_line + break_line; popup_text += GetCommandHelpHeader(note_header_color, "[Notes]"); - popup_text += SplitCommandHelpText(notes, note_color, max_length, !alt_note_color.empty(), alt_note_color); + popup_text += SplitCommandHelpText(params.notes, note_color, max_length, !alt_note_color.empty(), alt_note_color); } - if (!example_format.empty()) { + if (!params.example_format.empty()) { popup_text += filler_dia; popup_text += GetCommandHelpHeader(example_header_color, "[Examples]"); - popup_text += SplitCommandHelpText(example_format, example_color, max_length, !alt_example_color.empty(), alt_example_color); + popup_text += SplitCommandHelpText(params.example_format, example_color, max_length, !alt_example_color.empty(), alt_example_color); } - if (!examples_one.empty()) { - popup_text += break_line; - popup_text += break_line; - popup_text += SplitCommandHelpText(examples_one, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); + if (!params.examples_one.empty()) { + popup_text += break_line + break_line; + popup_text += SplitCommandHelpText(params.examples_one, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); } - if (!examples_two.empty()) { - popup_text += SplitCommandHelpText(examples_two, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); + if (!params.examples_two.empty()) { + popup_text += SplitCommandHelpText(params.examples_two, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); } - if (!examples_three.empty()) { - popup_text += SplitCommandHelpText(examples_three, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); + if (!params.examples_three.empty()) { + popup_text += SplitCommandHelpText(params.examples_three, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); } - if (!options.empty()) { + if (!params.options.empty()) { popup_text += filler_dia; popup_text += GetCommandHelpHeader(option_header_color, "[Options]"); - popup_text += SplitCommandHelpText(options, option_color, max_length, !alt_option_color.empty(), alt_option_color); + popup_text += SplitCommandHelpText(params.options, option_color, max_length, !alt_option_color.empty(), alt_option_color); } - if (!options_one.empty()) { - popup_text += break_line; - popup_text += break_line; - popup_text += SplitCommandHelpText(options_one, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); + if (!params.options_one.empty()) { + popup_text += break_line + break_line; + popup_text += SplitCommandHelpText(params.options_one, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); } - if (!options_two.empty()) { - popup_text += SplitCommandHelpText(options_two, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); + if (!params.options_two.empty()) { + popup_text += SplitCommandHelpText(params.options_two, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); } - if (!options_three.empty()) { - popup_text += SplitCommandHelpText(options_three, secondary_header_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); + if (!params.options_three.empty()) { + popup_text += SplitCommandHelpText(params.options_three, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); } - if (!actionables.empty()) { + if (!params.actionables.empty()) { popup_text += filler_dia; popup_text += GetCommandHelpHeader(actionable_header_color, "[Actionables]"); - popup_text += SplitCommandHelpText(actionables, actionable_color, max_length, !alt_actionable_color.empty(), alt_actionable_color); + popup_text += SplitCommandHelpText(params.actionables, actionable_color, max_length, !alt_actionable_color.empty(), alt_actionable_color); } popup_text = DialogueWindow::Table(popup_text); diff --git a/zone/client.h b/zone/client.h index 20e6ebba6b..1ac7f893a8 100644 --- a/zone/client.h +++ b/zone/client.h @@ -197,6 +197,19 @@ struct RespawnOption float heading; }; +struct BotCommandHelpParams { + std::vector description = {}; + std::vector notes = {}; + std::vector example_format = {}; + std::vector examples_one = {}; + std::vector examples_two = {}; + std::vector examples_three = {}; + std::vector actionables = {}; + std::vector options = {}; + std::vector options_one = {}; + std::vector options_two = {}; + std::vector options_three = {}; +}; // do not ask what all these mean because I have no idea! // named from the client's CEverQuest::GetInnateDesc, they're missing some @@ -1259,16 +1272,7 @@ class Client : public Mob void SendOPTranslocateConfirm(Mob *Caster, uint16 SpellID); // Help Window - std::string SendCommandHelpWindow( - Client* c, - std::vector description, - std::vector notes, - std::vector example_format, - std::vector examples_one, std::vector examples_two, std::vector examples_three, - std::vector actionables, - std::vector options, - std::vector options_one, std::vector options_two, std::vector options_three - ); + std::string SendBotCommandHelpWindow(const BotCommandHelpParams& params); std::string GetCommandHelpHeader(std::string color, std::string header); std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor = false, std::string secondaryColor = ""); void SendSpellTypePrompts(bool commandedTypes = false, bool clientOnlyTypes = false); diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp index 0c3c8c3920..21b744823c 100644 --- a/zone/gm_commands/spell_delays.cpp +++ b/zone/gm_commands/spell_delays.cpp @@ -7,17 +7,14 @@ void command_spell_delays(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - std::vector description = - { - "Controls how often bots can cast certain spell types on you" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Controls how often bots can cast certain spell types on you" }; + p.notes = { "- All pet types are control your how your pet will be affected" }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value]" @@ -28,7 +25,7 @@ void command_spell_delays(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set Very Fast Heals to be received every 1 second:", fmt::format( @@ -42,7 +39,7 @@ void command_spell_delays(Client* c, const Seperator* sep) BotSpellTypes::VeryFastHeals ) }; - std::vector examples_two = + p.examples_two = { "To check your current Regular Heal delay:", fmt::format( @@ -56,26 +53,8 @@ void command_spell_delays(Client* c, const Seperator* sep) BotSpellTypes::RegularHeal ) }; - std::vector examples_three = { }; - - std::vector actionables = { }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp index dcb0639dea..a50bc957e6 100644 --- a/zone/gm_commands/spell_holds.cpp +++ b/zone/gm_commands/spell_holds.cpp @@ -11,17 +11,14 @@ void command_spell_holds(Client *c, const Seperator *sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - std::vector description = - { - "Toggles whether or not bots can cast certain spell types on you" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Toggles whether or not bots can cast certain spell types on you" }; + p.notes = { "- All pet types are control your how your pet will be affected" }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value]" @@ -32,7 +29,7 @@ void command_spell_holds(Client *c, const Seperator *sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set DoTs to be held:", fmt::format( @@ -46,7 +43,7 @@ void command_spell_holds(Client *c, const Seperator *sep) BotSpellTypes::DOT ) }; - std::vector examples_two = + p.examples_two = { "To check your current DoT settings:", fmt::format( @@ -60,26 +57,8 @@ void command_spell_holds(Client *c, const Seperator *sep) BotSpellTypes::DOT ) }; - std::vector examples_three = { }; - - std::vector actionables = { }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp index c6452d76f5..914163f27a 100644 --- a/zone/gm_commands/spell_max_thresholds.cpp +++ b/zone/gm_commands/spell_max_thresholds.cpp @@ -7,17 +7,14 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - std::vector description = - { - "Threshold of your own health when bots will start casting the chosen spell type" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Threshold of your own health when bots will start casting the chosen spell type" }; + p.notes = { "- All pet types are control your how your pet will be affected" }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value]" @@ -28,7 +25,7 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set Complete Heals to start at 90% health:", fmt::format( @@ -42,7 +39,7 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) BotSpellTypes::CompleteHeal ) }; - std::vector examples_two = + p.examples_two = { "To check your current HoT Heal settings:", fmt::format( @@ -56,26 +53,8 @@ void command_spell_max_thresholds(Client* c, const Seperator* sep) BotSpellTypes::HoTHeals ) }; - std::vector examples_three = { }; - - std::vector actionables = { }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp index 3bff9dbb2f..f8b48dbedf 100644 --- a/zone/gm_commands/spell_min_thresholds.cpp +++ b/zone/gm_commands/spell_min_thresholds.cpp @@ -7,17 +7,14 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) const bool is_help = !strcasecmp(sep->arg[1], "help"); if (is_help) { - std::vector description = - { - "Threshold of your own health when bots will stop casting the chosen spell type" - }; + BotCommandHelpParams p; - std::vector notes = + p.description = { "Threshold of your own health when bots will stop casting the chosen spell type" }; + p.notes = { "- All pet types are control your how your pet will be affected" }; - - std::vector example_format = + p.example_format = { fmt::format( "{} [Type Shortname] [value]" @@ -28,7 +25,7 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) , sep->arg[0] ) }; - std::vector examples_one = + p.examples_one = { "To set Fast Heals to be stopped at 65% health:", fmt::format( @@ -42,7 +39,7 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) BotSpellTypes::FastHeals ) }; - std::vector examples_two = + p.examples_two = { "To check your current Cure settings:", fmt::format( @@ -56,26 +53,8 @@ void command_spell_min_thresholds(Client* c, const Seperator* sep) BotSpellTypes::Cure ) }; - std::vector examples_three = { }; - - std::vector actionables = { }; - - std::vector options = { }; - std::vector options_one = { }; - std::vector options_two = { }; - std::vector options_three = { }; - - std::string popup_text = c->SendCommandHelpWindow( - c, - description, - notes, - example_format, - examples_one, examples_two, examples_three, - actionables, - options, - options_one, options_two, options_three - ); + std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); From 81d9ab3dd045216ab386a6bda969bb5709a3d0da Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:48:32 -0600 Subject: [PATCH 292/394] Change char_id to character_id for bot_settings --- .../database_update_manifest_bots.cpp | 4 +-- .../base/base_bot_settings_repository.h | 32 +++++++++---------- zone/bot_database.cpp | 14 ++++---- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 748ca5962e..76f564baf3 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -171,7 +171,7 @@ CHANGE COLUMN `spellid` `spell_id` smallint(5) UNSIGNED NOT NULL DEFAULT 0 AFTER .match = "", .sql = R"( CREATE TABLE `bot_settings` ( - `char_id` INT(10) UNSIGNED NOT NULL, + `character_id` INT(10) UNSIGNED NOT NULL, `bot_id` INT(10) UNSIGNED NOT NULL, `stance` TINYINT(3) UNSIGNED NOT NULL DEFAULT '0', `setting_id` SMALLINT(5) UNSIGNED NOT NULL DEFAULT '0', @@ -179,7 +179,7 @@ CREATE TABLE `bot_settings` ( `value` INT(10) UNSIGNED NOT NULL, `category_name` VARCHAR(64) NULL DEFAULT '' COLLATE 'utf8mb4_general_ci', `setting_name` VARCHAR(64) NULL DEFAULT '' COLLATE 'utf8mb4_general_ci', - PRIMARY KEY (`char_id`, `bot_id`, `stance`, `setting_id`, `setting_type`) USING BTREE + PRIMARY KEY (`character_id`, `bot_id`, `stance`, `setting_id`, `setting_type`) USING BTREE ) COLLATE='utf8mb4_general_ci' ENGINE=InnoDB diff --git a/common/repositories/base/base_bot_settings_repository.h b/common/repositories/base/base_bot_settings_repository.h index 7595a49333..8fbaff17d0 100644 --- a/common/repositories/base/base_bot_settings_repository.h +++ b/common/repositories/base/base_bot_settings_repository.h @@ -19,7 +19,7 @@ class BaseBotSettingsRepository { public: struct BotSettings { - uint32_t char_id; + uint32_t character_id; uint32_t bot_id; uint8_t stance; uint16_t setting_id; @@ -31,13 +31,13 @@ class BaseBotSettingsRepository { static std::string PrimaryKey() { - return std::string("char_id"); + return std::string("character_id"); } static std::vector Columns() { return { - "char_id", + "character_id", "bot_id", "stance", "setting_id", @@ -51,7 +51,7 @@ class BaseBotSettingsRepository { static std::vector SelectColumns() { return { - "char_id", + "character_id", "bot_id", "stance", "setting_id", @@ -99,7 +99,7 @@ class BaseBotSettingsRepository { { BotSettings e{}; - e.char_id = 0; + e.character_id = 0; e.bot_id = 0; e.stance = 0; e.setting_id = 0; @@ -117,7 +117,7 @@ class BaseBotSettingsRepository { ) { for (auto &bot_settings : bot_settingss) { - if (bot_settings.char_id == bot_settings_id) { + if (bot_settings.character_id == bot_settings_id) { return bot_settings; } } @@ -143,7 +143,7 @@ class BaseBotSettingsRepository { if (results.RowCount() == 1) { BotSettings e{}; - e.char_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.bot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.stance = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; @@ -184,7 +184,7 @@ class BaseBotSettingsRepository { auto columns = Columns(); - v.push_back(columns[0] + " = " + std::to_string(e.char_id)); + v.push_back(columns[0] + " = " + std::to_string(e.character_id)); v.push_back(columns[1] + " = " + std::to_string(e.bot_id)); v.push_back(columns[2] + " = " + std::to_string(e.stance)); v.push_back(columns[3] + " = " + std::to_string(e.setting_id)); @@ -199,7 +199,7 @@ class BaseBotSettingsRepository { TableName(), Strings::Implode(", ", v), PrimaryKey(), - e.char_id + e.character_id ) ); @@ -213,7 +213,7 @@ class BaseBotSettingsRepository { { std::vector v; - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); @@ -231,7 +231,7 @@ class BaseBotSettingsRepository { ); if (results.Success()) { - e.char_id = results.LastInsertedID(); + e.character_id = results.LastInsertedID(); return e; } @@ -250,7 +250,7 @@ class BaseBotSettingsRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); @@ -291,7 +291,7 @@ class BaseBotSettingsRepository { for (auto row = results.begin(); row != results.end(); ++row) { BotSettings e{}; - e.char_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.bot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.stance = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; @@ -323,7 +323,7 @@ class BaseBotSettingsRepository { for (auto row = results.begin(); row != results.end(); ++row) { BotSettings e{}; - e.char_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; + e.character_id = row[0] ? static_cast(strtoul(row[0], nullptr, 10)) : 0; e.bot_id = row[1] ? static_cast(strtoul(row[1], nullptr, 10)) : 0; e.stance = row[2] ? static_cast(strtoul(row[2], nullptr, 10)) : 0; e.setting_id = row[3] ? static_cast(strtoul(row[3], nullptr, 10)) : 0; @@ -405,7 +405,7 @@ class BaseBotSettingsRepository { { std::vector v; - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); @@ -435,7 +435,7 @@ class BaseBotSettingsRepository { for (auto &e: entries) { std::vector v; - v.push_back(std::to_string(e.char_id)); + v.push_back(std::to_string(e.character_id)); v.push_back(std::to_string(e.bot_id)); v.push_back(std::to_string(e.stance)); v.push_back(std::to_string(e.setting_id)); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 760108ad92..5313a2efb7 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2220,7 +2220,7 @@ bool BotDatabase::LoadBotSettings(Mob* m) std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {} AND `stance` = {}", mob_id, stance_id); + query = fmt::format("`character_id` = {} AND `stance` = {}", mob_id, stance_id); } else { query = fmt::format("`bot_id` = {} AND `stance` = {}", mob_id, stance_id); @@ -2274,7 +2274,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) } uint32 bot_id = (m->IsBot() ? m->CastToBot()->GetBotID() : 0); - uint32 char_id = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); + uint32 character_id = (m->IsClient() ? m->CastToClient()->CharacterID() : 0); uint8 stance_id = (m->IsBot() ? m->CastToBot()->GetBotStance() : 0); if (stance_id == Stance::Passive) { @@ -2285,7 +2285,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) std::string query = ""; if (m->IsClient()) { - query = fmt::format("`char_id` = {} AND `stance` = {}", char_id, stance_id); + query = fmt::format("`character_id` = {} AND `stance` = {}", character_id, stance_id); } else { query = fmt::format("`bot_id` = {} AND `stance` = {}", bot_id, stance_id); @@ -2301,7 +2301,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) for (uint16 i = BotBaseSettings::START_ALL; i <= BotBaseSettings::END; ++i) { if (m->CastToBot()->GetBotBaseSetting(i) != m->CastToBot()->GetDefaultBotBaseSetting(i, bot_stance)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = char_id, + .character_id = character_id, .bot_id = bot_id, .stance = stance_id, .setting_id = static_cast(i), @@ -2321,7 +2321,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { if (m->CastToBot()->GetSetting(i, x) != m->CastToBot()->GetDefaultSetting(i, x, bot_stance)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = char_id, + .character_id = character_id, .bot_id = bot_id, .stance = stance_id, .setting_id = static_cast(x), @@ -2342,7 +2342,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) if (m->IsClient()) { if (m->CastToClient()->GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock) != m->GetIllusionBlock()) { // Only illusion block supported auto e = BotSettingsRepository::BotSettings{ - .char_id = char_id, + .character_id = character_id, .bot_id = bot_id, .stance = stance_id, .setting_id = static_cast(BotBaseSettings::IllusionBlock), @@ -2362,7 +2362,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { auto e = BotSettingsRepository::BotSettings{ - .char_id = char_id, + .character_id = character_id, .bot_id = bot_id, .stance = stance_id, .setting_id = static_cast(x), From 37a61e7234feae7ee902bf7b2d3688c13fbb34f3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 22 Jan 2025 15:52:15 -0600 Subject: [PATCH 293/394] update todo --- zone/bot.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index 7269db225f..6f1870caad 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,6 +31,7 @@ /* TODO bot rewrite: +--go through IsPetOwnerClient checks for pets --command cleanup (move to new help window, make more descriptive) --Add quest methods for functions */ From 42b41748610cc13b0a1cab0c916b4ac73e725da3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:13:47 -0600 Subject: [PATCH 294/394] Fix typo on merge conflict --- common/eqemu_logsys.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index de2fc72e97..9178aaa3a7 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -252,7 +252,7 @@ namespace Logs { "XTargets", "EvolveItem", "PositionUpdate", - "KSM" // Kernel Samepage Merging + "KSM", // Kernel Samepage Merging "Bot Settings", "Bot Spell Checks", "Bot Spell Type Checks", From 850e996b71a92e293ea2bc89c21ef6a339d9ef1c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Wed, 22 Jan 2025 16:41:48 -0600 Subject: [PATCH 295/394] Cleanup command format changes, remove hardcoded class IDs in examples. --- zone/bot_commands/behind_mob.cpp | 15 +++++++------ zone/bot_commands/blocked_buffs.cpp | 21 +++++++----------- zone/bot_commands/bot.cpp | 23 ++++++++------------ zone/bot_commands/copy_settings.cpp | 13 ++--------- zone/bot_commands/default_settings.cpp | 10 ++++----- zone/bot_commands/depart.cpp | 3 +-- zone/bot_commands/discipline.cpp | 5 +---- zone/bot_commands/distance_ranged.cpp | 4 +--- zone/bot_commands/sit_hp_percent.cpp | 5 +++-- zone/bot_commands/sit_in_combat.cpp | 5 +++-- zone/bot_commands/sit_mana_percent.cpp | 5 +++-- zone/bot_commands/spell_aggro_checks.cpp | 20 +++++++---------- zone/bot_commands/spell_engaged_priority.cpp | 20 +++++++---------- zone/bot_commands/spell_holds.cpp | 10 ++------- zone/bot_commands/spell_idle_priority.cpp | 20 +++++++---------- zone/bot_commands/spell_max_hp_pct.cpp | 10 ++------- zone/bot_commands/spell_max_mana_pct.cpp | 10 ++------- zone/bot_commands/spell_max_thresholds.cpp | 10 ++------- zone/bot_commands/spell_min_hp_pct.cpp | 10 ++------- zone/bot_commands/spell_min_mana_pct.cpp | 10 ++------- zone/bot_commands/spell_min_thresholds.cpp | 20 +++++++---------- zone/bot_commands/spell_pursue_priority.cpp | 20 +++++++---------- zone/bot_commands/spell_target_count.cpp | 10 ++------- zone/gm_commands/spell_holds.cpp | 8 +++---- 24 files changed, 101 insertions(+), 186 deletions(-) diff --git a/zone/bot_commands/behind_mob.cpp b/zone/bot_commands/behind_mob.cpp index 5de7571f1b..be7e417df1 100644 --- a/zone/bot_commands/behind_mob.cpp +++ b/zone/bot_commands/behind_mob.cpp @@ -12,18 +12,19 @@ void bot_command_behind_mob(Client* c, const Seperator* sep) BotCommandHelpParams p; p.description = { "Toggles whether or not bots will stay behind the mob during combat." }; - p.example_format = { - fmt::format("{} [value] [actionable]", sep->arg[0]) - }; - p.examples_one = { + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = + { "To set Monks to stay behind the mob:", - fmt::format("{} 1 byclass 7", sep->arg[0]) + fmt::format("{} 1 byclass {}", sep->arg[0], Class::Monk) }; - p.examples_two = { + p.examples_two = + { "To force all bots to stay behind mobs:", fmt::format("{} 1 spawned", sep->arg[0]) }; - p.examples_three = { + p.examples_three = + { "To check the behind mob status of all bots:", fmt::format("{} current spawned", sep->arg[0]) }; diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index 8cb5396044..c37bada86a 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -19,18 +19,19 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) p.description = { "Allows you to set, view and wipe blocked buffs for the selected bots." }; p.notes = { "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list." }; - p.example_format = { - fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) - }; - p.examples_one = { + p.example_format = { fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) }; + p.examples_one = + { "To add Courage(Spell ID #202) to the targeted bot's blocked list:", fmt::format("{} add 202", sep->arg[0]) }; - p.examples_two = { + p.examples_two = + { "To view the targeted bot's blocked buff list:", fmt::format("{} list", sep->arg[0]) }; - p.examples_three = { + p.examples_three = + { "To wipe all Warriors bots' blocked buff list:", fmt::format( "{} wipe byclass {}", sep->arg[0], Class::Warrior) }; @@ -243,13 +244,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) "- You can 'set' spells to be blocked, 'remove' spells from the blocked list, 'list' the current blocked spells or 'wipe' the entire list.", "- This controls whether or not any pet the selected bot(s) own will prevent certain beneficial buffs from landing on them." }; - p.example_format = - { - fmt::format( - "{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]" - , sep->arg[0] - ) - }; + p.example_format = { fmt::format("{} [add [ID] | remove [ID] | list | wipe] [actionable, default: target]", sep->arg[0]) }; p.examples_one = { "To add Courage (Spell ID #202) to the targeted bot's blocked list:", diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index e91c5eb324..3a5e326761 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -486,9 +486,7 @@ void bot_command_follow_distance(Client *c, const Seperator *sep) fmt::format("[Default]: {}", RuleI(Bots, MaxFollowDistance)), fmt::format("- You must use a value between 1 and {}.", RuleI(Bots, MaxFollowDistance)) }; - p.example_format = { - fmt::format("{} [reset]/[set [value]] [actionable]", sep->arg[0]) - }; + p.example_format = { fmt::format("{} [reset]/[set [value]] [actionable]", sep->arg[0]) }; p.examples_one = { "To set all bots to follow at a distance of 25:", fmt::format("{} set 25 spawned", sep->arg[0]) @@ -1157,7 +1155,8 @@ void bot_command_stance(Client *c, const Seperator *sep) BotCommandHelpParams p; p.description = { "Change a bot's stance to control the way it behaves." }; - p.notes = { + p.notes = + { "- Changing a stance will reset all settings to match that stance type.", "- Any changes made will only save to that stance for future use.", fmt::format( @@ -1221,15 +1220,10 @@ void bot_command_stance(Client *c, const Seperator *sep) Stance::AEBurn ) }; - - p.example_format = { - fmt::format( "{} [current | value: {}-{}]", - sep->arg[0], - Stance::Passive, - Stance::AEBurn - ) - }; - p.examples_one = { + p.example_format = + { fmt::format( "{} [current | value]", sep->arg[0]) }; + p.examples_one = + { "To set all bots to BurnAE:", fmt::format("{} {} spawned {}", sep->arg[0], @@ -1237,7 +1231,8 @@ void bot_command_stance(Client *c, const Seperator *sep) Class::ShadowKnight ) }; - p.examples_two = { + p.examples_two = + { "To set all Shadowknights to Aggressive:", fmt::format("{} {} byclass {}", sep->arg[0], diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 058385c3b5..12d6d0b2d0 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -19,20 +19,11 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" }; - p.example_format = - { - fmt::format( - "{} [from] [to] [option]" - , sep->arg[0] - ) - }; + p.example_format = { fmt::format("{} [from] [to] [option]", sep->arg[0]) }; p.examples_one = { "To copy all settings from BotA to BotB:", - fmt::format( - "{} BotA BotB all", - sep->arg[0] - ) + fmt::format("{} BotA BotB all", sep->arg[0]) }; p.examples_two = { diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index de09a2ee2c..7186335e2a 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -13,16 +13,14 @@ void bot_command_default_settings(Client* c, const Seperator* sep) p.description = { "Restores a bot's setting(s) to defaults" }; p.notes = { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only"}; - p.example_format = - { - fmt::format("{} [option] [actionable]", sep->arg[0]) - }; + p.example_format = { fmt::format("{} [option] [actionable]", sep->arg[0]) }; p.examples_one = { "To restore delays for Clerics:", fmt::format( - "{} delays byclass 2", - sep->arg[0] + "{} delays byclass {}", + sep->arg[0], + Class::Cleric ) }; p.examples_two = diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 39cb1bffdd..88b2d7a07e 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -21,8 +21,7 @@ void bot_command_depart(Client* c, const Seperator* sep) p.examples_one = { "To tell everyone to list their portable locations:", - fmt::format( - "{} list spawned", + fmt::format("{} list spawned", sep->arg[0] ) }; diff --git a/zone/bot_commands/discipline.cpp b/zone/bot_commands/discipline.cpp index 0d372bf063..8246b14060 100644 --- a/zone/bot_commands/discipline.cpp +++ b/zone/bot_commands/discipline.cpp @@ -16,10 +16,7 @@ void bot_command_discipline(Client* c, const Seperator* sep) "Tells applicable bots to use the specified disciplines." }; p.notes = { "Aside from Lay On Hands and Harm Touch, you will need to know the spell ID of the discipline to tell a bot to attempt to use it." }; - p.example_format = - { - fmt::format("{} [aggressive | defensive | spell ID] [actionable, default: spawned]", sep->arg[0]) - }; + p.example_format = { fmt::format("{} [aggressive | defensive | spell ID] [actionable, default: spawned]", sep->arg[0]) }; p.examples_one = { "To tell all bots to use an aggressive discipline:", diff --git a/zone/bot_commands/distance_ranged.cpp b/zone/bot_commands/distance_ranged.cpp index 7a3d2108b8..1c7857daae 100644 --- a/zone/bot_commands/distance_ranged.cpp +++ b/zone/bot_commands/distance_ranged.cpp @@ -18,9 +18,7 @@ void bot_command_distance_ranged(Client* c, const Seperator* sep) "- Casters will never go closer than their maximum melee range.", "- Throwing bots will never get closer than the minimum value for ranged to work, or beyond the range of their items." }; - p.example_format = { - fmt::format("{} [value] [actionable]", sep->arg[0]) - }; + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; p.examples_one = { "To set Wizards to a range of 100:", fmt::format( diff --git a/zone/bot_commands/sit_hp_percent.cpp b/zone/bot_commands/sit_hp_percent.cpp index 0c42081913..0843aa5b92 100644 --- a/zone/bot_commands/sit_hp_percent.cpp +++ b/zone/bot_commands/sit_hp_percent.cpp @@ -17,8 +17,9 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep) { "To set Clerics to sit at 45% HP:", fmt::format( - "{} 45 byclass 2", - sep->arg[0] + "{} 45 byclass {}", + sep->arg[0], + Class::Cleric ) }; p.examples_two = diff --git a/zone/bot_commands/sit_in_combat.cpp b/zone/bot_commands/sit_in_combat.cpp index cfe868662a..07a2f917e4 100644 --- a/zone/bot_commands/sit_in_combat.cpp +++ b/zone/bot_commands/sit_in_combat.cpp @@ -17,8 +17,9 @@ void bot_command_sit_in_combat(Client* c, const Seperator* sep) { "To set Clerics to sit in combat:", fmt::format( - "{} 1 byclass 2", - sep->arg[0] + "{} 1 byclass {}", + sep->arg[0], + Class::Cleric ) }; p.examples_two = diff --git a/zone/bot_commands/sit_mana_percent.cpp b/zone/bot_commands/sit_mana_percent.cpp index 26e43a3be2..e85d976c89 100644 --- a/zone/bot_commands/sit_mana_percent.cpp +++ b/zone/bot_commands/sit_mana_percent.cpp @@ -17,8 +17,9 @@ void bot_command_sit_mana_percent(Client* c, const Seperator* sep) { "To set Clerics to sit at 45% Mana:", fmt::format( - "{} 45 byclass 2", - sep->arg[0] + "{} 45 byclass {}", + sep->arg[0], + Class::Cleric ) }; p.examples_two = diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index 274718e5a2..2c5b92aafe 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -14,14 +14,8 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) p.description = { "Toggles whether or not bots will cast a spell type if they think it will get them aggro." }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { @@ -41,14 +35,16 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) { "To set Shadowknights to ignore aggro checks on snares:", fmt::format( - "{} {} 0 byclass 5", + "{} {} 0 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + c->GetSpellTypeShortNameByID(BotSpellTypes::Snare), + Class::ShadowKnight ), fmt::format( - "{} {} 0 byclass 5", + "{} {} 0 byclass {}", sep->arg[0], - BotSpellTypes::Snare + BotSpellTypes::Snare, + Class::ShadowKnight ) }; p.examples_three = diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index c87e18acdc..3621c6c6bf 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -19,27 +19,23 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { "To set all Shaman to cast slows first:", fmt::format( - "{} {} 1 byclass 10", + "{} {} 1 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Slow) + c->GetSpellTypeShortNameByID(BotSpellTypes::Slow), + Class::Shaman ), fmt::format( - "{} {} 1 byclass 10", + "{} {} 1 byclass {}", sep->arg[0], - BotSpellTypes::Slow + BotSpellTypes::Slow, + Class::Shaman ) }; p.examples_two = diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index ad374163ba..b7ad108ae3 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -15,14 +15,8 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) p.notes = { "- All pet types are based off the pet owner's setting when a pet is the target" }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 5310d189ec..830cd8f3b0 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -19,27 +19,23 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { "To set all Clerics to cast fast heals third:", fmt::format( - "{} {} 3 byclass 2", + "{} {} 3 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals), + Class::Cleric ), fmt::format( - "{} {} 3 byclass 2", + "{} {} 3 byclass {}", sep->arg[0], - BotSpellTypes::FastHeals + BotSpellTypes::FastHeals, + Class::Cleric ) }; p.examples_two = diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 450030dad4..c1a9d43b3d 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -14,14 +14,8 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) p.description = { "Controls at what health percentage a bot will start casting different spell types." }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 1d554fc1b2..38c78a7e4c 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -14,14 +14,8 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) p.description = { "Controls at what mana percentage a bot will stop casting different spell types." }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 13cd6e635d..7aaeeceb4d 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -21,14 +21,8 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 5ee69b996e..64a1531fc6 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -14,14 +14,8 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) p.description = { "Controls at what health percentage a bot will stop casting different spell types." }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index 3c760d82e3..f99539c583 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -14,14 +14,8 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) p.description = { "Controls at what mana percentage a bot will stop casting different spell types." }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index 606fe5b1bf..fdd30c0b90 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -21,14 +21,8 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { @@ -48,14 +42,16 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { "To set all Druids to stop casting DoTs at 15%:", fmt::format( - "{} {} 15 byclass 6", + "{} {} 15 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT), + Class::Druid ), fmt::format( - "{} {} 15 byclass 6", + "{} {} 15 byclass {}", sep->arg[0], - BotSpellTypes::DOT + BotSpellTypes::DOT, + Class::Druid ) }; p.examples_three = diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index 51725ba947..eeba9792cc 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -19,14 +19,8 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { @@ -46,14 +40,16 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) { "To set all Shaman to not cast cures:", fmt::format( - "{} {} 0 byclass 10", + "{} {} 0 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + c->GetSpellTypeShortNameByID(BotSpellTypes::Cure), + Class::Shaman ), fmt::format( - "{} {} 0 byclass 10", + "{} {} 0 byclass {}", sep->arg[0], - BotSpellTypes::Cure + BotSpellTypes::Cure, + Class::Shaman ) }; p.examples_three = diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index 536719d6e4..c141678466 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -14,14 +14,8 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) p.description = { "Decides how many eligible targets are required for an AE or group spell to cast by spell type." }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) }; p.examples_one = { diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp index a50bc957e6..be0c9cbca9 100644 --- a/zone/gm_commands/spell_holds.cpp +++ b/zone/gm_commands/spell_holds.cpp @@ -21,12 +21,12 @@ void command_spell_holds(Client *c, const Seperator *sep) p.example_format = { fmt::format( - "{} [Type Shortname] [value]" - , sep->arg[0] + "{} [Type Shortname] [value]", + sep->arg[0] ), fmt::format( - "{} [Type ID] [value]" - , sep->arg[0] + "{} [Type ID] [value]", + sep->arg[0] ) }; p.examples_one = From 4ff7e98c68884fd625611d599a119f9f314bede5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:50:30 -0600 Subject: [PATCH 296/394] Set #illusionblock for players to guide access --- zone/command.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/command.cpp b/zone/command.cpp index 686255c6de..8b24484fb2 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -148,7 +148,7 @@ int command_init(void) command_add("help", "[Search Criteria] - List available commands and their description, specify partial command as argument to search", AccountStatus::Player, command_help) || command_add("hotfix", "[hotfix_name] - Reloads shared memory into a hotfix, equiv to load_shared_memory followed by apply_shared_memory", AccountStatus::GMImpossible, command_hotfix) || command_add("hp", "Refresh your HP bar from the server.", AccountStatus::Player, command_hp) || - command_add("illusionblock", "Controls whether or not illusion effects will land on you when cast by other players or bots", AccountStatus::Player, command_illusion_block) || + command_add("illusionblock", "Controls whether or not illusion effects will land on you when cast by other players or bots", AccountStatus::Guide, command_illusion_block) || command_add("instance", "Modify Instances", AccountStatus::GMMgmt, command_instance) || command_add("interrogateinv", "use [help] argument for available options", AccountStatus::Player, command_interrogateinv) || command_add("interrupt", "[Message ID] [Color] - Interrupt your casting. Arguments are optional.", AccountStatus::Guide, command_interrupt) || From 400bf12795089a6ace6afd79ffad1229a4a1673c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 11:51:58 -0600 Subject: [PATCH 297/394] Move client commands for bot spells from gm commands to existing bot commands --- zone/bot_commands/spell_delays.cpp | 174 ++++++++++++++------- zone/bot_commands/spell_max_thresholds.cpp | 144 +++++++++++------ zone/bot_commands/spell_min_thresholds.cpp | 144 +++++++++++------ zone/client_bot.cpp | 2 +- zone/command.cpp | 8 - zone/command.h | 4 - zone/gm_commands/spell_delays.cpp | 157 ------------------- zone/gm_commands/spell_holds.cpp | 161 ------------------- zone/gm_commands/spell_max_thresholds.cpp | 157 ------------------- zone/gm_commands/spell_min_thresholds.cpp | 157 ------------------- 10 files changed, 315 insertions(+), 793 deletions(-) delete mode 100644 zone/gm_commands/spell_delays.cpp delete mode 100644 zone/gm_commands/spell_holds.cpp delete mode 100644 zone/gm_commands/spell_max_thresholds.cpp delete mode 100644 zone/gm_commands/spell_min_thresholds.cpp diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index ea2ecfee46..1decded543 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -1,7 +1,6 @@ #include "../bot_command.h" -void bot_command_spell_delays(Client* c, const Seperator* sep) -{ +void bot_command_spell_delays(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_delays", sep->arg[0], "spelldelays")) { c->Message(Chat::White, "note: Controls how long a bot will wait between casts of different spell types."); @@ -14,48 +13,47 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) p.description = { "Controls how long a bot will wait between casts of different spell types." }; p.notes = { + "- Targeting yourself for this command will allow you to control your own settings for how bots cast on you", "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", - "- e.g., BotA is healing BotB using BotB's settings", + "- e.g., BotA is healing BotB using BotB's settings" }; p.example_format = { - fmt::format( - "{} [Type Shortname] [value] [actionable]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value] [actionable]" - , sep->arg[0] - ) + fmt::format("{} [Type Shortname] [value] [actionable, default: target]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable, default: target]", sep->arg[0]) }; p.examples_one = { "To set all Necromancers to an 8s DoT delay:", fmt::format( - "{} {} 8000 byclass 11", + "{} {} 8000 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + c->GetSpellTypeShortNameByID(BotSpellTypes::DOT), + Class::Necromancer ), fmt::format( - "{} {} 8000 byclass 11", + "{} {} 8000 byclass {}", sep->arg[0], - BotSpellTypes::DOT + BotSpellTypes::DOT, + Class::Necromancer ) }; p.examples_two = { "To set all Warriors to receive Fast Heals every 2.5s:", fmt::format( - "{} {} 2500 byclass 1", + "{} {} 2500 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals), + Class::Warrior ), fmt::format( - "{} {} 2500 byclass 1", + "{} {} 2500 byclass {}", sep->arg[0], - BotSpellTypes::FastHeals + BotSpellTypes::FastHeals, + Class::Warrior ) }; p.examples_three = @@ -72,13 +70,14 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) BotSpellTypes::Nuke ) }; - p.actionables = { "target, byname, ownergroup, ownerraid targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); c->SendPopupToClient(sep->arg[0], popup_text.c_str()); c->SendSpellTypePrompts(); + c->SendSpellTypePrompts(false, true); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( @@ -99,13 +98,26 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) bool current_check = false; uint16 spell_type = 0; uint32 type_value = 0; + Mob* target = c->GetTarget(); + bool clientSetting = (target && target == c); - // String/Int type checks if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + if ( + (clientSetting && !IsClientBotSpellType(spell_type)) || + (!clientSetting && (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END)) + ) { + c->Message( + Chat::Yellow, + clientSetting ? "Invalid spell type for clients." : "You must choose a valid spell type. Spell types range from %i to %i", + BotSpellTypes::START, + BotSpellTypes::END + ); + + if (clientSetting) { + c->SendSpellTypePrompts(false, true); + } return; } @@ -113,6 +125,11 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { spell_type = c->GetSpellTypeIDByShortName(arg1); + + if (clientSetting && !IsClientBotSpellType(spell_type)) { + c->Message(Chat::Yellow, "Invalid spell type for clients."); + c->SendSpellTypePrompts(false, true); + } } else { c->Message( @@ -157,66 +174,103 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string class_race_arg = sep->arg[ab_arg]; - bool class_race_check = false; - if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { - class_race_check = true; - } + std::string actionable_arg = sep->arg[ab_arg]; - std::vector sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { - return; + if (actionable_arg.empty()) { + actionable_arg = "target"; } - sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + if (!clientSetting) { + + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; - Bot* first_found = nullptr; - int success_count = 0; - for (auto my_bot : sbl) { - if (my_bot->BotPassiveCheck()) { - continue; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } - if (!first_found) { - first_found = my_bot; + std::vector sbl; + + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; } - if (current_check) { - c->Message( - Chat::Green, - fmt::format( - "{} says, 'My [{}] spell delay is currently [{}] seconds.'", - my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), - my_bot->GetSpellDelay(spell_type) / 1000.00 - ).c_str() - ); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + Bot* first_found = nullptr; + int success_count = 0; + + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell delay is currently [{}] seconds.'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellDelay(spell_type) / 1000.00 + ).c_str() + ); + } + else { + my_bot->SetSpellDelay(spell_type, type_value); + ++success_count; + } } - else { - my_bot->SetSpellDelay(spell_type, type_value); - ++success_count; + + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] spell delay was set to [{}] seconds.'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellDelay(spell_type) / 1000.00 + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] spell delay to [{}] seconds.", + success_count, + c->GetSpellTypeNameByID(spell_type), + type_value / 1000.00 + ).c_str() + ); + } } } - if (!current_check) { - if (success_count == 1 && first_found) { + else { + if (current_check) { c->Message( Chat::Green, fmt::format( - "{} says, 'My [{}] spell delay was set to [{}] seconds.'", - first_found->GetCleanName(), + "Your [{}] spell delay is currently [{}] seconds.", c->GetSpellTypeNameByID(spell_type), - first_found->GetSpellDelay(spell_type) / 1000.00 + c->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); } else { + c->SetSpellDelay(spell_type, type_value); + c->Message( Chat::Green, fmt::format( - "{} of your bots set their [{}] spell delay to [{}] seconds.", - success_count, + "Your [{}] spell delay was set to [{}] seconds.", c->GetSpellTypeNameByID(spell_type), - type_value / 1000.00 + c->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); } diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 7aaeeceb4d..328345a2dd 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -1,7 +1,6 @@ #include "../bot_command.h" -void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) -{ +void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_max_thresholds", sep->arg[0], "spellmaxthresholds")) { c->Message(Chat::White, "note: Controls at what target HP % the bot will start casting different spell types."); @@ -14,6 +13,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) p.description = { "Controls at what target HP % the bot will start casting different spell types." }; p.notes = { + "- Targeting yourself for this command will allow you to control your own settings for how bots cast on you", "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", @@ -21,8 +21,8 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) }; p.example_format = { - fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), - fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) + fmt::format("{} [Type Shortname] [value] [actionable, default: target]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable, default: target]", sep->arg[0]) }; p.examples_one = { @@ -73,6 +73,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) c->SendPopupToClient(sep->arg[0], popup_text.c_str()); c->SendSpellTypePrompts(); + c->SendSpellTypePrompts(false, true); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( @@ -93,13 +94,26 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) bool current_check = false; uint16 spell_type = 0; uint32 type_value = 0; + Mob* target = c->GetTarget(); + bool clientSetting = (target && target == c); - // String/Int type checks if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + if ( + (clientSetting && !IsClientBotSpellType(spell_type)) || + (!clientSetting && (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END)) + ) { + c->Message( + Chat::Yellow, + clientSetting ? "Invalid spell type for clients." : "You must choose a valid spell type. Spell types range from %i to %i", + BotSpellTypes::START, + BotSpellTypes::END + ); + + if (clientSetting) { + c->SendSpellTypePrompts(false, true); + } return; } @@ -107,6 +121,11 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { spell_type = c->GetSpellTypeIDByShortName(arg1); + + if (clientSetting && !IsClientBotSpellType(spell_type)) { + c->Message(Chat::Yellow, "Invalid spell type for clients."); + c->SendSpellTypePrompts(false, true); + } } else { c->Message( @@ -151,66 +170,103 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string class_race_arg = sep->arg[ab_arg]; - bool class_race_check = false; - if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { - class_race_check = true; - } + std::string actionable_arg = sep->arg[ab_arg]; - std::vector sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { - return; + if (actionable_arg.empty()) { + actionable_arg = "target"; } - sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + if (!clientSetting) { - Bot* first_found = nullptr; - int success_count = 0; - for (auto my_bot : sbl) { - if (my_bot->BotPassiveCheck()) { - continue; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } - if (!first_found) { - first_found = my_bot; + std::vector sbl; + + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; } - if (current_check) { - c->Message( - Chat::Green, - fmt::format( - "{} says, 'My [{}] maximum threshold is currently [{}%%].'", - my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), - my_bot->GetSpellMaxThreshold(spell_type) - ).c_str() - ); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + Bot* first_found = nullptr; + int success_count = 0; + + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum threshold is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellMaxThreshold(spell_type) + ).c_str() + ); + } + else { + my_bot->SetSpellMaxThreshold(spell_type, type_value); + ++success_count; + } } - else { - my_bot->SetSpellMaxThreshold(spell_type, type_value); - ++success_count; + + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] maximum threshold was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellMaxThreshold(spell_type) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] maximum threshold to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spell_type), + type_value + ).c_str() + ); + } } } - if (!current_check) { - if (success_count == 1 && first_found) { + else { + if (current_check) { c->Message( Chat::Green, fmt::format( - "{} says, 'My [{}] maximum threshold was set to [{}%%].'", - first_found->GetCleanName(), + "Your [{}] maximum threshold is currently [{}%%].", c->GetSpellTypeNameByID(spell_type), - first_found->GetSpellMaxThreshold(spell_type) + c->GetSpellMaxThreshold(spell_type) ).c_str() ); } else { + c->SetSpellMaxThreshold(spell_type, type_value); + c->Message( Chat::Green, fmt::format( - "{} of your bots set their [{}] maximum threshold to [{}%%].", - success_count, + "Your [{}] maximum threshold was set to [{}%%].", c->GetSpellTypeNameByID(spell_type), - type_value + c->GetSpellMaxThreshold(spell_type) ).c_str() ); } diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index fdd30c0b90..f39194e2ad 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -1,7 +1,6 @@ #include "../bot_command.h" -void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) -{ +void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { if (helper_command_alias_fail(c, "bot_command_spell_min_thresholds", sep->arg[0], "spellminthresholds")) { c->Message(Chat::White, "note: Controls at what target HP % the bot will stop casting different spell types."); @@ -14,6 +13,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) p.description = { "Controls at what target HP % the bot will stop casting different spell types." }; p.notes = { + "- Targeting yourself for this command will allow you to control your own settings for how bots cast on you", "- All pet types are based off the pet's owner's setting", "- Any remaining types use the owner's setting when a pet is the target", "- All Heals, Cures, Buffs (DS and resists included) are based off the target's setting, not the caster", @@ -21,8 +21,8 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) }; p.example_format = { - fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), - fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) + fmt::format("{} [Type Shortname] [value] [actionable, default: target]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable, default: target]", sep->arg[0]) }; p.examples_one = { @@ -75,6 +75,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) c->SendPopupToClient(sep->arg[0], popup_text.c_str()); c->SendSpellTypePrompts(); + c->SendSpellTypePrompts(false, true); if (RuleB(Bots, SendClassRaceOnHelp)) { c->Message( @@ -95,13 +96,26 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) bool current_check = false; uint16 spell_type = 0; uint32 type_value = 0; + Mob* target = c->GetTarget(); + bool clientSetting = (target && target == c); - // String/Int type checks if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + if ( + (clientSetting && !IsClientBotSpellType(spell_type)) || + (!clientSetting && (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END)) + ) { + c->Message( + Chat::Yellow, + clientSetting ? "Invalid spell type for clients." : "You must choose a valid spell type. Spell types range from %i to %i", + BotSpellTypes::START, + BotSpellTypes::END + ); + + if (clientSetting) { + c->SendSpellTypePrompts(false, true); + } return; } @@ -109,6 +123,11 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) else { if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { spell_type = c->GetSpellTypeIDByShortName(arg1); + + if (clientSetting && !IsClientBotSpellType(spell_type)) { + c->Message(Chat::Yellow, "Invalid spell type for clients."); + c->SendSpellTypePrompts(false, true); + } } else { c->Message( @@ -153,66 +172,103 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) } const int ab_mask = ActionableBots::ABM_Type1; - std::string class_race_arg = sep->arg[ab_arg]; - bool class_race_check = false; - if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { - class_race_check = true; - } + std::string actionable_arg = sep->arg[ab_arg]; - std::vector sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { - return; + if (actionable_arg.empty()) { + actionable_arg = "target"; } - sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + if (!clientSetting) { - Bot* first_found = nullptr; - int success_count = 0; - for (auto my_bot : sbl) { - if (my_bot->BotPassiveCheck()) { - continue; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } - if (!first_found) { - first_found = my_bot; + std::vector sbl; + + if (ActionableBots::PopulateSBL(c, actionable_arg, sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; } - if (current_check) { - c->Message( - Chat::Green, - fmt::format( - "{} says, 'My [{}] minimum threshold is currently [{}%%].'", - my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), - my_bot->GetSpellMinThreshold(spell_type) - ).c_str() - ); + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + Bot* first_found = nullptr; + int success_count = 0; + + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum threshold is currently [{}%%].'", + my_bot->GetCleanName(), + c->GetSpellTypeNameByID(spell_type), + my_bot->GetSpellMinThreshold(spell_type) + ).c_str() + ); + } + else { + my_bot->SetSpellMinThreshold(spell_type, type_value); + ++success_count; + } } - else { - my_bot->SetSpellMinThreshold(spell_type, type_value); - ++success_count; + + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] minimum threshold was set to [{}%%].'", + first_found->GetCleanName(), + c->GetSpellTypeNameByID(spell_type), + first_found->GetSpellMinThreshold(spell_type) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] minimum threshold to [{}%%].", + success_count, + c->GetSpellTypeNameByID(spell_type), + type_value + ).c_str() + ); + } } } - if (!current_check) { - if (success_count == 1 && first_found) { + else { + if (current_check) { c->Message( Chat::Green, fmt::format( - "{} says, 'My [{}] minimum threshold was set to [{}%%].'", - first_found->GetCleanName(), + "Your [{}] minimum threshold is currently [{}%%].", c->GetSpellTypeNameByID(spell_type), - first_found->GetSpellMinThreshold(spell_type) + c->GetSpellMinThreshold(spell_type) ).c_str() ); } else { + c->SetSpellMinThreshold(spell_type, type_value); + c->Message( Chat::Green, fmt::format( - "{} of your bots set their [{}] minimum threshold to [{}%%].", - success_count, + "Your [{}] minimum threshold was set to [{}%%].", c->GetSpellTypeNameByID(spell_type), - type_value + c->GetSpellMinThreshold(spell_type) ).c_str() ); } diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 374d645476..348c5ea41b 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -298,7 +298,7 @@ void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) Message( Chat::Yellow, fmt::format( - "You can view spell types by {} or {}.", + "You can view client spell types by {} or {}.", Saylink::Silent( fmt::format("^spelltypeids client"), "ID" ), diff --git a/zone/command.cpp b/zone/command.cpp index 8b24484fb2..94a0ef9291 100644 --- a/zone/command.cpp +++ b/zone/command.cpp @@ -219,10 +219,6 @@ int command_init(void) command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", AccountStatus::Steward, command_spawn) || command_add("spawneditmass", "[Search Criteria] [Edit Option] [Edit Value] [Apply] Mass editing spawn command (Apply is optional, 0 = False, 1 = True, default is False)", AccountStatus::GMLeadAdmin, command_spawneditmass) || command_add("spawnfix", "Find targeted NPC in database based on its X/Y/heading and update the database to make it spawn at your current location/heading.", AccountStatus::GMAreas, command_spawnfix) || - command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, command_spell_delays) || - //command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, command_spell_holds) || //currently unusued - command_add("spellmaxthresholds", "Controls the minimum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_max_thresholds) || - command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, command_spell_min_thresholds) || command_add("stun", "[duration] - Stuns you or your target for duration", AccountStatus::GMAdmin, command_stun) || command_add("summon", "[Character Name] - Summons your corpse, NPC, or player target, or by character name if specified", AccountStatus::QuestTroupe, command_summon) || command_add("summonburiedplayercorpse", "Summons the target's oldest buried corpse, if any exist.", AccountStatus::GMAdmin, command_summonburiedplayercorpse) || @@ -918,10 +914,6 @@ void command_bot(Client *c, const Seperator *sep) #include "gm_commands/spawn.cpp" #include "gm_commands/spawneditmass.cpp" #include "gm_commands/spawnfix.cpp" -#include "gm_commands/spell_delays.cpp" -#include "gm_commands/spell_holds.cpp" -#include "gm_commands/spell_max_thresholds.cpp" -#include "gm_commands/spell_min_thresholds.cpp" #include "gm_commands/faction_association.cpp" #include "gm_commands/stun.cpp" #include "gm_commands/summon.cpp" diff --git a/zone/command.h b/zone/command.h index d3096ce39b..4ea728a30b 100644 --- a/zone/command.h +++ b/zone/command.h @@ -173,10 +173,6 @@ void command_shutdown(Client *c, const Seperator *sep); void command_spawn(Client *c, const Seperator *sep); void command_spawneditmass(Client *c, const Seperator *sep); void command_spawnfix(Client *c, const Seperator *sep); -void command_spell_delays(Client* c, const Seperator* sep); -//void command_spell_holds(Client* c, const Seperator* sep); //currently unusued -void command_spell_max_thresholds(Client* c, const Seperator* sep); -void command_spell_min_thresholds(Client* c, const Seperator* sep); void command_stun(Client *c, const Seperator *sep); void command_summon(Client *c, const Seperator *sep); void command_summonburiedplayercorpse(Client *c, const Seperator *sep); diff --git a/zone/gm_commands/spell_delays.cpp b/zone/gm_commands/spell_delays.cpp deleted file mode 100644 index 21b744823c..0000000000 --- a/zone/gm_commands/spell_delays.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "../command.h" - -void command_spell_delays(Client* c, const Seperator* sep) -{ - const int arguments = sep->argnum; - if (arguments) { - const bool is_help = !strcasecmp(sep->arg[1], "help"); - - if (is_help) { - BotCommandHelpParams p; - - p.description = { "Controls how often bots can cast certain spell types on you" }; - p.notes = - { - "- All pet types are control your how your pet will be affected" - }; - p.example_format = - { - fmt::format( - "{} [Type Shortname] [value]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value]" - , sep->arg[0] - ) - }; - p.examples_one = - { - "To set Very Fast Heals to be received every 1 second:", - fmt::format( - "{} {} 1000", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::VeryFastHeals) - ), - fmt::format( - "{} {} 1000", - sep->arg[0], - BotSpellTypes::VeryFastHeals - ) - }; - p.examples_two = - { - "To check your current Regular Heal delay:", - fmt::format( - "{} {} current", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::RegularHeal) - ), - fmt::format( - "{} {} current", - sep->arg[0], - BotSpellTypes::RegularHeal - ) - }; - - std::string popup_text = c->SendBotCommandHelpWindow(p); - popup_text = DialogueWindow::Table(popup_text); - - c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->SendSpellTypePrompts(false, true); - - return; - } - } - - std::string arg1 = sep->arg[1]; - std::string arg2 = sep->arg[2]; - int ab_arg = 2; - bool current_check = false; - uint16 spell_type = 0; - uint32 type_value = 0; - - if (sep->IsNumber(1)) { - spell_type = atoi(sep->arg[1]); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - - return; - } - } - else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - } - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - // Enable/Disable/Current checks - if (sep->IsNumber(2)) { - type_value = atoi(sep->arg[2]); - ++ab_arg; - if (type_value < 0 || type_value > 60000) { - c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); - - return; - } - } - else if (!arg2.compare("current")) { - ++ab_arg; - current_check = true; - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - - if (current_check) { - c->Message( - Chat::Green, - fmt::format( - "Your [{}] delay is currently {} seconds.'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellDelay(spell_type) / 1000.00 - ).c_str() - ); - } - else { - c->SetSpellDelay(spell_type, type_value); - c->Message( - Chat::Green, - fmt::format( - "Your [{}] delay was set to {} seconds.'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellDelay(spell_type) / 1000.00 - ).c_str() - ); - } -} diff --git a/zone/gm_commands/spell_holds.cpp b/zone/gm_commands/spell_holds.cpp deleted file mode 100644 index be0c9cbca9..0000000000 --- a/zone/gm_commands/spell_holds.cpp +++ /dev/null @@ -1,161 +0,0 @@ -#include "../command.h" - -void command_spell_holds(Client *c, const Seperator *sep) -{ - //unused for clients - c->Message(Chat::Yellow, "Spell Holds for players is currently unused."); - return; - - const int arguments = sep->argnum; - if (arguments) { - const bool is_help = !strcasecmp(sep->arg[1], "help"); - - if (is_help) { - BotCommandHelpParams p; - - p.description = { "Toggles whether or not bots can cast certain spell types on you" }; - p.notes = - { - "- All pet types are control your how your pet will be affected" - }; - p.example_format = - { - fmt::format( - "{} [Type Shortname] [value]", - sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value]", - sep->arg[0] - ) - }; - p.examples_one = - { - "To set DoTs to be held:", - fmt::format( - "{} {} 1", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) - ), - fmt::format( - "{} {} 1", - sep->arg[0], - BotSpellTypes::DOT - ) - }; - p.examples_two = - { - "To check your current DoT settings:", - fmt::format( - "{} {} current", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) - ), - fmt::format( - "{} {} current", - sep->arg[0], - BotSpellTypes::DOT - ) - }; - - std::string popup_text = c->SendBotCommandHelpWindow(p); - popup_text = DialogueWindow::Table(popup_text); - - c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->SendSpellTypePrompts(false, true); - - return; - } - } - - std::string arg1 = sep->arg[1]; - std::string arg2 = sep->arg[2]; - int ab_arg = 2; - bool current_check = false; - uint16 spell_type = 0; - uint32 type_value = 0; - - if (sep->IsNumber(1)) { - spell_type = atoi(sep->arg[1]); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - - return; - } - } - else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - } - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - // Enable/Disable/Current checks - if (sep->IsNumber(2)) { - type_value = atoi(sep->arg[2]); - ++ab_arg; - if (type_value < 0 || type_value > 1) { - c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); - - return; - } - } - else if (!arg2.compare("current")) { - ++ab_arg; - current_check = true; - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - - if (current_check) { - c->Message( - Chat::Green, - fmt::format( - "Your [{}] spell hold is currently [{}].'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellHold(spell_type) ? "enabled" : "disabled" - ).c_str() - ); - } - else { - c->SetSpellHold(spell_type, type_value); - c->Message( - Chat::Green, - fmt::format( - "Your [{}] spell hold was [{}].'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellHold(spell_type) ? "enabled" : "disabled" - ).c_str() - ); - } -} diff --git a/zone/gm_commands/spell_max_thresholds.cpp b/zone/gm_commands/spell_max_thresholds.cpp deleted file mode 100644 index 914163f27a..0000000000 --- a/zone/gm_commands/spell_max_thresholds.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "../command.h" - -void command_spell_max_thresholds(Client* c, const Seperator* sep) -{ - const int arguments = sep->argnum; - if (arguments) { - const bool is_help = !strcasecmp(sep->arg[1], "help"); - - if (is_help) { - BotCommandHelpParams p; - - p.description = { "Threshold of your own health when bots will start casting the chosen spell type" }; - p.notes = - { - "- All pet types are control your how your pet will be affected" - }; - p.example_format = - { - fmt::format( - "{} [Type Shortname] [value]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value]" - , sep->arg[0] - ) - }; - p.examples_one = - { - "To set Complete Heals to start at 90% health:", - fmt::format( - "{} {} 90", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::CompleteHeal) - ), - fmt::format( - "{} {} 90", - sep->arg[0], - BotSpellTypes::CompleteHeal - ) - }; - p.examples_two = - { - "To check your current HoT Heal settings:", - fmt::format( - "{} {} current", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::HoTHeals) - ), - fmt::format( - "{} {} current", - sep->arg[0], - BotSpellTypes::HoTHeals - ) - }; - - std::string popup_text = c->SendBotCommandHelpWindow(p); - popup_text = DialogueWindow::Table(popup_text); - - c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->SendSpellTypePrompts(false, true); - - return; - } - } - - std::string arg1 = sep->arg[1]; - std::string arg2 = sep->arg[2]; - int ab_arg = 2; - bool current_check = false; - uint16 spell_type = 0; - uint32 type_value = 0; - - if (sep->IsNumber(1)) { - spell_type = atoi(sep->arg[1]); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - - return; - } - } - else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - } - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - // Enable/Disable/Current checks - if (sep->IsNumber(2)) { - type_value = atoi(sep->arg[2]); - ++ab_arg; - if (type_value < 0 || type_value > 100) { - c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); - - return; - } - } - else if (!arg2.compare("current")) { - ++ab_arg; - current_check = true; - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - - if (current_check) { - c->Message( - Chat::Green, - fmt::format( - "Your [{}] maximum hold is currently [{}%%].'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellMaxThreshold(spell_type) - ).c_str() - ); - } - else { - c->SetSpellMaxThreshold(spell_type, type_value); - c->Message( - Chat::Green, - fmt::format( - "Your [{}] maximum hold was set to [{}%%].'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellMaxThreshold(spell_type) - ).c_str() - ); - } -} diff --git a/zone/gm_commands/spell_min_thresholds.cpp b/zone/gm_commands/spell_min_thresholds.cpp deleted file mode 100644 index f8b48dbedf..0000000000 --- a/zone/gm_commands/spell_min_thresholds.cpp +++ /dev/null @@ -1,157 +0,0 @@ -#include "../command.h" - -void command_spell_min_thresholds(Client* c, const Seperator* sep) -{ - const int arguments = sep->argnum; - if (arguments) { - const bool is_help = !strcasecmp(sep->arg[1], "help"); - - if (is_help) { - BotCommandHelpParams p; - - p.description = { "Threshold of your own health when bots will stop casting the chosen spell type" }; - p.notes = - { - "- All pet types are control your how your pet will be affected" - }; - p.example_format = - { - fmt::format( - "{} [Type Shortname] [value]" - , sep->arg[0] - ), - fmt::format( - "{} [Type ID] [value]" - , sep->arg[0] - ) - }; - p.examples_one = - { - "To set Fast Heals to be stopped at 65% health:", - fmt::format( - "{} {} 65", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) - ), - fmt::format( - "{} {} 65", - sep->arg[0], - BotSpellTypes::FastHeals - ) - }; - p.examples_two = - { - "To check your current Cure settings:", - fmt::format( - "{} {} current", - sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) - ), - fmt::format( - "{} {} current", - sep->arg[0], - BotSpellTypes::Cure - ) - }; - - std::string popup_text = c->SendBotCommandHelpWindow(p); - popup_text = DialogueWindow::Table(popup_text); - - c->SendPopupToClient(sep->arg[0], popup_text.c_str()); - c->SendSpellTypePrompts(false, true); - - return; - } - } - - std::string arg1 = sep->arg[1]; - std::string arg2 = sep->arg[2]; - int ab_arg = 2; - bool current_check = false; - uint16 spell_type = 0; - uint32 type_value = 0; - - if (sep->IsNumber(1)) { - spell_type = atoi(sep->arg[1]); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - - return; - } - } - else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); - - if (!IsClientBotSpellType(spell_type)) { - c->Message(Chat::Yellow, "Invalid spell type."); - c->SendSpellTypePrompts(false, true); - } - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - } - - // Enable/Disable/Current checks - if (sep->IsNumber(2)) { - type_value = atoi(sep->arg[2]); - ++ab_arg; - if (type_value < 0 || type_value > 100) { - c->Message(Chat::Yellow, "You must enter a value between 0-100 (0%% to 100%% of your health)."); - - return; - } - } - else if (!arg2.compare("current")) { - ++ab_arg; - current_check = true; - } - else { - c->Message( - Chat::Yellow, - fmt::format( - "Incorrect argument, use {} for information regarding this command.", - Saylink::Silent( - fmt::format("{} help", sep->arg[0]) - ) - ).c_str() - ); - - return; - } - - if (current_check) { - c->Message( - Chat::Green, - fmt::format( - "Your [{}] minimum hold is currently [{}%%].'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellMinThreshold(spell_type) - ).c_str() - ); - } - else { - c->SetSpellMinThreshold(spell_type, type_value); - c->Message( - Chat::Green, - fmt::format( - "Your [{}] minimum hold was set to [{}%%].'", - c->GetSpellTypeNameByID(spell_type), - c->GetSpellMinThreshold(spell_type) - ).c_str() - ); - } -} From fa69e4ac1a24dad1c7c91359150f485354292f91 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 12:21:45 -0600 Subject: [PATCH 298/394] Fix alignment issues --- common/spdat.h | 498 ++++++++++++++++++++++++------------------------- zone/bot.h | 166 ++++++++--------- zone/mob.h | 39 ++-- 3 files changed, 351 insertions(+), 352 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index 7ee45e7105..08cf55ee88 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -627,269 +627,269 @@ enum ProcType enum SpellTypes : uint32 { - SpellType_Nuke = (1 << 0), - SpellType_Heal = (1 << 1), - SpellType_Root = (1 << 2), - SpellType_Buff = (1 << 3), - SpellType_Escape = (1 << 4), - SpellType_Pet = (1 << 5), - SpellType_Lifetap = (1 << 6), - SpellType_Snare = (1 << 7), - SpellType_DOT = (1 << 8), - SpellType_Dispel = (1 << 9), - SpellType_InCombatBuff = (1 << 10), - SpellType_Mez = (1 << 11), - SpellType_Charm = (1 << 12), - SpellType_Slow = (1 << 13), - SpellType_Debuff = (1 << 14), - SpellType_Cure = (1 << 15), - SpellType_Resurrect = (1 << 16), - SpellType_HateRedux = (1 << 17), - SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs - SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs - SpellType_PreCombatBuff = (1 << 20), - SpellType_PreCombatBuffSong = (1 << 21) + SpellType_Nuke = (1 << 0), + SpellType_Heal = (1 << 1), + SpellType_Root = (1 << 2), + SpellType_Buff = (1 << 3), + SpellType_Escape = (1 << 4), + SpellType_Pet = (1 << 5), + SpellType_Lifetap = (1 << 6), + SpellType_Snare = (1 << 7), + SpellType_DOT = (1 << 8), + SpellType_Dispel = (1 << 9), + SpellType_InCombatBuff = (1 << 10), + SpellType_Mez = (1 << 11), + SpellType_Charm = (1 << 12), + SpellType_Slow = (1 << 13), + SpellType_Debuff = (1 << 14), + SpellType_Cure = (1 << 15), + SpellType_Resurrect = (1 << 16), + SpellType_HateRedux = (1 << 17), + SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs + SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs + SpellType_PreCombatBuff = (1 << 20), + SpellType_PreCombatBuffSong = (1 << 21) }; namespace BotSpellTypes { - constexpr uint16 Nuke = 0; - constexpr uint16 RegularHeal = 1; - constexpr uint16 Root = 2; - constexpr uint16 Buff = 3; - constexpr uint16 Escape = 4; - constexpr uint16 Pet = 5; - constexpr uint16 Lifetap = 6; - constexpr uint16 Snare = 7; - constexpr uint16 DOT = 8; - constexpr uint16 Dispel = 9; - constexpr uint16 InCombatBuff = 10; - constexpr uint16 Mez = 11; - constexpr uint16 Charm = 12; - constexpr uint16 Slow = 13; - constexpr uint16 Debuff = 14; - constexpr uint16 Cure = 15; - constexpr uint16 Resurrect = 16; - constexpr uint16 HateRedux = 17; - constexpr uint16 InCombatBuffSong = 18; - constexpr uint16 OutOfCombatBuffSong = 19; - constexpr uint16 PreCombatBuff = 20; - constexpr uint16 PreCombatBuffSong = 21; - constexpr uint16 Fear = 22; - constexpr uint16 Stun = 23; - constexpr uint16 HateLine = 24; - constexpr uint16 GroupCures = 25; - constexpr uint16 CompleteHeal = 26; - constexpr uint16 FastHeals = 27; - constexpr uint16 VeryFastHeals = 28; - constexpr uint16 GroupHeals = 29; - constexpr uint16 GroupCompleteHeals = 30; - constexpr uint16 GroupHoTHeals = 31; - constexpr uint16 HoTHeals = 32; - constexpr uint16 AENukes = 33; - constexpr uint16 AERains = 34; - constexpr uint16 AEMez = 35; - constexpr uint16 AEStun = 36; - constexpr uint16 AEDebuff = 37; - constexpr uint16 AESlow = 38; - constexpr uint16 AESnare = 39; - constexpr uint16 AEFear = 40; - constexpr uint16 AEDispel = 41; - constexpr uint16 AERoot = 42; - constexpr uint16 AEDoT = 43; - constexpr uint16 AELifetap = 44; - constexpr uint16 AEHateLine = 45; - constexpr uint16 PBAENuke = 46; - constexpr uint16 PetBuffs = 47; - constexpr uint16 PetRegularHeals = 48; - constexpr uint16 PetCompleteHeals = 49; - constexpr uint16 PetFastHeals = 50; - constexpr uint16 PetVeryFastHeals = 51; - constexpr uint16 PetHoTHeals = 52; - constexpr uint16 PetCures = 53; - constexpr uint16 DamageShields = 54; - constexpr uint16 ResistBuffs = 55; - constexpr uint16 PetDamageShields = 56; - constexpr uint16 PetResistBuffs = 57; + constexpr uint16 Nuke = 0; + constexpr uint16 RegularHeal = 1; + constexpr uint16 Root = 2; + constexpr uint16 Buff = 3; + constexpr uint16 Escape = 4; + constexpr uint16 Pet = 5; + constexpr uint16 Lifetap = 6; + constexpr uint16 Snare = 7; + constexpr uint16 DOT = 8; + constexpr uint16 Dispel = 9; + constexpr uint16 InCombatBuff = 10; + constexpr uint16 Mez = 11; + constexpr uint16 Charm = 12; + constexpr uint16 Slow = 13; + constexpr uint16 Debuff = 14; + constexpr uint16 Cure = 15; + constexpr uint16 Resurrect = 16; + constexpr uint16 HateRedux = 17; + constexpr uint16 InCombatBuffSong = 18; + constexpr uint16 OutOfCombatBuffSong = 19; + constexpr uint16 PreCombatBuff = 20; + constexpr uint16 PreCombatBuffSong = 21; + constexpr uint16 Fear = 22; + constexpr uint16 Stun = 23; + constexpr uint16 HateLine = 24; + constexpr uint16 GroupCures = 25; + constexpr uint16 CompleteHeal = 26; + constexpr uint16 FastHeals = 27; + constexpr uint16 VeryFastHeals = 28; + constexpr uint16 GroupHeals = 29; + constexpr uint16 GroupCompleteHeals = 30; + constexpr uint16 GroupHoTHeals = 31; + constexpr uint16 HoTHeals = 32; + constexpr uint16 AENukes = 33; + constexpr uint16 AERains = 34; + constexpr uint16 AEMez = 35; + constexpr uint16 AEStun = 36; + constexpr uint16 AEDebuff = 37; + constexpr uint16 AESlow = 38; + constexpr uint16 AESnare = 39; + constexpr uint16 AEFear = 40; + constexpr uint16 AEDispel = 41; + constexpr uint16 AERoot = 42; + constexpr uint16 AEDoT = 43; + constexpr uint16 AELifetap = 44; + constexpr uint16 AEHateLine = 45; + constexpr uint16 PBAENuke = 46; + constexpr uint16 PetBuffs = 47; + constexpr uint16 PetRegularHeals = 48; + constexpr uint16 PetCompleteHeals = 49; + constexpr uint16 PetFastHeals = 50; + constexpr uint16 PetVeryFastHeals = 51; + constexpr uint16 PetHoTHeals = 52; + constexpr uint16 PetCures = 53; + constexpr uint16 DamageShields = 54; + constexpr uint16 ResistBuffs = 55; + constexpr uint16 PetDamageShields = 56; + constexpr uint16 PetResistBuffs = 57; // Command Spell Types - constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic - constexpr uint16 Lull = 101; - constexpr uint16 Succor = 102; - constexpr uint16 BindAffinity = 103; - constexpr uint16 Identify = 104; - constexpr uint16 Levitate = 105; - constexpr uint16 Rune = 106; - constexpr uint16 WaterBreathing = 107; - constexpr uint16 Size = 108; - constexpr uint16 Invisibility = 109; - constexpr uint16 MovementSpeed = 110; - constexpr uint16 SendHome = 111; - constexpr uint16 SummonCorpse = 112; - constexpr uint16 AELull = 113; + constexpr uint16 Teleport = 100; // this is handled by ^depart so uses other logic + constexpr uint16 Lull = 101; + constexpr uint16 Succor = 102; + constexpr uint16 BindAffinity = 103; + constexpr uint16 Identify = 104; + constexpr uint16 Levitate = 105; + constexpr uint16 Rune = 106; + constexpr uint16 WaterBreathing = 107; + constexpr uint16 Size = 108; + constexpr uint16 Invisibility = 109; + constexpr uint16 MovementSpeed = 110; + constexpr uint16 SendHome = 111; + constexpr uint16 SummonCorpse = 112; + constexpr uint16 AELull = 113; // Discipline Types - constexpr uint16 Discipline = 200; - constexpr uint16 DiscAggressive = 201; - constexpr uint16 DiscDefensive = 202; - constexpr uint16 DiscUtility = 203; - - constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this - constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed - constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this - constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed - constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this - constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed + constexpr uint16 Discipline = 200; + constexpr uint16 DiscAggressive = 201; + constexpr uint16 DiscDefensive = 202; + constexpr uint16 DiscUtility = 203; + + constexpr uint16 START = BotSpellTypes::Nuke; // Do not remove or change this + constexpr uint16 END = BotSpellTypes::PetResistBuffs; // Do not remove this, increment as needed + constexpr uint16 COMMANDED_START = BotSpellTypes::Lull; // Do not remove or change this + constexpr uint16 COMMANDED_END = BotSpellTypes::AELull; // Do not remove this, increment as needed + constexpr uint16 DISCIPLINE_START = BotSpellTypes::Discipline; // Do not remove or change this + constexpr uint16 DISCIPLINE_END = BotSpellTypes::DiscUtility; // Do not remove this, increment as needed } static std::map spellType_names = { - { BotSpellTypes::Nuke, "Nuke" }, - { BotSpellTypes::RegularHeal, "Regular Heal" }, - { BotSpellTypes::Root, "Root" }, - { BotSpellTypes::Buff, "Buff" }, - { BotSpellTypes::Escape, "Escape" }, - { BotSpellTypes::Pet, "Pet" }, - { BotSpellTypes::Lifetap, "Lifetap" }, - { BotSpellTypes::Snare, "Snare" }, - { BotSpellTypes::DOT, "DoT" }, - { BotSpellTypes::Dispel, "Dispel" }, - { BotSpellTypes::InCombatBuff, "In-Combat Buff" }, - { BotSpellTypes::Mez, "Mez" }, - { BotSpellTypes::Charm, "Charm" }, - { BotSpellTypes::Slow, "Slow" }, - { BotSpellTypes::Debuff, "Debuff" }, - { BotSpellTypes::Cure, "Cure" }, - { BotSpellTypes::GroupCures, "Group Cure" }, - { BotSpellTypes::PetCures, "Pet Cure" }, - { BotSpellTypes::Resurrect, "Resurrect" }, - { BotSpellTypes::HateRedux, "Hate Reduction" }, - { BotSpellTypes::InCombatBuffSong, "In-Combat Buff Song" }, - { BotSpellTypes::OutOfCombatBuffSong, "Out-of-Combat Buff Song" }, - { BotSpellTypes::PreCombatBuff, "Pre-Combat Buff" }, - { BotSpellTypes::PreCombatBuffSong, "Pre-Combat Buff Song" }, - { BotSpellTypes::Fear, "Fear" }, - { BotSpellTypes::Stun, "Stun" }, - { BotSpellTypes::CompleteHeal, "Complete Heal" }, - { BotSpellTypes::FastHeals, "Fast Heal" }, - { BotSpellTypes::VeryFastHeals, "Very Fast Heal" }, - { BotSpellTypes::GroupHeals, "Group Heal" }, - { BotSpellTypes::GroupCompleteHeals, "Group Complete Heal" }, - { BotSpellTypes::GroupHoTHeals, "Group HoT Heal" }, - { BotSpellTypes::HoTHeals, "HoT Heal" }, - { BotSpellTypes::AENukes, "AE Nuke" }, - { BotSpellTypes::AERains, "AE Rain" }, - { BotSpellTypes::AEMez, "AE Mez" }, - { BotSpellTypes::AEStun, "AE Stun" }, - { BotSpellTypes::AEDebuff, "AE Debuff" }, - { BotSpellTypes::AESlow, "AE Slow" }, - { BotSpellTypes::AESnare, "AE Snare" }, - { BotSpellTypes::AEFear, "AE Fear" }, - { BotSpellTypes::AEDispel, "AE Dispel" }, - { BotSpellTypes::AERoot, "AE Root" }, - { BotSpellTypes::AEDoT, "AE DoT" }, - { BotSpellTypes::AELifetap, "AE Lifetap" }, - { BotSpellTypes::PBAENuke, "PBAE Nuke" }, - { BotSpellTypes::PetBuffs, "Pet Buff" }, - { BotSpellTypes::PetRegularHeals, "Pet Regular Heal" }, - { BotSpellTypes::PetCompleteHeals, "Pet Complete Heal" }, - { BotSpellTypes::PetFastHeals, "Pet Fast Heal" }, - { BotSpellTypes::PetVeryFastHeals, "Pet Very Fast Heal" }, - { BotSpellTypes::PetHoTHeals, "Pet HoT Heal" }, - { BotSpellTypes::DamageShields, "Damage Shield" }, - { BotSpellTypes::ResistBuffs, "Resist Buff" }, - { BotSpellTypes::PetDamageShields, "Pet Damage Shield" }, - { BotSpellTypes::PetResistBuffs, "Pet Resist Buff" }, - { BotSpellTypes::HateLine, "Hate Line" }, - { BotSpellTypes::AEHateLine, "AE Hate Line" }, - { BotSpellTypes::Lull, "Lull" }, - { BotSpellTypes::Teleport, "Teleport" }, - { BotSpellTypes::Succor, "Succor" }, - { BotSpellTypes::BindAffinity, "Bind Affinity" }, - { BotSpellTypes::Identify, "Identify" }, - { BotSpellTypes::Levitate, "Levitate" }, - { BotSpellTypes::Rune, "Rune" }, - { BotSpellTypes::WaterBreathing, "Water Breathing" }, - { BotSpellTypes::Size, "Size" }, - { BotSpellTypes::Invisibility, "Invisibility" }, - { BotSpellTypes::MovementSpeed, "Movement Speed" }, - { BotSpellTypes::SendHome, "Send Home" }, - { BotSpellTypes::SummonCorpse, "Summon Corpse" }, - { BotSpellTypes::AELull, "AE Lull" } + { BotSpellTypes::Nuke, "Nuke" }, + { BotSpellTypes::RegularHeal, "Regular Heal" }, + { BotSpellTypes::Root, "Root" }, + { BotSpellTypes::Buff, "Buff" }, + { BotSpellTypes::Escape, "Escape" }, + { BotSpellTypes::Pet, "Pet" }, + { BotSpellTypes::Lifetap, "Lifetap" }, + { BotSpellTypes::Snare, "Snare" }, + { BotSpellTypes::DOT, "DoT" }, + { BotSpellTypes::Dispel, "Dispel" }, + { BotSpellTypes::InCombatBuff, "In-Combat Buff" }, + { BotSpellTypes::Mez, "Mez" }, + { BotSpellTypes::Charm, "Charm" }, + { BotSpellTypes::Slow, "Slow" }, + { BotSpellTypes::Debuff, "Debuff" }, + { BotSpellTypes::Cure, "Cure" }, + { BotSpellTypes::GroupCures, "Group Cure" }, + { BotSpellTypes::PetCures, "Pet Cure" }, + { BotSpellTypes::Resurrect, "Resurrect" }, + { BotSpellTypes::HateRedux, "Hate Reduction" }, + { BotSpellTypes::InCombatBuffSong, "In-Combat Buff Song" }, + { BotSpellTypes::OutOfCombatBuffSong, "Out-of-Combat Buff Song" }, + { BotSpellTypes::PreCombatBuff, "Pre-Combat Buff" }, + { BotSpellTypes::PreCombatBuffSong, "Pre-Combat Buff Song" }, + { BotSpellTypes::Fear, "Fear" }, + { BotSpellTypes::Stun, "Stun" }, + { BotSpellTypes::CompleteHeal, "Complete Heal" }, + { BotSpellTypes::FastHeals, "Fast Heal" }, + { BotSpellTypes::VeryFastHeals, "Very Fast Heal" }, + { BotSpellTypes::GroupHeals, "Group Heal" }, + { BotSpellTypes::GroupCompleteHeals, "Group Complete Heal" }, + { BotSpellTypes::GroupHoTHeals, "Group HoT Heal" }, + { BotSpellTypes::HoTHeals, "HoT Heal" }, + { BotSpellTypes::AENukes, "AE Nuke" }, + { BotSpellTypes::AERains, "AE Rain" }, + { BotSpellTypes::AEMez, "AE Mez" }, + { BotSpellTypes::AEStun, "AE Stun" }, + { BotSpellTypes::AEDebuff, "AE Debuff" }, + { BotSpellTypes::AESlow, "AE Slow" }, + { BotSpellTypes::AESnare, "AE Snare" }, + { BotSpellTypes::AEFear, "AE Fear" }, + { BotSpellTypes::AEDispel, "AE Dispel" }, + { BotSpellTypes::AERoot, "AE Root" }, + { BotSpellTypes::AEDoT, "AE DoT" }, + { BotSpellTypes::AELifetap, "AE Lifetap" }, + { BotSpellTypes::PBAENuke, "PBAE Nuke" }, + { BotSpellTypes::PetBuffs, "Pet Buff" }, + { BotSpellTypes::PetRegularHeals, "Pet Regular Heal" }, + { BotSpellTypes::PetCompleteHeals, "Pet Complete Heal" }, + { BotSpellTypes::PetFastHeals, "Pet Fast Heal" }, + { BotSpellTypes::PetVeryFastHeals, "Pet Very Fast Heal" }, + { BotSpellTypes::PetHoTHeals, "Pet HoT Heal" }, + { BotSpellTypes::DamageShields, "Damage Shield" }, + { BotSpellTypes::ResistBuffs, "Resist Buff" }, + { BotSpellTypes::PetDamageShields, "Pet Damage Shield" }, + { BotSpellTypes::PetResistBuffs, "Pet Resist Buff" }, + { BotSpellTypes::HateLine, "Hate Line" }, + { BotSpellTypes::AEHateLine, "AE Hate Line" }, + { BotSpellTypes::Lull, "Lull" }, + { BotSpellTypes::Teleport, "Teleport" }, + { BotSpellTypes::Succor, "Succor" }, + { BotSpellTypes::BindAffinity, "Bind Affinity" }, + { BotSpellTypes::Identify, "Identify" }, + { BotSpellTypes::Levitate, "Levitate" }, + { BotSpellTypes::Rune, "Rune" }, + { BotSpellTypes::WaterBreathing, "Water Breathing" }, + { BotSpellTypes::Size, "Size" }, + { BotSpellTypes::Invisibility, "Invisibility" }, + { BotSpellTypes::MovementSpeed, "Movement Speed" }, + { BotSpellTypes::SendHome, "Send Home" }, + { BotSpellTypes::SummonCorpse, "Summon Corpse" }, + { BotSpellTypes::AELull, "AE Lull" } }; static std::map spellType_shortNames = { - { BotSpellTypes::Nuke, "nukes" }, - { BotSpellTypes::RegularHeal, "regularheals" }, - { BotSpellTypes::Root, "roots" }, - { BotSpellTypes::Buff, "buffs" }, - { BotSpellTypes::Escape, "escapes" }, - { BotSpellTypes::Pet, "pets" }, - { BotSpellTypes::Lifetap, "lifetaps" }, - { BotSpellTypes::Snare, "snares" }, - { BotSpellTypes::DOT, "dots" }, - { BotSpellTypes::Dispel, "dispels" }, - { BotSpellTypes::InCombatBuff, "incombatbuffs" }, - { BotSpellTypes::Mez, "mez" }, - { BotSpellTypes::Charm, "charms" }, - { BotSpellTypes::Slow, "slows" }, - { BotSpellTypes::Debuff, "debuffs" }, - { BotSpellTypes::Cure, "cures" }, - { BotSpellTypes::GroupCures, "groupcures" }, - { BotSpellTypes::PetCures, "petcure" }, - { BotSpellTypes::Resurrect, "resurrect" }, - { BotSpellTypes::HateRedux, "hateredux" }, - { BotSpellTypes::InCombatBuffSong, "incombatbuffsongs" }, - { BotSpellTypes::OutOfCombatBuffSong, "outofcombatbuffsongs" }, - { BotSpellTypes::PreCombatBuff, "precombatbuffs" }, - { BotSpellTypes::PreCombatBuffSong, "precombatbuffsongs" }, - { BotSpellTypes::Fear, "fears" }, - { BotSpellTypes::Stun, "stuns" }, - { BotSpellTypes::CompleteHeal, "completeheals" }, - { BotSpellTypes::FastHeals, "fastheals" }, - { BotSpellTypes::VeryFastHeals, "veryfastheals" }, - { BotSpellTypes::GroupHeals, "groupheals" }, - { BotSpellTypes::GroupCompleteHeals, "groupcompleteheals" }, - { BotSpellTypes::GroupHoTHeals, "grouphotheals" }, - { BotSpellTypes::HoTHeals, "hotheals" }, - { BotSpellTypes::AENukes, "aenukes" }, - { BotSpellTypes::AERains, "aerains" }, - { BotSpellTypes::AEMez, "aemez" }, - { BotSpellTypes::AEStun, "aestuns" }, - { BotSpellTypes::AEDebuff, "aedebuffs" }, - { BotSpellTypes::AESlow, "aeslows" }, - { BotSpellTypes::AESnare, "aesnares" }, - { BotSpellTypes::AEFear, "aefears" }, - { BotSpellTypes::AEDispel, "aedispels" }, - { BotSpellTypes::AERoot, "aeroots" }, - { BotSpellTypes::AEDoT, "aedots" }, - { BotSpellTypes::AELifetap, "aelifetaps" }, - { BotSpellTypes::PBAENuke, "pbaenukes" }, - { BotSpellTypes::PetBuffs, "petbuffs" }, - { BotSpellTypes::PetRegularHeals, "petregularheals" }, - { BotSpellTypes::PetCompleteHeals, "petcompleteheals" }, - { BotSpellTypes::PetFastHeals, "petfastheals" }, - { BotSpellTypes::PetVeryFastHeals, "petveryfastheals" }, - { BotSpellTypes::PetHoTHeals, "pethotheals" }, - { BotSpellTypes::DamageShields, "damageshields" }, - { BotSpellTypes::ResistBuffs, "resistbuffs" }, - { BotSpellTypes::PetDamageShields, "petdamageshields" }, - { BotSpellTypes::PetResistBuffs, "petresistbuffs" }, - { BotSpellTypes::HateLine, "hateline" }, - { BotSpellTypes::AEHateLine, "aehateline" }, - { BotSpellTypes::Lull, "lull" }, - { BotSpellTypes::Teleport, "teleport" }, - { BotSpellTypes::Succor, "succor" }, - { BotSpellTypes::BindAffinity, "bindaffinity" }, - { BotSpellTypes::Identify, "identify" }, - { BotSpellTypes::Levitate, "levitate" }, - { BotSpellTypes::Rune, "rune" }, - { BotSpellTypes::WaterBreathing, "waterbreathing" }, - { BotSpellTypes::Size, "size" }, - { BotSpellTypes::Invisibility, "invisibility" }, - { BotSpellTypes::MovementSpeed, "movementspeed" }, - { BotSpellTypes::SendHome, "sendhome" }, - { BotSpellTypes::SummonCorpse, "summoncorpse" }, - { BotSpellTypes::AELull, "aelull" } + { BotSpellTypes::Nuke, "nukes" }, + { BotSpellTypes::RegularHeal, "regularheals" }, + { BotSpellTypes::Root, "roots" }, + { BotSpellTypes::Buff, "buffs" }, + { BotSpellTypes::Escape, "escapes" }, + { BotSpellTypes::Pet, "pets" }, + { BotSpellTypes::Lifetap, "lifetaps" }, + { BotSpellTypes::Snare, "snares" }, + { BotSpellTypes::DOT, "dots" }, + { BotSpellTypes::Dispel, "dispels" }, + { BotSpellTypes::InCombatBuff, "incombatbuffs" }, + { BotSpellTypes::Mez, "mez" }, + { BotSpellTypes::Charm, "charms" }, + { BotSpellTypes::Slow, "slows" }, + { BotSpellTypes::Debuff, "debuffs" }, + { BotSpellTypes::Cure, "cures" }, + { BotSpellTypes::GroupCures, "groupcures" }, + { BotSpellTypes::PetCures, "petcure" }, + { BotSpellTypes::Resurrect, "resurrect" }, + { BotSpellTypes::HateRedux, "hateredux" }, + { BotSpellTypes::InCombatBuffSong, "incombatbuffsongs" }, + { BotSpellTypes::OutOfCombatBuffSong, "outofcombatbuffsongs" }, + { BotSpellTypes::PreCombatBuff, "precombatbuffs" }, + { BotSpellTypes::PreCombatBuffSong, "precombatbuffsongs" }, + { BotSpellTypes::Fear, "fears" }, + { BotSpellTypes::Stun, "stuns" }, + { BotSpellTypes::CompleteHeal, "completeheals" }, + { BotSpellTypes::FastHeals, "fastheals" }, + { BotSpellTypes::VeryFastHeals, "veryfastheals" }, + { BotSpellTypes::GroupHeals, "groupheals" }, + { BotSpellTypes::GroupCompleteHeals, "groupcompleteheals" }, + { BotSpellTypes::GroupHoTHeals, "grouphotheals" }, + { BotSpellTypes::HoTHeals, "hotheals" }, + { BotSpellTypes::AENukes, "aenukes" }, + { BotSpellTypes::AERains, "aerains" }, + { BotSpellTypes::AEMez, "aemez" }, + { BotSpellTypes::AEStun, "aestuns" }, + { BotSpellTypes::AEDebuff, "aedebuffs" }, + { BotSpellTypes::AESlow, "aeslows" }, + { BotSpellTypes::AESnare, "aesnares" }, + { BotSpellTypes::AEFear, "aefears" }, + { BotSpellTypes::AEDispel, "aedispels" }, + { BotSpellTypes::AERoot, "aeroots" }, + { BotSpellTypes::AEDoT, "aedots" }, + { BotSpellTypes::AELifetap, "aelifetaps" }, + { BotSpellTypes::PBAENuke, "pbaenukes" }, + { BotSpellTypes::PetBuffs, "petbuffs" }, + { BotSpellTypes::PetRegularHeals, "petregularheals" }, + { BotSpellTypes::PetCompleteHeals, "petcompleteheals" }, + { BotSpellTypes::PetFastHeals, "petfastheals" }, + { BotSpellTypes::PetVeryFastHeals, "petveryfastheals" }, + { BotSpellTypes::PetHoTHeals, "pethotheals" }, + { BotSpellTypes::DamageShields, "damageshields" }, + { BotSpellTypes::ResistBuffs, "resistbuffs" }, + { BotSpellTypes::PetDamageShields, "petdamageshields" }, + { BotSpellTypes::PetResistBuffs, "petresistbuffs" }, + { BotSpellTypes::HateLine, "hateline" }, + { BotSpellTypes::AEHateLine, "aehateline" }, + { BotSpellTypes::Lull, "lull" }, + { BotSpellTypes::Teleport, "teleport" }, + { BotSpellTypes::Succor, "succor" }, + { BotSpellTypes::BindAffinity, "bindaffinity" }, + { BotSpellTypes::Identify, "identify" }, + { BotSpellTypes::Levitate, "levitate" }, + { BotSpellTypes::Rune, "rune" }, + { BotSpellTypes::WaterBreathing, "waterbreathing" }, + { BotSpellTypes::Size, "size" }, + { BotSpellTypes::Invisibility, "invisibility" }, + { BotSpellTypes::MovementSpeed, "movementspeed" }, + { BotSpellTypes::SendHome, "sendhome" }, + { BotSpellTypes::SummonCorpse, "summoncorpse" }, + { BotSpellTypes::AELull, "aelull" } }; const uint32 SPELL_TYPES_DETRIMENTAL = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow); diff --git a/zone/bot.h b/zone/bot.h index 1265ed5262..7381cbfbc4 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -94,28 +94,28 @@ enum BotCastingChanceConditional : uint8 }; namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed - constexpr uint8 BaseSetting = 0; - constexpr uint8 SpellHold = 1; - constexpr uint8 SpellDelay = 2; - constexpr uint8 SpellMinThreshold = 3; - constexpr uint8 SpellMaxThreshold = 4; - constexpr uint8 SpellTypeAggroCheck = 5; - constexpr uint8 SpellTypeMinManaPct = 6; - constexpr uint8 SpellTypeMaxManaPct = 7; - constexpr uint8 SpellTypeMinHPPct = 8; - constexpr uint8 SpellTypeMaxHPPct = 9; - constexpr uint8 SpellTypeIdlePriority = 10; - constexpr uint8 SpellTypeEngagedPriority = 11; - constexpr uint8 SpellTypePursuePriority = 12; - constexpr uint8 SpellTypeAEOrGroupTargetCount = 13; - constexpr uint8 SpellTypeRecastDelay = 14; - - constexpr uint16 START = BotSettingCategories::BaseSetting; - constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; - constexpr uint16 START_CLIENT = BotSettingCategories::SpellHold; - constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; - constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; - constexpr uint16 END_FULL = BotSettingCategories::SpellTypeRecastDelay; + constexpr uint8 BaseSetting = 0; + constexpr uint8 SpellHold = 1; + constexpr uint8 SpellDelay = 2; + constexpr uint8 SpellMinThreshold = 3; + constexpr uint8 SpellMaxThreshold = 4; + constexpr uint8 SpellTypeAggroCheck = 5; + constexpr uint8 SpellTypeMinManaPct = 6; + constexpr uint8 SpellTypeMaxManaPct = 7; + constexpr uint8 SpellTypeMinHPPct = 8; + constexpr uint8 SpellTypeMaxHPPct = 9; + constexpr uint8 SpellTypeIdlePriority = 10; + constexpr uint8 SpellTypeEngagedPriority = 11; + constexpr uint8 SpellTypePursuePriority = 12; + constexpr uint8 SpellTypeAEOrGroupTargetCount = 13; + constexpr uint8 SpellTypeRecastDelay = 14; + + constexpr uint16 START = BotSettingCategories::BaseSetting; + constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; + constexpr uint16 START_CLIENT = BotSettingCategories::SpellHold; + constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; + constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; + constexpr uint16 END_FULL = BotSettingCategories::SpellTypeRecastDelay; }; static std::map botSpellCategory_names = { @@ -137,81 +137,81 @@ static std::map botSpellCategory_names = { }; namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed - constexpr uint8 Idle = 0; - constexpr uint8 Engaged = 1; - constexpr uint8 Pursue = 2; + constexpr uint8 Idle = 0; + constexpr uint8 Engaged = 1; + constexpr uint8 Pursue = 2; - constexpr uint16 START = BotPriorityCategories::Idle; - constexpr uint16 END = BotPriorityCategories::Pursue; // Increment as needed + constexpr uint16 START = BotPriorityCategories::Idle; + constexpr uint16 END = BotPriorityCategories::Pursue; // Increment as needed }; namespace BotBaseSettings { - constexpr uint16 ExpansionBitmask = 0; - constexpr uint16 ShowHelm = 1; - constexpr uint16 FollowDistance = 2; - constexpr uint16 StopMeleeLevel = 3; - constexpr uint16 EnforceSpellSettings = 4; - constexpr uint16 RangedSetting = 5; - constexpr uint16 PetSetTypeSetting = 6; - constexpr uint16 BehindMob = 7; - constexpr uint16 DistanceRanged = 8; - constexpr uint16 IllusionBlock = 9; - constexpr uint16 MaxMeleeRange = 10; - constexpr uint16 MedInCombat = 11; - constexpr uint16 SitHPPct = 12; - constexpr uint16 SitManaPct = 13; - - constexpr uint16 START_ALL = ExpansionBitmask; - constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players - constexpr uint16 END = BotBaseSettings::SitManaPct; // Increment as needed + constexpr uint16 ExpansionBitmask = 0; + constexpr uint16 ShowHelm = 1; + constexpr uint16 FollowDistance = 2; + constexpr uint16 StopMeleeLevel = 3; + constexpr uint16 EnforceSpellSettings = 4; + constexpr uint16 RangedSetting = 5; + constexpr uint16 PetSetTypeSetting = 6; + constexpr uint16 BehindMob = 7; + constexpr uint16 DistanceRanged = 8; + constexpr uint16 IllusionBlock = 9; + constexpr uint16 MaxMeleeRange = 10; + constexpr uint16 MedInCombat = 11; + constexpr uint16 SitHPPct = 12; + constexpr uint16 SitManaPct = 13; + + constexpr uint16 START_ALL = ExpansionBitmask; + constexpr uint16 START = BotBaseSettings::ShowHelm; // Everything above this cannot be copied, changed or viewed by players + constexpr uint16 END = BotBaseSettings::SitManaPct; // Increment as needed }; static std::map botBaseSettings_names = { - { BotBaseSettings::ExpansionBitmask, "ExpansionBitmask" }, - { BotBaseSettings::ShowHelm, "ShowHelm" }, - { BotBaseSettings::FollowDistance, "FollowDistance" }, - { BotBaseSettings::StopMeleeLevel, "StopMeleeLevel" }, - { BotBaseSettings::EnforceSpellSettings, "EnforceSpellSettings" }, - { BotBaseSettings::RangedSetting, "RangedSetting" }, - { BotBaseSettings::PetSetTypeSetting, "PetSetTypeSetting" }, - { BotBaseSettings::BehindMob, "BehindMob" }, - { BotBaseSettings::DistanceRanged, "DistanceRanged" }, - { BotBaseSettings::IllusionBlock, "IllusionBlock" }, - { BotBaseSettings::MaxMeleeRange, "MaxMeleeRange" }, - { BotBaseSettings::MedInCombat, "MedInCombat" }, - { BotBaseSettings::SitHPPct, "SitHPPct" }, - { BotBaseSettings::SitManaPct, "SitManaPct" } + { BotBaseSettings::ExpansionBitmask, "ExpansionBitmask" }, + { BotBaseSettings::ShowHelm, "ShowHelm" }, + { BotBaseSettings::FollowDistance, "FollowDistance" }, + { BotBaseSettings::StopMeleeLevel, "StopMeleeLevel" }, + { BotBaseSettings::EnforceSpellSettings, "EnforceSpellSettings" }, + { BotBaseSettings::RangedSetting, "RangedSetting" }, + { BotBaseSettings::PetSetTypeSetting, "PetSetTypeSetting" }, + { BotBaseSettings::BehindMob, "BehindMob" }, + { BotBaseSettings::DistanceRanged, "DistanceRanged" }, + { BotBaseSettings::IllusionBlock, "IllusionBlock" }, + { BotBaseSettings::MaxMeleeRange, "MaxMeleeRange" }, + { BotBaseSettings::MedInCombat, "MedInCombat" }, + { BotBaseSettings::SitHPPct, "SitHPPct" }, + { BotBaseSettings::SitManaPct, "SitManaPct" } }; namespace CommandedSubTypes { - constexpr uint16 SingleTarget = 1; - constexpr uint16 GroupTarget = 2; - constexpr uint16 AETarget = 3; - constexpr uint16 SeeInvis = 4; - constexpr uint16 Invis = 5; - constexpr uint16 InvisUndead = 6; - constexpr uint16 InvisAnimals = 7; - constexpr uint16 Shrink = 8; - constexpr uint16 Grow = 9; - constexpr uint16 Selo = 10; - - constexpr uint16 START = CommandedSubTypes::SingleTarget; - constexpr uint16 END = CommandedSubTypes::Selo; + constexpr uint16 SingleTarget = 1; + constexpr uint16 GroupTarget = 2; + constexpr uint16 AETarget = 3; + constexpr uint16 SeeInvis = 4; + constexpr uint16 Invis = 5; + constexpr uint16 InvisUndead = 6; + constexpr uint16 InvisAnimals = 7; + constexpr uint16 Shrink = 8; + constexpr uint16 Grow = 9; + constexpr uint16 Selo = 10; + + constexpr uint16 START = CommandedSubTypes::SingleTarget; + constexpr uint16 END = CommandedSubTypes::Selo; }; - static std::map botSubType_names = { - { CommandedSubTypes::SingleTarget, "SingleTarget" }, - { CommandedSubTypes::GroupTarget, "GroupTarget" }, - { CommandedSubTypes::AETarget, "AETarget" }, - { CommandedSubTypes::SeeInvis, "SeeInvis" }, - { CommandedSubTypes::Invis, "Invis" }, - { CommandedSubTypes::InvisUndead, "InvisUndead" }, - { CommandedSubTypes::InvisAnimals, "InvisAnimals" }, - { CommandedSubTypes::Shrink, "Shrink" }, - { CommandedSubTypes::Grow, "Grow" }, - { CommandedSubTypes::Selo, "Selo" } + { CommandedSubTypes::SingleTarget, "SingleTarget" }, + { CommandedSubTypes::GroupTarget, "GroupTarget" }, + { CommandedSubTypes::AETarget, "AETarget" }, + { CommandedSubTypes::SeeInvis, "SeeInvis" }, + { CommandedSubTypes::Invis, "Invis" }, + { CommandedSubTypes::InvisUndead, "InvisUndead" }, + { CommandedSubTypes::InvisAnimals, "InvisAnimals" }, + { CommandedSubTypes::Shrink, "Shrink" }, + { CommandedSubTypes::Grow, "Grow" }, + { CommandedSubTypes::Selo, "Selo" } }; + class Bot : public NPC { friend class Mob; public: diff --git a/zone/mob.h b/zone/mob.h index e8bac4129d..513b4b6f23 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -93,26 +93,25 @@ struct AppearanceStruct { uint8 texture = UINT8_MAX; }; -struct BotSpellSettings_Struct -{ - uint16 spellType; // type ID of bot category - std::string shortName; // type short name of bot category - std::string name; // type name of bot category - bool hold; // 0 = allow spell type, 1 = hold spell type - uint16 delay; // delay between casts of spell type, 1ms-60,000ms - uint8 minThreshold; // minimum target health threshold to allow casting of spell type - uint8 maxThreshold; // maximum target health threshold to allow casting of spell type - uint16 resistLimit; // resist limit to skip spell type - bool aggroCheck; // whether or not to check for possible aggro before casting - uint8 minManaPct; // lower mana percentage limit to allow spell cast - uint8 maxManaPct; // upper mana percentage limit to allow spell cast - uint8 minHPPct; // lower HP percentage limit to allow spell cast - uint8 maxHPPct; // upper HP percentage limit to allow spell cast - uint16 idlePriority; // idle priority of the spell type - uint16 engagedPriority; // engaged priority of the spell type - uint16 pursuePriority; // pursue priority of the spell type - uint16 AEOrGroupTargetCount; // require target count to cast an AE or Group spell type - Timer recastTimer; // recast timer based off delay +struct BotSpellSettings_Struct { + uint16 spellType; // type ID of bot category + std::string shortName; // type short name of bot category + std::string name; // type name of bot category + bool hold; // 0 = allow spell type, 1 = hold spell type + uint16 delay; // delay between casts of spell type, 1ms-60,000ms + uint8 minThreshold; // minimum target health threshold to allow casting of spell type + uint8 maxThreshold; // maximum target health threshold to allow casting of spell type + uint16 resistLimit; // resist limit to skip spell type + bool aggroCheck; // whether or not to check for possible aggro before casting + uint8 minManaPct; // lower mana percentage limit to allow spell cast + uint8 maxManaPct; // upper mana percentage limit to allow spell cast + uint8 minHPPct; // lower HP percentage limit to allow spell cast + uint8 maxHPPct; // upper HP percentage limit to allow spell cast + uint16 idlePriority; // idle priority of the spell type + uint16 engagedPriority; // engaged priority of the spell type + uint16 pursuePriority; // pursue priority of the spell type + uint16 AEOrGroupTargetCount; // require target count to cast an AE or Group spell type + Timer recastTimer; // recast timer based off delay }; class DataBucketKey; From 3ac38610078d506a48d05acd58abc026a3df9e19 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 12:27:06 -0600 Subject: [PATCH 299/394] More alignment fixes --- zone/bot.h | 42 +++++++++++++++++++++--------------------- zone/client.h | 22 +++++++++++----------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/zone/bot.h b/zone/bot.h index 7381cbfbc4..d93d265eba 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -48,12 +48,12 @@ extern WorldServer worldserver; constexpr int NegativeItemReuse = -1; // Unlinked timer for items -constexpr uint8 SumWater = 1; -constexpr uint8 SumFire = 2; -constexpr uint8 SumAir = 3; -constexpr uint8 SumEarth = 4; -constexpr uint8 MonsterSum = 5; -constexpr uint8 SumMageMultiElement = 6; +constexpr uint8 SumWater = 1; +constexpr uint8 SumFire = 2; +constexpr uint8 SumAir = 3; +constexpr uint8 SumEarth = 4; +constexpr uint8 MonsterSum = 5; +constexpr uint8 SumMageMultiElement = 6; // nHSND negative Healer/Slower/Nuker/Doter // pH positive Healer @@ -119,21 +119,21 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed }; static std::map botSpellCategory_names = { - { BotSettingCategories::BaseSetting, "BaseSetting" }, - { BotSettingCategories::SpellHold, "SpellHolds" }, - { BotSettingCategories::SpellDelay, "SpellDelays" }, - { BotSettingCategories::SpellMinThreshold, "SpellMinThresholds" }, - { BotSettingCategories::SpellMaxThreshold, "SpellMaxThresholds" }, - { BotSettingCategories::SpellTypeAggroCheck, "SpellAggroChecks" }, - { BotSettingCategories::SpellTypeMinManaPct, "SpellMinManaPct" }, - { BotSettingCategories::SpellTypeMaxManaPct, "SpellMaxManaPct" }, - { BotSettingCategories::SpellTypeMinHPPct, "SpellMinHPPct" }, - { BotSettingCategories::SpellTypeMaxHPPct, "SpellMaxHPPct" }, - { BotSettingCategories::SpellTypeIdlePriority, "SpellIdlePriority" }, - { BotSettingCategories::SpellTypeEngagedPriority, "SpellEngagedPriority" }, - { BotSettingCategories::SpellTypePursuePriority, "SpellPursuePriority" }, - { BotSettingCategories::SpellTypeAEOrGroupTargetCount, "SpellTargetCounts" }, - { BotSettingCategories::SpellTypeRecastDelay, "SpellRecastDelay" } + { BotSettingCategories::BaseSetting, "BaseSetting" }, + { BotSettingCategories::SpellHold, "SpellHolds" }, + { BotSettingCategories::SpellDelay, "SpellDelays" }, + { BotSettingCategories::SpellMinThreshold, "SpellMinThresholds" }, + { BotSettingCategories::SpellMaxThreshold, "SpellMaxThresholds" }, + { BotSettingCategories::SpellTypeAggroCheck, "SpellAggroChecks" }, + { BotSettingCategories::SpellTypeMinManaPct, "SpellMinManaPct" }, + { BotSettingCategories::SpellTypeMaxManaPct, "SpellMaxManaPct" }, + { BotSettingCategories::SpellTypeMinHPPct, "SpellMinHPPct" }, + { BotSettingCategories::SpellTypeMaxHPPct, "SpellMaxHPPct" }, + { BotSettingCategories::SpellTypeIdlePriority, "SpellIdlePriority" }, + { BotSettingCategories::SpellTypeEngagedPriority, "SpellEngagedPriority" }, + { BotSettingCategories::SpellTypePursuePriority, "SpellPursuePriority" }, + { BotSettingCategories::SpellTypeAEOrGroupTargetCount, "SpellTargetCounts" }, + { BotSettingCategories::SpellTypeRecastDelay, "SpellRecastDelay" } }; namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed diff --git a/zone/client.h b/zone/client.h index 7a1d13e36b..bfd02cce37 100644 --- a/zone/client.h +++ b/zone/client.h @@ -198,17 +198,17 @@ struct RespawnOption }; struct BotCommandHelpParams { - std::vector description = {}; - std::vector notes = {}; - std::vector example_format = {}; - std::vector examples_one = {}; - std::vector examples_two = {}; - std::vector examples_three = {}; - std::vector actionables = {}; - std::vector options = {}; - std::vector options_one = {}; - std::vector options_two = {}; - std::vector options_three = {}; + std::vector description = {}; + std::vector notes = {}; + std::vector example_format = {}; + std::vector examples_one = {}; + std::vector examples_two = {}; + std::vector examples_three = {}; + std::vector actionables = {}; + std::vector options = {}; + std::vector options_one = {}; + std::vector options_two = {}; + std::vector options_three = {}; }; // do not ask what all these mean because I have no idea! From 529d26a1321844f90d1ee7219e1a823c6a0fbbc1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:28:12 -0600 Subject: [PATCH 300/394] More cleanup 1 --- common/net/websocket_server.cpp | 4 +- common/spdat.cpp | 304 +++++++------ common/spdat_bot.cpp | 4 + zone/aggro.cpp | 10 +- zone/attack.cpp | 4 +- zone/bot.cpp | 769 ++++++++++++++++++++++---------- zone/bot.h | 4 +- 7 files changed, 709 insertions(+), 390 deletions(-) diff --git a/common/net/websocket_server.cpp b/common/net/websocket_server.cpp index f74f437bac..1f034f1daa 100644 --- a/common/net/websocket_server.cpp +++ b/common/net/websocket_server.cpp @@ -25,7 +25,7 @@ struct MethodHandlerEntry struct EQ::Net::WebsocketServer::Impl { std::unique_ptr server; - std::unique_ptr ping_timer; + std::unique_ptr m_ping_timer; std::map, std::unique_ptr> connections; std::map methods; websocket_server ws_server; @@ -54,7 +54,7 @@ EQ::Net::WebsocketServer::WebsocketServer(const std::string &addr, int port) return websocketpp::lib::error_code(); }); - _impl->ping_timer = std::make_unique(5000, true, [this](EQ::Timer *t) { + _impl->m_ping_timer = std::make_unique(5000, true, [this](EQ::Timer *t) { auto iter = _impl->connections.begin(); while (iter != _impl->connections.end()) { diff --git a/common/spdat.cpp b/common/spdat.cpp index b38bc2ad72..5151d4d5a1 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -127,24 +127,12 @@ bool IsMesmerizeSpell(uint16 spell_id) bool SpellBreaksMez(uint16 spell_id) { - if (IsDetrimentalSpell(spell_id) && IsAnyDamageSpell(spell_id)) { - return true; - } - - return false; + return (IsValidSpell(spell_id) && IsDetrimentalSpell(spell_id) && IsAnyDamageSpell(spell_id)); } bool IsStunSpell(uint16 spell_id) { - if (IsEffectInSpell(spell_id, SE_Stun)) { - return true; - } - - if (IsEffectInSpell(spell_id, SE_SpinTarget)) { - return true; - } - - return false; + return (IsValidSpell(spell_id) && IsEffectInSpell(spell_id, SE_Stun) || IsEffectInSpell(spell_id, SE_SpinTarget)); } bool IsSummonSpell(uint16 spell_id) @@ -171,6 +159,10 @@ bool IsSummonSpell(uint16 spell_id) bool IsDamageSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + if (IsLifetapSpell(spell_id)) { return false; } @@ -192,6 +184,10 @@ bool IsDamageSpell(uint16 spell_id) bool IsAnyDamageSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + if (IsLifetapSpell(spell_id)) { return false; } @@ -200,6 +196,7 @@ bool IsAnyDamageSpell(uint16 spell_id) for (int i = 0; i < EFFECT_COUNT; i++) { const auto effect_id = spell.effect_id[i]; + if ( spell.base_value[i] < 0 && ( @@ -207,8 +204,8 @@ bool IsAnyDamageSpell(uint16 spell_id) ( effect_id == SE_CurrentHP && spell.buff_duration < 1 - ) ) + ) ) { return true; } @@ -678,11 +675,17 @@ bool IsAnyNukeOrStunSpell(uint16 spell_id) { } bool IsAnyAESpell(uint16 spell_id) { - if (IsAESpell(spell_id) || IsPBAENukeSpell(spell_id) || IsPBAESpell(spell_id) || IsAERainSpell(spell_id) || IsAERainNukeSpell(spell_id) || IsAEDurationSpell(spell_id)) { - return true; - } - - return false; + return ( + IsValidSpell(spell_id) && + ( + IsAEDurationSpell(spell_id) || + IsAESpell(spell_id) || + IsAERainNukeSpell(spell_id) || + IsAERainSpell(spell_id) || + IsPBAESpell(spell_id) || + IsPBAENukeSpell(spell_id) + ) + ); } bool IsAESpell(uint16 spell_id) @@ -1445,43 +1448,42 @@ bool IsCompleteHealSpell(uint16 spell_id) } -bool IsFastHealSpell(uint16 spell_id) -{ - spell_id = ( - IsEffectInSpell(spell_id, SE_CurrentHP) ? - spell_id : - GetSpellTriggerSpellID(spell_id, SE_CurrentHP) - ); - - if (!spell_id) { - spell_id = ( - IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? - spell_id : - GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) - ); - } - - if (spell_id) { - if ( - spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME && - spells[spell_id].good_effect && - !IsGroupSpell(spell_id) - ) { - for (int i = 0; i < EFFECT_COUNT; i++) { - if ( - spells[spell_id].base_value[i] > 0 && - ( - spells[spell_id].effect_id[i] == SE_CurrentHP || - spells[spell_id].effect_id[i] == SE_CurrentHPOnce - ) - ) { - return true; - } - } - } - } - - return false; +bool IsFastHealSpell(uint16 spell_id) { + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHP) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHP) + ); + + if (!spell_id) { + spell_id = ( + IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? + spell_id : + GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) + ); + } + + if (spell_id && IsValidSpell(spell_id)) { + if ( + spells[spell_id].cast_time <= MAX_FAST_HEAL_CASTING_TIME && + spells[spell_id].good_effect && + !IsGroupSpell(spell_id) + ) { + for (int i = 0; i < EFFECT_COUNT; i++) { + if ( + spells[spell_id].base_value[i] > 0 && + ( + spells[spell_id].effect_id[i] == SE_CurrentHP || + spells[spell_id].effect_id[i] == SE_CurrentHPOnce + ) + ) { + return true; + } + } + } + } + + return false; } bool IsVeryFastHealSpell(uint16 spell_id) @@ -1570,17 +1572,17 @@ bool IsRegularPetHealSpell(uint16 spell_id) IsEffectInSpell(spell_id, SE_CurrentHP) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHP) - ); + ); if (!spell_id) { spell_id = ( IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) - ); + ); } - if (spell_id) { + if (spell_id && IsValidSpell(spell_id)) { if ( (spells[spell_id].target_type == ST_Pet || spells[spell_id].target_type == ST_Undead) && !IsCompleteHealSpell(spell_id) && @@ -1611,14 +1613,14 @@ bool IsRegularGroupHealSpell(uint16 spell_id) IsEffectInSpell(spell_id, SE_CurrentHP) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHP) - ); + ); if (!spell_id) { spell_id = ( IsEffectInSpell(spell_id, SE_CurrentHPOnce) ? spell_id : GetSpellTriggerSpellID(spell_id, SE_CurrentHPOnce) - ); + ); } if (spell_id) { @@ -1634,8 +1636,8 @@ bool IsRegularGroupHealSpell(uint16 spell_id) ( spells[spell_id].effect_id[i] == SE_CurrentHP || spells[spell_id].effect_id[i] == SE_CurrentHPOnce - ) - ) { + ) + ) { return true; } } @@ -1645,36 +1647,36 @@ bool IsRegularGroupHealSpell(uint16 spell_id) return false; } -bool IsGroupCompleteHealSpell(uint16 spell_id) -{ - if ( - ( - spell_id == SPELL_COMPLETE_HEAL || - IsEffectInSpell(spell_id, SE_CompleteHeal) || - IsPercentalHealSpell(spell_id) || - GetSpellTriggerSpellID(spell_id, SE_CompleteHeal) - ) && - IsGroupSpell(spell_id) - ) { - return true; - } +bool IsGroupCompleteHealSpell(uint16 spell_id) { + if ( + IsValidSpell(spell_id) && + ( + spell_id == SPELL_COMPLETE_HEAL || + IsEffectInSpell(spell_id, SE_CompleteHeal) || + IsPercentalHealSpell(spell_id) || + GetSpellTriggerSpellID(spell_id, SE_CompleteHeal) + ) && + IsGroupSpell(spell_id) + ) { + return true; + } - return false; + return false; } -bool IsGroupHealOverTimeSpell(uint16 spell_id) -{ - if ( - ( - IsEffectInSpell(spell_id, SE_HealOverTime) || - GetSpellTriggerSpellID(spell_id, SE_HealOverTime) - ) && - IsGroupSpell(spell_id) - ) { - return true; - } +bool IsGroupHealOverTimeSpell(uint16 spell_id) { + if ( + IsValidSpell(spell_id) && + ( + IsEffectInSpell(spell_id, SE_HealOverTime) || + GetSpellTriggerSpellID(spell_id, SE_HealOverTime) + ) && + IsGroupSpell(spell_id) + ) { + return true; + } - return false; + return false; } bool IsAnyHealSpell(uint16 spell_id) { @@ -1745,22 +1747,25 @@ bool IsEscapeSpell(uint16 spell_id) { return false; } - if ( + return ( IsInvulnerabilitySpell(spell_id) || IsEffectInSpell(spell_id, SE_FeignDeath) || IsEffectInSpell(spell_id, SE_DeathSave) || IsEffectInSpell(spell_id, SE_Destroy) || - (IsEffectInSpell(spell_id, SE_WipeHateList) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_WipeHateList)] > 0) - ) { - return true; - } - - return false; + ( + IsEffectInSpell(spell_id, SE_WipeHateList) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_WipeHateList)] > 0 + ) + ); } bool IsDebuffSpell(uint16 spell_id) { - if ( + if (!IsValidSpell(spell_id)) { + return false; + } + + return !( IsBeneficialSpell(spell_id) || IsHealthSpell(spell_id) || IsStunSpell(spell_id) || @@ -1773,11 +1778,7 @@ bool IsDebuffSpell(uint16 spell_id) IsFearSpell(spell_id) || IsEffectInSpell(spell_id, SE_InstantHate) || IsEffectInSpell(spell_id, SE_TossUp) - ) { - return false; - } - - return true; + ); } bool IsHateReduxSpell(uint16 spell_id) { @@ -1785,20 +1786,29 @@ bool IsHateReduxSpell(uint16 spell_id) { return false; } - if ( - (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] < 0) || - (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] < 0) || - (IsEffectInSpell(spell_id, SE_ReduceHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_ReduceHate)] < 0) - ) { - return true; - } - - return false; + return ( + ( + IsEffectInSpell(spell_id, SE_InstantHate) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] < 0 + ) || + ( + IsEffectInSpell(spell_id, SE_Hate) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] < 0 + ) || + ( + IsEffectInSpell(spell_id, SE_ReduceHate) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_ReduceHate)] < 0 + ) + ); } bool IsResistDebuffSpell(uint16 spell_id) { - if ( + if (!IsValidSpell(spell_id)) { + return false; + } + + return ( !IsBeneficialSpell(spell_id) && ( IsEffectInSpell(spell_id, SE_ResistFire) || @@ -1809,42 +1819,34 @@ bool IsResistDebuffSpell(uint16 spell_id) IsEffectInSpell(spell_id, SE_ResistAll) || IsEffectInSpell(spell_id, SE_ResistCorruption) ) - ) { - return true; - } - - return false; + ); } bool IsSelfConversionSpell(uint16 spell_id) { - if ( + if (!IsValidSpell(spell_id)) { + return false; + } + + return ( GetSpellTargetType(spell_id) == ST_Self && IsEffectInSpell(spell_id, SE_CurrentMana) && IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0 - ) { - return true; - } - - return false; + ); } // returns true for both detrimental and beneficial buffs bool IsBuffSpell(uint16 spell_id) { - if ( + return ( IsValidSpell(spell_id) && ( spells[spell_id].buff_duration || spells[spell_id].buff_duration_formula ) - ) { - return true; - } - - return false; + ); } bool IsPersistDeathSpell(uint16 spell_id) @@ -2777,18 +2779,14 @@ bool IsLichSpell(uint16 spell_id) return false; } - if ( + return ( GetSpellTargetType(spell_id) == ST_Self && IsEffectInSpell(spell_id, SE_CurrentMana) && IsEffectInSpell(spell_id, SE_CurrentHP) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentMana)] > 0 && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_CurrentHP)] < 0 && spells[spell_id].buff_duration > 0 - ) { - return true; - } - - return false; + ); } bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) { @@ -2804,15 +2802,25 @@ bool IsValidSpellAndLoS(uint32 spell_id, bool has_los) { } bool IsInstantHealSpell(uint32 spell_id) { - if (IsRegularSingleTargetHealSpell(spell_id) || IsRegularGroupHealSpell(spell_id) || IsRegularPetHealSpell(spell_id) || IsRegularGroupHealSpell(spell_id) || spell_id == SPELL_COMPLETE_HEAL) { - return true; + if (!IsValidSpell(spell_id)) { + return false; } - return false; + return ( + IsRegularSingleTargetHealSpell(spell_id) || + IsRegularGroupHealSpell(spell_id) || + IsRegularPetHealSpell(spell_id) || + IsRegularGroupHealSpell(spell_id) || + spell_id == SPELL_COMPLETE_HEAL + ); } bool IsResurrectSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + return IsEffectInSpell(spell_id, SE_Revive); } @@ -2869,12 +2877,18 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { } bool IsHateSpell(uint16 spell_id) { - if ( - (IsEffectInSpell(spell_id, SE_Hate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0) || - (IsEffectInSpell(spell_id, SE_InstantHate) && spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0) - ) { - return true; + if (!IsValidSpell(spell_id)) { + return false; } - return false; + return ( + ( + IsEffectInSpell(spell_id, SE_Hate) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_Hate)] > 0 + ) || + ( + IsEffectInSpell(spell_id, SE_InstantHate) && + spells[spell_id].base_value[GetSpellEffectIndex(spell_id, SE_InstantHate)] > 0 + ) + ); } diff --git a/common/spdat_bot.cpp b/common/spdat_bot.cpp index f02a823b8d..6f40c30015 100644 --- a/common/spdat_bot.cpp +++ b/common/spdat_bot.cpp @@ -419,6 +419,10 @@ bool IsPullingBotSpellType(uint16 spell_type) { } uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return UINT16_MAX; + } + uint16 correct_type = UINT16_MAX; SPDat_Spell_Struct spell = spells[spell_id]; std::string teleport_zone = spell.teleport_zone; diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 6fb2f45ce6..a2c439fdb5 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -1295,8 +1295,8 @@ bool Mob::CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarge return zone->zonemap->CheckLoS(posWatcher, posTarget); } -bool Mob::CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ) { - if (zone->zonemap == nullptr) { +bool Mob::CheckPositioningLosFN(Mob* other, float x, float y, float z) { + if (!zone->zonemap) { //not sure what the best return is on error //should make this a database variable, but im lazy today #ifdef LOS_DEFAULT_CAN_SEE @@ -1318,9 +1318,9 @@ bool Mob::CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ) oloc.y = other->GetY(); oloc.z = other->GetZ() + (other->GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : other->GetSize()) / 2 * SEE_POSITION; - myloc.x = posX; - myloc.y = posY; - myloc.z = posZ + (GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : GetSize()) / 2 * HEAD_POSITION; + myloc.x = x; + myloc.y = y; + myloc.z = z + (GetSize() == 0.0 ? LOS_DEFAULT_HEIGHT : GetSize()) / 2 * HEAD_POSITION; #if LOSDEBUG>=5 LogDebug("LOS from ([{}], [{}], [{}]) to ([{}], [{}], [{}]) sizes: ([{}], [{}])", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize); diff --git a/zone/attack.cpp b/zone/attack.cpp index 5dd9e521dd..278a8b9546 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1250,8 +1250,8 @@ int64 Mob::GetWeaponDamage(Mob *against, const EQ::ItemInstance *weapon_item, in ( !IsBot() || (IsBot() && !RuleB(Bots, AllowBotEquipAnyClassGear)) - ) - ) { + ) + ) { return 0; } diff --git a/zone/bot.cpp b/zone/bot.cpp index f0192526f8..2b2f0f8dea 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -37,7 +37,7 @@ TODO bot rewrite: */ // This constructor is used during the bot create command -Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) { +Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), m_ping_timer(1) { GiveNPCTypeData(npcTypeData); if (botOwner) { @@ -87,7 +87,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm SetPauseAI(false); m_combat_jitter_timer.Disable(); - auto_save_timer.Disable(); + m_auto_save_timer.Disable(); m_rogue_evade_timer.Disable(); m_monk_evade_timer.Disable(); m_auto_defend_timer.Disable(); @@ -103,7 +103,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm m_previous_pet_order = SPO_Guard; rest_timer.Disable(); - ping_timer.Disable(); + m_ping_timer.Disable(); LoadDefaultBotSettings(); SetCastedSpellType(UINT16_MAX); @@ -145,7 +145,7 @@ Bot::Bot( uint32 lastZoneId, NPCType *npcTypeData ) - : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) + : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), m_ping_timer(1) { GiveNPCTypeData(npcTypeData); @@ -219,7 +219,7 @@ Bot::Bot( SetPauseAI(false); m_combat_jitter_timer.Disable(); - auto_save_timer.Disable(); + m_auto_save_timer.Disable(); m_rogue_evade_timer.Disable(); m_monk_evade_timer.Disable(); m_auto_defend_timer.Disable(); @@ -235,7 +235,7 @@ Bot::Bot( m_previous_pet_order = SPO_Guard; rest_timer.Disable(); - ping_timer.Disable(); + m_ping_timer.Disable(); strcpy(name, GetCleanName()); @@ -248,6 +248,7 @@ Bot::Bot( if (GetClass() == Class::Rogue) { m_rogue_evade_timer.Start(); } + if (GetClass() == Class::Monk) { m_monk_evade_timer.Start(); } @@ -571,8 +572,10 @@ void Bot::SetSuffix(std::string_view bot_suffix) { uint32 Bot::GetBotRangedValue() { const EQ::ItemInstance *range_inst = GetBotItem(EQ::invslot::slotRange); const EQ::ItemInstance *ammo_inst = GetBotItem(EQ::invslot::slotAmmo); - if (!range_inst) + + if (!range_inst) { return 0; + } const EQ::ItemData *range_item = range_inst->GetItem(); const EQ::ItemData *ammo_item = nullptr; @@ -582,17 +585,35 @@ uint32 Bot::GetBotRangedValue() { } // Bow requires arrows - if (range_item && range_item->ItemType == EQ::item::ItemTypeBow && (!ammo_item || ammo_item->ItemType != EQ::item::ItemTypeArrow)) { + if ( + range_item && + range_item->ItemType == EQ::item::ItemTypeBow && + ( + !ammo_item || + ammo_item->ItemType != EQ::item::ItemTypeArrow + ) + ) { return 0; } // Throwing items - if (range_item && (range_item->ItemType == EQ::item::ItemTypeSmallThrowing || range_item->ItemType == EQ::item::ItemTypeLargeThrowing)) { + if ( + range_item && + ( + range_item->ItemType == EQ::item::ItemTypeSmallThrowing || + range_item->ItemType == EQ::item::ItemTypeLargeThrowing + ) + ) { return range_item->Range; } // Bows and arrows - if (range_item && ammo_item && range_item->ItemType == EQ::item::ItemTypeBow && ammo_item->ItemType == EQ::item::ItemTypeArrow) { + if ( + range_item && + ammo_item && + range_item->ItemType == EQ::item::ItemTypeBow && + ammo_item->ItemType == EQ::item::ItemTypeArrow + ) { return (range_item->Range + ammo_item->Range); } @@ -1710,19 +1731,19 @@ bool Bot::Process() } if (IsMoving()) { - ping_timer.Disable(); + m_ping_timer.Disable(); } else { - if (!ping_timer.Enabled()) { - ping_timer.Start(BOT_KEEP_ALIVE_INTERVAL); + if (!m_ping_timer.Enabled()) { + m_ping_timer.Start(BOT_KEEP_ALIVE_INTERVAL); } - if (ping_timer.Check()) { + if (m_ping_timer.Check()) { SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); } } - if (auto_save_timer.Check()) { + if (m_auto_save_timer.Check()) { clock_t t = std::clock(); /* Function timer start */ Save(); LogDebug( @@ -1730,7 +1751,7 @@ bool Bot::Process() GetBotID(), ((float)(std::clock() - t)) / CLOCKS_PER_SEC ); - auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); + m_auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); } if (ForcedMovement) { @@ -1802,34 +1823,66 @@ void Bot::SpellProcess() { } void Bot::BotMeditate(bool is_sitting) { - if (GetManaRatio() < GetSitManaPct() || (GetHPRatio() < GetSitHPPct() && GetLevel() < GetStopMeleeLevel())) { - if ((!IsEngaged() || (IsEngaged() && GetMedInCombat() && !HasTargetReflection())) && !is_sitting) { - Sit(); - } - } - else { - if (is_sitting) { - Stand(); - } - } + if ( + GetManaRatio() < GetSitManaPct() || + (GetHPRatio() < GetSitHPPct() && GetLevel() < GetStopMeleeLevel()) + ) { + if ( + ( + !IsEngaged() || + (IsEngaged() && GetMedInCombat() && !HasTargetReflection()) + ) && + !is_sitting + ) { + Sit(); + } + } else { + if (is_sitting) { + Stand(); + } + } } bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { - if (!other || !IsAttackAllowed(other) || IsCasting() || DivineAura() || IsStunned() || IsMezzed() || (GetAppearance() == eaDead)) { + if ( + !other || + !IsAttackAllowed(other) || + IsCasting() || + DivineAura() || + IsStunned() || + IsMezzed() || + (GetAppearance() == eaDead) + ) { return false; } if ( !GetPullingFlag() && ( - (GetBotStance() != Stance::Aggressive && GetBotStance() != Stance::Burn && GetBotStance() != Stance::AEBurn) && + ( + GetBotStance() != Stance::Aggressive && + GetBotStance() != Stance::Burn && + GetBotStance() != Stance::AEBurn + ) && other->GetHPRatio() > 99.0f ) ) { return false; } - if (!can_double_attack && ((attack_timer.Enabled() && !attack_timer.Check(false)) || (ranged_timer.Enabled() && !ranged_timer.Check()))) { + if ( + !can_double_attack && + ( + ( + attack_timer.Enabled() && + !attack_timer.Check(false) + ) || + ( + ranged_timer.Enabled() && + !ranged_timer.Check() + ) + ) + ) { LogCombatDetail("Bot ranged attack canceled. Timer not up. Attack [{}] ranged [{}]", attack_timer.GetRemainingTime(), ranged_timer.GetRemainingTime()); return false; } @@ -1855,16 +1908,29 @@ bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { // Bow requires arrows if ( !ammo || - (ranged_weapon && + ( + ranged_weapon && ( - (ranged_weapon->ItemType != EQ::item::ItemTypeBow && ranged_weapon->ItemType != EQ::item::ItemTypeSmallThrowing && ranged_weapon->ItemType != EQ::item::ItemTypeLargeThrowing) || - (ranged_weapon->ItemType == EQ::item::ItemTypeBow && (ammo->ItemType != EQ::item::ItemTypeArrow)) || ( - (ranged_weapon->ItemType == EQ::item::ItemTypeSmallThrowing || ranged_weapon->ItemType == EQ::item::ItemTypeLargeThrowing) && + ranged_weapon->ItemType != EQ::item::ItemTypeBow && + ranged_weapon->ItemType != EQ::item::ItemTypeSmallThrowing && + ranged_weapon->ItemType != EQ::item::ItemTypeLargeThrowing) || + ( + ranged_weapon->ItemType == EQ::item::ItemTypeBow && + (ammo->ItemType != EQ::item::ItemTypeArrow) + ) || + ( + ( + ranged_weapon->ItemType == EQ::item::ItemTypeSmallThrowing || + ranged_weapon->ItemType == EQ::item::ItemTypeLargeThrowing + ) && ammo_item->GetCharges() < 1 || ( - (RuleI(Bots, StackSizeMin) != -1 && ranged_item->GetCharges() != ranged_weapon->StackSize) || - ranged_item->GetCharges() < RuleI(Bots, StackSizeMin) + ( + RuleI(Bots, StackSizeMin) != -1 && + ranged_item->GetCharges() != ranged_weapon->StackSize + ) || + ranged_item->GetCharges() < RuleI(Bots, StackSizeMin) ) ) ) @@ -1993,8 +2059,8 @@ bool Bot::CheckTripleAttack() GetClass() == Class::Ranger || GetClass() == Class::Monk || GetClass() == Class::Berserker - ) - ) { + ) + ) { switch (GetClass()) { case Class::Warrior: chance = RuleI(Combat, ClassicTripleAttackChanceWarrior); @@ -2256,7 +2322,12 @@ void Bot::AI_Process() } if (at_combat_range) { - if (!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && RuleB(Bots, AllowRangedPulling) && IsBotRanged() && ranged_timer.Check(false)) { + if ( + !tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && + RuleB(Bots, AllowRangedPulling) && + IsBotRanged() && + ranged_timer.Check(false) + ) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { @@ -2268,7 +2339,11 @@ void Bot::AI_Process() return; } - if (RuleB(Bots, AllowAISpellPulling) && !IsBotNonSpellFighter() && AI_HasSpells()) { + if ( + RuleB(Bots, AllowAISpellPulling) && + !IsBotNonSpellFighter() && + AI_HasSpells() + ) { SetPullingSpell(true); AI_EngagedCastCheck(); SetPullingSpell(false); @@ -2305,15 +2380,27 @@ void Bot::AI_Process() jitter_cooldown = true; } - if (IsMoving() || GetCombatJitterFlag() || GetCombatOutOfRangeJitterFlag()) { - if (!GetCombatJitterFlag() || !IsMoving() || GetCombatOutOfRangeJitterFlag()) { + if ( + IsMoving() || + GetCombatJitterFlag() || + GetCombatOutOfRangeJitterFlag() + ) { + if ( + !GetCombatJitterFlag() || + !IsMoving() || + GetCombatOutOfRangeJitterFlag() + ) { StopMoving(CalculateHeadingToTarget(tar->GetX(), tar->GetY())); } return; } - if (!jitter_cooldown && AI_movement_timer->Check() && (!spellend_timer.Enabled() || GetClass() == Class::Bard)) { + if ( + !jitter_cooldown && + AI_movement_timer->Check() && + (!spellend_timer.Enabled() || GetClass() == Class::Bard) + ) { DoCombatPositioning(tar, Goal, stop_melee_level, tar_distance, melee_distance_min, melee_distance, melee_distance_max, behind_mob, front_mob); return; } @@ -2333,7 +2420,11 @@ void Bot::AI_Process() return; } - if (!tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && IsBotRanged() && ranged_timer.Check(false)) { + if ( + !tar->GetSpecialAbility(SpecialAbility::RangedAttackImmunity) && + IsBotRanged() && + ranged_timer.Check(false) + ) { if (BotRangedAttack(tar) && CheckDoubleRangedAttack()) { BotRangedAttack(tar, true); } @@ -2399,7 +2490,14 @@ void Bot::AI_Process() SetTarget(nullptr); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 1)) { + if ( + HasPet() && + ( + GetClass() != Class::Enchanter || + GetPet()->GetPetType() != petAnimation || + GetAA(aaAnimationEmpathy) >= 1 + ) + ) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -2652,9 +2750,7 @@ bool Bot::TryAutoDefend(Client* bot_owner, float leash_distance) { } bool Bot::TryMeditate() { - if (!IsMoving() && !spellend_timer.Enabled()) { - if (IsEngaged() && HasOrMayGetAggro(IsSitting())) { if (IsSitting()) { Stand(); @@ -2687,7 +2783,14 @@ bool Bot::TryPursueTarget(float leash_distance, glm::vec3& Goal) { WipeHateList(); SetTarget(nullptr); - if (HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { + if ( + HasPet() && + ( + GetClass() != Class::Enchanter || + GetPet()->GetPetType() != petAnimation || + GetAA(aaAnimationEmpathy) >= 2 + ) + ) { GetPet()->WipeHateList(); GetPet()->SetTarget(nullptr); } @@ -3544,8 +3647,8 @@ bool Bot::Spawn(Client* botCharacterOwner) { // Load pet LoadPet(); SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0); - ping_timer.Start(8000); - auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); + m_ping_timer.Start(8000); + m_auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); pDontHealMeBefore = 0; pDontGroupHealMeBefore = 0; @@ -4293,7 +4396,10 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* } if (RuleI(Bots, StackSizeMin) != -1) { - if (trade_instance->IsStackable() && trade_instance->GetCharges() < RuleI(Bots, StackSizeMin)) { // temp until partial stacks are implemented + if ( + trade_instance->IsStackable() && + trade_instance->GetCharges() < RuleI(Bots, StackSizeMin) + ) { // temp until partial stacks are implemented if (trade_event_exists) { event_trade.push_back(ClientTrade(trade_instance, trade_index)); continue; @@ -4312,7 +4418,10 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* } } } - else if (trade_instance->IsStackable() && trade_instance->GetCharges() < trade_instance->GetItem()->StackSize) { + else if ( + trade_instance->IsStackable() && + trade_instance->GetCharges() < trade_instance->GetItem()->StackSize + ) { client->Message( Chat::Yellow, fmt::format( @@ -4381,9 +4490,15 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* } if ( - (!trade_instance->IsClassEquipable(GetClass()) && !RuleB(Bots, AllowBotEquipAnyClassGear))|| + ( + !trade_instance->IsClassEquipable(GetClass()) && + !RuleB(Bots, AllowBotEquipAnyClassGear) + ) || GetLevel() < trade_instance->GetItem()->ReqLevel || - (!trade_instance->IsRaceEquipable(GetBaseRace()) && !RuleB(Bots, AllowBotEquipAnyRaceGear)) + ( + !trade_instance->IsRaceEquipable(GetBaseRace()) && + !RuleB(Bots, AllowBotEquipAnyRaceGear) + ) ) { if (trade_event_exists) { event_trade.push_back(ClientTrade(trade_instance, trade_index)); @@ -5096,10 +5211,19 @@ int Bot::GetBaseSkillDamage(EQ::skills::SkillType skill, Mob *target) if (!inst->GetItemBackstabDamage()) base += inst->GetItemWeaponDamage(true); if (target) { - if (inst->GetItemElementalFlag(true) && inst->GetItemElementalDamage(true)) + if ( + inst->GetItemElementalFlag(true) && + inst->GetItemElementalDamage(true) + ) { base += target->ResistElementalWeaponDmg(inst); - if (inst->GetItemBaneDamageBody(true) || inst->GetItemBaneDamageRace(true)) + } + + if ( + inst->GetItemBaneDamageBody(true) || + inst->GetItemBaneDamageRace(true) + ) { base += target->CheckBaneDamage(inst); + } } } return static_cast(static_cast(base) * (skill_bonus + 2.0f)); @@ -5161,7 +5285,17 @@ void Bot::DoSpecialAttackDamage(Mob *who, EQ::skills::SkillType skill, int32 max } void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { - if (!target || GetAppearance() == eaDead || spellend_timer.Enabled() || IsFeared() || IsStunned() || IsMezzed() || DivineAura() || GetHP() < 0 || !IsAttackAllowed(target)) { + if ( + !target || + GetAppearance() == eaDead || + spellend_timer.Enabled() || + IsFeared() || + IsStunned() || + IsMezzed() || + DivineAura() || + GetHP() < 0 || + !IsAttackAllowed(target) + ) { return; } @@ -6043,14 +6177,36 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe int j = BotGetSpells(i); int spelltype = BotGetSpellType(i); bool spellequal = (j == thespell); - bool spelltypeequal = ((spelltype == BotSpellTypes::RegularHeal) || (spelltype == BotSpellTypes::Escape) || (spelltype == BotSpellTypes::Pet)); - bool spelltypetargetequal = ((spelltype == BotSpellTypes::Buff) && (spells[thespell].target_type == ST_Self)); - bool spelltypeclassequal = ((spelltype == BotSpellTypes::InCombatBuff) && (GetClass() == Class::Shaman)); + bool spelltypeequal = ( + (spelltype == BotSpellTypes::RegularHeal) || + (spelltype == BotSpellTypes::Escape) || + (spelltype == BotSpellTypes::Pet) + ); + bool spelltypetargetequal = ( + (spelltype == BotSpellTypes::Buff) && + (spells[thespell].target_type == ST_Self) + ); + bool spelltypeclassequal = ( + (spelltype == BotSpellTypes::InCombatBuff) && + (GetClass() == Class::Shaman) + ); bool slotequal = (slot == EQ::spells::CastingSlot::Item); + if (spellequal || slotequal) { - if ((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) { - if (((spells[thespell].effect_id[0] == 0) && (spells[thespell].base_value[0] < 0)) && - (spellTarget->GetHP() < ((spells[thespell].base_value[0] * (-1)) + 100))) { + if ( + ( + spelltypeequal || spelltypetargetequal + ) || + spelltypeclassequal || + slotequal + ) { + if ( + ( + (spells[thespell].effect_id[0] == 0) && + (spells[thespell].base_value[0] < 0) + ) && + (spellTarget->GetHP() < ((spells[thespell].base_value[0] * (-1)) + 100)) + ) { LogSpells("GroupBuffing failure"); return false; } @@ -6089,7 +6245,13 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe SpellOnTarget(thespell, m); - if (m->GetPetID() && (!RuleB(Bots, RequirePetAffinity) || m->HasPetAffinity())) { + if ( + m->GetPetID() && + ( + !RuleB(Bots, RequirePetAffinity) || + m->HasPetAffinity() + ) + ) { SpellOnTarget(thespell, m->GetPet()); } @@ -7295,7 +7457,13 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spell_type) { result = AttemptAICastSpell(spell_type, tar); if (!result) { - if (tar->HasPet() && (!m->GetPet()->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars))) { + if ( + tar->HasPet() && + ( + !m->GetPet()->IsFamiliar() || + RuleB(Bots, AllowBuffingHealingFamiliars) + ) + ) { tar = m->GetPet(); if (!tar) { @@ -7730,39 +7898,42 @@ bool Bot::GetNeedsHateRedux(Mob *tar) { return false; } -bool Bot::HasOrMayGetAggro(bool SitAggro, uint32 spell_id) { - bool may_get_aggro = false; +bool Bot::HasOrMayGetAggro(bool sit_aggro, uint32 spell_id) { + if ( + !GetTarget() || + !GetTarget()->GetHateTop() + ) { + return false; + } - if (GetTarget() && GetTarget()->GetHateTop()) { - Mob* top_hate = GetTarget()->GetHateTop(); - if (top_hate == this) { - may_get_aggro = true; - } - else { - uint32 my_hate_amt = GetTarget()->GetHateAmount(this); - uint32 top_hate_amt = GetTarget()->GetHateAmount(top_hate); + Mob* top_hate = GetTarget()->GetHateTop(); - if (SitAggro && !spell_id) { - my_hate_amt *= (1 + (RuleI(Aggro, SittingAggroMod) / 100)); - } + if (top_hate == this) { + return true; + } - if (spell_id && IsValidSpell(spell_id) && GetTarget()) { - my_hate_amt += CheckAggroAmount(spell_id, GetTarget()); - } + uint32 my_hate = GetTarget()->GetHateAmount(this); + uint32 top_hate_amt = GetTarget()->GetHateAmount(top_hate); - if ( - top_hate_amt < 1 || - ( - my_hate_amt > 0 && - (uint8)((my_hate_amt / top_hate_amt) * 100) > RuleI(Bots, HasOrMayGetAggroThreshold) - ) - ) { - may_get_aggro = true; - } - } - } + if (sit_aggro && !spell_id) { + my_hate *= (1 + (RuleI(Aggro, SittingAggroMod) / 100)); + } + + if (spell_id && IsValidSpell(spell_id)) { + my_hate += CheckAggroAmount(spell_id, GetTarget()); + } - return may_get_aggro; + if ( + top_hate_amt < 1 || + ( + my_hate > 0 && + (my_hate * 100 / top_hate_amt) > RuleI(Bots, HasOrMayGetAggroThreshold) + ) + ) { + return true; + } + + return false; } void Bot::SetDefaultBotStance() { @@ -9296,16 +9467,25 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { return false; } - if (IsPullingSpell() && IsPullingBotSpellType(spell_type)) { //Skip remaining checks for commanded + if ( + IsPullingSpell() && + IsPullingBotSpellType(spell_type) + ) { //Skip remaining checks for commanded return true; } - if (GetManaRatio() < GetSpellTypeMinManaLimit(spell_type) || GetManaRatio() > GetSpellTypeMaxManaLimit(spell_type)) { + if ( + GetManaRatio() < GetSpellTypeMinManaLimit(spell_type) || + GetManaRatio() > GetSpellTypeMaxManaLimit(spell_type) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinManaLimit or GetSpellTypeMaxManaLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } - if (GetHPRatio() < GetSpellTypeMinHPLimit(spell_type) || GetHPRatio() > GetSpellTypeMaxHPLimit(spell_type)) { + if ( + GetHPRatio() < GetSpellTypeMinHPLimit(spell_type) || + GetHPRatio() > GetSpellTypeMaxHPLimit(spell_type) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetSpellTypeMinHPLimit or GetSpellTypeMaxHPLimit.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9320,7 +9500,10 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { case BotSpellTypes::AEMez: return true; default: - if (GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellMinThreshold(spell_type, tar) || GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellMaxThreshold(spell_type, tar)) { + if ( + GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellMinThreshold(spell_type, tar) || + GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellMaxThreshold(spell_type, tar) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9339,7 +9522,10 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck if (spells[spell_id].target_type == ST_Self && tar != this) { if ( !IsEffectInSpell(spell_id, SE_SummonCorpse) || - (IsEffectInSpell(spell_id, SE_SummonCorpse) && !RuleB(Bots, AllowCommandedSummonCorpse)) + ( + IsEffectInSpell(spell_id, SE_SummonCorpse) && + !RuleB(Bots, AllowCommandedSummonCorpse) + ) ) { tar = this; } @@ -9358,17 +9544,31 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (IsFeared() || IsSilenced() || IsAmnesiad()) { + if ( + IsFeared() || + IsSilenced() || + IsAmnesiad() + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Incapacitated.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } - if ((IsStunned() || IsMezzed() || DivineAura()) && !IsCastNotStandingSpell(spell_id)) { + if ( + ( + IsStunned() || + IsMezzed() || + DivineAura() + ) && + !IsCastNotStandingSpell(spell_id) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !IsCastNotStandingSpell.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } - if (IsDetrimentalSpell(spell_id) && !zone->CanDoCombat()) { + if ( + IsDetrimentalSpell(spell_id) && + !zone->CanDoCombat() + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to !CanDoCombat.'", GetCleanName(), GetSpellName(spell_id), (tar ? tar->GetCleanName() : "nobody")); return false; } @@ -9388,12 +9588,18 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (spells[spell_id].caster_requirement_id && !PassCastRestriction(spells[spell_id].caster_requirement_id)) { + if ( + spells[spell_id].caster_requirement_id && + !PassCastRestriction(spells[spell_id].caster_requirement_id) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !PassCastRestriction.'", GetCleanName(), GetSpellName(spell_id)); return false; } - if (!spells[spell_id].can_cast_in_combat && spells[spell_id].can_cast_out_of_combat) { + if ( + !spells[spell_id].can_cast_in_combat && + spells[spell_id].can_cast_out_of_combat + ) { if (IsBeneficialSpell(spell_id)) { if (IsEngaged()) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_in_combat.'", GetCleanName(), GetSpellName(spell_id)); @@ -9401,7 +9607,10 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck } } } - else if (spells[spell_id].can_cast_in_combat && !spells[spell_id].can_cast_out_of_combat) { + else if ( + spells[spell_id].can_cast_in_combat && + !spells[spell_id].can_cast_out_of_combat + ) { if (IsBeneficialSpell(spell_id)) { if (!IsEngaged()) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !can_cast_out_of_combat.'", GetCleanName(), GetSpellName(spell_id)); @@ -9413,47 +9622,71 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck if (!IsDiscipline(spell_id)) { int chance = GetFocusEffect(focusFcMute, spell_id); - if (chance && zone->random.Roll(chance)) { + if ( + chance && + zone->random.Roll(chance) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to focusFcMute.'", GetCleanName(), GetSpellName(spell_id)); return false; } } - if (!zone->CanLevitate() && IsEffectInSpell(spell_id, SE_Levitate)) { + if ( + !zone->CanLevitate() && + IsEffectInSpell(spell_id, SE_Levitate) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !CanLevitate.'", GetCleanName(), GetSpellName(spell_id)); return false; } - if (spells[spell_id].time_of_day == SpellTimeRestrictions::Day && !zone->zone_time.IsDayTime()) { + if ( + spells[spell_id].time_of_day == SpellTimeRestrictions::Day && + !zone->zone_time.IsDayTime() + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !IsDayTime.'", GetCleanName(), GetSpellName(spell_id)); return false; } - if (spells[spell_id].time_of_day == SpellTimeRestrictions::Night && !zone->zone_time.IsNightTime()) { + if ( + spells[spell_id].time_of_day == SpellTimeRestrictions::Night && + !zone->zone_time.IsNightTime() + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !IsNightTime.'", GetCleanName(), GetSpellName(spell_id)); return false; } - if (spells[spell_id].zone_type == 1 && !zone->CanCastOutdoor()) { + if ( + spells[spell_id].zone_type == 1 && + !zone->CanCastOutdoor() + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to !CanCastOutdoor.'", GetCleanName(), GetSpellName(spell_id)); return false; } - if (BotSpellTypeRequiresTarget(spell_type) && !tar) { + if ( + BotSpellTypeRequiresTarget(spell_type) && + !tar + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast due to CastChecks !tar.'", GetCleanName()); return false; } if ( - spells[spell_id].target_type == ST_Self - && tar != this && - (spell_type != BotSpellTypes::SummonCorpse || RuleB(Bots, AllowCommandedSummonCorpse)) + spells[spell_id].target_type == ST_Self && + tar != this && + ( + spell_type != BotSpellTypes::SummonCorpse || + RuleB(Bots, AllowCommandedSummonCorpse) + ) ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } - if (this == tar && IsSacrificeSpell(spell_id)) { + if ( + this == tar && + IsSacrificeSpell(spell_id) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} due to IsSacrificeSpell.'", GetCleanName(), GetSpellName(spell_id)); return false; } @@ -9463,7 +9696,10 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && !CombatRange(tar)) { + if ( + tar->GetSpecialAbility(SpecialAbility::CastingFromRangeImmunity) && + !CombatRange(tar) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to CastingFromRangeImmunity.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9481,8 +9717,14 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck if ( IsBeneficialSpell(spell_id) && ( - (RuleB(Spells, EnableBlockedBuffs) && tar->IsClient()) || - (RuleB(Bots, AllowBotBlockedBuffs) && tar->IsBot()) + ( + RuleB(Spells, EnableBlockedBuffs) && + tar->IsClient() + ) || + ( + RuleB(Bots, AllowBotBlockedBuffs) && + tar->IsBot() + ) ) ) { if (tar->IsBlockedBuff(spell_id)) { @@ -9492,10 +9734,19 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck } if ( - IsBeneficialSpell(spell_id) && tar->IsPet() && + IsBeneficialSpell(spell_id) && + tar->IsPet() && ( - (RuleB(Spells, EnableBlockedBuffs) && tar->GetOwner() && tar->GetOwner()->IsClient()) || - (RuleB(Bots, AllowBotBlockedBuffs) && tar->GetOwner() && tar->GetOwner()->IsBot()) + ( + RuleB(Spells, EnableBlockedBuffs) && + tar->GetOwner() && + tar->GetOwner()->IsClient() + ) || + ( + RuleB(Bots, AllowBotBlockedBuffs) && + tar->GetOwner() && + tar->GetOwner()->IsBot() + ) ) ) { if (tar->GetOwner()->IsBlockedPetBuff(spell_id)) { @@ -9518,7 +9769,12 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck } if ( - (RequiresStackCheck(spell_type) || (!RequiresStackCheck(spell_type) && CalcBuffDuration(this, tar, spell_id) != 0)) + ( + RequiresStackCheck(spell_type) || + ( + !RequiresStackCheck(spell_type) && + CalcBuffDuration(this, tar, spell_id) != 0) + ) && tar->CanBuffStack(spell_id, GetLevel(), true) < 0 ) { @@ -9526,11 +9782,18 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (IsBeneficialSpell(spell_id) && tar->BuffCount() >= tar->GetCurrentBuffSlots() && CalcBuffDuration(this, tar, spell_id) != 0) { + if ( + IsBeneficialSpell(spell_id) && + tar->BuffCount() >= tar->GetCurrentBuffSlots() && + CalcBuffDuration(this, tar, spell_id) != 0 + ) { return false; } - if (!ae_check && !IsValidSpellRange(spell_id, tar)) { + if ( + !ae_check && + !IsValidSpellRange(spell_id, tar) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsValidSpellRange.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9539,7 +9802,12 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return true; } - if (!IsTaunting() && GetSpellTypeAggroCheck(spell_type) && HasOrMayGetAggro(IsSitting(), spell_id) && !tar->IsFleeing()) { + if ( + !IsTaunting() && + GetSpellTypeAggroCheck(spell_type) && + HasOrMayGetAggro(IsSitting(), spell_id) && + !tar->IsFleeing() + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to HasOrMayGetAggro.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9549,7 +9817,11 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id)) { + if ( + spells[spell_id].target_type != ST_Self && + IsBeneficialSpell(spell_id) && + IsTargetAlreadyReceivingSpell(tar, spell_id) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9585,27 +9857,64 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { case BotSpellTypes::Invisibility: case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: - if (tar == this && spells[spell_id].target_type == ST_TargetsTarget) { + if ( + tar == this && + spells[spell_id].target_type == ST_TargetsTarget + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to target_type checks. Using {}'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName(), GetSpellTargetType(spell_id)); return false; } - if ((spell_type != BotSpellTypes::Teleport && spell_type != BotSpellTypes::Succor) && (IsEffectInSpell(spell_id, SE_Teleport) || IsEffectInSpell(spell_id, SE_Succor))) { + if ( + ( + spell_type != BotSpellTypes::Teleport && + spell_type != BotSpellTypes::Succor + ) && + ( + IsEffectInSpell(spell_id, SE_Teleport) || + IsEffectInSpell(spell_id, SE_Succor) + ) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Teleport.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } - if (tar->IsPet() && !RuleB(Bots, CanCastIllusionsOnPets) && IsEffectInSpell(spell_id, SE_Illusion)) { + if ( + tar->IsPet() && + !RuleB(Bots, CanCastIllusionsOnPets) && + IsEffectInSpell(spell_id, SE_Illusion) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetSE_Illusion.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } - if (spells[spell_id].target_type == ST_Pet && (!tar->IsPet() || (tar->GetOwner() != this && !RuleB(Bots, CanCastPetOnlyOnOthersPets)))) { + if ( + spells[spell_id].target_type == ST_Pet && + ( + !tar->IsPet() || + ( + tar->GetOwner() != this && + !RuleB(Bots, CanCastPetOnlyOnOthersPets) + ) + ) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetOnly.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } - if ((IsGroupSpell(spell_id) && tar->IsPet()) && (!tar->GetOwner() || (RuleB(Bots, RequirePetAffinity) && !tar->GetOwner()->HasPetAffinity()))) { + if ( + ( + IsGroupSpell(spell_id) && + tar->IsPet() + ) && + ( + !tar->GetOwner() || + ( + RuleB(Bots, RequirePetAffinity) && + !tar->GetOwner()->HasPetAffinity() + ) + ) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to PetGroupSpellTarget.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9614,10 +9923,17 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { switch (tar->GetArchetype()) { case Archetype::Caster: if ( - tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && + tar->IsBot() && + (tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel()) && ( - IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS)) || - (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) + IsEffectInSpell(spell_id, SE_AttackSpeed) || + IsEffectInSpell(spell_id, SE_ReverseDS) + ) || + ( + SpellEffectsCount(spell_id) == 1 && + ( + IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR) + ) ) ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); @@ -9626,10 +9942,12 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { break; case Archetype::Melee: if ( - IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || IsEffectInSpell(spell_id, SE_ManaPool) || - IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || + IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || + IsEffectInSpell(spell_id, SE_ManaPool) || + IsEffectInSpell(spell_id, SE_CastingLevel) || + IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) - ) { + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9642,7 +9960,10 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { // Differences for each type if (spell_type != BotSpellTypes::InCombatBuff) { - if (IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || IsEffectInSpell(spell_id, SE_Rune)) { + if ( + IsEffectInSpell(spell_id, SE_AbsorbMagicAtt) || + IsEffectInSpell(spell_id, SE_Rune) + ) { for (int i = 0; i < tar->GetMaxTotalSlots(); i++) { uint32 buff_count = tar->GetMaxTotalSlots(); @@ -9666,10 +9987,18 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { switch (tar->GetArchetype()) { case Archetype::Caster: if ( - tar->IsBot() && tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel() && + tar->IsBot() && + (tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel()) && + ( + IsEffectInSpell(spell_id, SE_AttackSpeed) || + IsEffectInSpell(spell_id, SE_ReverseDS) + ) || ( - IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS)) || - (SpellEffectsCount(spell_id) == 1 && (IsEffectInSpell(spell_id, SE_ATK) || IsEffectInSpell(spell_id, SE_STR)) + SpellEffectsCount(spell_id) == 1 && + ( + IsEffectInSpell(spell_id, SE_ATK) || + IsEffectInSpell(spell_id, SE_STR) + ) ) ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Caster.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); @@ -9678,10 +10007,12 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { break; case Archetype::Melee: if ( - IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || IsEffectInSpell(spell_id, SE_ManaPool) || - IsEffectInSpell(spell_id, SE_CastingLevel) || IsEffectInSpell(spell_id, SE_ManaRegen_v2) || + IsEffectInSpell(spell_id, SE_IncreaseSpellHaste) || + IsEffectInSpell(spell_id, SE_ManaPool) || + IsEffectInSpell(spell_id, SE_CastingLevel) || + IsEffectInSpell(spell_id, SE_ManaRegen_v2) || IsEffectInSpell(spell_id, SE_CurrentMana) - ) { + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to Archetype::Melee.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9695,7 +10026,10 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { break; case BotSpellTypes::AELull: case BotSpellTypes::Lull: - if (IsHarmonySpell(spell_id) && !HarmonySpellLevelCheck(spell_id, tar)) { + if ( + IsHarmonySpell(spell_id) && + !HarmonySpellLevelCheck(spell_id, tar) + ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to HarmonySpellLevelCheck.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -9831,13 +10165,21 @@ bool Bot::IsValidTargetType(uint16 spell_id, int target_type, uint8 body_type) { switch (target_type) { case ST_Undead: - if (body_type == BodyType::Undead || body_type == BodyType::SummonedUndead || body_type == BodyType::Vampire) { + if ( + body_type == BodyType::Undead || + body_type == BodyType::SummonedUndead || + body_type == BodyType::Vampire + ) { return true; } break; case ST_Summoned: - if (body_type == BodyType::Summoned || body_type == BodyType::Summoned2 || body_type == BodyType::Summoned3) { + if ( + body_type == BodyType::Summoned || + body_type == BodyType::Summoned2 || + body_type == BodyType::Summoned3 + ) { return true; } @@ -9855,13 +10197,19 @@ bool Bot::IsValidTargetType(uint16 spell_id, int target_type, uint8 body_type) { break; case ST_Giant: - if (body_type == BodyType::Giant || body_type == BodyType::RaidGiant) { + if ( + body_type == BodyType::Giant || + body_type == BodyType::RaidGiant + ) { return true; } break; case ST_Dragon: - if (body_type == BodyType::Dragon || body_type == BodyType::VeliousDragon) { + if ( + body_type == BodyType::Dragon || + body_type == BodyType::VeliousDragon + ) { return true; } @@ -9898,7 +10246,13 @@ bool Bot::IsMobEngagedByAnyone(Mob* tar) { return true; } - if (m->IsClient() && (m->CastToClient()->AutoAttackEnabled() || m->CastToClient()->AutoFireEnabled())) { + if ( + m->IsClient() && + ( + m->CastToClient()->AutoAttackEnabled() || + m->CastToClient()->AutoFireEnabled() + ) + ) { return true; } } @@ -10340,121 +10694,65 @@ uint16 Bot::GetDefaultSpellTypeIdlePriority(uint16 spell_type, uint8 bot_class, if (!IsBotSpellTypeBeneficial(spell_type)) { return 0; } - - uint16 priority = 0; switch (spell_type) { case BotSpellTypes::VeryFastHeals: - priority = 1; - - break; + return 1; case BotSpellTypes::FastHeals: - priority = 2; - - break; + return 2; case BotSpellTypes::GroupHeals: - priority = 3; - - break; + return 3; case BotSpellTypes::RegularHeal: - priority = 4; - - break; + return 4; case BotSpellTypes::GroupCompleteHeals: - priority = 5; - - break; + return 5; case BotSpellTypes::CompleteHeal: - priority = 6; - - break; + return 6; case BotSpellTypes::GroupHoTHeals: - priority = 7; - - break; + return 7; case BotSpellTypes::HoTHeals: - priority = 8; - - break; + return 8; case BotSpellTypes::GroupCures: - priority = 9; - - break; + return 9; case BotSpellTypes::Cure: - priority = 10; - - break; + return 10; case BotSpellTypes::PetVeryFastHeals: - priority = 11; - - break; + return 11; case BotSpellTypes::PetFastHeals: - priority = 12; - - break; + return 12; case BotSpellTypes::PetRegularHeals: - priority = 13; - - break; + return 13; case BotSpellTypes::PetCompleteHeals: - priority = 14; - - break; + return 14; case BotSpellTypes::PetHoTHeals: - priority = 15; - - break; + return 15; case BotSpellTypes::PetCures: - priority = 16; - - break; + return 16; case BotSpellTypes::Pet: - priority = 17; - - break; + return 17; case BotSpellTypes::Buff: - priority = 18; - - break; + return 18; case BotSpellTypes::OutOfCombatBuffSong: - priority = 19; - - break; + return 19; case BotSpellTypes::ResistBuffs: - priority = 20; - - break; + return 20; case BotSpellTypes::DamageShields: - priority = 21; - - break; + return 21; case BotSpellTypes::PetBuffs: - priority = 22; - - break; + return 22; case BotSpellTypes::PreCombatBuff: - priority = 23; - - break; + return 23; case BotSpellTypes::PreCombatBuffSong: - priority = 24; - - break; + return 24; case BotSpellTypes::PetResistBuffs: - priority = 25; - - break; + return 25; case BotSpellTypes::PetDamageShields: - priority = 26; - - break; + return 26; default: - priority = 0; //unused - - break; + return 0; //unused } - return priority; + return 0; } uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spell_type, uint8 bot_class, uint8 stance) { @@ -11381,7 +11679,7 @@ void Bot::DoCombatPositioning( if (IsTaunting()) { // Taunting adjustments Mob* mob_tar = tar->GetTarget(); - if (!mob_tar || mob_tar == nullptr) { + if (!mob_tar) { DoFaceCheckNoJitter(tar); return; @@ -11429,7 +11727,10 @@ void Bot::DoCombatPositioning( bool Bot::CheckDoubleRangedAttack() { int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; - return (chance && zone->random.Roll(chance)); + return ( + chance && + zone->random.Roll(chance) + ); } bool Bot::RequiresLoSForPositioning() { diff --git a/zone/bot.h b/zone/bot.h index d93d265eba..26e89df046 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -1112,7 +1112,7 @@ class Bot : public NPC { unsigned int RestRegenMana; unsigned int RestRegenEndurance; Timer rest_timer; - Timer ping_timer; + Timer m_ping_timer; int32 base_end; int32 cur_end; int32 max_end; @@ -1121,7 +1121,7 @@ class Bot : public NPC { Timer m_rogue_evade_timer; // Rogue evade timer Timer m_monk_evade_timer; // Monk evade FD timer Timer m_auto_defend_timer; - Timer auto_save_timer; + Timer m_auto_save_timer; Timer m_combat_jitter_timer; bool m_combat_jitter_flag; From 6364b00efddff47ea1461b89ad408c58dc7330f2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:58:27 -0600 Subject: [PATCH 301/394] More cleanup 2 --- common/spdat_bot.cpp | 7 - zone/bot.cpp | 334 +++++++++++++++++++++---------------------- zone/mob.h | 84 +++++------ zone/mob_ai.cpp | 12 +- 4 files changed, 214 insertions(+), 223 deletions(-) diff --git a/common/spdat_bot.cpp b/common/spdat_bot.cpp index 6f40c30015..da634ca0cb 100644 --- a/common/spdat_bot.cpp +++ b/common/spdat_bot.cpp @@ -385,12 +385,6 @@ bool IsCommandedBotSpellType(uint16 spell_type) { case BotSpellTypes::MovementSpeed: case BotSpellTypes::SendHome: case BotSpellTypes::SummonCorpse: - //case BotSpellTypes::Cure: - //case BotSpellTypes::GroupCures: - //case BotSpellTypes::DamageShields: - //case BotSpellTypes::PetDamageShields: - //case BotSpellTypes::ResistBuffs: - //case BotSpellTypes::PetResistBuffs: return true; default: return false; @@ -685,7 +679,6 @@ uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) { } } - return correct_type; } diff --git a/zone/bot.cpp b/zone/bot.cpp index 2b2f0f8dea..2df251bc55 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2148,14 +2148,6 @@ void Bot::AI_Process() } r_group = raid->GetGroup(GetName()); - - //if (mana_timer.Check(false)) { - // raid->SendHPManaEndPacketsFrom(this); - //} - // - //if (send_hp_update_timer.Check(false)) { - // raid->SendHPManaEndPacketsFrom(this); - //} } auto bot_group = GetGroup(); @@ -2842,35 +2834,51 @@ void Bot::DoAttackRounds(Mob* target, int hand) { Attack(target, hand, false, false); if (hand == EQ::invslot::slotPrimary) { + bool is_two_hander = HasTwoHanderEquipped(); - if (HasTwoHanderEquipped()) { - auto extraattackchance = aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + - itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE]; - if (extraattackchance && zone->random.Roll(extraattackchance)) { - auto extraattackamt = std::max({ aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); - for (int i = 0; i < extraattackamt; i++) { - Attack(target, hand, false, false); - } - } - } - else { - auto extraattackchance_primary = aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + - itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE]; - if (extraattackchance_primary && zone->random.Roll(extraattackchance_primary)) { - auto extraattackamt_primary = std::max({ aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); - for (int i = 0; i < extraattackamt_primary; i++) { - Attack(target, hand, false, false); - } + auto extra_attack_chance = is_two_hander + ? aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + + spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_CHANCE] + : aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + + spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE] + + itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_CHANCE]; + + int extra_attack_amt_aas = is_two_hander + ? aabonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] + : aabonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS]; + + int extra_attack_amt_spells = is_two_hander + ? spellbonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] + : spellbonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS]; + + int extra_attack_amt_items = is_two_hander + ? itembonuses.ExtraAttackChance[SBIndex::EXTRA_ATTACK_NUM_ATKS] + : itembonuses.ExtraAttackChancePrimary[SBIndex::EXTRA_ATTACK_NUM_ATKS]; + + int extra_attack_amt = std::max({ extra_attack_amt_aas, extra_attack_amt_spells, extra_attack_amt_items }); + + if (extra_attack_chance && zone->random.Roll(extra_attack_chance)) { + for (int i = 0; i < extra_attack_amt; i++) { + Attack(target, hand, false, false); } } } if (hand == EQ::invslot::slotSecondary) { - auto extraattackchance_secondary = aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + + auto extra_attack_chance_secondary = + aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + + spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE] + itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_CHANCE]; - if (extraattackchance_secondary && zone->random.Roll(extraattackchance_secondary)) { - auto extraattackamt_secondary = std::max({ aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] }); - for (int i = 0; i < extraattackamt_secondary; i++) { + + if (extra_attack_chance_secondary && zone->random.Roll(extra_attack_chance_secondary)) { + auto extra_attack_amt_secondary = std::max({ + aabonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], + spellbonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS], + itembonuses.ExtraAttackChanceSecondary[SBIndex::EXTRA_ATTACK_NUM_ATKS] + }); + + for (int i = 0; i < extra_attack_amt_secondary; i++) { Attack(target, hand, false, false); } } @@ -3650,20 +3658,21 @@ bool Bot::Spawn(Client* botCharacterOwner) { m_ping_timer.Start(8000); m_auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); - pDontHealMeBefore = 0; - pDontGroupHealMeBefore = 0; - pDontGroupHoTHealMeBefore = 0; - pDontBuffMeBefore = Timer::GetCurrentTime() + 400; - pDontDotMeBefore = 0; - pDontRootMeBefore = 0; - pDontSnareMeBefore = 0; - pDontCureMeBefore = 0; - pDontRegularHealMeBefore = 0; - pDontVeryFastHealMeBefore = 0; - pDontFastHealMeBefore = 0; - pDontCompleteHealMeBefore = 0; - pDontGroupCompleteHealMeBefore = 0; - pDontHotHealMeBefore = 0; + m_dont_heal_me_before = 0; + m_dont_regular_heal_me_before = 0; + m_dont_very_fast_heal_me_before = 0; + m_dont_fast_heal_me_before = 0; + m_dont_complete_heal_me_before = 0; + m_dont_hot_heal_me_before = 0; + m_dont_group_heal_me_before = 0; + m_dont_group_hot_heal_me_before = 0; + m_dont_group_complete_heal_me_before = 0; + m_dont_buff_me_before = Timer::GetCurrentTime() + 400; + m_dont_dot_me_before = 0; + m_dont_root_me_before = 0; + m_dont_snare_me_before = 0; + m_dont_cure_me_before = 0; + // there is something askew with spawn struct appearance fields... // I re-enabled this until I can sort it out @@ -4404,18 +4413,19 @@ void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* event_trade.push_back(ClientTrade(trade_instance, trade_index)); continue; } - else { - client->Message( - Chat::Yellow, - fmt::format( - "{} is too small of a stack, you need atleast {}, the trade has been cancelled!", - item_link, - RuleI(Bots, StackSizeMin) - ).c_str() - ); - client->ResetTrade(); - return; - } + + client->Message( + Chat::Yellow, + fmt::format( + "{} is too small of a stack, you need atleast {}, the trade has been cancelled!", + item_link, + RuleI(Bots, StackSizeMin) + ).c_str() + ); + client->ResetTrade(); + + return; + } } else if ( @@ -5407,34 +5417,35 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { case Class::Cleric: case Class::ShadowKnight: case Class::Paladin: - if ( - (GetBaseRace() == OGRE || GetBaseRace() == TROLL || GetBaseRace() == BARBARIAN) || + { + bool is_large_race = ( + GetBaseRace() == OGRE || + GetBaseRace() == TROLL || + GetBaseRace() == BARBARIAN + ); + bool has_bash_skill = GetSkill(EQ::skills::SkillBash) > 0; + bool has_shield_in_secondary = + m_inv.GetItem(EQ::invslot::slotSecondary) && + m_inv.GetItem(EQ::invslot::slotSecondary)->GetItem()->ItemType == EQ::item::ItemTypeShield; + bool has_two_hander_with_aa = + m_inv.GetItem(EQ::invslot::slotPrimary) && + m_inv.GetItem(EQ::invslot::slotPrimary)->GetItem()->IsType2HWeapon() && + GetAA(aa2HandBash) >= 1; + bool can_bash = + is_large_race || ( - GetSkill(EQ::skills::SkillBash) && + has_bash_skill && ( - ( - m_inv.GetItem(EQ::invslot::slotSecondary) && - m_inv.GetItem(EQ::invslot::slotSecondary)->GetItem()->ItemType == EQ::item::ItemTypeShield - ) - || - ( - m_inv.GetItem(EQ::invslot::slotPrimary) && - m_inv.GetItem(EQ::invslot::slotPrimary)->GetItem()->IsType2HWeapon() && - GetAA(aa2HandBash) >= 1 + has_shield_in_secondary || has_two_hander_with_aa ) - ) - ) - ) { - skill_to_use = EQ::skills::SkillBash; - - break; - } + ); - if (GetSkill(EQ::skills::SkillKick)) { - skill_to_use = EQ::skills::SkillKick; + if (can_bash) { + skill_to_use = EQ::skills::SkillBash; } break; + } case Class::Ranger: case Class::Beastlord: if (GetSkill(EQ::skills::SkillKick)) { @@ -5496,16 +5507,21 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { // bards can do riposte frenzy for some reason if (!IsRiposte && GetClass() == Class::Berserker) { int chance = GetLevel() * 2 + GetSkill(EQ::skills::SkillFrenzy); - if (zone->random.Roll0(450) < chance) + + if (zone->random.Roll0(450) < chance) { AtkRounds++; - if (zone->random.Roll0(450) < chance) + } + + if (zone->random.Roll0(450) < chance) { AtkRounds++; + } } while (AtkRounds > 0) { if (GetTarget() != this && TargetValidation(GetTarget())) { DoSpecialAttackDamage(GetTarget(), EQ::skills::SkillFrenzy, dmg, 0, dmg, HasteMod); } + AtkRounds--; } @@ -5899,13 +5915,6 @@ bool Bot::CastSpell( LogSpellsDetail("Spell casting canceled: not able to cast now. Valid? [{}] casting [{}] waiting? [{}] spellend? [{}] stunned? [{}] feared? [{}] mezed? [{}] silenced? [{}]", IsValidSpell(spell_id), casting_spell_id, delaytimer, spellend_timer.Enabled(), IsStunned(), IsFeared(), IsMezzed(), IsSilenced() ); - //if (IsSilenced() && !IsDiscipline(spell_id)) { - // MessageString(Chat::White, SILENCED_STRING); - //} - // - //if (IsAmnesiad() && IsDiscipline(spell_id)) { - // MessageString(Chat::White, MELEE_SILENCE); - //} if (casting_spell_id) { AI_Bot_Event_SpellCastFinished(false, static_cast(casting_spell_slot)); @@ -6755,56 +6764,45 @@ int64 Bot::CalcHPRegen() { } int64 Bot::CalcManaRegen() { - uint8 level = GetLevel(); - uint8 botclass = GetClass(); - int32 regen = 0; + uint8 level = GetLevel(); + uint8 bot_class = GetClass(); - if (GetClass() == Class::Bard) { - if (IsSitting()) { - BuffFadeBySitModifier(); - regen = 2; - regen += (itembonuses.ManaRegen + aabonuses.ManaRegen); - } - else { - regen = 1; - regen += (itembonuses.ManaRegen + aabonuses.ManaRegen); - } - } - else { - if (IsSitting()) { - BuffFadeBySitModifier(); - if (GetArchetype() != Archetype::Melee) { - regen = ((((GetSkill(EQ::skills::SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4); - regen += (spellbonuses.ManaRegen + itembonuses.ManaRegen); - } - else - regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); - } - else - regen = (2 + spellbonuses.ManaRegen + itembonuses.ManaRegen); + // Default values + int32 regen = 2; // Default regen for non-sitting state + float mana_regen_rate = std::max(0.0f, RuleR(Bots, ManaRegen)); - if (IsHeroicINTCasterClass(GetClass())) { - regen += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25; - } - else if (IsHeroicWISCasterClass(GetClass())) { - regen += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25; - } - else { - regen = 0; - } + if (bot_class == Class::Bard) { + regen = IsSitting() ? 2 : 1; - regen += aabonuses.ManaRegen; - regen = ((regen * RuleI(Character, ManaRegenMultiplier)) / 100); - float mana_regen_rate = RuleR(Bots, ManaRegen); + if (IsSitting()) { + BuffFadeBySitModifier(); + } - if (mana_regen_rate < 0.0f) { - mana_regen_rate = 0.0f; - } + regen += itembonuses.ManaRegen + aabonuses.ManaRegen; - regen = (regen * mana_regen_rate); - } + return regen; + } - return regen; + if (IsSitting()) { + BuffFadeBySitModifier(); + + if (GetArchetype() != Archetype::Melee) { + regen = (((GetSkill(EQ::skills::SkillMeditate) / 10) + (level - (level / 4))) / 4) + 4; + } + } + + regen += spellbonuses.ManaRegen + itembonuses.ManaRegen; + + if (IsHeroicINTCasterClass(bot_class)) { + regen += itembonuses.HeroicINT * RuleR(Character, HeroicIntelligenceMultiplier) / 25; + } else if (IsHeroicWISCasterClass(bot_class)) { + regen += itembonuses.HeroicWIS * RuleR(Character, HeroicWisdomMultiplier) / 25; + } + + regen += aabonuses.ManaRegen; + regen = (regen * RuleI(Character, ManaRegenMultiplier)) / 100; + + return static_cast(regen * mana_regen_rate); } uint64 Bot::GetClassHPFactor() { @@ -10222,43 +10220,49 @@ bool Bot::IsValidTargetType(uint16 spell_id, int target_type, uint8 body_type) { } bool Bot::IsMobEngagedByAnyone(Mob* tar) { - if (!tar) { - return false; - } + if (!tar) { + return false; + } - for (Mob* m : GetSpellTargetList()) { - if (m->GetTarget() == tar) { - if ( - m->IsBot() && - m->IsEngaged() && + for (Mob* m : GetSpellTargetList()) { + if (m->GetTarget() != tar) { + continue; + } + + bool bot_is_engaged = m->IsBot() && m->IsEngaged(); + bool bot_melee_or_casting = + bot_is_engaged && + ( + !m->CastToBot()->IsBotNonSpellFighter() || ( - !m->CastToBot()->IsBotNonSpellFighter() || - ( - m->GetLevel() >= m->CastToBot()->GetStopMeleeLevel() && - !m->IsCasting() - ) + m->GetLevel() >= m->CastToBot()->GetStopMeleeLevel() && + !m->IsCasting() ) - ) { - return true; - } + ); - if (m->IsCasting() && SpellBreaksMez(m->CastingSpellID())) { - return true; - } + if (bot_melee_or_casting) { + return true; + } - if ( - m->IsClient() && - ( - m->CastToClient()->AutoAttackEnabled() || - m->CastToClient()->AutoFireEnabled() - ) - ) { - return true; - } - } - } + if ( + m->IsCasting() && + SpellBreaksMez(m->CastingSpellID()) + ) { + return true; + } - return false; + if ( + m->IsClient() && + ( + m->CastToClient()->AutoAttackEnabled() || + m->CastToClient()->AutoFireEnabled() + ) + ) { + return true; + } + } + + return false; } bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id) { @@ -10904,13 +10908,7 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spell_type, uint8 bot_class } uint16 Bot::GetDefaultSpellTypeResistLimit(uint16 spell_type, uint8 stance) { - - if (!IsBotSpellTypeBeneficial(spell_type)) { - return RuleI(Bots, SpellResistLimit); - } - else { - return 0; - } + return IsBotSpellTypeBeneficial(spell_type) ? 0 : RuleI(Bots, SpellResistLimit); } bool Bot::GetDefaultSpellTypeAggroCheck(uint16 spell_type, uint8 stance) { diff --git a/zone/mob.h b/zone/mob.h index 513b4b6f23..3fb130142c 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1338,35 +1338,35 @@ class Mob : public Entity { virtual int GetStuckBehavior() const { return 0; } void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); - inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; } - inline uint32 DontGroupHealMeBefore() const { return pDontGroupHealMeBefore; } - inline uint32 DontGroupHoTHealMeBefore() const { return pDontGroupHoTHealMeBefore; } - inline uint32 DontRegularHealMeBefore() const { return pDontRegularHealMeBefore; } - inline uint32 DontVeryFastHealMeBefore() const { return pDontVeryFastHealMeBefore; } - inline uint32 DontFastHealMeBefore() const { return pDontFastHealMeBefore; } - inline uint32 DontCompleteHealMeBefore() const { return pDontCompleteHealMeBefore; } - inline uint32 DontGroupCompleteHealMeBefore() const { return pDontGroupCompleteHealMeBefore; } - inline uint32 DontHotHealMeBefore() const { return pDontHotHealMeBefore; } - inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; } - inline uint32 DontDotMeBefore() const { return pDontDotMeBefore; } - inline uint32 DontRootMeBefore() const { return pDontRootMeBefore; } - inline uint32 DontSnareMeBefore() const { return pDontSnareMeBefore; } - inline uint32 DontCureMeBefore() const { return pDontCureMeBefore; } + inline uint32 DontHealMeBefore() const { return m_dont_heal_me_before; } + inline uint32 DontGroupHealMeBefore() const { return m_dont_group_heal_me_before; } + inline uint32 DontGroupHoTHealMeBefore() const { return m_dont_group_hot_heal_me_before; } + inline uint32 DontRegularHealMeBefore() const { return m_dont_regular_heal_me_before; } + inline uint32 DontVeryFastHealMeBefore() const { return m_dont_very_fast_heal_me_before; } + inline uint32 DontFastHealMeBefore() const { return m_dont_fast_heal_me_before; } + inline uint32 DontCompleteHealMeBefore() const { return m_dont_complete_heal_me_before; } + inline uint32 DontGroupCompleteHealMeBefore() const { return m_dont_group_complete_heal_me_before; } + inline uint32 DontHotHealMeBefore() const { return m_dont_hot_heal_me_before; } + inline uint32 DontBuffMeBefore() const { return m_dont_buff_me_before; } + inline uint32 DontDotMeBefore() const { return m_dont_dot_me_before; } + inline uint32 DontRootMeBefore() const { return m_dont_root_me_before; } + inline uint32 DontSnareMeBefore() const { return m_dont_snare_me_before; } + inline uint32 DontCureMeBefore() const { return m_dont_cure_me_before; } - void SetDontRootMeBefore(uint32 time) { pDontRootMeBefore = time; } - void SetDontHealMeBefore(uint32 time) { pDontHealMeBefore = time; } - void SetDontGroupHealMeBefore(uint32 time) { pDontGroupHealMeBefore = time; } - void SetDontGroupHoTHealMeBefore(uint32 time) { pDontGroupHoTHealMeBefore = time; } - void SetDontRegularHealMeBefore(uint32 time) { pDontRegularHealMeBefore = time; } - void SetDontVeryFastHealMeBefore(uint32 time) { pDontVeryFastHealMeBefore = time; } - void SetDontFastHealMeBefore(uint32 time) { pDontFastHealMeBefore = time; } - void SetDontCompleteHealMeBefore(uint32 time) { pDontCompleteHealMeBefore = time; } - void SetDontGroupCompleteHealMeBefore(uint32 time) { pDontGroupCompleteHealMeBefore = time; } - void SetDontHotHealMeBefore(uint32 time) { pDontHotHealMeBefore = time; } - void SetDontBuffMeBefore(uint32 time) { pDontBuffMeBefore = time; } - void SetDontDotMeBefore(uint32 time) { pDontDotMeBefore = time; } - void SetDontSnareMeBefore(uint32 time) { pDontSnareMeBefore = time; } - void SetDontCureMeBefore(uint32 time) { pDontCureMeBefore = time; } + void SetDontRootMeBefore(uint32 time) { m_dont_root_me_before = time; } + void SetDontHealMeBefore(uint32 time) { m_dont_heal_me_before = time; } + void SetDontGroupHealMeBefore(uint32 time) { m_dont_group_heal_me_before = time; } + void SetDontGroupHoTHealMeBefore(uint32 time) { m_dont_group_hot_heal_me_before = time; } + void SetDontRegularHealMeBefore(uint32 time) { m_dont_regular_heal_me_before = time; } + void SetDontVeryFastHealMeBefore(uint32 time) { m_dont_very_fast_heal_me_before = time; } + void SetDontFastHealMeBefore(uint32 time) { m_dont_fast_heal_me_before = time; } + void SetDontCompleteHealMeBefore(uint32 time) { m_dont_complete_heal_me_before = time; } + void SetDontGroupCompleteHealMeBefore(uint32 time) { m_dont_group_complete_heal_me_before = time; } + void SetDontHotHealMeBefore(uint32 time) { m_dont_hot_heal_me_before = time; } + void SetDontBuffMeBefore(uint32 time) { m_dont_buff_me_before = time; } + void SetDontDotMeBefore(uint32 time) { m_dont_dot_me_before = time; } + void SetDontSnareMeBefore(uint32 time) { m_dont_snare_me_before = time; } + void SetDontCureMeBefore(uint32 time) { m_dont_cure_me_before = time; } // calculate interruption of spell via movement of mob void SaveSpellLoc() { m_SpellLocation = glm::vec3(m_Position); } @@ -1961,20 +1961,20 @@ class Mob : public Entity { bool pause_timer_complete; bool DistractedFromGrid; - uint32 pDontHealMeBefore; - uint32 pDontGroupHealMeBefore; - uint32 pDontGroupHoTHealMeBefore; - uint32 pDontRegularHealMeBefore; - uint32 pDontVeryFastHealMeBefore; - uint32 pDontFastHealMeBefore; - uint32 pDontCompleteHealMeBefore; - uint32 pDontGroupCompleteHealMeBefore; - uint32 pDontHotHealMeBefore; - uint32 pDontBuffMeBefore; - uint32 pDontDotMeBefore; - uint32 pDontRootMeBefore; - uint32 pDontSnareMeBefore; - uint32 pDontCureMeBefore; + uint32 m_dont_heal_me_before; + uint32 m_dont_group_heal_me_before; + uint32 m_dont_group_hot_heal_me_before; + uint32 m_dont_regular_heal_me_before; + uint32 m_dont_very_fast_heal_me_before; + uint32 m_dont_fast_heal_me_before; + uint32 m_dont_complete_heal_me_before; + uint32 m_dont_group_complete_heal_me_before; + uint32 m_dont_hot_heal_me_before; + uint32 m_dont_buff_me_before; + uint32 m_dont_dot_me_before; + uint32 m_dont_root_me_before; + uint32 m_dont_snare_me_before; + uint32 m_dont_cure_me_before; // hp event int nexthpevent; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 69255073b1..63b76276c5 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -393,12 +393,12 @@ void Mob::AI_Init() minLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMin); maxLastFightingDelayMoving = RuleI(NPC, LastFightingDelayMovingMax); - pDontHealMeBefore = 0; - pDontBuffMeBefore = Timer::GetCurrentTime() + 400; - pDontDotMeBefore = 0; - pDontRootMeBefore = 0; - pDontSnareMeBefore = 0; - pDontCureMeBefore = 0; + m_dont_heal_me_before = 0; + m_dont_buff_me_before = Timer::GetCurrentTime() + 400; + m_dont_dot_me_before = 0; + m_dont_root_me_before = 0; + m_dont_snare_me_before = 0; + m_dont_cure_me_before = 0; } void NPC::AI_Init() From b836e78adb8b622a8f6e82791a0fc15d51994342 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:14:12 -0600 Subject: [PATCH 302/394] Fix BotMeditate to med at proper percentages --- zone/bot.cpp | 33 +++++++++++++++------------------ 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2df251bc55..1972836c6e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -1823,24 +1823,21 @@ void Bot::SpellProcess() { } void Bot::BotMeditate(bool is_sitting) { - if ( - GetManaRatio() < GetSitManaPct() || - (GetHPRatio() < GetSitHPPct() && GetLevel() < GetStopMeleeLevel()) - ) { - if ( - ( - !IsEngaged() || - (IsEngaged() && GetMedInCombat() && !HasTargetReflection()) - ) && - !is_sitting - ) { - Sit(); - } - } else { - if (is_sitting) { - Stand(); - } - } + bool needs_to_med = + (!IsEngaged() && (GetManaRatio() < 100 || GetHPRatio() < 100)) || + (GetMedInCombat() && !HasTargetReflection() && + ( + GetManaRatio() < GetSitManaPct() || + (GetHPRatio() < GetSitHPPct() && GetLevel() >= GetStopMeleeLevel()) + ) + ); + + if (needs_to_med && !is_sitting) { + Sit(); + } + else if (!needs_to_med && is_sitting) { + Stand(); + } } bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { From 88d3bcfdf55b92d78822ff918d91b66fcce3a1e6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:16:30 -0600 Subject: [PATCH 303/394] Correct GetStopMeleeLevel checks for some buff checks --- zone/bot.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 1972836c6e..3b8eeaab58 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9919,7 +9919,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { case Archetype::Caster: if ( tar->IsBot() && - (tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel()) && + (tar->GetLevel() >= tar->CastToBot()->GetStopMeleeLevel()) && ( IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS) @@ -9983,7 +9983,7 @@ bool Bot::CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar) { case Archetype::Caster: if ( tar->IsBot() && - (tar->GetLevel() > tar->CastToBot()->GetStopMeleeLevel()) && + (tar->GetLevel() >= tar->CastToBot()->GetStopMeleeLevel()) && ( IsEffectInSpell(spell_id, SE_AttackSpeed) || IsEffectInSpell(spell_id, SE_ReverseDS) From 389c8617673b8bd81ec88c371bc05b074853d8a5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 23 Jan 2025 15:25:52 -0600 Subject: [PATCH 304/394] Add back hpmanaend update to bot raid, force timer update to prevent spamming --- zone/bot.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index 3b8eeaab58..d2e89a512c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2144,6 +2144,11 @@ void Bot::AI_Process() SetVerifiedRaid(true); } + if (mana_timer.Check() || send_hp_update_timer.Check()) { + LogTestDebug("{} is sending a hp/mana update", GetCleanName()); + raid->SendHPManaEndPacketsFrom(this); + } + r_group = raid->GetGroup(GetName()); } From aea4b1f201eb41c60f9d008fd46b7ff182be04ad Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 10:27:13 -0600 Subject: [PATCH 305/394] Remove log --- zone/bot.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index d2e89a512c..9a4a17864c 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2145,7 +2145,6 @@ void Bot::AI_Process() } if (mana_timer.Check() || send_hp_update_timer.Check()) { - LogTestDebug("{} is sending a hp/mana update", GetCleanName()); raid->SendHPManaEndPacketsFrom(this); } From e1925d1b13770875d77bcdc8aca169968eb4aec9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:01:16 -0600 Subject: [PATCH 306/394] Cleanup ranged and ammo calculations - Adds throwing check for match --- zone/bot.cpp | 108 +++++++++++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 56 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 9a4a17864c..91d59dfa3f 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -584,33 +584,34 @@ uint32 Bot::GetBotRangedValue() { ammo_item = ammo_inst->GetItem(); } + bool has_ammo = ammo_item; + + if (!range_item || !has_ammo) { + return 0; + } + // Bow requires arrows if ( - range_item && range_item->ItemType == EQ::item::ItemTypeBow && - ( - !ammo_item || - ammo_item->ItemType != EQ::item::ItemTypeArrow - ) + ammo_item->ItemType != EQ::item::ItemTypeArrow ) { return 0; } // Throwing items if ( - range_item && - ( - range_item->ItemType == EQ::item::ItemTypeSmallThrowing || - range_item->ItemType == EQ::item::ItemTypeLargeThrowing - ) + range_item->ItemType == EQ::item::ItemTypeSmallThrowing || + range_item->ItemType == EQ::item::ItemTypeLargeThrowing ) { - return range_item->Range; + if (range_item->ID == ammo_item->ID) { + return range_item->Range; + } + + return 0; // mismatched throwing } // Bows and arrows if ( - range_item && - ammo_item && range_item->ItemType == EQ::item::ItemTypeBow && ammo_item->ItemType == EQ::item::ItemTypeArrow ) { @@ -1903,43 +1904,42 @@ bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { } // Bow requires arrows - if ( - !ammo || + // Check if ammo is invalid + bool is_invalid_ammo = !ammo; + + // Check if ranged weapon type is invalid + bool is_invalid_ranged_weapon_type = ranged_weapon && + (ranged_weapon->ItemType != EQ::item::ItemTypeBow && + ranged_weapon->ItemType != EQ::item::ItemTypeSmallThrowing && + ranged_weapon->ItemType != EQ::item::ItemTypeLargeThrowing); + + // Check if bow has the wrong ammo + bool is_bow_with_invalid_ammo = ranged_weapon && + (ranged_weapon->ItemType == EQ::item::ItemTypeBow && + (!ammo || ammo->ItemType != EQ::item::ItemTypeArrow)); + + // Check if throwing weapon has insufficient charges + bool is_throwing_weapon_with_insufficient_ammo = ranged_weapon && + (ranged_weapon->ItemType == EQ::item::ItemTypeSmallThrowing || ranged_weapon->ItemType == EQ::item::ItemTypeLargeThrowing) && ( - ranged_weapon && + !ammo_item || ammo_item->GetCharges() < 1 || // Not enough ammo ( - ( - ranged_weapon->ItemType != EQ::item::ItemTypeBow && - ranged_weapon->ItemType != EQ::item::ItemTypeSmallThrowing && - ranged_weapon->ItemType != EQ::item::ItemTypeLargeThrowing) || - ( - ranged_weapon->ItemType == EQ::item::ItemTypeBow && - (ammo->ItemType != EQ::item::ItemTypeArrow) - ) || - ( - ( - ranged_weapon->ItemType == EQ::item::ItemTypeSmallThrowing || - ranged_weapon->ItemType == EQ::item::ItemTypeLargeThrowing - ) && - ammo_item->GetCharges() < 1 || - ( - ( - RuleI(Bots, StackSizeMin) != -1 && - ranged_item->GetCharges() != ranged_weapon->StackSize - ) || - ranged_item->GetCharges() < RuleI(Bots, StackSizeMin) - ) - ) + (RuleI(Bots, StackSizeMin) != -1 && ranged_item->GetCharges() != ranged_weapon->StackSize) || // Invalid stack size + ranged_item->GetCharges() < RuleI(Bots, StackSizeMin) // Charges below minimum ) - ) - ) { + ); + + // Final ranged weapon validity check + bool is_invalid_ranged_weapon = is_invalid_ranged_weapon_type || is_bow_with_invalid_ammo || is_throwing_weapon_with_insufficient_ammo; + + // Final condition + if (is_invalid_ammo || is_invalid_ranged_weapon) { if (!ammo || ammo_item->GetCharges() < 1) { if (!GetCombatRoundForAlerts()) { SetCombatRoundForAlerts(); - BotGroupSay(this, "I do not have enough any ammo!"); + BotGroupSay(this, "I do not have any ammo!"); } } - return false; } @@ -1955,26 +1955,22 @@ bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { SendItemAnimation(other, ammo, (ranged_weapon->ItemType == EQ::item::ItemTypeBow ? EQ::skills::SkillArchery : EQ::skills::SkillThrowing)); if (ranged_weapon->ItemType == EQ::item::ItemTypeBow) { - DoArcheryAttackDmg(other, ranged_item, ammo_item); // watch - //EndlessQuiver AA base1 = 100% Chance to avoid consumption arrow. - int ChanceAvoidConsume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; + DoArcheryAttackDmg(other, ranged_item, ammo_item); - // Consume Ammo, unless Ammo Consumption is disabled or player has Endless Quiver + int chance_avoid_consume = aabonuses.ConsumeProjectile + itembonuses.ConsumeProjectile + spellbonuses.ConsumeProjectile; bool consumes_ammo = RuleB(Bots, BotArcheryConsumesAmmo); - if ( - consumes_ammo && - ( - ranged_weapon->ExpendableArrow || - !ChanceAvoidConsume || - (ChanceAvoidConsume < 100 && zone->random.Int(0, 99) > ChanceAvoidConsume) - ) - ) { - ammo_item->SetCharges((ammo_item->GetCharges() - 1)); + bool is_expendable_arrow = ranged_weapon->ExpendableArrow; + bool no_chance_to_avoid = chance_avoid_consume == 0; + bool failed_avoid_check = chance_avoid_consume < 100 && zone->random.Int(0, 99) > chance_avoid_consume; + bool should_consume_ammo = consumes_ammo && (is_expendable_arrow || no_chance_to_avoid || failed_avoid_check); + + if (should_consume_ammo) { + ammo_item->SetCharges(ammo_item->GetCharges() - 1); LogCombatDetail("Consumed Archery Ammo from slot {}.", EQ::invslot::slotAmmo); if (ammo_item->GetCharges() < 1) { RemoveBotItemBySlot(EQ::invslot::slotAmmo); - BotRemoveEquipItem(EQ::invslot::slotAmmo); + BotRemoveEquipItem(EQ::invslot::slotAmmo); } } else if (!consumes_ammo) { From 9134516847fdc4eedb7cd5c7ef0a9b45c70d7b5b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 11:01:39 -0600 Subject: [PATCH 307/394] Add check in distance calculations to stay at range if set even if no ammo or ranged --- zone/bot.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index 91d59dfa3f..f9d9505d71 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3180,6 +3180,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it float min_distance = RuleI(Combat, MinRangedAttackDist); float max_distance = GetBotRangedValue(); float desired_range = GetBotDistanceRanged(); + max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged if set to ranged even if items/ammo aren't correct melee_distance_min = std::max(min_distance, (desired_range / 2)); melee_distance = std::min(max_distance, desired_range); } From 8b863f44026bdb9d701f7a83a221e7bcd554b1ef Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:23:10 -0600 Subject: [PATCH 308/394] Move melee distance calculations to better function --- zone/bot.cpp | 160 +++++++++++++++++++++------------------------ zone/bot.h | 29 +------- zone/bot_structs.h | 16 +++++ 3 files changed, 94 insertions(+), 111 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index f9d9505d71..edb4a3ece4 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2287,19 +2287,29 @@ void Bot::AI_Process() } // COMBAT RANGE CALCS - - bool at_combat_range = false; - bool behind_mob = BehindMob(tar, GetX(), GetY()); - bool front_mob = InFrontMob(tar, GetX(), GetY()); + bool front_mob = InFrontMob(tar, GetX(), GetY()); + bool behind_mob = BehindMob(tar, GetX(), GetY()); uint8 stop_melee_level = GetStopMeleeLevel(); - const EQ::ItemInstance* p_item; - const EQ::ItemInstance* s_item; - float melee_distance_min = 0.0f; - float melee_distance_max = 0.0f; - float melee_distance = 0.0f; - tar_distance = sqrt(tar_distance); + tar_distance = sqrt(tar_distance); // sqrt this for future calculations + + CombatRangeInput input = { + .target = tar, + .target_distance = tar_distance, + .behind_mob = behind_mob, + .stop_melee_level = stop_melee_level + }; - CheckCombatRange(tar, tar_distance, at_combat_range, behind_mob, p_item, s_item, melee_distance_min, melee_distance, melee_distance_max, stop_melee_level); + CombatRangeOutput o = EvaluateCombatRange(input); + + // Combat range variables + bool at_combat_range = o.at_combat_range; + float melee_distance_min = o.melee_distance_min; + float melee_distance = o.melee_distance; + float melee_distance_max = o.melee_distance_max; + + // Item variables + const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary); + const EQ::ItemInstance* s_item = GetBotItem(EQ::invslot::slotSecondary); // PULLING FLAG (ACTIONABLE RANGE) @@ -3021,31 +3031,11 @@ bool Bot::TryEvade(Mob* tar) { return false; } -void Bot::CheckCombatRange(Mob* tar, float tar_distance, bool& at_combat_range, bool behind_mob, const EQ::ItemInstance*& p_item, const EQ::ItemInstance*& s_item, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stop_melee_level) { - at_combat_range = false; - - p_item = GetBotItem(EQ::invslot::slotPrimary); - s_item = GetBotItem(EQ::invslot::slotSecondary); - - bool backstab_weapon = false; - - if (GetBehindMob()) { - if (GetClass() == Class::Rogue) { - backstab_weapon = p_item && p_item->GetItemBackstabDamage(); - } - } - - // Calculate melee distances - CalcMeleeDistances(tar, p_item, s_item, backstab_weapon, behind_mob, melee_distance_min, melee_distance, melee_distance_max, stop_melee_level); +CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) { + CombatRangeOutput o; - if (tar_distance <= melee_distance) { - at_combat_range = true; - } -} - -void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_item, const EQ::ItemInstance* const& s_item, bool backstab_weapon, bool behind_mob, float& melee_distance_min, float& melee_distance, float& melee_distance_max, uint8 stop_melee_level) { float size_mod = GetSize(); - float other_size_mod = tar->GetSize(); + float other_size_mod = input.target->GetSize(); // For races with a fixed size if (GetRace() == Race::LavaDragon || GetRace() == Race::Wurm || GetRace() == Race::GhostDragon) { @@ -3056,7 +3046,7 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it } // For races with a fixed size - if (tar->GetRace() == Race::LavaDragon || tar->GetRace() == Race::Wurm || tar->GetRace() == Race::GhostDragon) { + if (input.target->GetRace() == Race::LavaDragon || input.target->GetRace() == Race::Wurm || input.target->GetRace() == Race::GhostDragon) { other_size_mod = 60.0f; } else if (other_size_mod < 6.0f) { @@ -3077,12 +3067,12 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it size_mod *= (size_mod * 4.0f); } - if (tar->GetRace() == Race::VeliousDragon) // Lord Vyemm and other velious dragons + if (input.target->GetRace() == Race::VeliousDragon) // Lord Vyemm and other velious dragons { size_mod *= 1.75; } - if (tar->GetRace() == Race::DragonSkeleton) // Dracoliche in Fear. Skeletal Dragon + if (input.target->GetRace() == Race::DragonSkeleton) // Dracoliche in Fear. Skeletal Dragon { size_mod *= 2.25; } @@ -3094,86 +3084,82 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it size_mod = (size_mod / 7.0f); } - melee_distance_max = size_mod; + o.melee_distance_max = size_mod; if (!RuleB(Bots, UseFlatNormalMeleeRange)) { + + bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon(); + bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield(); + bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage(); + switch (GetClass()) { case Class::Warrior: case Class::Paladin: case Class::ShadowKnight: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.45f; - } - else if ((s_item && s_item->GetItem()->IsTypeShield()) || (!p_item && !s_item)) { - melee_distance = melee_distance_max * 0.35f; - } - else { - melee_distance = melee_distance_max * 0.40f; - } + o.melee_distance = ( + is_two_hander ? o.melee_distance_max * 0.45f + : is_shield ? o.melee_distance_max * 0.35f + : o.melee_distance_max * 0.40f + ); + break; case Class::Necromancer: case Class::Wizard: case Class::Magician: case Class::Enchanter: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.95f; - } - else { - melee_distance = melee_distance_max * 0.75f; - } + o.melee_distance = ( + is_two_hander ? o.melee_distance_max * 0.95f + : o.melee_distance_max * 0.75f + ); + break; case Class::Rogue: - if (behind_mob && backstab_weapon) { - if (p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.30f; - } - else { - melee_distance = melee_distance_max * 0.25f; - } - break; - } - // Fall-through + o.melee_distance = ( + input.behind_mob && is_backstab_weapon + ? o.melee_distance_max * 0.35f + : o.melee_distance_max * 0.50f + ); + + break; default: - if (p_item && p_item->GetItem()->IsType2HWeapon()) { - melee_distance = melee_distance_max * 0.70f; - } - else { - melee_distance = melee_distance_max * 0.50f; - } + o.melee_distance = ( + is_two_hander ? o.melee_distance_max * 0.70f + : o.melee_distance_max * 0.50f + ); break; } - melee_distance = sqrt(melee_distance); - melee_distance_max = sqrt(melee_distance_max); + o.melee_distance = sqrt(o.melee_distance); + o.melee_distance_max = sqrt(o.melee_distance_max); } else { - melee_distance_max = sqrt(melee_distance_max); - melee_distance = melee_distance_max * RuleR(Bots, NormalMeleeRangeDistance); + o.melee_distance_max = sqrt(o.melee_distance_max); + o.melee_distance = o.melee_distance_max * RuleR(Bots, NormalMeleeRangeDistance); } - if (melee_distance > RuleR(Bots, MaxDistanceForMelee)) { - melee_distance = RuleR(Bots, MaxDistanceForMelee); + if (o.melee_distance > RuleR(Bots, MaxDistanceForMelee)) { + o.melee_distance = RuleR(Bots, MaxDistanceForMelee); } - melee_distance_min = melee_distance * RuleR(Bots, PercentMinMeleeDistance); + o.melee_distance_min = o.melee_distance * RuleR(Bots, PercentMinMeleeDistance); if (IsTaunting()) { - melee_distance_min = melee_distance * RuleR(Bots, PercentTauntMinMeleeDistance); - melee_distance = melee_distance * RuleR(Bots, TauntNormalMeleeRangeDistance); + o.melee_distance_min = o.melee_distance * RuleR(Bots, PercentTauntMinMeleeDistance); + o.melee_distance = o.melee_distance * RuleR(Bots, TauntNormalMeleeRangeDistance); } - bool is_stop_melee_level = GetLevel() >= stop_melee_level; + bool is_stop_melee_level = GetLevel() >= input.stop_melee_level; if (!IsTaunting() && !IsBotRanged() && !is_stop_melee_level && GetMaxMeleeRange()) { - melee_distance_min = melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); - melee_distance = melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); + o.melee_distance_min = o.melee_distance_max * RuleR(Bots, PercentMinMaxMeleeRangeDistance); + o.melee_distance = o.melee_distance_max * RuleR(Bots, PercentMaxMeleeRangeDistance); } if (is_stop_melee_level && !IsBotRanged()) { float desired_range = GetBotDistanceRanged(); - melee_distance_min = std::max(melee_distance, (desired_range / 2)); - melee_distance = std::max((melee_distance + 1), desired_range); + o.melee_distance_min = std::max(o.melee_distance, (desired_range / 2)); + o.melee_distance = std::max((o.melee_distance + 1), desired_range); } if (IsBotRanged()) { @@ -3181,9 +3167,13 @@ void Bot::CalcMeleeDistances(const Mob* tar, const EQ::ItemInstance* const& p_it float max_distance = GetBotRangedValue(); float desired_range = GetBotDistanceRanged(); max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged if set to ranged even if items/ammo aren't correct - melee_distance_min = std::max(min_distance, (desired_range / 2)); - melee_distance = std::min(max_distance, desired_range); + o.melee_distance_min = std::max(min_distance, (desired_range / 2)); + o.melee_distance = std::min(max_distance, desired_range); } + + o.at_combat_range = (input.target_distance <= o.melee_distance); + + return o; } bool Bot::IsValidTarget( diff --git a/zone/bot.h b/zone/bot.h index 26e89df046..c6f43201d2 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -1006,36 +1006,13 @@ class Bot : public NPC { bool ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance); void BotPullerProcess(Client* bot_owner, Raid* raid); - - // Movement Methods - void CalcMeleeDistances( - const Mob* tar, - const EQ::ItemInstance* const& p_item, - const EQ::ItemInstance* const& s_item, - bool behind_mob, - bool backstab_weapon, - float& melee_distance_min, - float& melee_distance, - float& melee_distance_max, - uint8 stop_melee_level - ); - // Combat Checks + CombatRangeOutput EvaluateCombatRange(const CombatRangeInput& input); + void SetBerserkState(); bool CheckIfCasting(float fm_distance); void HealRotationChecks(); - void CheckCombatRange( - Mob* tar, - float tar_distance, - bool& at_combat_range, - bool behind_mob, - const EQ::ItemInstance*& p_item, - const EQ::ItemInstance*& s_item, - float& melee_distance_min, - float& melee_distance, - float& melee_distance_max, - uint8 stop_melee_level - ); + bool GetCombatJitterFlag() { return m_combat_jitter_flag; } void SetCombatJitterFlag(bool flag = true) { m_combat_jitter_flag = flag; } bool GetCombatOutOfRangeJitterFlag() { return m_combat_out_of_range_jitter_flag; } diff --git a/zone/bot_structs.h b/zone/bot_structs.h index a9d336a213..1d57979c8e 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -127,4 +127,20 @@ struct BotSpellTypesByClass_Struct { std::string description; }; +struct CombatRangeInput { + Mob* target; + float target_distance; + bool behind_mob; + uint8 stop_melee_level; + const EQ::ItemInstance* p_item; + const EQ::ItemInstance* s_item; +}; + +struct CombatRangeOutput { + bool at_combat_range = false; + float melee_distance_min = 0.0f; + float melee_distance = 0.0f; + float melee_distance_max = 0.0f; +}; + #endif // BOT_STRUCTS From cec1dbda942420697197c291c2ac2957cfecaeff Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:23:46 -0600 Subject: [PATCH 309/394] Add GetBuffTargets helper --- zone/bot.cpp | 42 ++++++++++++------------------------------ zone/bot.h | 1 + 2 files changed, 13 insertions(+), 30 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index edb4a3ece4..0d6ed55385 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -6216,16 +6216,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe } if (!noGroupSpell) { - std::vector v; - - if (RuleB(Bots, RaidBuffing)) { - v = GetSpellTargetList(); - } - else { - v = GatherSpellTargets(false, spellTarget); - } - - for (Mob* m : v) { + for (Mob* m : GetBuffTargets(spellTarget)) { if (IsEffectInSpell(thespell, SE_AbsorbMagicAtt) || IsEffectInSpell(thespell, SE_Rune)) { for (int i = 0; i < m->GetMaxTotalSlots(); i++) { uint32 buff_count = m->GetMaxTotalSlots(); @@ -6252,7 +6243,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQ::spe SpellOnTarget(thespell, m->GetPet()); } - SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (v.size() - 1))); + SetMana(GetMana() - (GetActSpellCost(thespell, spells[thespell].mana) * (GetBuffTargets(spellTarget).size() - 1))); } } @@ -6282,16 +6273,7 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel } if (spellTarget->IsOfClientBotMerc()) { - std::vector v; - - if (RuleB(Bots, RaidBuffing)) { - v = GetSpellTargetList(); - } - else { - v = GatherSpellTargets(false, spellTarget); - } - - for (Mob* m : v) { + for (Mob* m : GetBuffTargets(spellTarget)) { if (m == this && spellTarget != this) { continue; } @@ -10048,17 +10030,9 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { return true; } - std::vector v; uint16 target_id = tar->GetID(); - if (RuleB(Bots, CrossRaidBuffingAndHealing)) { - v = GetSpellTargetList(); - } - else { - v = GetGroupSpellTargetList(); - } - - for (Mob* m : v) { + for (Mob* m : GetSpellTargetList()) { if ( m->IsBot() && m->IsCasting() && @@ -12381,3 +12355,11 @@ void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, st AIBot_spells_by_type[spell.type].emplace_back(spell_with_index); } } + +std::vector Bot::GetBuffTargets(Mob* spellTarget) { + if (RuleB(Bots, RaidBuffing)) { + return GetSpellTargetList(); + } + + return GatherSpellTargets(false, spellTarget); +} diff --git a/zone/bot.h b/zone/bot.h index c6f43201d2..ec9b764e8a 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -517,6 +517,7 @@ class Bot : public NPC { void SetSpellTargetList(std::vector spell_target_list) { _spellTargetList = spell_target_list; } std::vector GetGroupSpellTargetList() { return _groupSpellTargetList; } void SetGroupSpellTargetList(std::vector spell_target_list) { _groupSpellTargetList = spell_target_list; } + std::vector GetBuffTargets(Mob* spellTarget); Raid* GetStoredRaid() { return _storedRaid; } void SetStoredRaid(Raid* stored_raid) { _storedRaid = stored_raid; } bool GetVerifiedRaid() { return _verifiedRaid; } From f8ff6794c1658afc5ee4147878ad18f4faec471c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:45:20 -0600 Subject: [PATCH 310/394] Missing p_item, s_item in CombatRangeInput --- zone/bot.cpp | 15 ++++++++------- zone/bot_structs.h | 16 ++++++++-------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0d6ed55385..897f7bb9fb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2291,12 +2291,17 @@ void Bot::AI_Process() bool behind_mob = BehindMob(tar, GetX(), GetY()); uint8 stop_melee_level = GetStopMeleeLevel(); tar_distance = sqrt(tar_distance); // sqrt this for future calculations + // Item variables + const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary); + const EQ::ItemInstance* s_item = GetBotItem(EQ::invslot::slotSecondary); CombatRangeInput input = { .target = tar, .target_distance = tar_distance, .behind_mob = behind_mob, - .stop_melee_level = stop_melee_level + .stop_melee_level = stop_melee_level, + .p_item = p_item, + .s_item = s_item }; CombatRangeOutput o = EvaluateCombatRange(input); @@ -2307,10 +2312,6 @@ void Bot::AI_Process() float melee_distance = o.melee_distance; float melee_distance_max = o.melee_distance_max; - // Item variables - const EQ::ItemInstance* p_item = GetBotItem(EQ::invslot::slotPrimary); - const EQ::ItemInstance* s_item = GetBotItem(EQ::invslot::slotSecondary); - // PULLING FLAG (ACTIONABLE RANGE) if (GetPullingFlag()) { @@ -3088,8 +3089,8 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) { if (!RuleB(Bots, UseFlatNormalMeleeRange)) { - bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon(); - bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield(); + bool is_two_hander = input.p_item && input.p_item->GetItem()->IsType2HWeapon(); + bool is_shield = input.s_item && input.s_item->GetItem()->IsTypeShield(); bool is_backstab_weapon = input.p_item && input.p_item->GetItemBackstabDamage(); switch (GetClass()) { diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 1d57979c8e..278f5418e9 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -128,19 +128,19 @@ struct BotSpellTypesByClass_Struct { }; struct CombatRangeInput { - Mob* target; - float target_distance; - bool behind_mob; - uint8 stop_melee_level; + Mob* target; + float target_distance; + bool behind_mob; + uint8 stop_melee_level; const EQ::ItemInstance* p_item; const EQ::ItemInstance* s_item; }; struct CombatRangeOutput { - bool at_combat_range = false; - float melee_distance_min = 0.0f; - float melee_distance = 0.0f; - float melee_distance_max = 0.0f; + bool at_combat_range = false; + float melee_distance_min = 0.0f; + float melee_distance = 0.0f; + float melee_distance_max = 0.0f; }; #endif // BOT_STRUCTS From f50b64cb11ad26dd5e380fd911dcc626b779d912 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 12:56:20 -0600 Subject: [PATCH 311/394] Linux test? --- zone/bot.h | 16 ++++++++++++++++ zone/bot_structs.h | 16 ---------------- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/zone/bot.h b/zone/bot.h index ec9b764e8a..673bde3ffa 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -212,6 +212,22 @@ static std::map botSubType_names = { { CommandedSubTypes::Selo, "Selo" } }; +struct CombatRangeInput { + Mob* target; + float target_distance; + bool behind_mob; + uint8 stop_melee_level; + const EQ::ItemInstance* p_item; + const EQ::ItemInstance* s_item; +}; + +struct CombatRangeOutput { + bool at_combat_range = false; + float melee_distance_min = 0.0f; + float melee_distance = 0.0f; + float melee_distance_max = 0.0f; +}; + class Bot : public NPC { friend class Mob; public: diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 278f5418e9..a9d336a213 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -127,20 +127,4 @@ struct BotSpellTypesByClass_Struct { std::string description; }; -struct CombatRangeInput { - Mob* target; - float target_distance; - bool behind_mob; - uint8 stop_melee_level; - const EQ::ItemInstance* p_item; - const EQ::ItemInstance* s_item; -}; - -struct CombatRangeOutput { - bool at_combat_range = false; - float melee_distance_min = 0.0f; - float melee_distance = 0.0f; - float melee_distance_max = 0.0f; -}; - #endif // BOT_STRUCTS From 06dcd62d8c36a0e1bce2bf115f8d87f91e3d3a1f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 13:09:39 -0600 Subject: [PATCH 312/394] Reduce GetCorrectBotSpellType branching slightly This is still an ugly ass function but my brain is melted --- common/spdat_bot.cpp | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/common/spdat_bot.cpp b/common/spdat_bot.cpp index da634ca0cb..e6e6a68c2f 100644 --- a/common/spdat_bot.cpp +++ b/common/spdat_bot.cpp @@ -644,38 +644,38 @@ uint16 GetCorrectBotSpellType(uint16 spell_type, uint16 spell_id) { else if (IsRegularGroupHealSpell(spell_id)) { correct_type = BotSpellTypes::GroupHeals; } + + return correct_type; } - else { - if (IsVeryFastHealSpell(spell_id)) { - correct_type = BotSpellTypes::VeryFastHeals; - } - else if (IsFastHealSpell(spell_id)) { - correct_type = BotSpellTypes::FastHeals; - } - else if (IsCompleteHealSpell(spell_id)) { - correct_type = BotSpellTypes::CompleteHeal; - } - else if (IsHealOverTimeSpell(spell_id)) { - correct_type = BotSpellTypes::HoTHeals; - } - else if (IsRegularSingleTargetHealSpell(spell_id)) { - correct_type = BotSpellTypes::RegularHeal; - } - else if (IsRegularPetHealSpell(spell_id)) { - correct_type = BotSpellTypes::RegularHeal; - } + + if (IsVeryFastHealSpell(spell_id)) { + correct_type = BotSpellTypes::VeryFastHeals; + } + else if (IsFastHealSpell(spell_id)) { + correct_type = BotSpellTypes::FastHeals; + } + else if (IsCompleteHealSpell(spell_id)) { + correct_type = BotSpellTypes::CompleteHeal; + } + else if (IsHealOverTimeSpell(spell_id)) { + correct_type = BotSpellTypes::HoTHeals; + } + else if (IsRegularSingleTargetHealSpell(spell_id)) { + correct_type = BotSpellTypes::RegularHeal; + } + else if (IsRegularPetHealSpell(spell_id)) { + correct_type = BotSpellTypes::RegularHeal; } } else if (IsAnyBuffSpell(spell_id)) { + correct_type = BotSpellTypes::Buff; + if (IsResistanceOnlySpell(spell_id)) { correct_type = BotSpellTypes::ResistBuffs; } else if (IsDamageShieldOnlySpell(spell_id)) { correct_type = BotSpellTypes::DamageShields; } - else { - correct_type = BotSpellTypes::Buff; - } } } From 58a7d258996e38787927ed8e94129877a29a2f9d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 15:42:25 -0600 Subject: [PATCH 313/394] Line fixes --- zone/bot.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 897f7bb9fb..8a6c7a4541 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -3101,7 +3101,7 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) { is_two_hander ? o.melee_distance_max * 0.45f : is_shield ? o.melee_distance_max * 0.35f : o.melee_distance_max * 0.40f - ); + ); break; case Class::Necromancer: @@ -3111,7 +3111,7 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) { o.melee_distance = ( is_two_hander ? o.melee_distance_max * 0.95f : o.melee_distance_max * 0.75f - ); + ); break; case Class::Rogue: @@ -3119,14 +3119,14 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) { input.behind_mob && is_backstab_weapon ? o.melee_distance_max * 0.35f : o.melee_distance_max * 0.50f - ); + ); break; default: o.melee_distance = ( is_two_hander ? o.melee_distance_max * 0.70f : o.melee_distance_max * 0.50f - ); + ); break; } @@ -3159,8 +3159,8 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) { if (is_stop_melee_level && !IsBotRanged()) { float desired_range = GetBotDistanceRanged(); - o.melee_distance_min = std::max(o.melee_distance, (desired_range / 2)); - o.melee_distance = std::max((o.melee_distance + 1), desired_range); + o.melee_distance_min = std::max(o.melee_distance, (desired_range / 2)); + o.melee_distance = std::max((o.melee_distance + 1), desired_range); } if (IsBotRanged()) { @@ -3168,8 +3168,8 @@ CombatRangeOutput Bot::EvaluateCombatRange(const CombatRangeInput& input) { float max_distance = GetBotRangedValue(); float desired_range = GetBotDistanceRanged(); max_distance = (max_distance == 0 ? desired_range : max_distance); // stay ranged if set to ranged even if items/ammo aren't correct - o.melee_distance_min = std::max(min_distance, (desired_range / 2)); - o.melee_distance = std::min(max_distance, desired_range); + o.melee_distance_min = std::max(min_distance, (desired_range / 2)); + o.melee_distance = std::min(max_distance, desired_range); } o.at_combat_range = (input.target_distance <= o.melee_distance); From 22121b741922496fac9e06b530e1a7712e5168ac Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 20:10:42 -0600 Subject: [PATCH 314/394] Make bot pets only do half damage in pvp --- zone/attack.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 278a8b9546..99375763c3 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2398,7 +2398,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool LogCombat("Final damage against [{}]: [{}]", other->GetName(), my_hit.damage_done); - if (other->IsClient() && IsPet() && GetOwner()->IsClient()) { + if (other->IsClient() && IsPet() && GetOwner()->IsOfClientBot()) { //pets do half damage to clients in pvp my_hit.damage_done /= 2; if (my_hit.damage_done < 1) { From fbaa09569e18d1cea7ac86a34f87d7ccc584ef53 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 20:25:18 -0600 Subject: [PATCH 315/394] Add bot pet pvp damage to tune --- zone/tune.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/tune.cpp b/zone/tune.cpp index e46a8b32f3..c26c92597e 100644 --- a/zone/tune.cpp +++ b/zone/tune.cpp @@ -853,7 +853,7 @@ int64 Mob::TuneNPCAttack(Mob* other, bool no_avoid, bool no_hit_chance, int hit_ TuneDoAttack(other, my_hit, nullptr, no_avoid, no_hit_chance, ac_override, add_ac, avoidance_override, accuracy_override, add_avoidance, add_accuracy); LogCombat("Final damage against [{}]: [{}]", other->GetName(), my_hit.damage_done); - if (other->IsClient() && IsPet() && GetOwner()->IsClient()) { + if (other->IsClient() && IsPet() && GetOwner()->IsOfClientBot()) { //pets do half damage to clients in pvp my_hit.damage_done /= 2; if (my_hit.damage_done < 1) From 41f362ce616cad073005fa1d1157f1f5380dcbbc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 20:26:48 -0600 Subject: [PATCH 316/394] Add bot pet check for AIYellForHelp --- zone/npc.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index ba5782c347..7210dee79c 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -3666,7 +3666,7 @@ void NPC::AIYellForHelp(Mob *sender, Mob *attacker) && mob != attacker && mob->GetPrimaryFaction() != 0 && !mob->IsEngaged() - && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsClient())) + && ((!mob->IsPet()) || (mob->IsPet() && mob->GetOwner() && !mob->GetOwner()->IsOfClientBot())) ) { /** From b82ed57cd468a734a722abab8d9e3ef943d58f46 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 24 Jan 2025 20:29:16 -0600 Subject: [PATCH 317/394] Add bots to UseSpellImpliedTargeting --- zone/spells.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 6a9d0a367b..bea78285e7 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -182,14 +182,14 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot, } //Goal of Spells:UseSpellImpliedTargeting is to replicate the EQ2 feature where spells will 'pass through' invalid targets to target's target to try to find a valid target. - if (RuleB(Spells,UseSpellImpliedTargeting) && IsClient()) { + if (RuleB(Spells,UseSpellImpliedTargeting) && IsOfClientBot()) { Mob* spell_target = entity_list.GetMobID(target_id); if (spell_target) { Mob* targets_target = spell_target->GetTarget(); if (targets_target) { // If either this is beneficial and the target is not a player or player's pet or vis versa - if ((IsBeneficialSpell(spell_id) && (!(spell_target->IsClient() || (spell_target->HasOwner() && spell_target->GetOwner()->IsClient())))) - || (IsDetrimentalSpell(spell_id) && (spell_target->IsClient() || (spell_target->HasOwner() && spell_target->GetOwner()->IsClient())))) { + if ((IsBeneficialSpell(spell_id) && (!(spell_target->IsOfClientBot() || (spell_target->HasOwner() && spell_target->GetOwner()->IsOfClientBot())))) + || (IsDetrimentalSpell(spell_id) && (spell_target->IsOfClientBot() || (spell_target->HasOwner() && spell_target->GetOwner()->IsOfClientBot())))) { //Check if the target's target is a valid target; we can use DoCastingChecksOnTarget() here because we can let it handle the failure as vanilla would if (DoCastingChecksOnTarget(true, spell_id, targets_target)) { target_id = targets_target->GetID(); From 2df4a4e4369bd8d113bd4c58d35c91faaf2c6d70 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 25 Jan 2025 07:01:21 -0600 Subject: [PATCH 318/394] Move toggleranged, togglehelm and illusionblock to new help window. Add actionable support --- zone/bot_commands/bot.cpp | 314 ++++++++++++++++++++++----- zone/bot_commands/illusion_block.cpp | 3 +- zone/gm_commands/illusion_block.cpp | 30 ++- 3 files changed, 286 insertions(+), 61 deletions(-) diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 3a5e326761..a8346a267c 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1596,31 +1596,95 @@ void bot_command_summon(Client *c, const Seperator *sep) void bot_command_toggle_ranged(Client *c, const Seperator *sep) { if (helper_command_alias_fail(c, "bot_command_toggle_ranged", sep->arg[0], "bottoggleranged")) { + c->Message(Chat::White, "note: Toggles a ranged bot between melee and ranged weapon use."); + return; } if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); - c->Message(Chat::White, "note: Toggles a ranged bot between melee and ranged weapon use"); + BotCommandHelpParams p; + + p.description = { "Toggles a ranged bot between melee and ranged weapon use." }; + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = + { + "To set BotA to use their ranged:", + fmt::format( + "{} 1 byname BotA", + sep->arg[0] + ) + }; + p.examples_two = + { + "To set all ranger bots to ranged:", + fmt::format( + "{} 1 byclass {}", + sep->arg[0], + Class::Ranger + ) + }; + p.examples_two = + { + "To check the ranged status of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0], + Class::Ranger + ) + }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + return; } - const int ab_mask = ActionableBots::ABM_Type1; - std::string arg1 = sep->arg[1]; - bool ranged_state = false; - bool toggle_ranged = true; int ab_arg = 1; - if (!arg1.compare("on")) { - ranged_state = true; - toggle_ranged = false; + bool current_check = false; + uint32 type_value = 0; + + if (sep->IsNumber(1)) { + type_value = atoi(sep->arg[1]); ++ab_arg; + if (type_value < 0 || type_value > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } } - else if (!arg1.compare("off")) { - toggle_ranged = false; + else if (!arg1.compare("current")) { ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; } + const int ab_mask = ActionableBots::ABM_Type1; std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; @@ -1629,93 +1693,231 @@ void bot_command_toggle_ranged(Client *c, const Seperator *sep) } std::vector sbl; - if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } - for (auto bot_iter : sbl) { - if (!bot_iter) { - continue; - } + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); - if (bot_iter->GetBotRangedValue() < RuleI(Combat, MinRangedAttackDist)) { - c->Message(Chat::Yellow, "%s does not have proper weapons or ammo to be at range.", bot_iter->GetCleanName()); + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { continue; } - if (toggle_ranged) { - bot_iter->SetBotRangedSetting(!bot_iter->IsBotRanged()); + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} currently ranged.'", + my_bot->GetCleanName(), + my_bot->IsBotRanged() ? "am" : "am no longer" + ).c_str() + ); } else { - bot_iter->SetBotRangedSetting(ranged_state); + if (my_bot->GetBotRangedValue() < RuleI(Combat, MinRangedAttackDist)) { + c->Message(Chat::Yellow, "%s does not have proper weapons or ammo to be at range.", my_bot->GetCleanName()); + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + my_bot->SetBotRangedSetting(type_value); + my_bot->ChangeBotRangedWeapons(my_bot->IsBotRanged()); + ++success_count; } + } - bot_iter->ChangeBotRangedWeapons(bot_iter->IsBotRanged()); + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} ranged.'", + first_found->GetCleanName(), + first_found->GetIllusionBlock() ? "am now" : "am no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} ranged.", + success_count, + type_value ? "are now" : "are no longer" + ).c_str() + ); + } } } void bot_command_toggle_helm(Client *c, const Seperator *sep) { - if (helper_command_alias_fail(c, "bot_command_toggle_helm", sep->arg[0], "bottogglehelm")) + if (helper_command_alias_fail(c, "bot_command_toggle_helm", sep->arg[0], "bottogglehelm")) { + c->Message(Chat::White, "note: Toggles whether or not bots will show their helm."); + return; + } + if (helper_is_help_or_usage(sep->arg[1])) { - c->Message(Chat::White, "usage: %s ([option: on | off]) ([actionable: target | byname | ownergroup | ownerraid | targetgroup | namesgroup | healrotationmembers | healrotationtargets | mmr | byclass | byrace | spawned] ([actionable_name]))", sep->arg[0]); + BotCommandHelpParams p; + + p.description = { "Toggles whether or not bots will show their helm." }; + p.example_format = { fmt::format("{} [value] [actionable]", sep->arg[0]) }; + p.examples_one = + { + "To set BotA to show their helm:", + fmt::format( + "{} 1 byname BotA", + sep->arg[0] + ) + }; + p.examples_two = + { + "To set all bots to show their helm:", + fmt::format( + "{} 1 spawned", + sep->arg[0] + ) + }; + p.examples_three = + { + "To check the toggle helm status of all bots:", + fmt::format( + "{} current spawned", + sep->arg[0] + ) + }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + return; } - const int ab_mask = ActionableBots::ABM_NoFilter; - + std::string arg1 = sep->arg[1]; - bool helm_state = false; - bool toggle_helm = true; int ab_arg = 1; - if (!arg1.compare("on")) { - helm_state = true; - toggle_helm = false; + bool current_check = false; + uint32 type_value = 0; + + if (sep->IsNumber(1)) { + type_value = atoi(sep->arg[1]); ++ab_arg; + if (type_value < 0 || type_value > 1) { + c->Message(Chat::Yellow, "You must enter either 0 for disabled or 1 for enabled."); + + return; + } } - else if (!arg1.compare("off")) { - toggle_helm = false; + else if (!arg1.compare("current")) { ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; } std::vector sbl; - auto ab_type = ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, sep->arg[(ab_arg + 1)]); - if (ab_type == ActionableBots::ABT_None) + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; + } - int bot_count = 0; - for (auto bot_iter : sbl) { - if (!bot_iter) + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { continue; + } - if (toggle_helm) - bot_iter->SetShowHelm(!bot_iter->GetShowHelm()); - else - bot_iter->SetShowHelm(helm_state); + if (!first_found) { + first_found = my_bot; + } - if (ab_type != ActionableBots::ABT_All) { + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} show my helm.'", + my_bot->GetCleanName(), + my_bot->GetShowHelm() ? "will" : "will not" + ).c_str() + ); + } + else { + my_bot->SetShowHelm(type_value); - EQApplicationPacket* outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); - SpawnAppearance_Struct* saptr = (SpawnAppearance_Struct*)outapp->pBuffer; - saptr->spawn_id = bot_iter->GetID(); + auto outapp = new EQApplicationPacket(OP_SpawnAppearance, sizeof(SpawnAppearance_Struct)); + SpawnAppearance_Struct* saptr = (SpawnAppearance_Struct*) outapp->pBuffer; + saptr->spawn_id = my_bot->GetID(); saptr->type = AppearanceType::ShowHelm; - saptr->parameter = bot_iter->GetShowHelm(); + saptr->parameter = my_bot->GetShowHelm(); - entity_list.QueueClients(bot_iter, outapp); + entity_list.QueueClients(my_bot, outapp, true); safe_delete(outapp); - //helper_bot_appearance_form_update(bot_iter); + ++success_count; } - ++bot_count; } - - if (ab_type == ActionableBots::ABT_All) { - c->Message(Chat::White, "%s all of your bot show helm flags", toggle_helm ? "Toggled" : (helm_state ? "Set" : "Cleared")); - } - else { - c->Message(Chat::White, "%s %i of your spawned bot show helm flags", toggle_helm ? "Toggled" : (helm_state ? "Set" : "Cleared"), bot_count); + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I {} show my helm.'", + first_found->GetCleanName(), + first_found->GetIllusionBlock() ? "will now" : "will no longer" + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots {} show their helm.", + success_count, + type_value ? "will now" : "will no longer" + ).c_str() + ); + } } // Notes: diff --git a/zone/bot_commands/illusion_block.cpp b/zone/bot_commands/illusion_block.cpp index f38b72d92a..01c9eafa46 100644 --- a/zone/bot_commands/illusion_block.cpp +++ b/zone/bot_commands/illusion_block.cpp @@ -54,7 +54,6 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) ); } - return; } @@ -94,6 +93,7 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) const int ab_mask = ActionableBots::ABM_Type1; std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } @@ -131,6 +131,7 @@ void bot_command_illusion_block(Client* c, const Seperator* sep) ++success_count; } } + if (!current_check) { if (success_count == 1 && first_found) { c->Message( diff --git a/zone/gm_commands/illusion_block.cpp b/zone/gm_commands/illusion_block.cpp index b0457d7da5..1715c0f8f7 100644 --- a/zone/gm_commands/illusion_block.cpp +++ b/zone/gm_commands/illusion_block.cpp @@ -4,10 +4,32 @@ void command_illusion_block(Client* c, const Seperator* sep) { int arguments = sep->argnum; if (!arguments || !strcasecmp(sep->arg[1], "help")) { - c->Message(Chat::White, "usage: #illusionblock [help | current | value]."); - c->Message(Chat::White, "note: Used to control whether or not illusion effects will land on you."); - c->Message(Chat::White, "note: A value of 0 is disabled (Allow Illusions), 1 is enabled (Block Illusions)."); - c->Message(Chat::White, "note: Use [current] to check the current setting."); + BotCommandHelpParams p; + + p.description = { "Toggles whether or not you will block the illusion effects of spells cast by players or bots." }; + p.example_format = { fmt::format("{} [value]", sep->arg[0]) }; + p.examples_one = + { + "To enable illusion block:", + fmt::format( + "{} 1", + sep->arg[0] + ) + }; + p.examples_two = + { + "To disable illusion block:", + fmt::format( + "{} 0", + sep->arg[0] + ) + }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + return; } From abaefc8e7f429c1cb55dbd7edcc2abceec166277 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 25 Jan 2025 07:01:39 -0600 Subject: [PATCH 319/394] Add bot and bot pet checks to various spells, auras and targeting checks that were missing. --- zone/attack.cpp | 10 +++++----- zone/aura.cpp | 34 +++++++++++++++++----------------- zone/bot.cpp | 2 +- zone/client.cpp | 4 ++-- zone/client_packet.cpp | 2 +- zone/effects.cpp | 2 +- zone/entity.cpp | 2 +- zone/mob.cpp | 2 +- zone/mob_ai.cpp | 12 ++++++------ zone/npc.cpp | 2 +- zone/spells.cpp | 8 ++++---- zone/tune.cpp | 4 ++-- 12 files changed, 42 insertions(+), 42 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index 99375763c3..3b4d207990 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2811,7 +2811,7 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy const uint32 con_level = give_exp->GetLevelCon(GetLevel()); if (con_level != ConsiderColor::Gray) { - if (!GetOwner() || (GetOwner() && !GetOwner()->IsClient())) { + if (!GetOwner() || (GetOwner() && !GetOwner()->IsOfClientBot())) { give_exp_client->AddEXP(ExpSource::Kill, final_exp, con_level, false, this); if ( @@ -6486,7 +6486,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac int mod = GetSpecialAbilityParam(SpecialAbility::Rampage, 2); if (mod > 0) spec_mod = mod; - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { //SE_PC_Pet_Rampage SPA 464 on pet, damage modifier int spell_mod = spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD]; if (spell_mod > spec_mod) @@ -6497,7 +6497,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac int mod = GetSpecialAbilityParam(SpecialAbility::AreaRampage, 2); if (mod > 0) spec_mod = mod; - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { //SE_PC_Pet_AE_Rampage SPA 465 on pet, damage modifier int spell_mod = spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD]; if (spell_mod > spec_mod) @@ -6929,7 +6929,7 @@ void Mob::DoMainHandAttackRounds(Mob *target, ExtraAttackOptions *opts, bool ram Attack(target, EQ::invslot::slotPrimary, false, false, false, opts); if (CanThisClassDoubleAttack() && CheckDoubleAttack()) { Attack(target, EQ::invslot::slotPrimary, false, false, false, opts); - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { int chance = spellbonuses.PC_Pet_Flurry + itembonuses.PC_Pet_Flurry + aabonuses.PC_Pet_Flurry; if (chance && zone->random.Roll(chance)) { Flurry(nullptr); @@ -6990,7 +6990,7 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts, bool ramp if (CanThisClassDoubleAttack() && GetLevel() > 35 && CheckDoubleAttack() && !rampage) { Attack(target, EQ::invslot::slotSecondary, false, false, false, opts); - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { int chance = spellbonuses.PC_Pet_Flurry + itembonuses.PC_Pet_Flurry + aabonuses.PC_Pet_Flurry; if (chance && zone->random.Roll(chance)) { Flurry(nullptr); diff --git a/zone/aura.cpp b/zone/aura.cpp index f1285dc5e0..28fe7f15bf 100644 --- a/zone/aura.cpp +++ b/zone/aura.cpp @@ -81,7 +81,7 @@ void Aura::ProcessOnAllFriendlies(Mob *owner) if (!mob) { continue; } - if (mob->IsClient() || mob->IsPetOwnerClient() || mob->IsMerc() || mob->IsBot()) { + if (mob->IsOfClientBotMerc() || mob->IsPetOwnerOfClientBot()) { auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // we are already on the list, let's check for removal @@ -131,7 +131,7 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) std::set delayed_remove; bool is_buff = IsBuffSpell(spell_id); // non-buff spells don't cast on enter - if (owner->IsRaidGrouped() && owner->IsClient()) { // currently raids are just client, but safety check + if (owner->IsRaidGrouped() && owner->IsOfClientBot()) { // currently raids are just client, but safety check auto raid = owner->GetRaid(); if (raid == nullptr) { // well shit owner->RemoveAura(GetID(), false, true); @@ -198,17 +198,17 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // verify still good! - if (mob->IsClient()) { + if (mob->IsOfClientBot()) { if (!verify_raid_client(mob->CastToClient())) { delayed_remove.insert(mob->GetID()); } } - else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { + else if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner()) { if (!verify_raid_client_pet(mob)) { delayed_remove.insert(mob->GetID()); } } - else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) { auto npc = mob->CastToNPC(); if (!verify_raid_client_swarm(npc)) { delayed_remove.insert(mob->GetID()); @@ -216,19 +216,19 @@ void Aura::ProcessOnAllGroupMembers(Mob *owner) } } else { // we're not on it! - if (mob->IsClient() && verify_raid_client(mob->CastToClient())) { + if (mob->IsOfClientBot() && verify_raid_client(mob->CastToClient())) { casted_on.insert(mob->GetID()); if (is_buff) { SpellFinished(spell_id, mob); } } - else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + else if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner() && verify_raid_client_pet(mob)) { casted_on.insert(mob->GetID()); if (is_buff) { SpellFinished(spell_id, mob); } } - else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) { auto npc = mob->CastToNPC(); if (verify_raid_client_swarm(npc)) { casted_on.insert(mob->GetID()); @@ -376,7 +376,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) auto group_member = owner->GetOwnerOrSelf(); if (group_member->IsRaidGrouped() && - group_member->IsClient()) { // currently raids are just client, but safety check + group_member->IsOfClientBot()) { // currently raids are just client, but safety check auto raid = group_member->GetRaid(); if (raid == nullptr) { // well shit owner->RemoveAura(GetID(), false, true); @@ -428,12 +428,12 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) auto it = casted_on.find(mob->GetID()); if (it != casted_on.end()) { // verify still good! - if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner()) { + if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner()) { if (!verify_raid_client_pet(mob)) { delayed_remove.insert(mob->GetID()); } } - else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) { auto npc = mob->CastToNPC(); if (!verify_raid_client_swarm(npc)) { delayed_remove.insert(mob->GetID()); @@ -441,16 +441,16 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) } } else { // we're not on it! - if (mob->IsClient()) { + if (mob->IsOfClientBot()) { continue; // never hit client } - else if (mob->IsPet() && mob->IsPetOwnerClient() && mob->GetOwner() && verify_raid_client_pet(mob)) { + else if (mob->IsPet() && mob->IsPetOwnerOfClientBot() && mob->GetOwner() && verify_raid_client_pet(mob)) { casted_on.insert(mob->GetID()); if (is_buff) { SpellFinished(spell_id, mob); } } - else if (mob->IsNPC() && mob->IsPetOwnerClient()) { + else if (mob->IsNPC() && mob->IsPetOwnerOfClientBot()) { auto npc = mob->CastToNPC(); if (verify_raid_client_swarm(npc)) { casted_on.insert(mob->GetID()); @@ -499,7 +499,7 @@ void Aura::ProcessOnGroupMembersPets(Mob *owner) } } else { // not on, check if we should be! - if (mob->IsClient()) { + if (mob->IsOfClientBot()) { continue; } else if (mob->IsPet() && verify_group_pet(mob)) { @@ -690,7 +690,7 @@ void Aura::ProcessSpawns() continue; } - if (!e.second->IsClient()) { + if (!e.second->IsOfClientBot()) { continue; } @@ -800,7 +800,7 @@ bool Aura::ShouldISpawnFor(Client *c) return true; } - if (owner->IsRaidGrouped() && owner->IsClient()) { + if (owner->IsRaidGrouped() && owner->IsOfClientBot()) { auto raid = owner->GetRaid(); if (raid == nullptr) { return false; diff --git a/zone/bot.cpp b/zone/bot.cpp index 8a6c7a4541..f1b6fbb5da 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,7 +31,7 @@ /* TODO bot rewrite: ---go through IsPetOwnerClient checks for pets +--move togglehelm and bottoggleranged to new window format --command cleanup (move to new help window, make more descriptive) --Add quest methods for functions */ diff --git a/zone/client.cpp b/zone/client.cpp index bc6f720ee2..71110240ac 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -8807,10 +8807,10 @@ void Client::ProcessAggroMeter() // probably should have PVP rules ... if (cur_tar && cur_tar != this) { - if (cur_tar->IsNPC() && !cur_tar->IsPetOwnerClient() && cur_tar->GetID() != m_aggrometer.get_target_id()) { + if (cur_tar->IsNPC() && !cur_tar->IsPetOwnerOfClientBot() && cur_tar->GetID() != m_aggrometer.get_target_id()) { m_aggrometer.set_target_id(cur_tar->GetID()); send_targetinfo = true; - } else if ((cur_tar->IsPetOwnerClient() || cur_tar->IsClient()) && cur_tar->GetTarget() && cur_tar->GetTarget()->GetID() != m_aggrometer.get_target_id()) { + } else if ((cur_tar->IsPetOwnerOfClientBot() || cur_tar->IsClient()) && cur_tar->GetTarget() && cur_tar->GetTarget()->GetID() != m_aggrometer.get_target_id()) { m_aggrometer.set_target_id(cur_tar->GetTarget()->GetID()); send_targetinfo = true; } diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index f51bac48b0..27a674b85b 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -15230,7 +15230,7 @@ void Client::Handle_OP_TargetMouse(const EQApplicationPacket *app) GetTarget()->IsTargeted(1); return; } - else if (GetTarget()->IsPetOwnerClient()) + else if (GetTarget()->IsPetOwnerOfClientBot()) { GetTarget()->IsTargeted(1); return; diff --git a/zone/effects.cpp b/zone/effects.cpp index 63e349f1b3..2b6472446e 100644 --- a/zone/effects.cpp +++ b/zone/effects.cpp @@ -1142,7 +1142,7 @@ void EntityList::AESpell( continue; } - if (spells[spell_id].target_type == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerClient()) { + if (spells[spell_id].target_type == ST_TargetAENoPlayersPets && current_mob->IsPetOwnerOfClientBot()) { continue; } diff --git a/zone/entity.cpp b/zone/entity.cpp index ced5f6130c..1ec17ad643 100644 --- a/zone/entity.cpp +++ b/zone/entity.cpp @@ -5549,7 +5549,7 @@ Mob *EntityList::GetClosestMobByBodyType(Mob *sender, uint8 BodyType, bool skip_ continue; // Do not detect client pets - if (skip_client_pets && CurrentMob->IsPet() && CurrentMob->IsPetOwnerClient()) + if (skip_client_pets && CurrentMob->IsPet() && CurrentMob->IsPetOwnerOfClientBot()) continue; CurrentDistance = ((CurrentMob->GetY() - sender->GetY()) * (CurrentMob->GetY() - sender->GetY())) + diff --git a/zone/mob.cpp b/zone/mob.cpp index 977859bd9a..d0374f07a9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4891,7 +4891,7 @@ bool Mob::HateSummon() { } else { bool target_is_client_pet = ( target->IsPet() && - target->IsPetOwnerClient() + target->IsPetOwnerOfClientBot() ); bool set_new_guard_spot = !(IsNPC() && target_is_client_pet); diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 63b76276c5..f39797c108 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -139,7 +139,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates if ( (spells[AIspells[i].spellid].target_type == ST_Target || tar == this) && tar->DontHealMeBefore() < Timer::GetCurrentTime() - && !(tar->IsPet() && tar->GetOwner()->IsClient()) //no buffing PC's pets + && !(tar->IsPet() && tar->GetOwner()->IsOfClientBot()) //no buffing PC's pets ) { auto hp_ratio = tar->GetIntHPRatio(); @@ -180,7 +180,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates && tar->DontBuffMeBefore() < Timer::GetCurrentTime() && !tar->IsImmuneToSpell(AIspells[i].spellid, this) && tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0 - && !(tar->IsPet() && tar->GetOwner()->IsClient() && this != tar) //no buffing PC's pets, but they can buff themself + && !(tar->IsPet() && tar->GetOwner()->IsOfClientBot() && this != tar) //no buffing PC's pets, but they can buff themself ) { if(!checked_los) { @@ -1219,7 +1219,7 @@ void Mob::AI_Process() { //SE_PC_Pet_Rampage SPA 464 on pet, chance modifier - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { int chance = spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_CHANCE]; if (chance && zone->random.Roll(chance)) { Rampage(nullptr); @@ -1262,7 +1262,7 @@ void Mob::AI_Process() { } //SE_PC_Pet_Rampage SPA 465 on pet, chance modifier - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { int chance = spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE] + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_CHANCE]; if (chance && zone->random.Roll(chance)) { Rampage(nullptr); @@ -1945,8 +1945,8 @@ void Mob::StartEnrage() return; } - if(RuleB(NPC, LiveLikeEnrage) && !((IsPet() && !IsCharmed() && GetOwner() && GetOwner()->IsClient()) || - (CastToNPC()->GetSwarmOwner() && entity_list.GetMob(CastToNPC()->GetSwarmOwner())->IsClient()))) { + if(RuleB(NPC, LiveLikeEnrage) && !((IsPet() && !IsCharmed() && GetOwner() && GetOwner()->IsOfClientBot()) || + (CastToNPC()->GetSwarmOwner() && entity_list.GetMob(CastToNPC()->GetSwarmOwner())->IsOfClientBot()))) { return; } diff --git a/zone/npc.cpp b/zone/npc.cpp index 7210dee79c..468e4b35e1 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -555,7 +555,7 @@ void NPC::SetTarget(Mob* mob) { // either normal pet and owner is client or charmed pet and owner is client Mob *owner = nullptr; - if (IsPet() && IsPetOwnerClient()) { + if (IsPet() && IsPetOwnerOfClientBot()) { owner = GetOwner(); } else if (IsCharmed()) { owner = GetOwner(); diff --git a/zone/spells.cpp b/zone/spells.cpp index bea78285e7..6a8ba036f9 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2708,7 +2708,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in // it can affect up to 7 people if the targeted group is not our own // Allow pets who cast group spells to affect the group. - if (spell_target->IsPetOwnerClient() && IsPetOwnerClient()) { + if (spell_target->IsPetOwnerOfClientBot() && IsPetOwnerOfClientBot()) { Mob* owner = spell_target->GetOwner(); if (owner) { @@ -5389,7 +5389,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use // JULY 24, 2002 changes int level = GetLevel(); - if (RuleB(Spells,July242002PetResists) && IsPetOwnerClient() && caster->IsNPC() && !caster->IsPetOwnerClient()) { + if (RuleB(Spells,July242002PetResists) && IsPetOwnerOfClientBot() && caster->IsNPC() && !caster->IsPetOwnerOfClientBot()) { auto owner = GetOwner(); if (owner != nullptr) { target_resist = std::max(target_resist, owner->GetResist(resist_type)); @@ -7029,7 +7029,7 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust) float b = (GetY() * dX - dY * GetX()) / (dX - GetX()); while (iter != targets_in_range.end()) { - if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerClient())) || + if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerOfClientBot())) || (*iter)->BehindMob(this, (*iter)->GetX(), (*iter)->GetY())) { ++iter; continue; @@ -7113,7 +7113,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust) auto iter = targets_in_range.begin(); while (iter != targets_in_range.end()) { - if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerClient()))) { + if (!(*iter) || (beneficial_targets && ((*iter)->IsNPC() && !(*iter)->IsPetOwnerOfClientBot()))) { ++iter; continue; } diff --git a/zone/tune.cpp b/zone/tune.cpp index c26c92597e..39d6e9bdb2 100644 --- a/zone/tune.cpp +++ b/zone/tune.cpp @@ -1556,7 +1556,7 @@ void Mob::TuneCommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraA int mod = GetSpecialAbilityParam(SpecialAbility::Rampage, 2); if (mod > 0) spec_mod = mod; - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { //SE_PC_Pet_Rampage SPA 464 on pet, damage modifier int spell_mod = spellbonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD]; if (spell_mod > spec_mod) @@ -1567,7 +1567,7 @@ void Mob::TuneCommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraA int mod = GetSpecialAbilityParam(SpecialAbility::AreaRampage, 2); if (mod > 0) spec_mod = mod; - if ((IsPet() || IsTempPet()) && IsPetOwnerClient()) { + if ((IsPet() || IsTempPet()) && IsPetOwnerOfClientBot()) { //SE_PC_Pet_AE_Rampage SPA 465 on pet, damage modifier int spell_mod = spellbonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + itembonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD] + aabonuses.PC_Pet_AE_Rampage[SBIndex::PET_RAMPAGE_DMG_MOD]; if (spell_mod > spec_mod) From d5be066feee1eb202cafd4b86870ff4c45cbf706 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 25 Jan 2025 07:24:29 -0600 Subject: [PATCH 320/394] update todo --- zone/bot.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index f1b6fbb5da..9c628b54c1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,8 +31,7 @@ /* TODO bot rewrite: ---move togglehelm and bottoggleranged to new window format ---command cleanup (move to new help window, make more descriptive) +--command cleanup remaining commands (move to new help window, make more descriptive) --Add quest methods for functions */ From 764715f625e8c2e36589f8fde2955069494fba0c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 25 Jan 2025 23:19:09 -0600 Subject: [PATCH 321/394] New lines --- zone/bot.cpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 9c628b54c1..c7300f6f6b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11766,7 +11766,13 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* break; } - if (!m->IsNPC() || (!IsCommandedSpell() && !m->CastToNPC()->IsOnHatelist(caster->GetOwner()))) { + if ( + !m->IsNPC() || + ( + !IsCommandedSpell() && + !m->CastToNPC()->IsOnHatelist(caster->GetOwner()) + ) + ) { continue; } @@ -11783,7 +11789,11 @@ bool Bot::HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* } } else { - if (!tar || spell_range < Distance(caster->GetPosition(), tar->GetPosition()) || !DoLosChecks(this, m)) { + if ( + !tar || + spell_range < Distance(caster->GetPosition(), tar->GetPosition()) || + !DoLosChecks(m) + ) { continue; } From fbb2341fcad77fefa9f5f56ae429bdefa6fe43d8 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 09:44:46 -0600 Subject: [PATCH 322/394] Correct DoLosChecks --- zone/bot.cpp | 17 ++++---- zone/bot_command.cpp | 2 +- zone/bot_commands/attack.cpp | 9 +++- zone/bot_commands/click_item.cpp | 11 +++++ zone/bot_commands/precombat.cpp | 11 ++++- zone/bot_commands/pull.cpp | 8 +++- zone/botspellsai.cpp | 2 +- zone/client_packet.cpp | 16 ++++++- zone/mob.cpp | 71 +++++++++++++++++++++----------- zone/mob.h | 6 +-- 10 files changed, 112 insertions(+), 41 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c7300f6f6b..940e0ce27b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2272,7 +2272,6 @@ void Bot::AI_Process() // This causes conflicts with default pet handler (bounces between targets) if (NOT_PULLING_BOT && HasPet() && (GetClass() != Class::Enchanter || GetPet()->GetPetType() != petAnimation || GetAA(aaAnimationEmpathy) >= 2)) { - // We don't add to hate list here because it's assumed to already be on the list GetPet()->SetTarget(tar); } @@ -2316,7 +2315,7 @@ void Bot::AI_Process() if (GetPullingFlag()) { if (!TargetValidation(tar)) { return; } - if (!DoLosChecks(this, tar)) { + if (!DoLosChecks(tar)) { return; } @@ -3196,7 +3195,7 @@ bool Bot::IsValidTarget( (!Charmed() && tar->GetUltimateOwner()->IsOfClientBotMerc()) || lo_distance > leash_distance || tar_distance > leash_distance || - (!GetAttackingFlag() && !CheckLosCheat(this, tar) && !CheckLosCheat(leash_owner, tar)) || + (!GetAttackingFlag() && !CheckLosCheat(tar) && !leash_owner->CheckLosCheat(tar)) || !IsAttackAllowed(tar) ) { invalid_target_state = true; @@ -9382,6 +9381,10 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) bool is_casting_bard_song = false; Mob* tar = (GetOwner()->GetTarget() ? GetOwner()->GetTarget() : this); + if (!DoLosChecks(tar)) { + return; + } + if (IsCasting()) { InterruptSpell(); } @@ -10252,7 +10255,7 @@ bool Bot::IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id) { return false; } - if (!DoLosChecks(this, npc)) { + if (!DoLosChecks(npc)) { return false; } @@ -11084,7 +11087,7 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { tar = this; } - if (!DoLosChecks(this, tar)) { + if (!DoLosChecks(tar)) { return false; } @@ -11219,7 +11222,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { return false; } - if (!DoLosChecks(this, tar)) { + if (!DoLosChecks(tar)) { return false; } @@ -11712,7 +11715,7 @@ bool Bot::HasRequiredLoSForPositioning(Mob* tar) { return true; } - if (RequiresLoSForPositioning() && !DoLosChecks(this, tar)) { + if (RequiresLoSForPositioning() && !DoLosChecks(tar)) { return false; } diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 2c05b748f9..7575e9c007 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -803,7 +803,7 @@ void helper_send_usage_required_bots(Client *bot_owner, uint16 spell_type) auto& spell_map = bot->GetCommandedSpellTypesMinLevels(); if (spell_map.empty()) { - bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type"); //deleteme + bot_owner->Message(Chat::Yellow, "No bots are capable of casting this spell type"); return; } diff --git a/zone/bot_commands/attack.cpp b/zone/bot_commands/attack.cpp index 6b8a929cbe..1cb67bf458 100644 --- a/zone/bot_commands/attack.cpp +++ b/zone/bot_commands/attack.cpp @@ -18,7 +18,12 @@ void bot_command_attack(Client *c, const Seperator *sep) if (!target_mob) { - c->Message(Chat::White, "You must an enemy to use this command"); + c->Message(Chat::Yellow, "You must an enemy to use this command"); + return; + } + + if (!c->DoLosChecks(target_mob)) { + c->Message(Chat::Red, "You must have Line of Sight to use this command."); return; } @@ -71,7 +76,7 @@ void bot_command_attack(Client *c, const Seperator *sep) ); } else { c->Message( - Chat::White, + Chat::PetResponse, fmt::format( "{} of your bots are attacking {}.", sbl.size(), diff --git a/zone/bot_commands/click_item.cpp b/zone/bot_commands/click_item.cpp index a101dfbd26..d0ba47b697 100644 --- a/zone/bot_commands/click_item.cpp +++ b/zone/bot_commands/click_item.cpp @@ -47,11 +47,22 @@ void bot_command_click_item(Client* c, const Seperator* sep) sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + Mob* tar = c->GetTarget(); + for (auto my_bot : sbl) { if (my_bot->BotPassiveCheck()) { continue; } + if ( + tar && + tar != c && + tar->GetOwner() != c && + !c->DoLosChecks(tar) + ) { + continue; + } + if (RuleI(Bots, BotsClickItemsMinLvl) > my_bot->GetLevel()) { c->Message(Chat::White, "%s must be level %i to use clickable items.", my_bot->GetCleanName(), RuleI(Bots, BotsClickItemsMinLvl)); continue; diff --git a/zone/bot_commands/precombat.cpp b/zone/bot_commands/precombat.cpp index 86ac7569a4..073dccf722 100644 --- a/zone/bot_commands/precombat.cpp +++ b/zone/bot_commands/precombat.cpp @@ -5,15 +5,22 @@ void bot_command_precombat(Client* c, const Seperator* sep) if (helper_command_alias_fail(c, "bot_command_precombat", sep->arg[0], "precombat")) { return; } - if (helper_is_help_or_usage(sep->arg[1])) { + if (helper_is_help_or_usage(sep->arg[1])) { c->Message(Chat::White, "usage: %s ([set | clear])", sep->arg[0]); + return; } if (!c->GetTarget() || !c->IsAttackAllowed(c->GetTarget())) { - c->Message(Chat::White, "This command requires an attackable target."); + + return; + } + + if (!c->DoLosChecks(c->GetTarget())) { + c->Message(Chat::Red, "You must have Line of Sight to use this command."); + return; } diff --git a/zone/bot_commands/pull.cpp b/zone/bot_commands/pull.cpp index e082e05f01..22c27ee251 100644 --- a/zone/bot_commands/pull.cpp +++ b/zone/bot_commands/pull.cpp @@ -48,6 +48,12 @@ void bot_command_pull(Client *c, const Seperator *sep) return; } + if (!c->DoLosChecks(target_mob)) { + c->Message(Chat::Red, "You must have Line of Sight to use this command."); + + return; + } + if (target_mob->IsNPC() && target_mob->GetHateList().size()) { c->Message(Chat::White, "Your current target is already engaged!"); @@ -55,8 +61,8 @@ void bot_command_pull(Client *c, const Seperator *sep) } Bot* bot_puller = nullptr; - for (auto bot_iter : sbl) { + for (auto bot_iter : sbl) { if (bot_iter->GetAppearance() == eaDead || bot_iter->GetBotStance() == Stance::Passive) { continue; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 7cffb07c9b..17f1d8d81e 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -65,7 +65,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ bot_spell.ManaCost = 0; if (BotSpellTypeRequiresLoS(spell_type) && tar != this) { - SetHasLoS(DoLosChecks(this, tar)); + SetHasLoS(DoLosChecks(tar)); } else { SetHasLoS(true); diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 27a674b85b..22afc4e669 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11091,10 +11091,17 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) case PET_ATTACK: { if (!target) break; + + if (!DoLosChecks(target)) { + mypet->SayString(this, NOT_LEGAL_TARGET); + break; + } + if (target->IsMezzed()) { MessageString(Chat::NPCQuestSay, CANNOT_WAKE, mypet->GetCleanName(), target->GetCleanName()); break; } + if (mypet->IsFeared()) break; //prevent pet from attacking stuff while feared @@ -11149,8 +11156,15 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (mypet->IsFeared()) break; //prevent pet from attacking stuff while feared - if (!GetTarget()) + if (!GetTarget()) { break; + } + + if (!DoLosChecks(GetTarget())) { + mypet->SayString(this, NOT_LEGAL_TARGET); + break; + } + if (GetTarget()->IsMezzed()) { MessageString(Chat::NPCQuestSay, CANNOT_WAKE, mypet->GetCleanName(), GetTarget()->GetCleanName()); break; diff --git a/zone/mob.cpp b/zone/mob.cpp index d0374f07a9..2d30132306 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8679,33 +8679,54 @@ bool Mob::IsInGroupOrRaid(Mob* other, bool same_raid_group) { return group && group == other_group; } -bool Mob::DoLosChecks(Mob* who, Mob* other) { - if (!who->CheckLosFN(other) || !who->CheckWaterLoS(other)) { - if (who->CheckLosCheatExempt(who, other)) { - return true; - } - - if (CheckLosCheat(who, other)) { +bool Mob::DoLosChecks(Mob* other) { + if (!CheckLosFN(other) || !CheckWaterLoS(other)) { + if (CheckLosCheatExempt(other)) { return true; } + return false; + } + if (!CheckLosCheat(other)) { return false; } return true; } -bool Mob::CheckLosCheat(Mob* who, Mob* other) { +bool Mob::CheckLosCheat(Mob* other) { if (RuleB(Map, CheckForLoSCheat)) { for (auto itr : entity_list.GetDoorsList()) { Doors* d = itr.second; - if (d && !d->IsDoorOpen() && (d->GetTriggerType() == 255 || d->GetLockpick() != 0 || d->GetKeyItem() != 0 || d->GetNoKeyring() != 0)) { - if (DistanceNoZ(who->GetPosition(), d->GetPosition()) <= 50) { - auto who_to_door = DistanceNoZ(who->GetPosition(), d->GetPosition()); + + if ( + !d->IsDoorOpen() && + ( + d->GetKeyItem() || + d->GetLockpick() || + d->IsDoorOpen() || + d->IsDoorBlacklisted() || + d->GetNoKeyring() != 0 || + d->GetDoorParam() > 0 + ) + ) { + // If the door is a trigger door, check if the trigger door is open + if (d->GetTriggerDoorID() > 0) { + auto td = entity_list.GetDoorsByDoorID(d->GetTriggerDoorID()); + + if (td) { + if (Strings::RemoveNumbers(d->GetDoorName()) != Strings::RemoveNumbers(td->GetDoorName())) { + continue; + } + } + } + + if (DistanceNoZ(GetPosition(), d->GetPosition()) <= 50) { + auto who_to_door = DistanceNoZ(GetPosition(), d->GetPosition()); auto other_to_door = DistanceNoZ(other->GetPosition(), d->GetPosition()); - auto who_to_other = DistanceNoZ(who->GetPosition(), other->GetPosition()); + auto who_to_other = DistanceNoZ(GetPosition(), other->GetPosition()); auto distance_difference = who_to_other - (who_to_door + other_to_door); - + if (distance_difference >= (-1 * RuleR(Maps, RangeCheckForLoSCheat)) && distance_difference <= RuleR(Maps, RangeCheckForLoSCheat)) { return false; } @@ -8717,19 +8738,23 @@ bool Mob::CheckLosCheat(Mob* who, Mob* other) { return true; } -bool Mob::CheckLosCheatExempt(Mob* who, Mob* other) { +bool Mob::CheckLosCheatExempt(Mob* other) { if (RuleB(Map, EnableLoSCheatExemptions)) { + /* This is an exmaple of how to configure exemptions for LoS checks. glm::vec4 exempt_check_who; glm::vec4 exempt_check_other; - /* This is an exmaple of how to configure exemptions for LoS checks. - if (zone->GetZoneID() == 222) { //PoEarthB - exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns - //check to be sure the player and the target are in the pit to PoEarthB - //if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) - //otherwise they can pass LoS checks even if they don't have true LoS - if (who->GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(who->GetPosition(), exempt_check_who) <= 800) { - return true; - } + + switch (zone->GetZoneID()) { + case POEARTHB: + exempt_check_who.x = 2051; exempt_check_who.y = 407; exempt_check_who.z = -219; //Middle of councilman spawns + //exempt_check_other.x = 1455; exempt_check_other.y = 415; exempt_check_other.z = -242; + //check to be sure the player and the target are outside of the councilman area + //if the player is inside the cove they cannot be higher than the ceiling (no exploiting from uptop) + if (GetZ() <= -171 && other->GetZ() <= -171 && DistanceNoZ(other->GetPosition(), exempt_check_who) <= 800 && DistanceNoZ(GetPosition(), exempt_check_who) <= 800) { + return true; + } + default: + return false; } */ } diff --git a/zone/mob.h b/zone/mob.h index 3fb130142c..312311a1d8 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -872,9 +872,9 @@ class Mob : public Entity { static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget); virtual bool CheckWaterLoS(Mob* m); bool CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ); - bool CheckLosCheat(Mob* who, Mob* other); - bool CheckLosCheatExempt(Mob* who, Mob* other); - bool DoLosChecks(Mob* who, Mob* other); + bool CheckLosCheat(Mob* other); + bool CheckLosCheatExempt(Mob* other); + bool DoLosChecks(Mob* other); bool TargetValidation(Mob* other); inline void SetLastLosState(bool value) { last_los_check = value; } inline bool CheckLastLosState() const { return last_los_check; } From 72fb75a6a28f95a781020159eab95dfdcb4824cf Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 19:43:47 -0600 Subject: [PATCH 323/394] Remove Log TestDebug --- common/eqemu_logsys.h | 4 +--- common/eqemu_logsys_log_aliases.h | 10 ---------- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/common/eqemu_logsys.h b/common/eqemu_logsys.h index 9178aaa3a7..b03050a7be 100644 --- a/common/eqemu_logsys.h +++ b/common/eqemu_logsys.h @@ -148,7 +148,6 @@ namespace Logs { BotSettings, BotSpellChecks, BotSpellTypeChecks, - TestDebug, MaxCategoryID /* Don't Remove this */ }; @@ -255,8 +254,7 @@ namespace Logs { "KSM", // Kernel Samepage Merging "Bot Settings", "Bot Spell Checks", - "Bot Spell Type Checks", - "Test Debug" + "Bot Spell Type Checks" }; } diff --git a/common/eqemu_logsys_log_aliases.h b/common/eqemu_logsys_log_aliases.h index 2b3e1c83a9..331d22a6c7 100644 --- a/common/eqemu_logsys_log_aliases.h +++ b/common/eqemu_logsys_log_aliases.h @@ -904,16 +904,6 @@ OutF(LogSys, Logs::Detail, Logs::BotSpellTypeChecks, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ } while (0) -#define LogTestDebug(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::General, Logs::TestDebug))\ - OutF(LogSys, Logs::General, Logs::TestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - -#define LogTestDebugDetail(message, ...) do {\ - if (LogSys.IsLogEnabled(Logs::Detail, Logs::TestDebug))\ - OutF(LogSys, Logs::Detail, Logs::TestDebug, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ -} while (0) - #define Log(debug_level, log_category, message, ...) do {\ if (LogSys.IsLogEnabled(debug_level, log_category))\ LogSys.Out(debug_level, log_category, __FILE__, __func__, __LINE__, message, ##__VA_ARGS__);\ From 41b8cb3e71bc7fa389b2e442a8c64af6a495070a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 20:05:36 -0600 Subject: [PATCH 324/394] Remove _Struct from struct declarations --- zone/bot.cpp | 16 ++++++++-------- zone/bot.h | 16 ++++++++-------- zone/bot_commands/blocked_buffs.cpp | 4 ++-- zone/bot_database.cpp | 12 ++++++------ zone/bot_structs.h | 6 +++--- zone/client_bot.cpp | 2 +- zone/mob.h | 4 ++-- 7 files changed, 30 insertions(+), 30 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 940e0ce27b..2e4b3c3683 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -8807,7 +8807,7 @@ void Bot::SetSpellRecastTimer(uint16 spell_id, int32 recast_delay) { } if (CheckSpellRecastTimer(spell_id)) { - BotTimer_Struct t; + BotTimer t; t.timer_id = spells[spell_id].timer_id; t.timer_value = (Timer::GetCurrentTime() + recast_delay); @@ -8913,7 +8913,7 @@ void Bot::SetDisciplineReuseTimer(uint16 spell_id, int32 reuse_timer) } if (CheckDisciplineReuseTimer(spell_id)) { - BotTimer_Struct t; + BotTimer t; t.timer_id = spells[spell_id].timer_id; t.timer_value = (Timer::GetCurrentTime() + reuse_timer); @@ -9010,7 +9010,7 @@ void Bot::SetItemReuseTimer(uint32 item_id, uint32 reuse_timer) } if (CheckItemReuseTimer(item_id)) { - BotTimer_Struct t; + BotTimer t; t.timer_id = (item->RecastType == NegativeItemReuse ? item->ID : item->RecastType); t.timer_value = ( @@ -10476,7 +10476,7 @@ void Bot::LoadDefaultBotSettings() { } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - BotSpellSettings_Struct t; + BotSpellSettings t; t.spellType = i; t.shortName = GetSpellTypeShortNameByID(i); @@ -12035,7 +12035,7 @@ void Bot::CopyBotBlockedBuffs(Bot* to) { to->ClearBotBlockedBuffs(); - std::vector blocked_buffs = GetBotBlockedBuffs(); + std::vector blocked_buffs = GetBotBlockedBuffs(); if (!blocked_buffs.empty()) { for (auto& blocked_buff : blocked_buffs) { @@ -12051,7 +12051,7 @@ void Bot::CopyBotBlockedPetBuffs(Bot* to) { to->ClearBotBlockedBuffs(); - std::vector blocked_buffs = GetBotBlockedBuffs(); + std::vector blocked_buffs = GetBotBlockedBuffs(); if (!blocked_buffs.empty()) { for (auto& blocked_buff : blocked_buffs) { @@ -12225,7 +12225,7 @@ void Bot::SetBotBlockedBuff(uint16 spell_id, bool block) auto it = std::find_if( bot_blocked_buffs.begin(), bot_blocked_buffs.end(), - [spell_id](const BotBlockedBuffs_Struct& buff) { return buff.spell_id == spell_id; } + [spell_id](const BotBlockedBuffs& buff) { return buff.spell_id == spell_id; } ); if (it != bot_blocked_buffs.end()) { @@ -12272,7 +12272,7 @@ void Bot::SetBotBlockedPetBuff(uint16 spell_id, bool block) auto it = std::find_if( bot_blocked_buffs.begin(), bot_blocked_buffs.end(), - [spell_id](const BotBlockedBuffs_Struct& buff) { return buff.spell_id == spell_id; } + [spell_id](const BotBlockedBuffs& buff) { return buff.spell_id == spell_id; } ); if (it != bot_blocked_buffs.end()) { diff --git a/zone/bot.h b/zone/bot.h index 673bde3ffa..71772bd5d2 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -965,11 +965,11 @@ class Bot : public NPC { // New accessors for BotDatabase access bool DeleteBot(); - std::vector GetBotTimers() { return bot_timers; } - void SetBotTimers(std::vector timers) { bot_timers = timers; } - std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } - void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } - const std::map>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } + std::vector GetBotTimers() { return bot_timers; } + void SetBotTimers(std::vector timers) { bot_timers = timers; } + std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } + void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } + const std::map>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } uint32 GetLastZoneID() const { return _lastZoneId; } int32 GetBaseAC() const { return _baseAC; } int32 GetBaseATK() const { return _baseATK; } @@ -1076,10 +1076,10 @@ class Bot : public NPC { std::vector AIBot_spells_enforced; std::unordered_map> AIBot_spells_by_type; - std::map> commanded_spells_min_level; + std::map> commanded_spells_min_level; - std::vector bot_timers; - std::vector bot_blocked_buffs; + std::vector bot_timers; + std::vector bot_blocked_buffs; private: // Class Members diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index c37bada86a..8aa7673689 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -154,7 +154,7 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) bot_iter->SetBotBlockedBuff(spell_id, false); } else if (list) { - std::vector blocked_buffs = bot_iter->GetBotBlockedBuffs(); + std::vector blocked_buffs = bot_iter->GetBotBlockedBuffs(); bool found = false; if (!blocked_buffs.empty()) { @@ -391,7 +391,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) bot_iter->SetBotBlockedPetBuff(spell_id, false); } else if (list) { - std::vector blocked_buffs = bot_iter->GetBotBlockedBuffs(); + std::vector blocked_buffs = bot_iter->GetBotBlockedBuffs(); bool found = false; if (!blocked_buffs.empty()) { diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 5313a2efb7..6acd29f00c 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -800,9 +800,9 @@ bool BotDatabase::LoadTimers(Bot* b) ) ); - std::vector v; + std::vector v; - BotTimer_Struct t{ }; + BotTimer t{ }; for (const auto& e : l) { if (e.timer_value < (Timer::GetCurrentTime() + e.recast_time)) { @@ -836,7 +836,7 @@ bool BotDatabase::SaveTimers(Bot* b) return false; } - std::vector v = b->GetBotTimers(); + std::vector v = b->GetBotTimers(); if (v.empty()) { return true; @@ -2422,9 +2422,9 @@ bool BotDatabase::LoadBotBlockedBuffs(Bot* b) ) ); - std::vector v; + std::vector v; - BotBlockedBuffs_Struct t{ }; + BotBlockedBuffs t{ }; for (const auto& e : l) { t.spell_id = e.spell_id; @@ -2451,7 +2451,7 @@ bool BotDatabase::SaveBotBlockedBuffs(Bot* b) return false; } - std::vector v = b->GetBotBlockedBuffs(); + std::vector v = b->GetBotBlockedBuffs(); if (v.empty()) { return true; diff --git a/zone/bot_structs.h b/zone/bot_structs.h index a9d336a213..47db1706da 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -99,7 +99,7 @@ struct BotSpells_Struct_wIndex { uint8 bucket_comparison; }; -struct BotTimer_Struct { +struct BotTimer { uint32 timer_id; uint32 timer_value; uint32 recast_time; @@ -115,14 +115,14 @@ struct BotSpellTypeOrder { uint16 priority; }; -struct BotBlockedBuffs_Struct { +struct BotBlockedBuffs { uint32_t bot_id; uint32_t spell_id; uint8_t blocked; uint8_t blocked_pet; }; -struct BotSpellTypesByClass_Struct { +struct BotSpellTypesByClass { uint8_t min_level = 255; std::string description; }; diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 348c5ea41b..679fbef001 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -228,7 +228,7 @@ void Client::LoadDefaultBotSettings() { LogBotSettingsDetail("{} says, 'Setting default {} [{}] to [{}]'", GetCleanName(), CastToBot()->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock)); for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - BotSpellSettings_Struct t; + BotSpellSettings t; t.spellType = i; t.shortName = GetSpellTypeShortNameByID(i); diff --git a/zone/mob.h b/zone/mob.h index 312311a1d8..84ff76c8af 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -93,7 +93,7 @@ struct AppearanceStruct { uint8 texture = UINT8_MAX; }; -struct BotSpellSettings_Struct { +struct BotSpellSettings { uint16 spellType; // type ID of bot category std::string shortName; // type short name of bot category std::string name; // type name of bot category @@ -231,7 +231,7 @@ class Mob : public Entity { // Bot attack flag Timer bot_attack_flag_timer; - std::vector m_bot_spell_settings; + std::vector m_bot_spell_settings; //Somewhat sorted: needs documenting! From f51d13ee7cd031c08c900987e6c51bf0a7491cc9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 21:09:06 -0600 Subject: [PATCH 325/394] Add bot check to IsAttackAllowed for GetUltimateOwner to skip entity list where possible --- zone/aggro.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index a2c439fdb5..3c4bfd0e68 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -745,8 +745,8 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) // can't damage own pet (applies to everthing) Mob *target_owner = target->GetOwner(); Mob *our_owner = GetOwner(); - Mob* target_ultimate_owner = target->GetUltimateOwner(); - Mob* our_ultimate_owner = GetUltimateOwner(); + Mob* target_ultimate_owner = (target->IsBot() ? target->CastToBot()->GetBotOwner() : target->GetUltimateOwner()); + Mob* our_ultimate_owner = (IsBot() ? CastToBot()->GetBotOwner() : GetUltimateOwner()); if (target_owner && target_owner == this) { return false; From 187665f7b6d4dba1c933081dd29d3e4e5a4c5ec0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 21:11:37 -0600 Subject: [PATCH 326/394] Wrap SaveBotSettings in Bots Enabled check --- zone/client.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/zone/client.cpp b/zone/client.cpp index 71110240ac..1ca2617e42 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -780,7 +780,9 @@ bool Client::Save(uint8 iCommitNow) { database.SaveCharacterEXPModifier(this); - database.botdb.SaveBotSettings(this); + if (RuleB(Bots, Enabled)) { + database.botdb.SaveBotSettings(this); + } return true; } From 975af5ee9c3b106d798a7569bd6f44758678937f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 21:11:42 -0600 Subject: [PATCH 327/394] Remove comment --- zone/client_packet.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 22afc4e669..2281e20ad5 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1510,8 +1510,6 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) LogError("Error loading AA points for [{}]", GetName()); } - /* Load Bots */ - LoadDefaultBotSettings(); database.botdb.LoadBotSettings(this); From 3dfa099718e4d204ddef3d3c9370484a05e52063 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 21:56:25 -0600 Subject: [PATCH 328/394] Wrap bot setting loading for clients in bots enabled rule --- zone/client_packet.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index 2281e20ad5..d54f608041 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -1510,9 +1510,10 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app) LogError("Error loading AA points for [{}]", GetName()); } - LoadDefaultBotSettings(); - - database.botdb.LoadBotSettings(this); + if (RuleB(Bots, Enabled)) { + LoadDefaultBotSettings(); + database.botdb.LoadBotSettings(this); + } if (SPDAT_RECORDS > 0) { for (uint32 z = 0; z < EQ::spells::SPELL_GEM_COUNT; z++) { From 323f144984990aecf182939463083e86e44ed54e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:23:55 -0600 Subject: [PATCH 329/394] Cleanup BlockedBuffs logic in SpellOnTarget --- zone/spells.cpp | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 6a8ba036f9..9e63aaadb6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -4062,13 +4062,22 @@ bool Mob::SpellOnTarget( return false; } + bool client_blocked_buffs = + RuleB(Spells, EnableBlockedBuffs) && + ( + spelltar->IsClient() || + (spelltar->IsPet() && spelltar->IsPetOwnerClient()) + ); + + bool bot_blocked_buffs = + RuleB(Bots, AllowBotBlockedBuffs) && + ( + spelltar->IsBot() || + (spelltar->IsPet() && spelltar->IsPetOwnerBot()) + ); + // now check if the spell is allowed to land - if ( - (RuleB(Spells, EnableBlockedBuffs) && spelltar->IsClient()) || - (RuleB(Spells, EnableBlockedBuffs) && spelltar->IsPet() && spelltar->GetOwner() && spelltar->GetOwner()->IsClient()) || - (RuleB(Bots, AllowBotBlockedBuffs) && spelltar->IsBot()) || - (RuleB(Bots, AllowBotBlockedBuffs) && spelltar->IsPet() && spelltar->GetOwner() && spelltar->GetOwner()->IsBot()) - ) { + if (client_blocked_buffs || bot_blocked_buffs) { // We return true here since the caster's client should act like normal if (spelltar->IsBlockedBuff(spell_id)) { LogSpells( From b3a550f19322321487313f34b036db457a862991 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:27:15 -0600 Subject: [PATCH 330/394] Rename BotSpells_Struct/BotSpells_Struct_wIndex --- zone/bot.cpp | 6 +++--- zone/bot.h | 10 +++++----- zone/bot_commands/discipline.cpp | 2 +- zone/bot_structs.h | 4 ++-- zone/botspellsai.cpp | 22 +++++++++++----------- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 2e4b3c3683..f8b85f2a9e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -12329,7 +12329,7 @@ void Bot::CleanBotBlockedBuffs() } } -std::vector Bot::BotGetSpellsByType(uint16 spell_type) { +std::vector Bot::BotGetSpellsByType(uint16 spell_type) { if (AIBot_spells_by_type[spell_type].empty()) { spell_type = GetParentSpellType(spell_type); } @@ -12337,7 +12337,7 @@ std::vector Bot::BotGetSpellsByType(uint16 spell_type) return AIBot_spells_by_type[spell_type]; } -void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type) { +void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type) { AIBot_spells_by_type.clear(); for (size_t i = 0; i < AIBot_spells.size(); ++i) { @@ -12347,7 +12347,7 @@ void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, st continue; } - BotSpells_Struct_wIndex spell_with_index{ + BotSpells_wIndex spell_with_index{ static_cast(i), spell.type, spell.spellid, diff --git a/zone/bot.h b/zone/bot.h index 71772bd5d2..43df08fd71 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -304,7 +304,7 @@ class Bot : public NPC { uint16 BotGetSpells(int spellslot) { return AIBot_spells[spellslot].spellid; } uint32 BotGetSpellType(int spellslot) { return AIBot_spells[spellslot].type; } uint16 BotGetSpellPriority(int spellslot) { return AIBot_spells[spellslot].priority; } - std::vector BotGetSpellsByType(uint16 spell_type); + std::vector BotGetSpellsByType(uint16 spell_type); float GetProcChances(float ProcBonus, uint16 hand) override; int GetHandToHandDamage(void) override; bool TryFinishingBlow(Mob *defender, int64 &damage) override; @@ -540,7 +540,7 @@ class Bot : public NPC { void SetVerifiedRaid(bool status) { _verifiedRaid = status; } uint16 GetTempSpellType() { return _tempSpellType; } void SetTempSpellType(uint16 spell_type) { _tempSpellType = spell_type; } - void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); + void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spell_type); @@ -1072,9 +1072,9 @@ class Bot : public NPC { void SetGroupDoter(bool flag = true) { m_CastingRoles.GroupDoter = flag; } std::deque bot_signal_q; - std::vector AIBot_spells; - std::vector AIBot_spells_enforced; - std::unordered_map> AIBot_spells_by_type; + std::vector AIBot_spells; + std::vector AIBot_spells_enforced; + std::unordered_map> AIBot_spells_by_type; std::map> commanded_spells_min_level; diff --git a/zone/bot_commands/discipline.cpp b/zone/bot_commands/discipline.cpp index 8246b14060..146ed60906 100644 --- a/zone/bot_commands/discipline.cpp +++ b/zone/bot_commands/discipline.cpp @@ -134,7 +134,7 @@ void bot_command_discipline(Client* c, const Seperator* sep) } if (spell_id == UINT16_MAX) { // Aggressive/Defensive type - std::vector bot_spell_list; + std::vector bot_spell_list; if (aggressive) { bot_spell_list = bot_iter->BotGetSpellsByType(BotSpellTypes::DiscAggressive); diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 47db1706da..c40e820b8d 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -64,7 +64,7 @@ struct BotSpellSetting { bool is_enabled = true; }; -struct BotSpells_Struct { +struct BotSpells { uint32 type; // 0 = never, must be one (and only one) of the defined values int16 spellid; // <= 0 = no spell int16 manacost; // -1 = use spdat, -2 = no cast time @@ -81,7 +81,7 @@ struct BotSpells_Struct { uint8 bucket_comparison; }; -struct BotSpells_Struct_wIndex { +struct BotSpells_wIndex { uint32 index; //index of AIBot_spells uint32 type; // 0 = never, must be one (and only one) of the defined values int16 spellid; // <= 0 = no spell diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 17f1d8d81e..25982f7f49 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -907,7 +907,7 @@ std::list Bot::GetBotSpellsForSpellEffect(Bot* caster, uint16 spell_ty } if (caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { @@ -945,7 +945,7 @@ std::list Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* caster, ui } if (caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { @@ -986,7 +986,7 @@ std::list Bot::GetBotSpellsBySpellType(Bot* caster, uint16 spell_type) } if (caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { @@ -1015,7 +1015,7 @@ std::vector Bot::GetPrioritizedBotSpellsBySpellType(Bot* cas std::vector result; if (caster && caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { @@ -1104,7 +1104,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* caster, uint16 spell_type) { result.ManaCost = 0; if (caster && caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { @@ -1212,7 +1212,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot* caster, Mob* tar, uint16 spe result.ManaCost = 0; if (caster && caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpell(bot_spell_list[i].spellid)) { continue; @@ -1932,7 +1932,7 @@ BotSpell Bot::GetDebuffBotSpell(Bot* caster, Mob *tar, uint16 spell_type) { return result; if (caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { @@ -1978,7 +1978,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* caster, Mob *tar, uint16 spell bool needs_disease_resist_debuff = (tar->GetDR() + level_mod) > 100; if (caster->AI_HasSpells()) { - std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); + std::vector bot_spell_list = caster->BotGetSpellsByType(spell_type); for (int i = bot_spell_list.size() - 1; i >= 0; i--) { if (!IsValidSpellAndLoS(bot_spell_list[i].spellid, caster->HasLoS())) { @@ -2437,7 +2437,7 @@ bool Bot::AI_AddBotSpells(uint32 bot_spell_id) { } } - std::sort(AIBot_spells.begin(), AIBot_spells.end(), [](const BotSpells_Struct& a, const BotSpells_Struct& b) { + std::sort(AIBot_spells.begin(), AIBot_spells.end(), [](const BotSpells& a, const BotSpells& b) { return a.priority > b.priority; }); @@ -2604,7 +2604,7 @@ void Bot::AddSpellToBotList( } HasAISpell = true; - BotSpells_Struct t; + BotSpells t; t.priority = in_priority; t.spellid = in_spell_id; @@ -2650,7 +2650,7 @@ void Bot::AddSpellToBotEnforceList( } HasAISpell = true; - BotSpells_Struct t; + BotSpells t; t.priority = iPriority; t.spellid = iSpellID; From b84ccab24ac61984772bcc6b413b3f9511756f6a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:36:29 -0600 Subject: [PATCH 331/394] Rename spawn/create status bypass rules, fix return for spawn limit --- common/ruletypes.h | 8 ++++---- zone/client_bot.cpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 5ca50dc7e0..3039b5d9e7 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -875,10 +875,10 @@ RULE_BOOL(Bots, ShowResistMessagesToOwner, true, "Default True. If enabled, when RULE_BOOL(Bots, BotBuffLevelRestrictions, true, "Buffs will not land on low level bots like live players") RULE_BOOL(Bots, BotsUseLiveBlockedMessage, true, "Setting whether detailed spell block messages should be used for bots as players do on the live servers") RULE_BOOL(Bots, BotSoftDeletes, true, "When bots are deleted, they are only soft deleted") -RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass the anti-spam system") -RULE_INT(Bots, StatusSpawnLimit, 120, "Minimum status to bypass spawn limit. Default 120.") -RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass the anti-spam system") -RULE_INT(Bots, StatusCreateLimit, 120, "Minimum status to bypass spawn limit. Default 120.") +RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass spawn limit. Default 100.") +RULE_INT(Bots, MinStatusBypassSpawnLimit, 120, "Spawn limit with status bypass. Default 120.") +RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass create limit. Default 100.") +RULE_INT(Bots, MinStatusBypassCreateLimit, 120, "Create limit with status bypass. Default 120.") RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.") RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 679fbef001..fb9bb3c63a 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -19,7 +19,7 @@ uint32 Client::GetBotCreationLimit(uint8 class_id) { uint32 bot_creation_limit = RuleI(Bots, CreationLimit); if (Admin() >= RuleI(Bots, MinStatusToBypassCreateLimit)) { - return RuleI(Bots, StatusCreateLimit); + return RuleI(Bots, MinStatusBypassCreateLimit); } const auto bucket_name = fmt::format( @@ -69,7 +69,7 @@ int Client::GetBotSpawnLimit(uint8 class_id) { int bot_spawn_limit = RuleI(Bots, SpawnLimit); if (Admin() >= RuleI(Bots, MinStatusToBypassSpawnLimit)) { - return RuleI(Bots, MinStatusToBypassSpawnLimit); + return RuleI(Bots, MinStatusBypassSpawnLimit); } const auto bucket_name = fmt::format( From 65b2fa35337343ae3ea497e1bb3bd64630bce10c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 26 Jan 2025 22:40:57 -0600 Subject: [PATCH 332/394] Remove unnecessary return in CanBuffStack, cleanup --- zone/spells.cpp | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 9e63aaadb6..bcd89c5bf1 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3840,41 +3840,56 @@ int Mob::CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite) if (!IsValidSpell(curbuf.spellid)) { // if we haven't found a free slot, this is the first one so save it - if(firstfree == -2) + if (firstfree == -2) { firstfree = i; + } + continue; } - if (IsBot() && (GetClass() == Class::Bard) && curbuf.spellid == spellid && curbuf.ticsremaining == 0 && curbuf.casterid == GetID()) { + if ( + IsBot() && + GetClass() == Class::Bard && + curbuf.spellid == spellid && + curbuf.ticsremaining == 0 && + curbuf.casterid == GetID() + ) { LogAI("Bard check for song, spell [{}] has [{}] ticks remaining.", spellid, curbuf.ticsremaining); firstfree = i; - return firstfree; } else { - if (curbuf.spellid == spellid) + if (curbuf.spellid == spellid) { return(-1); //do not recast a buff we already have on, we recast fast enough that we dont need to refresh our buffs + } } + // there's a buff in this slot ret = CheckStackConflict(curbuf.spellid, curbuf.casterlevel, spellid, caster_level, nullptr, nullptr, i); - if(ret == 1) { + + if (ret == 1) { // should overwrite current slot - if(iFailIfOverwrite) { + if (iFailIfOverwrite) { LogAIDetail("Buff [{}] would overwrite [{}] in slot [{}], reporting stack failure", spellid, curbuf.spellid, i); return(-1); } - if(firstfree == -2) + + if (firstfree == -2) { firstfree = i; + } } - if(ret == -1) { + if(ret == -1) { LogAIDetail("Buff [{}] would conflict with [{}] in slot [{}], reporting stack failure", spellid, curbuf.spellid, i); return -1; // stop the spell, can't stack it } + if (ret == 2) { //ResurrectionEffectBlock handling to move potential overwrites to a new buff slock while keeping Res Sickness LogAIDetail("Adding buff [{}] will overwrite spell [{}] in slot [{}] with caster level [{}], but ResurrectionEffectBlock is set to 2. Attempting to move [{}] to an empty buff slot.", spellid, curbuf.spellid, i, curbuf.casterlevel, spellid); + for (int x = 0; x < buff_count; x++) { const Buffs_Struct& curbuf = buffs[x]; + if (IsValidSpell(curbuf.spellid)) { continue; } From 2c78803137f472527a5e45b494a26460ad4c9cb6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 27 Jan 2025 00:46:53 -0600 Subject: [PATCH 333/394] Enable recastdelay support for clients --- zone/client_bot.cpp | 1 + zone/mob.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index fb9bb3c63a..7bfc5fd140 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -237,6 +237,7 @@ void Client::LoadDefaultBotSettings() { t.delay = GetDefaultSpellDelay(i); t.minThreshold = GetDefaultSpellMinThreshold(i); t.maxThreshold = GetDefaultSpellMaxThreshold(i); + t.recastTimer.Start(); m_bot_spell_settings.push_back(t); diff --git a/zone/mob.h b/zone/mob.h index 84ff76c8af..b0444d4a88 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -433,7 +433,7 @@ class Mob : public Entity { bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); virtual bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); - inline bool SpellTypeRecastCheck(uint16 spellType) { return (IsClient () ? true : m_bot_spell_settings [spellType].recastTimer.GetRemainingTime () > 0 ? false : true); } + inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recastTimer.GetRemainingTime(); } uint16 GetSpellTypeIDByShortName(std::string spellType_string); bool IsValidBotSpellCategory(uint8 setting_type); std::string GetBotSpellCategoryName(uint8 setting_type); From 49ad193dcc4a3b00d039e86ebe0bf4ad4d45e9f1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 27 Jan 2025 19:46:06 -0600 Subject: [PATCH 334/394] Remove unused variables --- zone/client.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/zone/client.h b/zone/client.h index 28b2de3768..d7a6a29f4d 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2286,18 +2286,6 @@ class Client : public Mob bool m_bot_pulling; bool m_bot_precombat; - uint8 fast_heal_threshold; - uint8 heal_threshold; - uint8 complete_heal_threshold; - uint8 hot_heal_threshold; - uint32 fast_heal_delay; - uint32 heal_delay; - uint32 complete_heal_delay; - uint32 hot_heal_delay; - uint32 cure_delay; - uint8 cure_min_threshold; - uint8 cure_threshold; - bool illusion_block; uint32 _assistee; bool CanTradeFVNoDropItem(); From ef0ff104cf3b5bf76e76775c0bb80749c51d71d3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 27 Jan 2025 19:46:54 -0600 Subject: [PATCH 335/394] Rename _assistee to bot_assistee --- zone/client.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/client.h b/zone/client.h index d7a6a29f4d..480e44624a 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2260,8 +2260,8 @@ class Client : public Mob bool GetBotPulling() { return m_bot_pulling; } void SetBotPulling(bool flag = true) { m_bot_pulling = flag; } - uint32 GetAssistee() { return _assistee; } - void SetAssistee(uint32 id = 0) { _assistee = id; } + uint32 GetAssistee() { return bot_assistee; } + void SetAssistee(uint32 id = 0) { bot_assistee = id; } bool GetBotPrecombat() { return m_bot_precombat; } void SetBotPrecombat(bool flag = true) { m_bot_precombat = flag; } @@ -2286,7 +2286,7 @@ class Client : public Mob bool m_bot_pulling; bool m_bot_precombat; - uint32 _assistee; + uint32 bot_assistee; bool CanTradeFVNoDropItem(); void SendMobPositions(); From 249fb0f5dd423b5a401684e9d959c708e56c9f9b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:46:09 -0600 Subject: [PATCH 336/394] hardcode BotCommandHelpWindow colors --- common/ruletypes.h | 23 ----------------- zone/client.cpp | 62 ++++++++++++++++++---------------------------- zone/client.h | 2 +- 3 files changed, 25 insertions(+), 62 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 3039b5d9e7..d7f477f0ca 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -1126,29 +1126,6 @@ RULE_CATEGORY(Command) RULE_BOOL(Command, DyeCommandRequiresDyes, false, "Enable this to require a Prismatic Dye (32557) each time someone uses #dye.") RULE_BOOL(Command, HideMeCommandDisablesTells, true, "Disable this to allow tells to be received when using #hideme.") RULE_INT(Command, MaxHelpLineLength, 53, "Maximum length of a line before splitting it in to new lines for DiaWind. Default 53.") -RULE_STRING(Command, DescriptionColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, DescriptionHeaderColor, "indian_red", "Color for command help windows") -RULE_STRING(Command, AltDescriptionColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, NoteColor, "dark_orange", "Color for command help windows") -RULE_STRING(Command, NoteHeaderColor, "indian_red", "Color for command help windows") -RULE_STRING(Command, AltNoteColor, "dark_orange", "Color for command help windows") -RULE_STRING(Command, ExampleColor, "goldenrod", "Color for command help windows") -RULE_STRING(Command, ExampleHeaderColor, "indian_red", "Color for command help windows") -RULE_STRING(Command, SubExampleColor, "slate_blue", "Color for command help windows") -RULE_STRING(Command, AltExampleColor, "goldenrod", "Color for command help windows") -RULE_STRING(Command, SubAltExampleColor, "goldenrod", "Color for command help windows") -RULE_STRING(Command, OptionColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, OptionHeaderColor, "indian_red", "Color for command help windows") -RULE_STRING(Command, SubOptionColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, AltOptionColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, SubAltOptionColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, ActionableColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, ActionableHeaderColor, "indian_red", "Color for command help windows") -RULE_STRING(Command, AltActionableColor, "light_grey", "Color for command help windows") -RULE_STRING(Command, HeaderColor, "indian_red", "Color for command help windows") -RULE_STRING(Command, SecondaryHeaderColor, "slate_blue", "Color for command help windows") -RULE_STRING(Command, AltHeaderColor, "indian_red", "Color for command help windows") -RULE_STRING(Command, FillerLineColor, "dark_grey", "Color for command help windows") RULE_CATEGORY_END() RULE_CATEGORY(Doors) diff --git a/zone/client.cpp b/zone/client.cpp index 1ca2617e42..40583dd33b 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13224,26 +13224,12 @@ std::string Client::SendBotCommandHelpWindow(const BotCommandHelpParams& params) unsigned string_length = 0; unsigned current_place = 0; uint16 max_length = RuleI(Command, MaxHelpLineLength); // Line length before splitting - const std::string& description_color = RuleS(Command, DescriptionColor); - const std::string& description_header_color = RuleS(Command, DescriptionHeaderColor); - const std::string& alt_description_color = RuleS(Command, AltDescriptionColor); - const std::string& note_color = RuleS(Command, NoteColor); - const std::string& note_header_color = RuleS(Command, NoteHeaderColor); - const std::string& alt_note_color = RuleS(Command, AltNoteColor); - const std::string& example_color = RuleS(Command, ExampleColor); - const std::string& example_header_color = RuleS(Command, ExampleHeaderColor); - const std::string& sub_example_color = RuleS(Command, SubExampleColor); - const std::string& alt_example_color = RuleS(Command, AltExampleColor); - const std::string& sub_alt_example_color = RuleS(Command, SubAltExampleColor); - const std::string& option_color = RuleS(Command, OptionColor); - const std::string& option_header_color = RuleS(Command, OptionHeaderColor); - const std::string& sub_option_color = RuleS(Command, SubOptionColor); - const std::string& alt_option_color = RuleS(Command, AltOptionColor); - const std::string& sub_alt_option_color = RuleS(Command, SubAltOptionColor); - const std::string& actionable_color = RuleS(Command, ActionableColor); - const std::string& actionable_header_color = RuleS(Command, ActionableHeaderColor); - const std::string& alt_actionable_color = RuleS(Command, AltActionableColor); - const std::string& filler_line_color = RuleS(Command, FillerLineColor); + const std::string& header_color = "indian_red"; + const std::string& description_color = "light_grey"; + const std::string& description_color_secondary = "dark_orange"; + const std::string& example_color = "goldenrod"; + const std::string& example_color_secondary = "slate_blue"; + const std::string& filler_line_color = "dark_grey"; std::string filler_line = "--------------------------------------------------------------------"; std::string filler_dia = DialogueWindow::TableRow(DialogueWindow::TableCell(fmt::format("{}", DialogueWindow::ColorMessage(filler_line_color, filler_line)))); @@ -13251,58 +13237,58 @@ std::string Client::SendBotCommandHelpWindow(const BotCommandHelpParams& params) std::string popup_text; if (!params.description.empty()) { - popup_text += GetCommandHelpHeader(description_header_color, "[Description]"); - popup_text += SplitCommandHelpText(params.description, description_color, max_length, !alt_description_color.empty(), alt_description_color); + popup_text += GetCommandHelpHeader(header_color, "[Description]"); + popup_text += SplitCommandHelpText(params.description, description_color, max_length); } if (!params.notes.empty()) { popup_text += break_line + break_line; - popup_text += GetCommandHelpHeader(note_header_color, "[Notes]"); - popup_text += SplitCommandHelpText(params.notes, note_color, max_length, !alt_note_color.empty(), alt_note_color); + popup_text += GetCommandHelpHeader(header_color, "[Notes]"); + popup_text += SplitCommandHelpText(params.notes, description_color_secondary, max_length); } if (!params.example_format.empty()) { popup_text += filler_dia; - popup_text += GetCommandHelpHeader(example_header_color, "[Examples]"); - popup_text += SplitCommandHelpText(params.example_format, example_color, max_length, !alt_example_color.empty(), alt_example_color); + popup_text += GetCommandHelpHeader(header_color, "[Examples]"); + popup_text += SplitCommandHelpText(params.example_format, example_color, max_length); } if (!params.examples_one.empty()) { popup_text += break_line + break_line; - popup_text += SplitCommandHelpText(params.examples_one, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); + popup_text += SplitCommandHelpText(params.examples_one, example_color_secondary, max_length); } if (!params.examples_two.empty()) { - popup_text += SplitCommandHelpText(params.examples_two, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); + popup_text += SplitCommandHelpText(params.examples_two, example_color_secondary, max_length); } if (!params.examples_three.empty()) { - popup_text += SplitCommandHelpText(params.examples_three, sub_example_color, max_length, !sub_alt_example_color.empty(), sub_alt_example_color); + popup_text += SplitCommandHelpText(params.examples_three, example_color_secondary, max_length); } if (!params.options.empty()) { popup_text += filler_dia; - popup_text += GetCommandHelpHeader(option_header_color, "[Options]"); - popup_text += SplitCommandHelpText(params.options, option_color, max_length, !alt_option_color.empty(), alt_option_color); + popup_text += GetCommandHelpHeader(header_color, "[Options]"); + popup_text += SplitCommandHelpText(params.options, description_color, max_length); } if (!params.options_one.empty()) { popup_text += break_line + break_line; - popup_text += SplitCommandHelpText(params.options_one, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); + popup_text += SplitCommandHelpText(params.options_one, description_color, max_length); } if (!params.options_two.empty()) { - popup_text += SplitCommandHelpText(params.options_two, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); + popup_text += SplitCommandHelpText(params.options_two, description_color, max_length); } if (!params.options_three.empty()) { - popup_text += SplitCommandHelpText(params.options_three, sub_option_color, max_length, !sub_alt_option_color.empty(), sub_alt_option_color); + popup_text += SplitCommandHelpText(params.options_three, description_color, max_length); } if (!params.actionables.empty()) { popup_text += filler_dia; - popup_text += GetCommandHelpHeader(actionable_header_color, "[Actionables]"); - popup_text += SplitCommandHelpText(params.actionables, actionable_color, max_length, !alt_actionable_color.empty(), alt_actionable_color); + popup_text += GetCommandHelpHeader(header_color, "[Actionables]"); + popup_text += SplitCommandHelpText(params.actionables, description_color, max_length); } popup_text = DialogueWindow::Table(popup_text); @@ -13323,7 +13309,7 @@ std::string Client::GetCommandHelpHeader(std::string color, std::string header) return return_text; } -std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 max_length, bool second_color, std::string secondary_color) { +std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 max_length) { std::string return_text; for (int i = 0; i < msg.size(); i++) { @@ -13362,7 +13348,7 @@ std::string Client::SplitCommandHelpText(std::vector msg, std::stri for (const auto& s : msg_split) { return_text += DialogueWindow::TableRow( - DialogueWindow::TableCell(DialogueWindow::ColorMessage(((second_color && i == 0) ? color : secondary_color), s)) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(color, s)) ); } } diff --git a/zone/client.h b/zone/client.h index 480e44624a..643ffe55ff 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1278,7 +1278,7 @@ class Client : public Mob // Help Window std::string SendBotCommandHelpWindow(const BotCommandHelpParams& params); std::string GetCommandHelpHeader(std::string color, std::string header); - std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength, bool secondColor = false, std::string secondaryColor = ""); + std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength); void SendSpellTypePrompts(bool commandedTypes = false, bool clientOnlyTypes = false); // Task System Methods From 69d5d1a883f1948dbf31daa4d6d3fcc4f6b5a9a5 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 27 Jan 2025 22:47:09 -0600 Subject: [PATCH 337/394] todo --- zone/bot.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index f8b85f2a9e..eaf6d19b13 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,6 +31,8 @@ /* TODO bot rewrite: +--fix ^cast summoncorpse +--implement spell type option to ^defaultsettings lile ^copysettings --command cleanup remaining commands (move to new help window, make more descriptive) --Add quest methods for functions */ From b5a200554beb2966686f78a70efca5dbb74fec55 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:33:16 -0600 Subject: [PATCH 338/394] Fix ^cast summoncorpse --- zone/bot.cpp | 19 +++++++++---------- zone/spells.cpp | 18 ++++++++---------- 2 files changed, 17 insertions(+), 20 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index eaf6d19b13..277d06605d 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9644,16 +9644,15 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if ( - spells[spell_id].target_type == ST_Self && - tar != this && - ( - spell_type != BotSpellTypes::SummonCorpse || - RuleB(Bots, AllowCommandedSummonCorpse) - ) - ) { - LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); - return false; + if (spells[spell_id].target_type == ST_Self && tar != this) { + if (spell_type == BotSpellTypes::SummonCorpse && RuleB(Bots, AllowCommandedSummonCorpse)) { + // Don't cancel (Summon Corpse is allowed) + } + else { + LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to ST_Self.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); + + return false; + } } if ( diff --git a/zone/spells.cpp b/zone/spells.cpp index bcd89c5bf1..e5dc8e1dd6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -779,12 +779,7 @@ bool Mob::DoCastingChecksOnTarget(bool check_on_casting, int32 spell_id, Mob *sp if (IsGroupSpell(spell_id)) { return true; } else if (spells[spell_id].target_type == ST_Self) { - if (IsBot() && (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse))) { - //spell_target = this; - } - else { - spell_target = this; - } + spell_target = this; } } else { if (IsGroupSpell(spell_id) && spell_target != this) { @@ -1925,10 +1920,13 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // single target spells case ST_Self: { - if (IsBot() && (IsEffectInSpell(spell_id, SE_SummonCorpse) && RuleB(Bots, AllowCommandedSummonCorpse))) { - //spell_target = this; - } - else { + if ( + !( + IsBot() && + IsEffectInSpell(spell_id, SE_SummonCorpse) && + RuleB(Bots, AllowCommandedSummonCorpse) + ) + ) { // summon corpse spells are self only, bots need a fallthrough to summon corpses spell_target = this; } From 56a9853e6380199d6baadfc63e936d2aaafe70f6 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Mon, 27 Jan 2025 23:42:42 -0600 Subject: [PATCH 339/394] todo --- zone/bot.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 277d06605d..43b51c2785 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,8 +31,7 @@ /* TODO bot rewrite: ---fix ^cast summoncorpse ---implement spell type option to ^defaultsettings lile ^copysettings +--implement spell type option to ^defaultsettings like ^copysettings, cleanup and add all options. --command cleanup remaining commands (move to new help window, make more descriptive) --Add quest methods for functions */ From 989c6a43d6afddbff0adb46b099d4381a651aa93 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 21:30:48 -0600 Subject: [PATCH 340/394] Reimplement secondary colors to BotSendCommandHelpWindow --- zone/client.cpp | 34 ++++++++++++++++++---------------- zone/client.h | 6 +++--- 2 files changed, 21 insertions(+), 19 deletions(-) diff --git a/zone/client.cpp b/zone/client.cpp index 40583dd33b..45e7d39ffd 100644 --- a/zone/client.cpp +++ b/zone/client.cpp @@ -13229,6 +13229,8 @@ std::string Client::SendBotCommandHelpWindow(const BotCommandHelpParams& params) const std::string& description_color_secondary = "dark_orange"; const std::string& example_color = "goldenrod"; const std::string& example_color_secondary = "slate_blue"; + const std::string& option_color = "light_grey"; + const std::string& option_color_secondary = "slate_blue"; const std::string& filler_line_color = "dark_grey"; std::string filler_line = "--------------------------------------------------------------------"; @@ -13237,57 +13239,57 @@ std::string Client::SendBotCommandHelpWindow(const BotCommandHelpParams& params) std::string popup_text; if (!params.description.empty()) { - popup_text += GetCommandHelpHeader(header_color, "[Description]"); + popup_text += GetCommandHelpHeader("[Description]", header_color); popup_text += SplitCommandHelpText(params.description, description_color, max_length); } if (!params.notes.empty()) { popup_text += break_line + break_line; - popup_text += GetCommandHelpHeader(header_color, "[Notes]"); + popup_text += GetCommandHelpHeader("[Notes]", header_color); popup_text += SplitCommandHelpText(params.notes, description_color_secondary, max_length); } if (!params.example_format.empty()) { popup_text += filler_dia; - popup_text += GetCommandHelpHeader(header_color, "[Examples]"); + popup_text += GetCommandHelpHeader("[Examples]", header_color); popup_text += SplitCommandHelpText(params.example_format, example_color, max_length); } if (!params.examples_one.empty()) { popup_text += break_line + break_line; - popup_text += SplitCommandHelpText(params.examples_one, example_color_secondary, max_length); + popup_text += SplitCommandHelpText(params.examples_one, example_color, max_length, example_color_secondary); } if (!params.examples_two.empty()) { - popup_text += SplitCommandHelpText(params.examples_two, example_color_secondary, max_length); + popup_text += SplitCommandHelpText(params.examples_two, example_color, max_length, example_color_secondary); } if (!params.examples_three.empty()) { - popup_text += SplitCommandHelpText(params.examples_three, example_color_secondary, max_length); + popup_text += SplitCommandHelpText(params.examples_three, example_color, max_length, example_color_secondary); } if (!params.options.empty()) { popup_text += filler_dia; - popup_text += GetCommandHelpHeader(header_color, "[Options]"); - popup_text += SplitCommandHelpText(params.options, description_color, max_length); + popup_text += GetCommandHelpHeader("[Options]", header_color); + popup_text += SplitCommandHelpText(params.options, option_color, max_length); } if (!params.options_one.empty()) { popup_text += break_line + break_line; - popup_text += SplitCommandHelpText(params.options_one, description_color, max_length); + popup_text += SplitCommandHelpText(params.options_one, option_color_secondary, max_length); } if (!params.options_two.empty()) { - popup_text += SplitCommandHelpText(params.options_two, description_color, max_length); + popup_text += SplitCommandHelpText(params.options_two, option_color_secondary, max_length); } if (!params.options_three.empty()) { - popup_text += SplitCommandHelpText(params.options_three, description_color, max_length); + popup_text += SplitCommandHelpText(params.options_three, option_color_secondary, max_length); } if (!params.actionables.empty()) { popup_text += filler_dia; - popup_text += GetCommandHelpHeader(header_color, "[Actionables]"); + popup_text += GetCommandHelpHeader("[Actionables]", header_color); popup_text += SplitCommandHelpText(params.actionables, description_color, max_length); } @@ -13296,12 +13298,12 @@ std::string Client::SendBotCommandHelpWindow(const BotCommandHelpParams& params) return popup_text; } -std::string Client::GetCommandHelpHeader(std::string color, std::string header) { +std::string Client::GetCommandHelpHeader(std::string msg, std::string color) { std::string return_text = DialogueWindow::TableRow( DialogueWindow::TableCell( fmt::format( "{}", - DialogueWindow::ColorMessage(color, header) + DialogueWindow::ColorMessage(color, msg) ) ) ); @@ -13309,7 +13311,7 @@ std::string Client::GetCommandHelpHeader(std::string color, std::string header) return return_text; } -std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 max_length) { +std::string Client::SplitCommandHelpText(std::vector msg, std::string color, uint16 max_length, std::string secondary_color) { std::string return_text; for (int i = 0; i < msg.size(); i++) { @@ -13348,7 +13350,7 @@ std::string Client::SplitCommandHelpText(std::vector msg, std::stri for (const auto& s : msg_split) { return_text += DialogueWindow::TableRow( - DialogueWindow::TableCell(DialogueWindow::ColorMessage(color, s)) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(((!secondary_color.empty() && i== 0) ? secondary_color : color), s)) ); } } diff --git a/zone/client.h b/zone/client.h index 643ffe55ff..52e4788b6a 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1277,9 +1277,9 @@ class Client : public Mob // Help Window std::string SendBotCommandHelpWindow(const BotCommandHelpParams& params); - std::string GetCommandHelpHeader(std::string color, std::string header); - std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 maxLength); - void SendSpellTypePrompts(bool commandedTypes = false, bool clientOnlyTypes = false); + std::string GetCommandHelpHeader(std::string msg, std::string color); + std::string SplitCommandHelpText(std::vector msg, std::string color, uint16 max_length, std::string secondary_color = ""); + void SendSpellTypePrompts(bool commanded_types = false, bool client_only_types = false); // Task System Methods void LoadClientTaskState(); From 60d96d6ac63ec2476e02b1c1a358bbc40ac6a270 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 22:08:07 -0600 Subject: [PATCH 341/394] Give ^copysettings/^defaultsettings more options, cleanup. --- zone/bot.cpp | 1 - zone/bot_commands/copy_settings.cpp | 93 ++++++--- zone/bot_commands/default_settings.cpp | 262 ++++++++++++++----------- 3 files changed, 211 insertions(+), 145 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 43b51c2785..4c54b38727 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -31,7 +31,6 @@ /* TODO bot rewrite: ---implement spell type option to ^defaultsettings like ^copysettings, cleanup and add all options. --command cleanup remaining commands (move to new help window, make more descriptive) --Add quest methods for functions */ diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 12d6d0b2d0..53d03486c2 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -39,20 +39,32 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), }; + p.examples_three = + { + "To copy only spellsettings from BotA to BotB:", + fmt::format( + "{} BotA BotB spellsettings", + sep->arg[0] + ), + fmt::format( + "{} BotA BotB spellsettings", + sep->arg[0] + ), + }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "[all], [misc], [spellsettings], [spelltypesettings], [spellholds], [spelldelays], [spellminthresholds], [spellmaxthresholds], [spellminmanapct], [spellmaxmanapct], [spellminhppct], [spellmaxhppct], [spellidlepriority], [spellengagedpriority], [spellpursuepriority], [spellaggrochecks], [spelltargetcounts], [sithppercent], [sitmanapercent], [blockedbuffs], [blockedpetbuffs]" }; - std::vector options_one = + p.options = { "all, misc, spellsettings, spelltypesettings, holds, delays, minthresholds, maxthresholds, minmanapct, maxmanapct, minhppct, maxhppct, idlepriority, engagedpriority, pursuepriority, aggrochecks, targetcounts, blockedbuffs, blockedpetbuffs" }; + p.options_one = { "[spellsettings] will copy ^spellsettings options", "[spelltypesettings] copies all spell type settings", "[all] copies all settings" }; - std::vector options_two = + p.options_two = { "[misc] copies all miscellaneous options such as:", "- ^showhelm, ^followd, ^stopmeleelevel, ^enforcespellsettings, ^bottoggleranged, ^petsettype, ^behindmob, ^distanceranged, ^illusionblock, ^sitincombat, ^sithppercent, ^sitmanapercent, ^blockedbuffs, ^blockedpetbuffs" }; - std::vector options_three = { "The remaining options copy that specific type" }; + p.options_three = { "The remaining options copy that specific type" }; std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); @@ -73,8 +85,8 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) return; } - std::string arg1 = sep->arg[1]; - + int spell_type_arg_int = 4; + std::string spell_type_arg = sep->arg[spell_type_arg_int]; int ab_arg = 2; bool valid_option = false; uint16 spell_type = UINT16_MAX; @@ -83,34 +95,59 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) { "all", "misc", - "spellsettings", - "spelltypesettings", - "spellholds", - "spelldelays", - "spellminthresholds", - "spellmaxthresholds", - "spellminmanapct", - "spellmaxmanapct", - "spellminhppct", - "spellmaxhppct", - "spellidlepriority", + "spellsettings", + "spelltypesettings", + "spellholds", + "spelldelays", + "spellminthresholds", + "spellmaxthresholds", + "spellminmanapct", + "spellmaxmanapct", + "spellminhppct", + "spellmaxhppct", + "spellidlepriority", "spellengagedpriority", - "spellpursuepriority", - "spellaggrochecks", - "spelltargetcounts", - "blockedbuffs", + "spellpursuepriority", + "spellaggrochecks", + "spelltargetcounts", + "blockedbuffs", "blockedpetbuffs" }; - if (sep->IsNumber(4)) { - spell_type = atoi(sep->arg[4]); - } - else { - spell_type = c->GetSpellTypeIDByShortName(sep->arg[4]); + if (sep->IsNumber(spell_type_arg_int)) { + spell_type = atoi(sep->arg[spell_type_arg_int]); + + if (!c->IsValidSpellType(spell_type)) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } } + else if (!spell_type_arg.empty()) { + if (c->GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) { + spell_type = c->GetSpellTypeIDByShortName(spell_type_arg); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { - spell_type = UINT16_MAX; + return; + } } for (int i = 0; i < options.size(); i++) { diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 7186335e2a..a0dd3f01cf 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -38,7 +38,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) ) }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "all, misc, spellsettings, spelltypesettings, holds, delays, minthresholds, maxthresholds minmanapct, maxmanapct, minhppct, maxhppct, idlepriority, engagedpriority, pursuepriority, aggrocheck, targetcounts" }; + p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts" }; p.options_one = { "[spellsettings] will restore ^spellsettings options", @@ -48,12 +48,13 @@ void bot_command_default_settings(Client* c, const Seperator* sep) p.options_two = { "[misc] restores all miscellaneous options such as:", - "- ^showhelm, ^followd, ^stopmeleelevel", - "- ^enforcespellsettings, ^bottoggleranged, ^petsettype", - "- ^behindmob, ^distanceranged, ^illusionblock", - "- ^sitincombat, ^sithppercent and ^sitmanapercent", + "- ^showhelm, ^followd, ^stopmeleelevel, ^enforcespellsettings, ^bottoggleranged, ^petsettype, ^behindmob, ^distanceranged, ^illusionblock, ^sitincombat, ^sithppercent and ^sitmanapercent", + }; + p.options_three = + { + "
", + "**The remaining options restore that specific type**" }; - p.options_three = { "The remaining options restore that specific type" }; std::string popup_text = c->SendBotCommandHelpWindow(p); popup_text = DialogueWindow::Table(popup_text); @@ -74,62 +75,77 @@ void bot_command_default_settings(Client* c, const Seperator* sep) return; } - std::string arg1 = sep->arg[1]; - + int spell_type_arg_int = 2; + std::string spell_type_arg = sep->arg[spell_type_arg_int]; int ab_arg = 2; bool valid_option = false; uint16 spell_type = UINT16_MAX; + uint16 setting_type = UINT16_MAX; std::vector options = { "all", - "misc" + "misc", "spellsettings", "spelltypesettings", - "holds", - "delays", - "minthresholds", - "maxthresholds", - "aggrocheck", - "minmanapct", - "maxmanapct", - "minhppct", - "maxhppct", - "idlepriority", - "engagedpriority", - "pursuepriority", - "targetcounts" + "spellholds", + "spelldelays", + "spellminthresholds", + "spellmaxthresholds", + "spellminmanapct", + "spellmaxmanapct", + "spellminhppct", + "spellmaxhppct", + "spellidlepriority", + "spellengagedpriority", + "spellpursuepriority", + "spellaggrochecks", + "spelltargetcounts" }; - for (int i = 0; i < options.size(); i++) { - if (sep->arg[1] == options[i]) { - if (options[i] != "all" && options[i] != "misc" && options[i] != "spellsettings") { - - if (sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { - if (sep->IsNumber(2)) { - spell_type = atoi(sep->arg[2]); - } + if (sep->IsNumber(spell_type_arg_int)) { + spell_type = atoi(sep->arg[spell_type_arg_int]); - if (c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(sep->arg[2]); - } + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + return; + } - return; - } + ++ab_arg; + } + else if (!spell_type_arg.empty()) { + if (c->GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) { + spell_type = c->GetSpellTypeIDByShortName(spell_type_arg); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); - ++ab_arg; - } - } - else if ( - (options[i] == "all" || options[i] == "misc" || options[i] == "spellsettings") && - ((sep->IsNumber(2) || c->GetSpellTypeIDByShortName(sep->arg[2]) != UINT16_MAX)) - ) { - break; - } + return; + } + ++ab_arg; + } + for (int i = 0; i < options.size(); i++) { + if (sep->arg[1] == options[i]) { + setting_type = c->GetBotSpellCategoryIDByShortName(sep->arg[1]); valid_option = true; + break; } } @@ -151,11 +167,13 @@ void bot_command_default_settings(Client* c, const Seperator* sep) const int ab_mask = ActionableBots::ABM_Type1; std::string class_race_arg = sep->arg[ab_arg]; bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { class_race_check = true; } std::vector sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { return; } @@ -174,12 +192,92 @@ void bot_command_default_settings(Client* c, const Seperator* sep) bot_stance = my_bot->GetBotStance(); - if (!strcasecmp(sep->arg[1], "misc")) { + if (setting_type != UINT16_MAX) { + if (spell_type != UINT16_MAX) { + my_bot->SetBotSetting(setting_type, spell_type, my_bot->GetDefaultSetting(setting_type, spell_type, bot_stance)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetBotSetting(setting_type, i, my_bot->GetDefaultSetting(setting_type, i, bot_stance)); + } + } + + output = (spell_type != UINT16_MAX ? c->GetSpellTypeNameByID(spell_type) : ""); + output += sep->arg[3]; + } + else if (!strcasecmp(sep->arg[1], "misc")) { for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance)); output = "miscellanous settings"; } } + else if (!strcasecmp(sep->arg[1], "spellsettings")) { + my_bot->ResetBotSpellSettings(); + output = "^spellsettings"; + } + else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { + if (spell_type != UINT16_MAX) { + my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(spell_type, bot_stance)); + my_bot->SetSpellDelay(spell_type, my_bot->GetDefaultSpellDelay(spell_type, bot_stance)); + my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); + my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); + my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance)); + my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); + my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance)); + my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance)); + my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance)); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + } + } + + output = "spell type settings"; + } + else if (!strcasecmp(sep->arg[1], "all")) { + for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { + my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance)); + } + + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); + my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); + my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); + my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); + my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); + my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); + my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + }; + + my_bot->ResetBotSpellSettings(); + my_bot->ClearBotBlockedBuffs(); + + output = "settings"; + + } else if (!strcasecmp(sep->arg[1], "holds")) { if (spell_type != UINT16_MAX) { my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(spell_type, bot_stance)); @@ -336,76 +434,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep) output = "target count settings"; } - else if (!strcasecmp(sep->arg[1], "spellsettings")) { - my_bot->ResetBotSpellSettings(); - output = "^spellsettings"; - } - else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { - if (spell_type != UINT16_MAX) { - my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(spell_type, bot_stance)); - my_bot->SetSpellDelay(spell_type, my_bot->GetDefaultSpellDelay(spell_type, bot_stance)); - my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); - my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); - my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance)); - my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); - my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance)); - my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance)); - my_bot->SetSpellTypeMaxHPLimit(spell_type, my_bot->GetDefaultSpellTypeMaxHPLimit(spell_type, bot_stance)); - my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); - } - else { - for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); - } - } - - output = "spell type settings"; - } - else if (!strcasecmp(sep->arg[1], "all")) { - for (uint16 i = BotBaseSettings::START; i <= BotBaseSettings::END; ++i) { - my_bot->SetBotBaseSetting(i, my_bot->GetDefaultBotBaseSetting(i, bot_stance)); - } - - for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); - my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); - my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); - my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); - my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); - my_bot->SetSpellTypeMaxHPLimit(i, my_bot->GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); - my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); - }; - - my_bot->ResetBotSpellSettings(); - my_bot->ClearBotBlockedBuffs(); - - my_bot->Save(); - - output = "settings"; - - } + my_bot->Save(); ++success_count; } From 548e3ca81bf550d178a0efcff410d7bc578b26ac Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 22:08:20 -0600 Subject: [PATCH 342/394] Cleanup some commands --- zone/bot_commands/spell_aggro_checks.cpp | 12 ++++++++++-- zone/bot_commands/spell_delays.cpp | 12 ++++++++---- zone/bot_commands/spell_engaged_priority.cpp | 2 +- zone/bot_commands/spell_holds.cpp | 2 +- zone/bot_commands/spell_idle_priority.cpp | 2 +- zone/bot_commands/spell_max_hp_pct.cpp | 2 +- zone/bot_commands/spell_max_mana_pct.cpp | 2 +- zone/bot_commands/spell_max_thresholds.cpp | 12 ++++++++---- zone/bot_commands/spell_min_hp_pct.cpp | 2 +- zone/bot_commands/spell_min_mana_pct.cpp | 2 +- zone/bot_commands/spell_min_thresholds.cpp | 12 ++++++++---- zone/bot_commands/spell_pursue_priority.cpp | 2 +- zone/bot_commands/spell_target_count.cpp | 2 +- 13 files changed, 43 insertions(+), 23 deletions(-) diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index 2c5b92aafe..1c5a34e02c 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -94,8 +94,16 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { - c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message( + Chat::Yellow, + fmt::format( + "You must choose a valid spell type. Use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); return; } diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 1decded543..a284316604 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -106,13 +106,17 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { if ( (clientSetting && !IsClientBotSpellType(spell_type)) || - (!clientSetting && (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END)) + (!clientSetting && !EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) ) { c->Message( Chat::Yellow, - clientSetting ? "Invalid spell type for clients." : "You must choose a valid spell type. Spell types range from %i to %i", - BotSpellTypes::START, - BotSpellTypes::END + fmt::format( + "{}. Use {} for information regarding this command.", + (!clientSetting ? "You must choose a valid spell type" : "You must choose a valid client spell type"), + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() ); if (clientSetting) { diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index 3621c6c6bf..d013661788 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -99,7 +99,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index b7ad108ae3..5b5815fb55 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -78,7 +78,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 830cd8f3b0..4ae8276390 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -99,7 +99,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index c1a9d43b3d..5bc42a7adc 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -91,7 +91,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 38c78a7e4c..0e526ab8ea 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -91,7 +91,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 328345a2dd..ec4faca4e5 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -102,13 +102,17 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { if ( (clientSetting && !IsClientBotSpellType(spell_type)) || - (!clientSetting && (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END)) + (!clientSetting && !EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) ) { c->Message( Chat::Yellow, - clientSetting ? "Invalid spell type for clients." : "You must choose a valid spell type. Spell types range from %i to %i", - BotSpellTypes::START, - BotSpellTypes::END + fmt::format( + "{}. Use {} for information regarding this command.", + (!clientSetting ? "You must choose a valid spell type" : "You must choose a valid client spell type"), + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() ); if (clientSetting) { diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 64a1531fc6..11da31ac89 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -91,7 +91,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index f99539c583..4fa3405dfc 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -91,7 +91,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index f39194e2ad..6fcd43beda 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -104,13 +104,17 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { if ( (clientSetting && !IsClientBotSpellType(spell_type)) || - (!clientSetting && (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END)) + (!clientSetting && !EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) ) { c->Message( Chat::Yellow, - clientSetting ? "Invalid spell type for clients." : "You must choose a valid spell type. Spell types range from %i to %i", - BotSpellTypes::START, - BotSpellTypes::END + fmt::format( + "{}. Use {} for information regarding this command.", + (!clientSetting ? "You must choose a valid spell type" : "You must choose a valid client spell type"), + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() ); if (clientSetting) { diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index eeba9792cc..feb08fbe22 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -99,7 +99,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index c141678466..322de0d2d6 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -91,7 +91,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (spell_type < BotSpellTypes::START || spell_type > BotSpellTypes::END) { + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); return; From 626b7fd02875a8a98e67cba272b2156942b339d0 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 22:10:05 -0600 Subject: [PATCH 343/394] Add comment to CheckLosCheat/CheckLosCheatExempt --- zone/mob.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index b0444d4a88..83ba2b024b 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -872,8 +872,8 @@ class Mob : public Entity { static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget); virtual bool CheckWaterLoS(Mob* m); bool CheckPositioningLosFN(Mob* other, float posX, float posY, float posZ); - bool CheckLosCheat(Mob* other); - bool CheckLosCheatExempt(Mob* other); + bool CheckLosCheat(Mob* other); //door skipping checks for LoS + bool CheckLosCheatExempt(Mob* other); //exemptions to bypass los bool DoLosChecks(Mob* other); bool TargetValidation(Mob* other); inline void SetLastLosState(bool value) { last_los_check = value; } From db000640280ae04bd26856a9260f6c91c72ea930 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:04:13 -0600 Subject: [PATCH 344/394] Make struct BotSpellSettings snake case --- zone/bot.cpp | 81 +++++++++++++++++++++++++++------------------ zone/bot.h | 14 ++++---- zone/client_bot.cpp | 18 +++++----- zone/mob.h | 44 ++++++++++++------------ zone/mob_bot.cpp | 10 +++--- 5 files changed, 91 insertions(+), 76 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 4c54b38727..f2c25f8b94 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10477,31 +10477,46 @@ void Bot::LoadDefaultBotSettings() { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { BotSpellSettings t; - t.spellType = i; - t.shortName = GetSpellTypeShortNameByID(i); - t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i, bot_stance); - t.delay = GetDefaultSpellDelay(i, bot_stance); - t.minThreshold = GetDefaultSpellMinThreshold(i, bot_stance); - t.maxThreshold = GetDefaultSpellMaxThreshold(i, bot_stance); - t.resistLimit = GetDefaultSpellTypeResistLimit(i, bot_stance); - t.aggroCheck = GetDefaultSpellTypeAggroCheck(i, bot_stance); - t.minManaPct = GetDefaultSpellTypeMinManaLimit(i, bot_stance); - t.maxManaPct = GetDefaultSpellTypeMaxManaLimit(i, bot_stance); - t.minHPPct = GetDefaultSpellTypeMinHPLimit(i, bot_stance); - t.maxHPPct = GetDefaultSpellTypeMaxHPLimit(i, bot_stance); - t.idlePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Idle, GetClass(), bot_stance); - t.engagedPriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, GetClass(), bot_stance); - t.pursuePriority = GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, GetClass(), bot_stance); - t.AEOrGroupTargetCount = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); - t.recastTimer.Start(); + t.spell_type = i; + t.short_name = GetSpellTypeShortNameByID(i); + t.name = GetSpellTypeNameByID(i); + t.hold = GetDefaultSpellHold(i, bot_stance); + t.delay = GetDefaultSpellDelay(i, bot_stance); + t.min_threshold = GetDefaultSpellMinThreshold(i, bot_stance); + t.max_threshold = GetDefaultSpellMaxThreshold(i, bot_stance); + t.resist_limit = GetDefaultSpellTypeResistLimit(i, bot_stance); + t.aggro_check = GetDefaultSpellTypeAggroCheck(i, bot_stance); + t.min_mana_pct = GetDefaultSpellTypeMinManaLimit(i, bot_stance); + t.max_mana_pct = GetDefaultSpellTypeMaxManaLimit(i, bot_stance); + t.min_hp_pct = GetDefaultSpellTypeMinHPLimit(i, bot_stance); + t.max_hp_pct = GetDefaultSpellTypeMaxHPLimit(i, bot_stance); + t.idle_priority = GetDefaultSpellTypePriority( + i, + BotPriorityCategories::Idle, + GetClass(), + bot_stance + ); + t.engaged_priority = GetDefaultSpellTypePriority( + i, + BotPriorityCategories::Engaged, + GetClass(), + bot_stance + ); + t.pursue_priority = GetDefaultSpellTypePriority( + i, + BotPriorityCategories::Pursue, + GetClass(), + bot_stance + ); + t.ae_or_group_target_count = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); + t.recast_timer.Start(); m_bot_spell_settings.push_back(t); - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.shortName, t.spellType, Stance::GetName(bot_stance), bot_stance); + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.short_name, t.spell_type, Stance::GetName(bot_stance), bot_stance); LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, bot_stance), GetDefaultSpellDelay(i, bot_stance), GetDefaultSpellMinThreshold(i, bot_stance), GetDefaultSpellMaxThreshold(i, bot_stance)); LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, bot_stance), GetDefaultSpellTypeMinManaLimit(i, bot_stance), GetDefaultSpellTypeMaxManaLimit(i, bot_stance), GetDefaultSpellTypeMinHPLimit(i, bot_stance), GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); - LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | AEOrGroupTargetCount = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | ae_or_group_target_count = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); } } @@ -10565,11 +10580,11 @@ BotSpell Bot::GetSpellByHealType(uint16 spell_type, Mob* tar) { uint16 Bot::GetSpellTypePriority(uint16 spell_type, uint8 priority_type) { switch (priority_type) { case BotPriorityCategories::Idle: - return m_bot_spell_settings[spell_type].idlePriority; + return m_bot_spell_settings[spell_type].idle_priority; case BotPriorityCategories::Engaged: - return m_bot_spell_settings[spell_type].engagedPriority; + return m_bot_spell_settings[spell_type].engaged_priority; case BotPriorityCategories::Pursue: - return m_bot_spell_settings[spell_type].pursuePriority; + return m_bot_spell_settings[spell_type].pursue_priority; default: return 0; } @@ -10963,13 +10978,13 @@ uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 sta void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority) { switch (priority_type) { case BotPriorityCategories::Idle: - m_bot_spell_settings[spell_type].idlePriority = priority; + m_bot_spell_settings[spell_type].idle_priority = priority; break; case BotPriorityCategories::Engaged: - m_bot_spell_settings[spell_type].engagedPriority = priority; + m_bot_spell_settings[spell_type].engaged_priority = priority; break; case BotPriorityCategories::Pursue: - m_bot_spell_settings[spell_type].pursuePriority = priority; + m_bot_spell_settings[spell_type].pursue_priority = priority; break; default: return; @@ -10977,31 +10992,31 @@ void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 pr } void Bot::SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit) { - m_bot_spell_settings[spell_type].resistLimit = resist_limit; + m_bot_spell_settings[spell_type].resist_limit = resist_limit; } void Bot::SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check) { - m_bot_spell_settings[spell_type].aggroCheck = aggro_check; + m_bot_spell_settings[spell_type].aggro_check = aggro_check; } void Bot::SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit) { - m_bot_spell_settings[spell_type].minManaPct = mana_limit; + m_bot_spell_settings[spell_type].min_mana_pct = mana_limit; } void Bot::SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit) { - m_bot_spell_settings[spell_type].maxManaPct = mana_limit; + m_bot_spell_settings[spell_type].max_mana_pct = mana_limit; } void Bot::SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit) { - m_bot_spell_settings[spell_type].minHPPct = hp_limit; + m_bot_spell_settings[spell_type].min_hp_pct = hp_limit; } void Bot::SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit) { - m_bot_spell_settings[spell_type].maxHPPct = hp_limit; + m_bot_spell_settings[spell_type].max_hp_pct = hp_limit; } void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count) { - m_bot_spell_settings[spell_type].AEOrGroupTargetCount = target_count; + m_bot_spell_settings[spell_type].ae_or_group_target_count = target_count; } std::list Bot::GetSpellTypesPrioritized(uint8 priority_type) { diff --git a/zone/bot.h b/zone/bot.h index 43df08fd71..1e1186e568 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -581,19 +581,19 @@ class Bot : public NPC { int GetSetting(uint16 setting_category, uint16 setting_type); uint16 GetSpellTypePriority(uint16 spell_type, uint8 priority_type); void SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority); - inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].resistLimit; } + inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].resist_limit; } void SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit); - inline bool GetSpellTypeAggroCheck(uint16 spell_type) const { return m_bot_spell_settings[spell_type].aggroCheck; } + inline bool GetSpellTypeAggroCheck(uint16 spell_type) const { return m_bot_spell_settings[spell_type].aggro_check; } void SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check); - inline uint8 GetSpellTypeMinManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].minManaPct; } - inline uint8 GetSpellTypeMaxManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].maxManaPct; } + inline uint8 GetSpellTypeMinManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_mana_pct; } + inline uint8 GetSpellTypeMaxManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_mana_pct; } void SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit); void SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit); - inline uint8 GetSpellTypeMinHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].minHPPct; } - inline uint8 GetSpellTypeMaxHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].maxHPPct; } + inline uint8 GetSpellTypeMinHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_hp_pct; } + inline uint8 GetSpellTypeMaxHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_hp_pct; } void SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit); void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit); - inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return m_bot_spell_settings[spell_type].AEOrGroupTargetCount; } + inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return m_bot_spell_settings[spell_type].ae_or_group_target_count; } void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count); bool BotPassiveCheck(); diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 7bfc5fd140..e10b3d42a8 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -230,18 +230,18 @@ void Client::LoadDefaultBotSettings() { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { BotSpellSettings t; - t.spellType = i; - t.shortName = GetSpellTypeShortNameByID(i); - t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i); - t.delay = GetDefaultSpellDelay(i); - t.minThreshold = GetDefaultSpellMinThreshold(i); - t.maxThreshold = GetDefaultSpellMaxThreshold(i); - t.recastTimer.Start(); + t.spell_type = i; + t.short_name = GetSpellTypeShortNameByID(i); + t.name = GetSpellTypeNameByID(i); + t.hold = GetDefaultSpellHold(i); + t.delay = GetDefaultSpellDelay(i); + t.min_threshold = GetDefaultSpellMinThreshold(i); + t.max_threshold = GetDefaultSpellMaxThreshold(i); + t.recast_timer.Start(); m_bot_spell_settings.push_back(t); - LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.shortName, t.spellType); + LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.short_name, t.spell_type); LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); } } diff --git a/zone/mob.h b/zone/mob.h index 83ba2b024b..517e7b12b5 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -94,24 +94,24 @@ struct AppearanceStruct { }; struct BotSpellSettings { - uint16 spellType; // type ID of bot category - std::string shortName; // type short name of bot category - std::string name; // type name of bot category - bool hold; // 0 = allow spell type, 1 = hold spell type - uint16 delay; // delay between casts of spell type, 1ms-60,000ms - uint8 minThreshold; // minimum target health threshold to allow casting of spell type - uint8 maxThreshold; // maximum target health threshold to allow casting of spell type - uint16 resistLimit; // resist limit to skip spell type - bool aggroCheck; // whether or not to check for possible aggro before casting - uint8 minManaPct; // lower mana percentage limit to allow spell cast - uint8 maxManaPct; // upper mana percentage limit to allow spell cast - uint8 minHPPct; // lower HP percentage limit to allow spell cast - uint8 maxHPPct; // upper HP percentage limit to allow spell cast - uint16 idlePriority; // idle priority of the spell type - uint16 engagedPriority; // engaged priority of the spell type - uint16 pursuePriority; // pursue priority of the spell type - uint16 AEOrGroupTargetCount; // require target count to cast an AE or Group spell type - Timer recastTimer; // recast timer based off delay + uint16 spell_type; // type ID of bot category + std::string short_name; // type short name of bot category + std::string name; // type name of bot category + bool hold; // 0 = allow spell type, 1 = hold spell type + uint16 delay; // delay between casts of spell type, 1ms-60,000ms + uint8 min_threshold; // minimum target health threshold to allow casting of spell type + uint8 max_threshold; // maximum target health threshold to allow casting of spell type + uint16 resist_limit; // resist limit to skip spell type + bool aggro_check; // whether or not to check for possible aggro before casting + uint8 min_mana_pct; // lower mana percentage limit to allow spell cast + uint8 max_mana_pct; // upper mana percentage limit to allow spell cast + uint8 min_hp_pct; // lower HP percentage limit to allow spell cast + uint8 max_hp_pct; // upper HP percentage limit to allow spell cast + uint16 idle_priority; // idle priority of the spell type + uint16 engaged_priority; // engaged priority of the spell type + uint16 pursue_priority; // pursue priority of the spell type + uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type + Timer recast_timer; // recast timer based off delay }; class DataBucketKey; @@ -433,7 +433,7 @@ class Mob : public Entity { bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); virtual bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); - inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recastTimer.GetRemainingTime(); } + inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } uint16 GetSpellTypeIDByShortName(std::string spellType_string); bool IsValidBotSpellCategory(uint8 setting_type); std::string GetBotSpellCategoryName(uint8 setting_type); @@ -456,12 +456,12 @@ class Mob : public Entity { void SetSpellHold(uint16 spell_type, bool hold_status); inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } void SetSpellDelay(uint16 spell_type, uint16 delay_value); - inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].minThreshold; } + inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value); - inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].maxThreshold; } + inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value); - inline uint16 GetSpellTypeRecastTimer(uint16 spell_type) { return m_bot_spell_settings[spell_type].recastTimer.GetRemainingTime(); } + inline uint16 GetSpellTypeRecastTimer(uint16 spell_type) { return m_bot_spell_settings[spell_type].recast_timer.GetRemainingTime(); } void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time); uint8 GetHPRatioForSpellType(uint16 spell_type, Mob* tar); diff --git a/zone/mob_bot.cpp b/zone/mob_bot.cpp index a71fa2ebfa..bc5f90f8b9 100644 --- a/zone/mob_bot.cpp +++ b/zone/mob_bot.cpp @@ -589,26 +589,26 @@ void Mob::SetSpellDelay(uint16 spell_type, uint16 delay_value) { } void Mob::SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { - m_bot_spell_settings[spell_type].minThreshold = threshold_value; + m_bot_spell_settings[spell_type].min_threshold = threshold_value; } void Mob::SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { - m_bot_spell_settings[spell_type].maxThreshold = threshold_value; + m_bot_spell_settings[spell_type].max_threshold = threshold_value; } void Mob::SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { - m_bot_spell_settings[spell_type].recastTimer.Start(recast_time); + m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } void Mob::StartBotSpellTimers() { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - m_bot_spell_settings[i].recastTimer.Start(); + m_bot_spell_settings[i].recast_timer.Start(); } } void Mob::DisableBotSpellTimers() { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - m_bot_spell_settings[i].recastTimer.Disable(); + m_bot_spell_settings[i].recast_timer.Disable(); } } From f99c19d3ee8d8a0fff8ba8eeb374eb5310ede54f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:11:00 -0600 Subject: [PATCH 345/394] Allow duplicate casts of same spell on target for heals and cures --- zone/bot.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/zone/bot.cpp b/zone/bot.cpp index f2c25f8b94..fe5f20fdfb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9790,6 +9790,10 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck if ( spells[spell_id].target_type != ST_Self && IsBeneficialSpell(spell_id) && + !IsAnyHealSpell(spell_id) && + !IsCureSpell(spell_id) && + !IsHealOverTimeSpell(spell_id) && + !IsGroupHealOverTimeSpell(spell_id) && IsTargetAlreadyReceivingSpell(tar, spell_id) ) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsTargetAlreadyReceivingSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); From 6b5b19a8cf8af79faebadd15bbafbd885cbb829f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:11:09 -0600 Subject: [PATCH 346/394] Add default delay to cures --- zone/mob_bot.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/zone/mob_bot.cpp b/zone/mob_bot.cpp index bc5f90f8b9..297c9f6a18 100644 --- a/zone/mob_bot.cpp +++ b/zone/mob_bot.cpp @@ -353,6 +353,12 @@ uint16 Mob::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: return 22000; + case BotSpellTypes::Cure: + return 2000; + case BotSpellTypes::GroupCures: + return 3000; + case BotSpellTypes::PetCures: + return 5000; case BotSpellTypes::AEDoT: case BotSpellTypes::DOT: switch (stance) { From fdadde49de8eac99c582c50e63dfd282c6ed65a7 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Tue, 28 Jan 2025 23:13:25 -0600 Subject: [PATCH 347/394] Remove unused methods --- zone/mob.h | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 517e7b12b5..cb6f2ff0b4 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -1339,14 +1339,6 @@ class Mob : public Entity { void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); inline uint32 DontHealMeBefore() const { return m_dont_heal_me_before; } - inline uint32 DontGroupHealMeBefore() const { return m_dont_group_heal_me_before; } - inline uint32 DontGroupHoTHealMeBefore() const { return m_dont_group_hot_heal_me_before; } - inline uint32 DontRegularHealMeBefore() const { return m_dont_regular_heal_me_before; } - inline uint32 DontVeryFastHealMeBefore() const { return m_dont_very_fast_heal_me_before; } - inline uint32 DontFastHealMeBefore() const { return m_dont_fast_heal_me_before; } - inline uint32 DontCompleteHealMeBefore() const { return m_dont_complete_heal_me_before; } - inline uint32 DontGroupCompleteHealMeBefore() const { return m_dont_group_complete_heal_me_before; } - inline uint32 DontHotHealMeBefore() const { return m_dont_hot_heal_me_before; } inline uint32 DontBuffMeBefore() const { return m_dont_buff_me_before; } inline uint32 DontDotMeBefore() const { return m_dont_dot_me_before; } inline uint32 DontRootMeBefore() const { return m_dont_root_me_before; } @@ -1355,14 +1347,6 @@ class Mob : public Entity { void SetDontRootMeBefore(uint32 time) { m_dont_root_me_before = time; } void SetDontHealMeBefore(uint32 time) { m_dont_heal_me_before = time; } - void SetDontGroupHealMeBefore(uint32 time) { m_dont_group_heal_me_before = time; } - void SetDontGroupHoTHealMeBefore(uint32 time) { m_dont_group_hot_heal_me_before = time; } - void SetDontRegularHealMeBefore(uint32 time) { m_dont_regular_heal_me_before = time; } - void SetDontVeryFastHealMeBefore(uint32 time) { m_dont_very_fast_heal_me_before = time; } - void SetDontFastHealMeBefore(uint32 time) { m_dont_fast_heal_me_before = time; } - void SetDontCompleteHealMeBefore(uint32 time) { m_dont_complete_heal_me_before = time; } - void SetDontGroupCompleteHealMeBefore(uint32 time) { m_dont_group_complete_heal_me_before = time; } - void SetDontHotHealMeBefore(uint32 time) { m_dont_hot_heal_me_before = time; } void SetDontBuffMeBefore(uint32 time) { m_dont_buff_me_before = time; } void SetDontDotMeBefore(uint32 time) { m_dont_dot_me_before = time; } void SetDontSnareMeBefore(uint32 time) { m_dont_snare_me_before = time; } From 9b7267294529545a39c1eff7e7fdc89aed46497c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 30 Jan 2025 22:24:33 -0600 Subject: [PATCH 348/394] Implement missing ^spellresistlimits/^resistlimits command --- .../database_update_manifest_bots.cpp | 1 + zone/bot.cpp | 7 + zone/bot.h | 22 +- zone/bot_command.cpp | 2 + zone/bot_command.h | 1 + zone/bot_commands/spell_resist_limits.cpp | 214 ++++++++++++++++++ 6 files changed, 237 insertions(+), 10 deletions(-) create mode 100644 zone/bot_commands/spell_resist_limits.cpp diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index 76f564baf3..f73afd598b 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -270,6 +270,7 @@ UPDATE `bot_command_settings` SET `aliases`= 'minhp' WHERE `bot_command`='spellm UPDATE `bot_command_settings` SET `aliases`= 'minmana' WHERE `bot_command`='spellminmanapct'; UPDATE `bot_command_settings` SET `aliases`= 'minthresholds' WHERE `bot_command`='spellminthresholds'; UPDATE `bot_command_settings` SET `aliases`= 'pursuepriority' WHERE `bot_command`='spellpursuepriority'; +UPDATE `bot_command_settings` SET `aliases`= 'resistlimits' WHERE `bot_command`='spellresistlimits'; UPDATE `bot_command_settings` SET `aliases`= 'targetcount' WHERE `bot_command`='spelltargetcount'; UPDATE `bot_command_settings` SET `aliases`= 'disc' WHERE `bot_command`='discipline'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|vc') ELSE 'vc' END WHERE `bot_command`='viewcombos' AND `aliases` NOT LIKE '%vc%'; diff --git a/zone/bot.cpp b/zone/bot.cpp index fe5f20fdfb..1436cd363e 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10295,6 +10295,9 @@ void Bot::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_valu case BotSettingCategories::SpellMaxThreshold: SetSpellMaxThreshold(bot_setting, setting_value); break; + case BotSettingCategories::SpellTypeResistLimit: + SetSpellTypeResistLimit(bot_setting, setting_value); + break; case BotSettingCategories::SpellTypeAggroCheck: SetSpellTypeAggroCheck(bot_setting, setting_value); break; @@ -10606,6 +10609,8 @@ int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 s return GetDefaultSpellMinThreshold(setting_type, stance); case BotSettingCategories::SpellMaxThreshold: return GetDefaultSpellMaxThreshold(setting_type, stance); + case BotSettingCategories::SpellTypeResistLimit: + return GetDefaultSpellTypeResistLimit(setting_type, stance); case BotSettingCategories::SpellTypeAggroCheck: return GetDefaultSpellTypeAggroCheck(setting_type, stance); case BotSettingCategories::SpellTypeMinManaPct: @@ -10641,6 +10646,8 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { return GetSpellMinThreshold(setting_type); case BotSettingCategories::SpellMaxThreshold: return GetSpellMaxThreshold(setting_type); + case BotSettingCategories::SpellTypeResistLimit: + return GetSpellTypeResistLimit(setting_type); case BotSettingCategories::SpellTypeAggroCheck: return GetSpellTypeAggroCheck(setting_type); case BotSettingCategories::SpellTypeMinManaPct: diff --git a/zone/bot.h b/zone/bot.h index 1e1186e568..870eac312f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -99,16 +99,17 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint8 SpellDelay = 2; constexpr uint8 SpellMinThreshold = 3; constexpr uint8 SpellMaxThreshold = 4; - constexpr uint8 SpellTypeAggroCheck = 5; - constexpr uint8 SpellTypeMinManaPct = 6; - constexpr uint8 SpellTypeMaxManaPct = 7; - constexpr uint8 SpellTypeMinHPPct = 8; - constexpr uint8 SpellTypeMaxHPPct = 9; - constexpr uint8 SpellTypeIdlePriority = 10; - constexpr uint8 SpellTypeEngagedPriority = 11; - constexpr uint8 SpellTypePursuePriority = 12; - constexpr uint8 SpellTypeAEOrGroupTargetCount = 13; - constexpr uint8 SpellTypeRecastDelay = 14; + constexpr uint8 SpellTypeResistLimit = 5; + constexpr uint8 SpellTypeAggroCheck = 6; + constexpr uint8 SpellTypeMinManaPct = 7; + constexpr uint8 SpellTypeMaxManaPct = 8; + constexpr uint8 SpellTypeMinHPPct = 9; + constexpr uint8 SpellTypeMaxHPPct = 10; + constexpr uint8 SpellTypeIdlePriority = 11; + constexpr uint8 SpellTypeEngagedPriority = 12; + constexpr uint8 SpellTypePursuePriority = 13; + constexpr uint8 SpellTypeAEOrGroupTargetCount = 14; + constexpr uint8 SpellTypeRecastDelay = 15; constexpr uint16 START = BotSettingCategories::BaseSetting; constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; @@ -124,6 +125,7 @@ static std::map botSpellCategory_names = { { BotSettingCategories::SpellDelay, "SpellDelays" }, { BotSettingCategories::SpellMinThreshold, "SpellMinThresholds" }, { BotSettingCategories::SpellMaxThreshold, "SpellMaxThresholds" }, + { BotSettingCategories::SpellTypeResistLimit, "SpellResistLimit" }, { BotSettingCategories::SpellTypeAggroCheck, "SpellAggroChecks" }, { BotSettingCategories::SpellTypeMinManaPct, "SpellMinManaPct" }, { BotSettingCategories::SpellTypeMaxManaPct, "SpellMaxManaPct" }, diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 7575e9c007..2398538cc8 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -190,6 +190,7 @@ int bot_command_init(void) bot_command_add("spellminhppct", "Controls at what HP percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_hp_pct) || bot_command_add("spellminmanapct", "Controls at what mana percent a bot will start casting different spell types", AccountStatus::Player, bot_command_spell_min_mana_pct) || bot_command_add("spellminthresholds", "Controls the maximum target HP threshold for a spell to be cast for a specific type", AccountStatus::Player, bot_command_spell_min_thresholds) || + bot_command_add("spellresistlimits", "Controls the resist limits for bots to cast spells on their target", AccountStatus::Player, bot_command_spell_resist_limits) || bot_command_add("spellpursuepriority", "Controls the order of casts by spell type when pursuing in combat", AccountStatus::Player, bot_command_spell_pursue_priority) || bot_command_add("spelltargetcount", "Sets the required target amount for group/AE spells by spell type", AccountStatus::Player, bot_command_spell_target_count) || bot_command_add("spellinfo", "Opens a dialogue window with spell info", AccountStatus::Player, bot_spell_info_dialogue_window) || @@ -969,6 +970,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/spell_min_mana_pct.cpp" #include "bot_commands/spell_min_thresholds.cpp" #include "bot_commands/spell_pursue_priority.cpp" +#include "bot_commands/spell_resist_limits.cpp" #include "bot_commands/spell_target_count.cpp" #include "bot_commands/spelltypes.cpp" #include "bot_commands/summon.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 553f098c41..6304fb1946 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1095,6 +1095,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep); void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep); void bot_command_spell_min_thresholds(Client* c, const Seperator* sep); void bot_command_spell_pursue_priority(Client* c, const Seperator* sep); +void bot_command_spell_resist_limits(Client* c, const Seperator* sep); void bot_command_spell_target_count(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); diff --git a/zone/bot_commands/spell_resist_limits.cpp b/zone/bot_commands/spell_resist_limits.cpp new file mode 100644 index 0000000000..e7c928c338 --- /dev/null +++ b/zone/bot_commands/spell_resist_limits.cpp @@ -0,0 +1,214 @@ +#include "../bot_command.h" + +void bot_command_spell_resist_limits(Client* c, const Seperator* sep) { + if (helper_command_alias_fail(c, "bot_command_spell_resist_limits", sep->arg[0], "spellresistlimits")) { + c->Message(Chat::White, "note: Sets the limit of a target's resists to where a bot won't attempt to cast due to resist chances."); + + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + BotCommandHelpParams p; + + p.description = { "Sets the limit of a target's resists to where a bot won't attempt to cast due to resist chances." }; + p.example_format = + { + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) + }; + p.examples_one = + { + "To set all bots' slow resist limit to 250:", + fmt::format( + "{} {} 250 spawned", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Slow) + ), + fmt::format( + "{} {} 250 spawned", + sep->arg[0], + BotSpellTypes::Slow + ) + }; + p.examples_two = + { + "To set Magicians to limit their resist to 175 for nukes:", + fmt::format( + "{} {} 175 byclass {}", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke), + Class::Magician + ), + fmt::format( + "{} {} 175 byclass {}", + sep->arg[0], + BotSpellTypes::Nuke, + Class::Magician + + ) + }; + p.examples_three = + { + "To check the current debuff resist limit on all bots:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Debuff + ) + }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + + return; + } + + std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spell_type = 0; + uint32 type_value = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spell_type = atoi(sep->arg[1]); + + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + type_value = atoi(sep->arg[2]); + ++ab_arg; + + if (type_value < 0 || type_value > 1000) { + c->Message(Chat::Yellow, "You must enter a value between 1-1000."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::vector sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] resist limit is currently [{}].'", + my_bot->GetCleanName(), + Bot::GetSpellTypeNameByID(spell_type), + my_bot->GetSpellTypeResistLimit(spell_type) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeResistLimit(spell_type, type_value); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'My [{}] resist limit was set to [{}].'", + first_found->GetCleanName(), + Bot::GetSpellTypeNameByID(spell_type), + first_found->GetSpellTypeResistLimit(spell_type) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots set their [{}] resist limit to [{}].", + success_count, + Bot::GetSpellTypeNameByID(spell_type), + type_value + ).c_str() + ); + } + } +} From e039ce2e3708cb83d4c4ec30c06ebf6a2ae8c574 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 30 Jan 2025 22:25:54 -0600 Subject: [PATCH 349/394] Move functions out of mob.h and cleanup --- zone/CMakeLists.txt | 1 - zone/bot.cpp | 877 ++++++++++++++++++- zone/bot.h | 154 ++-- zone/bot_command.cpp | 4 +- zone/bot_commands/blocked_buffs.cpp | 2 +- zone/bot_commands/bot.cpp | 4 +- zone/bot_commands/cast.cpp | 14 +- zone/bot_commands/copy_settings.cpp | 23 +- zone/bot_commands/default_settings.cpp | 21 +- zone/bot_commands/spell_aggro_checks.cpp | 16 +- zone/bot_commands/spell_delays.cpp | 20 +- zone/bot_commands/spell_engaged_priority.cpp | 18 +- zone/bot_commands/spell_holds.cpp | 14 +- zone/bot_commands/spell_idle_priority.cpp | 18 +- zone/bot_commands/spell_max_hp_pct.cpp | 16 +- zone/bot_commands/spell_max_mana_pct.cpp | 16 +- zone/bot_commands/spell_max_thresholds.cpp | 20 +- zone/bot_commands/spell_min_hp_pct.cpp | 16 +- zone/bot_commands/spell_min_mana_pct.cpp | 16 +- zone/bot_commands/spell_min_thresholds.cpp | 20 +- zone/bot_commands/spell_pursue_priority.cpp | 18 +- zone/bot_commands/spell_target_count.cpp | 25 +- zone/bot_database.cpp | 45 +- zone/bot_structs.h | 21 + zone/botspellsai.cpp | 4 +- zone/client.h | 22 +- zone/client_bot.cpp | 142 ++- zone/gm_commands/illusion_block.cpp | 8 +- zone/mob.h | 88 -- zone/mob_bot.cpp | 778 ---------------- zone/spell_effects.cpp | 26 +- zone/spells.cpp | 105 --- 32 files changed, 1312 insertions(+), 1260 deletions(-) delete mode 100644 zone/mob_bot.cpp diff --git a/zone/CMakeLists.txt b/zone/CMakeLists.txt index 83963f5208..798114399c 100644 --- a/zone/CMakeLists.txt +++ b/zone/CMakeLists.txt @@ -98,7 +98,6 @@ SET(zone_sources mob.cpp mob_ai.cpp mob_appearance.cpp - mob_bot.cpp mob_movement_manager.cpp mob_info.cpp npc.cpp diff --git a/zone/bot.cpp b/zone/bot.cpp index 1436cd363e..078785520b 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -108,7 +108,6 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm SetCastedSpellType(UINT16_MAX); SetCommandedSpell(false); SetPullingSpell(false); - //DisableBotSpellTimers(); // Do this once and only in this constructor GenerateAppearance(); @@ -3267,6 +3266,14 @@ Mob* Bot::GetBotTarget(Client* bot_owner) return t; } +bool Bot::TargetValidation(Mob* other) { + if (!other || GetAppearance() == eaDead) { + return false; + } + + return true; +} + bool Bot::ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance) { if ( (NOT_GUARDING && fm_distance <= GetFollowDistance()) || @@ -3646,14 +3653,6 @@ bool Bot::Spawn(Client* botCharacterOwner) { m_auto_save_timer.Start(RuleI(Bots, AutosaveIntervalSeconds) * 1000); m_dont_heal_me_before = 0; - m_dont_regular_heal_me_before = 0; - m_dont_very_fast_heal_me_before = 0; - m_dont_fast_heal_me_before = 0; - m_dont_complete_heal_me_before = 0; - m_dont_hot_heal_me_before = 0; - m_dont_group_heal_me_before = 0; - m_dont_group_hot_heal_me_before = 0; - m_dont_group_complete_heal_me_before = 0; m_dont_buff_me_before = Timer::GetCurrentTime() + 400; m_dont_dot_me_before = 0; m_dont_root_me_before = 0; @@ -9674,7 +9673,7 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck return false; } - if (tar->IsImmuneToBotSpell(spell_id, this)) { + if (tar->CastToBot()->IsImmuneToBotSpell(spell_id, this)) { LogBotSpellChecksDetail("{} says, 'Cancelling cast of {} on {} due to IsImmuneToBotSpell.'", GetCleanName(), GetSpellName(spell_id), tar->GetCleanName()); return false; } @@ -10104,7 +10103,7 @@ bool Bot::DoResistCheck(Mob* tar, uint16 spell_id, int32 resist_limit) { default: return true; } - //LogBotSpellChecksDetail("DoResistCheck on {} for {} - TarResist [{}] LMod [{}] ResistDiff [{}] - Adjust [{}] > ResistLim [{}]", tar->GetCleanName(), GetSpellName(spell_id), target_resist, level_mod, resist_difficulty, (target_resist + level_mod - resist_difficulty), resist_limit); + if ((target_resist + level_mod - resist_difficulty) > resist_limit) { return false; } @@ -10548,10 +10547,20 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + if (tar->IsClient()) { + tar->GetOwner()->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + } + else { + tar->GetOwner()->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + } } else if (IsBotSpellTypeOtherBeneficial(spell_type)) { - tar->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + if (tar->IsClient()) { + tar->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + } + else { + tar->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + } } else { SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); @@ -10671,6 +10680,134 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { } } +bool Bot::GetDefaultSpellHold(uint16 spell_type, uint8 stance) { + uint8 bot_class = GetClass(); + + switch (spell_type) { + case BotSpellTypes::FastHeals: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::Pet: + case BotSpellTypes::Escape: + case BotSpellTypes::Lifetap: + case BotSpellTypes::Buff: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::DamageShields: + case BotSpellTypes::ResistBuffs: + return false; + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::GroupHeals: + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::PetFastHeals: + case BotSpellTypes::PetRegularHeals: + case BotSpellTypes::PetVeryFastHeals: + case BotSpellTypes::RegularHeal: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + switch (stance) { + case Stance::Aggressive: + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::PreCombatBuffSong: + if (bot_class == Class::Bard) { + return false; + } + else { + return true; + } + case BotSpellTypes::Nuke: + case BotSpellTypes::DOT: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::Assist: + return true; + default: + return false; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::AEStun: + case BotSpellTypes::AEDoT: + case BotSpellTypes::AELifetap: + case BotSpellTypes::PBAENuke: + switch (stance) { + case Stance::AEBurn: + return false; + default: + return true; + } + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Slow: + case BotSpellTypes::AESlow: + case BotSpellTypes::HateRedux: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return true; + default: + return false; + } + case BotSpellTypes::Snare: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Assist: + return true; + default: + return false; + } + case BotSpellTypes::HateLine: + if (bot_class == Class::ShadowKnight || bot_class == Class::Paladin) { + switch (stance) { + case Stance::Aggressive: + return false; + default: + return true; + } + } + else { + return true; + } + case BotSpellTypes::Charm: + case BotSpellTypes::Resurrect: + case BotSpellTypes::AESnare: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::AEHateLine: + case BotSpellTypes::PetCures: + case BotSpellTypes::PetHoTHeals: + case BotSpellTypes::PetCompleteHeals: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + default: + return true; + } +} + uint16 Bot::GetDefaultSpellTypePriority(uint16 spell_type, uint8 priority_type, uint8 bot_class, uint8 stance) { switch (priority_type) { case BotPriorityCategories::Idle: @@ -10840,7 +10977,7 @@ uint16 Bot::GetDefaultSpellTypeEngagedPriority(uint16 spell_type, uint8 bot_clas case BotSpellTypes::InCombatBuffSong: return 44; default: - return 0; + return 0; //unused } } @@ -10893,7 +11030,7 @@ uint16 Bot::GetDefaultSpellTypePursuePriority(uint16 spell_type, uint8 bot_class case BotSpellTypes::PetCures: return 23; default: - return 0; + return 0; //unused } } @@ -10983,7 +11120,19 @@ uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 sta return RuleI(Bots, MinTargetsForGroupSpell); } - return 0; + return 1; +} + +bool Bot::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellHold(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->IsPetOwnerBot()) { + return tar->GetOwner()->CastToBot()->GetSpellHold(GetPetBotSpellType(spell_type)); + } + + return GetSpellHold(spell_type); } void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority) { @@ -11002,34 +11151,6 @@ void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 pr } } -void Bot::SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit) { - m_bot_spell_settings[spell_type].resist_limit = resist_limit; -} - -void Bot::SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check) { - m_bot_spell_settings[spell_type].aggro_check = aggro_check; -} - -void Bot::SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit) { - m_bot_spell_settings[spell_type].min_mana_pct = mana_limit; -} - -void Bot::SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit) { - m_bot_spell_settings[spell_type].max_mana_pct = mana_limit; -} - -void Bot::SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit) { - m_bot_spell_settings[spell_type].min_hp_pct = hp_limit; -} - -void Bot::SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit) { - m_bot_spell_settings[spell_type].max_hp_pct = hp_limit; -} - -void Bot::SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count) { - m_bot_spell_settings[spell_type].ae_or_group_target_count = target_count; -} - std::list Bot::GetSpellTypesPrioritized(uint8 priority_type) { std::list cast_order; std::list temp_cast_order; @@ -11391,6 +11512,13 @@ uint16 Bot::GetParentSpellType(uint16 spell_type) { return spell_type; } +bool Bot::IsValidBotSpellType(uint16 spell_type) { + return ( + EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END) || + EQ::ValueWithin(spell_type, BotSpellTypes::COMMANDED_START, BotSpellTypes::COMMANDED_END) + ); +} + bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { if (IsAEBotSpellType(spell_type) && !IsAnyAESpell(spell_id)) { return false; @@ -11712,6 +11840,85 @@ void Bot::DoCombatPositioning( return; } +bool Bot::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) { + bool Result = false; + + if (target) { + float look_heading = 0; + + min_distance = min_distance; + max_distance = max_distance; + float temp_x = 0; + float temp_y = 0; + float temp_z = target->GetZ(); + float best_z = 0; + auto offset = GetZOffset(); + const float tar_x = target->GetX(); + const float tar_y = target->GetY(); + float tar_distance = 0; + + glm::vec3 temp_z_Position; + glm::vec4 temp_m_Position; + + const uint16 max_iterations_allowed = 50; + uint16 counter = 0; + + while (counter < max_iterations_allowed) { + temp_x = tar_x + zone->random.Real(-max_distance, max_distance); + temp_y = tar_y + zone->random.Real(-max_distance, max_distance); + + temp_z_Position.x = temp_z; + temp_z_Position.y = temp_y; + temp_z_Position.z = temp_z; + best_z = GetFixedZ(temp_z_Position); + + if (best_z != BEST_Z_INVALID) { + temp_z = best_z; + } + else { + counter++; + continue; + } + + temp_m_Position.x = temp_x; + temp_m_Position.y = temp_y; + temp_m_Position.z = temp_z; + + tar_distance = Distance(target->GetPosition(), temp_m_Position); + + if (tar_distance > max_distance || tar_distance < min_distance) { + counter++; + continue; + } + + if (front_only && !InFrontMob(target, temp_x, temp_y)) { + counter++; + continue; + } + else if (behind_only && !BehindMob(target, temp_x, temp_y)) { + counter++; + continue; + } + + if (!bypass_los && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, temp_x, temp_y, temp_z)) { + counter++; + continue; + } + + Result = true; + break; + } + + if (Result) { + x_dest = temp_x; + y_dest = temp_y; + z_dest = temp_z; + } + } + + return Result; +} + bool Bot::CheckDoubleRangedAttack() { int32 chance = spellbonuses.DoubleRangedAttack + itembonuses.DoubleRangedAttack + aabonuses.DoubleRangedAttack; @@ -12394,6 +12601,55 @@ void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unor } } +std::vector Bot::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots, bool no_pets) { + std::vector valid_spell_targets; + + auto is_valid_target = [no_clients, no_bots](Mob* member) { + return member && + ((member->IsClient() && !no_clients) || (member->IsBot() && !no_bots)); + }; + + if (IsRaidGrouped()) { + Raid* raid = IsBot() ? CastToBot()->GetStoredRaid() : GetRaid(); + + if (raid) { + if (entire_raid) { + for (const auto& m : raid->members) { + if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { + valid_spell_targets.emplace_back(m.member); + } + } + } + else { + auto group_name = target ? raid->GetGroup(target->GetName()) : raid->GetGroup(GetName()); + auto raid_group = raid->GetRaidGroupMembers(group_name); + + for (const auto& m : raid_group) { + if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { + valid_spell_targets.emplace_back(m.member); + } + } + } + } + } + else if (IsGrouped()) { + Group* group = GetGroup(); + + if (group) { + for (const auto& m : group->members) { + if (is_valid_target(m)) { + valid_spell_targets.emplace_back(m); + } + } + } + } + else { + valid_spell_targets.emplace_back(this); + } + + return valid_spell_targets; +} + std::vector Bot::GetBuffTargets(Mob* spellTarget) { if (RuleB(Bots, RaidBuffing)) { return GetSpellTargetList(); @@ -12401,3 +12657,536 @@ std::vector Bot::GetBuffTargets(Mob* spellTarget) { return GatherSpellTargets(false, spellTarget); } + +uint8 Bot::GetHPRatioForSpellType(uint16 spell_type, Mob* tar) { + switch (spell_type) { + case BotSpellTypes::Escape: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + return GetHPRatio(); + default: + return tar->GetHPRatio(); + } + + return tar->GetHPRatio(); +} + +uint16 Bot::GetPetBotSpellType(uint16 spell_type) { + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + return BotSpellTypes::PetVeryFastHeals; + case BotSpellTypes::FastHeals: + return BotSpellTypes::PetFastHeals; + case BotSpellTypes::RegularHeal: + return BotSpellTypes::PetRegularHeals; + case BotSpellTypes::CompleteHeal: + return BotSpellTypes::PetCompleteHeals; + case BotSpellTypes::HoTHeals: + return BotSpellTypes::PetHoTHeals; + case BotSpellTypes::Buff: + return BotSpellTypes::PetBuffs; + case BotSpellTypes::Cure: + return BotSpellTypes::PetCures; + case BotSpellTypes::DamageShields: + return BotSpellTypes::PetDamageShields; + case BotSpellTypes::ResistBuffs: + return BotSpellTypes::PetResistBuffs; + default: + break; + } + + return spell_type; +} + +uint16 Bot::GetSpellTypeIDByShortName(std::string spell_type_string) { + + for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + + for (int i = BotSpellTypes::COMMANDED_START; i <= BotSpellTypes::COMMANDED_END; ++i) { + if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { + return i; + } + } + + return UINT16_MAX; +} + +bool Bot::IsValidBotSpellCategory(uint8 setting_type) { + return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END_FULL); +} + +std::string Bot::GetBotSpellCategoryName(uint8 setting_type) { + return Bot::IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; +} + +uint16 Bot::GetBotSpellCategoryIDByShortName(std::string setting_string) { + for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { + if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) { + return i; + } + } + + return UINT16_MAX; +} + +bool Bot::IsValidBotBaseSetting(uint16 setting_type) { + return EQ::ValueWithin(setting_type, BotBaseSettings::START_ALL, BotBaseSettings::END); +} + +std::string Bot::GetBotSettingCategoryName(uint16 setting_type) { + return Bot::IsValidBotBaseSetting(setting_type) ? botBaseSettings_names[setting_type] : "UNKNOWN SETTING"; +} + +uint16 Bot::GetBaseSettingIDByShortName(std::string setting_string) { + for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { + if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSettingCategoryName(i)))) { + return i; + } + } + + return UINT16_MAX; +} + +std::string Bot::GetSpellTypeShortNameByID(uint16 spell_type) { + return IsValidBotSpellType(spell_type) ? spellType_shortNames[spell_type] : "UNKNOWN SPELLTYPE"; +} + +std::string Bot::GetSpellTypeNameByID(uint16 spell_type) { + return IsValidBotSpellType(spell_type) ? spellType_names[spell_type] : "UNKNOWN SPELLTYPE"; +} + +bool Bot::IsValidSubType(uint16 sub_type) { + return EQ::ValueWithin(sub_type, CommandedSubTypes::START, CommandedSubTypes::END); +} + +std::string Bot::GetSubTypeNameByID(uint16 sub_type) { + return IsValidBotSpellType(sub_type) ? botSubType_names[sub_type] : "UNKNOWN SUBTYPE"; +} + +uint16 Bot::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return 1500; + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return 2500; + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return 4000; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 8000; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return 22000; + case BotSpellTypes::Cure: + return 2000; + case BotSpellTypes::GroupCures: + return 3000; + case BotSpellTypes::PetCures: + return 5000; + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 2000; + case Stance::Efficient: + return 8000; + default: + return 4000; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AEStun: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 1; + case Stance::Aggressive: + return 3000; + case Stance::Efficient: + return 10000; + default: + return 6000; + } + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + return 8000; + case BotSpellTypes::Fear: + case BotSpellTypes::AEFear: + return 15000; + default: + return 1; + } +} + +uint8 Bot::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { + switch (spell_type) { + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 20; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::Nuke: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + default: + return 5; + } + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 0; + case Stance::Efficient: + return 40; + default: + return 25; + } + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + return 85; + default: + return 0; + } +} + +uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { + uint8 bot_class = GetClass(); + + switch (spell_type) { + case BotSpellTypes::Escape: + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 40; + case Stance::Efficient: + default: + return 25; + } + case BotSpellTypes::AELifetap: + case BotSpellTypes::Lifetap: + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 55; + case Stance::Efficient: + return 35; + default: + return 40; + } + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 70; + case Stance::Efficient: + return 50; + default: + return 60; + } + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 90; + case Stance::Efficient: + return 65; + default: + return 80; + } + case BotSpellTypes::AENukes: + case BotSpellTypes::AERains: + case BotSpellTypes::PBAENuke: + case BotSpellTypes::AEStun: + case BotSpellTypes::Nuke: + case BotSpellTypes::AEDoT: + case BotSpellTypes::DOT: + case BotSpellTypes::AERoot: + case BotSpellTypes::Root: + case BotSpellTypes::AESlow: + case BotSpellTypes::Slow: + case BotSpellTypes::AESnare: + case BotSpellTypes::Snare: + case BotSpellTypes::AEFear: + case BotSpellTypes::Fear: + case BotSpellTypes::AEDispel: + case BotSpellTypes::Dispel: + case BotSpellTypes::AEDebuff: + case BotSpellTypes::Debuff: + case BotSpellTypes::Stun: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + return 100; + case Stance::Aggressive: + return 100; + case Stance::Efficient: + return 90; + default: + return 99; + } + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { + return 60; + } + else { + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 95; + case Stance::Efficient: + return 80; + default: + return 90; + } + } + case BotSpellTypes::Buff: + case BotSpellTypes::Charm: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::DamageShields: + case BotSpellTypes::HateRedux: + case BotSpellTypes::InCombatBuff: + case BotSpellTypes::InCombatBuffSong: + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::OutOfCombatBuffSong: + case BotSpellTypes::Pet: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PreCombatBuff: + case BotSpellTypes::PreCombatBuffSong: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::ResistBuffs: + case BotSpellTypes::Resurrect: + case BotSpellTypes::HateLine: + case BotSpellTypes::AEHateLine: + default: + return 100; + } +} + +uint16 Bot::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellDelay(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->IsClient() ? tar->GetOwner()->CastToClient()->GetSpellDelay(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->GetSpellDelay(GetPetBotSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() ? tar->CastToClient()->GetSpellDelay(spell_type) : tar->CastToBot()->GetSpellDelay(spell_type); + } + + return GetSpellDelay(spell_type); +} + +bool Bot::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { + if (!tar) { + return SpellTypeRecastCheck(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->IsClient() ? tar->GetOwner()->CastToClient()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() ? tar->CastToClient()->SpellTypeRecastCheck(spell_type) : tar->CastToBot()->SpellTypeRecastCheck(spell_type); + } + + return SpellTypeRecastCheck(spell_type); +} + +uint8 Bot::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellMinThreshold(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->IsClient() ? tar->GetOwner()->CastToClient()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() ? tar->CastToClient()->GetSpellMinThreshold(spell_type) : tar->CastToBot()->GetSpellMinThreshold(spell_type); + } + + return GetSpellMinThreshold(spell_type); +} + +uint8 Bot::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { + if (!tar) { + return GetSpellMaxThreshold(spell_type); + } + + if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { + return tar->IsClient() ? tar->GetOwner()->CastToClient()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)); + } + + if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { + return tar->IsClient() ? tar->CastToClient()->GetSpellMaxThreshold(spell_type) : tar->CastToBot()->GetSpellMaxThreshold(spell_type); + } + + return GetSpellMaxThreshold(spell_type); +} + +bool Bot::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) { + int effect_index; + + if (!caster) { + return false; + } + + LogSpells("Checking to see if we are immune to spell [{}] cast by [{}]", spell_id, caster->GetName()); + + if (!IsValidSpell(spell_id)) { + return true; + } + + if (GetSpecialAbility(SpecialAbility::DispellImmunity) && IsDispelSpell(spell_id)) { + return true; + } + + if (GetSpecialAbility(SpecialAbility::PacifyImmunity) && IsHarmonySpell(spell_id)) { + return true; + } + + if (!GetSpecialAbility(SpecialAbility::MesmerizeImmunity) && IsMesmerizeSpell(spell_id)) { + // check max level for spell + effect_index = GetSpellEffectIndex(spell_id, SE_Mez); + assert(effect_index >= 0); + // NPCs get to ignore the max level + if ( + (GetLevel() > spells[spell_id].max_value[effect_index]) && + (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))) + ) { + return true; + } + } + + // slow and haste spells + if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) { + return true; + } + + // client vs client fear + if (!GetSpecialAbility(SpecialAbility::FearImmunity) && IsEffectInSpell(spell_id, SE_Fear)) { + effect_index = GetSpellEffectIndex(spell_id, SE_Fear); + + if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) { + LogSpells("Clients cannot fear eachother!"); + caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up + return true; + } + else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { + return true; + } + else if (CheckAATimer(aaTimerWarcry)) { + return true; + } + } + + if (!GetSpecialAbility(SpecialAbility::CharmImmunity) && IsCharmSpell(spell_id)) { + + if (this == caster) { + return true; + } + + //let npcs cast whatever charm on anyone + if (!caster->IsNPC()) { + // check level limit of charm spell + effect_index = GetSpellEffectIndex(spell_id, SE_Charm); + assert(effect_index >= 0); + if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { + return true; + } + } + } + + if ( + GetSpecialAbility(SpecialAbility::SnareImmunity) && + ( + IsEffectInSpell(spell_id, SE_Root) || + IsEffectInSpell(spell_id, SE_MovementSpeed) + ) + ) { + if (GetSpecialAbility(SpecialAbility::SnareImmunity)) { + return true; + } + } + + if (IsLifetapSpell(spell_id)) { + if (this == caster) { + return true; + } + } + + if (IsSacrificeSpell(spell_id)) { + if (this == caster) { + return true; + } + } + + return false; +} diff --git a/zone/bot.h b/zone/bot.h index 870eac312f..67618f9cfe 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -113,7 +113,7 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint16 START = BotSettingCategories::BaseSetting; constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; - constexpr uint16 START_CLIENT = BotSettingCategories::SpellHold; + constexpr uint16 START_CLIENT = BotSettingCategories::SpellDelay; constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; constexpr uint16 END_FULL = BotSettingCategories::SpellTypeRecastDelay; @@ -527,47 +527,51 @@ class Bot : public NPC { { return Mob::Attack(other, Hand, FromRiposte, IsStrikethrough, IsFromSpell, opts); } void DoAttackRounds(Mob* target, int hand); - bool PrecastChecks(Mob* tar, uint16 spell_type); - bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false); - bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar); - bool BotHasEnoughMana(uint16 spell_id); - std::vector GetSpellTargetList() { return _spellTargetList; } - void SetSpellTargetList(std::vector spell_target_list) { _spellTargetList = spell_target_list; } - std::vector GetGroupSpellTargetList() { return _groupSpellTargetList; } - void SetGroupSpellTargetList(std::vector spell_target_list) { _groupSpellTargetList = spell_target_list; } - std::vector GetBuffTargets(Mob* spellTarget); + bool BotPassiveCheck(); Raid* GetStoredRaid() { return _storedRaid; } void SetStoredRaid(Raid* stored_raid) { _storedRaid = stored_raid; } bool GetVerifiedRaid() { return _verifiedRaid; } void SetVerifiedRaid(bool status) { _verifiedRaid = status; } uint16 GetTempSpellType() { return _tempSpellType; } void SetTempSpellType(uint16 spell_type) { _tempSpellType = spell_type; } - void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); + bool IsMobEngagedByAnyone(Mob* tar); + void SetBotTimers(std::vector timers) { bot_timers = timers; } + + // Targeting + std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); + bool HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar); + void SetHasLoS(bool has_los) { _hasLoS = has_los; } + bool HasLoS() const { return _hasLoS; } + bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); + + // Cast checks + bool PrecastChecks(Mob* tar, uint16 spell_type); + bool CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool prechecks = false, bool ae_check = false); + bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); + bool CanCastSpellType(uint16 spell_type, uint16 spell_id, Mob* tar); + bool BotHasEnoughMana(uint16 spell_id); bool IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id); bool DoResistCheck(Mob* target, uint16 spell_id, int32 resist_limit); bool DoResistCheckBySpellType(Mob* tar, uint16 spell_id, uint16 spell_type); bool IsValidTargetType(uint16 spell_id, int target_type, uint8 body_type); - bool IsMobEngagedByAnyone(Mob* tar); - void SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value); - void CopySettings(Bot* to, uint8 setting_type, uint16 spell_type = UINT16_MAX); - void CopyBotSpellSettings(Bot* to); - void ResetBotSpellSettings(); - void CopyBotBlockedBuffs(Bot* to); - void CopyBotBlockedPetBuffs(Bot* to); - int GetBotBaseSetting(uint16 bot_setting); - int GetDefaultBotBaseSetting(uint16 bot_setting, uint8 stance = Stance::Balanced); - void SetBotBaseSetting(uint16 bot_setting, int setting_value); - void LoadDefaultBotSettings(); - void SetBotSpellRecastTimer(uint16 spell_type, Mob* spelltar, bool pre_cast = false); - uint16 GetSpellByAA(int id, AA::Rank* &rank); - void CleanBotBlockedBuffs(); - void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); } - bool IsBlockedBuff(int32 spell_id) override; - bool IsBlockedPetBuff(int32 spell_id) override; - void SetBotBlockedBuff(uint16 spell_id, bool block); - void SetBotBlockedPetBuff(uint16 spell_id, bool block); + // Spell checks + static bool IsValidBotSpellType(uint16 spell_type); + uint16 GetPetBotSpellType(uint16 spell_type); + + // Movement checks + bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); + std::vector GetSpellTargetList() { return _spellTargetList; } + void SetSpellTargetList(std::vector spell_target_list) { _spellTargetList = spell_target_list; } + std::vector GetGroupSpellTargetList() { return _groupSpellTargetList; } + void SetGroupSpellTargetList(std::vector spell_target_list) { _groupSpellTargetList = spell_target_list; } + std::vector GetBuffTargets(Mob* spellTarget); + + // Bot settings + void LoadDefaultBotSettings(); int GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 stance = Stance::Balanced); + int GetDefaultBotBaseSetting(uint16 bot_setting, uint8 stance = Stance::Balanced); + bool GetDefaultSpellHold(uint16 spell_type, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypePriority(uint16 spell_type, uint8 priority_type, uint8 bot_class, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeIdlePriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeEngagedPriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced); @@ -580,25 +584,66 @@ class Bot : public NPC { uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 stance = Stance::Balanced); + static bool IsValidBotBaseSetting(uint16 setting_type); + static std::string GetBotSettingCategoryName(uint16 setting_type); + uint16 GetBaseSettingIDByShortName(std::string setting_string); + int GetBotBaseSetting(uint16 bot_setting); + void SetBotBaseSetting(uint16 bot_setting, int setting_value); int GetSetting(uint16 setting_category, uint16 setting_type); + void SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value); + void CopySettings(Bot* to, uint8 setting_type, uint16 spell_type = UINT16_MAX); + void CopyBotSpellSettings(Bot* to); + void ResetBotSpellSettings(); + + void CopyBotBlockedBuffs(Bot* to); + void CopyBotBlockedPetBuffs(Bot* to); + void CleanBotBlockedBuffs(); + void ClearBotBlockedBuffs() { bot_blocked_buffs.clear(); } + bool IsBlockedBuff(int32 spell_id) override; + bool IsBlockedPetBuff(int32 spell_id) override; + void SetBotBlockedBuff(uint16 spell_id, bool block); + void SetBotBlockedPetBuff(uint16 spell_id, bool block); + std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } + void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } + + void SetBotSpellRecastTimer(uint16 spell_type, Mob* spelltar, bool pre_cast = false); + inline bool GetSpellHold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].hold; } + inline void SetSpellHold(uint16 spell_type, bool value) { m_bot_spell_settings[spell_type].hold = value; } + bool GetUltimateSpellHold(uint16 spell_type, Mob* tar); uint16 GetSpellTypePriority(uint16 spell_type, uint8 priority_type); void SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority); inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].resist_limit; } - void SetSpellTypeResistLimit(uint16 spell_type, uint16 resist_limit); + inline void SetSpellTypeResistLimit(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].resist_limit = value; } inline bool GetSpellTypeAggroCheck(uint16 spell_type) const { return m_bot_spell_settings[spell_type].aggro_check; } - void SetSpellTypeAggroCheck(uint16 spell_type, bool aggro_check); + inline void SetSpellTypeAggroCheck(uint16 spell_type, bool value) { m_bot_spell_settings[spell_type].aggro_check = value; } + uint8 GetHPRatioForSpellType(uint16 spell_type, Mob* tar); inline uint8 GetSpellTypeMinManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_mana_pct; } inline uint8 GetSpellTypeMaxManaLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_mana_pct; } - void SetSpellTypeMinManaLimit(uint16 spell_type, uint8 mana_limit); - void SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 mana_limit); + inline void SetSpellTypeMinManaLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].min_mana_pct = value; } + inline void SetSpellTypeMaxManaLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].max_mana_pct = value; } inline uint8 GetSpellTypeMinHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_hp_pct; } inline uint8 GetSpellTypeMaxHPLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_hp_pct; } - void SetSpellTypeMinHPLimit(uint16 spell_type, uint8 hp_limit); - void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 hp_limit); + inline void SetSpellTypeMinHPLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].min_hp_pct = value; } + inline void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].max_hp_pct = value; } inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return m_bot_spell_settings[spell_type].ae_or_group_target_count; } - void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 target_count); - bool BotPassiveCheck(); - + inline void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].ae_or_group_target_count = value; } + inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } + inline void SetSpellDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } + inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } + inline void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].min_threshold = threshold_value; } + inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } + inline void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } + inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } + void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } + uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint16 GetUltimateSpellDelay(uint16 spell_type, Mob* tar); + bool GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar); + uint8 GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar); + uint8 GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar); + void SetIllusionBlock(bool value) { _illusionBlock = value; } + bool GetIllusionBlock() const { return _illusionBlock; } bool GetShowHelm() const { return _showHelm; } void SetShowHelm(bool show_helm) { _showHelm = show_helm; } bool GetBehindMob() const { return _behindMobStatus; } @@ -615,20 +660,29 @@ class Bot : public NPC { void SetSitHPPct(uint8 value) { _SitHPPct = value; } uint8 GetSitManaPct() const { return _SitManaPct; } void SetSitManaPct(uint8 value) { _SitManaPct = value; } - void SetHasLoS(bool has_los) { _hasLoS = has_los; } - bool HasLoS() const { return _hasLoS; } + // Spell lists + void CheckBotSpells(); + void MapSpellTypeLevels(); + const std::map>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } std::list GetSpellTypesPrioritized(uint8 priority_type); uint16 GetParentSpellType(uint16 spell_type); bool IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id); inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spell_type); bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id); + bool IsValidBotSpellCategory(uint8 setting_type); + static std::string GetBotSpellCategoryName(uint8 setting_type); + static uint16 GetBotSpellCategoryIDByShortName(std::string setting_string); + void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); + uint16 GetSpellByAA(int id, AA::Rank*& rank); - bool HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar); - - void CheckBotSpells(); - void MapSpellTypeLevels(); + // Spell Type + static uint16 GetSpellTypeIDByShortName(std::string spellType_string); + static std::string GetSpellTypeNameByID(uint16 spell_type); + static std::string GetSpellTypeShortNameByID(uint16 spell_type); + bool IsValidSubType(uint16 sub_type); + static std::string GetSubTypeNameByID(uint16 sub_type); [[nodiscard]] int GetMaxBuffSlots() const final { return EQ::spells::LONG_BUFFS; } [[nodiscard]] int GetMaxSongSlots() const final { return EQ::spells::SHORT_BUFFS; } @@ -693,7 +747,6 @@ class Bot : public NPC { static BotSpell GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_type = BotSpellTypes::RegularHeal); static Mob* GetFirstIncomingMobToMez(Bot* caster, int16 spell_id, uint16 spell_type, bool AE); - bool IsValidMezTarget(Mob* owner, Mob* npc, uint16 spell_id); static BotSpell GetBestBotSpellForMez(Bot* caster, uint16 spell_type = BotSpellTypes::Mez); static BotSpell GetBestBotMagicianPetSpell(Bot* caster, uint16 spell_type = BotSpellTypes::Pet); static std::string GetBotMagicianPetType(Bot* caster); @@ -968,10 +1021,6 @@ class Bot : public NPC { // New accessors for BotDatabase access bool DeleteBot(); std::vector GetBotTimers() { return bot_timers; } - void SetBotTimers(std::vector timers) { bot_timers = timers; } - std::vector GetBotBlockedBuffs() { return bot_blocked_buffs; } - void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } - const std::map>& GetCommandedSpellTypesMinLevels() { return commanded_spells_min_level; } uint32 GetLastZoneID() const { return _lastZoneId; } int32 GetBaseAC() const { return _baseAC; } int32 GetBaseATK() const { return _baseATK; } @@ -1020,6 +1069,7 @@ class Bot : public NPC { Mob* tar, float tar_distance ); + bool TargetValidation(Mob* other); bool PullingFlagChecks(Client* bot_owner); bool ReturningFlagChecks(Client* bot_owner, Mob* leash_owner, float fm_distance); @@ -1158,7 +1208,9 @@ class Bot : public NPC { bool _hasLoS; bool _commandedSpell; bool _pullingSpell; - + + bool _illusionBlock; + std::vector m_bot_spell_settings; std::vector _spellTargetList; std::vector _groupSpellTargetList; Raid* _storedRaid; diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index 2398538cc8..bc5c8c45d8 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -907,9 +907,9 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { } popup_text += DialogueWindow::TableRow( - DialogueWindow::TableCell(DialogueWindow::ColorMessage(forest_green, c->GetSpellTypeNameByID(i))) + DialogueWindow::TableCell(DialogueWindow::ColorMessage(forest_green, Bot::GetSpellTypeNameByID(i))) + - DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, c->GetSpellTypeShortNameByID(i)))) + DialogueWindow::TableCell((!arg0.compare("^spelltypeids") ? DialogueWindow::ColorMessage(slate_blue, std::to_string(i)) : DialogueWindow::ColorMessage(slate_blue, Bot::GetSpellTypeShortNameByID(i)))) ); } diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index 8aa7673689..b604ef59d6 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -251,7 +251,7 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) fmt::format( "{} add 202", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ) }; p.examples_two = diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index a8346a267c..186ef39587 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1739,7 +1739,7 @@ void bot_command_toggle_ranged(Client *c, const Seperator *sep) fmt::format( "{} says, 'I {} ranged.'", first_found->GetCleanName(), - first_found->GetIllusionBlock() ? "am now" : "am no longer" + first_found->IsBotRanged() ? "am now" : "am no longer" ).c_str() ); } @@ -1904,7 +1904,7 @@ void bot_command_toggle_helm(Client *c, const Seperator *sep) fmt::format( "{} says, 'I {} show my helm.'", first_found->GetCleanName(), - first_found->GetIllusionBlock() ? "will now" : "will no longer" + first_found->GetShowHelm() ? "will now" : "will no longer" ).c_str() ); } diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 74176788ac..6f5c13c266 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -34,7 +34,7 @@ void bot_command_cast(Client* c, const Seperator* sep) fmt::format( "{} {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( "{} {}", @@ -208,7 +208,7 @@ void bot_command_cast(Client* c, const Seperator* sep) if (sep->IsNumber(1)) { spell_type = atoi(sep->arg[1]); - if (!c->IsValidSpellType(spell_type)) { + if (!Bot::IsValidBotSpellType(spell_type)) { c->Message( Chat::Yellow, fmt::format( @@ -223,8 +223,8 @@ void bot_command_cast(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -347,7 +347,7 @@ void bot_command_cast(Client* c, const Seperator* sep) fmt::format( "[{}] is an invalid target. {} requires a pet to be targeted.", tar->GetCleanName(), - tar->GetSpellTypeNameByID(spell_type) + Bot::GetSpellTypeNameByID(spell_type) ).c_str() ); @@ -589,10 +589,10 @@ void bot_command_cast(Client* c, const Seperator* sep) } else { if (sub_type == UINT16_MAX) { - type = c->GetSpellTypeNameByID(spell_type); + type = Bot::GetSpellTypeNameByID(spell_type); } else { - type = c->GetSubTypeNameByID(sub_type); + type = Bot::GetSubTypeNameByID(sub_type); } } diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 53d03486c2..66564926a1 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -19,7 +19,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only" }; - p.example_format = { fmt::format("{} [from] [to] [option]", sep->arg[0]) }; + p.example_format = { fmt::format("{} [from] [to] [option] [optional: spelltype id/short name]", sep->arg[0]) }; p.examples_one = { "To copy all settings from BotA to BotB:", @@ -31,12 +31,12 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) fmt::format( "{} BotA BotB spelltypesettings {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( "{} BotA BotB spelltypesettings {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), }; p.examples_three = @@ -52,7 +52,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) ), }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "all, misc, spellsettings, spelltypesettings, holds, delays, minthresholds, maxthresholds, minmanapct, maxmanapct, minhppct, maxhppct, idlepriority, engagedpriority, pursuepriority, aggrochecks, targetcounts, blockedbuffs, blockedpetbuffs" }; + p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, spellresistlimits, blockedbuffs, blockedpetbuffs" }; p.options_one = { "[spellsettings] will copy ^spellsettings options", @@ -109,7 +109,8 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) "spellengagedpriority", "spellpursuepriority", "spellaggrochecks", - "spelltargetcounts", + "spelltargetcounts", + "spellresistlimits", "blockedbuffs", "blockedpetbuffs" }; @@ -117,7 +118,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) if (sep->IsNumber(spell_type_arg_int)) { spell_type = atoi(sep->arg[spell_type_arg_int]); - if (!c->IsValidSpellType(spell_type)) { + if (!Bot::IsValidBotSpellType(spell_type)) { c->Message( Chat::Yellow, fmt::format( @@ -132,8 +133,8 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) } } else if (!spell_type_arg.empty()) { - if (c->GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(spell_type_arg); + if (Bot::GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(spell_type_arg); } else { c->Message( @@ -152,7 +153,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) for (int i = 0; i < options.size(); i++) { if (sep->arg[3] == options[i]) { - setting_type = c->GetBotSpellCategoryIDByShortName(sep->arg[3]); + setting_type = Bot::GetBotSpellCategoryIDByShortName(sep->arg[3]); valid_option = true; break; } @@ -236,7 +237,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) } } - output = from->GetBotSpellCategoryName(setting_type); + output = Bot::GetBotSpellCategoryName(setting_type); } else { if (!strcasecmp(sep->arg[3], "misc")) { @@ -355,7 +356,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) ( spell_type != UINT16_MAX ? fmt::format(" [{}] ", - c->GetSpellTypeNameByID(spell_type) + Bot::GetSpellTypeNameByID(spell_type) ) : " " ), diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index a0dd3f01cf..36fc7a060e 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -13,7 +13,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) p.description = { "Restores a bot's setting(s) to defaults" }; p.notes = { "- You can put a spell type ID or shortname after any option except [all], [misc] and [spellsettings] to restore that specifc spell type only"}; - p.example_format = { fmt::format("{} [option] [actionable]", sep->arg[0]) }; + p.example_format = { fmt::format("{} [option] [optional: spelltype id/short name] [actionable]", sep->arg[0]) }; p.examples_one = { "To restore delays for Clerics:", @@ -29,7 +29,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) fmt::format( "{} delays {} byname BotA", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare) ), fmt::format( "{} delays {} byname BotA", @@ -38,7 +38,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) ) }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts" }; + p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts, spellresistlimits" }; p.options_one = { "[spellsettings] will restore ^spellsettings options", @@ -99,7 +99,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep) "spellengagedpriority", "spellpursuepriority", "spellaggrochecks", - "spelltargetcounts" + "spelltargetcounts", + "spellresistlimits" }; if (sep->IsNumber(spell_type_arg_int)) { @@ -122,8 +123,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep) ++ab_arg; } else if (!spell_type_arg.empty()) { - if (c->GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(spell_type_arg); + if (Bot::GetSpellTypeIDByShortName(spell_type_arg) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(spell_type_arg); } else { c->Message( @@ -143,7 +144,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) for (int i = 0; i < options.size(); i++) { if (sep->arg[1] == options[i]) { - setting_type = c->GetBotSpellCategoryIDByShortName(sep->arg[1]); + setting_type = Bot::GetBotSpellCategoryIDByShortName(sep->arg[1]); valid_option = true; break; @@ -202,7 +203,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } } - output = (spell_type != UINT16_MAX ? c->GetSpellTypeNameByID(spell_type) : ""); + output = (spell_type != UINT16_MAX ? Bot::GetSpellTypeNameByID(spell_type) : ""); output += sep->arg[3]; } else if (!strcasecmp(sep->arg[1], "misc")) { @@ -448,7 +449,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) ( spell_type != UINT16_MAX ? fmt::format("My [{}] ", - c->GetSpellTypeNameByID(spell_type) + Bot::GetSpellTypeNameByID(spell_type) ) : "My " ), @@ -465,7 +466,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) ( spell_type != UINT16_MAX ? fmt::format(" [{}] ", - c->GetSpellTypeNameByID(spell_type) + Bot::GetSpellTypeNameByID(spell_type) ) : " " ), diff --git a/zone/bot_commands/spell_aggro_checks.cpp b/zone/bot_commands/spell_aggro_checks.cpp index 1c5a34e02c..41ae404447 100644 --- a/zone/bot_commands/spell_aggro_checks.cpp +++ b/zone/bot_commands/spell_aggro_checks.cpp @@ -23,7 +23,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} {} 1 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( "{} {} 1 spawned", @@ -37,7 +37,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} {} 0 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare), + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare), Class::ShadowKnight ), fmt::format( @@ -53,7 +53,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT) ), fmt::format( "{} {} current spawned", @@ -109,8 +109,8 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -185,7 +185,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] aggro check is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypeAggroCheck(spell_type) ? "enabled" : "disabled" ).c_str() ); @@ -202,7 +202,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] aggro check was [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypeAggroCheck(spell_type) ? "enabled" : "disabled" ).c_str() ); @@ -213,7 +213,7 @@ void bot_command_spell_aggro_checks(Client* c, const Seperator* sep) fmt::format( "{} of your bots [{}] their [{}] aggro check.", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ? "enabled" : "disabled" ).c_str() ); diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index a284316604..1f0f19fa69 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -30,7 +30,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { fmt::format( "{} {} 8000 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT), + Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT), Class::Necromancer ), fmt::format( @@ -46,7 +46,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { fmt::format( "{} {} 2500 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals), + Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals), Class::Warrior ), fmt::format( @@ -62,7 +62,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( "{} {} current spawned", @@ -127,8 +127,8 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); if (clientSetting && !IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type for clients."); @@ -219,7 +219,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { fmt::format( "{} says, 'My [{}] spell delay is currently [{}] seconds.'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); @@ -237,7 +237,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { fmt::format( "{} says, 'My [{}] spell delay was set to [{}] seconds.'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); @@ -248,7 +248,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { fmt::format( "{} of your bots set their [{}] spell delay to [{}] seconds.", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value / 1000.00 ).c_str() ); @@ -261,7 +261,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { Chat::Green, fmt::format( "Your [{}] spell delay is currently [{}] seconds.", - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), c->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); @@ -273,7 +273,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { Chat::Green, fmt::format( "Your [{}] spell delay was set to [{}] seconds.", - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), c->GetSpellDelay(spell_type) / 1000.00 ).c_str() ); diff --git a/zone/bot_commands/spell_engaged_priority.cpp b/zone/bot_commands/spell_engaged_priority.cpp index d013661788..2d6d0b17b0 100644 --- a/zone/bot_commands/spell_engaged_priority.cpp +++ b/zone/bot_commands/spell_engaged_priority.cpp @@ -28,7 +28,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} {} 1 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Slow), + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Slow), Class::Shaman ), fmt::format( @@ -44,7 +44,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} {} 0 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare) ), fmt::format( "{} {} 0 spawned", @@ -58,7 +58,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Dispel) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Dispel) ), fmt::format( "{} {} current spawned", @@ -106,8 +106,8 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else if (!arg1.compare("list")) { ++ab_arg; @@ -186,7 +186,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] engaged cast priority is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged) ).c_str() ); @@ -200,7 +200,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] engaged cast priority for is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(current_cast.spellType), + Bot::GetSpellTypeNameByID(current_cast.spellType), (current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority)) ).c_str() ); @@ -228,7 +228,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] engaged cast priority was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Engaged) ).c_str() ); @@ -239,7 +239,7 @@ void bot_command_spell_engaged_priority(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] engaged cast priority to [{}].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index 5b5815fb55..d83274d39e 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -24,7 +24,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) fmt::format( "{} {} 1 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT) ), fmt::format( "{} {} 1 spawned", @@ -38,7 +38,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT) ), fmt::format( "{} {} current spawned", @@ -85,8 +85,8 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -156,7 +156,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] spell hold is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellHold(spell_type) ? "enabled" : "disabled" ).c_str() ); @@ -173,7 +173,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] spell hold was [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellHold(spell_type) ? "enabled" : "disabled" ).c_str() ); @@ -185,7 +185,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) "{} of your bots [{}] their [{}] spell hold.", success_count, type_value ? "enabled" : "disabled", - c->GetSpellTypeNameByID(spell_type) + Bot::GetSpellTypeNameByID(spell_type) ).c_str() ); } diff --git a/zone/bot_commands/spell_idle_priority.cpp b/zone/bot_commands/spell_idle_priority.cpp index 4ae8276390..a37e035df7 100644 --- a/zone/bot_commands/spell_idle_priority.cpp +++ b/zone/bot_commands/spell_idle_priority.cpp @@ -28,7 +28,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} {} 3 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals), + Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals), Class::Cleric ), fmt::format( @@ -44,7 +44,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} {} 0 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Cure) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Cure) ), fmt::format( "{} {} 0 spawned", @@ -58,7 +58,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Buff) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Buff) ), fmt::format( "{} {} current spawned", @@ -106,8 +106,8 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else if (!arg1.compare("list")) { ++ab_arg; @@ -186,7 +186,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] idle cast priority is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Idle) ).c_str() ); @@ -200,7 +200,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] idle cast priority for is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(current_cast.spellType), + Bot::GetSpellTypeNameByID(current_cast.spellType), (current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority)) ).c_str() ); @@ -228,7 +228,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] idle cast priority was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Idle) ).c_str() ); @@ -239,7 +239,7 @@ void bot_command_spell_idle_priority(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] idle cast priority to [{}].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_commands/spell_max_hp_pct.cpp b/zone/bot_commands/spell_max_hp_pct.cpp index 5bc42a7adc..249e9179d8 100644 --- a/zone/bot_commands/spell_max_hp_pct.cpp +++ b/zone/bot_commands/spell_max_hp_pct.cpp @@ -23,7 +23,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 100 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare) ), fmt::format( "{} {} 10 spawned", @@ -37,7 +37,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 30 byname BotA", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) ), fmt::format( "{} {} 30 byname BotA", @@ -51,7 +51,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun) ), fmt::format( "{} {} current spawned", @@ -98,8 +98,8 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -174,7 +174,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum HP is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypeMaxHPLimit(spell_type) ).c_str() ); @@ -191,7 +191,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum HP was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypeMaxHPLimit(spell_type) ).c_str() ); @@ -202,7 +202,7 @@ void bot_command_spell_max_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] maximum HP to [{}%%].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_commands/spell_max_mana_pct.cpp b/zone/bot_commands/spell_max_mana_pct.cpp index 0e526ab8ea..ab42ce8a5e 100644 --- a/zone/bot_commands/spell_max_mana_pct.cpp +++ b/zone/bot_commands/spell_max_mana_pct.cpp @@ -23,7 +23,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 10 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare) ), fmt::format( "{} {} 10 spawned", @@ -37,7 +37,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 90 byname BotA", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) ), fmt::format( "{} {} 90 byname BotA", @@ -51,7 +51,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun) ), fmt::format( "{} {} current spawned", @@ -98,8 +98,8 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -174,7 +174,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum mana is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypeMaxManaLimit(spell_type) ).c_str() ); @@ -191,7 +191,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] maximum mana was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypeMaxManaLimit(spell_type) ).c_str() ); @@ -202,7 +202,7 @@ void bot_command_spell_max_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] maximum mana to [{}%%].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index ec4faca4e5..4ef30d2ad0 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -30,7 +30,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} {} 99 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare) ), fmt::format( "{} {} 99 spawned", @@ -44,7 +44,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} {} 99 byname Enchbot", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff) ), fmt::format( "{} {} 99 byname Enchbot", @@ -58,7 +58,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( "{} {} current spawned", @@ -123,8 +123,8 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); if (clientSetting && !IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type for clients."); @@ -215,7 +215,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} says, 'My [{}] maximum threshold is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellMaxThreshold(spell_type) ).c_str() ); @@ -233,7 +233,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} says, 'My [{}] maximum threshold was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellMaxThreshold(spell_type) ).c_str() ); @@ -244,7 +244,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} of your bots set their [{}] maximum threshold to [{}%%].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); @@ -257,7 +257,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { Chat::Green, fmt::format( "Your [{}] maximum threshold is currently [{}%%].", - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), c->GetSpellMaxThreshold(spell_type) ).c_str() ); @@ -269,7 +269,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { Chat::Green, fmt::format( "Your [{}] maximum threshold was set to [{}%%].", - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), c->GetSpellMaxThreshold(spell_type) ).c_str() ); diff --git a/zone/bot_commands/spell_min_hp_pct.cpp b/zone/bot_commands/spell_min_hp_pct.cpp index 11da31ac89..61d6313e37 100644 --- a/zone/bot_commands/spell_min_hp_pct.cpp +++ b/zone/bot_commands/spell_min_hp_pct.cpp @@ -23,7 +23,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 10 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare) ), fmt::format( "{} {} 10 spawned", @@ -37,7 +37,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 30 byname BotA", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) ), fmt::format( "{} {} 30 byname BotA", @@ -51,7 +51,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun) ), fmt::format( "{} {} current spawned", @@ -98,8 +98,8 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -174,7 +174,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum HP is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypeMinHPLimit(spell_type) ).c_str() ); @@ -191,7 +191,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum HP was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypeMinHPLimit(spell_type) ).c_str() ); @@ -202,7 +202,7 @@ void bot_command_spell_min_hp_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] minimum HP to [{}%%].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_commands/spell_min_mana_pct.cpp b/zone/bot_commands/spell_min_mana_pct.cpp index 4fa3405dfc..71672cee9e 100644 --- a/zone/bot_commands/spell_min_mana_pct.cpp +++ b/zone/bot_commands/spell_min_mana_pct.cpp @@ -23,7 +23,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 10 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Snare) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Snare) ), fmt::format( "{} {} 10 spawned", @@ -37,7 +37,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} {} 30 byname BotA", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) ), fmt::format( "{} {} 30 byname BotA", @@ -51,7 +51,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Stun) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Stun) ), fmt::format( "{} {} current spawned", @@ -98,8 +98,8 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -174,7 +174,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum mana is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypeMinManaLimit(spell_type) ).c_str() ); @@ -191,7 +191,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] minimum mana was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypeMinManaLimit(spell_type) ).c_str() ); @@ -202,7 +202,7 @@ void bot_command_spell_min_mana_pct(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] minimum mana to [{}%%].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index 6fcd43beda..85d72a553e 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -30,7 +30,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} {} 10 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff) ), fmt::format( "{} {} 10 spawned", @@ -44,7 +44,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} {} 15 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::DOT), + Bot::GetSpellTypeShortNameByID(BotSpellTypes::DOT), Class::Druid ), fmt::format( @@ -60,7 +60,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::FastHeals) ), fmt::format( "{} {} current spawned", @@ -125,8 +125,8 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); if (clientSetting && !IsClientBotSpellType(spell_type)) { c->Message(Chat::Yellow, "Invalid spell type for clients."); @@ -217,7 +217,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} says, 'My [{}] minimum threshold is currently [{}%%].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellMinThreshold(spell_type) ).c_str() ); @@ -235,7 +235,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} says, 'My [{}] minimum threshold was set to [{}%%].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellMinThreshold(spell_type) ).c_str() ); @@ -246,7 +246,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { fmt::format( "{} of your bots set their [{}] minimum threshold to [{}%%].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); @@ -259,7 +259,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { Chat::Green, fmt::format( "Your [{}] minimum threshold is currently [{}%%].", - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), c->GetSpellMinThreshold(spell_type) ).c_str() ); @@ -271,7 +271,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { Chat::Green, fmt::format( "Your [{}] minimum threshold was set to [{}%%].", - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), c->GetSpellMinThreshold(spell_type) ).c_str() ); diff --git a/zone/bot_commands/spell_pursue_priority.cpp b/zone/bot_commands/spell_pursue_priority.cpp index feb08fbe22..5267012141 100644 --- a/zone/bot_commands/spell_pursue_priority.cpp +++ b/zone/bot_commands/spell_pursue_priority.cpp @@ -28,7 +28,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} {} 1 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Nuke) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke) ), fmt::format( "{} {} 1 spawned", @@ -42,7 +42,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} {} 0 byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Cure), + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Cure), Class::Shaman ), fmt::format( @@ -58,7 +58,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::Buff) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Buff) ), fmt::format( "{} {} current spawned", @@ -106,8 +106,8 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else if (!arg1.compare("list")) { ++ab_arg; @@ -186,7 +186,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] pursue cast priority is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue) ).c_str() ); @@ -200,7 +200,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] pursue cast priority for is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(current_cast.spellType), + Bot::GetSpellTypeNameByID(current_cast.spellType), (current_cast.priority == 0 ? "disabled (0)" : std::to_string(current_cast.priority)) ).c_str() ); @@ -228,7 +228,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] pursue cast priority was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypePriority(spell_type, BotPriorityCategories::Pursue) ).c_str() ); @@ -239,7 +239,7 @@ void bot_command_spell_pursue_priority(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] pursue cast priority to [{}].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_commands/spell_target_count.cpp b/zone/bot_commands/spell_target_count.cpp index 322de0d2d6..ad938d9b52 100644 --- a/zone/bot_commands/spell_target_count.cpp +++ b/zone/bot_commands/spell_target_count.cpp @@ -23,7 +23,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} {} 5 spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::AEMez) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::AEMez) ), fmt::format( "{} {} 5 spawned", @@ -35,14 +35,16 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) { "To set Wizards to require 5 targets for AENukes:", fmt::format( - "{} {} 3 byname BotA", + "{} {} byclass {}", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::AENukes) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::AENukes), + Class::Wizard ), fmt::format( - "{} {} 3 byname BotA", + "{} {} byclass {}", sep->arg[0], - BotSpellTypes::AENukes + BotSpellTypes::AENukes, + Class::Wizard ) }; p.examples_three = @@ -51,7 +53,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} {} current spawned", sep->arg[0], - c->GetSpellTypeShortNameByID(BotSpellTypes::AESlow) + Bot::GetSpellTypeShortNameByID(BotSpellTypes::AESlow) ), fmt::format( "{} {} current spawned", @@ -98,8 +100,8 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) } } else { - if (c->GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { - spell_type = c->GetSpellTypeIDByShortName(arg1); + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); } else { c->Message( @@ -119,6 +121,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) if (sep->IsNumber(2)) { type_value = atoi(sep->arg[2]); ++ab_arg; + if (type_value < 1 || type_value > 100) { c->Message(Chat::Yellow, "You must enter a value between 1-100."); @@ -174,7 +177,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] target count is currently [{}].'", my_bot->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), my_bot->GetSpellTypeAEOrGroupTargetCount(spell_type) ).c_str() ); @@ -191,7 +194,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} says, 'My [{}] target count was set to [{}].'", first_found->GetCleanName(), - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), first_found->GetSpellTypeAEOrGroupTargetCount(spell_type) ).c_str() ); @@ -202,7 +205,7 @@ void bot_command_spell_target_count(Client* c, const Seperator* sep) fmt::format( "{} of your bots set their [{}] target count to [{}].", success_count, - c->GetSpellTypeNameByID(spell_type), + Bot::GetSpellTypeNameByID(spell_type), type_value ).c_str() ); diff --git a/zone/bot_database.cpp b/zone/bot_database.cpp index 6acd29f00c..d4131a3493 100644 --- a/zone/bot_database.cpp +++ b/zone/bot_database.cpp @@ -2241,7 +2241,7 @@ bool BotDatabase::LoadBotSettings(Mob* m) if (e.setting_type == BotSettingCategories::BaseSetting) { LogBotSettings("[{}] says, 'Loading {} [{}] - setting to [{}].", m->GetCleanName(), - m->GetBotSettingCategoryName(e.setting_type), + Bot::GetBotSettingCategoryName(e.setting_type), e.setting_type, e.value ); @@ -2249,15 +2249,20 @@ bool BotDatabase::LoadBotSettings(Mob* m) else { LogBotSettings("[{}] says, 'Loading {} [{}], {} [{}] - setting to [{}].", m->GetCleanName(), - m->GetBotSpellCategoryName(e.setting_type), + Bot::GetBotSpellCategoryName(e.setting_type), e.setting_type, - m->GetSpellTypeNameByID(e.setting_id), + Bot::GetSpellTypeNameByID(e.setting_id), e.setting_id, e.value ); } - m->SetBotSetting(e.setting_type, e.setting_id, e.value); + if (m->IsClient()) { + m->CastToClient()->SetBotSetting(e.setting_type, e.setting_id, e.value); + } + else { + m->CastToBot()->SetBotSetting(e.setting_type, e.setting_id, e.value); + } } return true; @@ -2294,7 +2299,7 @@ bool BotDatabase::SaveBotSettings(Mob* m) BotSettingsRepository::DeleteWhere(database, query); std::vector v; - + if (m->IsBot()) { uint8 bot_stance = m->CastToBot()->GetBotStance(); @@ -2307,13 +2312,13 @@ bool BotDatabase::SaveBotSettings(Mob* m) .setting_id = static_cast(i), .setting_type = static_cast(BotSettingCategories::BaseSetting), .value = static_cast(m->CastToBot()->GetBotBaseSetting(i)), - .category_name = m->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), - .setting_name = m->GetBotSettingCategoryName(i) + .category_name = Bot::GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = Bot::GetBotSettingCategoryName(i) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSettingCategoryName(i), i, e.value, m->CastToBot()->GetDefaultBotBaseSetting(i)); } } @@ -2327,39 +2332,39 @@ bool BotDatabase::SaveBotSettings(Mob* m) .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToBot()->GetSetting(i, x), - .category_name = m->GetBotSpellCategoryName(i), - .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) + .category_name = Bot::GetBotSpellCategoryName(i), + .setting_name = Bot::GetSpellTypeNameByID(x) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, bot_stance)); + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToBot()->GetDefaultSetting(i, x, bot_stance)); } } } } if (m->IsClient()) { - if (m->CastToClient()->GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock) != m->GetIllusionBlock()) { // Only illusion block supported + if (m->CastToClient()->GetDefaultBotSettings(BotSettingCategories::BaseSetting, BotBaseSettings::IllusionBlock) != m->CastToClient()->GetIllusionBlock()) { // Only illusion block supported auto e = BotSettingsRepository::BotSettings{ .character_id = character_id, .bot_id = bot_id, .stance = stance_id, .setting_id = static_cast(BotBaseSettings::IllusionBlock), .setting_type = static_cast(BotSettingCategories::BaseSetting), - .value = m->GetIllusionBlock(), - .category_name = m->GetBotSpellCategoryName(BotSettingCategories::BaseSetting), - .setting_name = m->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock) + .value = m->CastToClient()->GetIllusionBlock(), + .category_name = Bot::GetBotSpellCategoryName(BotSettingCategories::BaseSetting), + .setting_name = Bot::GetBotSettingCategoryName(BotBaseSettings::IllusionBlock) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->GetIllusionBlock()); + LogBotSettings("{} says, 'Saving {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSettingCategoryName(BotBaseSettings::IllusionBlock), BotBaseSettings::IllusionBlock, e.value, m->CastToClient()->GetIllusionBlock()); } for (uint16 i = BotSettingCategories::START_CLIENT; i <= BotSettingCategories::END_CLIENT; ++i) { for (uint16 x = BotSpellTypes::START; x <= BotSpellTypes::END; ++x) { - LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); + LogBotSettings("{} says, 'Checking {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, m->CastToClient()->GetBotSetting(i, x), m->CastToClient()->GetDefaultBotSettings(i, x)); if (m->CastToClient()->GetBotSetting(i, x) != m->CastToClient()->GetDefaultBotSettings(i, x)) { auto e = BotSettingsRepository::BotSettings{ .character_id = character_id, @@ -2368,13 +2373,13 @@ bool BotDatabase::SaveBotSettings(Mob* m) .setting_id = static_cast(x), .setting_type = static_cast(i), .value = m->CastToClient()->GetBotSetting(i, x), - .category_name = m->GetBotSpellCategoryName(i), - .setting_name = m->CastToBot()->GetSpellTypeNameByID(x) + .category_name = Bot::GetBotSpellCategoryName(i), + .setting_name = Bot::GetSpellTypeNameByID(x) }; v.emplace_back(e); - LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), m->GetBotSpellCategoryName(i), m->CastToBot()->GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); + LogBotSettings("{} says, 'Saving {} {} [{}] - set to [{}] default [{}].'", m->GetCleanName(), Bot::GetBotSpellCategoryName(i), Bot::GetSpellTypeNameByID(x), x, e.value, m->CastToClient()->GetDefaultBotSettings(i, x)); } } } diff --git a/zone/bot_structs.h b/zone/bot_structs.h index c40e820b8d..e3bcc86789 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -110,6 +110,27 @@ struct BotTimer { uint32 item_id; }; +struct BotSpellSettings { + uint16 spell_type; // type ID of bot category + std::string short_name; // type short name of bot category + std::string name; // type name of bot category + bool hold; // 0 = allow spell type, 1 = hold spell type + uint16 delay; // delay between casts of spell type, 1ms-60,000ms + uint8 min_threshold; // minimum target health threshold to allow casting of spell type + uint8 max_threshold; // maximum target health threshold to allow casting of spell type + uint16 resist_limit; // resist limit to skip spell type + bool aggro_check; // whether or not to check for possible aggro before casting + uint8 min_mana_pct; // lower mana percentage limit to allow spell cast + uint8 max_mana_pct; // upper mana percentage limit to allow spell cast + uint8 min_hp_pct; // lower HP percentage limit to allow spell cast + uint8 max_hp_pct; // upper HP percentage limit to allow spell cast + uint16 idle_priority; // idle priority of the spell type + uint16 engaged_priority; // engaged priority of the spell type + uint16 pursue_priority; // pursue priority of the spell type + uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type + Timer recast_timer; // recast timer based off delay +}; + struct BotSpellTypeOrder { uint16 spellType; uint16 priority; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 25982f7f49..344669a978 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -3013,7 +3013,7 @@ void Bot::MapSpellTypeLevels() { auto end = std::max({ BotSpellTypes::END, BotSpellTypes::COMMANDED_END, BotSpellTypes::DISCIPLINE_END }); for (int i = start; i <= end; ++i) { - if (!Bot::IsValidSpellType(i)) { + if (!Bot::IsValidBotSpellType(i)) { continue; } @@ -3036,7 +3036,7 @@ void Bot::MapSpellTypeLevels() { if ( !EQ::ValueWithin(bot_class, Class::Warrior, Class::Berserker) || - !Bot::IsValidSpellType(spell_type) + !Bot::IsValidBotSpellType(spell_type) ) { continue; } diff --git a/zone/client.h b/zone/client.h index 52e4788b6a..3e4162d405 100644 --- a/zone/client.h +++ b/zone/client.h @@ -74,6 +74,8 @@ namespace EQ #include "../common/repositories/buyer_buy_lines_repository.h" #include "../common/repositories/character_evolving_items_repository.h" +#include "bot_structs.h" + #ifdef _WINDOWS // since windows defines these within windef.h (which windows.h include) // we are required to undefine these to use min and max from @@ -2279,14 +2281,30 @@ class Client : public Mob void LoadDefaultBotSettings(); int GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting); int GetBotSetting(uint8 setting_type, uint16 bot_setting); - void SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value); + void SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value); + + uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } + inline void SetSpellDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } + inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } + inline void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].min_threshold = threshold_value; } + inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } + inline void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } + inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } + void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } + + void SetIllusionBlock(bool value) { _illusionBlock = value; } + bool GetIllusionBlock() const { return _illusionBlock; } private: bool bot_owner_options[_booCount]; bool m_bot_pulling; bool m_bot_precombat; - uint32 bot_assistee; + std::vector m_bot_spell_settings; + bool _illusionBlock; bool CanTradeFVNoDropItem(); void SendMobPositions(); diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index e10b3d42a8..1711503f99 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -231,9 +231,8 @@ void Client::LoadDefaultBotSettings() { BotSpellSettings t; t.spell_type = i; - t.short_name = GetSpellTypeShortNameByID(i); - t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i); + t.short_name = Bot::GetSpellTypeShortNameByID(i); + t.name = Bot::GetSpellTypeNameByID(i); t.delay = GetDefaultSpellDelay(i); t.min_threshold = GetDefaultSpellMinThreshold(i); t.max_threshold = GetDefaultSpellMaxThreshold(i); @@ -242,16 +241,14 @@ void Client::LoadDefaultBotSettings() { m_bot_spell_settings.push_back(t); LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.short_name, t.spell_type); - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); + LogBotSettingsDetail("{} says, 'Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); } } int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) { switch (setting_type) { case BotSettingCategories::BaseSetting: - return false; - case BotSettingCategories::SpellHold: - return GetDefaultSpellHold(bot_setting); + return false; // only setting supported currently is illusion block case BotSettingCategories::SpellDelay: return GetDefaultSpellDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: @@ -263,8 +260,8 @@ int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) { int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) { switch (setting_type) { - case BotSettingCategories::SpellHold: - return GetSpellHold(bot_setting); + case BotSettingCategories::BaseSetting: + return GetIllusionBlock(); // only setting supported currently case BotSettingCategories::SpellDelay: return GetSpellDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: @@ -277,10 +274,7 @@ int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) { void Client::SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value) { switch (setting_type) { case BotSettingCategories::BaseSetting: - SetBaseSetting(bot_setting, setting_value); - break; - case BotSettingCategories::SpellHold: - SetSpellHold(bot_setting, setting_value); + SetIllusionBlock(setting_value); // only setting supported currently break; case BotSettingCategories::SpellDelay: SetSpellDelay(bot_setting, setting_value); @@ -353,3 +347,125 @@ void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) return; } + +uint16 Client::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + return 1500; + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + return 2500; + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + return 4000; + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + return 8000; + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + return 22000; + case BotSpellTypes::Cure: + return 2000; + case BotSpellTypes::GroupCures: + return 3000; + case BotSpellTypes::PetCures: + return 5000; + default: + return 1; + } +} + +uint8 Client::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { + switch (spell_type) { + default: + return 0; + } +} + +uint8 Client::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { + uint8 bot_class = GetClass(); + + switch (spell_type) { + case BotSpellTypes::VeryFastHeals: + case BotSpellTypes::PetVeryFastHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 40; + case Stance::Efficient: + default: + return 25; + } + case BotSpellTypes::FastHeals: + case BotSpellTypes::PetFastHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 55; + case Stance::Efficient: + return 35; + default: + return 40; + } + case BotSpellTypes::GroupHeals: + case BotSpellTypes::RegularHeal: + case BotSpellTypes::PetRegularHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 70; + case Stance::Efficient: + return 50; + default: + return 60; + } + case BotSpellTypes::CompleteHeal: + case BotSpellTypes::GroupCompleteHeals: + case BotSpellTypes::PetCompleteHeals: + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 90; + case Stance::Efficient: + return 65; + default: + return 80; + } + case BotSpellTypes::GroupHoTHeals: + case BotSpellTypes::HoTHeals: + case BotSpellTypes::PetHoTHeals: + if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { + return 60; + } + else { + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 95; + case Stance::Efficient: + return 80; + default: + return 90; + } + } + case BotSpellTypes::Buff: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::PetBuffs: + case BotSpellTypes::PetDamageShields: + case BotSpellTypes::PetResistBuffs: + case BotSpellTypes::ResistBuffs: + default: + return 100; + } +} diff --git a/zone/gm_commands/illusion_block.cpp b/zone/gm_commands/illusion_block.cpp index 1715c0f8f7..67c3afa31c 100644 --- a/zone/gm_commands/illusion_block.cpp +++ b/zone/gm_commands/illusion_block.cpp @@ -37,17 +37,17 @@ void command_illusion_block(Client* c, const Seperator* sep) int set_status = atoi(sep->arg[1]); if (set_status == 0 || set_status == 1) { c->SetIllusionBlock(set_status); - c->Message(Chat::White, "Your Illusion Block has been %s.", (set_status ? "enabled" : "disabled")); + c->Message(Chat::Green, "Your Illusion Block has been %s.", (set_status ? "enabled" : "disabled")); } else { - c->Message(Chat::White, "You must enter 0 for disabled or 1 for enabled."); + c->Message(Chat::Yellow, "You must enter 0 for disabled or 1 for enabled."); return; } } else if (!strcasecmp(sep->arg[1], "current")) { - c->Message(Chat::White, "You're currently %s illusions.", (c->GetIllusionBlock() ? "blocking" : "allowing")); + c->Message(Chat::Green, "You're currently %s illusions.", (c->GetIllusionBlock() ? "blocking" : "allowing")); } else { - c->Message(Chat::White, "Incorrect argument, use %s help for a list of options.", sep->arg[0]); + c->Message(Chat::Yellow , "Incorrect argument, use %s help for a list of options.", sep->arg[0]); } } diff --git a/zone/mob.h b/zone/mob.h index cb6f2ff0b4..961a0abfda 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -93,27 +93,6 @@ struct AppearanceStruct { uint8 texture = UINT8_MAX; }; -struct BotSpellSettings { - uint16 spell_type; // type ID of bot category - std::string short_name; // type short name of bot category - std::string name; // type name of bot category - bool hold; // 0 = allow spell type, 1 = hold spell type - uint16 delay; // delay between casts of spell type, 1ms-60,000ms - uint8 min_threshold; // minimum target health threshold to allow casting of spell type - uint8 max_threshold; // maximum target health threshold to allow casting of spell type - uint16 resist_limit; // resist limit to skip spell type - bool aggro_check; // whether or not to check for possible aggro before casting - uint8 min_mana_pct; // lower mana percentage limit to allow spell cast - uint8 max_mana_pct; // upper mana percentage limit to allow spell cast - uint8 min_hp_pct; // lower HP percentage limit to allow spell cast - uint8 max_hp_pct; // upper HP percentage limit to allow spell cast - uint16 idle_priority; // idle priority of the spell type - uint16 engaged_priority; // engaged priority of the spell type - uint16 pursue_priority; // pursue priority of the spell type - uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type - Timer recast_timer; // recast timer based off delay -}; - class DataBucketKey; class Mob : public Entity { public: @@ -231,8 +210,6 @@ class Mob : public Entity { // Bot attack flag Timer bot_attack_flag_timer; - std::vector m_bot_spell_settings; - //Somewhat sorted: needs documenting! //Attack @@ -429,59 +406,6 @@ class Mob : public Entity { virtual bool CheckSpellLevelRestriction(Mob *caster, uint16 spell_id); virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster); - // Bot functions - bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); - virtual bool IsImmuneToBotSpell(uint16 spell_id, Mob* caster); - std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); - inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } - uint16 GetSpellTypeIDByShortName(std::string spellType_string); - bool IsValidBotSpellCategory(uint8 setting_type); - std::string GetBotSpellCategoryName(uint8 setting_type); - uint16 GetBotSpellCategoryIDByShortName(std::string setting_string); - bool IsValidBotBaseSetting(uint16 setting_type); - std::string GetBotSettingCategoryName(uint16 setting_type); - uint16 GetBaseSettingIDByShortName(std::string setting_string); - bool IsValidSpellType(uint16 spell_type); - std::string GetSpellTypeNameByID(uint16 spell_type); - std::string GetSpellTypeShortNameByID(uint16 spell_type); - bool IsValidSubType(uint16 sub_type); - std::string GetSubTypeNameByID(uint16 sub_type); - - bool GetDefaultSpellHold(uint16 spell_type, uint8 stance = Stance::Balanced); - uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); - - inline bool GetSpellHold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].hold; } - void SetSpellHold(uint16 spell_type, bool hold_status); - inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } - void SetSpellDelay(uint16 spell_type, uint16 delay_value); - inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } - void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value); - inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } - void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value); - - inline uint16 GetSpellTypeRecastTimer(uint16 spell_type) { return m_bot_spell_settings[spell_type].recast_timer.GetRemainingTime(); } - void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time); - - uint8 GetHPRatioForSpellType(uint16 spell_type, Mob* tar); - bool GetUltimateSpellHold(uint16 spell_type, Mob* tar); - uint16 GetUltimateSpellDelay(uint16 spell_type, Mob* tar); - bool GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar); - uint8 GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar); - uint8 GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar); - - uint16 GetPetBotSpellType(uint16 spell_type); - - void DisableBotSpellTimers(); - void StartBotSpellTimers(); - - void SetBotSetting(uint8 spell_type, uint16 bot_setting, int setting_value); - void SetBaseSetting(uint16 base_setting, int setting_value); - - void SetIllusionBlock(bool value) { _illusionBlock = value; } - bool GetIllusionBlock() const { return _illusionBlock; } - virtual float GetAOERange(uint16 spell_id); void InterruptSpell(uint16 spellid = SPELL_UNKNOWN); void InterruptSpell(uint16, uint16, uint16 spellid = SPELL_UNKNOWN); @@ -875,7 +799,6 @@ class Mob : public Entity { bool CheckLosCheat(Mob* other); //door skipping checks for LoS bool CheckLosCheatExempt(Mob* other); //exemptions to bypass los bool DoLosChecks(Mob* other); - bool TargetValidation(Mob* other); inline void SetLastLosState(bool value) { last_los_check = value; } inline bool CheckLastLosState() const { return last_los_check; } std::string GetMobDescription(); @@ -1946,14 +1869,6 @@ class Mob : public Entity { bool pause_timer_complete; bool DistractedFromGrid; uint32 m_dont_heal_me_before; - uint32 m_dont_group_heal_me_before; - uint32 m_dont_group_hot_heal_me_before; - uint32 m_dont_regular_heal_me_before; - uint32 m_dont_very_fast_heal_me_before; - uint32 m_dont_fast_heal_me_before; - uint32 m_dont_complete_heal_me_before; - uint32 m_dont_group_complete_heal_me_before; - uint32 m_dont_hot_heal_me_before; uint32 m_dont_buff_me_before; uint32 m_dont_dot_me_before; uint32 m_dont_root_me_before; @@ -1976,9 +1891,6 @@ class Mob : public Entity { //bot attack flags std::vector bot_attack_flags; - //bot related settings - bool _illusionBlock; - glm::vec3 m_TargetRing; GravityBehavior flymode; diff --git a/zone/mob_bot.cpp b/zone/mob_bot.cpp deleted file mode 100644 index 297c9f6a18..0000000000 --- a/zone/mob_bot.cpp +++ /dev/null @@ -1,778 +0,0 @@ -#include "bot.h" -#include "mob.h" - -bool Mob::PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only, bool front_only, bool bypass_los) { - bool Result = false; - - if (target) { - float look_heading = 0; - - min_distance = min_distance; - max_distance = max_distance; - float temp_x = 0; - float temp_y = 0; - float temp_z = target->GetZ(); - float best_z = 0; - auto offset = GetZOffset(); - const float tar_x = target->GetX(); - const float tar_y = target->GetY(); - float tar_distance = 0; - - glm::vec3 temp_z_Position; - glm::vec4 temp_m_Position; - - const uint16 max_iterations_allowed = 50; - uint16 counter = 0; - - while (counter < max_iterations_allowed) { - temp_x = tar_x + zone->random.Real(-max_distance, max_distance); - temp_y = tar_y + zone->random.Real(-max_distance, max_distance); - - temp_z_Position.x = temp_z; - temp_z_Position.y = temp_y; - temp_z_Position.z = temp_z; - best_z = GetFixedZ(temp_z_Position); - - if (best_z != BEST_Z_INVALID) { - temp_z = best_z; - } - else { - counter++; - continue; - } - - temp_m_Position.x = temp_x; - temp_m_Position.y = temp_y; - temp_m_Position.z = temp_z; - - tar_distance = Distance(target->GetPosition(), temp_m_Position); - - if (tar_distance > max_distance || tar_distance < min_distance) { - counter++; - continue; - } - - if (front_only && !InFrontMob(target, temp_x, temp_y)) { - counter++; - continue; - } - else if (behind_only && !BehindMob(target, temp_x, temp_y)) { - counter++; - continue; - } - - if (!bypass_los && CastToBot()->RequiresLoSForPositioning() && !CheckPositioningLosFN(target, temp_x, temp_y, temp_z)) { - counter++; - continue; - } - - Result = true; - break; - } - - if (Result) { - x_dest = temp_x; - y_dest = temp_y; - z_dest = temp_z; - } - } - - return Result; -} - -std::vector Mob::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots, bool no_pets) { - std::vector valid_spell_targets; - - auto is_valid_target = [no_clients, no_bots](Mob* member) { - return member && - ((member->IsClient() && !no_clients) || (member->IsBot() && !no_bots)); - }; - - if (IsRaidGrouped()) { - Raid* raid = IsBot() ? CastToBot()->GetStoredRaid() : GetRaid(); - - if (raid) { - if (entire_raid) { - for (const auto& m : raid->members) { - if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { - valid_spell_targets.emplace_back(m.member); - } - } - } - else { - auto group_name = target ? raid->GetGroup(target->GetName()) : raid->GetGroup(GetName()); - auto raid_group = raid->GetRaidGroupMembers(group_name); - - for (const auto& m : raid_group) { - if (is_valid_target(m.member) && m.group_number != RAID_GROUPLESS) { - valid_spell_targets.emplace_back(m.member); - } - } - } - } - } - else if (IsGrouped()) { - Group* group = GetGroup(); - - if (group) { - for (const auto& m : group->members) { - if (is_valid_target(m)) { - valid_spell_targets.emplace_back(m); - } - } - } - } - else { - valid_spell_targets.emplace_back(this); - } - - return valid_spell_targets; -} - -uint16 Mob::GetSpellTypeIDByShortName(std::string spell_type_string) { - - for (int i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { - return i; - } - } - - for (int i = BotSpellTypes::COMMANDED_START; i <= BotSpellTypes::COMMANDED_END; ++i) { - if (!Strings::ToLower(spell_type_string).compare(GetSpellTypeShortNameByID(i))) { - return i; - } - } - - return UINT16_MAX; -} - -bool Mob::IsValidBotSpellCategory(uint8 setting_type) { - return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END_FULL); -} - -std::string Mob::GetBotSpellCategoryName(uint8 setting_type) { - return IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; -} - -uint16 Mob::GetBotSpellCategoryIDByShortName(std::string setting_string) { - for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { - if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSpellCategoryName(i)))) { - return i; - } - } - - return UINT16_MAX; -} - -bool Mob::IsValidBotBaseSetting(uint16 setting_type) { - return EQ::ValueWithin(setting_type, BotBaseSettings::START_ALL, BotBaseSettings::END); -} - -std::string Mob::GetBotSettingCategoryName(uint16 setting_type) { - return IsValidBotBaseSetting(setting_type) ? botBaseSettings_names[setting_type] : "UNKNOWN SETTING"; -} - -uint16 Mob::GetBaseSettingIDByShortName(std::string setting_string) { - for (int i = BotSettingCategories::START; i <= BotSettingCategories::END; ++i) { - if (!Strings::ToLower(setting_string).compare(Strings::ToLower(GetBotSettingCategoryName(i)))) { - return i; - } - } - - return UINT16_MAX; -} - -bool Mob::IsValidSpellType(uint16 spell_type) { - return ( - EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END) || - EQ::ValueWithin(spell_type, BotSpellTypes::COMMANDED_START, BotSpellTypes::COMMANDED_END) - ); -} - -std::string Mob::GetSpellTypeShortNameByID(uint16 spell_type) { - return IsValidSpellType(spell_type) ? spellType_shortNames[spell_type] : "UNKNOWN SPELLTYPE"; -} - -std::string Mob::GetSpellTypeNameByID(uint16 spell_type) { - return IsValidSpellType(spell_type) ? spellType_names[spell_type] : "UNKNOWN SPELLTYPE"; -} - -bool Mob::IsValidSubType(uint16 sub_type) { - return EQ::ValueWithin(sub_type, CommandedSubTypes::START, CommandedSubTypes::END); -} - -std::string Mob::GetSubTypeNameByID(uint16 sub_type) { - return IsValidSpellType(sub_type) ? botSubType_names[sub_type] : "UNKNOWN SUBTYPE"; -} - -bool Mob::GetDefaultSpellHold(uint16 spell_type, uint8 stance) { - uint8 bot_class = GetClass(); - - switch (spell_type) { - case BotSpellTypes::FastHeals: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::Pet: - case BotSpellTypes::Escape: - case BotSpellTypes::Lifetap: - case BotSpellTypes::Buff: - case BotSpellTypes::PetBuffs: - case BotSpellTypes::InCombatBuff: - case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::DamageShields: - case BotSpellTypes::ResistBuffs: - return false; - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::GroupHeals: - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::PetFastHeals: - case BotSpellTypes::PetRegularHeals: - case BotSpellTypes::PetVeryFastHeals: - case BotSpellTypes::RegularHeal: - switch (stance) { - case Stance::Aggressive: - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - switch (stance) { - case Stance::Aggressive: - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::PreCombatBuffSong: - if (bot_class == Class::Bard) { - return false; - } - else { - return true; - } - case BotSpellTypes::Nuke: - case BotSpellTypes::DOT: - case BotSpellTypes::Stun: - switch (stance) { - case Stance::Assist: - return true; - default: - return false; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::AEStun: - case BotSpellTypes::AEDoT: - case BotSpellTypes::AELifetap: - case BotSpellTypes::PBAENuke: - switch (stance) { - case Stance::AEBurn: - return false; - default: - return true; - } - case BotSpellTypes::Mez: - case BotSpellTypes::AEMez: - case BotSpellTypes::Debuff: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Slow: - case BotSpellTypes::AESlow: - case BotSpellTypes::HateRedux: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return true; - default: - return false; - } - case BotSpellTypes::Snare: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Assist: - return true; - default: - return false; - } - case BotSpellTypes::HateLine: - if (bot_class == Class::ShadowKnight || bot_class == Class::Paladin) { - switch (stance) { - case Stance::Aggressive: - return false; - default: - return true; - } - } - else { - return true; - } - case BotSpellTypes::Charm: - case BotSpellTypes::Resurrect: - case BotSpellTypes::AESnare: - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AEFear: - case BotSpellTypes::Fear: - case BotSpellTypes::AEHateLine: - case BotSpellTypes::PetCures: - case BotSpellTypes::PetHoTHeals: - case BotSpellTypes::PetCompleteHeals: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetResistBuffs: - default: - return true; - } -} - -uint16 Mob::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { - switch (spell_type) { - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::PetVeryFastHeals: - return 1500; - case BotSpellTypes::FastHeals: - case BotSpellTypes::PetFastHeals: - return 2500; - case BotSpellTypes::GroupHeals: - case BotSpellTypes::RegularHeal: - case BotSpellTypes::PetRegularHeals: - return 4000; - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::PetCompleteHeals: - return 8000; - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetHoTHeals: - return 22000; - case BotSpellTypes::Cure: - return 2000; - case BotSpellTypes::GroupCures: - return 3000; - case BotSpellTypes::PetCures: - return 5000; - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return 1; - case Stance::Aggressive: - return 2000; - case Stance::Efficient: - return 8000; - default: - return 4000; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: - case BotSpellTypes::AESnare: - case BotSpellTypes::Snare: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: - case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: - case BotSpellTypes::AEStun: - case BotSpellTypes::Stun: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return 1; - case Stance::Aggressive: - return 3000; - case Stance::Efficient: - return 10000; - default: - return 6000; - } - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - return 8000; - case BotSpellTypes::Fear: - case BotSpellTypes::AEFear: - return 15000; - default: - return 1; - } -} - -uint8 Mob::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { - switch (spell_type) { - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 0; - default: - return 20; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::Nuke: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 0; - default: - return 5; - } - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 0; - case Stance::Efficient: - return 40; - default: - return 25; - } - case BotSpellTypes::Mez: - case BotSpellTypes::AEMez: - return 85; - default: - return 0; - } -} - -uint8 Mob::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { - uint8 bot_class = GetClass(); - - switch (spell_type) { - case BotSpellTypes::Escape: - case BotSpellTypes::VeryFastHeals: - case BotSpellTypes::PetVeryFastHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 40; - case Stance::Efficient: - default: - return 25; - } - case BotSpellTypes::AELifetap: - case BotSpellTypes::Lifetap: - case BotSpellTypes::FastHeals: - case BotSpellTypes::PetFastHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 55; - case Stance::Efficient: - return 35; - default: - return 40; - } - case BotSpellTypes::GroupHeals: - case BotSpellTypes::RegularHeal: - case BotSpellTypes::PetRegularHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 70; - case Stance::Efficient: - return 50; - default: - return 60; - } - case BotSpellTypes::CompleteHeal: - case BotSpellTypes::GroupCompleteHeals: - case BotSpellTypes::PetCompleteHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 90; - case Stance::Efficient: - return 65; - default: - return 80; - } - case BotSpellTypes::AENukes: - case BotSpellTypes::AERains: - case BotSpellTypes::PBAENuke: - case BotSpellTypes::AEStun: - case BotSpellTypes::Nuke: - case BotSpellTypes::AEDoT: - case BotSpellTypes::DOT: - case BotSpellTypes::AERoot: - case BotSpellTypes::Root: - case BotSpellTypes::AESlow: - case BotSpellTypes::Slow: - case BotSpellTypes::AESnare: - case BotSpellTypes::Snare: - case BotSpellTypes::AEFear: - case BotSpellTypes::Fear: - case BotSpellTypes::AEDispel: - case BotSpellTypes::Dispel: - case BotSpellTypes::AEDebuff: - case BotSpellTypes::Debuff: - case BotSpellTypes::Stun: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - return 100; - case Stance::Aggressive: - return 100; - case Stance::Efficient: - return 90; - default: - return 99; - } - case BotSpellTypes::GroupHoTHeals: - case BotSpellTypes::HoTHeals: - case BotSpellTypes::PetHoTHeals: - if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { - return 60; - } - else { - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 95; - case Stance::Efficient: - return 80; - default: - return 90; - } - } - case BotSpellTypes::Buff: - case BotSpellTypes::Charm: - case BotSpellTypes::Cure: - case BotSpellTypes::GroupCures: - case BotSpellTypes::PetCures: - case BotSpellTypes::DamageShields: - case BotSpellTypes::HateRedux: - case BotSpellTypes::InCombatBuff: - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::Mez: - case BotSpellTypes::AEMez: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::Pet: - case BotSpellTypes::PetBuffs: - case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::PreCombatBuffSong: - case BotSpellTypes::PetDamageShields: - case BotSpellTypes::PetResistBuffs: - case BotSpellTypes::ResistBuffs: - case BotSpellTypes::Resurrect: - case BotSpellTypes::HateLine: - case BotSpellTypes::AEHateLine: - default: - return 100; - } -} - -void Mob::SetSpellHold(uint16 spell_type, bool hold_status) { - m_bot_spell_settings[spell_type].hold = hold_status; -} - -void Mob::SetSpellDelay(uint16 spell_type, uint16 delay_value) { - m_bot_spell_settings[spell_type].delay = delay_value; -} - -void Mob::SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { - m_bot_spell_settings[spell_type].min_threshold = threshold_value; -} - -void Mob::SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { - m_bot_spell_settings[spell_type].max_threshold = threshold_value; -} - -void Mob::SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { - m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); -} - -void Mob::StartBotSpellTimers() { - for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - m_bot_spell_settings[i].recast_timer.Start(); - } -} - -void Mob::DisableBotSpellTimers() { - for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - m_bot_spell_settings[i].recast_timer.Disable(); - } -} - -bool Mob::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellHold(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellHold(GetPetBotSpellType(spell_type)); - } - - return GetSpellHold(spell_type); -} - -uint16 Mob::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellDelay(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellDelay(GetPetBotSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->GetSpellDelay(spell_type); - } - - return GetSpellDelay(spell_type); -} - -bool Mob::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { - if (!tar) { - return SpellTypeRecastCheck(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->SpellTypeRecastCheck(spell_type); - } - - return SpellTypeRecastCheck(spell_type); -} - -uint8 Mob::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellMinThreshold(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->GetSpellMinThreshold(spell_type); - } - - return GetSpellMinThreshold(spell_type); -} - -uint8 Mob::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { - if (!tar) { - return GetSpellMaxThreshold(spell_type); - } - - if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->GetOwner()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)); - } - - if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->GetSpellMaxThreshold(spell_type); - } - - return GetSpellMaxThreshold(spell_type); -} - -uint16 Mob::GetPetBotSpellType(uint16 spell_type) { - switch (spell_type) { - case BotSpellTypes::VeryFastHeals: - return BotSpellTypes::PetVeryFastHeals; - case BotSpellTypes::FastHeals: - return BotSpellTypes::PetFastHeals; - case BotSpellTypes::RegularHeal: - return BotSpellTypes::PetRegularHeals; - case BotSpellTypes::CompleteHeal: - return BotSpellTypes::PetCompleteHeals; - case BotSpellTypes::HoTHeals: - return BotSpellTypes::PetHoTHeals; - case BotSpellTypes::Buff: - return BotSpellTypes::PetBuffs; - case BotSpellTypes::Cure: - return BotSpellTypes::PetCures; - case BotSpellTypes::DamageShields: - return BotSpellTypes::PetDamageShields; - case BotSpellTypes::ResistBuffs: - return BotSpellTypes::PetResistBuffs; - default: - break; - } - - return spell_type; -} - -uint8 Mob::GetHPRatioForSpellType(uint16 spell_type, Mob* tar) { - switch (spell_type) { - case BotSpellTypes::Escape: - case BotSpellTypes::HateRedux: - case BotSpellTypes::InCombatBuff: - case BotSpellTypes::InCombatBuffSong: - case BotSpellTypes::AELifetap: - case BotSpellTypes::Lifetap: - case BotSpellTypes::OutOfCombatBuffSong: - case BotSpellTypes::Pet: - case BotSpellTypes::PreCombatBuff: - case BotSpellTypes::PreCombatBuffSong: - return GetHPRatio(); - default: - return tar->GetHPRatio(); - } - - return tar->GetHPRatio(); -} - -void Mob::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_value) { - if (!IsOfClientBot()) { - return; - } - - if (IsClient()) { - CastToClient()->SetBotSetting(setting_type, bot_setting, setting_value); - return; - } - - if (IsBot()) { - CastToBot()->SetBotSetting(setting_type, bot_setting, setting_value); - return; - } - - return; -} - -void Mob::SetBaseSetting(uint16 base_setting, int setting_value) { - switch (base_setting) { - case BotBaseSettings::IllusionBlock: - SetIllusionBlock(setting_value); - break; - default: - break; - } -} - -bool Mob::TargetValidation(Mob* other) { - if (!other || GetAppearance() == eaDead) { - return false; - } - - return true; -} diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index df0f745428..5bd2484b35 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1483,8 +1483,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); #endif - if (caster && caster->IsOfClientBot() && GetIllusionBlock()) { - break; + if (caster && caster->IsOfClientBot()) { + if (IsClient()) { + if (CastToClient()->GetIllusionBlock()) { + break; + } + } + else { + if (CastToBot()->GetIllusionBlock()) { + break; + } + } } ApplySpellEffectIllusion(spell_id, caster, buffslot, spells[spell_id].base_value[i], spells[spell_id].limit_value[i], spells[spell_id].max_value[i]); @@ -1496,8 +1505,17 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove #ifdef SPELL_EFFECT_SPAM snprintf(effect_desc, _EDLEN, "Illusion Copy"); #endif - if (caster && caster->IsOfClientBot() && GetIllusionBlock()) { - break; + if (caster && caster->IsOfClientBot()) { + if (IsClient()) { + if (CastToClient()->GetIllusionBlock()) { + break; + } + } + else { + if (CastToBot()->GetIllusionBlock()) { + break; + } + } } if(caster && caster->GetTarget()){ diff --git a/zone/spells.cpp b/zone/spells.cpp index e5dc8e1dd6..f1da3f0e1a 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -7568,108 +7568,3 @@ bool Mob::CheckWaterLoS(Mob* m) zone->watermap->InLiquid(m->GetPosition()) ); } - -bool Mob::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) -{ - int effect_index; - - if (!caster) { - return false; - } - - //TODO: this function loops through the effect list for - //this spell like 10 times, this could easily be consolidated - //into one loop through with a switch statement. - - LogSpells("Checking to see if we are immune to spell [{}] cast by [{}]", spell_id, caster->GetName()); - - if (!IsValidSpell(spell_id)) { - return true; - } - - if (GetSpecialAbility(SpecialAbility::DispellImmunity) && IsDispelSpell(spell_id)) { - return true; - } - - if (GetSpecialAbility(SpecialAbility::PacifyImmunity) && IsHarmonySpell(spell_id)) { - return true; - } - - if (!GetSpecialAbility(SpecialAbility::MesmerizeImmunity) && IsMesmerizeSpell(spell_id)) { - // check max level for spell - effect_index = GetSpellEffectIndex(spell_id, SE_Mez); - assert(effect_index >= 0); - // NPCs get to ignore the max level - if ( - (GetLevel() > spells[spell_id].max_value[effect_index]) && - (!caster->IsNPC() || (caster->IsNPC() && !RuleB(Spells, NPCIgnoreBaseImmunity))) - ) { - return true; - } - } - - // slow and haste spells - if (GetSpecialAbility(SpecialAbility::SlowImmunity) && IsEffectInSpell(spell_id, SE_AttackSpeed)) { - return true; - } - - // client vs client fear - if (!GetSpecialAbility(SpecialAbility::FearImmunity) && IsEffectInSpell(spell_id, SE_Fear)) { - effect_index = GetSpellEffectIndex(spell_id, SE_Fear); - - if (IsClient() && caster->IsClient() && (caster->CastToClient()->GetGM() == false)) { - LogSpells("Clients cannot fear eachother!"); - caster->MessageString(Chat::Red, IMMUNE_FEAR); // need to verify message type, not in MQ2Cast for easy look up - return true; - } - else if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { - return true; - } - else if (CheckAATimer(aaTimerWarcry)) { - return true; - } - } - - if (!GetSpecialAbility(SpecialAbility::CharmImmunity) && IsCharmSpell(spell_id)) { - - if (this == caster) { - return true; - } - - //let npcs cast whatever charm on anyone - if (!caster->IsNPC()) { - // check level limit of charm spell - effect_index = GetSpellEffectIndex(spell_id, SE_Charm); - assert(effect_index >= 0); - if (GetLevel() > spells[spell_id].max_value[effect_index] && spells[spell_id].max_value[effect_index] != 0) { - return true; - } - } - } - - if ( - GetSpecialAbility(SpecialAbility::SnareImmunity) && - ( - IsEffectInSpell(spell_id, SE_Root) || - IsEffectInSpell(spell_id, SE_MovementSpeed) - ) - ) { - if (GetSpecialAbility(SpecialAbility::SnareImmunity)) { - return true; - } - } - - if (IsLifetapSpell(spell_id)) { - if (this == caster) { - return true; - } - } - - if (IsSacrificeSpell(spell_id)) { - if (this == caster) { - return true; - } - } - - return false; -} From e695f1c499c3aa790f62f7ed17222f8e1506be8f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 30 Jan 2025 22:38:03 -0600 Subject: [PATCH 350/394] Return for GetRawBotList This checks offline bots too --- zone/groups.cpp | 16 +++++++--------- zone/groups.h | 2 +- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/zone/groups.cpp b/zone/groups.cpp index f040e8d917..6e5d2c489a 100644 --- a/zone/groups.cpp +++ b/zone/groups.cpp @@ -1017,11 +1017,9 @@ void Group::GetBotList(std::list& bot_list, bool clear_list) } } -void Group::GetRawBotList(std::list& bot_list, bool clear_list) +std::list Group::GetRawBotList() { - if (clear_list) { - bot_list.clear(); - } + std::list bot_list; const auto& l = GroupIdRepository::GetWhere( database, @@ -1032,10 +1030,12 @@ void Group::GetRawBotList(std::list& bot_list, bool clear_list) ); for (const auto& e : l) { - if (e.bot_id) { + if (e.bot_id) { bot_list.push_back(e.bot_id); } } + + return bot_list; } bool Group::Process() { @@ -1263,8 +1263,7 @@ void Client::LeaveGroup() { } if (RuleB(Bots, Enabled)) { - std::list sbl; - g->GetRawBotList(sbl); + std::list sbl = g->GetRawBotList(); for (auto botID : sbl) { auto b = entity_list.GetBotByBotID(botID); @@ -2618,8 +2617,7 @@ void Group::AddToGroup(AddToGroupRequest r) } void Group::RemoveClientsBots(Client* c) { - std::list sbl; - GetRawBotList(sbl); + std::list sbl = GetRawBotList(); for (auto botID : sbl) { auto b = entity_list.GetBotByBotID(botID); diff --git a/zone/groups.h b/zone/groups.h index 9a49b7d3cd..91fcee13e1 100644 --- a/zone/groups.h +++ b/zone/groups.h @@ -69,7 +69,7 @@ class Group : public GroupIDConsumer { void GetMemberList(std::list& member_list, bool clear_list = true); void GetClientList(std::list& client_list, bool clear_list = true); void GetBotList(std::list& bot_list, bool clear_list = true); - void GetRawBotList(std::list& bot_list, bool clear_list = true); + std::list GetRawBotList(); bool IsGroupMember(Mob* c); bool IsGroupMember(const char* name); bool Process(); From c08893ffcb115395ff0b51ee941680948e0aaa39 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:03:57 -0600 Subject: [PATCH 351/394] Rename BotGroupSay to RaidGroupSay --- zone/bot.cpp | 34 ++++++++++++++++----------------- zone/bot.h | 2 +- zone/bot_commands/attack.cpp | 2 +- zone/bot_commands/bot.cpp | 2 +- zone/bot_commands/cast.cpp | 2 +- zone/bot_commands/depart.cpp | 6 +++--- zone/bot_commands/guard.cpp | 2 +- zone/bot_commands/hold.cpp | 2 +- zone/bot_commands/pet.cpp | 4 ++-- zone/bot_commands/pick_lock.cpp | 4 ++-- zone/bot_commands/taunt.cpp | 4 ++-- zone/bot_commands/track.cpp | 2 +- zone/botspellsai.cpp | 22 ++++++++++----------- zone/lua_bot.cpp | 6 +++--- zone/lua_bot.h | 2 +- zone/perl_bot.cpp | 6 +++--- zone/special_attacks.cpp | 2 +- 17 files changed, 52 insertions(+), 52 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 078785520b..c690b26aa3 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -624,14 +624,14 @@ void Bot::ChangeBotRangedWeapons(bool isRanged) { BotAddEquipItem(EQ::invslot::slotPrimary, GetBotItemBySlot(EQ::invslot::slotPrimary)); BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotSecondary)); SetAttackTimer(); - BotGroupSay(this, "My blade is ready"); + RaidGroupSay(this, "My blade is ready"); } else { BotRemoveEquipItem(EQ::invslot::slotPrimary); BotRemoveEquipItem(EQ::invslot::slotSecondary); BotAddEquipItem(EQ::invslot::slotAmmo, GetBotItemBySlot(EQ::invslot::slotAmmo)); BotAddEquipItem(EQ::invslot::slotSecondary, GetBotItemBySlot(EQ::invslot::slotRange)); SetAttackTimer(); - BotGroupSay(this, "My blades are sheathed"); + RaidGroupSay(this, "My blades are sheathed"); } } @@ -1935,7 +1935,7 @@ bool Bot::BotRangedAttack(Mob* other, bool can_double_attack) { if (!ammo || ammo_item->GetCharges() < 1) { if (!GetCombatRoundForAlerts()) { SetCombatRoundForAlerts(); - BotGroupSay(this, "I do not have any ammo!"); + RaidGroupSay(this, "I do not have any ammo!"); } } return false; @@ -2967,11 +2967,11 @@ bool Bot::TryEvade(Mob* tar) { } if (zone->random.Int(0, 260) < (int)GetSkill(EQ::skills::SkillHide)) { - BotGroupSay(this, "I have momentarily ducked away from the main combat."); + RaidGroupSay(this, "I have momentarily ducked away from the main combat."); RogueEvade(tar); } else { - BotGroupSay(this, "My attempts at ducking clear of combat fail."); + RaidGroupSay(this, "My attempts at ducking clear of combat fail."); } //SendAppearancePacket(AT_Invis, Invisibility::Visible); @@ -3010,11 +3010,11 @@ bool Bot::TryEvade(Mob* tar) { if (zone->random.Real(0, 160) > totalfeign) { //SendAppearancePacket(AT_Anim, ANIM_DEATH); - BotGroupSay(this, "I have fallen to the ground."); + RaidGroupSay(this, "I have fallen to the ground."); SetFeigned(false); } else { - BotGroupSay(this, "I have successfully feigned my death."); + RaidGroupSay(this, "I have successfully feigned my death."); SetFeigned(true); //SendAppearancePacket(AT_Anim, ANIM_DEATH); } @@ -3544,7 +3544,7 @@ void Bot::BotPullerProcess(Client* bot_owner, Raid* raid) { const auto msg = fmt::format("Pulling {}.", pull_target->GetCleanName()); raid->RaidSay(msg.c_str(), GetCleanName(), 0, 100); } else { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Pulling {}.", @@ -5319,7 +5319,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) { if (IsTaunting() && target->IsNPC() && taunt_time) { if (GetTarget() && GetTarget()->GetHateTop() && GetTarget()->GetHateTop() != this) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Taunting {}.", @@ -6255,7 +6255,7 @@ bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQ::spel bool isMainGroupMGB = false; if (isMainGroupMGB && (GetClass() != Class::Bard)) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting {} as a Mass Group Buff.", @@ -7906,7 +7906,7 @@ void Bot::SetDefaultBotStance() { _botStance = GetClass() == Class::Warrior ? Stance::Aggressive : Stance::Balanced; } -void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) { +void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { char buf[1000]; va_list ap; va_start(ap, msg); @@ -7962,7 +7962,7 @@ void Bot::BotGroupSay(Mob* speaker, const char* msg, ...) { bool Bot::UseDiscipline(uint32 spell_id, uint32 target) { if (!IsValidSpell(spell_id)) { - BotGroupSay(this, "Not a valid spell."); + RaidGroupSay(this, "Not a valid spell."); return false; } @@ -9390,7 +9390,7 @@ void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id) SetIsUsingItemClick(true); - BotGroupSay( + RaidGroupSay( this, fmt::format( "Attempting to cast [{}] on {}.", @@ -11264,7 +11264,7 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { if (IsCasting()) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Interrupting {}. I have been commanded to try to cast an AA - {} on {}.", @@ -11278,7 +11278,7 @@ bool Bot::AttemptAACastSpell(Mob* tar, uint16 spell_id, AA::Rank* rank) { } if (CastSpell(spell_id, tar->GetID())) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting an AA - {} on {}.", @@ -11381,7 +11381,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { } if (IsCasting()) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Interrupting {}. I have been commanded to try to cast {} on {}.", @@ -11397,7 +11397,7 @@ bool Bot::AttemptForcedCastSpell(Mob* tar, uint16 spell_id, bool is_disc) { return false; } - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting {} on {}.", diff --git a/zone/bot.h b/zone/bot.h index 67618f9cfe..e4ef972168 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -774,7 +774,7 @@ class Bot : public NPC { // Static Bot Group Methods static bool AddBotToGroup(Bot* bot, Group* group); static bool RemoveBotFromGroup(Bot* bot, Group* group); - static void BotGroupSay(Mob *speaker, const char *msg, ...); + static void RaidGroupSay(Mob *speaker, const char *msg, ...); // "GET" Class Methods uint32 GetBotID() const { return _botID; } diff --git a/zone/bot_commands/attack.cpp b/zone/bot_commands/attack.cpp index 1cb67bf458..9efc751949 100644 --- a/zone/bot_commands/attack.cpp +++ b/zone/bot_commands/attack.cpp @@ -67,7 +67,7 @@ void bot_command_attack(Client *c, const Seperator *sep) } if (attacker_count == 1 && first_attacker) { - Bot::BotGroupSay( + Bot::RaidGroupSay( first_attacker, fmt::format( "Attacking {}.", diff --git a/zone/bot_commands/bot.cpp b/zone/bot_commands/bot.cpp index 186ef39587..4c22dd379a 100644 --- a/zone/bot_commands/bot.cpp +++ b/zone/bot_commands/bot.cpp @@ -1136,7 +1136,7 @@ void bot_command_spawn(Client *c, const Seperator *sep) } if (!silent_tell && c->GetBotOption(Client::booSpawnMessageSay)) { - Bot::BotGroupSay(my_bot, bot_spawn_message[message_index].c_str()); + Bot::RaidGroupSay(my_bot, bot_spawn_message[message_index].c_str()); } else if (!silent_tell && c->GetBotOption(Client::booSpawnMessageTell)) { my_bot->OwnerMessage(bot_spawn_message[message_index]); diff --git a/zone/bot_commands/cast.cpp b/zone/bot_commands/cast.cpp index 6f5c13c266..5ac4d65c32 100644 --- a/zone/bot_commands/cast.cpp +++ b/zone/bot_commands/cast.cpp @@ -505,7 +505,7 @@ void bot_command_cast(Client* c, const Seperator* sep) } if (IsBotSpellTypeDetrimental(spell_type) && !bot_iter->IsAttackAllowed(new_tar)) { - bot_iter->BotGroupSay( + bot_iter->RaidGroupSay( bot_iter, fmt::format( "I cannot attack [{}].", diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index 88b2d7a07e..cf5e101c55 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -175,7 +175,7 @@ void bot_command_depart(Client* c, const Seperator* sep) } } - Bot::BotGroupSay( + Bot::RaidGroupSay( bot_iter, fmt::format( "I can port you to {}.", @@ -211,7 +211,7 @@ void bot_command_depart(Client* c, const Seperator* sep) } if (bot_iter->IsCommandedSpell() && bot_iter->IsCasting()) { - Bot::BotGroupSay( + Bot::RaidGroupSay( bot_iter, fmt::format( "Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.", @@ -234,7 +234,7 @@ void bot_command_depart(Client* c, const Seperator* sep) } if (bot_iter->GetClass() != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - Bot::BotGroupSay( + Bot::RaidGroupSay( bot_iter, fmt::format( "Casting {} [{}] on {}.", diff --git a/zone/bot_commands/guard.cpp b/zone/bot_commands/guard.cpp index 324bfb03cf..808de155d5 100644 --- a/zone/bot_commands/guard.cpp +++ b/zone/bot_commands/guard.cpp @@ -47,7 +47,7 @@ void bot_command_guard(Client *c, const Seperator *sep) } if (sbl.size() == 1) { - Bot::BotGroupSay( + Bot::RaidGroupSay( sbl.front(), fmt::format( "{}uarding this position.", diff --git a/zone/bot_commands/hold.cpp b/zone/bot_commands/hold.cpp index e4528a9909..56ca1f5a8d 100644 --- a/zone/bot_commands/hold.cpp +++ b/zone/bot_commands/hold.cpp @@ -47,7 +47,7 @@ void bot_command_hold(Client *c, const Seperator *sep) } if (sbl.size() == 1) { - Bot::BotGroupSay( + Bot::RaidGroupSay( sbl.front(), fmt::format( "{}olding my attacks.", diff --git a/zone/bot_commands/pet.cpp b/zone/bot_commands/pet.cpp index 04889f1cfd..712b551180 100644 --- a/zone/bot_commands/pet.cpp +++ b/zone/bot_commands/pet.cpp @@ -74,7 +74,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep) if (bot_iter->IsBotCharmer()) { bot_iter->SetBotCharmer(false); if (sbl.size() == 1) - Bot::BotGroupSay(bot_iter, "Using a summoned pet"); + Bot::RaidGroupSay(bot_iter, "Using a summoned pet"); ++summoned_pet; continue; } @@ -86,7 +86,7 @@ void bot_command_pet_remove(Client *c, const Seperator *sep) } bot_iter->SetBotCharmer(true); if (sbl.size() == 1) - Bot::BotGroupSay(bot_iter, "Available for Charming"); + Bot::RaidGroupSay(bot_iter, "Available for Charming"); ++charmed_pet; } diff --git a/zone/bot_commands/pick_lock.cpp b/zone/bot_commands/pick_lock.cpp index d34ab6f983..f368087e2f 100644 --- a/zone/bot_commands/pick_lock.cpp +++ b/zone/bot_commands/pick_lock.cpp @@ -24,7 +24,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep) Bot* my_bot = sbl.front(); my_bot->InterruptSpell(); - Bot::BotGroupSay(my_bot, "Attempting to pick the lock."); + Bot::RaidGroupSay(my_bot, "Attempting to pick the lock."); std::list door_list; entity_list.GetDoorsList(door_list); @@ -51,7 +51,7 @@ void bot_command_pick_lock(Client *c, const Seperator *sep) ++open_count; } else { - Bot::BotGroupSay(my_bot, "I am not skilled enough for this lock."); + Bot::RaidGroupSay(my_bot, "I am not skilled enough for this lock."); } } c->Message(Chat::White, "%i door%s attempted - %i door%s successful", door_count, ((door_count != 1) ? ("s") : ("")), open_count, ((open_count != 1) ? ("s") : (""))); diff --git a/zone/bot_commands/taunt.cpp b/zone/bot_commands/taunt.cpp index 69b49b42e9..1496646908 100644 --- a/zone/bot_commands/taunt.cpp +++ b/zone/bot_commands/taunt.cpp @@ -130,7 +130,7 @@ void bot_command_taunt(Client* c, const Seperator* sep) bot_iter->SetTaunting(taunt_state); - Bot::BotGroupSay( + Bot::RaidGroupSay( bot_iter, fmt::format( "I am {} taunting.", @@ -154,7 +154,7 @@ void bot_command_taunt(Client* c, const Seperator* sep) bot_iter->GetPet()->CastToNPC()->SetTaunting(taunt_state); - Bot::BotGroupSay( + Bot::RaidGroupSay( bot_iter, fmt::format( "My Pet is {} taunting.", diff --git a/zone/bot_commands/track.cpp b/zone/bot_commands/track.cpp index f134c45965..5c62f3a1de 100644 --- a/zone/bot_commands/track.cpp +++ b/zone/bot_commands/track.cpp @@ -67,6 +67,6 @@ void bot_command_track(Client *c, const Seperator *sep) } my_bot->InterruptSpell(); - Bot::BotGroupSay(my_bot, tracking_msg.c_str()); + Bot::RaidGroupSay(my_bot, tracking_msg.c_str()); entity_list.ShowSpawnWindow(c, (c->GetLevel() * base_distance), track_named); } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 344669a978..211e9b83e1 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -231,7 +231,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ } if (IsCommandedSpell() && IsCasting()) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Interrupting {}. I have been commanded to try to cast a [{}] spell, {} on {}.", @@ -258,7 +258,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ } if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting {} [{}] on {}.", @@ -306,7 +306,7 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel SetCastedSpellType(spell_type); } - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting {} [{}] on {}.", @@ -342,7 +342,7 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { if (IsGroupSpell(bot_spell.SpellId)) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Curing the group with {}.", @@ -359,7 +359,7 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } } else { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Curing {} with {}.", @@ -417,7 +417,7 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { SetCastedSpellType(spell_type); - BotGroupSay( + RaidGroupSay( this, fmt::format( "Summoning a pet [{}].", @@ -474,7 +474,7 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe SetCastedSpellType(spell_type); if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting {} [{}] on {}.", @@ -494,7 +494,7 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe SetCastedSpellType(spell_type); if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting {} [{}] on {}.", @@ -522,7 +522,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { if (IsGroupSpell(bot_spell.SpellId)) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Healing the group with {} [{}].", @@ -543,7 +543,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } else { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Healing {} with {} [{}].", @@ -882,7 +882,7 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) { } if (castedSpell) { - BotGroupSay( + RaidGroupSay( this, fmt::format( "Casting {} on {}, please stay in range!", diff --git a/zone/lua_bot.cpp b/zone/lua_bot.cpp index dd208ae450..dc1742feea 100644 --- a/zone/lua_bot.cpp +++ b/zone/lua_bot.cpp @@ -659,9 +659,9 @@ void Lua_Bot::DeleteBot() { self->DeleteBot(); } -void Lua_Bot::BotGroupSay(const char* message) { +void Lua_Bot::RaidGroupSay(const char* message) { Lua_Safe_Call_Void(); - self->BotGroupSay(self, message); + self->RaidGroupSay(self, message); } luabind::scope lua_register_bot() { @@ -693,7 +693,7 @@ luabind::scope lua_register_bot() { .def("ApplySpellRaid", (void(Lua_Bot::*)(int,int,int,bool))&Lua_Bot::ApplySpellRaid) .def("ApplySpellRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::ApplySpellRaid) .def("ApplySpellRaid", (void(Lua_Bot::*)(int,int,int,bool,bool))&Lua_Bot::ApplySpellRaid) - .def("BotGroupSay", (void(Lua_Bot::*)(const char*)) & Lua_Bot::BotGroupSay) + .def("RaidGroupSay", (void(Lua_Bot::*)(const char*))&Lua_Bot::RaidGroupSay) .def("Camp", (void(Lua_Bot::*)(void))&Lua_Bot::Camp) .def("Camp", (void(Lua_Bot::*)(bool))&Lua_Bot::Camp) .def("ClearDisciplineReuseTimer", (void(Lua_Bot::*)())&Lua_Bot::ClearDisciplineReuseTimer) diff --git a/zone/lua_bot.h b/zone/lua_bot.h index d0b94d654d..86e3ca28bf 100644 --- a/zone/lua_bot.h +++ b/zone/lua_bot.h @@ -46,7 +46,7 @@ class Lua_Bot : public Lua_Mob Lua_Mob GetOwner(); int16 HasBotItem(uint32 item_id); void OwnerMessage(std::string message); - void BotGroupSay(const char* message); + void RaidGroupSay(const char* message); bool ReloadBotDataBuckets(); bool ReloadBotOwnerDataBuckets(); bool ReloadBotSpells(); diff --git a/zone/perl_bot.cpp b/zone/perl_bot.cpp index ae9484bfce..b4aaee5454 100644 --- a/zone/perl_bot.cpp +++ b/zone/perl_bot.cpp @@ -615,9 +615,9 @@ void Perl_Bot_DeleteBot(Bot* self) // @categories Script Utility self->DeleteBot(); } -void Perl_Bot_BotGroupSay(Bot* self, const char* message) // @categories Script Utility +void Perl_Bot_RaidGroupSay(Bot* self, const char* message) // @categories Script Utility { - self->BotGroupSay(self, message); + self->RaidGroupSay(self, message); } void perl_register_bot() @@ -649,7 +649,7 @@ void perl_register_bot() package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int))&Perl_Bot_ApplySpellRaid); package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int, bool))&Perl_Bot_ApplySpellRaid); package.add("ApplySpellRaid", (void(*)(Bot*, int, int, int, bool, bool))&Perl_Bot_ApplySpellRaid); - package.add("BotGroupSay", &Perl_Bot_BotGroupSay); + package.add("RaidGroupSay", &Perl_Bot_RaidGroupSay); package.add("Camp", (void(*)(Bot*))&Perl_Bot_Camp); package.add("Camp", (void(*)(Bot*, bool))&Perl_Bot_Camp); package.add("ClearDisciplineReuseTimer", (void(*)(Bot*))&Perl_Bot_ClearDisciplineReuseTimer); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index db4c7bf47c..26c5bd87e3 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -737,7 +737,7 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { if (!CastToBot()->GetCombatRoundForAlerts()) { CastToBot()->SetCombatRoundForAlerts(); - CastToBot()->BotGroupSay(this, "I can't backstab with this weapon!"); + CastToBot()->RaidGroupSay(this, "I can't backstab with this weapon!"); } return; From 01a0b7f5790e7c8c95d0b1b8f5e9e2e56f38db19 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:10:56 -0600 Subject: [PATCH 352/394] Prevent bots from forming their own group if a bot that is a group leader is removed from the raid --- zone/bot_raid.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/zone/bot_raid.cpp b/zone/bot_raid.cpp index 3f7cc5e4c9..aba5981a7c 100644 --- a/zone/bot_raid.cpp +++ b/zone/bot_raid.cpp @@ -96,19 +96,10 @@ void Raid::HandleBotGroupDisband(uint32 owner, uint32 gid) // Remove the entire BOT group in this case if (b && gid != RAID_GROUPLESS && IsRaidMember(b->GetName()) && IsGroupLeader(b->GetName())) { auto r_group_members = GetRaidGroupMembers(GetGroup(b->GetName())); - auto g = new Group(b); - entity_list.AddGroup(g); - g->AddToGroup(b); - database.SetGroupLeaderName(g->GetID(), b->GetName()); for (auto m: r_group_members) { if (m.member->IsBot()) { auto b_member = m.member->CastToBot(); - if (strcmp(b_member->GetName(), b->GetName()) == 0) { - b->SetFollowID(owner); - } else { - Bot::AddBotToGroup(b_member, g); - } Bot::RemoveBotFromRaid(b_member); } } From 3ce9579a6c98271eedc6032a6e65591752ceab6f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Thu, 30 Jan 2025 23:17:22 -0600 Subject: [PATCH 353/394] Linux fix? --- zone/bot_structs.h | 1 + 1 file changed, 1 insertion(+) diff --git a/zone/bot_structs.h b/zone/bot_structs.h index e3bcc86789..99662e83b6 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -20,6 +20,7 @@ #define BOT_STRUCTS #include "../common/types.h" +#include "../common/timer.h" #include From d810d320692d36055bf7e235c353796386f5a982 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 00:16:18 -0600 Subject: [PATCH 354/394] IsPetOwner fixes --- zone/npc.cpp | 2 +- zone/spells.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/npc.cpp b/zone/npc.cpp index 468e4b35e1..7210dee79c 100644 --- a/zone/npc.cpp +++ b/zone/npc.cpp @@ -555,7 +555,7 @@ void NPC::SetTarget(Mob* mob) { // either normal pet and owner is client or charmed pet and owner is client Mob *owner = nullptr; - if (IsPet() && IsPetOwnerOfClientBot()) { + if (IsPet() && IsPetOwnerClient()) { owner = GetOwner(); } else if (IsCharmed()) { owner = GetOwner(); diff --git a/zone/spells.cpp b/zone/spells.cpp index f1da3f0e1a..43ffbca1d8 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2722,8 +2722,8 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in SpellOnTarget(spell_id, this); } } - } else if (spell_target->IsRaidGrouped() && spell_target->IsClient()) { - Raid *target_raid = entity_list.GetRaidByClient(spell_target->CastToClient()); + } else if (spell_target->IsRaidGrouped() && spell_target->IsOfClientBot()) { + Raid *target_raid = (IsClient() ? entity_list.GetRaidByClient(spell_target->CastToClient()) : entity_list.GetRaidByBot(spell_target->CastToBot())); uint32 gid = 0xFFFFFFFF; if (target_raid) { gid = target_raid->GetGroup(spell_target->GetName()); From bf8a576003ac5c77240ad1118bec059707489c9a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:27:02 -0600 Subject: [PATCH 355/394] Add remove option to list for ^blockedbuffs / ^blockedpetbuffs --- zone/bot_commands/blocked_buffs.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/zone/bot_commands/blocked_buffs.cpp b/zone/bot_commands/blocked_buffs.cpp index b604ef59d6..a8f346a5de 100644 --- a/zone/bot_commands/blocked_buffs.cpp +++ b/zone/bot_commands/blocked_buffs.cpp @@ -164,10 +164,11 @@ void bot_command_blocked_buffs(Client* c, const Seperator* sep) c->Message( Chat::Yellow, fmt::format( - "{} says, '{} [#{}] is currently blocked.'", + "{} says, '{} [#{}] is currently blocked. [{}]'", bot_iter->GetCleanName(), spells[blocked_buff.spell_id].name, - blocked_buff.spell_id + blocked_buff.spell_id, + Saylink::Silent(fmt::format("^blockedbuffs remove {}", blocked_buff.spell_id),"Remove") ).c_str() ); } @@ -401,10 +402,11 @@ void bot_command_blocked_pet_buffs(Client* c, const Seperator* sep) c->Message( Chat::Yellow, fmt::format( - "{} says, '{} [#{}] is currently blocked for my pet.'", + "{} says, '{} [#{}] is currently blocked for my pet. [{}]'", bot_iter->GetCleanName(), spells[blocked_buff.spell_id].name, - blocked_buff.spell_id + blocked_buff.spell_id, + Saylink::Silent(fmt::format("^blockedpetbuffs remove {}", blocked_buff.spell_id), "Remove") ).c_str() ); } From bd97453852ccb6d2671c15edbd1d54d5d5093fbb Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:31:48 -0600 Subject: [PATCH 356/394] Implement ^spellannouncecasts to toggle announcing casts of spell types --- .../database_update_manifest_bots.cpp | 1 + zone/bot.cpp | 63 +++++- zone/bot.h | 10 +- zone/bot_command.cpp | 2 + zone/bot_command.h | 1 + zone/bot_commands/bot_settings.cpp | 4 +- zone/bot_commands/copy_settings.cpp | 17 +- zone/bot_commands/default_settings.cpp | 35 ++- zone/bot_commands/depart.cpp | 20 +- zone/bot_commands/spell_announce_cast.cpp | 214 ++++++++++++++++++ zone/bot_structs.h | 1 + zone/botspellsai.cpp | 199 +++++++++------- 12 files changed, 456 insertions(+), 111 deletions(-) create mode 100644 zone/bot_commands/spell_announce_cast.cpp diff --git a/common/database/database_update_manifest_bots.cpp b/common/database/database_update_manifest_bots.cpp index f73afd598b..db1e269cf3 100644 --- a/common/database/database_update_manifest_bots.cpp +++ b/common/database/database_update_manifest_bots.cpp @@ -274,6 +274,7 @@ UPDATE `bot_command_settings` SET `aliases`= 'resistlimits' WHERE `bot_command`= UPDATE `bot_command_settings` SET `aliases`= 'targetcount' WHERE `bot_command`='spelltargetcount'; UPDATE `bot_command_settings` SET `aliases`= 'disc' WHERE `bot_command`='discipline'; UPDATE `bot_command_settings` SET `aliases`= CASE WHEN LENGTH(`aliases`) > 0 THEN CONCAT(`aliases`, '|vc') ELSE 'vc' END WHERE `bot_command`='viewcombos' AND `aliases` NOT LIKE '%vc%'; +UPDATE `bot_command_settings` SET `aliases`= 'announcecasts' WHERE `bot_command`='spellannouncecasts'; )" }, ManifestEntry{ diff --git a/zone/bot.cpp b/zone/bot.cpp index c690b26aa3..def50981e1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7915,6 +7915,7 @@ void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { if (speaker->IsRaidGrouped()) { Raid* r = speaker->CastToBot()->GetStoredRaid(); + if (r) { for (const auto& m : r->members) { if (m.member && !m.is_bot) { @@ -7932,6 +7933,7 @@ void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { } else if (speaker->HasGroup()) { Group* g = speaker->GetGroup(); + if (g) { for (auto& m : g->members) { if (m && !m->IsBot()) { @@ -7948,7 +7950,6 @@ void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { } } else { - //speaker->Say("%s", buf); speaker->GetOwner()->FilteredMessageString( speaker, Chat::PetResponse, @@ -10324,6 +10325,9 @@ void Bot::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_valu case BotSettingCategories::SpellTypeAEOrGroupTargetCount: SetSpellTypeAEOrGroupTargetCount(bot_setting, setting_value); break; + case BotSettingCategories::SpellTypeAnnounceCast: + SetSpellTypeAnnounceCast(bot_setting, setting_value); + break; } } @@ -10515,6 +10519,7 @@ void Bot::LoadDefaultBotSettings() { bot_stance ); t.ae_or_group_target_count = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); + t.announce_cast = GetDefaultSpellTypeAnnounceCast(i, bot_stance); t.recast_timer.Start(); m_bot_spell_settings.push_back(t); @@ -10522,7 +10527,8 @@ void Bot::LoadDefaultBotSettings() { LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.short_name, t.spell_type, Stance::GetName(bot_stance), bot_stance); LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, bot_stance), GetDefaultSpellDelay(i, bot_stance), GetDefaultSpellMinThreshold(i, bot_stance), GetDefaultSpellMaxThreshold(i, bot_stance)); LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, bot_stance), GetDefaultSpellTypeMinManaLimit(i, bot_stance), GetDefaultSpellTypeMaxManaLimit(i, bot_stance), GetDefaultSpellTypeMinHPLimit(i, bot_stance), GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); - LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}] | ae_or_group_target_count = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance)); + LogBotSettingsDetail("{} says, 'TargetCount = [{}] | AnnounceCast = [{}]'", GetCleanName(), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance), GetDefaultSpellTypeAnnounceCast(i, bot_stance)); } } @@ -10638,6 +10644,8 @@ int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 s return GetDefaultSpellTypePriority(setting_type, BotPriorityCategories::Pursue, GetClass(), stance); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: return GetDefaultSpellTypeAEOrGroupTargetCount(setting_type, stance); + case BotSettingCategories::SpellTypeAnnounceCast: + return GetDefaultSpellTypeAnnounceCast(setting_type, stance); default: break; } @@ -10675,6 +10683,8 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { return GetSpellTypePriority(setting_type, BotPriorityCategories::Pursue); case BotSettingCategories::SpellTypeAEOrGroupTargetCount: return GetSpellTypeAEOrGroupTargetCount(setting_type); + case BotSettingCategories::SpellTypeAnnounceCast: + return GetSpellTypeAnnounceCast(setting_type); default: break; } @@ -11123,6 +11133,29 @@ uint16 Bot::GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 sta return 1; } +uint16 Bot::GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance) { + switch (GetClass()) { + case Class::Bard: + switch (spell_type) { + case BotSpellTypes::Mez: + case BotSpellTypes::AEMez: + case BotSpellTypes::Cure: + case BotSpellTypes::GroupCures: + case BotSpellTypes::PetCures: + case BotSpellTypes::Charm: + return 1; + default: + return 0; + } + + return 1; + default: + return 1; + } + + return 1; +} + bool Bot::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { if (!tar) { return GetSpellHold(spell_type); @@ -12112,6 +12145,17 @@ void Bot::CopySettings(Bot* to, uint8 setting_type, uint16 spell_type) { } } + break; + case BotSettingCategories::SpellTypeResistLimit: + if (spell_type != UINT16_MAX) { + to->SetSpellTypeResistLimit(spell_type, GetSpellTypeResistLimit(spell_type)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeResistLimit(i, GetSpellTypeResistLimit(i)); + } + } + break; case BotSettingCategories::SpellTypeMinManaPct: if (spell_type != UINT16_MAX) { @@ -12200,6 +12244,17 @@ void Bot::CopySettings(Bot* to, uint8 setting_type, uint16 spell_type) { } } + break; + case BotSettingCategories::SpellTypeAnnounceCast: + if (spell_type != UINT16_MAX) { + to->SetSpellTypeAnnounceCast(spell_type, GetSpellTypeAnnounceCast(spell_type)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + to->SetSpellTypeAnnounceCast(i, GetSpellTypeAnnounceCast(i)); + } + } + break; } } @@ -12723,11 +12778,11 @@ uint16 Bot::GetSpellTypeIDByShortName(std::string spell_type_string) { } bool Bot::IsValidBotSpellCategory(uint8 setting_type) { - return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END_FULL); + return EQ::ValueWithin(setting_type, BotSettingCategories::START, BotSettingCategories::END); } std::string Bot::GetBotSpellCategoryName(uint8 setting_type) { - return Bot::IsValidBotBaseSetting(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; + return Bot::IsValidBotSpellCategory(setting_type) ? botSpellCategory_names[setting_type] : "UNKNOWN CATEGORY"; } uint16 Bot::GetBotSpellCategoryIDByShortName(std::string setting_string) { diff --git a/zone/bot.h b/zone/bot.h index e4ef972168..4d779534bc 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -109,7 +109,7 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint8 SpellTypeEngagedPriority = 12; constexpr uint8 SpellTypePursuePriority = 13; constexpr uint8 SpellTypeAEOrGroupTargetCount = 14; - constexpr uint8 SpellTypeRecastDelay = 15; + constexpr uint8 SpellTypeAnnounceCast = 15; constexpr uint16 START = BotSettingCategories::BaseSetting; constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; @@ -117,6 +117,7 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; constexpr uint16 END_FULL = BotSettingCategories::SpellTypeRecastDelay; + constexpr uint16 END = BotSettingCategories::SpellTypeAnnounceCast; }; static std::map botSpellCategory_names = { @@ -135,7 +136,7 @@ static std::map botSpellCategory_names = { { BotSettingCategories::SpellTypeEngagedPriority, "SpellEngagedPriority" }, { BotSettingCategories::SpellTypePursuePriority, "SpellPursuePriority" }, { BotSettingCategories::SpellTypeAEOrGroupTargetCount, "SpellTargetCounts" }, - { BotSettingCategories::SpellTypeRecastDelay, "SpellRecastDelay" } + { BotSettingCategories::SpellTypeAnnounceCast, "SpellAnnounceCasts" } }; namespace BotPriorityCategories { // Update GetBotSpellCategoryName as needed @@ -582,6 +583,7 @@ class Bot : public NPC { uint8 GetDefaultSpellTypeMaxManaLimit(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellTypeMinHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellTypeMaxHPLimit(uint16 spell_type, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint8 stance = Stance::Balanced); static bool IsValidBotBaseSetting(uint16 setting_type); @@ -627,6 +629,8 @@ class Bot : public NPC { inline void SetSpellTypeMaxHPLimit(uint16 spell_type, uint8 value) { m_bot_spell_settings[spell_type].max_hp_pct = value; } inline uint16 GetSpellTypeAEOrGroupTargetCount(uint16 spell_type) const { return m_bot_spell_settings[spell_type].ae_or_group_target_count; } inline void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].ae_or_group_target_count = value; } + inline uint16 GetSpellTypeAnnounceCast(uint16 spell_type) const { return m_bot_spell_settings[spell_type].announce_cast; } + inline void SetSpellTypeAnnounceCast(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].announce_cast = value; } inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } inline void SetSpellDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } @@ -671,7 +675,7 @@ class Bot : public NPC { inline uint16 GetCastedSpellType() const { return _castedSpellType; } void SetCastedSpellType(uint16 spell_type); bool IsValidSpellTypeSubType(uint16 spell_type, uint16 sub_type, uint16 spell_id); - bool IsValidBotSpellCategory(uint8 setting_type); + static bool IsValidBotSpellCategory(uint8 setting_type); static std::string GetBotSpellCategoryName(uint8 setting_type); static uint16 GetBotSpellCategoryIDByShortName(std::string setting_string); void AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unordered_map>& AIBot_spells_by_type); diff --git a/zone/bot_command.cpp b/zone/bot_command.cpp index bc5c8c45d8..cd82dba6f0 100644 --- a/zone/bot_command.cpp +++ b/zone/bot_command.cpp @@ -180,6 +180,7 @@ int bot_command_init(void) bot_command_add("sitincombat", "Toggles whether or a not a bot will attempt to med or sit to heal in combat", AccountStatus::Player, bot_command_sit_in_combat) || bot_command_add("sitmanapercent", "Mana threshold for a bot to start sitting in combat if allowed", AccountStatus::Player, bot_command_sit_mana_percent) || bot_command_add("spellaggrochecks", "Toggles whether or not bots will cast a spell type if they think it will get them aggro", AccountStatus::Player, bot_command_spell_aggro_checks) || + bot_command_add("spellannouncecasts", "Turn on or off cast announcements by spell type", AccountStatus::Player, bot_command_spell_announce_cast) || bot_command_add("spellengagedpriority", "Controls the order of casts by spell type when engaged in combat", AccountStatus::Player, bot_command_spell_engaged_priority) || bot_command_add("spelldelays", "Controls the delay between casts for a specific spell type", AccountStatus::Player, bot_command_spell_delays) || bot_command_add("spellholds", "Controls whether a bot holds the specified spell type or not", AccountStatus::Player, bot_command_spell_holds) || @@ -959,6 +960,7 @@ void SendSpellTypeWindow(Client* c, const Seperator* sep) { #include "bot_commands/sit_mana_percent.cpp" #include "bot_commands/spell.cpp" #include "bot_commands/spell_aggro_checks.cpp" +#include "bot_commands/spell_announce_cast.cpp" #include "bot_commands/spell_delays.cpp" #include "bot_commands/spell_engaged_priority.cpp" #include "bot_commands/spell_holds.cpp" diff --git a/zone/bot_command.h b/zone/bot_command.h index 6304fb1946..77d449c44d 100644 --- a/zone/bot_command.h +++ b/zone/bot_command.h @@ -1084,6 +1084,7 @@ void bot_command_sit_hp_percent(Client* c, const Seperator* sep); void bot_command_sit_in_combat(Client* c, const Seperator* sep); void bot_command_sit_mana_percent(Client* c, const Seperator* sep); void bot_command_spell_aggro_checks(Client* c, const Seperator* sep); +void bot_command_spell_announce_cast(Client* c, const Seperator* sep); void bot_command_spell_delays(Client* c, const Seperator* sep); void bot_command_spell_engaged_priority(Client* c, const Seperator* sep); void bot_command_spell_holds(Client* c, const Seperator* sep); diff --git a/zone/bot_commands/bot_settings.cpp b/zone/bot_commands/bot_settings.cpp index 8552afc36f..98c4dccd75 100644 --- a/zone/bot_commands/bot_settings.cpp +++ b/zone/bot_commands/bot_settings.cpp @@ -19,7 +19,8 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) "sithppercent", "sitincombat", "sitmanapercent", - "spellaggrocheck", + "spellaggrochecks", + "spellannouncecasts", "spelldelays", "spellengagedpriority", "spellholds", @@ -31,6 +32,7 @@ void bot_command_bot_settings(Client* c, const Seperator* sep) "spellminmanapct", "spellminthresholds", "spellpursuepriority", + "spellresistlimits", "spelltargetcount", "spelllist", "stance", diff --git a/zone/bot_commands/copy_settings.cpp b/zone/bot_commands/copy_settings.cpp index 66564926a1..49b2c6d2e7 100644 --- a/zone/bot_commands/copy_settings.cpp +++ b/zone/bot_commands/copy_settings.cpp @@ -52,7 +52,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) ), }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, spellresistlimits, blockedbuffs, blockedpetbuffs" }; + p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrochecks, spelltargetcounts, spellresistlimits, spellannouncecasts, blockedbuffs, blockedpetbuffs" }; p.options_one = { "[spellsettings] will copy ^spellsettings options", @@ -111,6 +111,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) "spellaggrochecks", "spelltargetcounts", "spellresistlimits", + "spellannouncecasts", "blockedbuffs", "blockedpetbuffs" }; @@ -255,14 +256,16 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -271,14 +274,16 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, i); from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i); } } @@ -299,14 +304,16 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeMaxHPPct, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeIdlePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, spell_type); - from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, spell_type); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, spell_type); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, spell_type); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -315,6 +322,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellMinThreshold, i); from->CopySettings(to, BotSettingCategories::SpellMaxThreshold, i); from->CopySettings(to, BotSettingCategories::SpellTypeAggroCheck, i); + from->CopySettings(to, BotSettingCategories::SpellTypeResistLimit, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMaxManaPct, i); from->CopySettings(to, BotSettingCategories::SpellTypeMinHPPct, i); @@ -323,6 +331,7 @@ void bot_command_copy_settings(Client* c, const Seperator* sep) from->CopySettings(to, BotSettingCategories::SpellTypeEngagedPriority, i); from->CopySettings(to, BotSettingCategories::SpellTypePursuePriority, i); from->CopySettings(to, BotSettingCategories::SpellTypeAEOrGroupTargetCount, i); + from->CopySettings(to, BotSettingCategories::SpellTypeAnnounceCast, i); } } diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 36fc7a060e..7c22312c07 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -38,7 +38,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) ) }; p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; - p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts, spellresistlimits" }; + p.options = { "all, misc, spellsettings, spelltypesettings, spellholds, spelldelays, spellminthresholds, spellmaxthresholds, spellminmanapct, spellmaxmanapct, spellminhppct, spellmaxhppct, spellidlepriority, spellengagedpriority, spellpursuepriority, spellaggrocheck, spelltargetcounts, spellresistlimits, spellannouncecasts" }; p.options_one = { "[spellsettings] will restore ^spellsettings options", @@ -100,7 +100,8 @@ void bot_command_default_settings(Client* c, const Seperator* sep) "spellpursuepriority", "spellaggrochecks", "spelltargetcounts", - "spellresistlimits" + "spellresistlimits", + "spellannouncecasts" }; if (sep->IsNumber(spell_type_arg_int)) { @@ -223,6 +224,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance)); + my_bot->SetSpellTypeResistLimit(spell_type, my_bot->GetDefaultSpellTypeResistLimit(spell_type, bot_stance)); my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); my_bot->SetSpellTypeMaxManaLimit(spell_type, my_bot->GetDefaultSpellTypeMaxManaLimit(spell_type, bot_stance)); my_bot->SetSpellTypeMinHPLimit(spell_type, my_bot->GetDefaultSpellTypeMinHPLimit(spell_type, bot_stance)); @@ -231,6 +233,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(spell_type, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); + my_bot->SetSpellTypeAnnounceCast(spell_type, my_bot->GetDefaultSpellTypeAnnounceCast(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { @@ -239,6 +242,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance)); my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); @@ -247,6 +251,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance)); } } @@ -263,6 +268,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); + my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance)); my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); my_bot->SetSpellTypeMaxManaLimit(i, my_bot->GetDefaultSpellTypeMaxManaLimit(i, bot_stance)); my_bot->SetSpellTypeMinHPLimit(i, my_bot->GetDefaultSpellTypeMinHPLimit(i, bot_stance)); @@ -271,6 +277,7 @@ void bot_command_default_settings(Client* c, const Seperator* sep) my_bot->SetSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Engaged, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetDefaultSpellTypePriority(i, BotPriorityCategories::Pursue, my_bot->GetClass(), bot_stance)); my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + my_bot->SetSpellTypeAnnounceCast(i, my_bot->GetDefaultSpellTypeAnnounceCast(i, bot_stance)); }; my_bot->ResetBotSpellSettings(); @@ -339,6 +346,18 @@ void bot_command_default_settings(Client* c, const Seperator* sep) output = "aggro check settings"; } + else if (!strcasecmp(sep->arg[1], "resist limit")) { + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + } + } + + output = "resist limit settings"; + } else if (!strcasecmp(sep->arg[1], "minmanapct")) { if (spell_type != UINT16_MAX) { my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); @@ -435,6 +454,18 @@ void bot_command_default_settings(Client* c, const Seperator* sep) output = "target count settings"; } + else if (!strcasecmp(sep->arg[1], "announcecast")) { + if (spell_type != UINT16_MAX) { + my_bot->SetSpellTypeAEOrGroupTargetCount(spell_type, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(spell_type, bot_stance)); + } + else { + for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { + my_bot->SetSpellTypeAEOrGroupTargetCount(i, my_bot->GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance)); + } + } + + output = "announce cast settings"; + } my_bot->Save(); ++success_count; diff --git a/zone/bot_commands/depart.cpp b/zone/bot_commands/depart.cpp index cf5e101c55..82c7fee505 100644 --- a/zone/bot_commands/depart.cpp +++ b/zone/bot_commands/depart.cpp @@ -233,17 +233,15 @@ void bot_command_depart(Client* c, const Seperator* sep) bot_iter->SetCastedSpellType(BotSpellTypes::Teleport); } - if (bot_iter->GetClass() != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - Bot::RaidGroupSay( - bot_iter, - fmt::format( - "Casting {} [{}] on {}.", - GetSpellName(itr->SpellId), - bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), - (tar == bot_iter ? "myself" : tar->GetCleanName()) - ).c_str() - ); - } + Bot::RaidGroupSay( + bot_iter, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(itr->SpellId), + bot_iter->GetSpellTypeNameByID(BotSpellTypes::Teleport), + (tar == bot_iter ? "myself" : tar->GetCleanName()) + ).c_str() + ); is_success = true; } diff --git a/zone/bot_commands/spell_announce_cast.cpp b/zone/bot_commands/spell_announce_cast.cpp new file mode 100644 index 0000000000..4825997249 --- /dev/null +++ b/zone/bot_commands/spell_announce_cast.cpp @@ -0,0 +1,214 @@ +#include "../bot_command.h" + +void bot_command_spell_announce_cast(Client* c, const Seperator* sep) { + if (helper_command_alias_fail(c, "bot_command_spell_announce_cast", sep->arg[0], "spellannouncecasts")) { + c->Message(Chat::White, "note: Allows you to enable or disable cast announcements for bots by spell type."); + + return; + } + + if (helper_is_help_or_usage(sep->arg[1])) { + BotCommandHelpParams p; + + p.description = { "Allows you to enable or disable cast announcements for bots by spell type." }; + p.example_format = + { + fmt::format("{} [Type Shortname] [value] [actionable]", sep->arg[0]), + fmt::format("{} [Type ID] [value] [actionable]", sep->arg[0]) + }; + p.examples_one = + { + "To set all bots to stop announcing dispels:", + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Dispel) + ), + fmt::format( + "{} {} 0 spawned", + sep->arg[0], + BotSpellTypes::Dispel + ) + }; + p.examples_two = + { + "To set Wizards to not announce nukes:", + fmt::format( + "{} {} 0 byclass {}", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Nuke), + Class::Wizard + ), + fmt::format( + "{} {} 0 byclass {}", + sep->arg[0], + BotSpellTypes::Nuke, + Class::Wizard + + ) + }; + p.examples_three = + { + "To check the current announcement setting for debuffs:", + fmt::format( + "{} {} current spawned", + sep->arg[0], + Bot::GetSpellTypeShortNameByID(BotSpellTypes::Debuff) + ), + fmt::format( + "{} {} current spawned", + sep->arg[0], + BotSpellTypes::Debuff + ) + }; + p.actionables = { "target, byname, ownergroup, ownerraid, targetgroup, namesgroup, healrotationtargets, mmr, byclass, byrace, spawned" }; + + std::string popup_text = c->SendBotCommandHelpWindow(p); + popup_text = DialogueWindow::Table(popup_text); + + c->SendPopupToClient(sep->arg[0], popup_text.c_str()); + c->SendSpellTypePrompts(); + + if (RuleB(Bots, SendClassRaceOnHelp)) { + c->Message( + Chat::Yellow, + fmt::format( + "Use {} for information about race/class IDs.", + Saylink::Silent("^classracelist") + ).c_str() + ); + } + + return; + } + + std::string arg1 = sep->arg[1]; + std::string arg2 = sep->arg[2]; + int ab_arg = 2; + bool current_check = false; + uint16 spell_type = 0; + uint32 type_value = 0; + + // String/Int type checks + if (sep->IsNumber(1)) { + spell_type = atoi(sep->arg[1]); + + if (!EQ::ValueWithin(spell_type, BotSpellTypes::START, BotSpellTypes::END)) { + c->Message(Chat::Yellow, "You must choose a valid spell type. Spell types range from %i to %i", BotSpellTypes::START, BotSpellTypes::END); + + return; + } + } + else { + if (Bot::GetSpellTypeIDByShortName(arg1) != UINT16_MAX) { + spell_type = Bot::GetSpellTypeIDByShortName(arg1); + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + } + + if (sep->IsNumber(2)) { + type_value = atoi(sep->arg[2]); + ++ab_arg; + + if (type_value != 0 && type_value != 1) { + c->Message(Chat::Yellow, "You must enter either 0 for [Disabled] or 1 for [Enabled]."); + + return; + } + } + else if (!arg2.compare("current")) { + ++ab_arg; + current_check = true; + } + else { + c->Message( + Chat::Yellow, + fmt::format( + "Incorrect argument, use {} for information regarding this command.", + Saylink::Silent( + fmt::format("{} help", sep->arg[0]) + ) + ).c_str() + ); + + return; + } + + const int ab_mask = ActionableBots::ABM_Type1; + std::string class_race_arg = sep->arg[ab_arg]; + bool class_race_check = false; + if (!class_race_arg.compare("byclass") || !class_race_arg.compare("byrace")) { + class_race_check = true; + } + + std::vector sbl; + if (ActionableBots::PopulateSBL(c, sep->arg[ab_arg], sbl, ab_mask, !class_race_check ? sep->arg[ab_arg + 1] : nullptr, class_race_check ? atoi(sep->arg[ab_arg + 1]) : 0) == ActionableBots::ABT_None) { + return; + } + + sbl.erase(std::remove(sbl.begin(), sbl.end(), nullptr), sbl.end()); + + Bot* first_found = nullptr; + int success_count = 0; + for (auto my_bot : sbl) { + if (my_bot->BotPassiveCheck()) { + continue; + } + + if (!first_found) { + first_found = my_bot; + } + + if (current_check) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I currently {} announce [{}] casts.'", + my_bot->GetCleanName(), + (my_bot->GetSpellTypeAnnounceCast(spell_type) ? "do" : "do not"), + Bot::GetSpellTypeNameByID(spell_type) + ).c_str() + ); + } + else { + my_bot->SetSpellTypeAnnounceCast(spell_type, type_value); + ++success_count; + } + } + if (!current_check) { + if (success_count == 1 && first_found) { + c->Message( + Chat::Green, + fmt::format( + "{} says, 'I will {} announce [{}] casts.'", + first_found->GetCleanName(), + (first_found->GetSpellTypeAnnounceCast(spell_type) ? "now" : "no longer"), + Bot::GetSpellTypeNameByID(spell_type) + ).c_str() + ); + } + else { + c->Message( + Chat::Green, + fmt::format( + "{} of your bots will {} announce [{}] casts.", + success_count, + (type_value ? "now" : "no longer"), + Bot::GetSpellTypeNameByID(spell_type) + ).c_str() + ); + } + } +} diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 99662e83b6..47ac304553 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -129,6 +129,7 @@ struct BotSpellSettings { uint16 engaged_priority; // engaged priority of the spell type uint16 pursue_priority; // pursue priority of the spell type uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type + uint16 announce_cast; // announce when casting a certain spell type Timer recast_timer; // recast timer based off delay }; diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 211e9b83e1..33709ef2b6 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -257,18 +257,20 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ SetCastedSpellType(spell_type); } - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - RaidGroupSay( - this, - fmt::format( - "Casting {} [{}] on {}.", - GetSpellName(s.SpellId), - GetSpellTypeNameByID(spell_type), - (tar == this ? "myself" : tar->GetCleanName()) - ).c_str() - ); + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; } + RaidGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(s.SpellId), + GetSpellTypeNameByID(spell_type), + (tar == this ? "myself" : tar->GetCleanName()) + ).c_str() + ); + return true; } } @@ -306,6 +308,10 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel SetCastedSpellType(spell_type); } + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + RaidGroupSay( this, fmt::format( @@ -340,41 +346,47 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - if (IsGroupSpell(bot_spell.SpellId)) { - RaidGroupSay( - this, - fmt::format( - "Curing the group with {}.", - GetSpellName(bot_spell.SpellId) - ).c_str() - ); - + if (IsGroupSpell(bot_spell.SpellId)) { + if (!IsCommandedSpell()) { const std::vector v = GatherSpellTargets(false, tar); - if (!IsCommandedSpell()) { - for (Mob* m : v) { - SetBotSpellRecastTimer(spell_type, m, true); - } + for (Mob* m : v) { + SetBotSpellRecastTimer(spell_type, m, true); } } - else { - RaidGroupSay( - this, - fmt::format( - "Curing {} with {}.", - (tar == this ? "myself" : tar->GetCleanName()), - GetSpellName(bot_spell.SpellId) - ).c_str() - ); - if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spell_type, tar, true); - } + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + RaidGroupSay( + this, + fmt::format( + "Curing the group with {}.", + GetSpellName(bot_spell.SpellId) + ).c_str() + ); + } + else { + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spell_type, tar, true); + } + + if (!GetSpellTypeAnnounceCast(spell_type)) { return true; } + + RaidGroupSay( + this, + fmt::format( + "Curing {} with {}.", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(bot_spell.SpellId) + ).c_str() + ); } + + return true; } return false; @@ -417,6 +429,11 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { SetCastedSpellType(spell_type); + + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + RaidGroupSay( this, fmt::format( @@ -473,37 +490,41 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { SetCastedSpellType(spell_type); - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - RaidGroupSay( - this, - fmt::format( - "Casting {} [{}] on {}.", - GetSpellName(s.SpellId), - GetSpellTypeNameByID(spell_type), - tar->GetCleanName() - ).c_str() - ); + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; } - return true; - } - } - } - else { - if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { - SetCastedSpellType(spell_type); - - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { RaidGroupSay( this, fmt::format( "Casting {} [{}] on {}.", - GetSpellName(bot_spell.SpellId), + GetSpellName(s.SpellId), GetSpellTypeNameByID(spell_type), tar->GetCleanName() ).c_str() ); + + return true; } + } + } + else { + if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { + SetCastedSpellType(spell_type); + + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + + RaidGroupSay( + this, + fmt::format( + "Casting {} [{}] on {}.", + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type), + tar->GetCleanName() + ).c_str() + ); return true; } @@ -520,45 +541,51 @@ bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { - if (bot_class != Class::Bard || RuleB(Bots, BardsAnnounceCasts)) { - if (IsGroupSpell(bot_spell.SpellId)) { - RaidGroupSay( - this, - fmt::format( - "Healing the group with {} [{}].", - GetSpellName(bot_spell.SpellId), - GetSpellTypeNameByID(spell_type) - ).c_str() - ); - - if (bot_class != Class::Bard) { + if (IsGroupSpell(bot_spell.SpellId)) { + if (bot_class != Class::Bard) { + if (!IsCommandedSpell()) { const std::vector v = GatherSpellTargets(false, tar); - if (!IsCommandedSpell()) { - for (Mob* m : v) { - SetBotSpellRecastTimer(spell_type, m, true); - } + for (Mob* m : v) { + SetBotSpellRecastTimer(spell_type, m, true); } } + } + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; } - else { - RaidGroupSay( - this, - fmt::format( - "Healing {} with {} [{}].", - (tar == this ? "myself" : tar->GetCleanName()), - GetSpellName(bot_spell.SpellId), - GetSpellTypeNameByID(spell_type) - ).c_str() - ); - if (bot_class != Class::Bard) { - if (!IsCommandedSpell()) { - SetBotSpellRecastTimer(spell_type, tar, true); - } + RaidGroupSay( + this, + fmt::format( + "Healing the group with {} [{}].", + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type) + ).c_str() + ); + + } + else { + if (bot_class != Class::Bard) { + if (!IsCommandedSpell()) { + SetBotSpellRecastTimer(spell_type, tar, true); } } + + if (!GetSpellTypeAnnounceCast(spell_type)) { + return true; + } + + RaidGroupSay( + this, + fmt::format( + "Healing {} with {} [{}].", + (tar == this ? "myself" : tar->GetCleanName()), + GetSpellName(bot_spell.SpellId), + GetSpellTypeNameByID(spell_type) + ).c_str() + ); } return true; From af4bbff05f535c311db0c27b7168717c6156f25c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:31:59 -0600 Subject: [PATCH 357/394] Remove rule Bots:BardsAnnounceCasts --- common/ruletypes.h | 1 - 1 file changed, 1 deletion(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index d7f477f0ca..06f49edad6 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -879,7 +879,6 @@ RULE_INT(Bots, MinStatusToBypassSpawnLimit, 100, "Minimum status to bypass spawn RULE_INT(Bots, MinStatusBypassSpawnLimit, 120, "Spawn limit with status bypass. Default 120.") RULE_INT(Bots, MinStatusToBypassCreateLimit, 100, "Minimum status to bypass create limit. Default 100.") RULE_INT(Bots, MinStatusBypassCreateLimit, 120, "Create limit with status bypass. Default 120.") -RULE_BOOL(Bots, BardsAnnounceCasts, false, "This determines whether or not Bard bots will announce that they're casting songs (Buffs, Heals, Nukes, Slows, etc.) they will always announce Mez.") RULE_BOOL(Bots, EnableBotTGB, true, "If enabled bots will cast group buffs as TGB.") RULE_BOOL(Bots, DoResponseAnimations, true, "If enabled bots will do animations to certain responses or commands.") RULE_INT(Bots, DefaultFollowDistance, 20, "Default 20. Distance a bot will follow behind.") From 11963a158c74764ea8af41245815042e7ce6bc38 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:32:38 -0600 Subject: [PATCH 358/394] Update bot.h --- zone/bot.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/zone/bot.h b/zone/bot.h index 4d779534bc..c792430a3f 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -115,8 +115,6 @@ namespace BotSettingCategories { // Update GetBotSpellCategoryName as needed constexpr uint16 START_NO_BASE = BotSettingCategories::SpellHold; constexpr uint16 START_CLIENT = BotSettingCategories::SpellDelay; constexpr uint16 END_CLIENT = BotSettingCategories::SpellMaxThreshold; - constexpr uint16 END = BotSettingCategories::SpellTypeAEOrGroupTargetCount; - constexpr uint16 END_FULL = BotSettingCategories::SpellTypeRecastDelay; constexpr uint16 END = BotSettingCategories::SpellTypeAnnounceCast; }; From a765e3239c4c175d42535e5c4ad45cb2655d1204 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:33:19 -0600 Subject: [PATCH 359/394] Remove unused no_pets option from GatherSpellTargets --- zone/bot.cpp | 2 +- zone/bot.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index def50981e1..91f07d9223 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -12656,7 +12656,7 @@ void Bot::AssignBotSpellsToTypes(std::vector& AIBot_spells, std::unor } } -std::vector Bot::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots, bool no_pets) { +std::vector Bot::GatherSpellTargets(bool entire_raid, Mob* target, bool no_clients, bool no_bots) { std::vector valid_spell_targets; auto is_valid_target = [no_clients, no_bots](Mob* member) { diff --git a/zone/bot.h b/zone/bot.h index c792430a3f..87824774ac 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -537,7 +537,7 @@ class Bot : public NPC { void SetBotTimers(std::vector timers) { bot_timers = timers; } // Targeting - std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false, bool no_pets = false); + std::vector GatherSpellTargets(bool entireRaid = false, Mob* target = nullptr, bool no_clients = false, bool no_bots = false); bool HasValidAETarget(Bot* caster, uint16 spell_id, uint16 spell_type, Mob* tar); void SetHasLoS(bool has_los) { _hasLoS = has_los; } bool HasLoS() const { return _hasLoS; } From 939e5ea38e7491778468c4a0a7b6e687f0a9e456 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:34:13 -0600 Subject: [PATCH 360/394] Move ^attack response back to normal chat window (other) --- zone/bot_commands/attack.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/bot_commands/attack.cpp b/zone/bot_commands/attack.cpp index 9efc751949..05344da125 100644 --- a/zone/bot_commands/attack.cpp +++ b/zone/bot_commands/attack.cpp @@ -67,8 +67,8 @@ void bot_command_attack(Client *c, const Seperator *sep) } if (attacker_count == 1 && first_attacker) { - Bot::RaidGroupSay( - first_attacker, + c->Message( + Chat::Green, fmt::format( "Attacking {}.", target_mob->GetCleanName() @@ -76,7 +76,7 @@ void bot_command_attack(Client *c, const Seperator *sep) ); } else { c->Message( - Chat::PetResponse, + Chat::Green, fmt::format( "{} of your bots are attacking {}.", sbl.size(), From 2211e125d84181f3cc9bcbc497cac5f5788cc72c Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 14:45:00 -0600 Subject: [PATCH 361/394] Set lower limit of spell delays to 100 rather than 1 --- zone/bot.cpp | 6 +++--- zone/bot_commands/spell_delays.cpp | 4 ++-- zone/client_bot.cpp | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 91f07d9223..c9a26d3938 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -12860,7 +12860,7 @@ uint16 Bot::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { switch (stance) { case Stance::AEBurn: case Stance::Burn: - return 1; + return 100; case Stance::Aggressive: return 2000; case Stance::Efficient: @@ -12883,7 +12883,7 @@ uint16 Bot::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { switch (stance) { case Stance::AEBurn: case Stance::Burn: - return 1; + return 100; case Stance::Aggressive: return 3000; case Stance::Efficient: @@ -12898,7 +12898,7 @@ uint16 Bot::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { case BotSpellTypes::AEFear: return 15000; default: - return 1; + return 100; } } diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 1f0f19fa69..11547f4e19 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -153,8 +153,8 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { if (sep->IsNumber(2)) { type_value = atoi(sep->arg[2]); ++ab_arg; - if (type_value < 1 || type_value > 60000) { - c->Message(Chat::Yellow, "You must enter a value between 1-60000 (1ms to 60s)."); + if (type_value < 100 || type_value > 60000) { + c->Message(Chat::Yellow, "You must enter a value between 100-60000 (100ms to 60s)."); return; } diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 1711503f99..27d5719a17 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -375,7 +375,7 @@ uint16 Client::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { case BotSpellTypes::PetCures: return 5000; default: - return 1; + return 100; } } From 09bbe87268c4327fc2cf2fdd82df6fd517e17053 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:27:25 -0600 Subject: [PATCH 362/394] Correct pet checks on GetUltimateSpell functions --- zone/bot.cpp | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c9a26d3938..7af8f4e5a0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -10553,11 +10553,13 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - if (tar->IsClient()) { - tar->GetOwner()->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + Mob* owner = tar->GetOwner(); + + if (owner->IsClient()) { + owner->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); } else { - tar->GetOwner()->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + owner->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); } } else if (IsBotSpellTypeOtherBeneficial(spell_type)) { @@ -13088,7 +13090,9 @@ uint16 Bot::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->IsClient() ? tar->GetOwner()->CastToClient()->GetSpellDelay(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->GetSpellDelay(GetPetBotSpellType(spell_type)); + Mob* owner = tar->GetOwner(); + + return owner->IsClient() ? owner->CastToClient()->GetSpellDelay(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellDelay(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { @@ -13104,7 +13108,9 @@ bool Bot::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->IsClient() ? tar->GetOwner()->CastToClient()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)); + Mob* owner = tar->GetOwner(); + + return owner->IsClient() ? owner->CastToClient()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)) : owner->CastToBot()->SpellTypeRecastCheck(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { @@ -13120,7 +13126,9 @@ uint8 Bot::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->IsClient() ? tar->GetOwner()->CastToClient()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)); + Mob* owner = tar->GetOwner(); + + return owner->IsClient() ? owner->CastToClient()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { @@ -13136,7 +13144,9 @@ uint8 Bot::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { - return tar->IsClient() ? tar->GetOwner()->CastToClient()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)) : tar->GetOwner()->CastToBot()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)); + Mob* owner = tar->GetOwner(); + + return owner->IsClient() ? owner->CastToClient()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { From c5aa1ea06f45450e257712da5c32cd38a5cf788e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:30:25 -0600 Subject: [PATCH 363/394] Add rules (Bots, AICastSpellTypeDelay, Bots, AICastSpellTypeHeldDelay) to prevent spamming of failed spell type AI casts --- common/ruletypes.h | 2 ++ zone/bot.cpp | 4 ++++ zone/bot.h | 2 ++ zone/bot_structs.h | 1 + zone/botspellsai.cpp | 21 +++++++++++++++++++++ 5 files changed, 30 insertions(+) diff --git a/common/ruletypes.h b/common/ruletypes.h index 06f49edad6..99abf1c147 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -898,6 +898,8 @@ RULE_STRING(Bots, ZonesWithSpawnLimits, "", "Comma-delimited list of zones where RULE_STRING(Bots, ZoneSpawnLimits, "", "Comma-delimited list of spawn limits for zones.") RULE_STRING(Bots, ZonesWithForcedSpawnLimits, "", "Comma-delimited list of zones where bot spawn limits are forced. This will take priority over any other type of spawn limits.") RULE_STRING(Bots, ZoneForcedSpawnLimits, "", "Comma-delimited list of forced spawn limits for zones.") +RULE_INT(Bots, AICastSpellTypeDelay, 100, "Delay in milliseconds between AI cast attempts for each spell type. Default 100ms") +RULE_INT(Bots, AICastSpellTypeHeldDelay, 2500, "Delay in milliseconds between AI cast attempts for each spell type that is held or disabled. Default 2500ms (2.5s)") RULE_CATEGORY_END() RULE_CATEGORY(Chat) diff --git a/zone/bot.cpp b/zone/bot.cpp index 7af8f4e5a0..22f18f7743 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9434,6 +9434,9 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { LogBotSpellChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); if (GetUltimateSpellHold(spell_type, tar)) { + if (!IsCommandedSpell()) { + SetSpellTypeAITimer(spell_type, RuleI(Bots, AICastSpellTypeHeldDelay)); + } LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -10521,6 +10524,7 @@ void Bot::LoadDefaultBotSettings() { t.ae_or_group_target_count = GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance); t.announce_cast = GetDefaultSpellTypeAnnounceCast(i, bot_stance); t.recast_timer.Start(); + t.ai_delay.Start(); m_bot_spell_settings.push_back(t); diff --git a/zone/bot.h b/zone/bot.h index 87824774ac..92ee7bc41b 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -637,6 +637,8 @@ class Bot : public NPC { inline void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } + inline bool SpellTypeAIDelayCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].ai_delay.GetRemainingTime(); } + void SetSpellTypeAITimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].ai_delay.Start(recast_time); } uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); diff --git a/zone/bot_structs.h b/zone/bot_structs.h index 47ac304553..baa1aa73c3 100644 --- a/zone/bot_structs.h +++ b/zone/bot_structs.h @@ -131,6 +131,7 @@ struct BotSpellSettings { uint16 ae_or_group_target_count; // require target count to cast an AE or Group spell type uint16 announce_cast; // announce when casting a certain spell type Timer recast_timer; // recast timer based off delay + Timer ai_delay; // spell timer based off delay }; struct BotSpellTypeOrder { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 33709ef2b6..f08d78b7e9 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -663,6 +663,7 @@ bool Bot::AI_PursueCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeHeldDelay)); LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -679,12 +680,18 @@ bool Bot::AI_PursueCastCheck() { continue; } + if (!SpellTypeAIDelayCheck(current_cast.spellType)) { + continue; + } + result = AttemptAICastSpell(current_cast.spellType, nullptr); if (!result && IsBotSpellTypeBeneficial(current_cast.spellType)) { result = AttemptCloseBeneficialSpells(current_cast.spellType); } + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeDelay)); + if (result) { break; } @@ -731,6 +738,7 @@ bool Bot::AI_IdleCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeHeldDelay)); LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -755,6 +763,10 @@ bool Bot::AI_IdleCastCheck() { continue; } + if (!SpellTypeAIDelayCheck(current_cast.spellType)) { + continue; + } + result = AttemptAICastSpell(current_cast.spellType, nullptr); if (result) { @@ -763,6 +775,8 @@ bool Bot::AI_IdleCastCheck() { result = AttemptCloseBeneficialSpells(current_cast.spellType); + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeDelay)); + if (result) { break; } @@ -799,6 +813,7 @@ bool Bot::AI_EngagedCastCheck() { for (auto& current_cast : cast_order) { if (current_cast.priority == 0) { + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeHeldDelay)); LogBotSpellChecksDetail("{} says, '[{}] is priority 0, skipping.'", GetCleanName(), GetSpellTypeNameByID(current_cast.spellType)); continue; } @@ -815,8 +830,14 @@ bool Bot::AI_EngagedCastCheck() { continue; } + if (!SpellTypeAIDelayCheck(current_cast.spellType)) { + continue; + } + result = AttemptAICastSpell(current_cast.spellType, nullptr); + SetSpellTypeAITimer(current_cast.spellType, RuleI(Bots, AICastSpellTypeDelay)); + if (!result && IsBotSpellTypeBeneficial(current_cast.spellType)) { result = AttemptCloseBeneficialSpells(current_cast.spellType); } From e5bcb6acfc4315fbb2d027755f5da2833014a0a3 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 15:40:47 -0600 Subject: [PATCH 364/394] Correct pet buff type logic to catch DS/Resists with other spell effects in them --- common/spdat.cpp | 57 ++++++++++++++++++++++++++++++++++++++++++++ common/spdat.h | 2 ++ zone/bot.cpp | 7 +++--- zone/botspellsai.cpp | 5 +--- 4 files changed, 64 insertions(+), 7 deletions(-) diff --git a/common/spdat.cpp b/common/spdat.cpp index 5151d4d5a1..831c7f35ec 100644 --- a/common/spdat.cpp +++ b/common/spdat.cpp @@ -2824,6 +2824,34 @@ bool IsResurrectSpell(uint16 spell_id) return IsEffectInSpell(spell_id, SE_Revive); } +bool IsResistanceBuffSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (IsBlankSpellEffect(spell_id, i)) { + continue; + } + + if ( + spell.effect_id[i] == SE_ResistFire || + spell.effect_id[i] == SE_ResistCold || + spell.effect_id[i] == SE_ResistPoison || + spell.effect_id[i] == SE_ResistDisease || + spell.effect_id[i] == SE_ResistMagic || + spell.effect_id[i] == SE_ResistCorruption || + spell.effect_id[i] == SE_ResistAll + ) { + return true; + } + } + + return false; +} + bool IsResistanceOnlySpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) { return false; @@ -2876,6 +2904,35 @@ bool IsDamageShieldOnlySpell(uint16 spell_id) { return true; } +bool IsDamageShieldAndResistSpell(uint16 spell_id) { + if (!IsValidSpell(spell_id)) { + return false; + } + + const auto& spell = spells[spell_id]; + + for (int i = 0; i < EFFECT_COUNT; i++) { + if (IsBlankSpellEffect(spell_id, i)) { + continue; + } + + if ( + spell.effect_id[i] != SE_DamageShield && + spell.effect_id[i] != SE_ResistFire && + spell.effect_id[i] != SE_ResistCold && + spell.effect_id[i] != SE_ResistPoison && + spell.effect_id[i] != SE_ResistDisease && + spell.effect_id[i] != SE_ResistMagic && + spell.effect_id[i] != SE_ResistCorruption && + spell.effect_id[i] != SE_ResistAll + ) { + return false; + } + } + + return true; +} + bool IsHateSpell(uint16 spell_id) { if (!IsValidSpell(spell_id)) { return false; diff --git a/common/spdat.h b/common/spdat.h index 08cf55ee88..b60ab51865 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -1908,8 +1908,10 @@ bool IsLichSpell(uint16 spell_id); bool IsInstantHealSpell(uint32 spell_id); bool IsResurrectSpell(uint16 spell_id); bool RequiresStackCheck(uint16 spell_type); +bool IsResistanceBuffSpell(uint16 spell_id); bool IsResistanceOnlySpell(uint16 spell_id); bool IsDamageShieldOnlySpell(uint16 spell_id); +bool IsDamageShieldAndResistSpell(uint16 spell_id); bool IsHateSpell(uint16 spell_id); #endif diff --git a/zone/bot.cpp b/zone/bot.cpp index 22f18f7743..7657f8b361 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -11572,7 +11572,8 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { case BotSpellTypes::PetBuffs: if ( IsResistanceOnlySpell(spell_id) || - IsDamageShieldOnlySpell(spell_id) + IsDamageShieldOnlySpell(spell_id) || + IsDamageShieldAndResistSpell(spell_id) ) { return false; } @@ -11580,14 +11581,14 @@ bool Bot::IsValidSpellTypeBySpellID(uint16 spell_type, uint16 spell_id) { return true; case BotSpellTypes::ResistBuffs: case BotSpellTypes::PetResistBuffs: - if (IsResistanceOnlySpell(spell_id)) { + if (IsResistanceBuffSpell(spell_id)) { return true; } return false; case BotSpellTypes::DamageShields: case BotSpellTypes::PetDamageShields: - if (IsDamageShieldOnlySpell(spell_id)) { + if (IsEffectInSpell(spell_id, SE_DamageShield)) { return true; } diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index f08d78b7e9..8eab457e0f 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -32,10 +32,7 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ !AI_HasSpells() || (spell_type == BotSpellTypes::Pet && tar != this) || (IsPetBotSpellType(spell_type) && !tar->IsPet()) || - (spell_type == BotSpellTypes::Buff && tar->IsPet()) || - (spell_type == BotSpellTypes::InCombatBuffSong && tar->IsPet()) || - (spell_type == BotSpellTypes::OutOfCombatBuffSong && tar->IsPet()) || - (spell_type == BotSpellTypes::PreCombatBuffSong && tar->IsPet()) || + (!IsPetBotSpellType(spell_type) && tar->IsPet()) || (!RuleB(Bots, AllowBuffingHealingFamiliars) && tar->IsFamiliar()) || (tar->IsPet() && tar->IsCharmed() && spell_type == BotSpellTypes::PetBuffs && !RuleB(Bots, AllowCharmedPetBuffs)) || (tar->IsPet() && tar->IsCharmed() && spell_type == BotSpellTypes::PetCures && !RuleB(Bots, AllowCharmedPetCures)) || From 660cf91297fbde294451d7486d1a5cb67e42de94 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:35:54 -0600 Subject: [PATCH 365/394] Fix defaults for clients --- zone/client.h | 6 ++--- zone/client_bot.cpp | 64 +++++++-------------------------------------- 2 files changed, 13 insertions(+), 57 deletions(-) diff --git a/zone/client.h b/zone/client.h index 3e4162d405..61d924f3b7 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2283,9 +2283,9 @@ class Client : public Mob int GetBotSetting(uint8 setting_type, uint16 bot_setting); void SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value); - uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint16 GetDefaultSpellDelay(uint16 spell_type); + uint8 GetDefaultSpellMinThreshold(uint16 spell_type); + uint8 GetDefaultSpellMaxThreshold(uint16 spell_type); inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } inline void SetSpellDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 27d5719a17..f1fc61dbd2 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -348,7 +348,7 @@ void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) return; } -uint16 Client::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { +uint16 Client::GetDefaultSpellDelay(uint16 spell_type) { switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: @@ -379,83 +379,39 @@ uint16 Client::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { } } -uint8 Client::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { +uint8 Client::GetDefaultSpellMinThreshold(uint16 spell_type) { switch (spell_type) { default: return 0; } } -uint8 Client::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { - uint8 bot_class = GetClass(); +uint8 Client::GetDefaultSpellMaxThreshold(uint16 spell_type) { + uint8 client_class = GetClass(); switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 40; - case Stance::Efficient: - default: - return 25; - } + return 25; case BotSpellTypes::FastHeals: case BotSpellTypes::PetFastHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 55; - case Stance::Efficient: - return 35; - default: - return 40; - } + return 40; case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: case BotSpellTypes::PetRegularHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 70; - case Stance::Efficient: - return 50; - default: - return 60; - } + return 60; case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::PetCompleteHeals: - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 90; - case Stance::Efficient: - return 65; - default: - return 80; - } + return 80; case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: - if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { + if (client_class == Class::Necromancer || client_class == Class::Shaman) { return 60; } else { - switch (stance) { - case Stance::AEBurn: - case Stance::Burn: - case Stance::Aggressive: - return 95; - case Stance::Efficient: - return 80; - default: - return 90; - } + return 90; } case BotSpellTypes::Buff: case BotSpellTypes::Cure: From fef8c623ff43a1b223b201acf28c443cecf48b8f Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 16:45:20 -0600 Subject: [PATCH 366/394] Add more logic for necros/shaman for default heal thresholds due to lich and canni --- zone/bot.cpp | 32 ++++++++++++++++++++++++++++++-- zone/client_bot.cpp | 9 +++++++-- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 7657f8b361..b40149aed6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -12989,6 +12989,20 @@ uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { } case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: + if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellHold(BotSpellTypes::InCombatBuff))) { + return 60; + } + + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 70; + case Stance::Efficient: + return 50; + default: + return 60; + } case BotSpellTypes::PetRegularHeals: switch (stance) { case Stance::AEBurn: @@ -13002,6 +13016,20 @@ uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { } case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: + if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellHold(BotSpellTypes::InCombatBuff))) { + return 55; + } + + switch (stance) { + case Stance::AEBurn: + case Stance::Burn: + case Stance::Aggressive: + return 90; + case Stance::Efficient: + return 65; + default: + return 80; + } case BotSpellTypes::PetCompleteHeals: switch (stance) { case Stance::AEBurn: @@ -13047,8 +13075,8 @@ uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: - if (bot_class == Class::Necromancer || bot_class == Class::Shaman) { - return 60; + if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellHold(BotSpellTypes::InCombatBuff))) { + return 70; } else { switch (stance) { diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index f1fc61dbd2..2219c19dcf 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -403,12 +403,17 @@ uint8 Client::GetDefaultSpellMaxThreshold(uint16 spell_type) { case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: case BotSpellTypes::PetCompleteHeals: - return 80; + if (client_class == Class::Necromancer || client_class == Class::Shaman) { + return 55; + } + else { + return 80; + } case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: if (client_class == Class::Necromancer || client_class == Class::Shaman) { - return 60; + return 70; } else { return 90; From 980aa65eccffd02dbeca9692760ca788723110af Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:05:50 -0600 Subject: [PATCH 367/394] Rename SpellHold, SpellDelay, SpellMinThreshold, SpellMaxThreshold, SpellRecastDelay to fit SpellType style naming --- zone/bot.cpp | 151 ++++++++++++--------- zone/bot.h | 34 ++--- zone/bot_commands/default_settings.cpp | 48 ++++--- zone/bot_commands/spell_delays.cpp | 12 +- zone/bot_commands/spell_holds.cpp | 6 +- zone/bot_commands/spell_max_thresholds.cpp | 12 +- zone/bot_commands/spell_min_thresholds.cpp | 12 +- zone/client.h | 18 +-- zone/client_bot.cpp | 34 ++--- 9 files changed, 177 insertions(+), 150 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index b40149aed6..98b8a242b1 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -2928,7 +2928,7 @@ bool Bot::TryFacingTarget(Mob* tar) { bool Bot::TryEvade(Mob* tar) { - if (GetSpellHold(BotSpellTypes::Escape)) { + if (GetSpellTypeHold(BotSpellTypes::Escape)) { return false; } @@ -7856,7 +7856,11 @@ bool Bot::GetNeedsHateRedux(Mob *tar) { } if (tar->IsBot()) { - if (tar->GetHPRatio() > GetUltimateSpellMinThreshold(BotSpellTypes::HateRedux, tar) && tar->GetHPRatio() < GetUltimateSpellMaxThreshold(BotSpellTypes::HateRedux, tar)) { + if (tar->GetHPRatio() > GetUltimateSpellTypeMinThreshold(BotSpellTypes::HateRedux, tar) && tar->GetHPRatio() < + GetUltimateSpellTypeMaxThreshold( + BotSpellTypes::HateRedux, + tar + )) { return true; } } @@ -9433,11 +9437,11 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { LogBotSpellChecksDetail("{} says, 'Running [{}] PreChecks on [{}].'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); - if (GetUltimateSpellHold(spell_type, tar)) { + if (GetUltimateSpellTypeHold(spell_type, tar)) { if (!IsCommandedSpell()) { SetSpellTypeAITimer(spell_type, RuleI(Bots, AICastSpellTypeHeldDelay)); } - LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellHold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeHold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9464,8 +9468,8 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { return false; } - if (!GetUltimateSpellDelayCheck(spell_type, tar)) { - LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + if (!GetUltimateSpellTypeDelayCheck(spell_type, tar)) { + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeDelayCheck.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } @@ -9475,10 +9479,10 @@ bool Bot::PrecastChecks(Mob* tar, uint16 spell_type) { return true; default: if ( - GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellMinThreshold(spell_type, tar) || - GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellMaxThreshold(spell_type, tar) + GetHPRatioForSpellType(spell_type, tar) < GetUltimateSpellTypeMinThreshold(spell_type, tar) || + GetHPRatioForSpellType(spell_type, tar) > GetUltimateSpellTypeMaxThreshold(spell_type, tar) ) { - LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellMinThreshold or GetUltimateSpellMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); + LogBotSpellChecksDetail("{} says, 'Cancelling cast of [{}] on [{}] due to GetUltimateSpellTypeMinThreshold or GetUltimateSpellTypeMaxThreshold.'", GetCleanName(), GetSpellTypeNameByID(spell_type), tar->GetCleanName()); return false; } } @@ -10287,16 +10291,16 @@ void Bot::SetBotSetting(uint8 setting_type, uint16 bot_setting, int setting_valu SetBotBaseSetting(bot_setting, setting_value); break; case BotSettingCategories::SpellHold: - SetSpellHold(bot_setting, setting_value); + SetSpellTypeHold(bot_setting, setting_value); break; case BotSettingCategories::SpellDelay: - SetSpellDelay(bot_setting, setting_value); + SetSpellTypeDelay(bot_setting, setting_value); break; case BotSettingCategories::SpellMinThreshold: - SetSpellMinThreshold(bot_setting, setting_value); + SetSpellTypeMinThreshold(bot_setting, setting_value); break; case BotSettingCategories::SpellMaxThreshold: - SetSpellMaxThreshold(bot_setting, setting_value); + SetSpellTypeMaxThreshold(bot_setting, setting_value); break; case BotSettingCategories::SpellTypeResistLimit: SetSpellTypeResistLimit(bot_setting, setting_value); @@ -10493,10 +10497,10 @@ void Bot::LoadDefaultBotSettings() { t.spell_type = i; t.short_name = GetSpellTypeShortNameByID(i); t.name = GetSpellTypeNameByID(i); - t.hold = GetDefaultSpellHold(i, bot_stance); - t.delay = GetDefaultSpellDelay(i, bot_stance); - t.min_threshold = GetDefaultSpellMinThreshold(i, bot_stance); - t.max_threshold = GetDefaultSpellMaxThreshold(i, bot_stance); + t.hold = GetDefaultSpellTypeHold(i, bot_stance); + t.delay = GetDefaultSpellTypeDelay(i, bot_stance); + t.min_threshold = GetDefaultSpellTypeMinThreshold(i, bot_stance); + t.max_threshold = GetDefaultSpellTypeMaxThreshold(i, bot_stance); t.resist_limit = GetDefaultSpellTypeResistLimit(i, bot_stance); t.aggro_check = GetDefaultSpellTypeAggroCheck(i, bot_stance); t.min_mana_pct = GetDefaultSpellTypeMinManaLimit(i, bot_stance); @@ -10529,7 +10533,11 @@ void Bot::LoadDefaultBotSettings() { m_bot_spell_settings.push_back(t); LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}] - [{} [#{}] stance]'", GetCleanName(), t.name, t.short_name, t.spell_type, Stance::GetName(bot_stance), bot_stance); - LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellHold(i, bot_stance), GetDefaultSpellDelay(i, bot_stance), GetDefaultSpellMinThreshold(i, bot_stance), GetDefaultSpellMaxThreshold(i, bot_stance)); + LogBotSettingsDetail("{} says, 'Hold = [{}] | Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), + GetDefaultSpellTypeHold(i, bot_stance), + GetDefaultSpellTypeDelay(i, bot_stance), + GetDefaultSpellTypeMinThreshold(i, bot_stance), + GetDefaultSpellTypeMaxThreshold(i, bot_stance)); LogBotSettingsDetail("{} says, 'AggroCheck = [{}] | MinManaPCT = [{}\%] | MaxManaPCT = [{}\%] | MinHPPCT = [{}\% | MaxHPPCT = [{}\%]'", GetCleanName(), GetDefaultSpellTypeAggroCheck(i, bot_stance), GetDefaultSpellTypeMinManaLimit(i, bot_stance), GetDefaultSpellTypeMaxManaLimit(i, bot_stance), GetDefaultSpellTypeMinHPLimit(i, bot_stance), GetDefaultSpellTypeMaxHPLimit(i, bot_stance)); LogBotSettingsDetail("{} says, 'IdlePriority = [{}] | EngagedPriority = [{}] | PursuePriority = [{}]'", GetCleanName(), GetDefaultSpellTypeIdlePriority(i, GetClass(), bot_stance), GetDefaultSpellTypeEngagedPriority(i, GetClass(), bot_stance), GetDefaultSpellTypePursuePriority(i, GetClass(), bot_stance)); LogBotSettingsDetail("{} says, 'TargetCount = [{}] | AnnounceCast = [{}]'", GetCleanName(), GetDefaultSpellTypeAEOrGroupTargetCount(i, bot_stance), GetDefaultSpellTypeAnnounceCast(i, bot_stance)); @@ -10560,22 +10568,22 @@ void Bot::SetBotSpellRecastTimer(uint16 spell_type, Mob* tar, bool precast) { Mob* owner = tar->GetOwner(); if (owner->IsClient()) { - owner->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + owner->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay)); } else { - owner->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + owner->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay)); } } else if (IsBotSpellTypeOtherBeneficial(spell_type)) { if (tar->IsClient()) { - tar->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + tar->CastToClient()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay)); } else { - tar->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + tar->CastToBot()->SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay)); } } else { - SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellDelay(spell_type, tar) + added_delay)); + SetSpellTypeRecastTimer(spell_type, (GetUltimateSpellTypeDelay(spell_type, tar) + added_delay)); } } @@ -10623,13 +10631,13 @@ int Bot::GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 s case BotSettingCategories::BaseSetting: return GetDefaultBotBaseSetting(setting_type, stance); case BotSettingCategories::SpellHold: - return GetDefaultSpellHold(setting_type, stance); + return GetDefaultSpellTypeHold(setting_type, stance); case BotSettingCategories::SpellDelay: - return GetDefaultSpellDelay(setting_type, stance); + return GetDefaultSpellTypeDelay(setting_type, stance); case BotSettingCategories::SpellMinThreshold: - return GetDefaultSpellMinThreshold(setting_type, stance); + return GetDefaultSpellTypeMinThreshold(setting_type, stance); case BotSettingCategories::SpellMaxThreshold: - return GetDefaultSpellMaxThreshold(setting_type, stance); + return GetDefaultSpellTypeMaxThreshold(setting_type, stance); case BotSettingCategories::SpellTypeResistLimit: return GetDefaultSpellTypeResistLimit(setting_type, stance); case BotSettingCategories::SpellTypeAggroCheck: @@ -10662,13 +10670,13 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { case BotSettingCategories::BaseSetting: return GetBotBaseSetting(setting_type); case BotSettingCategories::SpellHold: - return GetSpellHold(setting_type); + return GetSpellTypeHold(setting_type); case BotSettingCategories::SpellDelay: - return GetSpellDelay(setting_type); + return GetSpellTypeDelay(setting_type); case BotSettingCategories::SpellMinThreshold: - return GetSpellMinThreshold(setting_type); + return GetSpellTypeMinThreshold(setting_type); case BotSettingCategories::SpellMaxThreshold: - return GetSpellMaxThreshold(setting_type); + return GetSpellTypeMaxThreshold(setting_type); case BotSettingCategories::SpellTypeResistLimit: return GetSpellTypeResistLimit(setting_type); case BotSettingCategories::SpellTypeAggroCheck: @@ -10696,7 +10704,7 @@ int Bot::GetSetting(uint16 setting_category, uint16 setting_type) { } } -bool Bot::GetDefaultSpellHold(uint16 spell_type, uint8 stance) { +bool Bot::GetDefaultSpellTypeHold(uint16 spell_type, uint8 stance) { uint8 bot_class = GetClass(); switch (spell_type) { @@ -11162,16 +11170,16 @@ uint16 Bot::GetDefaultSpellTypeAnnounceCast(uint16 spell_type, uint8 stance) { return 1; } -bool Bot::GetUltimateSpellHold(uint16 spell_type, Mob* tar) { +bool Bot::GetUltimateSpellTypeHold(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellHold(spell_type); + return GetSpellTypeHold(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->IsPetOwnerBot()) { - return tar->GetOwner()->CastToBot()->GetSpellHold(GetPetBotSpellType(spell_type)); + return tar->GetOwner()->CastToBot()->GetSpellTypeHold(GetPetBotSpellType(spell_type)); } - return GetSpellHold(spell_type); + return GetSpellTypeHold(spell_type); } void Bot::SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority) { @@ -11974,7 +11982,7 @@ bool Bot::RequiresLoSForPositioning() { } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - if (IsBotSpellTypeDetrimental(i) && !GetSpellHold(i)) { + if (IsBotSpellTypeDetrimental(i) && !GetSpellTypeHold(i)) { return true; } } @@ -12100,44 +12108,44 @@ void Bot::CopySettings(Bot* to, uint8 setting_type, uint16 spell_type) { break; case BotSettingCategories::SpellHold: if (spell_type != UINT16_MAX) { - to->SetSpellHold(spell_type, GetSpellHold(spell_type)); + to->SetSpellTypeHold(spell_type, GetSpellTypeHold(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - to->SetSpellHold(i, GetSpellHold(i)); + to->SetSpellTypeHold(i, GetSpellTypeHold(i)); } } break; case BotSettingCategories::SpellDelay: if (spell_type != UINT16_MAX) { - to->SetSpellDelay(spell_type, GetSpellDelay(spell_type)); + to->SetSpellTypeDelay(spell_type, GetSpellTypeDelay(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - to->SetSpellDelay(i, GetSpellDelay(i)); + to->SetSpellTypeDelay(i, GetSpellTypeDelay(i)); } } break; case BotSettingCategories::SpellMinThreshold: if (spell_type != UINT16_MAX) { - to->SetSpellMinThreshold(spell_type, GetSpellMinThreshold(spell_type)); + to->SetSpellTypeMinThreshold(spell_type, GetSpellTypeMinThreshold(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - to->SetSpellMinThreshold(i, GetSpellMinThreshold(i)); + to->SetSpellTypeMinThreshold(i, GetSpellTypeMinThreshold(i)); } } break; case BotSettingCategories::SpellMaxThreshold: if (spell_type != UINT16_MAX) { - to->SetSpellMaxThreshold(spell_type, GetSpellMaxThreshold(spell_type)); + to->SetSpellTypeMaxThreshold(spell_type, GetSpellTypeMaxThreshold(spell_type)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - to->SetSpellMaxThreshold(i, GetSpellMaxThreshold(i)); + to->SetSpellTypeMaxThreshold(i, GetSpellTypeMaxThreshold(i)); } } @@ -12836,7 +12844,7 @@ std::string Bot::GetSubTypeNameByID(uint16 sub_type) { return IsValidBotSpellType(sub_type) ? botSubType_names[sub_type] : "UNKNOWN SUBTYPE"; } -uint16 Bot::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { +uint16 Bot::GetDefaultSpellTypeDelay(uint16 spell_type, uint8 stance) { switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: @@ -12909,7 +12917,7 @@ uint16 Bot::GetDefaultSpellDelay(uint16 spell_type, uint8 stance) { } } -uint8 Bot::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { +uint8 Bot::GetDefaultSpellTypeMinThreshold(uint16 spell_type, uint8 stance) { switch (spell_type) { case BotSpellTypes::AEDebuff: case BotSpellTypes::Debuff: @@ -12957,7 +12965,7 @@ uint8 Bot::GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance) { } } -uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { +uint8 Bot::GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance) { uint8 bot_class = GetClass(); switch (spell_type) { @@ -12989,7 +12997,7 @@ uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { } case BotSpellTypes::GroupHeals: case BotSpellTypes::RegularHeal: - if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellHold(BotSpellTypes::InCombatBuff))) { + if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellTypeHold(BotSpellTypes::InCombatBuff))) { return 60; } @@ -13016,7 +13024,7 @@ uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { } case BotSpellTypes::CompleteHeal: case BotSpellTypes::GroupCompleteHeals: - if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellHold(BotSpellTypes::InCombatBuff))) { + if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellTypeHold(BotSpellTypes::InCombatBuff))) { return 55; } @@ -13075,7 +13083,7 @@ uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { case BotSpellTypes::GroupHoTHeals: case BotSpellTypes::HoTHeals: case BotSpellTypes::PetHoTHeals: - if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellHold(BotSpellTypes::InCombatBuff))) { + if (bot_class == Class::Necromancer || (bot_class == Class::Shaman && !GetSpellTypeHold(BotSpellTypes::InCombatBuff))) { return 70; } else { @@ -13117,25 +13125,28 @@ uint8 Bot::GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance) { } } -uint16 Bot::GetUltimateSpellDelay(uint16 spell_type, Mob* tar) { +uint16 Bot::GetUltimateSpellTypeDelay(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellDelay(spell_type); + return GetSpellTypeDelay(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { Mob* owner = tar->GetOwner(); - return owner->IsClient() ? owner->CastToClient()->GetSpellDelay(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellDelay(GetPetBotSpellType(spell_type)); + return owner->IsClient() ? owner->CastToClient()->GetSpellTypeDelay(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeDelay( + GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->IsClient() ? tar->CastToClient()->GetSpellDelay(spell_type) : tar->CastToBot()->GetSpellDelay(spell_type); + return tar->IsClient() ? tar->CastToClient()->GetSpellTypeDelay(spell_type) : tar->CastToBot()->GetSpellTypeDelay( + spell_type + ); } - return GetSpellDelay(spell_type); + return GetSpellTypeDelay(spell_type); } -bool Bot::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { +bool Bot::GetUltimateSpellTypeDelayCheck(uint16 spell_type, Mob* tar) { if (!tar) { return SpellTypeRecastCheck(spell_type); } @@ -13153,40 +13164,46 @@ bool Bot::GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar) { return SpellTypeRecastCheck(spell_type); } -uint8 Bot::GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar) { +uint8 Bot::GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellMinThreshold(spell_type); + return GetSpellTypeMinThreshold(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { Mob* owner = tar->GetOwner(); - return owner->IsClient() ? owner->CastToClient()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellMinThreshold(GetPetBotSpellType(spell_type)); + return owner->IsClient() ? owner->CastToClient()->GetSpellTypeMinThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeMinThreshold( + GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->IsClient() ? tar->CastToClient()->GetSpellMinThreshold(spell_type) : tar->CastToBot()->GetSpellMinThreshold(spell_type); + return tar->IsClient() ? tar->CastToClient()->GetSpellTypeMinThreshold(spell_type) : tar->CastToBot()->GetSpellTypeMinThreshold( + spell_type + ); } - return GetSpellMinThreshold(spell_type); + return GetSpellTypeMinThreshold(spell_type); } -uint8 Bot::GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar) { +uint8 Bot::GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar) { if (!tar) { - return GetSpellMaxThreshold(spell_type); + return GetSpellTypeMaxThreshold(spell_type); } if (tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot()) { Mob* owner = tar->GetOwner(); - return owner->IsClient() ? owner->CastToClient()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellMaxThreshold(GetPetBotSpellType(spell_type)); + return owner->IsClient() ? owner->CastToClient()->GetSpellTypeMaxThreshold(GetPetBotSpellType(spell_type)) : owner->CastToBot()->GetSpellTypeMaxThreshold( + GetPetBotSpellType(spell_type)); } if (IsBotSpellTypeOtherBeneficial(spell_type) && tar->IsOfClientBot()) { - return tar->IsClient() ? tar->CastToClient()->GetSpellMaxThreshold(spell_type) : tar->CastToBot()->GetSpellMaxThreshold(spell_type); + return tar->IsClient() ? tar->CastToClient()->GetSpellTypeMaxThreshold(spell_type) : tar->CastToBot()->GetSpellTypeMaxThreshold( + spell_type + ); } - return GetSpellMaxThreshold(spell_type); + return GetSpellTypeMaxThreshold(spell_type); } bool Bot::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) { diff --git a/zone/bot.h b/zone/bot.h index 92ee7bc41b..dd84509c89 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -570,7 +570,7 @@ class Bot : public NPC { void LoadDefaultBotSettings(); int GetDefaultSetting(uint16 setting_category, uint16 setting_type, uint8 stance = Stance::Balanced); int GetDefaultBotBaseSetting(uint16 bot_setting, uint8 stance = Stance::Balanced); - bool GetDefaultSpellHold(uint16 spell_type, uint8 stance = Stance::Balanced); + bool GetDefaultSpellTypeHold(uint16 spell_type, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypePriority(uint16 spell_type, uint8 priority_type, uint8 bot_class, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeIdlePriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced); uint16 GetDefaultSpellTypeEngagedPriority(uint16 spell_type, uint8 bot_class, uint8 stance = Stance::Balanced); @@ -607,9 +607,6 @@ class Bot : public NPC { void SetBotBlockedBuffs(std::vector blocked_buffs) { bot_blocked_buffs = blocked_buffs; } void SetBotSpellRecastTimer(uint16 spell_type, Mob* spelltar, bool pre_cast = false); - inline bool GetSpellHold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].hold; } - inline void SetSpellHold(uint16 spell_type, bool value) { m_bot_spell_settings[spell_type].hold = value; } - bool GetUltimateSpellHold(uint16 spell_type, Mob* tar); uint16 GetSpellTypePriority(uint16 spell_type, uint8 priority_type); void SetSpellTypePriority(uint16 spell_type, uint8 priority_type, uint16 priority); inline uint16 GetSpellTypeResistLimit(uint16 spell_type) const { return m_bot_spell_settings[spell_type].resist_limit; } @@ -629,23 +626,26 @@ class Bot : public NPC { inline void SetSpellTypeAEOrGroupTargetCount(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].ae_or_group_target_count = value; } inline uint16 GetSpellTypeAnnounceCast(uint16 spell_type) const { return m_bot_spell_settings[spell_type].announce_cast; } inline void SetSpellTypeAnnounceCast(uint16 spell_type, uint16 value) { m_bot_spell_settings[spell_type].announce_cast = value; } - inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } - inline void SetSpellDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } - inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } - inline void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].min_threshold = threshold_value; } - inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } - inline void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } + inline bool GetSpellTypeHold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].hold; } + inline void SetSpellTypeHold(uint16 spell_type, bool value) { m_bot_spell_settings[spell_type].hold = value; } + inline uint16 GetSpellTypeDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } + inline void SetSpellTypeDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } + inline uint8 GetSpellTypeMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } + inline void SetSpellTypeMinThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].min_threshold = threshold_value; } + inline uint8 GetSpellTypeMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } + inline void SetSpellTypeMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } inline bool SpellTypeAIDelayCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].ai_delay.GetRemainingTime(); } void SetSpellTypeAITimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].ai_delay.Start(recast_time); } - uint16 GetDefaultSpellDelay(uint16 spell_type, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); - uint8 GetDefaultSpellMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); - uint16 GetUltimateSpellDelay(uint16 spell_type, Mob* tar); - bool GetUltimateSpellDelayCheck(uint16 spell_type, Mob* tar); - uint8 GetUltimateSpellMinThreshold(uint16 spell_type, Mob* tar); - uint8 GetUltimateSpellMaxThreshold(uint16 spell_type, Mob* tar); + bool GetUltimateSpellTypeHold(uint16 spell_type, Mob* tar); + uint16 GetDefaultSpellTypeDelay(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMinThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint8 GetDefaultSpellTypeMaxThreshold(uint16 spell_type, uint8 stance = Stance::Balanced); + uint16 GetUltimateSpellTypeDelay(uint16 spell_type, Mob* tar); + bool GetUltimateSpellTypeDelayCheck(uint16 spell_type, Mob* tar); + uint8 GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar); + uint8 GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar); void SetIllusionBlock(bool value) { _illusionBlock = value; } bool GetIllusionBlock() const { return _illusionBlock; } bool GetShowHelm() const { return _showHelm; } diff --git a/zone/bot_commands/default_settings.cpp b/zone/bot_commands/default_settings.cpp index 7c22312c07..40df680797 100644 --- a/zone/bot_commands/default_settings.cpp +++ b/zone/bot_commands/default_settings.cpp @@ -219,10 +219,14 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "spelltypesettings")) { if (spell_type != UINT16_MAX) { - my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(spell_type, bot_stance)); - my_bot->SetSpellDelay(spell_type, my_bot->GetDefaultSpellDelay(spell_type, bot_stance)); - my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); - my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); + my_bot->SetSpellTypeHold(spell_type, my_bot->GetDefaultSpellTypeHold(spell_type, bot_stance)); + my_bot->SetSpellTypeDelay(spell_type, my_bot->GetDefaultSpellTypeDelay(spell_type, bot_stance)); + my_bot->SetSpellTypeMinThreshold( + spell_type, + my_bot->GetDefaultSpellTypeMinThreshold(spell_type, bot_stance)); + my_bot->SetSpellTypeMaxThreshold( + spell_type, + my_bot->GetDefaultSpellTypeMaxThreshold(spell_type, bot_stance)); my_bot->SetSpellTypeAggroCheck(spell_type, my_bot->GetDefaultSpellTypeAggroCheck(spell_type, bot_stance)); my_bot->SetSpellTypeResistLimit(spell_type, my_bot->GetDefaultSpellTypeResistLimit(spell_type, bot_stance)); my_bot->SetSpellTypeMinManaLimit(spell_type, my_bot->GetDefaultSpellTypeMinManaLimit(spell_type, bot_stance)); @@ -237,10 +241,10 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); + my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance)); + my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance)); + my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance)); + my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance)); my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance)); my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); @@ -263,10 +267,10 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); + my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance)); + my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance)); + my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance)); + my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance)); my_bot->SetSpellTypeAggroCheck(i, my_bot->GetDefaultSpellTypeAggroCheck(i, bot_stance)); my_bot->SetSpellTypeResistLimit(i, my_bot->GetDefaultSpellTypeResistLimit(i, bot_stance)); my_bot->SetSpellTypeMinManaLimit(i, my_bot->GetDefaultSpellTypeMinManaLimit(i, bot_stance)); @@ -288,11 +292,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "holds")) { if (spell_type != UINT16_MAX) { - my_bot->SetSpellHold(spell_type, my_bot->GetDefaultSpellHold(spell_type, bot_stance)); + my_bot->SetSpellTypeHold(spell_type, my_bot->GetDefaultSpellTypeHold(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellHold(i, my_bot->GetDefaultSpellHold(i, bot_stance)); + my_bot->SetSpellTypeHold(i, my_bot->GetDefaultSpellTypeHold(i, bot_stance)); } } @@ -300,11 +304,11 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "delays")) { if (spell_type != UINT16_MAX) { - my_bot->SetSpellDelay(spell_type, my_bot->GetDefaultSpellDelay(spell_type, bot_stance)); + my_bot->SetSpellTypeDelay(spell_type, my_bot->GetDefaultSpellTypeDelay(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellDelay(i, my_bot->GetDefaultSpellDelay(i, bot_stance)); + my_bot->SetSpellTypeDelay(i, my_bot->GetDefaultSpellTypeDelay(i, bot_stance)); } } @@ -312,11 +316,13 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "minthresholds")) { if (spell_type != UINT16_MAX) { - my_bot->SetSpellMinThreshold(spell_type, my_bot->GetDefaultSpellMinThreshold(spell_type, bot_stance)); + my_bot->SetSpellTypeMinThreshold( + spell_type, + my_bot->GetDefaultSpellTypeMinThreshold(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellMinThreshold(i, my_bot->GetDefaultSpellMinThreshold(i, bot_stance)); + my_bot->SetSpellTypeMinThreshold(i, my_bot->GetDefaultSpellTypeMinThreshold(i, bot_stance)); } } @@ -324,11 +330,13 @@ void bot_command_default_settings(Client* c, const Seperator* sep) } else if (!strcasecmp(sep->arg[1], "maxthresholds")) { if (spell_type != UINT16_MAX) { - my_bot->SetSpellMaxThreshold(spell_type, my_bot->GetDefaultSpellMaxThreshold(spell_type, bot_stance)); + my_bot->SetSpellTypeMaxThreshold( + spell_type, + my_bot->GetDefaultSpellTypeMaxThreshold(spell_type, bot_stance)); } else { for (uint16 i = BotSpellTypes::START; i <= BotSpellTypes::END; ++i) { - my_bot->SetSpellMaxThreshold(i, my_bot->GetDefaultSpellMaxThreshold(i, bot_stance)); + my_bot->SetSpellTypeMaxThreshold(i, my_bot->GetDefaultSpellTypeMaxThreshold(i, bot_stance)); } } diff --git a/zone/bot_commands/spell_delays.cpp b/zone/bot_commands/spell_delays.cpp index 11547f4e19..98efb3f225 100644 --- a/zone/bot_commands/spell_delays.cpp +++ b/zone/bot_commands/spell_delays.cpp @@ -220,12 +220,12 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { "{} says, 'My [{}] spell delay is currently [{}] seconds.'", my_bot->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - my_bot->GetSpellDelay(spell_type) / 1000.00 + my_bot->GetSpellTypeDelay(spell_type) / 1000.00 ).c_str() ); } else { - my_bot->SetSpellDelay(spell_type, type_value); + my_bot->SetSpellTypeDelay(spell_type, type_value); ++success_count; } } @@ -238,7 +238,7 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { "{} says, 'My [{}] spell delay was set to [{}] seconds.'", first_found->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - first_found->GetSpellDelay(spell_type) / 1000.00 + first_found->GetSpellTypeDelay(spell_type) / 1000.00 ).c_str() ); } @@ -262,19 +262,19 @@ void bot_command_spell_delays(Client* c, const Seperator* sep) { fmt::format( "Your [{}] spell delay is currently [{}] seconds.", Bot::GetSpellTypeNameByID(spell_type), - c->GetSpellDelay(spell_type) / 1000.00 + c->GetSpellTypeDelay(spell_type) / 1000.00 ).c_str() ); } else { - c->SetSpellDelay(spell_type, type_value); + c->SetSpellTypeDelay(spell_type, type_value); c->Message( Chat::Green, fmt::format( "Your [{}] spell delay was set to [{}] seconds.", Bot::GetSpellTypeNameByID(spell_type), - c->GetSpellDelay(spell_type) / 1000.00 + c->GetSpellTypeDelay(spell_type) / 1000.00 ).c_str() ); } diff --git a/zone/bot_commands/spell_holds.cpp b/zone/bot_commands/spell_holds.cpp index d83274d39e..26d42ad823 100644 --- a/zone/bot_commands/spell_holds.cpp +++ b/zone/bot_commands/spell_holds.cpp @@ -157,12 +157,12 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) "{} says, 'My [{}] spell hold is currently [{}].'", my_bot->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - my_bot->GetSpellHold(spell_type) ? "enabled" : "disabled" + my_bot->GetSpellTypeHold(spell_type) ? "enabled" : "disabled" ).c_str() ); } else { - my_bot->SetSpellHold(spell_type, type_value); + my_bot->SetSpellTypeHold(spell_type, type_value); ++success_count; } } @@ -174,7 +174,7 @@ void bot_command_spell_holds(Client* c, const Seperator* sep) "{} says, 'My [{}] spell hold was [{}].'", first_found->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - first_found->GetSpellHold(spell_type) ? "enabled" : "disabled" + first_found->GetSpellTypeHold(spell_type) ? "enabled" : "disabled" ).c_str() ); } diff --git a/zone/bot_commands/spell_max_thresholds.cpp b/zone/bot_commands/spell_max_thresholds.cpp index 4ef30d2ad0..ca7f78aa32 100644 --- a/zone/bot_commands/spell_max_thresholds.cpp +++ b/zone/bot_commands/spell_max_thresholds.cpp @@ -216,12 +216,12 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { "{} says, 'My [{}] maximum threshold is currently [{}%%].'", my_bot->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - my_bot->GetSpellMaxThreshold(spell_type) + my_bot->GetSpellTypeMaxThreshold(spell_type) ).c_str() ); } else { - my_bot->SetSpellMaxThreshold(spell_type, type_value); + my_bot->SetSpellTypeMaxThreshold(spell_type, type_value); ++success_count; } } @@ -234,7 +234,7 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { "{} says, 'My [{}] maximum threshold was set to [{}%%].'", first_found->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - first_found->GetSpellMaxThreshold(spell_type) + first_found->GetSpellTypeMaxThreshold(spell_type) ).c_str() ); } @@ -258,19 +258,19 @@ void bot_command_spell_max_thresholds(Client* c, const Seperator* sep) { fmt::format( "Your [{}] maximum threshold is currently [{}%%].", Bot::GetSpellTypeNameByID(spell_type), - c->GetSpellMaxThreshold(spell_type) + c->GetSpellTypeMaxThreshold(spell_type) ).c_str() ); } else { - c->SetSpellMaxThreshold(spell_type, type_value); + c->SetSpellTypeMaxThreshold(spell_type, type_value); c->Message( Chat::Green, fmt::format( "Your [{}] maximum threshold was set to [{}%%].", Bot::GetSpellTypeNameByID(spell_type), - c->GetSpellMaxThreshold(spell_type) + c->GetSpellTypeMaxThreshold(spell_type) ).c_str() ); } diff --git a/zone/bot_commands/spell_min_thresholds.cpp b/zone/bot_commands/spell_min_thresholds.cpp index 85d72a553e..ba614530c1 100644 --- a/zone/bot_commands/spell_min_thresholds.cpp +++ b/zone/bot_commands/spell_min_thresholds.cpp @@ -218,12 +218,12 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { "{} says, 'My [{}] minimum threshold is currently [{}%%].'", my_bot->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - my_bot->GetSpellMinThreshold(spell_type) + my_bot->GetSpellTypeMinThreshold(spell_type) ).c_str() ); } else { - my_bot->SetSpellMinThreshold(spell_type, type_value); + my_bot->SetSpellTypeMinThreshold(spell_type, type_value); ++success_count; } } @@ -236,7 +236,7 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { "{} says, 'My [{}] minimum threshold was set to [{}%%].'", first_found->GetCleanName(), Bot::GetSpellTypeNameByID(spell_type), - first_found->GetSpellMinThreshold(spell_type) + first_found->GetSpellTypeMinThreshold(spell_type) ).c_str() ); } @@ -260,19 +260,19 @@ void bot_command_spell_min_thresholds(Client* c, const Seperator* sep) { fmt::format( "Your [{}] minimum threshold is currently [{}%%].", Bot::GetSpellTypeNameByID(spell_type), - c->GetSpellMinThreshold(spell_type) + c->GetSpellTypeMinThreshold(spell_type) ).c_str() ); } else { - c->SetSpellMinThreshold(spell_type, type_value); + c->SetSpellTypeMinThreshold(spell_type, type_value); c->Message( Chat::Green, fmt::format( "Your [{}] minimum threshold was set to [{}%%].", Bot::GetSpellTypeNameByID(spell_type), - c->GetSpellMinThreshold(spell_type) + c->GetSpellTypeMinThreshold(spell_type) ).c_str() ); } diff --git a/zone/client.h b/zone/client.h index 61d924f3b7..b3ce3e3a77 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2283,15 +2283,15 @@ class Client : public Mob int GetBotSetting(uint8 setting_type, uint16 bot_setting); void SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 setting_value); - uint16 GetDefaultSpellDelay(uint16 spell_type); - uint8 GetDefaultSpellMinThreshold(uint16 spell_type); - uint8 GetDefaultSpellMaxThreshold(uint16 spell_type); - inline uint16 GetSpellDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } - inline void SetSpellDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } - inline uint8 GetSpellMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } - inline void SetSpellMinThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].min_threshold = threshold_value; } - inline uint8 GetSpellMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } - inline void SetSpellMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } + uint16 GetDefaultSpellTypeDelay(uint16 spell_type); + uint8 GetDefaultSpellTypeMinThreshold(uint16 spell_type); + uint8 GetDefaultSpellTypeMaxThreshold(uint16 spell_type); + inline uint16 GetSpellTypeDelay(uint16 spell_type) const { return m_bot_spell_settings[spell_type].delay; } + inline void SetSpellTypeDelay(uint16 spell_type, uint16 delay_value) { m_bot_spell_settings[spell_type].delay = delay_value; } + inline uint8 GetSpellTypeMinThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].min_threshold; } + inline void SetSpellTypeMinThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].min_threshold = threshold_value; } + inline uint8 GetSpellTypeMaxThreshold(uint16 spell_type) const { return m_bot_spell_settings[spell_type].max_threshold; } + inline void SetSpellTypeMaxThreshold(uint16 spell_type, uint8 threshold_value) { m_bot_spell_settings[spell_type].max_threshold = threshold_value; } inline bool SpellTypeRecastCheck(uint16 spellType) { return !m_bot_spell_settings[spellType].recast_timer.GetRemainingTime(); } void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } diff --git a/zone/client_bot.cpp b/zone/client_bot.cpp index 2219c19dcf..8e1f07c046 100644 --- a/zone/client_bot.cpp +++ b/zone/client_bot.cpp @@ -233,15 +233,17 @@ void Client::LoadDefaultBotSettings() { t.spell_type = i; t.short_name = Bot::GetSpellTypeShortNameByID(i); t.name = Bot::GetSpellTypeNameByID(i); - t.delay = GetDefaultSpellDelay(i); - t.min_threshold = GetDefaultSpellMinThreshold(i); - t.max_threshold = GetDefaultSpellMaxThreshold(i); + t.delay = GetDefaultSpellTypeDelay(i); + t.min_threshold = GetDefaultSpellTypeMinThreshold(i); + t.max_threshold = GetDefaultSpellTypeMaxThreshold(i); t.recast_timer.Start(); m_bot_spell_settings.push_back(t); LogBotSettingsDetail("{} says, 'Setting defaults for {} ({}) [#{}]'", GetCleanName(), t.name, t.short_name, t.spell_type); - LogBotSettingsDetail("{} says, 'Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), GetDefaultSpellDelay(i), GetDefaultSpellMinThreshold(i), GetDefaultSpellMaxThreshold(i)); + LogBotSettingsDetail("{} says, 'Delay = [{}ms] | MinThreshold = [{}\%] | MaxThreshold = [{}\%]'", GetCleanName(), + GetDefaultSpellTypeDelay(i), + GetDefaultSpellTypeMinThreshold(i), GetDefaultSpellTypeMaxThreshold(i)); } } @@ -250,11 +252,11 @@ int Client::GetDefaultBotSettings(uint8 setting_type, uint16 bot_setting) { case BotSettingCategories::BaseSetting: return false; // only setting supported currently is illusion block case BotSettingCategories::SpellDelay: - return GetDefaultSpellDelay(bot_setting); + return GetDefaultSpellTypeDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: - return GetDefaultSpellMinThreshold(bot_setting); + return GetDefaultSpellTypeMinThreshold(bot_setting); case BotSettingCategories::SpellMaxThreshold: - return GetDefaultSpellMaxThreshold(bot_setting); + return GetDefaultSpellTypeMaxThreshold(bot_setting); } } @@ -263,11 +265,11 @@ int Client::GetBotSetting(uint8 setting_type, uint16 bot_setting) { case BotSettingCategories::BaseSetting: return GetIllusionBlock(); // only setting supported currently case BotSettingCategories::SpellDelay: - return GetSpellDelay(bot_setting); + return GetSpellTypeDelay(bot_setting); case BotSettingCategories::SpellMinThreshold: - return GetSpellMinThreshold(bot_setting); + return GetSpellTypeMinThreshold(bot_setting); case BotSettingCategories::SpellMaxThreshold: - return GetSpellMaxThreshold(bot_setting); + return GetSpellTypeMaxThreshold(bot_setting); } } @@ -277,13 +279,13 @@ void Client::SetBotSetting(uint8 setting_type, uint16 bot_setting, uint32 settin SetIllusionBlock(setting_value); // only setting supported currently break; case BotSettingCategories::SpellDelay: - SetSpellDelay(bot_setting, setting_value); + SetSpellTypeDelay(bot_setting, setting_value); break; case BotSettingCategories::SpellMinThreshold: - SetSpellMinThreshold(bot_setting, setting_value); + SetSpellTypeMinThreshold(bot_setting, setting_value); break; case BotSettingCategories::SpellMaxThreshold: - SetSpellMaxThreshold(bot_setting, setting_value); + SetSpellTypeMaxThreshold(bot_setting, setting_value); break; } } @@ -348,7 +350,7 @@ void Client::SendSpellTypePrompts(bool commanded_types, bool client_only_types) return; } -uint16 Client::GetDefaultSpellDelay(uint16 spell_type) { +uint16 Client::GetDefaultSpellTypeDelay(uint16 spell_type) { switch (spell_type) { case BotSpellTypes::VeryFastHeals: case BotSpellTypes::PetVeryFastHeals: @@ -379,14 +381,14 @@ uint16 Client::GetDefaultSpellDelay(uint16 spell_type) { } } -uint8 Client::GetDefaultSpellMinThreshold(uint16 spell_type) { +uint8 Client::GetDefaultSpellTypeMinThreshold(uint16 spell_type) { switch (spell_type) { default: return 0; } } -uint8 Client::GetDefaultSpellMaxThreshold(uint16 spell_type) { +uint8 Client::GetDefaultSpellTypeMaxThreshold(uint16 spell_type) { uint8 client_class = GetClass(); switch (spell_type) { From a008e75a1c2d0e3b11dfb005f574a81b009eee8e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 17:27:13 -0600 Subject: [PATCH 368/394] Use GetTempSpellType() for announce check in RaidGroupSay --- zone/bot.cpp | 8 ++++++++ zone/botspellsai.cpp | 36 ------------------------------------ 2 files changed, 8 insertions(+), 36 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 98b8a242b1..83a6daceab 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7911,6 +7911,14 @@ void Bot::SetDefaultBotStance() { } void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { + if ( + speaker->CastToBot()->GetTempSpellType() && + speaker->CastToBot()->GetTempSpellType() != UINT16_MAX && + !speaker->CastToBot()->GetSpellTypeAnnounceCast(speaker->CastToBot()->GetTempSpellType()) + ) { + return; + } + char buf[1000]; va_list ap; va_start(ap, msg); diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index 8eab457e0f..d9e4863dfd 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -254,10 +254,6 @@ bool Bot::AICastSpell(Mob* tar, uint8 chance, uint16 spell_type, uint16 sub_targ SetCastedSpellType(spell_type); } - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -305,10 +301,6 @@ bool Bot::BotCastMez(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel SetCastedSpellType(spell_type); } - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -352,10 +344,6 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } } - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -369,10 +357,6 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe SetBotSpellRecastTimer(spell_type, tar, true); } - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -427,10 +411,6 @@ bool Bot::BotCastPet(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spel if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { SetCastedSpellType(spell_type); - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -487,10 +467,6 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(s.SpellIndex, tar, s.ManaCost)) { SetCastedSpellType(spell_type); - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -509,10 +485,6 @@ bool Bot::BotCastNuke(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { SetCastedSpellType(spell_type); - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -549,10 +521,6 @@ bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } } - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( @@ -570,10 +538,6 @@ bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe } } - if (!GetSpellTypeAnnounceCast(spell_type)) { - return true; - } - RaidGroupSay( this, fmt::format( From 2fdc4e86676b4ebf940137650ba87ebf25c8f821 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 18:57:04 -0600 Subject: [PATCH 369/394] Make all spell shortnames plural where applicable --- common/spdat.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/common/spdat.h b/common/spdat.h index b60ab51865..437910fa4e 100644 --- a/common/spdat.h +++ b/common/spdat.h @@ -835,8 +835,8 @@ static std::map spellType_shortNames = { { BotSpellTypes::Debuff, "debuffs" }, { BotSpellTypes::Cure, "cures" }, { BotSpellTypes::GroupCures, "groupcures" }, - { BotSpellTypes::PetCures, "petcure" }, - { BotSpellTypes::Resurrect, "resurrect" }, + { BotSpellTypes::PetCures, "petcures" }, + { BotSpellTypes::Resurrect, "resurrects" }, { BotSpellTypes::HateRedux, "hateredux" }, { BotSpellTypes::InCombatBuffSong, "incombatbuffsongs" }, { BotSpellTypes::OutOfCombatBuffSong, "outofcombatbuffsongs" }, @@ -874,8 +874,8 @@ static std::map spellType_shortNames = { { BotSpellTypes::ResistBuffs, "resistbuffs" }, { BotSpellTypes::PetDamageShields, "petdamageshields" }, { BotSpellTypes::PetResistBuffs, "petresistbuffs" }, - { BotSpellTypes::HateLine, "hateline" }, - { BotSpellTypes::AEHateLine, "aehateline" }, + { BotSpellTypes::HateLine, "hatelines" }, + { BotSpellTypes::AEHateLine, "aehatelines" }, { BotSpellTypes::Lull, "lull" }, { BotSpellTypes::Teleport, "teleport" }, { BotSpellTypes::Succor, "succor" }, From c3cbc4bcd4ba6b7073238ab962f514016ed41b3a Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 20:29:21 -0600 Subject: [PATCH 370/394] Update bot.cpp --- zone/bot.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 83a6daceab..f5726965d0 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7912,7 +7912,6 @@ void Bot::SetDefaultBotStance() { void Bot::RaidGroupSay(Mob* speaker, const char* msg, ...) { if ( - speaker->CastToBot()->GetTempSpellType() && speaker->CastToBot()->GetTempSpellType() != UINT16_MAX && !speaker->CastToBot()->GetSpellTypeAnnounceCast(speaker->CastToBot()->GetTempSpellType()) ) { From 498b64fea2d2808bf3e8c484d1b950d96de0a1f4 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Fri, 31 Jan 2025 22:51:11 -0600 Subject: [PATCH 371/394] Bots:BotsUseLiveBlockedMessage filter to spell failure --- zone/spells.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index f845f6f04d..b2f9e1e2a7 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -3638,7 +3638,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid if (caster->IsBot() && RuleB(Bots, BotsUseLiveBlockedMessage) && caster->GetClass() != Class::Bard) { caster->GetOwner()->Message( - Chat::Red, + Chat::SpellFailure, fmt::format( "{}'s {} did not take hold on {}. (Blocked by {}.)", caster->GetCleanName(), From 46a1b26529377e9117b9096eb01a2d5f7e76c8ea Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 1 Feb 2025 09:11:50 -0600 Subject: [PATCH 372/394] Move GetSpellTargetList to only get called when necessary to reduce overhead --- zone/bot.cpp | 25 +++++++++++++++---------- zone/bot.h | 12 ++++++------ 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index f5726965d0..0996ee2476 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -114,8 +114,8 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm GenerateBaseStats(); bot_timers.clear(); bot_blocked_buffs.clear(); - _spellTargetList.clear(); - _groupSpellTargetList.clear(); + _spell_target_list.clear(); + _group_spell_target_list.clear(); SetStoredRaid(nullptr); SetVerifiedRaid(false); p_raid_instance = nullptr; @@ -269,8 +269,8 @@ Bot::Bot( database.botdb.LoadBotBlockedBuffs(this); } - _spellTargetList.clear(); - _groupSpellTargetList.clear(); + _spell_target_list.clear(); + _group_spell_target_list.clear(); SetStoredRaid(nullptr); SetVerifiedRaid(false); p_raid_instance = nullptr; @@ -2197,10 +2197,8 @@ void Bot::AI_Process() return; } - std::vector spell_target_list = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); - SetSpellTargetList(spell_target_list); - std::vector group_spell_target_list = GatherSpellTargets(); - SetGroupSpellTargetList(group_spell_target_list); + _spell_target_list.clear(); + _group_spell_target_list.clear(); SetTempSpellType(UINT16_MAX); // HEAL ROTATION CASTING CHECKS @@ -2216,8 +2214,6 @@ void Bot::AI_Process() //ALT COMBAT (ACQUIRE HATE) glm::vec3 Goal(0, 0, 0); - - // We have aggro to choose from if (IsEngaged()) { if (rest_timer.Enabled()) { @@ -13312,3 +13308,12 @@ bool Bot::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) { return false; } + +std::vector Bot::GetSpellTargetList() { + if (_spell_target_list.empty()) { + std::vector spell_target_list = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); + SetSpellTargetList(spell_target_list); + } + + return _spell_target_list; +} diff --git a/zone/bot.h b/zone/bot.h index dd84509c89..757818bded 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -560,10 +560,10 @@ class Bot : public NPC { // Movement checks bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); - std::vector GetSpellTargetList() { return _spellTargetList; } - void SetSpellTargetList(std::vector spell_target_list) { _spellTargetList = spell_target_list; } - std::vector GetGroupSpellTargetList() { return _groupSpellTargetList; } - void SetGroupSpellTargetList(std::vector spell_target_list) { _groupSpellTargetList = spell_target_list; } + std::vector GetSpellTargetList(); + void SetSpellTargetList(std::vector spell_target_list) { _spell_target_list = spell_target_list; } + std::vector GetGroupSpellTargetList() { return _group_spell_target_list; } + void SetGroupSpellTargetList(std::vector spell_target_list) { _group_spell_target_list = spell_target_list; } std::vector GetBuffTargets(Mob* spellTarget); // Bot settings @@ -1215,8 +1215,8 @@ class Bot : public NPC { bool _illusionBlock; std::vector m_bot_spell_settings; - std::vector _spellTargetList; - std::vector _groupSpellTargetList; + std::vector _spell_target_list; + std::vector _group_spell_target_list; Raid* _storedRaid; bool _verifiedRaid; uint16 _tempSpellType; From 3a0ea236d1965b7db50ed3276100ba7b47685929 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sat, 1 Feb 2025 18:13:47 -0600 Subject: [PATCH 373/394] formatting --- zone/bot.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index 0996ee2476..c5f0278eeb 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -9753,7 +9753,8 @@ bool Bot::CastChecks(uint16 spell_id, Mob* tar, uint16 spell_type, bool precheck RequiresStackCheck(spell_type) || ( !RequiresStackCheck(spell_type) && - CalcBuffDuration(this, tar, spell_id) != 0) + CalcBuffDuration(this, tar, spell_id) != 0 + ) ) && tar->CanBuffStack(spell_id, GetLevel(), true) < 0 From 848cb949961474f0f80ac601919bbcdbc2711fd3 Mon Sep 17 00:00:00 2001 From: Akkadius Date: Sun, 2 Feb 2025 01:11:19 -0600 Subject: [PATCH 374/394] Formatting --- zone/mob.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/zone/mob.h b/zone/mob.h index 961a0abfda..1cf5844de8 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -862,7 +862,7 @@ class Mob : public Entity { void ShowStats(Client* client); void ShowBuffs(Client* c); bool PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, float &z_dest, bool lookForAftArc = true); - + virtual int GetKillExpMod() const { return 100; } // aura functions @@ -1260,20 +1260,20 @@ class Mob : public Entity { float GetFixedZ(const glm::vec3 &destination, int32 z_find_offset = 5); virtual int GetStuckBehavior() const { return 0; } - void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false); - inline uint32 DontHealMeBefore() const { return m_dont_heal_me_before; } - inline uint32 DontBuffMeBefore() const { return m_dont_buff_me_before; } - inline uint32 DontDotMeBefore() const { return m_dont_dot_me_before; } - inline uint32 DontRootMeBefore() const { return m_dont_root_me_before; } - inline uint32 DontSnareMeBefore() const { return m_dont_snare_me_before; } - inline uint32 DontCureMeBefore() const { return m_dont_cure_me_before; } - - void SetDontRootMeBefore(uint32 time) { m_dont_root_me_before = time; } - void SetDontHealMeBefore(uint32 time) { m_dont_heal_me_before = time; } - void SetDontBuffMeBefore(uint32 time) { m_dont_buff_me_before = time; } - void SetDontDotMeBefore(uint32 time) { m_dont_dot_me_before = time; } - void SetDontSnareMeBefore(uint32 time) { m_dont_snare_me_before = time; } - void SetDontCureMeBefore(uint32 time) { m_dont_cure_me_before = time; } + void NPCSpecialAttacks(const char *parse, int permtag, bool reset = true, bool remove = false); + inline uint32 DontHealMeBefore() const { return m_dont_heal_me_before; } + inline uint32 DontBuffMeBefore() const { return m_dont_buff_me_before; } + inline uint32 DontDotMeBefore() const { return m_dont_dot_me_before; } + inline uint32 DontRootMeBefore() const { return m_dont_root_me_before; } + inline uint32 DontSnareMeBefore() const { return m_dont_snare_me_before; } + inline uint32 DontCureMeBefore() const { return m_dont_cure_me_before; } + + void SetDontRootMeBefore(uint32 time) { m_dont_root_me_before = time; } + void SetDontHealMeBefore(uint32 time) { m_dont_heal_me_before = time; } + void SetDontBuffMeBefore(uint32 time) { m_dont_buff_me_before = time; } + void SetDontDotMeBefore(uint32 time) { m_dont_dot_me_before = time; } + void SetDontSnareMeBefore(uint32 time) { m_dont_snare_me_before = time; } + void SetDontCureMeBefore(uint32 time) { m_dont_cure_me_before = time; } // calculate interruption of spell via movement of mob void SaveSpellLoc() { m_SpellLocation = glm::vec3(m_Position); } From db9fe0f284ce181785df69880ef614287188d8fc Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:21:07 -0600 Subject: [PATCH 375/394] Simplify case SE_Illusion and SE_IllusionCopy for GetIllusionBlock --- zone/spell_effects.cpp | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index 910298ce18..b148d6cff5 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1481,15 +1481,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); #endif if (caster && caster->IsOfClientBot()) { - if (IsClient()) { - if (CastToClient()->GetIllusionBlock()) { - break; - } - } - else { - if (CastToBot()->GetIllusionBlock()) { - break; - } + auto target = IsClient() ? CastToClient() : CastToBot(); + + if (target && target->GetIllusionBlock()) { + break; } } @@ -1503,15 +1498,10 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Illusion Copy"); #endif if (caster && caster->IsOfClientBot()) { - if (IsClient()) { - if (CastToClient()->GetIllusionBlock()) { - break; - } - } - else { - if (CastToBot()->GetIllusionBlock()) { - break; - } + auto target = IsClient() ? CastToClient() : CastToBot(); + + if (target && target->GetIllusionBlock()) { + break; } } From cbb619c2d91478d8988f5f0a247618bb0a094c4b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:22:24 -0600 Subject: [PATCH 376/394] Clean up InterruptSpell --- zone/spells.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index b2f9e1e2a7..8b624fb769 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1257,12 +1257,11 @@ void Mob::InterruptSpell(uint16 message, uint16 color, uint16 spellid) bool bard_song_mode = false; //has the bard song gone to auto repeat mode if (IsBot()) { - CastToBot()->SetCastedSpellType(UINT16_MAX); - } + auto bot = CastToBot(); + bot->SetCastedSpellType(UINT16_MAX); - if (IsBot() && IsValidSpell(spellid)) { - if (CastToBot()->CheckSpellRecastTimer(spellid)) { - CastToBot()->ClearSpellRecastTimer(spellid); + if (IsValidSpell(spellid) && bot->CheckSpellRecastTimer(spellid)) { + bot->ClearSpellRecastTimer(spellid); } } From b90f11f635be6e8f30d9021ffc4121046203659b Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:28:06 -0600 Subject: [PATCH 377/394] Cleanup IsBot() checks for DetermineSpellTargets->ST_GroupClientAndPet --- zone/spells.cpp | 20 ++++---------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 8b624fb769..2fe492f4a5 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2278,24 +2278,12 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce } else if(IsBot()) { - if(IsGrouped()) - { - group_id_caster = GetGroup()->GetID(); - } - else if(IsRaidGrouped()) - { - if(GetOwner()) - group_id_caster = (GetRaid()->GetGroup(GetOwner()->CastToClient()) == 0xFFFF) ? 0 : (GetRaid()->GetGroup(GetOwner()->CastToClient()) + 1); - } - if (IsGrouped()) - { - if (Group* group = GetGroup()) { + if (IsGrouped()) { + if (auto group = GetGroup()) { group_id_caster = group->GetID(); } - } - else if (IsRaidGrouped()) - { - if (Raid* raid = GetRaid()) { + } else if (IsRaidGrouped()) { + if (auto raid = GetRaid()) { uint32 group_id = raid->GetGroup(GetName()); group_id_caster = (group_id == 0xFFFFFFFF) ? 0 : (group_id + 1); } From 8a31caab6bc2dcb9588780a27ba7574f14134e19 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:29:41 -0600 Subject: [PATCH 378/394] Cleanup range/aoe_range check in SpellFinished --- zone/spells.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 2fe492f4a5..8ef2467398 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2537,12 +2537,11 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, in //range check our target, if we have one and it is not us float range = spells[spell_id].range + GetRangeDistTargetSizeMod(spell_target); - if ( - ( - (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB)) - ) && - IsTGBCompatibleSpell(spell_id) && IsGroupSpell(spell_id) - ) { + bool can_use_tgb = (IsClient() && CastToClient()->TGB()) || (IsBot() && RuleB(Bots, EnableBotTGB)); + bool is_tgb_compatible = IsTGBCompatibleSpell(spell_id); + bool is_group_spell = IsGroupSpell(spell_id); + + if (can_use_tgb && is_tgb_compatible && is_group_spell) { range = spells[spell_id].aoe_range; } From 32a7c01f333e797d234a11edec0570558df307e2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:37:50 -0600 Subject: [PATCH 379/394] Cleanup DetermineSpellTargets->ST_GroupNoPets --- zone/spells.cpp | 52 ++++++++++++++----------------------------------- 1 file changed, 15 insertions(+), 37 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 8ef2467398..65c73ed2f3 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2173,47 +2173,25 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_Group: case ST_GroupNoPets: { - if ( - IsClient() && CastToClient()->TGB() && - IsTGBCompatibleSpell(spell_id) && - (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)) - ) { - if ( - !target || - target->IsCorpse() || - ( - target->IsNPC() && - !(target->GetOwner() && target->GetOwner()->IsClient()) - ) - ) { - spell_target = this; - } - else { + spell_target = this; // Default to self + + bool tgb_enabled = (IsClient() && CastToClient()->TGB()) || + (IsBot() && RuleB(Bots, EnableBotTGB)); + bool tgb_compatible = IsTGBCompatibleSpell(spell_id); + bool item_tgb_allowed = (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)); + + if (tgb_enabled && tgb_compatible && item_tgb_allowed) { + bool valid_target = target && !target->IsCorpse() && + (!target->IsNPC() || + (target->GetOwner() && target->GetOwner()->IsOfClientBot())); + + if (valid_target) { spell_target = target; } } - else if ( - IsBot() && RuleB(Bots, EnableBotTGB) && - IsTGBCompatibleSpell(spell_id) && - (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)) - ) { - if ( - !spell_target || - spell_target->IsCorpse() || - ( - spell_target->IsNPC() && - !(spell_target->GetOwner() && spell_target->GetOwner()->IsOfClientBot()) - ) - ) { - spell_target = this; - } - } - else { - spell_target = this; - } - if (spell_target && spell_target->IsPet() && spells[spell_id].target_type == ST_GroupNoPets){ - MessageString(Chat::Red,NO_CAST_ON_PET); + if (spell_target && spell_target->IsPet() && spells[spell_id].target_type == ST_GroupNoPets) { + MessageString(Chat::Red, NO_CAST_ON_PET); return false; } From 53876e20720b142bc6d3eb6ee80f1670ebcd8026 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:39:23 -0600 Subject: [PATCH 380/394] Cleanup DetermineSpellTargets->ST_Self for bot summon corpse --- zone/spells.cpp | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 65c73ed2f3..546fb32acb 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -1919,14 +1919,12 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce // single target spells case ST_Self: { - if ( - !( - IsBot() && - IsEffectInSpell(spell_id, SE_SummonCorpse) && - RuleB(Bots, AllowCommandedSummonCorpse) - ) - ) { // summon corpse spells are self only, bots need a fallthrough to summon corpses - spell_target = this; + bool bot_can_summon_corpse = IsBot() && + IsEffectInSpell(spell_id, SE_SummonCorpse) && + RuleB(Bots, AllowCommandedSummonCorpse); + + if (!bot_can_summon_corpse) { + spell_target = this; // Summon corpse spells are self-only; bots need a fallthrough } CastAction = SingleTarget; From a3085be4c3b2abe76b47c64aca7f4b17ff3640e1 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:40:27 -0600 Subject: [PATCH 381/394] Cleanup DetermineSpellTargets->ST_Pet --- zone/spells.cpp | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 546fb32acb..2f5ccbe3a6 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2073,23 +2073,21 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce } case ST_Pet: { - if ( - !( - IsBot() && - spell_target && - spell_target->GetOwner() != this && - RuleB(Bots, CanCastPetOnlyOnOthersPets) - ) - ) { + bool bot_casting_on_other_pet = IsBot() && + spell_target && + spell_target->GetOwner() != this && + RuleB(Bots, CanCastPetOnlyOnOthersPets); + if (!bot_casting_on_other_pet) { spell_target = GetPet(); } - if(!spell_target) - { + + if (!spell_target) { LogSpells("Spell [{}] canceled: invalid target (no pet)", spell_id); - MessageString(Chat::Red,NO_PET); - return false; // can't cast these unless we have a target + MessageString(Chat::Red, NO_PET); + return false; // Can't cast these unless we have a target } + CastAction = SingleTarget; break; } From b57be45b515fdff2374b9b948deecbaaf4d854c2 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:42:02 -0600 Subject: [PATCH 382/394] Cleanup bot logic in TryBackstab --- zone/special_attacks.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 26c5bd87e3..71eec72558 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -727,19 +727,15 @@ void Mob::TryBackstab(Mob *other, int ReuseTime) { } } else if (IsBot()) { - const EQ::ItemInstance* inst = CastToBot()->GetBotItem(EQ::invslot::slotPrimary); - const EQ::ItemData* botpiercer = nullptr; + auto bot = CastToBot(); + auto inst = bot->GetBotItem(EQ::invslot::slotPrimary); + auto bot_piercer = inst ? inst->GetItem() : nullptr; - if (inst) { - botpiercer = inst->GetItem(); - } - - if (!botpiercer || (botpiercer->ItemType != EQ::item::ItemType1HPiercing)) { - if (!CastToBot()->GetCombatRoundForAlerts()) { - CastToBot()->SetCombatRoundForAlerts(); - CastToBot()->RaidGroupSay(this, "I can't backstab with this weapon!"); + if (!bot_piercer || bot_piercer->ItemType != EQ::item::ItemType1HPiercing) { + if (!bot->GetCombatRoundForAlerts()) { + bot->SetCombatRoundForAlerts(); + bot->RaidGroupSay(this, "I can't backstab with this weapon!"); } - return; } } From 91996f15f082d01126d85782b2d8ffcc9ce00ab8 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:45:06 -0600 Subject: [PATCH 383/394] Cleanup IsAttackAllowed checks for bots and their pets --- zone/aggro.cpp | 39 ++++++++++++++++++++------------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/zone/aggro.cpp b/zone/aggro.cpp index 3c4bfd0e68..4990f88bb9 100644 --- a/zone/aggro.cpp +++ b/zone/aggro.cpp @@ -743,28 +743,29 @@ bool Mob::IsAttackAllowed(Mob *target, bool isSpellAttack) } // can't damage own pet (applies to everthing) - Mob *target_owner = target->GetOwner(); - Mob *our_owner = GetOwner(); - Mob* target_ultimate_owner = (target->IsBot() ? target->CastToBot()->GetBotOwner() : target->GetUltimateOwner()); - Mob* our_ultimate_owner = (IsBot() ? CastToBot()->GetBotOwner() : GetUltimateOwner()); + Mob* target_owner = target->GetOwner(); + Mob* our_owner = GetOwner(); - if (target_owner && target_owner == this) { + // Self-owner check + if (target_owner == this || our_owner == target) { return false; } - else if ( - IsBot() && target_ultimate_owner && - ( - (target_ultimate_owner == our_ultimate_owner) || - (target_ultimate_owner->IsOfClientBot()) - ) - ) { - return false; - } - else if (our_owner && our_owner == target) { - return false; - } - else if (IsBot() && our_ultimate_owner && our_ultimate_owner == target) { - return false; + + // Bot-specific logic + if (IsBot()) { + Mob* target_ultimate_owner = target->IsBot() ? target->CastToBot()->GetBotOwner() : target->GetUltimateOwner(); + Mob* our_ultimate_owner = CastToBot()->GetBotOwner(); + + if (target_ultimate_owner) { + if (target_ultimate_owner == our_ultimate_owner || target_ultimate_owner->IsOfClientBot()) { + return false; + } + } + + // Bots should not attack their ultimate owner + if (our_ultimate_owner == target) { + return false; + } } // invalidate for swarm pets for later on if their owner is a corpse From f2df8ababd8d476c6694d0259674be38dc2480d4 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:45:51 -0600 Subject: [PATCH 384/394] Cleanup StopMoving for bots --- zone/mob.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index e5b82af12f..6531ce4783 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -1690,8 +1690,10 @@ void Mob::StopMoving() void Mob::StopMoving(float new_heading) { if (IsBot()) { - CastToBot()->SetCombatJitterFlag(false); - CastToBot()->SetCombatOutOfRangeJitterFlag(false); + auto bot = CastToBot(); + + bot->SetCombatJitterFlag(false); + bot->SetCombatOutOfRangeJitterFlag(false); } StopNavigation(); From 23960b9c776b977516e2b857e91287852be06210 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 08:47:04 -0600 Subject: [PATCH 385/394] Cleanup CanThisClassTripleAttack --- zone/mob.cpp | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/zone/mob.cpp b/zone/mob.cpp index 6531ce4783..ed2276cf73 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -4663,30 +4663,22 @@ bool Mob::CanThisClassDoubleAttack(void) const } } -bool Mob::CanThisClassTripleAttack() const -{ +bool Mob::CanThisClassTripleAttack() const { if (!IsOfClientBot()) { - return false; // When they added the real triple attack skill, mobs lost the ability to triple - } else { - if (RuleB(Combat, ClassicTripleAttack)) { - return ( - GetLevel() >= 60 && - ( - GetClass() == Class::Warrior || - GetClass() == Class::Ranger || - GetClass() == Class::Monk || - GetClass() == Class::Berserker - ) - ); - } else { - if (IsClient()) { - return CastToClient()->HasSkill(EQ::skills::SkillTripleAttack); - } - else { - return GetSkill(EQ::skills::SkillTripleAttack) > 0; - } - } + return false; // Mobs lost the ability to triple attack when the real skill was added } + + if (RuleB(Combat, ClassicTripleAttack)) { + return GetLevel() >= 60 && ( + GetClass() == Class::Warrior || + GetClass() == Class::Ranger || + GetClass() == Class::Monk || + GetClass() == Class::Berserker + ); + } + + return IsClient() ? CastToClient()->HasSkill(EQ::skills::SkillTripleAttack) + : GetSkill(EQ::skills::SkillTripleAttack) > 0; } bool Mob::CanThisClassParry(void) const From d71e121c89a211d18aa5ee9c6b8cb3e69d28753d Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 09:08:42 -0600 Subject: [PATCH 386/394] Fix casting for GetIllusionBlock checks --- zone/bot.h | 2 +- zone/client.h | 2 +- zone/mob.h | 1 + zone/spell_effects.cpp | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/zone/bot.h b/zone/bot.h index 757818bded..ec7a441ffd 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -647,7 +647,7 @@ class Bot : public NPC { uint8 GetUltimateSpellTypeMinThreshold(uint16 spell_type, Mob* tar); uint8 GetUltimateSpellTypeMaxThreshold(uint16 spell_type, Mob* tar); void SetIllusionBlock(bool value) { _illusionBlock = value; } - bool GetIllusionBlock() const { return _illusionBlock; } + bool GetIllusionBlock() const override { return _illusionBlock; } bool GetShowHelm() const { return _showHelm; } void SetShowHelm(bool show_helm) { _showHelm = show_helm; } bool GetBehindMob() const { return _behindMobStatus; } diff --git a/zone/client.h b/zone/client.h index ce4e5aa61c..cc1378319c 100644 --- a/zone/client.h +++ b/zone/client.h @@ -2299,7 +2299,7 @@ class Client : public Mob void SetSpellTypeRecastTimer(uint16 spell_type, uint32 recast_time) { m_bot_spell_settings[spell_type].recast_timer.Start(recast_time); } void SetIllusionBlock(bool value) { _illusionBlock = value; } - bool GetIllusionBlock() const { return _illusionBlock; } + bool GetIllusionBlock() const override { return _illusionBlock; } private: bool bot_owner_options[_booCount]; diff --git a/zone/mob.h b/zone/mob.h index 961a0abfda..0b72354a36 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -514,6 +514,7 @@ class Mob : public Entity { void ApplySpellBuff(int spell_id, int duration = 0, int level_override = -1); int GetBuffStatValueBySpell(int32 spell_id, const char* stat_identifier); int GetBuffStatValueBySlot(uint8 slot, const char* stat_identifier); + virtual bool GetIllusionBlock() const { return false; } //Basic Stats/Inventory virtual void SetLevel(uint8 in_level, bool command = false) { level = in_level; } diff --git a/zone/spell_effects.cpp b/zone/spell_effects.cpp index b148d6cff5..2365fa2fc9 100644 --- a/zone/spell_effects.cpp +++ b/zone/spell_effects.cpp @@ -1481,7 +1481,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Illusion: race %d", effect_value); #endif if (caster && caster->IsOfClientBot()) { - auto target = IsClient() ? CastToClient() : CastToBot(); + Mob* target = IsClient() ? static_cast(CastToClient()) : static_cast(CastToBot()); if (target && target->GetIllusionBlock()) { break; @@ -1498,7 +1498,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove snprintf(effect_desc, _EDLEN, "Illusion Copy"); #endif if (caster && caster->IsOfClientBot()) { - auto target = IsClient() ? CastToClient() : CastToBot(); + Mob* target = IsClient() ? static_cast(CastToClient()) : static_cast(CastToBot()); if (target && target->GetIllusionBlock()) { break; From 6338bd8acdc097fcb91b760f2019e82349420aaf Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 09:14:39 -0600 Subject: [PATCH 387/394] Formatting --- zone/spells.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index 2f5ccbe3a6..ebe0b82bac 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2172,14 +2172,14 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce spell_target = this; // Default to self bool tgb_enabled = (IsClient() && CastToClient()->TGB()) || - (IsBot() && RuleB(Bots, EnableBotTGB)); + (IsBot() && RuleB(Bots, EnableBotTGB)); bool tgb_compatible = IsTGBCompatibleSpell(spell_id); bool item_tgb_allowed = (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)); if (tgb_enabled && tgb_compatible && item_tgb_allowed) { bool valid_target = target && !target->IsCorpse() && - (!target->IsNPC() || - (target->GetOwner() && target->GetOwner()->IsOfClientBot())); + (!target->IsNPC() || + (target->GetOwner() && target->GetOwner()->IsOfClientBot())); if (valid_target) { spell_target = target; From 3e8ad56b07ea78ffc45c7e3783c9a01559b88369 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 12:02:19 -0600 Subject: [PATCH 388/394] Fix DetermineSpellTargets for group spells (this also wasn't properly checking the rule Character:EnableTGB in master) --- zone/spells.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/zone/spells.cpp b/zone/spells.cpp index ebe0b82bac..feaf38bcd0 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -2169,21 +2169,22 @@ bool Mob::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce case ST_Group: case ST_GroupNoPets: { - spell_target = this; // Default to self - - bool tgb_enabled = (IsClient() && CastToClient()->TGB()) || - (IsBot() && RuleB(Bots, EnableBotTGB)); + bool tgb_enabled = (IsClient() && RuleB(Character, EnableTGB) && CastToClient()->TGB()) || + (IsBot() && RuleB(Bots, EnableBotTGB)); bool tgb_compatible = IsTGBCompatibleSpell(spell_id); bool item_tgb_allowed = (slot != CastingSlot::Item || RuleB(Spells, AllowItemTGB)); if (tgb_enabled && tgb_compatible && item_tgb_allowed) { - bool valid_target = target && !target->IsCorpse() && - (!target->IsNPC() || - (target->GetOwner() && target->GetOwner()->IsOfClientBot())); + bool valid_target = spell_target && + !spell_target->IsCorpse() && + (!spell_target->IsNPC() || + (spell_target->GetOwner() && spell_target->IsPetOwnerOfClientBot())); - if (valid_target) { - spell_target = target; + if (!valid_target) { + spell_target = this; } + } else { + spell_target = this; } if (spell_target && spell_target->IsPet() && spells[spell_id].target_type == ST_GroupNoPets) { From 4e6e6f06d673fc2439e7c6c99e985501502c202e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 15:27:53 -0600 Subject: [PATCH 389/394] Cleanup spelltarget grabbing logic, consolidate group heals in to GetNumberNeedingHealedInGroup --- zone/bot.cpp | 102 +++++++++++++++++++++---------------------- zone/bot.h | 5 +-- zone/bot_raid.cpp | 20 --------- zone/botspellsai.cpp | 52 +++++++--------------- 4 files changed, 66 insertions(+), 113 deletions(-) diff --git a/zone/bot.cpp b/zone/bot.cpp index c5f0278eeb..7d1a8277d6 100644 --- a/zone/bot.cpp +++ b/zone/bot.cpp @@ -7402,16 +7402,19 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spell_type) { bool result = false; Mob* tar = nullptr; - for (Mob* m : GetSpellTargetList()) { + for (Mob* m : GetSpellTargetList(RuleB(Bots, CrossRaidBuffingAndHealing))) { tar = m; if (!tar) { continue; } - if (IsGroupTargetOnlyBotSpellType(spell_type)) { + if (RuleB(Bots, CrossRaidBuffingAndHealing) && IsGroupTargetOnlyBotSpellType(spell_type)) { Raid* raid = GetStoredRaid(); - if (raid && (raid->GetGroup(GetName()) == raid->GetGroup(tar->GetName()))) { + + if (raid && + (raid->GetGroup(GetName()) == raid->GetGroup(tar->GetName())) + ) { continue; } } @@ -7419,24 +7422,26 @@ bool Bot::AttemptCloseBeneficialSpells(uint16 spell_type) { result = AttemptAICastSpell(spell_type, tar); if (!result) { - if ( - tar->HasPet() && - ( - !m->GetPet()->IsFamiliar() || - RuleB(Bots, AllowBuffingHealingFamiliars) - ) - ) { - tar = m->GetPet(); + if (tar->HasPet()) { + Mob* pet = m->GetPet(); - if (!tar) { - continue; - } + if (!pet->IsFamiliar() || RuleB(Bots, AllowBuffingHealingFamiliars)) { + tar = pet; - if (!tar->IsOfClientBot() && !(tar->IsPet() && tar->GetOwner() && tar->GetOwner()->IsOfClientBot())) { - continue; - } + if (!tar) { + continue; + } - result = AttemptAICastSpell(spell_type, tar); + if (tar->IsOfClientBot() || + ( + tar->IsPet() && + tar->GetOwner() && + tar->GetOwner()->IsOfClientBot() + ) + ) { + result = AttemptAICastSpell(spell_type, tar); + } + } } } @@ -7679,29 +7684,21 @@ void EntityList::ShowSpawnWindow(Client* client, int Distance, bool NamedOnly) { return; } -uint8 Bot::GetNumberNeedingHealedInGroup(uint8 hpr, bool include_pets, Raid* raid) { - - uint8 need_healed = 0; - if (HasGroup()) { - - auto group_members = GetGroup(); - if (group_members) { - - for (auto member : group_members->members) { - if (member && !member->qglobal) { +uint8 Bot::GetNumberNeedingHealedInGroup(Mob* tar, uint16 spell_type, uint16 spell_id, float range) { + if (!tar->IsBot()) { + return 0; + } - if (member->GetHPRatio() <= hpr) { - need_healed++; - } + uint8 count = 0; + auto target_list = tar->IsClient() ? tar->CastToBot()->GatherSpellTargets() : tar->CastToBot()->GetSpellTargetList(); - if (include_pets && member->GetPet() && !member->GetPet()->IsFamiliar() && member->GetPet()->GetHPRatio() <= hpr) { - need_healed++; - } - } - } + for (Mob* m : target_list) { + if (tar->CalculateDistance(m) < range && CastChecks(spell_id, m, spell_type, true, IsGroupBotSpellType(spell_type))) { + ++count; } } - return GetNumberNeedingHealedInRaidGroup(need_healed, hpr, include_pets, raid); + + return count; } int Bot::GetRawACNoShield(int &shield_ac) { @@ -10049,27 +10046,24 @@ bool Bot::IsTargetAlreadyReceivingSpell(Mob* tar, uint16 spell_id) { uint16 target_id = tar->GetID(); - for (Mob* m : GetSpellTargetList()) { + for (Mob* m : GetSpellTargetList(RuleB(Bots, CrossRaidBuffingAndHealing))) { if ( m->IsBot() && m->IsCasting() && m->CastToBot()->casting_spell_targetid && m->CastingSpellID() == spell_id ) { - if (RuleB(Bots, CrossRaidBuffingAndHealing) && IsGroupSpell(spell_id)) { - std::vector t = GatherSpellTargets(false, tar); + if (m->CastToBot()->casting_spell_targetid == target_id) { + return true; + } - for (Mob* x : t) { + if (!RuleB(Bots, CrossRaidBuffingAndHealing) && IsGroupSpell(spell_id)) { + for (Mob* x : GatherSpellTargets(false, tar)) { if (x->GetID() == m->CastToBot()->casting_spell_targetid) { return true; } } } - else { - if (m->CastToBot()->casting_spell_targetid == target_id) { - return true; - } - } } } @@ -10203,7 +10197,7 @@ bool Bot::IsMobEngagedByAnyone(Mob* tar) { return false; } - for (Mob* m : GetSpellTargetList()) { + for (Mob* m : GetSpellTargetList(true)) { if (m->GetTarget() != tar) { continue; } @@ -12726,7 +12720,7 @@ std::vector Bot::GatherSpellTargets(bool entire_raid, Mob* target, bool no std::vector Bot::GetBuffTargets(Mob* spellTarget) { if (RuleB(Bots, RaidBuffing)) { - return GetSpellTargetList(); + return GetSpellTargetList(true); } return GatherSpellTargets(false, spellTarget); @@ -13310,11 +13304,13 @@ bool Bot::IsImmuneToBotSpell(uint16 spell_id, Mob* caster) { return false; } -std::vector Bot::GetSpellTargetList() { - if (_spell_target_list.empty()) { - std::vector spell_target_list = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); - SetSpellTargetList(spell_target_list); +std::vector Bot::GetSpellTargetList(bool entire_raid) { + if (entire_raid && _spell_target_list.empty()) { + _spell_target_list = GatherSpellTargets(RuleB(Bots, CrossRaidBuffingAndHealing)); + } + else if (!entire_raid && _group_spell_target_list.empty()) { + _group_spell_target_list = GatherSpellTargets(); } - return _spell_target_list; + return entire_raid ? _spell_target_list : _group_spell_target_list; } diff --git a/zone/bot.h b/zone/bot.h index ec7a441ffd..d9efbefb90 100644 --- a/zone/bot.h +++ b/zone/bot.h @@ -364,8 +364,7 @@ class Bot : public NPC { bool GetIsUsingItemClick() { return is_using_item_click; } void SetIsUsingItemClick(bool flag = true) { is_using_item_click = flag; } bool UseDiscipline(uint32 spell_id, uint32 target); - uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool include_pets, Raid* raid); - uint8 GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool include_pets, Raid* raid); + uint8 GetNumberNeedingHealedInGroup(Mob* tar, uint16 spell_type, uint16 spell_id, float range); bool GetNeedsCured(Mob *tar); bool GetNeedsHateRedux(Mob *tar); bool HasOrMayGetAggro(bool SitAggro, uint32 spell_id = 0); @@ -560,7 +559,7 @@ class Bot : public NPC { // Movement checks bool PlotBotPositionAroundTarget(Mob* target, float& x_dest, float& y_dest, float& z_dest, float min_distance, float max_distance, bool behind_only = false, bool front_only = false, bool bypass_los = false); - std::vector GetSpellTargetList(); + std::vector GetSpellTargetList(bool entire_raid = false); void SetSpellTargetList(std::vector spell_target_list) { _spell_target_list = spell_target_list; } std::vector GetGroupSpellTargetList() { return _group_spell_target_list; } void SetGroupSpellTargetList(std::vector spell_target_list) { _group_spell_target_list = spell_target_list; } diff --git a/zone/bot_raid.cpp b/zone/bot_raid.cpp index aba5981a7c..e1eb607484 100644 --- a/zone/bot_raid.cpp +++ b/zone/bot_raid.cpp @@ -133,26 +133,6 @@ void Raid::HandleOfflineBots(uint32 owner) { } } -uint8 Bot::GetNumberNeedingHealedInRaidGroup(uint8& need_healed, uint8 hpr, bool include_pets, Raid* raid) { - - if (raid) { - uint32 r_group = raid->GetGroup(GetName()); - - for (auto& m: raid->GetRaidGroupMembers(r_group)) { - if (m.member && !m.member->qglobal) { - if (m.member->GetHPRatio() <= hpr) { - need_healed++; - } - - if (include_pets && m.member->GetPet() && m.member->GetPet()->GetHPRatio() <= hpr) { - need_healed++; - } - } - } - } - return need_healed; -} - void Bot::ProcessRaidInvite(Mob* invitee, Client* invitor, bool group_invite) { if (!invitee || !invitor) { diff --git a/zone/botspellsai.cpp b/zone/botspellsai.cpp index d9e4863dfd..35ca8dedc1 100644 --- a/zone/botspellsai.cpp +++ b/zone/botspellsai.cpp @@ -337,9 +337,7 @@ bool Bot::BotCastCure(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (AIDoSpellCast(bot_spell.SpellIndex, tar, bot_spell.ManaCost)) { if (IsGroupSpell(bot_spell.SpellId)) { if (!IsCommandedSpell()) { - const std::vector v = GatherSpellTargets(false, tar); - - for (Mob* m : v) { + for (Mob* m : GatherSpellTargets(false, tar)) { SetBotSpellRecastTimer(spell_type, m, true); } } @@ -513,9 +511,7 @@ bool Bot::BotCastHeal(Mob* tar, uint8 bot_class, BotSpell& bot_spell, uint16 spe if (IsGroupSpell(bot_spell.SpellId)) { if (bot_class != Class::Bard) { if (!IsCommandedSpell()) { - const std::vector v = GatherSpellTargets(false, tar); - - for (Mob* m : v) { + for (Mob* m : GatherSpellTargets(false, tar)) { SetBotSpellRecastTimer(spell_type, m, true); } } @@ -1309,15 +1305,11 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* caster, Mob* tar, uint16 spell_ty for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsRegularGroupHealSpell(bot_spell_list_itr->SpellId)) { - if (!caster->IsCommandedSpell()) { - target_count = 0; + if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) { + uint16 spell_id = bot_spell_list_itr->SpellId; - for (Mob* m : caster->GetSpellTargetList()) { - if (caster->IsValidSpellRange(bot_spell_list_itr->SpellId, m) && caster->CastChecks(bot_spell_list_itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { - ++target_count; - } - } + if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { + target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { continue; @@ -1351,14 +1343,10 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* caster, Mob* tar, uint16 for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) { - if (!caster->IsCommandedSpell()) { - target_count = 0; + uint16 spell_id = bot_spell_list_itr->SpellId; - for (Mob* m : caster->GetSpellTargetList()) { - if (caster->IsValidSpellRange(bot_spell_list_itr->SpellId, m) && caster->CastChecks(bot_spell_list_itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { - ++target_count; - } - } + if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { + target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { continue; @@ -1389,17 +1377,13 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* caster, Mob* tar, uint16 int target_count = 0; - for(std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { + for (std::list::iterator bot_spell_list_itr = bot_spell_list.begin(); bot_spell_list_itr != bot_spell_list.end(); ++bot_spell_list_itr) { // Assuming all the spells have been loaded into this list by level and in descending order - if (IsGroupCompleteHealSpell(bot_spell_list_itr->SpellId)) { - if (!caster->IsCommandedSpell()) { - target_count = 0; + if (IsGroupHealOverTimeSpell(bot_spell_list_itr->SpellId)) { + uint16 spell_id = bot_spell_list_itr->SpellId; - for (Mob* m : caster->GetSpellTargetList()) { - if (caster->IsValidSpellRange(bot_spell_list_itr->SpellId, m) && caster->CastChecks(bot_spell_list_itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { - ++target_count; - } - } + if (!caster->IsCommandedSpell() && caster->IsValidSpellRange(spell_id, tar)) { + target_count = caster->GetNumberNeedingHealedInGroup(tar, spell_type, spell_id, caster->GetAOERange(spell_id)); if (target_count < caster->GetSpellTypeAEOrGroupTargetCount(spell_type)) { continue; @@ -2045,13 +2029,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* caster, Mob* tar, uint16 spell_type) { continue; } - for (Mob* m : caster->GetSpellTargetList()) { - if (IsGroupBotSpellType(spell_type)) { - if (!caster->IsInGroupOrRaid(m, true)) { - continue; - } - } - + for (Mob* m : (IsGroupBotSpellType(spell_type) ? caster->GetSpellTargetList() : caster->GetSpellTargetList(true))) { if (caster->GetNeedsCured(m)) { if (caster->CastChecks(itr->SpellId, m, spell_type, true, IsGroupBotSpellType(spell_type))) { if (m->FindType(SE_PoisonCounter)) { From 0690783a9d1e99005d6bee0824597ea920e26df9 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 15:46:42 -0600 Subject: [PATCH 390/394] Throw added client los pet checks behind LoS cheat rule for bots --- zone/client_packet.cpp | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index ac571add96..fa76d76df1 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11093,7 +11093,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (!target) break; - if (!DoLosChecks(target)) { + if (CheckLosCheat(target) && !DoLosChecks(target)) { mypet->SayString(this, NOT_LEGAL_TARGET); break; } @@ -11107,8 +11107,10 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) break; //prevent pet from attacking stuff while feared if (!mypet->IsAttackAllowed(target)) { - mypet->SayString(this, NOT_LEGAL_TARGET); - break; + if (CheckLosCheat(target) && !DoLosChecks(target)) { + mypet->SayString(this, NOT_LEGAL_TARGET); + break; + } } // default range is 200, takes Z into account @@ -11161,7 +11163,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) break; } - if (!DoLosChecks(GetTarget())) { + if (CheckLosCheat(GetTarget()) && !DoLosChecks(GetTarget())) { mypet->SayString(this, NOT_LEGAL_TARGET); break; } @@ -11172,8 +11174,10 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } if (!mypet->IsAttackAllowed(GetTarget())) { - mypet->SayString(this, NOT_LEGAL_TARGET); - break; + if (CheckLosCheat(GetTarget()) && !DoLosChecks(GetTarget())) { + mypet->SayString(this, NOT_LEGAL_TARGET); + break; + } } if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) { From 8f4668b69b6c73ff2ad0cb3b0e748718f8485026 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 19:04:27 -0600 Subject: [PATCH 391/394] CLeanup give_exp on npc death logic and ensure client pets always pass. --- zone/attack.cpp | 29 +++++++++++++++-------------- zone/mob.cpp | 1 + 2 files changed, 16 insertions(+), 14 deletions(-) diff --git a/zone/attack.cpp b/zone/attack.cpp index cc61f83cef..c6614992ef 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2617,22 +2617,23 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy } if (give_exp && give_exp->HasOwner()) { - bool owner_in_group = false; + auto owner = give_exp->GetOwner(); - if ( - give_exp->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) || - ( - give_exp->IsPet() && - give_exp->GetOwner() && - give_exp->GetOwner()->IsInGroupOrRaid(give_exp->GetUltimateOwner(), RuleB(Bots, SameRaidGroupForXP)) - ) - ) { - owner_in_group = true; - } - - give_exp = give_exp->GetUltimateOwner(); + if (owner) { + Mob* ulimate_owner = give_exp->GetUltimateOwner(); + bool pet_owner_is_client = give_exp->IsPet() && owner->IsClient(); + bool pet_owner_is_bot = give_exp->IsPet() && owner->IsBot(); + bool owner_is_client = owner->IsClient(); + + bool is_in_same_group_or_raid = ( + pet_owner_is_client || + (pet_owner_is_bot && owner->IsInGroupOrRaid(ulimate_owner, RuleB(Bots, SameRaidGroupForXP))) || + (owner_is_client && give_exp->IsInGroupOrRaid(ulimate_owner, RuleB(Bots, SameRaidGroupForXP))) + ); - if (!owner_in_group) { + give_exp = (is_in_same_group_or_raid ? give_exp->GetUltimateOwner() : nullptr); + } + else { give_exp = nullptr; } } diff --git a/zone/mob.cpp b/zone/mob.cpp index ed2276cf73..9d3b3007c9 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -8669,6 +8669,7 @@ bool Mob::DoLosChecks(Mob* other) { if (CheckLosCheatExempt(other)) { return true; } + return false; } From 53817f003cadeb7396a55c20dbce459499d7fb7e Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 19:29:54 -0600 Subject: [PATCH 392/394] Undo unintended rename from previous refactor --- common/net/websocket_server.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/net/websocket_server.cpp b/common/net/websocket_server.cpp index 1f034f1daa..f74f437bac 100644 --- a/common/net/websocket_server.cpp +++ b/common/net/websocket_server.cpp @@ -25,7 +25,7 @@ struct MethodHandlerEntry struct EQ::Net::WebsocketServer::Impl { std::unique_ptr server; - std::unique_ptr m_ping_timer; + std::unique_ptr ping_timer; std::map, std::unique_ptr> connections; std::map methods; websocket_server ws_server; @@ -54,7 +54,7 @@ EQ::Net::WebsocketServer::WebsocketServer(const std::string &addr, int port) return websocketpp::lib::error_code(); }); - _impl->m_ping_timer = std::make_unique(5000, true, [this](EQ::Timer *t) { + _impl->ping_timer = std::make_unique(5000, true, [this](EQ::Timer *t) { auto iter = _impl->connections.begin(); while (iter != _impl->connections.end()) { From 997ff78c367051f7b9b5a6766cef6f26f2b39b83 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 19:43:04 -0600 Subject: [PATCH 393/394] Remove pointless Bots, SameRaidGroupForXP rule --- common/ruletypes.h | 1 - zone/attack.cpp | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index 99abf1c147..71b5c4ebdc 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -764,7 +764,6 @@ RULE_REAL(Bots, ManaRegen, 2.0, "Adjust mana regen. Acts as a final multiplier, RULE_BOOL(Bots, PreferNoManaCommandSpells, true, "Give sorting priority to newer no-mana spells (i.e., 'Bind Affinity')") RULE_BOOL(Bots, QuestableSpawnLimit, false, "Optional quest method to manage bot spawn limits using the quest_globals name bot_spawn_limit, see: /bazaar/Aediles_Thrall.pl") RULE_INT(Bots, SpawnLimit, 71, "Number of bots a character can have spawned at one time, You + 71 bots is a 12 group pseudo-raid") -RULE_BOOL(Bots, SameRaidGroupForXP, false, "Whether or not bots are required to be in the same raid group to give exp. Default false") 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, CasterStopMeleeLevel, 13, "Level at which caster bots stop melee attacks") diff --git a/zone/attack.cpp b/zone/attack.cpp index c6614992ef..dd70222cea 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -2627,8 +2627,8 @@ bool NPC::Death(Mob* killer_mob, int64 damage, uint16 spell, EQ::skills::SkillTy bool is_in_same_group_or_raid = ( pet_owner_is_client || - (pet_owner_is_bot && owner->IsInGroupOrRaid(ulimate_owner, RuleB(Bots, SameRaidGroupForXP))) || - (owner_is_client && give_exp->IsInGroupOrRaid(ulimate_owner, RuleB(Bots, SameRaidGroupForXP))) + (pet_owner_is_bot && owner->IsInGroupOrRaid(ulimate_owner)) || + (owner_is_client && give_exp->IsInGroupOrRaid(ulimate_owner)) ); give_exp = (is_in_same_group_or_raid ? give_exp->GetUltimateOwner() : nullptr); From ed58f63190e45c9b3e1b7ec8a84d8a7122c11de8 Mon Sep 17 00:00:00 2001 From: nytmyr <53322305+nytmyr@users.noreply.github.com> Date: Sun, 2 Feb 2025 22:33:08 -0600 Subject: [PATCH 394/394] Revision to 0690783a9d1e99005d6bee0824597ea920e26df9 --- zone/client_packet.cpp | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/zone/client_packet.cpp b/zone/client_packet.cpp index fa76d76df1..2955c6b75e 100644 --- a/zone/client_packet.cpp +++ b/zone/client_packet.cpp @@ -11093,7 +11093,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) if (!target) break; - if (CheckLosCheat(target) && !DoLosChecks(target)) { + if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(target) || !CheckLosCheat(target))) { mypet->SayString(this, NOT_LEGAL_TARGET); break; } @@ -11107,10 +11107,8 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) break; //prevent pet from attacking stuff while feared if (!mypet->IsAttackAllowed(target)) { - if (CheckLosCheat(target) && !DoLosChecks(target)) { - mypet->SayString(this, NOT_LEGAL_TARGET); - break; - } + mypet->SayString(this, NOT_LEGAL_TARGET); + break; } // default range is 200, takes Z into account @@ -11163,7 +11161,7 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) break; } - if (CheckLosCheat(GetTarget()) && !DoLosChecks(GetTarget())) { + if (RuleB(Map, CheckForLoSCheat) && (!DoLosChecks(GetTarget()) || !CheckLosCheat(GetTarget()))) { mypet->SayString(this, NOT_LEGAL_TARGET); break; } @@ -11174,10 +11172,8 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app) } if (!mypet->IsAttackAllowed(GetTarget())) { - if (CheckLosCheat(GetTarget()) && !DoLosChecks(GetTarget())) { - mypet->SayString(this, NOT_LEGAL_TARGET); - break; - } + mypet->SayString(this, NOT_LEGAL_TARGET); + break; } if ((mypet->GetPetType() == petAnimation && aabonuses.PetCommands[PetCommand]) || mypet->GetPetType() != petAnimation) {