mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-26 11:01:28 +00:00
[Bots] Add Data Bucket support to Bot Spell Entries. (#2505)
* [Bots] Add Data Bucket support to Bot Spell Entries. * Cleanup Formatting and Functions * Consolidated "CheckDataBucket" Functions * Remove unneeded CastToClient * Add choice to format data buckets as either "character-id" or "bot-id" to Bot spells * Fix Formatting * Clean up. * Update npc.h * Fix Bot Casting issues * Formatting Co-authored-by: Kinglykrab <kinglykrab@gmail.com> Co-authored-by: Kinglykrab <89047260+Kinglykrab@users.noreply.github.com>
This commit is contained in:
parent
de63eaa4b2
commit
7e7358e9b6
@ -567,7 +567,7 @@ enum ReloadWorld : uint8 {
|
|||||||
ForceRepop
|
ForceRepop
|
||||||
};
|
};
|
||||||
|
|
||||||
enum MerchantBucketComparison : uint8 {
|
enum BucketComparison : uint8 {
|
||||||
BucketEqualTo = 0,
|
BucketEqualTo = 0,
|
||||||
BucketNotEqualTo,
|
BucketNotEqualTo,
|
||||||
BucketGreaterThanOrEqualTo,
|
BucketGreaterThanOrEqualTo,
|
||||||
|
|||||||
@ -37,7 +37,7 @@
|
|||||||
#define CURRENT_BINARY_DATABASE_VERSION 9212
|
#define CURRENT_BINARY_DATABASE_VERSION 9212
|
||||||
|
|
||||||
#ifdef BOTS
|
#ifdef BOTS
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9030
|
||||||
#else
|
#else
|
||||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@ -28,6 +28,7 @@
|
|||||||
9027|2020_03_30_bots_view_update.sql|SELECT * FROM db_version WHERE bots_version >= 9027|empty|
|
9027|2020_03_30_bots_view_update.sql|SELECT * FROM db_version WHERE bots_version >= 9027|empty|
|
||||||
9028|2021_06_04_bot_create_combinations.sql|SHOW TABLES LIKE 'bot_create_combinations'|empty|
|
9028|2021_06_04_bot_create_combinations.sql|SHOW TABLES LIKE 'bot_create_combinations'|empty|
|
||||||
9029|2022_06_21_bot_groups_auto_spawn.sql|SHOW COLUMNS from `bot_groups` LIKE 'auto_spawm'|empty|
|
9029|2022_06_21_bot_groups_auto_spawn.sql|SHOW COLUMNS from `bot_groups` LIKE 'auto_spawm'|empty|
|
||||||
|
9030|2022_10_27_bot_data_buckets.sql|SHOW COLUMNS FROM `bot_spells_entries` LIKE 'bucket_name'|empty|
|
||||||
|
|
||||||
# Upgrade conditions:
|
# Upgrade conditions:
|
||||||
# This won't be needed after this system is implemented, but it is used database that are not
|
# This won't be needed after this system is implemented, but it is used database that are not
|
||||||
|
|||||||
@ -0,0 +1,4 @@
|
|||||||
|
ALTER TABLE `bot_spells_entries`
|
||||||
|
ADD COLUMN `bucket_name` varchar(100) NOT NULL DEFAULT '' AFTER `max_hp`,
|
||||||
|
ADD COLUMN `bucket_value` varchar(100) NOT NULL DEFAULT '' AFTER `bucket_name`,
|
||||||
|
ADD COLUMN `bucket_comparison` tinyint UNSIGNED NULL DEFAULT 0 AFTER `bucket_value`;
|
||||||
77
zone/bot.cpp
77
zone/bot.cpp
@ -2188,7 +2188,6 @@ void Bot::AI_Bot_Start(uint32 iMoveDelay) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (NPCTypedata) {
|
if (NPCTypedata) {
|
||||||
AI_AddBotSpells(NPCTypedata->npc_spells_id);
|
|
||||||
ProcessSpecialAbilities(NPCTypedata->special_abilities);
|
ProcessSpecialAbilities(NPCTypedata->special_abilities);
|
||||||
AI_AddNPCSpellsEffects(NPCTypedata->npc_spells_effects_id);
|
AI_AddNPCSpellsEffects(NPCTypedata->npc_spells_effects_id);
|
||||||
}
|
}
|
||||||
@ -9235,6 +9234,8 @@ void Bot::CalcBotStats(bool showtext) {
|
|||||||
|
|
||||||
CalcBonuses();
|
CalcBonuses();
|
||||||
|
|
||||||
|
GetBotOwnerDataBuckets();
|
||||||
|
GetBotDataBuckets();
|
||||||
AI_AddBotSpells(GetBotSpellID());
|
AI_AddBotSpells(GetBotSpellID());
|
||||||
|
|
||||||
if(showtext) {
|
if(showtext) {
|
||||||
@ -10322,6 +10323,80 @@ void Bot::SpawnBotGroupByName(Client* c, std::string botgroup_name, uint32 leade
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Bot::GetBotOwnerDataBuckets()
|
||||||
|
{
|
||||||
|
auto bot_owner = GetBotOwner();
|
||||||
|
if (!bot_owner) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto query = fmt::format(
|
||||||
|
"SELECT `key`, `value` FROM data_buckets WHERE `key` LIKE '{}-%'",
|
||||||
|
Strings::Escape(bot_owner->GetBucketKey())
|
||||||
|
);
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
|
||||||
|
if (!results.Success() || !results.RowCount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto row : results) {
|
||||||
|
bot_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bot::GetBotDataBuckets()
|
||||||
|
{
|
||||||
|
auto query = fmt::format(
|
||||||
|
"SELECT `key`, `value` FROM data_buckets WHERE `key` LIKE '{}-%'",
|
||||||
|
Strings::Escape(GetBucketKey())
|
||||||
|
);
|
||||||
|
auto results = database.QueryDatabase(query);
|
||||||
|
|
||||||
|
if (!results.Success() || !results.RowCount()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto row : results) {
|
||||||
|
bot_data_buckets.insert(std::pair<std::string,std::string>(row[0], row[1]));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Bot::CheckDataBucket(std::string bucket_name, std::string bucket_value, uint8 bucket_comparison)
|
||||||
|
{
|
||||||
|
if (!bucket_name.empty() && !bucket_value.empty()) {
|
||||||
|
auto full_name = fmt::format(
|
||||||
|
"{}-{}",
|
||||||
|
GetBucketKey(),
|
||||||
|
bucket_name
|
||||||
|
);
|
||||||
|
|
||||||
|
auto player_value = bot_data_buckets[full_name];
|
||||||
|
if (player_value.empty() && GetBotOwner()) {
|
||||||
|
full_name = fmt::format(
|
||||||
|
"{}-{}",
|
||||||
|
GetBotOwner()->GetBucketKey(),
|
||||||
|
bucket_name
|
||||||
|
);
|
||||||
|
|
||||||
|
player_value = bot_data_buckets[full_name];
|
||||||
|
if (player_value.empty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zone->CheckDataBucket(bucket_comparison, bucket_value, player_value)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
19
zone/bot.h
19
zone/bot.h
@ -308,7 +308,19 @@ public:
|
|||||||
void DoEnduranceUpkeep(); //does the endurance upkeep
|
void DoEnduranceUpkeep(); //does the endurance upkeep
|
||||||
|
|
||||||
bool AI_AddBotSpells(uint32 iDBSpellsID);
|
bool AI_AddBotSpells(uint32 iDBSpellsID);
|
||||||
void AddSpellToBotList(int16 iPriority, uint16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, int8 min_hp, int8 max_hp);
|
void AddSpellToBotList(
|
||||||
|
int16 iPriority,
|
||||||
|
uint16 iSpellID,
|
||||||
|
uint32 iType,
|
||||||
|
int16 iManaCost,
|
||||||
|
int32 iRecastDelay,
|
||||||
|
int16 iResistAdjust,
|
||||||
|
int8 min_hp,
|
||||||
|
int8 max_hp,
|
||||||
|
std::string bucket_name,
|
||||||
|
std::string bucket_value,
|
||||||
|
uint8 bucket_comparison
|
||||||
|
);
|
||||||
void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot);
|
void AI_Bot_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot);
|
||||||
// AI Methods
|
// AI Methods
|
||||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes);
|
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes);
|
||||||
@ -349,6 +361,10 @@ public:
|
|||||||
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot);
|
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQ::spells::CastingSlot slot);
|
||||||
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
|
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQ::spells::CastingSlot slot = EQ::spells::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
|
||||||
|
|
||||||
|
bool GetBotOwnerDataBuckets();
|
||||||
|
bool GetBotDataBuckets();
|
||||||
|
bool CheckDataBucket(std::string bucket_name, std::string bucket_value, uint8 bucket_comparison);
|
||||||
|
|
||||||
// Bot Equipment & Inventory Class Methods
|
// Bot Equipment & Inventory Class Methods
|
||||||
void BotTradeAddItem(const EQ::ItemInstance* inst, uint16 slot_id, std::string* error_message, bool save_to_database = true);
|
void BotTradeAddItem(const EQ::ItemInstance* inst, uint16 slot_id, std::string* error_message, bool save_to_database = true);
|
||||||
void EquipBot(std::string* error_message);
|
void EquipBot(std::string* error_message);
|
||||||
@ -692,6 +708,7 @@ private:
|
|||||||
eStandingPetOrder m_previous_pet_order;
|
eStandingPetOrder m_previous_pet_order;
|
||||||
|
|
||||||
BotCastingRoles m_CastingRoles;
|
BotCastingRoles m_CastingRoles;
|
||||||
|
std::map<std::string,std::string> bot_data_buckets;
|
||||||
|
|
||||||
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
|
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
|
||||||
|
|
||||||
|
|||||||
@ -804,8 +804,9 @@ bool BotDatabase::SaveBuffs(Bot* bot_inst)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (int buff_index = 0; buff_index < BUFF_COUNT; ++buff_index) {
|
for (int buff_index = 0; buff_index < BUFF_COUNT; ++buff_index) {
|
||||||
if (bot_buffs[buff_index].spellid <= 0 || bot_buffs[buff_index].spellid == SPELL_UNKNOWN)
|
if (!IsValidSpell(bot_buffs[buff_index].spellid)) {
|
||||||
continue;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
query = StringFormat(
|
query = StringFormat(
|
||||||
"INSERT INTO `bot_buffs` ("
|
"INSERT INTO `bot_buffs` ("
|
||||||
|
|||||||
@ -19,6 +19,7 @@
|
|||||||
#ifdef BOTS
|
#ifdef BOTS
|
||||||
|
|
||||||
#include "bot.h"
|
#include "bot.h"
|
||||||
|
#include "../common/data_verification.h"
|
||||||
#include "../common/strings.h"
|
#include "../common/strings.h"
|
||||||
|
|
||||||
#if EQDEBUG >= 12
|
#if EQDEBUG >= 12
|
||||||
@ -1604,11 +1605,16 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
|
|||||||
std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect) {
|
std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect) {
|
||||||
std::list<BotSpell> result;
|
std::list<BotSpell> result;
|
||||||
|
|
||||||
|
auto bot_owner = botCaster->GetBotOwner();
|
||||||
|
if (!bot_owner) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (botCaster && botCaster->AI_HasSpells()) {
|
if (botCaster && botCaster->AI_HasSpells()) {
|
||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -1631,11 +1637,16 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEff
|
|||||||
std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType) {
|
std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType) {
|
||||||
std::list<BotSpell> result;
|
std::list<BotSpell> result;
|
||||||
|
|
||||||
|
auto bot_owner = botCaster->GetBotOwner();
|
||||||
|
if (!bot_owner) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (botCaster && botCaster->AI_HasSpells()) {
|
if (botCaster && botCaster->AI_HasSpells()) {
|
||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -1660,11 +1671,16 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster,
|
|||||||
std::list<BotSpell> Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType) {
|
std::list<BotSpell> Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType) {
|
||||||
std::list<BotSpell> result;
|
std::list<BotSpell> result;
|
||||||
|
|
||||||
|
auto bot_owner = botCaster->GetBotOwner();
|
||||||
|
if (!bot_owner) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
if (botCaster && botCaster->AI_HasSpells()) {
|
if (botCaster && botCaster->AI_HasSpells()) {
|
||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -1691,7 +1707,7 @@ std::list<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa
|
|||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -1708,8 +1724,13 @@ std::list<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (result.size() > 1)
|
if (result.size() > 1) {
|
||||||
result.sort([](BotSpell_wPriority& l, BotSpell_wPriority& r) { return l.Priority < r.Priority; });
|
result.sort(
|
||||||
|
[](BotSpell_wPriority& l, BotSpell_wPriority& r) {
|
||||||
|
return l.Priority < r.Priority;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -1726,7 +1747,7 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) {
|
|||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -1784,15 +1805,18 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) {
|
|||||||
for (std::list<BotSpell>::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); ++botSpellListItr) {
|
for (std::list<BotSpell>::iterator botSpellListItr = botHoTSpellList.begin(); botSpellListItr != botHoTSpellList.end(); ++botSpellListItr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||||
if (IsHealOverTimeSpell(botSpellListItr->SpellId)) {
|
if (IsHealOverTimeSpell(botSpellListItr->SpellId)) {
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(botSpellList[i].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if (
|
||||||
|
botSpellList[i].spellid == botSpellListItr->SpellId &&
|
||||||
|
(botSpellList[i].type & SpellType_Heal) &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
@ -1818,7 +1842,7 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) {
|
|||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -1828,7 +1852,6 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) {
|
|||||||
result.SpellId = botSpellList[i].spellid;
|
result.SpellId = botSpellList[i].spellid;
|
||||||
result.SpellIndex = i;
|
result.SpellIndex = i;
|
||||||
result.ManaCost = botSpellList[i].manacost;
|
result.ManaCost = botSpellList[i].manacost;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1853,7 +1876,6 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) {
|
|||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1874,11 +1896,16 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) {
|
|||||||
|
|
||||||
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||||
if((IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) || IsFastHealSpell(botSpellListItr->SpellId)) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if (
|
||||||
|
(
|
||||||
|
IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) ||
|
||||||
|
IsFastHealSpell(botSpellListItr->SpellId)
|
||||||
|
) &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1899,11 +1926,13 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) {
|
|||||||
|
|
||||||
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||||
if(IsRegularGroupHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if (
|
||||||
|
IsRegularGroupHealSpell(botSpellListItr->SpellId) &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1928,13 +1957,17 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) {
|
|||||||
if (IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) {
|
if (IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) {
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(botSpellList[i].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if (
|
||||||
|
botSpellList[i].spellid == botSpellListItr->SpellId &&
|
||||||
|
(botSpellList[i].type & SpellType_Heal) &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
@ -1961,11 +1994,13 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) {
|
|||||||
|
|
||||||
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||||
if(IsGroupCompleteHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if(
|
||||||
|
IsGroupCompleteHealSpell(botSpellListItr->SpellId) &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1986,7 +2021,10 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) {
|
|||||||
|
|
||||||
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
// Assuming all the spells have been loaded into this list by level and in descending order
|
||||||
if(IsMezSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if (
|
||||||
|
IsMezSpell(botSpellListItr->SpellId) &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
@ -2011,11 +2049,14 @@ BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) {
|
|||||||
|
|
||||||
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
// 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 && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if (
|
||||||
|
IsSlowSpell(botSpellListItr->SpellId) &&
|
||||||
|
spells[botSpellListItr->SpellId].resist_type == RESIST_MAGIC &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2036,7 +2077,11 @@ BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) {
|
|||||||
|
|
||||||
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
for (std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||||
// Assuming all the spells have been loaded into this list by level and in descending order
|
// 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 && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
if (
|
||||||
|
IsSlowSpell(botSpellListItr->SpellId) &&
|
||||||
|
spells[botSpellListItr->SpellId].resist_type == RESIST_DISEASE &&
|
||||||
|
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||||
|
) {
|
||||||
result.SpellId = botSpellListItr->SpellId;
|
result.SpellId = botSpellListItr->SpellId;
|
||||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||||
result.ManaCost = botSpellListItr->ManaCost;
|
result.ManaCost = botSpellListItr->ManaCost;
|
||||||
@ -2067,7 +2112,9 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell) {
|
|||||||
|
|
||||||
if (g) {
|
if (g) {
|
||||||
for (int counter = 0; counter < g->GroupCount(); counter++) {
|
for (int counter = 0; counter < g->GroupCount(); counter++) {
|
||||||
if(npc->IsOnHatelist(g->members[counter]) && g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) {
|
if (
|
||||||
|
npc->IsOnHatelist(g->members[counter]) &&
|
||||||
|
g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) {
|
||||||
result = npc;
|
result = npc;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -2335,7 +2382,7 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) {
|
|||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -2382,7 +2429,7 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) {
|
|||||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||||
|
|
||||||
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
for (int i = botSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (botSpellList[i].spellid <= 0 || botSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(botSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -2690,43 +2737,67 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
|||||||
// ok, this function should load the list, and the parent list then shove them into the struct and sort
|
// ok, this function should load the list, and the parent list then shove them into the struct and sort
|
||||||
npc_spells_id = iDBSpellsID;
|
npc_spells_id = iDBSpellsID;
|
||||||
AIBot_spells.clear();
|
AIBot_spells.clear();
|
||||||
if (iDBSpellsID == 0) {
|
if (!iDBSpellsID) {
|
||||||
AIautocastspell_timer->Disable();
|
AIautocastspell_timer->Disable();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DBbotspells_Struct* spell_list = content_db.GetBotSpells(iDBSpellsID);
|
|
||||||
|
auto* spell_list = content_db.GetBotSpells(iDBSpellsID);
|
||||||
if (!spell_list) {
|
if (!spell_list) {
|
||||||
AIautocastspell_timer->Disable();
|
AIautocastspell_timer->Disable();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
DBbotspells_Struct* parentlist = content_db.GetBotSpells(spell_list->parent_list);
|
|
||||||
|
|
||||||
std::string debug_msg = StringFormat("Loading NPCSpells onto %s: dbspellsid=%u, level=%u", GetName(), iDBSpellsID, GetLevel());
|
auto* parentlist = content_db.GetBotSpells(spell_list->parent_list);
|
||||||
|
|
||||||
|
auto debug_msg = fmt::format(
|
||||||
|
"Loading NPCSpells onto {}: dbspellsid={}, level={}",
|
||||||
|
GetName(),
|
||||||
|
iDBSpellsID,
|
||||||
|
GetLevel()
|
||||||
|
);
|
||||||
|
|
||||||
if (spell_list) {
|
if (spell_list) {
|
||||||
debug_msg.append(StringFormat(" (found, %u), parentlist=%u", spell_list->entries.size(), spell_list->parent_list));
|
debug_msg.append(
|
||||||
|
fmt::format(
|
||||||
|
" (found, {}), parentlist={}",
|
||||||
|
spell_list->entries.size(),
|
||||||
|
spell_list->parent_list
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
if (spell_list->parent_list) {
|
if (spell_list->parent_list) {
|
||||||
if (parentlist)
|
if (parentlist) {
|
||||||
debug_msg.append(StringFormat(" (found, %u)", parentlist->entries.size()));
|
debug_msg.append(
|
||||||
else
|
fmt::format(
|
||||||
|
" (found, {})",
|
||||||
|
parentlist->entries.size()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
debug_msg.append(" (not found)");
|
debug_msg.append(" (not found)");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
} else {
|
||||||
debug_msg.append(" (not found)");
|
debug_msg.append(" (not found)");
|
||||||
}
|
}
|
||||||
LogAIDetail("[{}]", debug_msg.c_str());
|
|
||||||
|
LogAIDetail("[{}]", debug_msg);
|
||||||
|
|
||||||
if (parentlist) {
|
if (parentlist) {
|
||||||
for (const auto &iter : parentlist->entries) {
|
for (const auto &iter : parentlist->entries) {
|
||||||
LogAI("([{}]) [{}]", iter.spellid, spells[iter.spellid].name);
|
LogAI("([{}]) [{}]", iter.spellid, spells[iter.spellid].name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogAIModerate("fin (parent list)");
|
LogAIModerate("fin (parent list)");
|
||||||
|
|
||||||
if (spell_list) {
|
if (spell_list) {
|
||||||
for (const auto &iter : spell_list->entries) {
|
for (const auto &iter : spell_list->entries) {
|
||||||
LogAIDetail("([{}]) [{}]", iter.spellid, spells[iter.spellid].name);
|
LogAIDetail("([{}]) [{}]", iter.spellid, spells[iter.spellid].name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LogAIModerate("fin (spell list)");
|
LogAIModerate("fin (spell list)");
|
||||||
|
|
||||||
uint16 attack_proc_spell = -1;
|
uint16 attack_proc_spell = -1;
|
||||||
@ -2768,14 +2839,21 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
|||||||
_idle_no_sp_recast_max = parentlist->idle_no_sp_recast_max;
|
_idle_no_sp_recast_max = parentlist->idle_no_sp_recast_max;
|
||||||
_idle_beneficial_chance = parentlist->idle_beneficial_chance;
|
_idle_beneficial_chance = parentlist->idle_beneficial_chance;
|
||||||
for (auto &e : parentlist->entries) {
|
for (auto &e : parentlist->entries) {
|
||||||
if (GetLevel() >= e.minlevel && GetLevel() <= e.maxlevel && e.spellid > 0) {
|
if (
|
||||||
if (!IsSpellInBotList(spell_list, e.spellid))
|
EQ::ValueWithin(GetLevel(), e.minlevel, e.maxlevel) &&
|
||||||
{
|
e.spellid &&
|
||||||
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp);
|
!IsSpellInBotList(spell_list, e.spellid)
|
||||||
}
|
) {
|
||||||
|
if (!e.bucket_name.empty() && !e.bucket_value.empty()) {
|
||||||
|
if (!CheckDataBucket(e.bucket_name, e.bucket_value, e.bucket_comparison)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp, e.bucket_name, e.bucket_value, e.bucket_comparison);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (spell_list->attack_proc >= 0) {
|
if (spell_list->attack_proc >= 0) {
|
||||||
attack_proc_spell = spell_list->attack_proc;
|
attack_proc_spell = spell_list->attack_proc;
|
||||||
proc_chance = spell_list->proc_chance;
|
proc_chance = spell_list->proc_chance;
|
||||||
@ -2792,10 +2870,20 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//If any casting variables are defined in the current list, ignore those in the parent list.
|
//If any casting variables are defined in the current list, ignore those in the parent list.
|
||||||
if (spell_list->fail_recast || spell_list->engaged_no_sp_recast_min || spell_list->engaged_no_sp_recast_max
|
if (
|
||||||
|| spell_list->engaged_beneficial_self_chance || spell_list->engaged_beneficial_other_chance || spell_list->engaged_detrimental_chance
|
spell_list->fail_recast ||
|
||||||
|| spell_list->pursue_no_sp_recast_min || spell_list->pursue_no_sp_recast_max || spell_list->pursue_detrimental_chance
|
spell_list->engaged_no_sp_recast_min ||
|
||||||
|| spell_list->idle_no_sp_recast_min || spell_list->idle_no_sp_recast_max || spell_list->idle_beneficial_chance) {
|
spell_list->engaged_no_sp_recast_max ||
|
||||||
|
spell_list->engaged_beneficial_self_chance ||
|
||||||
|
spell_list->engaged_beneficial_other_chance ||
|
||||||
|
spell_list->engaged_detrimental_chance ||
|
||||||
|
spell_list->pursue_no_sp_recast_min ||
|
||||||
|
spell_list->pursue_no_sp_recast_max ||
|
||||||
|
spell_list->pursue_detrimental_chance ||
|
||||||
|
spell_list->idle_no_sp_recast_min ||
|
||||||
|
spell_list->idle_no_sp_recast_max ||
|
||||||
|
spell_list->idle_beneficial_chance
|
||||||
|
) {
|
||||||
_fail_recast = spell_list->fail_recast;
|
_fail_recast = spell_list->fail_recast;
|
||||||
_engaged_no_sp_recast_min = spell_list->engaged_no_sp_recast_min;
|
_engaged_no_sp_recast_min = spell_list->engaged_no_sp_recast_min;
|
||||||
_engaged_no_sp_recast_max = spell_list->engaged_no_sp_recast_max;
|
_engaged_no_sp_recast_max = spell_list->engaged_no_sp_recast_max;
|
||||||
@ -2811,8 +2899,13 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (auto &e : spell_list->entries) {
|
for (auto &e : spell_list->entries) {
|
||||||
if (GetLevel() >= e.minlevel && GetLevel() <= e.maxlevel && e.spellid > 0) {
|
if (EQ::ValueWithin(GetLevel(), e.minlevel, e.maxlevel) && e.spellid) {
|
||||||
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp);
|
if (!e.bucket_name.empty() && !e.bucket_value.empty()) {
|
||||||
|
if (!CheckDataBucket(e.bucket_name, e.bucket_value, e.bucket_comparison)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp, e.bucket_name, e.bucket_value, e.bucket_comparison);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2860,14 +2953,20 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID) {
|
bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID) {
|
||||||
auto it = std::find_if(spell_list->entries.begin(), spell_list->entries.end(),
|
auto it = std::find_if(
|
||||||
[iSpellID](const DBbotspells_entries_Struct &a) { return a.spellid == iSpellID; });
|
spell_list->entries.begin(),
|
||||||
|
spell_list->entries.end(),
|
||||||
|
[iSpellID](const DBbotspells_entries_Struct &a) {
|
||||||
|
return a.spellid == iSpellID;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
return it != spell_list->entries.end();
|
return it != spell_list->entries.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
DBbotspells_Struct *ZoneDatabase::GetBotSpells(uint32 iDBSpellsID)
|
DBbotspells_Struct *ZoneDatabase::GetBotSpells(uint32 iDBSpellsID)
|
||||||
{
|
{
|
||||||
if (iDBSpellsID == 0) {
|
if (!iDBSpellsID) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2880,83 +2979,87 @@ DBbotspells_Struct *ZoneDatabase::GetBotSpells(uint32 iDBSpellsID)
|
|||||||
if (!Bot_Spells_LoadTried.count(iDBSpellsID)) { // no reason to ask the DB again if we have failed once already
|
if (!Bot_Spells_LoadTried.count(iDBSpellsID)) { // no reason to ask the DB again if we have failed once already
|
||||||
Bot_Spells_LoadTried.insert(iDBSpellsID);
|
Bot_Spells_LoadTried.insert(iDBSpellsID);
|
||||||
|
|
||||||
std::string query = StringFormat("SELECT id, parent_list, attack_proc, proc_chance, "
|
auto query = fmt::format(
|
||||||
|
"SELECT id, parent_list, attack_proc, proc_chance, "
|
||||||
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
||||||
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
||||||
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
||||||
"pursue_no_sp_recast_min, pursue_no_sp_recast_max, "
|
"pursue_no_sp_recast_min, pursue_no_sp_recast_max, "
|
||||||
"pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, "
|
"pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, "
|
||||||
"idle_b_chance FROM npc_spells WHERE id=%d",
|
"idle_b_chance FROM npc_spells WHERE id = {}",
|
||||||
iDBSpellsID);
|
iDBSpellsID
|
||||||
auto results = QueryDatabase(query);
|
);
|
||||||
if (!results.Success()) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (results.RowCount() != 1) {
|
auto results = QueryDatabase(query);
|
||||||
|
if (!results.Success() || !results.RowCount()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto row = results.begin();
|
auto row = results.begin();
|
||||||
DBbotspells_Struct spell_set;
|
DBbotspells_Struct spell_set;
|
||||||
|
|
||||||
spell_set.parent_list = atoi(row[1]);
|
spell_set.parent_list = std::stoul(row[1]);
|
||||||
spell_set.attack_proc = atoi(row[2]);
|
spell_set.attack_proc = static_cast<uint16>(std::stoul(row[2]));
|
||||||
spell_set.proc_chance = atoi(row[3]);
|
spell_set.proc_chance = static_cast<uint8>(std::stoul(row[3]));
|
||||||
spell_set.range_proc = atoi(row[4]);
|
spell_set.range_proc = static_cast<uint16>(std::stoul(row[4]));
|
||||||
spell_set.rproc_chance = atoi(row[5]);
|
spell_set.rproc_chance = static_cast<int16>(std::stoi(row[5]));
|
||||||
spell_set.defensive_proc = atoi(row[6]);
|
spell_set.defensive_proc = static_cast<uint16>(std::stoul(row[6]));
|
||||||
spell_set.dproc_chance = atoi(row[7]);
|
spell_set.dproc_chance = static_cast<int16>(std::stoi(row[7]));
|
||||||
spell_set.fail_recast = atoi(row[8]);
|
spell_set.fail_recast = std::stoul(row[8]);
|
||||||
spell_set.engaged_no_sp_recast_min = atoi(row[9]);
|
spell_set.engaged_no_sp_recast_min = std::stoul(row[9]);
|
||||||
spell_set.engaged_no_sp_recast_max = atoi(row[10]);
|
spell_set.engaged_no_sp_recast_max = std::stoul(row[10]);
|
||||||
spell_set.engaged_beneficial_self_chance = atoi(row[11]);
|
spell_set.engaged_beneficial_self_chance = static_cast<uint8>(std::stoul(row[11]));
|
||||||
spell_set.engaged_beneficial_other_chance = atoi(row[12]);
|
spell_set.engaged_beneficial_other_chance = static_cast<uint8>(std::stoul(row[12]));
|
||||||
spell_set.engaged_detrimental_chance = atoi(row[13]);
|
spell_set.engaged_detrimental_chance = static_cast<uint8>(std::stoul(row[13]));
|
||||||
spell_set.pursue_no_sp_recast_min = atoi(row[14]);
|
spell_set.pursue_no_sp_recast_min = std::stoul(row[14]);
|
||||||
spell_set.pursue_no_sp_recast_max = atoi(row[15]);
|
spell_set.pursue_no_sp_recast_max = std::stoul(row[15]);
|
||||||
spell_set.pursue_detrimental_chance = atoi(row[16]);
|
spell_set.pursue_detrimental_chance = static_cast<uint8>(std::stoul(row[16]));
|
||||||
spell_set.idle_no_sp_recast_min = atoi(row[17]);
|
spell_set.idle_no_sp_recast_min = std::stoul(row[17]);
|
||||||
spell_set.idle_no_sp_recast_max = atoi(row[18]);
|
spell_set.idle_no_sp_recast_max = std::stoul(row[18]);
|
||||||
spell_set.idle_beneficial_chance = atoi(row[19]);
|
spell_set.idle_beneficial_chance = static_cast<uint8>(std::stoul(row[19]));
|
||||||
|
|
||||||
// pulling fixed values from an auto-increment field is dangerous...
|
// pulling fixed values from an auto-increment field is dangerous...
|
||||||
query = StringFormat(
|
query = fmt::format(
|
||||||
"SELECT spellid, type, minlevel, maxlevel, "
|
"SELECT spellid, type, minlevel, maxlevel, "
|
||||||
"manacost, recast_delay, priority, min_hp, max_hp, resist_adjust "
|
"manacost, recast_delay, priority, min_hp, max_hp, resist_adjust, "
|
||||||
|
"bucket_name, bucket_value, bucket_comparison "
|
||||||
"FROM bot_spells_entries "
|
"FROM bot_spells_entries "
|
||||||
"WHERE npc_spells_id=%d ORDER BY minlevel",
|
"WHERE npc_spells_id = {} ORDER BY minlevel",
|
||||||
iDBSpellsID);
|
iDBSpellsID
|
||||||
|
);
|
||||||
results = QueryDatabase(query);
|
results = QueryDatabase(query);
|
||||||
|
if (!results.Success() || !results.RowCount()) {
|
||||||
if (!results.Success()) {
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
int entryIndex = 0;
|
for (auto row : results) {
|
||||||
for (row = results.begin(); row != results.end(); ++row, ++entryIndex) {
|
|
||||||
DBbotspells_entries_Struct entry;
|
DBbotspells_entries_Struct entry;
|
||||||
int spell_id = atoi(row[0]);
|
auto spell_id = std::stoi(row[0]);
|
||||||
entry.spellid = spell_id;
|
entry.spellid = spell_id;
|
||||||
entry.type = atoul(row[1]);
|
entry.type = std::stoul(row[1]);
|
||||||
entry.minlevel = atoi(row[2]);
|
entry.minlevel = static_cast<uint8>(std::stoul(row[2]));
|
||||||
entry.maxlevel = atoi(row[3]);
|
entry.maxlevel = static_cast<uint8>(std::stoul(row[3]));
|
||||||
entry.manacost = atoi(row[4]);
|
entry.manacost = static_cast<int16>(std::stoi(row[4]));
|
||||||
entry.recast_delay = atoi(row[5]);
|
entry.recast_delay = std::stoi(row[5]);
|
||||||
entry.priority = atoi(row[6]);
|
entry.priority = static_cast<int16>(std::stoi(row[6]));
|
||||||
entry.min_hp = atoi(row[7]);
|
entry.min_hp = static_cast<int8>(std::stoi(row[7]));
|
||||||
entry.max_hp = atoi(row[8]);
|
entry.max_hp = static_cast<int8>(std::stoi(row[8]));
|
||||||
|
entry.resist_adjust = static_cast<int16>(std::stoi(row[9]));
|
||||||
|
entry.bucket_name = row[10];
|
||||||
|
entry.bucket_value = row[11];
|
||||||
|
entry.bucket_comparison = static_cast<uint8>(std::stoul(row[12]));
|
||||||
|
|
||||||
// some spell types don't make much since to be priority 0, so fix that
|
// 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 (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0) {
|
||||||
entry.priority = 1;
|
entry.priority = 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (row[9]) {
|
if (row[9]) {
|
||||||
entry.resist_adjust = atoi(row[9]);
|
entry.resist_adjust = static_cast<int16>(std::stoi(row[9]));
|
||||||
}
|
} else if (IsValidSpell(spell_id)) {
|
||||||
else if (IsValidSpell(spell_id)) {
|
|
||||||
entry.resist_adjust = spells[spell_id].resist_difficulty;
|
entry.resist_adjust = spells[spell_id].resist_difficulty;
|
||||||
}
|
}
|
||||||
|
|
||||||
spell_set.entries.push_back(entry);
|
spell_set.entries.push_back(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2969,10 +3072,19 @@ DBbotspells_Struct *ZoneDatabase::GetBotSpells(uint32 iDBSpellsID)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// adds a spell to the list, taking into account priority and resorting list as needed.
|
// adds a spell to the list, taking into account priority and resorting list as needed.
|
||||||
void Bot::AddSpellToBotList(int16 iPriority, uint16 iSpellID, uint32 iType,
|
void Bot::AddSpellToBotList(
|
||||||
int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, int8 min_hp, int8 max_hp)
|
int16 iPriority,
|
||||||
{
|
uint16 iSpellID,
|
||||||
|
uint32 iType,
|
||||||
|
int16 iManaCost,
|
||||||
|
int32 iRecastDelay,
|
||||||
|
int16 iResistAdjust,
|
||||||
|
int8 min_hp,
|
||||||
|
int8 max_hp,
|
||||||
|
std::string bucket_name,
|
||||||
|
std::string bucket_value,
|
||||||
|
uint8 bucket_comparison
|
||||||
|
) {
|
||||||
if(!IsValidSpell(iSpellID)) {
|
if(!IsValidSpell(iSpellID)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -2989,6 +3101,9 @@ void Bot::AddSpellToBotList(int16 iPriority, uint16 iSpellID, uint32 iType,
|
|||||||
t.resist_adjust = iResistAdjust;
|
t.resist_adjust = iResistAdjust;
|
||||||
t.min_hp = min_hp;
|
t.min_hp = min_hp;
|
||||||
t.max_hp = max_hp;
|
t.max_hp = max_hp;
|
||||||
|
t.bucket_name = bucket_name;
|
||||||
|
t.bucket_value = bucket_value;
|
||||||
|
t.bucket_comparison = bucket_comparison;
|
||||||
|
|
||||||
AIBot_spells.push_back(t);
|
AIBot_spells.push_back(t);
|
||||||
|
|
||||||
|
|||||||
143
zone/client.cpp
143
zone/client.cpp
@ -11607,149 +11607,6 @@ void Client::SendReloadCommandMessages() {
|
|||||||
SendChatLineBreak();
|
SendChatLineBreak();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Client::CheckMerchantDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value)
|
|
||||||
{
|
|
||||||
std::vector<std::string> bucket_checks;
|
|
||||||
bool found = false;
|
|
||||||
bool passes = false;
|
|
||||||
|
|
||||||
switch (bucket_comparison) {
|
|
||||||
case MerchantBucketComparison::BucketEqualTo:
|
|
||||||
{
|
|
||||||
if (player_value != bucket_value) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketNotEqualTo:
|
|
||||||
{
|
|
||||||
if (player_value == bucket_value) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketGreaterThanOrEqualTo:
|
|
||||||
{
|
|
||||||
if (player_value < bucket_value) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketLesserThanOrEqualTo:
|
|
||||||
{
|
|
||||||
if (player_value > bucket_value) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketGreaterThan:
|
|
||||||
{
|
|
||||||
if (player_value <= bucket_value) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketLesserThan:
|
|
||||||
{
|
|
||||||
if (player_value >= bucket_value) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketIsAny:
|
|
||||||
{
|
|
||||||
bucket_checks = Strings::Split(bucket_value, "|");
|
|
||||||
if (bucket_checks.empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &bucket : bucket_checks) {
|
|
||||||
if (player_value == bucket) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!found) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketIsNotAny:
|
|
||||||
{
|
|
||||||
bucket_checks = Strings::Split(bucket_value, "|");
|
|
||||||
if (bucket_checks.empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto &bucket : bucket_checks) {
|
|
||||||
if (player_value == bucket) {
|
|
||||||
found = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (found) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketIsBetween:
|
|
||||||
{
|
|
||||||
bucket_checks = Strings::Split(bucket_value, "|");
|
|
||||||
if (bucket_checks.empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
std::stoll(player_value) < std::stoll(bucket_checks[0]) ||
|
|
||||||
std::stoll(player_value) > std::stoll(bucket_checks[1])
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case MerchantBucketComparison::BucketIsNotBetween:
|
|
||||||
{
|
|
||||||
bucket_checks = Strings::Split(bucket_value, "|");
|
|
||||||
if (bucket_checks.empty()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (
|
|
||||||
std::stoll(player_value) >= std::stoll(bucket_checks[0]) &&
|
|
||||||
std::stoll(player_value) <= std::stoll(bucket_checks[1])
|
|
||||||
) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
passes = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return passes;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<std::string,std::string> Client::GetMerchantDataBuckets()
|
std::map<std::string,std::string> Client::GetMerchantDataBuckets()
|
||||||
{
|
{
|
||||||
std::map<std::string,std::string> merchant_data_buckets;
|
std::map<std::string,std::string> merchant_data_buckets;
|
||||||
|
|||||||
@ -1633,7 +1633,6 @@ public:
|
|||||||
Timer m_list_task_timers_rate_limit = {};
|
Timer m_list_task_timers_rate_limit = {};
|
||||||
|
|
||||||
std::map<std::string,std::string> GetMerchantDataBuckets();
|
std::map<std::string,std::string> GetMerchantDataBuckets();
|
||||||
bool CheckMerchantDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
friend class Mob;
|
friend class Mob;
|
||||||
|
|||||||
@ -855,7 +855,7 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!CheckMerchantDataBucket(ml.bucket_comparison, bucket_value, player_value)) {
|
if (!zone->CheckDataBucket(ml.bucket_comparison, bucket_value, player_value)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2904,7 +2904,7 @@ std::list<MercSpell> Merc::GetMercSpellsBySpellType(Merc* caster, uint32 spellTy
|
|||||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||||
|
|
||||||
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(mercSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -2941,7 +2941,7 @@ MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, uint32 spellType) {
|
|||||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||||
|
|
||||||
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(mercSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -2979,7 +2979,7 @@ MercSpell Merc::GetMercSpellBySpellID(Merc* caster, uint16 spellid) {
|
|||||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||||
|
|
||||||
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(mercSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -3009,7 +3009,7 @@ std::list<MercSpell> Merc::GetMercSpellsForSpellEffect(Merc* caster, int spellEf
|
|||||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||||
|
|
||||||
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(mercSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
@ -3039,7 +3039,7 @@ std::list<MercSpell> Merc::GetMercSpellsForSpellEffectAndTargetType(Merc* caster
|
|||||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||||
|
|
||||||
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
for (int i = mercSpellList.size() - 1; i >= 0; i--) {
|
||||||
if (mercSpellList[i].spellid <= 0 || mercSpellList[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(mercSpellList[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
continue;
|
continue;
|
||||||
|
|||||||
@ -6867,6 +6867,10 @@ std::string Mob::GetBucketKey() {
|
|||||||
return fmt::format("character-{}", CastToClient()->CharacterID());
|
return fmt::format("character-{}", CastToClient()->CharacterID());
|
||||||
} else if (IsNPC()) {
|
} else if (IsNPC()) {
|
||||||
return fmt::format("npc-{}", GetNPCTypeID());
|
return fmt::format("npc-{}", GetNPCTypeID());
|
||||||
|
#ifdef BOTS
|
||||||
|
} else if (IsBot()) {
|
||||||
|
return fmt::format("bot-{}", CastToBot()->GetBotID());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -84,7 +84,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates
|
|||||||
|
|
||||||
float manaR = GetManaRatio();
|
float manaR = GetManaRatio();
|
||||||
for (int i = static_cast<int>(AIspells.size()) - 1; i >= 0; i--) {
|
for (int i = static_cast<int>(AIspells.size()) - 1; i >= 0; i--) {
|
||||||
if (AIspells[i].spellid <= 0 || AIspells[i].spellid >= SPDAT_RECORDS) {
|
if (!IsValidSpell(AIspells[i].spellid)) {
|
||||||
// this is both to quit early to save cpu and to avoid casting bad spells
|
// 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
|
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||||
//return false;
|
//return false;
|
||||||
|
|||||||
@ -69,6 +69,9 @@ struct BotSpells_Struct {
|
|||||||
int16 resist_adjust;
|
int16 resist_adjust;
|
||||||
int16 min_hp; // >0 won't cast if HP is below
|
int16 min_hp; // >0 won't cast if HP is below
|
||||||
int16 max_hp; // >0 won't cast if HP is above
|
int16 max_hp; // >0 won't cast if HP is above
|
||||||
|
std::string bucket_name;
|
||||||
|
std::string bucket_value;
|
||||||
|
uint8 bucket_comparison;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct AISpellsEffects_Struct {
|
struct AISpellsEffects_Struct {
|
||||||
|
|||||||
175
zone/zone.cpp
Executable file → Normal file
175
zone/zone.cpp
Executable file → Normal file
@ -2976,3 +2976,178 @@ std::string Zone::GetAAName(int aa_id)
|
|||||||
|
|
||||||
return std::string();
|
return std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Zone::CheckDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value)
|
||||||
|
{
|
||||||
|
std::vector<std::string> bucket_checks;
|
||||||
|
bool found = false;
|
||||||
|
bool passes = false;
|
||||||
|
|
||||||
|
switch (bucket_comparison) {
|
||||||
|
case BucketComparison::BucketEqualTo:
|
||||||
|
{
|
||||||
|
if (player_value != bucket_value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketNotEqualTo:
|
||||||
|
{
|
||||||
|
if (player_value == bucket_value) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketGreaterThanOrEqualTo:
|
||||||
|
{
|
||||||
|
if (!Strings::IsNumber(player_value) || !Strings::IsNumber(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::stoll(player_value) < std::stoll(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketLesserThanOrEqualTo:
|
||||||
|
{
|
||||||
|
if (!Strings::IsNumber(player_value) || !Strings::IsNumber(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::stoll(player_value) > std::stoll(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketGreaterThan:
|
||||||
|
{
|
||||||
|
if (!Strings::IsNumber(player_value) || !Strings::IsNumber(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::stoll(player_value) <= std::stoll(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketLesserThan:
|
||||||
|
{
|
||||||
|
if (!Strings::IsNumber(player_value) || !Strings::IsNumber(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::stoll(player_value) >= std::stoll(bucket_value)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketIsAny:
|
||||||
|
{
|
||||||
|
bucket_checks = Strings::Split(bucket_value, "|");
|
||||||
|
if (bucket_checks.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::find(bucket_checks.begin(), bucket_checks.end(), player_value) != bucket_checks.end()) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketIsNotAny:
|
||||||
|
{
|
||||||
|
bucket_checks = Strings::Split(bucket_value, "|");
|
||||||
|
if (bucket_checks.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::find(bucket_checks.begin(), bucket_checks.end(), player_value) != bucket_checks.end()) {
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (found) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketIsBetween:
|
||||||
|
{
|
||||||
|
bucket_checks = Strings::Split(bucket_value, "|");
|
||||||
|
if (bucket_checks.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Strings::IsNumber(player_value) ||
|
||||||
|
!Strings::IsNumber(bucket_checks[0]) ||
|
||||||
|
!Strings::IsNumber(bucket_checks[1])
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!EQ::ValueWithin(
|
||||||
|
std::stoll(player_value),
|
||||||
|
std::stoll(bucket_checks[0]),
|
||||||
|
std::stoll(bucket_checks[1])
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case BucketComparison::BucketIsNotBetween:
|
||||||
|
{
|
||||||
|
bucket_checks = Strings::Split(bucket_value, "|");
|
||||||
|
if (bucket_checks.empty()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!Strings::IsNumber(player_value) ||
|
||||||
|
!Strings::IsNumber(bucket_checks[0]) ||
|
||||||
|
!Strings::IsNumber(bucket_checks[1])
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
EQ::ValueWithin(
|
||||||
|
std::stoll(player_value),
|
||||||
|
std::stoll(bucket_checks[0]),
|
||||||
|
std::stoll(bucket_checks[1])
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
passes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return passes;
|
||||||
|
}
|
||||||
|
|||||||
@ -311,6 +311,8 @@ public:
|
|||||||
bool IsQuestHotReloadQueued() const;
|
bool IsQuestHotReloadQueued() const;
|
||||||
void SetQuestHotReloadQueued(bool in_quest_hot_reload_queued);
|
void SetQuestHotReloadQueued(bool in_quest_hot_reload_queued);
|
||||||
|
|
||||||
|
bool CheckDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value);
|
||||||
|
|
||||||
WaterMap *watermap;
|
WaterMap *watermap;
|
||||||
ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f);
|
ZonePoint *GetClosestZonePoint(const glm::vec3 &location, uint32 to, Client *client, float max_distance = 40000.0f);
|
||||||
ZonePoint *GetClosestZonePointWithoutZone(float x, float y, float z, Client *client, float max_distance = 40000.0f);
|
ZonePoint *GetClosestZonePointWithoutZone(float x, float y, float z, Client *client, float max_distance = 40000.0f);
|
||||||
|
|||||||
@ -119,6 +119,9 @@ struct DBbotspells_entries_Struct {
|
|||||||
int16 resist_adjust;
|
int16 resist_adjust;
|
||||||
int8 min_hp;
|
int8 min_hp;
|
||||||
int8 max_hp;
|
int8 max_hp;
|
||||||
|
std::string bucket_name;
|
||||||
|
std::string bucket_value;
|
||||||
|
uint8 bucket_comparison;
|
||||||
};
|
};
|
||||||
#pragma pack()
|
#pragma pack()
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user