mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-11 16:51:29 +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
|
||||
};
|
||||
|
||||
enum MerchantBucketComparison : uint8 {
|
||||
enum BucketComparison : uint8 {
|
||||
BucketEqualTo = 0,
|
||||
BucketNotEqualTo,
|
||||
BucketGreaterThanOrEqualTo,
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9212
|
||||
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9029
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9030
|
||||
#else
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||
#endif
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
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|
|
||||
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:
|
||||
# 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`;
|
||||
97
zone/bot.cpp
97
zone/bot.cpp
@ -2188,7 +2188,6 @@ void Bot::AI_Bot_Start(uint32 iMoveDelay) {
|
||||
}
|
||||
|
||||
if (NPCTypedata) {
|
||||
AI_AddBotSpells(NPCTypedata->npc_spells_id);
|
||||
ProcessSpecialAbilities(NPCTypedata->special_abilities);
|
||||
AI_AddNPCSpellsEffects(NPCTypedata->npc_spells_effects_id);
|
||||
}
|
||||
@ -3345,8 +3344,8 @@ void Bot::AI_Process()
|
||||
|
||||
TEST_COMBATANTS();
|
||||
auto ExtraAttackChanceBonus =
|
||||
(spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] +
|
||||
aabonuses.ExtraAttackChance[0]);
|
||||
(spellbonuses.ExtraAttackChance[0] + itembonuses.ExtraAttackChance[0] +
|
||||
aabonuses.ExtraAttackChance[0]);
|
||||
if (ExtraAttackChanceBonus) {
|
||||
|
||||
if (p_item && p_item->GetItem()->IsType2HWeapon()) {
|
||||
@ -3951,14 +3950,14 @@ bool Bot::Spawn(Client* botCharacterOwner) {
|
||||
void Bot::RemoveBotItemBySlot(uint16 slot_id, std::string *error_message)
|
||||
{
|
||||
if (!GetBotID()) {
|
||||
return;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!database.botdb.DeleteItemBySlot(GetBotID(), slot_id)) {
|
||||
*error_message = BotDatabase::fail::DeleteItemBySlot();
|
||||
if (!database.botdb.DeleteItemBySlot(GetBotID(), slot_id)) {
|
||||
*error_message = BotDatabase::fail::DeleteItemBySlot();
|
||||
}
|
||||
|
||||
m_inv.DeleteItem(slot_id);
|
||||
m_inv.DeleteItem(slot_id);
|
||||
UpdateEquipmentLight();
|
||||
}
|
||||
|
||||
@ -3982,7 +3981,7 @@ uint32 Bot::GetBotItemBySlot(uint16 slot_id)
|
||||
{
|
||||
uint32 item_id = 0;
|
||||
if (!GetBotID()) {
|
||||
return item_id;
|
||||
return item_id;
|
||||
}
|
||||
|
||||
if (!database.botdb.LoadItemBySlot(GetBotID(), slot_id, item_id)) {
|
||||
@ -7031,11 +7030,11 @@ int64 Bot::GetActSpellDamage(uint16 spell_id, int64 value, Mob* target) {
|
||||
//Crtical Hit Calculation pathway
|
||||
if (chance > 0 || (GetClass() == WIZARD && GetLevel() >= RuleI(Spells, WizCritLevel))) {
|
||||
|
||||
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
|
||||
int32 ratio = RuleI(Spells, BaseCritRatio); //Critical modifier is applied from spell effects only. Keep at 100 for live like criticals.
|
||||
|
||||
//Improved Harm Touch is a guaranteed crit if you have at least one level of SCF.
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH && (GetAA(aaSpellCastingFury) > 0) && (GetAA(aaUnholyTouch) > 0))
|
||||
chance = 100;
|
||||
chance = 100;
|
||||
|
||||
if (spells[spell_id].override_crit_chance > 0 && chance > spells[spell_id].override_crit_chance)
|
||||
chance = spells[spell_id].override_crit_chance;
|
||||
@ -7226,7 +7225,7 @@ int32 Bot::GetActSpellCasttime(uint16 spell_id, int32 casttime) {
|
||||
uint8 botlevel = GetLevel();
|
||||
uint8 botclass = GetClass();
|
||||
if (botlevel >= 51 && casttime >= 3000 && !spells[spell_id].good_effect &&
|
||||
(botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD)) {
|
||||
(botclass == SHADOWKNIGHT || botclass == RANGER || botclass == PALADIN || botclass == BEASTLORD)) {
|
||||
int level_mod = std::min(15, botlevel - 50);
|
||||
cast_reducer += level_mod * 3;
|
||||
}
|
||||
@ -9235,6 +9234,8 @@ void Bot::CalcBotStats(bool showtext) {
|
||||
|
||||
CalcBonuses();
|
||||
|
||||
GetBotOwnerDataBuckets();
|
||||
GetBotDataBuckets();
|
||||
AI_AddBotSpells(GetBotSpellID());
|
||||
|
||||
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 };
|
||||
|
||||
#endif
|
||||
|
||||
19
zone/bot.h
19
zone/bot.h
@ -308,7 +308,19 @@ public:
|
||||
void DoEnduranceUpkeep(); //does the endurance upkeep
|
||||
|
||||
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);
|
||||
// AI Methods
|
||||
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 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
|
||||
void BotTradeAddItem(const EQ::ItemInstance* inst, uint16 slot_id, std::string* error_message, bool save_to_database = true);
|
||||
void EquipBot(std::string* error_message);
|
||||
@ -692,6 +708,7 @@ private:
|
||||
eStandingPetOrder m_previous_pet_order;
|
||||
|
||||
BotCastingRoles m_CastingRoles;
|
||||
std::map<std::string,std::string> bot_data_buckets;
|
||||
|
||||
std::shared_ptr<HealRotation> m_member_of_heal_rotation;
|
||||
|
||||
|
||||
@ -804,8 +804,9 @@ bool BotDatabase::SaveBuffs(Bot* bot_inst)
|
||||
return false;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
query = StringFormat(
|
||||
"INSERT INTO `bot_buffs` ("
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
#ifdef BOTS
|
||||
|
||||
#include "bot.h"
|
||||
#include "../common/data_verification.h"
|
||||
#include "../common/strings.h"
|
||||
|
||||
#if EQDEBUG >= 12
|
||||
@ -1108,7 +1109,7 @@ bool Bot::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
|
||||
casting_spell_AIindex = i;
|
||||
LogAIDetail("Bot::AIDoSpellCast: spellid = [{}], tar = [{}], mana = [{}], Name: [{}]", AIBot_spells[i].spellid, tar->GetName(), mana_cost, spells[AIBot_spells[i].spellid].name);
|
||||
result = Mob::CastSpell(AIBot_spells[i].spellid, tar->GetID(), EQ::spells::CastingSlot::Gem2, spells[AIBot_spells[i].spellid].cast_time, AIBot_spells[i].manacost == -2 ? 0 : mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIBot_spells[i].resist_adjust));
|
||||
|
||||
|
||||
if(IsCasting() && IsSitting())
|
||||
Stand();
|
||||
}
|
||||
@ -1604,17 +1605,22 @@ bool Bot::AIHealRotation(Mob* tar, bool useFastHeals) {
|
||||
std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEffect) {
|
||||
std::list<BotSpell> result;
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
auto bot_owner = botCaster->GetBotOwner();
|
||||
if (!bot_owner) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
}
|
||||
|
||||
if(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) {
|
||||
if (IsEffectInSpell(botSpellList[i].spellid, spellEffect)) {
|
||||
BotSpell botSpell;
|
||||
botSpell.SpellId = botSpellList[i].spellid;
|
||||
botSpell.SpellIndex = i;
|
||||
@ -1631,18 +1637,23 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffect(Bot* botCaster, int spellEff
|
||||
std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster, int spellEffect, SpellTargetType targetType) {
|
||||
std::list<BotSpell> result;
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
auto bot_owner = botCaster->GetBotOwner();
|
||||
if (!bot_owner) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
}
|
||||
|
||||
if(IsEffectInSpell(botSpellList[i].spellid, spellEffect)) {
|
||||
if(spells[botSpellList[i].spellid].target_type == targetType) {
|
||||
if (IsEffectInSpell(botSpellList[i].spellid, spellEffect)) {
|
||||
if (spells[botSpellList[i].spellid].target_type == targetType) {
|
||||
BotSpell botSpell;
|
||||
botSpell.SpellId = botSpellList[i].spellid;
|
||||
botSpell.SpellIndex = i;
|
||||
@ -1660,17 +1671,22 @@ std::list<BotSpell> Bot::GetBotSpellsForSpellEffectAndTargetType(Bot* botCaster,
|
||||
std::list<BotSpell> Bot::GetBotSpellsBySpellType(Bot* botCaster, uint32 spellType) {
|
||||
std::list<BotSpell> result;
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
auto bot_owner = botCaster->GetBotOwner();
|
||||
if (!bot_owner) {
|
||||
return result;
|
||||
}
|
||||
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
}
|
||||
|
||||
if(botSpellList[i].type & spellType) {
|
||||
if (botSpellList[i].type & spellType) {
|
||||
BotSpell botSpell;
|
||||
botSpell.SpellId = botSpellList[i].spellid;
|
||||
botSpell.SpellIndex = i;
|
||||
@ -1691,7 +1707,7 @@ std::list<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
@ -1708,8 +1724,13 @@ std::list<BotSpell_wPriority> Bot::GetPrioritizedBotSpellsBySpellType(Bot* botCa
|
||||
}
|
||||
}
|
||||
|
||||
if (result.size() > 1)
|
||||
result.sort([](BotSpell_wPriority& l, BotSpell_wPriority& r) { return l.Priority < r.Priority; });
|
||||
if (result.size() > 1) {
|
||||
result.sort(
|
||||
[](BotSpell_wPriority& l, BotSpell_wPriority& r) {
|
||||
return l.Priority < r.Priority;
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
@ -1722,17 +1743,17 @@ BotSpell Bot::GetFirstBotSpellBySpellType(Bot* botCaster, uint32 spellType) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
}
|
||||
|
||||
if((botSpellList[i].type & spellType) && CheckSpellRecastTimers(botCaster, i)) {
|
||||
if ((botSpellList[i].type & spellType) && CheckSpellRecastTimers(botCaster, i)) {
|
||||
result.SpellId = botSpellList[i].spellid;
|
||||
result.SpellIndex = i;
|
||||
result.ManaCost = botSpellList[i].manacost;
|
||||
@ -1752,12 +1773,12 @@ BotSpell Bot::GetBestBotSpellForFastHeal(Bot *botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
||||
|
||||
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
|
||||
if(IsFastHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (IsFastHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
@ -1777,22 +1798,25 @@ BotSpell Bot::GetBestBotSpellForHealOverTime(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime);
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
if(IsHealOverTimeSpell(botSpellListItr->SpellId)) {
|
||||
|
||||
if (IsHealOverTimeSpell(botSpellListItr->SpellId)) {
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
}
|
||||
|
||||
if(botSpellList[i].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (
|
||||
botSpellList[i].spellid == botSpellListItr->SpellId &&
|
||||
(botSpellList[i].type & SpellType_Heal) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
@ -1814,21 +1838,20 @@ BotSpell Bot::GetBestBotSpellForPercentageHeal(Bot *botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
}
|
||||
|
||||
if(IsCompleteHealSpell(botSpellList[i].spellid) && CheckSpellRecastTimers(botCaster, i)) {
|
||||
if (IsCompleteHealSpell(botSpellList[i].spellid) && CheckSpellRecastTimers(botCaster, i)) {
|
||||
result.SpellId = botSpellList[i].spellid;
|
||||
result.SpellIndex = i;
|
||||
result.ManaCost = botSpellList[i].manacost;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1844,16 +1867,15 @@ BotSpell Bot::GetBestBotSpellForRegularSingleTargetHeal(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
||||
|
||||
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
|
||||
if(IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (IsRegularSingleTargetHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1869,16 +1891,21 @@ BotSpell Bot::GetFirstBotSpellForSingleTargetHeal(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
||||
|
||||
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
|
||||
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.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1894,16 +1921,18 @@ BotSpell Bot::GetBestBotSpellForGroupHeal(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CurrentHP);
|
||||
|
||||
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
|
||||
if(IsRegularGroupHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (
|
||||
IsRegularGroupHealSpell(botSpellListItr->SpellId) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1919,22 +1948,26 @@ BotSpell Bot::GetBestBotSpellForGroupHealOverTime(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botHoTSpellList = GetBotSpellsForSpellEffect(botCaster, SE_HealOverTime);
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
if(IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) {
|
||||
if (IsGroupHealOverTimeSpell(botSpellListItr->SpellId)) {
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
}
|
||||
|
||||
if(botSpellList[i].spellid == botSpellListItr->SpellId && (botSpellList[i].type & SpellType_Heal) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (
|
||||
botSpellList[i].spellid == botSpellListItr->SpellId &&
|
||||
(botSpellList[i].type & SpellType_Heal) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
@ -1956,16 +1989,18 @@ BotSpell Bot::GetBestBotSpellForGroupCompleteHeal(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_CompleteHeal);
|
||||
|
||||
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
|
||||
if(IsGroupCompleteHealSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if(
|
||||
IsGroupCompleteHealSpell(botSpellListItr->SpellId) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1981,12 +2016,15 @@ BotSpell Bot::GetBestBotSpellForMez(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_Mez);
|
||||
|
||||
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
|
||||
if(IsMezSpell(botSpellListItr->SpellId) && CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)) {
|
||||
if (
|
||||
IsMezSpell(botSpellListItr->SpellId) &&
|
||||
CheckSpellRecastTimers(botCaster, botSpellListItr->SpellIndex)
|
||||
) {
|
||||
result.SpellId = botSpellListItr->SpellId;
|
||||
result.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
@ -2006,16 +2044,19 @@ BotSpell Bot::GetBestBotSpellForMagicBasedSlow(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed);
|
||||
|
||||
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
|
||||
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.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2031,12 +2072,16 @@ BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_AttackSpeed);
|
||||
|
||||
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
|
||||
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.SpellIndex = botSpellListItr->SpellIndex;
|
||||
result.ManaCost = botSpellListItr->ManaCost;
|
||||
@ -2052,7 +2097,7 @@ BotSpell Bot::GetBestBotSpellForDiseaseBasedSlow(Bot* botCaster) {
|
||||
Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell) {
|
||||
Mob* result = 0;
|
||||
|
||||
if(botCaster && IsMezSpell(botSpell.SpellId)) {
|
||||
if (botCaster && IsMezSpell(botSpell.SpellId)) {
|
||||
|
||||
std::list<NPC*> npc_list;
|
||||
entity_list.GetNPCList(npc_list);
|
||||
@ -2060,14 +2105,16 @@ Mob* Bot::GetFirstIncomingMobToMez(Bot* botCaster, BotSpell botSpell) {
|
||||
for(std::list<NPC*>::iterator itr = npc_list.begin(); itr != npc_list.end(); ++itr) {
|
||||
NPC* npc = *itr;
|
||||
|
||||
if(DistanceSquaredNoZ(npc->GetPosition(), botCaster->GetPosition()) <= botCaster->GetActSpellRange(botSpell.SpellId, spells[botSpell.SpellId].range)) {
|
||||
if(!npc->IsMezzed()) {
|
||||
if(botCaster->HasGroup()) {
|
||||
if (DistanceSquaredNoZ(npc->GetPosition(), botCaster->GetPosition()) <= botCaster->GetActSpellRange(botSpell.SpellId, spells[botSpell.SpellId].range)) {
|
||||
if (!npc->IsMezzed()) {
|
||||
if (botCaster->HasGroup()) {
|
||||
Group* g = botCaster->GetGroup();
|
||||
|
||||
if(g) {
|
||||
for(int counter = 0; counter < g->GroupCount(); counter++) {
|
||||
if(npc->IsOnHatelist(g->members[counter]) && g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) {
|
||||
if (g) {
|
||||
for (int counter = 0; counter < g->GroupCount(); counter++) {
|
||||
if (
|
||||
npc->IsOnHatelist(g->members[counter]) &&
|
||||
g->members[counter]->GetTarget() != npc && g->members[counter]->IsEngaged()) {
|
||||
result = npc;
|
||||
break;
|
||||
}
|
||||
@ -2092,7 +2139,7 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) {
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffect(botCaster, SE_SummonPet);
|
||||
|
||||
std::string petType = GetBotMagicianPetType(botCaster);
|
||||
@ -2117,7 +2164,7 @@ BotSpell Bot::GetBestBotMagicianPetSpell(Bot *botCaster) {
|
||||
std::string Bot::GetBotMagicianPetType(Bot* botCaster) {
|
||||
std::string result;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
if(botCaster->IsPetChooser()) {
|
||||
switch(botCaster->GetPetChooserID()) {
|
||||
case 0:
|
||||
@ -2203,7 +2250,7 @@ BotSpell Bot::GetBestBotSpellForNukeByTargetType(Bot* botCaster, SpellTargetType
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster) {
|
||||
if (botCaster) {
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_CurrentHP, targetType);
|
||||
|
||||
for(std::list<BotSpell>::iterator botSpellListItr = botSpellList.begin(); botSpellListItr != botSpellList.end(); ++botSpellListItr) {
|
||||
@ -2229,7 +2276,7 @@ BotSpell Bot::GetBestBotSpellForStunByTargetType(Bot* botCaster, SpellTargetType
|
||||
result.SpellIndex = 0;
|
||||
result.ManaCost = 0;
|
||||
|
||||
if(botCaster)
|
||||
if (botCaster)
|
||||
{
|
||||
std::list<BotSpell> botSpellList = GetBotSpellsForSpellEffectAndTargetType(botCaster, SE_Stun, targetType);
|
||||
|
||||
@ -2331,11 +2378,11 @@ BotSpell Bot::GetDebuffBotSpell(Bot* botCaster, Mob *tar) {
|
||||
if(!tar || !botCaster)
|
||||
return result;
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
@ -2378,11 +2425,11 @@ BotSpell Bot::GetBestBotSpellForResistDebuff(Bot* botCaster, Mob *tar) {
|
||||
bool needsPoisonResistDebuff = (tar->GetPR() + level_mod) > 100 ? true: false;
|
||||
bool needsDiseaseResistDebuff = (tar->GetDR() + level_mod) > 100 ? true: false;
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::vector<BotSpells_Struct> botSpellList = botCaster->AIBot_spells;
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
@ -2426,7 +2473,7 @@ BotSpell Bot::GetBestBotSpellForCure(Bot* botCaster, Mob *tar) {
|
||||
bool isCursed = tar->FindType(SE_CurseCounter);
|
||||
bool isCorrupted = tar->FindType(SE_CorruptionCounter);
|
||||
|
||||
if(botCaster && botCaster->AI_HasSpells()) {
|
||||
if (botCaster && botCaster->AI_HasSpells()) {
|
||||
std::list<BotSpell_wPriority> cureList = GetPrioritizedBotSpellsBySpellType(botCaster, SpellType_Cure);
|
||||
|
||||
if(tar->HasGroup()) {
|
||||
@ -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
|
||||
npc_spells_id = iDBSpellsID;
|
||||
AIBot_spells.clear();
|
||||
if (iDBSpellsID == 0) {
|
||||
if (!iDBSpellsID) {
|
||||
AIautocastspell_timer->Disable();
|
||||
return false;
|
||||
}
|
||||
DBbotspells_Struct* spell_list = content_db.GetBotSpells(iDBSpellsID);
|
||||
|
||||
auto* spell_list = content_db.GetBotSpells(iDBSpellsID);
|
||||
if (!spell_list) {
|
||||
AIautocastspell_timer->Disable();
|
||||
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) {
|
||||
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 (parentlist)
|
||||
debug_msg.append(StringFormat(" (found, %u)", parentlist->entries.size()));
|
||||
else
|
||||
if (parentlist) {
|
||||
debug_msg.append(
|
||||
fmt::format(
|
||||
" (found, {})",
|
||||
parentlist->entries.size()
|
||||
)
|
||||
);
|
||||
} else {
|
||||
debug_msg.append(" (not found)");
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
debug_msg.append(" (not found)");
|
||||
}
|
||||
LogAIDetail("[{}]", debug_msg.c_str());
|
||||
|
||||
LogAIDetail("[{}]", debug_msg);
|
||||
|
||||
if (parentlist) {
|
||||
for (const auto &iter : parentlist->entries) {
|
||||
LogAI("([{}]) [{}]", iter.spellid, spells[iter.spellid].name);
|
||||
}
|
||||
}
|
||||
|
||||
LogAIModerate("fin (parent list)");
|
||||
|
||||
if (spell_list) {
|
||||
for (const auto &iter : spell_list->entries) {
|
||||
LogAIDetail("([{}]) [{}]", iter.spellid, spells[iter.spellid].name);
|
||||
}
|
||||
}
|
||||
|
||||
LogAIModerate("fin (spell list)");
|
||||
|
||||
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_beneficial_chance = parentlist->idle_beneficial_chance;
|
||||
for (auto &e : parentlist->entries) {
|
||||
if (GetLevel() >= e.minlevel && GetLevel() <= e.maxlevel && e.spellid > 0) {
|
||||
if (!IsSpellInBotList(spell_list, e.spellid))
|
||||
{
|
||||
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp);
|
||||
if (
|
||||
EQ::ValueWithin(GetLevel(), e.minlevel, e.maxlevel) &&
|
||||
e.spellid &&
|
||||
!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) {
|
||||
attack_proc_spell = spell_list->attack_proc;
|
||||
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 (spell_list->fail_recast || spell_list->engaged_no_sp_recast_min || 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) {
|
||||
if (
|
||||
spell_list->fail_recast ||
|
||||
spell_list->engaged_no_sp_recast_min ||
|
||||
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;
|
||||
_engaged_no_sp_recast_min = spell_list->engaged_no_sp_recast_min;
|
||||
_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) {
|
||||
if (GetLevel() >= e.minlevel && GetLevel() <= e.maxlevel && e.spellid > 0) {
|
||||
AddSpellToBotList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp);
|
||||
if (EQ::ValueWithin(GetLevel(), e.minlevel, e.maxlevel) && 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2823,7 +2916,7 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
||||
if (IsValidSpell(attack_proc_spell)) {
|
||||
AddProcToWeapon(attack_proc_spell, true, proc_chance);
|
||||
|
||||
if (RuleB(Spells, NPCInnateProcOverride)) {
|
||||
if (RuleB(Spells, NPCInnateProcOverride)) {
|
||||
innate_proc_spell_id = attack_proc_spell;
|
||||
}
|
||||
}
|
||||
@ -2860,14 +2953,20 @@ bool Bot::AI_AddBotSpells(uint32 iDBSpellsID) {
|
||||
}
|
||||
|
||||
bool IsSpellInBotList(DBbotspells_Struct* spell_list, uint16 iSpellID) {
|
||||
auto it = std::find_if(spell_list->entries.begin(), spell_list->entries.end(),
|
||||
[iSpellID](const DBbotspells_entries_Struct &a) { return a.spellid == iSpellID; });
|
||||
auto it = std::find_if(
|
||||
spell_list->entries.begin(),
|
||||
spell_list->entries.end(),
|
||||
[iSpellID](const DBbotspells_entries_Struct &a) {
|
||||
return a.spellid == iSpellID;
|
||||
}
|
||||
);
|
||||
|
||||
return it != spell_list->entries.end();
|
||||
}
|
||||
|
||||
DBbotspells_Struct *ZoneDatabase::GetBotSpells(uint32 iDBSpellsID)
|
||||
{
|
||||
if (iDBSpellsID == 0) {
|
||||
if (!iDBSpellsID) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@ -2880,99 +2979,112 @@ 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
|
||||
Bot_Spells_LoadTried.insert(iDBSpellsID);
|
||||
|
||||
std::string query = StringFormat("SELECT id, parent_list, attack_proc, proc_chance, "
|
||||
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
||||
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
||||
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
||||
"pursue_no_sp_recast_min, pursue_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",
|
||||
iDBSpellsID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto query = fmt::format(
|
||||
"SELECT id, parent_list, attack_proc, proc_chance, "
|
||||
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
||||
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
||||
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
||||
"pursue_no_sp_recast_min, pursue_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 = {}",
|
||||
iDBSpellsID
|
||||
);
|
||||
|
||||
if (results.RowCount() != 1) {
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto row = results.begin();
|
||||
DBbotspells_Struct spell_set;
|
||||
|
||||
spell_set.parent_list = atoi(row[1]);
|
||||
spell_set.attack_proc = atoi(row[2]);
|
||||
spell_set.proc_chance = atoi(row[3]);
|
||||
spell_set.range_proc = atoi(row[4]);
|
||||
spell_set.rproc_chance = atoi(row[5]);
|
||||
spell_set.defensive_proc = atoi(row[6]);
|
||||
spell_set.dproc_chance = atoi(row[7]);
|
||||
spell_set.fail_recast = atoi(row[8]);
|
||||
spell_set.engaged_no_sp_recast_min = atoi(row[9]);
|
||||
spell_set.engaged_no_sp_recast_max = atoi(row[10]);
|
||||
spell_set.engaged_beneficial_self_chance = atoi(row[11]);
|
||||
spell_set.engaged_beneficial_other_chance = atoi(row[12]);
|
||||
spell_set.engaged_detrimental_chance = atoi(row[13]);
|
||||
spell_set.pursue_no_sp_recast_min = atoi(row[14]);
|
||||
spell_set.pursue_no_sp_recast_max = atoi(row[15]);
|
||||
spell_set.pursue_detrimental_chance = atoi(row[16]);
|
||||
spell_set.idle_no_sp_recast_min = atoi(row[17]);
|
||||
spell_set.idle_no_sp_recast_max = atoi(row[18]);
|
||||
spell_set.idle_beneficial_chance = atoi(row[19]);
|
||||
spell_set.parent_list = std::stoul(row[1]);
|
||||
spell_set.attack_proc = static_cast<uint16>(std::stoul(row[2]));
|
||||
spell_set.proc_chance = static_cast<uint8>(std::stoul(row[3]));
|
||||
spell_set.range_proc = static_cast<uint16>(std::stoul(row[4]));
|
||||
spell_set.rproc_chance = static_cast<int16>(std::stoi(row[5]));
|
||||
spell_set.defensive_proc = static_cast<uint16>(std::stoul(row[6]));
|
||||
spell_set.dproc_chance = static_cast<int16>(std::stoi(row[7]));
|
||||
spell_set.fail_recast = std::stoul(row[8]);
|
||||
spell_set.engaged_no_sp_recast_min = std::stoul(row[9]);
|
||||
spell_set.engaged_no_sp_recast_max = std::stoul(row[10]);
|
||||
spell_set.engaged_beneficial_self_chance = static_cast<uint8>(std::stoul(row[11]));
|
||||
spell_set.engaged_beneficial_other_chance = static_cast<uint8>(std::stoul(row[12]));
|
||||
spell_set.engaged_detrimental_chance = static_cast<uint8>(std::stoul(row[13]));
|
||||
spell_set.pursue_no_sp_recast_min = std::stoul(row[14]);
|
||||
spell_set.pursue_no_sp_recast_max = std::stoul(row[15]);
|
||||
spell_set.pursue_detrimental_chance = static_cast<uint8>(std::stoul(row[16]));
|
||||
spell_set.idle_no_sp_recast_min = std::stoul(row[17]);
|
||||
spell_set.idle_no_sp_recast_max = std::stoul(row[18]);
|
||||
spell_set.idle_beneficial_chance = static_cast<uint8>(std::stoul(row[19]));
|
||||
|
||||
// pulling fixed values from an auto-increment field is dangerous...
|
||||
query = StringFormat(
|
||||
"SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, min_hp, max_hp, resist_adjust "
|
||||
"FROM bot_spells_entries "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel",
|
||||
iDBSpellsID);
|
||||
query = fmt::format(
|
||||
"SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, min_hp, max_hp, resist_adjust, "
|
||||
"bucket_name, bucket_value, bucket_comparison "
|
||||
"FROM bot_spells_entries "
|
||||
"WHERE npc_spells_id = {} ORDER BY minlevel",
|
||||
iDBSpellsID
|
||||
);
|
||||
results = QueryDatabase(query);
|
||||
|
||||
if (!results.Success()) {
|
||||
if (!results.Success() || !results.RowCount()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int entryIndex = 0;
|
||||
for (row = results.begin(); row != results.end(); ++row, ++entryIndex) {
|
||||
for (auto row : results) {
|
||||
DBbotspells_entries_Struct entry;
|
||||
int spell_id = atoi(row[0]);
|
||||
auto spell_id = std::stoi(row[0]);
|
||||
entry.spellid = spell_id;
|
||||
entry.type = atoul(row[1]);
|
||||
entry.minlevel = atoi(row[2]);
|
||||
entry.maxlevel = atoi(row[3]);
|
||||
entry.manacost = atoi(row[4]);
|
||||
entry.recast_delay = atoi(row[5]);
|
||||
entry.priority = atoi(row[6]);
|
||||
entry.min_hp = atoi(row[7]);
|
||||
entry.max_hp = atoi(row[8]);
|
||||
entry.type = std::stoul(row[1]);
|
||||
entry.minlevel = static_cast<uint8>(std::stoul(row[2]));
|
||||
entry.maxlevel = static_cast<uint8>(std::stoul(row[3]));
|
||||
entry.manacost = static_cast<int16>(std::stoi(row[4]));
|
||||
entry.recast_delay = std::stoi(row[5]);
|
||||
entry.priority = static_cast<int16>(std::stoi(row[6]));
|
||||
entry.min_hp = static_cast<int8>(std::stoi(row[7]));
|
||||
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
|
||||
if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0)
|
||||
if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0) {
|
||||
entry.priority = 1;
|
||||
}
|
||||
|
||||
if (row[9]) {
|
||||
entry.resist_adjust = atoi(row[9]);
|
||||
}
|
||||
else if (IsValidSpell(spell_id)) {
|
||||
entry.resist_adjust = static_cast<int16>(std::stoi(row[9]));
|
||||
} else if (IsValidSpell(spell_id)) {
|
||||
entry.resist_adjust = spells[spell_id].resist_difficulty;
|
||||
}
|
||||
|
||||
spell_set.entries.push_back(entry);
|
||||
}
|
||||
|
||||
Bot_Spells_Cache.insert(std::make_pair(iDBSpellsID, spell_set));
|
||||
|
||||
return &Bot_Spells_Cache[iDBSpellsID];
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// adds a spell to the list, taking into account priority and resorting list as needed.
|
||||
void Bot::AddSpellToBotList(int16 iPriority, uint16 iSpellID, uint32 iType,
|
||||
int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, int8 min_hp, int8 max_hp)
|
||||
{
|
||||
|
||||
void Bot::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
|
||||
) {
|
||||
if(!IsValidSpell(iSpellID)) {
|
||||
return;
|
||||
}
|
||||
@ -2989,6 +3101,9 @@ void Bot::AddSpellToBotList(int16 iPriority, uint16 iSpellID, uint32 iType,
|
||||
t.resist_adjust = iResistAdjust;
|
||||
t.min_hp = min_hp;
|
||||
t.max_hp = max_hp;
|
||||
t.bucket_name = bucket_name;
|
||||
t.bucket_value = bucket_value;
|
||||
t.bucket_comparison = bucket_comparison;
|
||||
|
||||
AIBot_spells.push_back(t);
|
||||
|
||||
|
||||
143
zone/client.cpp
143
zone/client.cpp
@ -11607,149 +11607,6 @@ void Client::SendReloadCommandMessages() {
|
||||
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> merchant_data_buckets;
|
||||
|
||||
@ -1633,7 +1633,6 @@ public:
|
||||
Timer m_list_task_timers_rate_limit = {};
|
||||
|
||||
std::map<std::string,std::string> GetMerchantDataBuckets();
|
||||
bool CheckMerchantDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value);
|
||||
|
||||
protected:
|
||||
friend class Mob;
|
||||
|
||||
@ -855,7 +855,7 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!CheckMerchantDataBucket(ml.bucket_comparison, bucket_value, player_value)) {
|
||||
if (!zone->CheckDataBucket(ml.bucket_comparison, bucket_value, player_value)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2904,7 +2904,7 @@ std::list<MercSpell> Merc::GetMercSpellsBySpellType(Merc* caster, uint32 spellTy
|
||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
@ -2941,7 +2941,7 @@ MercSpell Merc::GetFirstMercSpellBySpellType(Merc* caster, uint32 spellType) {
|
||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
@ -2979,7 +2979,7 @@ MercSpell Merc::GetMercSpellBySpellID(Merc* caster, uint16 spellid) {
|
||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
@ -3009,7 +3009,7 @@ std::list<MercSpell> Merc::GetMercSpellsForSpellEffect(Merc* caster, int spellEf
|
||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
@ -3039,7 +3039,7 @@ std::list<MercSpell> Merc::GetMercSpellsForSpellEffectAndTargetType(Merc* caster
|
||||
std::vector<MercSpell> mercSpellList = caster->GetMercSpells();
|
||||
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
continue;
|
||||
|
||||
@ -6867,6 +6867,10 @@ std::string Mob::GetBucketKey() {
|
||||
return fmt::format("character-{}", CastToClient()->CharacterID());
|
||||
} else if (IsNPC()) {
|
||||
return fmt::format("npc-{}", GetNPCTypeID());
|
||||
#ifdef BOTS
|
||||
} else if (IsBot()) {
|
||||
return fmt::format("bot-{}", CastToBot()->GetBotID());
|
||||
#endif
|
||||
}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
@ -84,7 +84,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates
|
||||
|
||||
float manaR = GetManaRatio();
|
||||
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
|
||||
// Bad info from database can trigger this incorrectly, but that should be fixed in DB, not here
|
||||
//return false;
|
||||
@ -2133,7 +2133,7 @@ bool Mob::Flurry(ExtraAttackOptions *opts)
|
||||
GetCleanName(),
|
||||
target->GetCleanName());
|
||||
}
|
||||
|
||||
|
||||
int num_attacks = GetSpecialAbilityParam(SPECATK_FLURRY, 1);
|
||||
num_attacks = num_attacks > 0 ? num_attacks : RuleI(Combat, MaxFlurryHits);
|
||||
for (int i = 0; i < num_attacks; i++)
|
||||
@ -2171,7 +2171,7 @@ bool Mob::Rampage(ExtraAttackOptions *opts)
|
||||
entity_list.MessageCloseString(this, true, 200, Chat::PetFlurry, NPC_RAMPAGE, GetCleanName());
|
||||
} else {
|
||||
entity_list.MessageCloseString(this, true, 200, Chat::NPCRampage, NPC_RAMPAGE, GetCleanName());
|
||||
}
|
||||
}
|
||||
int rampage_targets = GetSpecialAbilityParam(SPECATK_RAMPAGE, 1);
|
||||
if (rampage_targets == 0) // if set to 0 or not set in the DB
|
||||
rampage_targets = RuleI(Combat, DefaultRampageTargets);
|
||||
|
||||
49
zone/npc.h
49
zone/npc.h
@ -37,14 +37,14 @@
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
float min_x;
|
||||
float max_x;
|
||||
float min_y;
|
||||
float max_y;
|
||||
float min_z;
|
||||
float max_z;
|
||||
bool say;
|
||||
bool proximity_set;
|
||||
float min_x;
|
||||
float max_x;
|
||||
float min_y;
|
||||
float max_y;
|
||||
float min_z;
|
||||
float max_z;
|
||||
bool say;
|
||||
bool proximity_set;
|
||||
} NPCProximity;
|
||||
|
||||
struct AISpells_Struct {
|
||||
@ -60,15 +60,18 @@ struct AISpells_Struct {
|
||||
};
|
||||
|
||||
struct BotSpells_Struct {
|
||||
uint32 type; // 0 = never, must be one (and only one) of the defined values
|
||||
int16 spellid; // <= 0 = no spell
|
||||
int16 manacost; // -1 = use spdat, -2 = no cast time
|
||||
uint32 time_cancast; // when we can cast this spell next
|
||||
int32 recast_delay;
|
||||
int16 priority;
|
||||
int16 resist_adjust;
|
||||
int16 min_hp; // >0 won't cast if HP is below
|
||||
int16 max_hp; // >0 won't cast if HP is above
|
||||
uint32 type; // 0 = never, must be one (and only one) of the defined values
|
||||
int16 spellid; // <= 0 = no spell
|
||||
int16 manacost; // -1 = use spdat, -2 = no cast time
|
||||
uint32 time_cancast; // when we can cast this spell next
|
||||
int32 recast_delay;
|
||||
int16 priority;
|
||||
int16 resist_adjust;
|
||||
int16 min_hp; // >0 won't cast if HP is below
|
||||
int16 max_hp; // >0 won't cast if HP is above
|
||||
std::string bucket_name;
|
||||
std::string bucket_value;
|
||||
uint8 bucket_comparison;
|
||||
};
|
||||
|
||||
struct AISpellsEffects_Struct {
|
||||
@ -79,17 +82,17 @@ struct AISpellsEffects_Struct {
|
||||
};
|
||||
|
||||
struct AISpellsVar_Struct {
|
||||
uint32 fail_recast;
|
||||
uint32 fail_recast;
|
||||
uint32 engaged_no_sp_recast_min;
|
||||
uint32 engaged_no_sp_recast_max;
|
||||
uint8 engaged_beneficial_self_chance;
|
||||
uint8 engaged_beneficial_other_chance;
|
||||
uint8 engaged_detrimental_chance;
|
||||
uint32 pursue_no_sp_recast_min;
|
||||
uint32 pursue_no_sp_recast_max;
|
||||
uint8 pursue_detrimental_chance;
|
||||
uint32 idle_no_sp_recast_min;
|
||||
uint32 idle_no_sp_recast_max;
|
||||
uint32 pursue_no_sp_recast_min;
|
||||
uint32 pursue_no_sp_recast_max;
|
||||
uint8 pursue_detrimental_chance;
|
||||
uint32 idle_no_sp_recast_min;
|
||||
uint32 idle_no_sp_recast_max;
|
||||
uint8 idle_beneficial_chance;
|
||||
};
|
||||
|
||||
|
||||
177
zone/zone.cpp
Executable file → Normal file
177
zone/zone.cpp
Executable file → Normal file
@ -1875,7 +1875,7 @@ bool Zone::Depop(bool StartSpawnTimer) {
|
||||
// clear spell cache
|
||||
database.ClearNPCSpells();
|
||||
database.ClearBotSpells();
|
||||
|
||||
|
||||
zone->spawn_group_list.ReloadSpawnGroups();
|
||||
|
||||
return true;
|
||||
@ -2976,3 +2976,178 @@ std::string Zone::GetAAName(int aa_id)
|
||||
|
||||
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;
|
||||
void SetQuestHotReloadQueued(bool in_quest_hot_reload_queued);
|
||||
|
||||
bool CheckDataBucket(uint8 bucket_comparison, std::string bucket_value, std::string player_value);
|
||||
|
||||
WaterMap *watermap;
|
||||
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);
|
||||
|
||||
@ -109,16 +109,19 @@ struct DBnpcspellseffects_Struct {
|
||||
|
||||
#pragma pack(1)
|
||||
struct DBbotspells_entries_Struct {
|
||||
uint16 spellid;
|
||||
uint8 minlevel;
|
||||
uint8 maxlevel;
|
||||
uint32 type;
|
||||
int16 manacost;
|
||||
int16 priority;
|
||||
int32 recast_delay;
|
||||
int16 resist_adjust;
|
||||
int8 min_hp;
|
||||
int8 max_hp;
|
||||
uint16 spellid;
|
||||
uint8 minlevel;
|
||||
uint8 maxlevel;
|
||||
uint32 type;
|
||||
int16 manacost;
|
||||
int16 priority;
|
||||
int32 recast_delay;
|
||||
int16 resist_adjust;
|
||||
int8 min_hp;
|
||||
int8 max_hp;
|
||||
std::string bucket_name;
|
||||
std::string bucket_value;
|
||||
uint8 bucket_comparison;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
@ -136,11 +139,11 @@ struct DBbotspells_Struct {
|
||||
uint8 engaged_beneficial_self_chance;
|
||||
uint8 engaged_beneficial_other_chance;
|
||||
uint8 engaged_detrimental_chance;
|
||||
uint32 pursue_no_sp_recast_min;
|
||||
uint32 pursue_no_sp_recast_max;
|
||||
uint8 pursue_detrimental_chance;
|
||||
uint32 idle_no_sp_recast_min;
|
||||
uint32 idle_no_sp_recast_max;
|
||||
uint32 pursue_no_sp_recast_min;
|
||||
uint32 pursue_no_sp_recast_max;
|
||||
uint8 pursue_detrimental_chance;
|
||||
uint32 idle_no_sp_recast_min;
|
||||
uint32 idle_no_sp_recast_max;
|
||||
uint8 idle_beneficial_chance;
|
||||
std::vector<DBbotspells_entries_Struct> entries;
|
||||
};
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user