Added HealRotation HOT methodology (Heal Override Target) and load/save/delete capabilities

This commit is contained in:
Uleat 2016-04-08 20:58:17 -04:00
parent e75a53b775
commit 6e11128cbc
12 changed files with 723 additions and 25 deletions

View File

@ -1,5 +1,9 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 04/08/2016 ==
Uleat: Added Heal Rotation HOTs (Heal Override Targets) that can be set for proactive healing (default HR behavior is reactive)
Uleat: Added the ability to save/load/delete Heal Rotations based on targeted member - load is automatic when ^hrcreate is used on a bot that has a saved HR entry
== 04/07/2016 ==
Uleat: Rework of eq_dictionary to facilitate inventory work

View File

@ -32,7 +32,7 @@
#define CURRENT_BINARY_DATABASE_VERSION 9096
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9003
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9005
#else
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
#endif

View File

@ -2,6 +2,8 @@
9001|2016_03_24_bots_command_settings.sql|SHOW TABLES LIKE 'bot_command_settings'|empty|
9002|2016_03_24_bots_command_rules.sql|SELECT * FROM `rule_values` WHERE `rule_name` LIKE 'Bots:CommandSpellRank'|empty|
9003|2016_04_05_bots_pet_spell_id_field.sql|SHOW COLUMNS FROM `bot_pets` LIKE 'pet_id'|not_empty|
9004|2016_04_07_bots_heal_override_target.sql|SELECT `bot_command` FROM `bot_command_settings` WHERE `bot_command` LIKE 'healrotationclearhot'|empty|
9005|2016_04_08_bots_heal_rotations.sql|SHOW TABLES LIKE 'bot_heal_rotations'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,3 @@
INSERT INTO `bot_command_settings` VALUES
('healrotationclearhot', 0, 'hrclearhot'),
('healrotationsethot', 0, 'hrsethot');

View File

@ -0,0 +1,41 @@
CREATE TABLE `bot_heal_rotations` (
`heal_rotation_index` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`bot_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`interval` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`fast_heals` INT(3) UNSIGNED NOT NULL DEFAULT '0',
`adaptive_targeting` INT(3) UNSIGNED NOT NULL DEFAULT '0',
`casting_override` INT(3) UNSIGNED NOT NULL DEFAULT '0',
`safe_hp_base` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`safe_hp_cloth` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`safe_hp_leather` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`safe_hp_chain` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`safe_hp_plate` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`critical_hp_base` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`critical_hp_cloth` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`critical_hp_leather` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`critical_hp_chain` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
`critical_hp_plate` FLOAT(11) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`heal_rotation_index`),
CONSTRAINT `FK_bot_heal_rotations` FOREIGN KEY (`bot_id`) REFERENCES `bot_data` (`bot_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `bot_heal_rotation_members` (
`member_index` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`heal_rotation_index` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`bot_id` INT(11) UNSIGNED NOT NULL DEFAULT '0',
PRIMARY KEY (`member_index`),
CONSTRAINT `FK_bot_heal_rotation_members_1` FOREIGN KEY (`heal_rotation_index`) REFERENCES `bot_heal_rotations` (`heal_rotation_index`),
CONSTRAINT `FK_bot_heal_rotation_members_2` FOREIGN KEY (`bot_id`) REFERENCES `bot_data` (`bot_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
CREATE TABLE `bot_heal_rotation_targets` (
`target_index` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`heal_rotation_index` INT(11) UNSIGNED NOT NULL DEFAULT '0',
`target_name` varchar(64) NOT NULL DEFAULT '',
PRIMARY KEY (`target_index`),
CONSTRAINT `FK_bot_heal_rotation_targets` FOREIGN KEY (`heal_rotation_index`) REFERENCES `bot_heal_rotations` (`heal_rotation_index`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `bot_command_settings` VALUES
('healrotationdelete', 0, 'hrdelete'),
('healrotationsave', 0, 'hrsave');

View File

@ -1597,6 +1597,25 @@ bool Bot::DeleteBot()
if (!bot_owner)
return false;
if (!botdb.DeleteHealRotation(GetBotID())) {
bot_owner->Message(13, "%s", BotDatabase::fail::DeleteHealRotation());
return false;
}
std::string query = StringFormat("DELETE FROM `bot_heal_rotation_members` WHERE `bot_id` = '%u'", GetBotID());
auto results = botdb.QueryDatabase(query);
if (!results.Success()) {
bot_owner->Message(13, "Failed to delete heal rotation member '%s'", GetCleanName());
return false;
}
query = StringFormat("DELETE FROM `bot_heal_rotation_targets` WHERE `target_name` LIKE '%s'", GetCleanName());
results = botdb.QueryDatabase(query);
if (!results.Success()) {
bot_owner->Message(13, "Failed to delete heal rotation target '%s'", GetCleanName());
return false;
}
if (!DeletePet()) {
bot_owner->Message(13, "Failed to delete pet for '%s'", GetCleanName());
return false;
@ -8358,7 +8377,7 @@ bool Bot::IsMyHealRotationSet()
{
if (!IsHealRotationMember())
return false;
if (!m_member_of_heal_rotation->IsActive())
if (!m_member_of_heal_rotation->IsActive() && !m_member_of_heal_rotation->IsHOTActive())
return false;
if (!m_member_of_heal_rotation->CastingReady())
return false;

View File

@ -1370,13 +1370,17 @@ int bot_command_init(void)
bot_command_add("healrotationadjustsafe", "Adjusts the safe HP limit of the heal rotation instance's Class Armor Type criteria", 0, bot_subcommand_heal_rotation_adjust_safe) ||
bot_command_add("healrotationcastingoverride", "Enables or disables casting overrides within the heal rotation instance", 0, bot_subcommand_heal_rotation_casting_override) ||
bot_command_add("healrotationchangeinterval", "Changes casting interval between members within the heal rotation instance", 0, bot_subcommand_heal_rotation_change_interval) ||
bot_command_add("healrotationclearhot", "Clears the HOT of a heal rotation instance", 0, bot_subcommand_heal_rotation_clear_hot) ||
bot_command_add("healrotationcleartargets", "Removes all targets from a heal rotation instance", 0, bot_subcommand_heal_rotation_clear_targets) ||
bot_command_add("healrotationcreate", "Creates a bot heal rotation instance and designates a leader", 0, bot_subcommand_heal_rotation_create) ||
bot_command_add("healrotationdelete", "Deletes a bot heal rotation entry by leader", 0, bot_subcommand_heal_rotation_delete) ||
bot_command_add("healrotationfastheals", "Enables or disables fast heals within the heal rotation instance", 0, bot_subcommand_heal_rotation_fast_heals) ||
bot_command_add("healrotationlist", "Reports heal rotation instance(s) information", 0, bot_subcommand_heal_rotation_list) ||
bot_command_add("healrotationremovemember", "Removes a bot from a heal rotation instance", 0, bot_subcommand_heal_rotation_remove_member) ||
bot_command_add("healrotationremovetarget", "Removes target from a heal rotations instance", 0, bot_subcommand_heal_rotation_remove_target) ||
bot_command_add("healrotationresetlimits", "Resets all Class Armor Type HP limit criteria in a heal rotation to its default value", 0, bot_subcommand_heal_rotation_reset_limits) ||
bot_command_add("healrotationsave", "Saves a bot heal rotation entry by leader", 0, bot_subcommand_heal_rotation_save) ||
bot_command_add("healrotationsethot", "Sets the HOT in a heal rotation instance", 0, bot_subcommand_heal_rotation_set_hot) ||
bot_command_add("healrotationstart", "Starts a heal rotation", 0, bot_subcommand_heal_rotation_start) ||
bot_command_add("healrotationstop", "Stops a heal rotation", 0, bot_subcommand_heal_rotation_stop) ||
bot_command_add("help", "List available commands and their description - specify partial command as argument to search", 0, bot_command_help) ||
@ -3064,13 +3068,17 @@ void bot_command_heal_rotation(Client *c, const Seperator *sep)
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");
/* VS2012 code - end */
@ -3078,8 +3086,9 @@ void bot_command_heal_rotation(Client *c, const Seperator *sep)
/* VS2013 code
const std::list<const char*> subcommand_list = {
"healrotationadaptivetargeting", "healrotationaddmember", "healrotationaddtarget", "healrotationadjustcritical", "healrotationadjustsafe",
"healrotationcastoverride", "healrotationchangeinterval", "healrotationcleartargets", "healrotationcreate", "healrotationfastheals",
"healrotationlist", "healrotationremovemember", "healrotationremovetarget", "healrotationresetlimits", "healrotationstart", "healrotationstop"
"healrotationcastoverride", "healrotationchangeinterval", "healrotationclearhot", "healrotationcleartargets", "healrotationcreate",
"healrotationdelete", "healrotationfastheals", "healrotationlist", "healrotationremovemember", "healrotationremovetarget", "healrotationsave",
"healrotationresetlimits", "healrotationsethot", "healrotationstart", "healrotationstop"
};
*/
@ -6132,10 +6141,10 @@ void bot_subcommand_heal_rotation_adjust_critical(Client *c, const Seperator *se
else if (!critical_arg.compare("-"))
critical_ratio = (*current_member->MemberOfHealRotation())->ArmorTypeCriticalHPRatio(armor_type_value) - HP_RATIO_DELTA;
if (critical_ratio > SAFE_HP_RATIO_BASE)
critical_ratio = SAFE_HP_RATIO_BASE;
if (critical_ratio < CRITICAL_HP_RATIO_BASE)
critical_ratio = CRITICAL_HP_RATIO_BASE;
if (critical_ratio > SAFE_HP_RATIO_ABS)
critical_ratio = SAFE_HP_RATIO_ABS;
if (critical_ratio < CRITICAL_HP_RATIO_ABS)
critical_ratio = CRITICAL_HP_RATIO_ABS;
if (!(*current_member->MemberOfHealRotation())->SetArmorTypeCriticalHPRatio(armor_type_value, critical_ratio)) {
c->Message(m_fail, "Critical value %3.1f%%(%u) exceeds safe value %3.1f%%(%u) for %s's Heal Rotation",
@ -6198,10 +6207,10 @@ void bot_subcommand_heal_rotation_adjust_safe(Client *c, const Seperator *sep)
else if (!safe_arg.compare("-"))
safe_ratio = (*current_member->MemberOfHealRotation())->ArmorTypeSafeHPRatio(armor_type_value) - HP_RATIO_DELTA;
if (safe_ratio > SAFE_HP_RATIO_BASE)
safe_ratio = SAFE_HP_RATIO_BASE;
if (safe_ratio < CRITICAL_HP_RATIO_BASE)
safe_ratio = CRITICAL_HP_RATIO_BASE;
if (safe_ratio > SAFE_HP_RATIO_ABS)
safe_ratio = SAFE_HP_RATIO_ABS;
if (safe_ratio < CRITICAL_HP_RATIO_ABS)
safe_ratio = CRITICAL_HP_RATIO_ABS;
if (!(*current_member->MemberOfHealRotation())->SetArmorTypeSafeHPRatio(armor_type_value, safe_ratio)) {
c->Message(m_fail, "Safe value %3.1f%%(%u) does not exceed critical value %3.1f%%(%u) for %s's Heal Rotation",
@ -6256,13 +6265,13 @@ void bot_subcommand_heal_rotation_casting_override(Client *c, const Seperator *s
hr_casting_override = true;
}
else if (casting_override_arg.compare("off")) {
c->Message(m_action, "Fast heals are currently '%s' for %s's Heal Rotation", (((*current_member->MemberOfHealRotation())->CastingOverride()) ? ("on") : ("off")), current_member->GetCleanName());
c->Message(m_action, "Casting override is currently '%s' for %s's Heal Rotation", (((*current_member->MemberOfHealRotation())->CastingOverride()) ? ("on") : ("off")), current_member->GetCleanName());
return;
}
(*current_member->MemberOfHealRotation())->SetCastingOverride(hr_casting_override);
c->Message(m_action, "Fast heals are now '%s' for %s's Heal Rotation", (((*current_member->MemberOfHealRotation())->CastingOverride()) ? ("on") : ("off")), current_member->GetCleanName());
c->Message(m_action, "Casting override is now '%s' for %s's Heal Rotation", (((*current_member->MemberOfHealRotation())->CastingOverride()) ? ("on") : ("off")), current_member->GetCleanName());
}
void bot_subcommand_heal_rotation_change_interval(Client *c, const Seperator *sep)
@ -6323,6 +6332,42 @@ void bot_subcommand_heal_rotation_change_interval(Client *c, const Seperator *se
c->Message(m_action, "Casting interval is now '%i' second%s for %s's Heal Rotation", hr_change_interval_s, ((hr_change_interval_s == 1) ? ("") : ("s")), current_member->GetCleanName());
}
void bot_subcommand_heal_rotation_clear_hot(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_subcommand_heal_rotation_clear_hot", sep->arg[0], "healrotationclearhot"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: (<target_member>) %s ([member_name])", sep->arg[0]);
return;
}
std::list<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty())
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
if (sbl.empty()) {
c->Message(m_fail, "You must <target> or [name] a current member as a bot that you own to use this command");
return;
}
auto current_member = sbl.front();
if (!current_member) {
c->Message(m_unknown, "Error: Current member bot dereferenced to nullptr");
return;
}
if (!current_member->IsHealRotationMember()) {
c->Message(m_fail, "%s is not a current member of a Heal Rotation", current_member->GetCleanName());
return;
}
if (!(*current_member->MemberOfHealRotation())->ClearHOTTarget()) {
c->Message(m_fail, "Failed to clear %s's Heal Rotation HOT", current_member->GetCleanName());
}
c->Message(m_action, "Succeeded in clearing %s's Heal Rotation HOT", current_member->GetCleanName());
}
void bot_subcommand_heal_rotation_clear_targets(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_subcommand_heal_rotation_clear_targets", sep->arg[0], "healrotationcleartargets"))
@ -6437,8 +6482,115 @@ void bot_subcommand_heal_rotation_create(Client *c, const Seperator *sep)
c->Message(m_fail, "Failed to add %s as a current member to a new Heal Rotation", creator_member->GetCleanName());
return;
}
std::list<uint32> member_list;
std::list<std::string> target_list;
bool load_flag = false;
bool member_fail = false;
bool target_fail = false;
if (!botdb.LoadHealRotation(creator_member, member_list, target_list, load_flag, member_fail, target_fail))
c->Message(m_fail, "%s", BotDatabase::fail::LoadHealRotation());
c->Message(m_action, "Successfully added %s as a current member to a new Heal Rotation", creator_member->GetCleanName());
if (!load_flag) {
c->Message(m_action, "Successfully added %s as a current member to a new Heal Rotation", creator_member->GetCleanName());
return;
}
if (!member_fail) {
MyBots::PopulateSBL_BySpawnedBots(c, sbl);
for (auto member_iter : member_list) {
if (!member_iter || member_iter == creator_member->GetBotID())
continue;
bool member_found = false;
for (auto bot_iter : sbl) {
if (bot_iter->GetBotID() != member_iter)
continue;
if (!bot_iter->JoinHealRotationMemberPool(creator_member->MemberOfHealRotation()))
c->Message(m_fail, "Failed to add member '%s'", bot_iter->GetCleanName());
member_found = true;
break;
}
if (!member_found)
c->Message(m_fail, "Could not locate member with bot id '%u'", member_iter);
}
}
else {
c->Message(m_fail, "%s", BotDatabase::fail::LoadHealRotationMembers());
}
if (!target_fail) {
for (auto target_iter : target_list) {
if (target_iter.empty())
continue;
auto target_mob = entity_list.GetMob(target_iter.c_str());
if (!target_mob) {
c->Message(m_fail, "Could not locate target '%s'", target_iter.c_str());
continue;
}
if (!target_mob->JoinHealRotationTargetPool(creator_member->MemberOfHealRotation()))
c->Message(m_fail, "Failed to add target '%s'", target_mob->GetCleanName());
}
}
else {
c->Message(m_fail, "%s", BotDatabase::fail::LoadHealRotationTargets());
}
c->Message(m_action, "Successfully loaded %s's Heal Rotation", creator_member->GetCleanName());
}
void bot_subcommand_heal_rotation_delete(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_subcommand_heal_rotation_delete", sep->arg[0], "healrotationdelete"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: (<target_member>) %s ([option: all]) ([member_name])", sep->arg[0]);
return;
}
bool all_flag = false;
int name_arg = 1;
if (!strcasecmp(sep->arg[1], "all")) {
all_flag = true;
name_arg = 2;
}
if (all_flag) {
if (botdb.DeleteAllHealRotations(c->CharacterID()))
c->Message(m_action, "Succeeded in deleting all heal rotations");
else
c->Message(m_fail, "%s", BotDatabase::fail::DeleteAllHealRotations());
return;
}
std::list<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[name_arg]);
if (sbl.empty())
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
if (sbl.empty()) {
c->Message(m_fail, "You must <target> or [name] a current member as a bot that you own to use this command");
return;
}
auto current_member = sbl.front();
if (!current_member) {
c->Message(m_unknown, "Error: Current member bot dereferenced to nullptr");
return;
}
if (!botdb.DeleteHealRotation(current_member->GetBotID())) {
c->Message(m_fail, "%s", BotDatabase::fail::DeleteHealRotation());
return;
}
c->Message(m_action, "Succeeded in deleting %s's heal rotation", current_member->GetCleanName());
}
void bot_subcommand_heal_rotation_fast_heals(Client *c, const Seperator *sep)
@ -6529,6 +6681,8 @@ void bot_subcommand_heal_rotation_list(Client *c, const Seperator *sep)
c->Message(m_message, "Fast heals: '%s'", (((*current_member->MemberOfHealRotation())->FastHeals()) ? ("on") : ("off")));
c->Message(m_message, "Adaptive targeting: '%s'", (((*current_member->MemberOfHealRotation())->AdaptiveTargeting()) ? ("on") : ("off")));
c->Message(m_message, "Casting override: '%s'", (((*current_member->MemberOfHealRotation())->CastingOverride()) ? ("on") : ("off")));
c->Message(m_message, "HOT state: %s", (((*current_member->MemberOfHealRotation())->IsHOTActive()) ? ("active") : ("inactive")));
c->Message(m_message, "HOT target: %s", (((*current_member->MemberOfHealRotation())->HOTTarget()) ? ((*current_member->MemberOfHealRotation())->HOTTarget()->GetCleanName()) : ("null")));
c->Message(m_message, "Base hp limits - critical: %3.1f%%, safe: %3.1f%%",
(*current_member->MemberOfHealRotation())->ArmorTypeCriticalHPRatio(ARMOR_TYPE_UNKNOWN),
@ -6687,6 +6841,97 @@ void bot_subcommand_heal_rotation_reset_limits(Client *c, const Seperator *sep)
c->Message(m_action, "Class Armor Type HP limit criteria has been set to default values for %s's Heal Rotation", current_member->GetCleanName());
}
void bot_subcommand_heal_rotation_save(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_subcommand_heal_rotation_save", sep->arg[0], "healrotationsave"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: (<target_member>) %s ([member_name])", sep->arg[0]);
return;
}
std::list<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[1]);
if (sbl.empty())
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
if (sbl.empty()) {
c->Message(m_fail, "You must <target> or [name] a current member as a bot that you own to use this command");
return;
}
auto current_member = sbl.front();
if (!current_member) {
c->Message(m_unknown, "Error: Current member bot dereferenced to nullptr");
return;
}
if (!current_member->IsHealRotationMember()) {
c->Message(m_fail, "%s is not a current member of a Heal Rotation", current_member->GetCleanName());
return;
}
bool member_fail = false;
bool target_fail = false;
if (!botdb.SaveHealRotation(current_member, member_fail, target_fail)) {
c->Message(m_fail, "%s", BotDatabase::fail::SaveHealRotation());
return;
}
if (member_fail)
c->Message(m_fail, "Failed to save heal rotation members");
if (target_fail)
c->Message(m_fail, "Failed to save heal rotation targets");
c->Message(m_action, "Succeeded in saving %s's heal rotation", current_member->GetCleanName());
}
void bot_subcommand_heal_rotation_set_hot(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_subcommand_heal_rotation_set_hot", sep->arg[0], "healrotationsethot"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: (<target_member>) %s [heal_override_target_name] ([member_name])", sep->arg[0]);
return;
}
std::list<Bot*> sbl;
MyBots::PopulateSBL_ByNamedBot(c, sbl, sep->arg[2]);
if (sbl.empty())
MyBots::PopulateSBL_ByTargetedBot(c, sbl);
if (sbl.empty()) {
c->Message(m_fail, "You must <target> or [name] a current member as a bot that you own to use this command");
return;
}
auto current_member = sbl.front();
if (!current_member) {
c->Message(m_unknown, "Error: Current member bot dereferenced to nullptr");
return;
}
if (!current_member->IsHealRotationMember()) {
c->Message(m_fail, "%s is not a member of a Heal Rotation", current_member->GetCleanName());
return;
}
auto hot_target = entity_list.GetMob(sep->arg[1]);
if (!hot_target) {
c->Message(m_fail, "No target exists by the name '%s'", sep->arg[1]);
return;
}
if (!(*current_member->MemberOfHealRotation())->IsTargetInPool(hot_target)) {
c->Message(m_fail, "%s is not a target in %s's Heal Rotation", hot_target->GetCleanName(), current_member->GetCleanName());
return;
}
if (!(*current_member->MemberOfHealRotation())->SetHOTTarget(hot_target)) {
c->Message(m_fail, "Failed to set %s as the HOT in %s's Heal Rotation", hot_target->GetCleanName(), current_member->GetCleanName());
return;
}
c->Message(m_action, "Succeeded in setting %s as the HOT in %s's Heal Rotation", hot_target->GetCleanName(), current_member->GetCleanName());
}
void bot_subcommand_heal_rotation_start(Client *c, const Seperator *sep)
{
if (helper_command_alias_fail(c, "bot_subcommand_heal_rotation_start", sep->arg[0], "healrotationstart"))

View File

@ -633,13 +633,17 @@ void bot_subcommand_heal_rotation_adjust_critical(Client *c, const Seperator *se
void bot_subcommand_heal_rotation_adjust_safe(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_casting_override(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_change_interval(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_clear_hot(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_clear_targets(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_create(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_delete(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_fast_heals(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_list(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_remove_member(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_remove_target(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_reset_limits(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_save(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_set_hot(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_start(Client *c, const Seperator *sep);
void bot_subcommand_heal_rotation_stop(Client *c, const Seperator *sep);
void bot_subcommand_inventory_give(Client *c, const Seperator *sep);

View File

@ -2407,6 +2407,284 @@ bool BotDatabase::LoadGroupedBotsByGroupID(const uint32 group_id, std::list<uint
}
/* Bot heal rotation functions */
bool BotDatabase::LoadHealRotationIDByBotID(const uint32 bot_id, uint32& hr_index)
{
if (!bot_id)
return false;
query = StringFormat("SELECT `heal_rotation_index` FROM `bot_heal_rotations` WHERE `bot_id` = '%u' LIMIT 1", bot_id);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
if (!results.RowCount())
return true;
auto row = results.begin();
hr_index = atoi(row[0]);
return true;
}
bool BotDatabase::LoadHealRotation(Bot* hr_member, std::list<uint32>& member_list, std::list<std::string>& target_list, bool& load_flag, bool& member_fail, bool& target_fail)
{
if (!hr_member)
return false;
uint32 hr_index = 0;
if (!LoadHealRotationIDByBotID(hr_member->GetBotID(), hr_index))
return false;
if (!hr_index)
return true;
if (!hr_member->IsHealRotationMember())
return false;
query = StringFormat(
"SELECT "
" `interval`,"
" `fast_heals`,"
" `adaptive_targeting`,"
" `casting_override`,"
" `safe_hp_base`,"
" `safe_hp_cloth`,"
" `safe_hp_leather`,"
" `safe_hp_chain`,"
" `safe_hp_plate`,"
" `critical_hp_base`,"
" `critical_hp_cloth`,"
" `critical_hp_leather`,"
" `critical_hp_chain`,"
" `critical_hp_plate`"
" FROM `bot_heal_rotations`"
" WHERE `heal_rotation_index` = '%u'"
" LIMIT 1",
hr_index
);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
if (!results.RowCount())
return true;
auto row = results.begin();
(*hr_member->MemberOfHealRotation())->SetIntervalS((uint32)atoi(row[0]));
(*hr_member->MemberOfHealRotation())->SetFastHeals((bool)atoi(row[1]));
(*hr_member->MemberOfHealRotation())->SetAdaptiveTargeting((bool)atoi(row[2]));
(*hr_member->MemberOfHealRotation())->SetCastingOverride((bool)atoi(row[3]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeSafeHPRatio(ARMOR_TYPE_UNKNOWN, atof(row[4]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeSafeHPRatio(ARMOR_TYPE_CLOTH, atof(row[5]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeSafeHPRatio(ARMOR_TYPE_LEATHER, atof(row[6]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeSafeHPRatio(ARMOR_TYPE_CHAIN, atof(row[7]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeSafeHPRatio(ARMOR_TYPE_PLATE, atof(row[8]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeCriticalHPRatio(ARMOR_TYPE_UNKNOWN, atof(row[9]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeCriticalHPRatio(ARMOR_TYPE_CLOTH, atof(row[10]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeCriticalHPRatio(ARMOR_TYPE_LEATHER, atof(row[11]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeCriticalHPRatio(ARMOR_TYPE_CHAIN, atof(row[12]));
(*hr_member->MemberOfHealRotation())->SetArmorTypeCriticalHPRatio(ARMOR_TYPE_PLATE, atof(row[13]));
load_flag = true;
if (!LoadHealRotationMembers(hr_index, member_list))
member_fail = true;
if (!LoadHealRotationTargets(hr_index, target_list))
target_fail = true;
return true;
}
bool BotDatabase::LoadHealRotationMembers(const uint32 hr_index, std::list<uint32>& member_list)
{
if (!hr_index)
return false;
query = StringFormat("SELECT `bot_id` FROM `bot_heal_rotation_members` WHERE `heal_rotation_index` = '%u'", hr_index);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
if (!results.RowCount())
return true;
for (auto row : results) {
if (row[0])
member_list.push_back(atoi(row[0]));
}
return true;
}
bool BotDatabase::LoadHealRotationTargets(const uint32 hr_index, std::list<std::string>& target_list)
{
if (!hr_index)
return false;
query = StringFormat("SELECT `target_name` FROM `bot_heal_rotation_targets` WHERE `heal_rotation_index` = '%u'", hr_index);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
if (!results.RowCount())
return true;
for (auto row : results) {
if (row[0])
target_list.push_back(row[0]);
}
return true;
}
bool BotDatabase::SaveHealRotation(Bot* hr_member, bool& member_fail, bool& target_fail)
{
if (!hr_member)
return false;
if (!DeleteHealRotation(hr_member->GetBotID()))
return false;
if (!hr_member->IsHealRotationMember())
return false;
query = StringFormat(
"INSERT INTO `bot_heal_rotations` ("
"`bot_id`,"
" `interval`,"
" `fast_heals`,"
" `adaptive_targeting`,"
" `casting_override`,"
" `safe_hp_base`,"
" `safe_hp_cloth`,"
" `safe_hp_leather`,"
" `safe_hp_chain`,"
" `safe_hp_plate`,"
" `critical_hp_base`,"
" `critical_hp_cloth`,"
" `critical_hp_leather`,"
" `critical_hp_chain`,"
" `critical_hp_plate`"
")"
" VALUES ("
"'%u',"
" '%u',"
" '%u',"
" '%u',"
" '%u',"
" '%f',"
" '%f',"
" '%f',"
" '%f',"
" '%f',"
" '%f',"
" '%f',"
" '%f',"
" '%f',"
" '%f'"
")",
hr_member->GetBotID(),
((*hr_member->MemberOfHealRotation())->IntervalS()),
((*hr_member->MemberOfHealRotation())->FastHeals()),
((*hr_member->MemberOfHealRotation())->AdaptiveTargeting()),
((*hr_member->MemberOfHealRotation())->CastingOverride()),
((*hr_member->MemberOfHealRotation())->ArmorTypeSafeHPRatio(ARMOR_TYPE_UNKNOWN)),
((*hr_member->MemberOfHealRotation())->ArmorTypeSafeHPRatio(ARMOR_TYPE_CLOTH)),
((*hr_member->MemberOfHealRotation())->ArmorTypeSafeHPRatio(ARMOR_TYPE_LEATHER)),
((*hr_member->MemberOfHealRotation())->ArmorTypeSafeHPRatio(ARMOR_TYPE_CHAIN)),
((*hr_member->MemberOfHealRotation())->ArmorTypeSafeHPRatio(ARMOR_TYPE_PLATE)),
((*hr_member->MemberOfHealRotation())->ArmorTypeCriticalHPRatio(ARMOR_TYPE_UNKNOWN)),
((*hr_member->MemberOfHealRotation())->ArmorTypeCriticalHPRatio(ARMOR_TYPE_CLOTH)),
((*hr_member->MemberOfHealRotation())->ArmorTypeCriticalHPRatio(ARMOR_TYPE_LEATHER)),
((*hr_member->MemberOfHealRotation())->ArmorTypeCriticalHPRatio(ARMOR_TYPE_CHAIN)),
((*hr_member->MemberOfHealRotation())->ArmorTypeCriticalHPRatio(ARMOR_TYPE_PLATE))
);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
uint32 hr_index = results.LastInsertedID();
if (!hr_index)
return false;
std::list<Bot*>* member_list = (*hr_member->MemberOfHealRotation())->MemberList();
for (auto member_iter : *member_list) {
if (!member_iter)
continue;
query = StringFormat("INSERT INTO `bot_heal_rotation_members` (`heal_rotation_index`, `bot_id`) VALUES ('%u', '%u')", hr_index, member_iter->GetBotID());
auto results = QueryDatabase(query);
if (!results.Success()) {
member_fail = true;
break;
}
}
std::list<Mob*>* target_list = (*hr_member->MemberOfHealRotation())->TargetList();
for (auto target_iter : *target_list) {
if (!target_iter)
continue;
query = StringFormat("INSERT INTO `bot_heal_rotation_targets` (`heal_rotation_index`, `target_name`) VALUES ('%u', '%s')", hr_index, target_iter->GetCleanName());
auto results = QueryDatabase(query);
if (!results.Success()) {
target_fail = true;
break;
}
}
return true;
}
bool BotDatabase::DeleteHealRotation(const uint32 creator_id)
{
if (!creator_id)
return false;
uint32 hr_index = 0;
if (!LoadHealRotationIDByBotID(creator_id, hr_index))
return false;
if (!hr_index)
return true;
query = StringFormat("DELETE FROM `bot_heal_rotation_targets` WHERE `heal_rotation_index` = '%u'", hr_index);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
query = StringFormat("DELETE FROM `bot_heal_rotation_members` WHERE `heal_rotation_index` = '%u'", hr_index);
results = QueryDatabase(query);
if (!results.Success())
return false;
query = StringFormat("DELETE FROM `bot_heal_rotations` WHERE `heal_rotation_index` = '%u'", hr_index);
results = QueryDatabase(query);
if (!results.Success())
return false;
return true;
}
bool BotDatabase::DeleteAllHealRotations(const uint32 owner_id)
{
if (!owner_id)
return false;
query = StringFormat("SELECT `bot_id` FROM `bot_heal_rotations` WHERE `bot_id` IN (SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u')", owner_id);
auto results = QueryDatabase(query);
if (!results.Success())
return false;
for (auto row : results) {
if (!row[0])
continue;
DeleteHealRotation(atoi(row[0]));
}
return true;
}
/* Bot miscellaneous functions */
@ -2495,6 +2773,15 @@ const char* BotDatabase::fail::LoadBotGroupsListByOwnerID() { return "Failed to
/* fail::Bot group functions */
const char* BotDatabase::fail::LoadGroupedBotsByGroupID() { return "Failed to load grouped bots by group id"; }
/* fail::Bot heal rotation functions */
const char* BotDatabase::fail::LoadHealRotationIDByBotID() { return "Failed to load heal rotation id by bot id"; }
const char* BotDatabase::fail::LoadHealRotation() { return "Failed to load heal rotation"; }
const char* BotDatabase::fail::LoadHealRotationMembers() { return "Failed to load heal rotation members"; }
const char* BotDatabase::fail::LoadHealRotationTargets() { return "Failed to load heal rotation targets"; }
const char* BotDatabase::fail::SaveHealRotation() { return "Failed to save heal rotation"; }
const char* BotDatabase::fail::DeleteHealRotation() { return "Failed to delete heal rotation"; }
const char* BotDatabase::fail::DeleteAllHealRotations() { return "Failed to delete all heal rotations"; }
/* fail::Bot miscellaneous functions */
#endif // BOTS

View File

@ -167,6 +167,17 @@ public:
bool LoadGroupedBotsByGroupID(const uint32 group_id, std::list<uint32>& group_list);
/* Bot heal rotation functions */
bool LoadHealRotationIDByBotID(const uint32 bot_id, uint32& hr_index);
bool LoadHealRotation(Bot* hr_member, std::list<uint32>& member_list, std::list<std::string>& target_list, bool& load_flag, bool& member_fail, bool& target_fail);
bool LoadHealRotationMembers(const uint32 hr_index, std::list<uint32>& member_list);
bool LoadHealRotationTargets(const uint32 hr_index, std::list<std::string>& target_list);
bool SaveHealRotation(Bot* hr_member, bool& member_fail, bool& target_fail);
bool DeleteHealRotation(const uint32 creator_id);
bool DeleteAllHealRotations(const uint32 owner_id);
/* Bot miscellaneous functions */
@ -257,6 +268,15 @@ public:
/* fail::Bot group functions */
static const char* LoadGroupedBotsByGroupID();
/* fail::Bot heal rotation functions */
static const char* LoadHealRotationIDByBotID();
static const char* LoadHealRotation();
static const char* LoadHealRotationMembers();
static const char* LoadHealRotationTargets();
static const char* SaveHealRotation();
static const char* DeleteHealRotation();
static const char* DeleteAllHealRotations();
/* fail::Bot miscellaneous functions */
};

View File

@ -25,10 +25,10 @@
#define SAFE_HP_RATIO_CHAIN 80.0f
#define SAFE_HP_RATIO_PLATE 75.0f
#define CRITICAL_HP_RATIO_CLOTH 30.0f
#define CRITICAL_HP_RATIO_LEATHER 25.0f
#define CRITICAL_HP_RATIO_CHAIN 15.0f
#define CRITICAL_HP_RATIO_PLATE 10.0f
#define CRITICAL_HP_RATIO_CLOTH 45.0f
#define CRITICAL_HP_RATIO_LEATHER 40.0f
#define CRITICAL_HP_RATIO_CHAIN 35.0f
#define CRITICAL_HP_RATIO_PLATE 30.0f
HealRotation::HealRotation(Bot* hr_creator, uint32 interval_ms, bool fast_heals, bool adaptive_targeting, bool casting_override)
{
@ -51,6 +51,9 @@ HealRotation::HealRotation(Bot* hr_creator, uint32 interval_ms, bool fast_heals,
m_is_active = false;
m_consumed = false;
m_hot_target = nullptr;
m_hot_active = false;
}
void HealRotation::SetIntervalMS(uint32 interval_ms)
@ -143,6 +146,11 @@ bool HealRotation::RemoveTargetFromPool(Mob* hr_target)
if (target_iter != hr_target)
continue;
if (m_hot_target == hr_target) {
m_hot_target = nullptr;
m_hot_active = false;
}
m_target_healing_stats_2.erase(hr_target);
m_target_healing_stats_1.erase(hr_target);
m_target_pool.remove(hr_target);
@ -172,6 +180,8 @@ bool HealRotation::ClearMemberPool()
bool HealRotation::ClearTargetPool()
{
m_hot_target = nullptr;
m_hot_active = false;
m_is_active = false;
auto clear_list = m_target_pool;
@ -184,11 +194,32 @@ bool HealRotation::ClearTargetPool()
return m_target_pool.empty();
}
bool HealRotation::SetHOTTarget(Mob* hot_target)
{
if (!hot_target || !IsTargetInPool(hot_target))
return false;
m_hot_target = hot_target;
m_hot_active = true;
return true;
}
bool HealRotation::ClearHOTTarget()
{
m_hot_target = nullptr;
m_hot_active = false;
return true;
}
bool HealRotation::Start()
{
m_is_active = false;
if (m_member_pool.empty() || m_target_pool.empty())
if (m_member_pool.empty() || m_target_pool.empty()) {
validate_hot();
return false;
}
m_cycle_pool = m_member_pool;
m_is_active = true;
@ -207,7 +238,7 @@ bool HealRotation::Stop()
Bot* HealRotation::CastingMember()
{
if (!m_is_active)
if (!m_is_active && !m_hot_active)
return nullptr;
if (m_cycle_pool.empty()) {
@ -222,6 +253,9 @@ Bot* HealRotation::CastingMember()
bool HealRotation::PokeCastingTarget()
{
if (m_hot_target && m_hot_active)
return true;
if (!m_is_active)
return false;
@ -248,6 +282,9 @@ bool HealRotation::PokeCastingTarget()
Mob* HealRotation::CastingTarget()
{
if (m_hot_target && m_hot_active)
return m_hot_target;
if (!m_is_active)
return nullptr;
if (!m_active_heal_target)
@ -304,6 +341,16 @@ bool HealRotation::IsTargetInPool(Mob* hr_target)
return false;
}
bool HealRotation::IsHOTTarget(Mob* hot_target)
{
if (!hot_target)
return false;
if (m_hot_target != hot_target)
return false;
return true;
}
void HealRotation::SetMemberIsCasting(Bot* hr_member, bool flag)
{
if (!hr_member)
@ -444,7 +491,7 @@ bool HealRotation::SetArmorTypeSafeHPRatio(uint8 armor_type, float hp_ratio)
{
if (armor_type >= ARMOR_TYPE_COUNT)
return false;
if (hp_ratio < CRITICAL_HP_RATIO_BASE || hp_ratio > SAFE_HP_RATIO_BASE)
if (hp_ratio < CRITICAL_HP_RATIO_ABS || hp_ratio > SAFE_HP_RATIO_ABS)
return false;
if (hp_ratio < m_critical_hp_ratio[armor_type])
return false;
@ -458,7 +505,7 @@ bool HealRotation::SetArmorTypeCriticalHPRatio(uint8 armor_type, float hp_ratio)
{
if (armor_type >= ARMOR_TYPE_COUNT)
return false;
if (hp_ratio < CRITICAL_HP_RATIO_BASE || hp_ratio > SAFE_HP_RATIO_BASE)
if (hp_ratio < CRITICAL_HP_RATIO_ABS || hp_ratio > SAFE_HP_RATIO_ABS)
return false;
if (hp_ratio > m_safe_hp_ratio[armor_type])
return false;
@ -863,6 +910,19 @@ void HealRotation::bias_targets()
#endif
}
void HealRotation::validate_hot()
{
if (!m_hot_target) {
m_hot_active = false;
return;
}
if (!IsTargetInPool(m_hot_target)) {
m_hot_target = nullptr;
m_hot_active = false;
}
}
bool IsHealRotationMemberClass(uint8 class_id)
{
switch (class_id) {

View File

@ -35,8 +35,11 @@
#define HEALING_STATS_RESET_INTERVAL 60000
#define HEALING_STATS_RESET_INTERVAL_S 60
#define SAFE_HP_RATIO_ABS 100.0f
#define SAFE_HP_RATIO_BASE 95.0f
#define CRITICAL_HP_RATIO_BASE 10.0f
#define CRITICAL_HP_RATIO_ABS 0.0f
#define CRITICAL_HP_RATIO_BASE 30.0f
struct HealingStats
{
@ -49,7 +52,7 @@ class HealRotation
{
public:
HealRotation(Bot* hr_creator, uint32 interval_ms = CASTING_CYCLE_DEFAULT_INTERVAL, bool fast_heals = false, bool adaptive_targeting = false, bool casting_override = false);
HealRotation(HealRotation* allocator_shunt) {};
HealRotation(HealRotation* allocator_shunt) {}; // use should be limited to the shared_ptr<HealRotation> memory allocation call
void SetIntervalMS(uint32 interval_ms);
void SetIntervalS(uint32 interval_s);
@ -72,10 +75,15 @@ public:
bool ClearMemberPool();
bool ClearTargetPool();
Mob* HOTTarget() { return m_hot_target; }
bool SetHOTTarget(Mob* hot_target);
bool ClearHOTTarget();
bool Start();
bool Stop();
bool IsActive() { return m_is_active; }
bool IsHOTActive() { return m_hot_active; }
bool CastingReady() { return (Timer::GetCurrentTime() >= m_next_cast_time_ms); }
Bot* CastingMember();
bool PokeCastingTarget();
@ -88,6 +96,7 @@ public:
bool IsMemberInPool(Bot* hr_member);
bool IsTargetInPool(Mob* hr_target);
bool IsHOTTarget(Mob* hot_target);
void SetMemberIsCasting(Bot* hr_member, bool flag = true);
bool MemberIsCasting(Bot* hr_member);
@ -114,6 +123,7 @@ private:
void cycle_refresh();
bool healable_target(bool use_class_at = true, bool critical_only = false);
void bias_targets();
void validate_hot();
uint32 m_creation_time_ms;
uint32 m_last_heal_time_ms;
@ -131,6 +141,9 @@ private:
bool m_consumed;
Mob* m_hot_target;
bool m_hot_active;
std::list<Bot*> m_member_pool;
std::list<Bot*> m_cycle_pool;
std::list<Mob*> m_target_pool;