mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-13 14:41:28 +00:00
Update bot spell casting chances table and implemented 'pre-combat' mode for all bots (only bard is actively coded atm)
This commit is contained in:
parent
303f056075
commit
b1be667884
@ -2,6 +2,40 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||
-------------------------------------------------------
|
||||
== 02/26/2017 ==
|
||||
Uleat: Moved bot `npc_spells` entries from '701-712' to 3000 + <class_id> .. also, added melee types for future expansion
|
||||
Uleat: Moved bot spell casting chance values into database - this will allow admins to tailor their bots without having to rebuild server code
|
||||
- Each entry uses a 3-dimensional identifier: [spell type index][class id][stance index]
|
||||
- [spell type index] is not the SpellType_## bit value..use SpellType_##Index instead
|
||||
- [class id] values of 1-16 are valid and hold a direct correlation to server-coded player class values
|
||||
- [stance index] is a direct correlation (0-6)
|
||||
- the 'conditional fields' are currently predicated on 4 compounded boolean states:
|
||||
- `pH_value` represents bit '0'
|
||||
- `pS_value` represents bit '1'
|
||||
- `pN_value` represents bit '2'
|
||||
- `pD_value` represents bit '3'
|
||||
- all other conditional fields are masked based on these 4 predicates
|
||||
- the full conditional field enumeration is as follows:
|
||||
- `nHSND_value` - negative Healer/Slower/Nuker/Doter
|
||||
- `pH_value` - positive Healer
|
||||
- `pS_value` - positive Slower
|
||||
- `pHS_value` - positive Healer/Slower
|
||||
- `pN_value` - positive Nuker
|
||||
- `pHN_value` - positive Healer/Nuker
|
||||
- `pSN_value` - positive Slower/Nuker
|
||||
- `pHSN_value` - positive Healer/Slower/Nuker
|
||||
- `pD_value` - positive Doter
|
||||
- `pHD_value` - positive Healer/Doter
|
||||
- `pSD_value` - positive Slower/Doter
|
||||
- `pHSD_value` - positive Healer/Slower/Doter
|
||||
- `pND_value` - positive Nuker/Doter
|
||||
- `pHND_value` - positive Healer/Nuker/Doter
|
||||
- `pSND_value` - positive Slower/Nuker/Doter
|
||||
- `pHSND_value` - positive Healer/Slower/Nuker/Doter
|
||||
- Single- and mixed-bits fields should be filled-in based on the boolean 'AND' concept
|
||||
- (i.e., if 'healer' then `pH_value`=x; if 'slower' then `pS_value`=y; if 'healer' AND 'slower' then `pHS_value`=z; )
|
||||
- most cases can allow the same value across all fields..but, there are some that shouldn't and this format allows for their discrimination
|
||||
- Valid `##_value` entries are 0-100..though, the field accepts up to 255... Anything above 100 is clamped to 100 upon loading, however...
|
||||
- Not all conditions are currently coded and changing a field may not produce any results
|
||||
- The 'default' database values will be changed and tweaked as bot spell code modifications occur
|
||||
|
||||
== 02/25/2017 ==
|
||||
Uleat: Implemented rule-based node pathing for bots
|
||||
@ -16,6 +50,7 @@ Uleat: Implemented rule-based position update packet with movement timer check f
|
||||
- This appears to help with/eliminate rubber banding
|
||||
|
||||
== 02/23/2017 ==
|
||||
** THIS NOTE HAS BEEN SUPERCEDED ON 02/26/2017 **
|
||||
Uleat: Moved bot spell casting chance values into database - this will allow admins to tailor their bots without having to rebuild server code
|
||||
- Each entry uses a 4-dimensional identifier: [spell type index][class index][stance index][conditional index]
|
||||
- [spell type index] is not the SpellType_## bit value..use SpellType_##Index instead
|
||||
|
||||
@ -67,9 +67,11 @@ enum SpellTypes : uint32
|
||||
SpellType_HateRedux = (1 << 17),
|
||||
SpellType_InCombatBuffSong = (1 << 18), // bard in-combat group/ae buffs
|
||||
SpellType_OutOfCombatBuffSong = (1 << 19), // bard out-of-combat group/ae buffs
|
||||
SpellType_PreCombatBuff = (1 << 20),
|
||||
SpellType_PreCombatBuffSong = (1 << 21),
|
||||
|
||||
SpellTypes_Detrimental = (SpellType_Nuke | SpellType_Root | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Charm | SpellType_Debuff | SpellType_Slow),
|
||||
SpellTypes_Beneficial = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong),
|
||||
SpellTypes_Beneficial = (SpellType_Heal | SpellType_Buff | SpellType_Escape | SpellType_Pet | SpellType_InCombatBuff | SpellType_Cure | SpellType_HateRedux | SpellType_InCombatBuffSong | SpellType_OutOfCombatBuffSong | SpellType_PreCombatBuff | SpellType_PreCombatBuffSong),
|
||||
|
||||
SpellType_Any = 0xFFFFFFFF
|
||||
};
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
|
||||
#define CURRENT_BINARY_DATABASE_VERSION 9106
|
||||
#ifdef BOTS
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9014
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9015
|
||||
#else
|
||||
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 0 // must be 0
|
||||
#endif
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
9012|2017_02_26_bots_npc_spells_update_for_bots.sql|SELECT * FROM `npc_spells` WHERE `id` = '701' AND `name` = 'Cleric Bot'|not_empty|
|
||||
9013|2017_02_26_bots_spells_id_update_for_saved_bots.sql|SELECT * FROM `bot_data` WHERE `spells_id` >= '701' AND `spells_id` <= '712'|not_empty|
|
||||
9014|2017_02_26_bots_spells_id_update_for_bot_spells_entries.sql|SELECT * FROM `bot_spells_entries` WHERE `npc_spells_id` >= '701' AND `npc_spells_id` <= '712'|not_empty|
|
||||
9015|2017_02_26_bots_spell_casting_chances_update.sql|SHOW COLUMNS FROM `bot_spell_casting_chances` LIKE 'value'|not_empty|
|
||||
|
||||
# Upgrade conditions:
|
||||
# This won't be needed after this system is implemented, but it is used database that are not
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
55
zone/bot.cpp
55
zone/bot.cpp
@ -178,6 +178,8 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
|
||||
|
||||
m_CastingRoles.GroupHealer = false;
|
||||
m_CastingRoles.GroupSlower = false;
|
||||
m_CastingRoles.GroupNuker = false;
|
||||
m_CastingRoles.GroupDoter = false;
|
||||
|
||||
GenerateBaseStats();
|
||||
|
||||
@ -424,6 +426,7 @@ void Bot::GenerateBaseStats()
|
||||
int32 ColdResist = _baseCR;
|
||||
int32 CorruptionResist = _baseCorrup;
|
||||
|
||||
// pulling fixed values from an auto-increment field is dangerous...
|
||||
switch(this->GetClass()) {
|
||||
case WARRIOR:
|
||||
BotSpellID = 3001;
|
||||
@ -6917,6 +6920,8 @@ void Bot::UpdateGroupCastingRoles(const Group* group, bool disband)
|
||||
if (iter->IsBot()) {
|
||||
iter->CastToBot()->SetGroupHealer(false);
|
||||
iter->CastToBot()->SetGroupSlower(false);
|
||||
iter->CastToBot()->SetGroupNuker(false);
|
||||
iter->CastToBot()->SetGroupDoter(false);
|
||||
}
|
||||
}
|
||||
|
||||
@ -6925,11 +6930,14 @@ void Bot::UpdateGroupCastingRoles(const Group* group, bool disband)
|
||||
|
||||
Mob* healer = nullptr;
|
||||
Mob* slower = nullptr;
|
||||
Mob* nuker = nullptr;
|
||||
Mob* doter = nullptr;
|
||||
|
||||
for (auto iter : group->members) {
|
||||
if (!iter)
|
||||
continue;
|
||||
|
||||
// GroupHealer
|
||||
switch (iter->GetClass()) {
|
||||
case CLERIC:
|
||||
if (!healer)
|
||||
@ -6978,6 +6986,7 @@ void Bot::UpdateGroupCastingRoles(const Group* group, bool disband)
|
||||
break;
|
||||
}
|
||||
|
||||
// GroupSlower
|
||||
switch (iter->GetClass()) {
|
||||
case SHAMAN:
|
||||
if (!slower)
|
||||
@ -7009,14 +7018,43 @@ void Bot::UpdateGroupCastingRoles(const Group* group, bool disband)
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// GroupNuker
|
||||
switch (iter->GetClass()) {
|
||||
// wizard
|
||||
// magician
|
||||
// necromancer
|
||||
// enchanter
|
||||
// druid
|
||||
// cleric
|
||||
// shaman
|
||||
// shadowknight
|
||||
// paladin
|
||||
// ranger
|
||||
// beastlord
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
// GroupDoter
|
||||
switch (iter->GetClass()) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (healer && healer->IsBot())
|
||||
healer->CastToBot()->SetGroupHealer();
|
||||
if (slower && slower->IsBot())
|
||||
slower->CastToBot()->SetGroupSlower();
|
||||
if (nuker && nuker->IsBot())
|
||||
nuker->CastToBot()->SetGroupNuker();
|
||||
if (doter && doter->IsBot())
|
||||
doter->CastToBot()->SetGroupDoter();
|
||||
}
|
||||
|
||||
//void Bot::UpdateRaidCastingRoles(const Raid* raid, bool disband = false) { }
|
||||
|
||||
bool Bot::CanHeal() {
|
||||
bool result = false;
|
||||
|
||||
@ -7788,6 +7826,23 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl
|
||||
}
|
||||
}
|
||||
|
||||
if (iSpellTypes == SpellType_PreCombatBuff) {
|
||||
if (botCasterClass == BARD || caster->IsEngaged())
|
||||
return false;
|
||||
|
||||
if (caster->HasGroup()) {
|
||||
Group *g = caster->GetGroup();
|
||||
if (g) {
|
||||
for (int i = 0; i < MAX_GROUP_MEMBERS; i++) {
|
||||
if (g->members[i]) {
|
||||
if (caster->AICastSpell(g->members[i], iChance, SpellType_PreCombatBuff) || caster->AICastSpell(g->members[i]->GetPet(), iChance, SpellType_PreCombatBuff))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
55
zone/bot.h
55
zone/bot.h
@ -132,11 +132,48 @@ enum SpellTypeIndex {
|
||||
SpellType_HateReduxIndex,
|
||||
SpellType_InCombatBuffSongIndex,
|
||||
SpellType_OutOfCombatBuffSongIndex,
|
||||
SpellType_PreCombatBuffIndex,
|
||||
SpellType_PreCombatBuffSongIndex,
|
||||
MaxSpellTypes
|
||||
};
|
||||
|
||||
// negative Healer/Slower, positive Healer, postive Slower, positive Healer/Slower
|
||||
enum BotCastingChanceConditional : uint8 { nHS = 0, pH, pS, pHS, cntHS = 4 };
|
||||
// nHSND negative Healer/Slower/Nuker/Doter
|
||||
// pH positive Healer
|
||||
// pS positive Slower
|
||||
// pHS positive Healer/Slower
|
||||
// pN positive Nuker
|
||||
// pHN positive Healer/Nuker
|
||||
// pSN positive Slower/Nuker
|
||||
// pHSN positive Healer/Slower/Nuker
|
||||
// pD positive Doter
|
||||
// pHD positive Healer/Doter
|
||||
// pSD positive Slower/Doter
|
||||
// pHSD positive Healer/Slower/Doter
|
||||
// pND positive Nuker/Doter
|
||||
// pHND positive Healer/Nuker/Doter
|
||||
// pSND positive Slower/Nuker/Doter
|
||||
// pHSND positive Healer/Slower/Nuker/Doter
|
||||
// cntHSND count Healer/Slower/Nuker/Doter
|
||||
enum BotCastingChanceConditional : uint8
|
||||
{
|
||||
nHSND = 0,
|
||||
pH,
|
||||
pS,
|
||||
pHS,
|
||||
pN,
|
||||
pHN,
|
||||
pSN,
|
||||
pHSN,
|
||||
pD,
|
||||
pHD,
|
||||
pSD,
|
||||
pHSD,
|
||||
pND,
|
||||
pHND,
|
||||
pSND,
|
||||
pHSND,
|
||||
cntHSND = 16
|
||||
};
|
||||
|
||||
|
||||
class Bot : public NPC {
|
||||
@ -482,8 +519,16 @@ public:
|
||||
|
||||
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
|
||||
bool IsGroupSlower() { return m_CastingRoles.GroupSlower; }
|
||||
bool IsGroupNuker() { return m_CastingRoles.GroupNuker; }
|
||||
bool IsGroupDoter() { return m_CastingRoles.GroupDoter; }
|
||||
static void UpdateGroupCastingRoles(const Group* group, bool disband = false);
|
||||
|
||||
//bool IsRaidHealer() { return m_CastingRoles.RaidHealer; }
|
||||
//bool IsRaidSlower() { return m_CastingRoles.RaidSlower; }
|
||||
//bool IsRaidNuker() { return m_CastingRoles.RaidNuker; }
|
||||
//bool IsRaidDoter() { return m_CastingRoles.RaidDoter; }
|
||||
//static void UpdateRaidCastingRoles(const Raid* raid, bool disband = false);
|
||||
|
||||
bool IsBotCaster() { return IsCasterClass(GetClass()); }
|
||||
bool IsBotINTCaster() { return IsINTCasterClass(GetClass()); }
|
||||
bool IsBotWISCaster() { return IsWISCasterClass(GetClass()); }
|
||||
@ -650,6 +695,12 @@ protected:
|
||||
BotCastingRoles& GetCastingRoles() { return m_CastingRoles; }
|
||||
void SetGroupHealer(bool flag = true) { m_CastingRoles.GroupHealer = flag; }
|
||||
void SetGroupSlower(bool flag = true) { m_CastingRoles.GroupSlower = flag; }
|
||||
void SetGroupNuker(bool flag = true) { m_CastingRoles.GroupNuker = flag; }
|
||||
void SetGroupDoter(bool flag = true) { m_CastingRoles.GroupDoter = flag; }
|
||||
//void SetRaidHealer(bool flag = true) { m_CastingRoles.RaidHealer = flag; }
|
||||
//void SetRaidSlower(bool flag = true) { m_CastingRoles.RaidSlower = flag; }
|
||||
//void SetRaidNuker(bool flag = true) { m_CastingRoles.RaidNuker = flag; }
|
||||
//void SetRaidDoter(bool flag = true) { m_CastingRoles.RaidDoter = flag; }
|
||||
|
||||
private:
|
||||
// Class Members
|
||||
|
||||
@ -82,7 +82,7 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
|
||||
return true;
|
||||
}
|
||||
|
||||
static uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHS];
|
||||
static uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHSND];
|
||||
|
||||
bool BotDatabase::LoadBotSpellCastingChances()
|
||||
{
|
||||
@ -91,17 +91,29 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
||||
query =
|
||||
"SELECT"
|
||||
" `spell_type_index`,"
|
||||
" `class_index`,"
|
||||
" `class_id`,"
|
||||
" `stance_index`,"
|
||||
" `conditional_index`,"
|
||||
" `value` "
|
||||
" `nHSND_value`,"
|
||||
" `pH_value`,"
|
||||
" `pS_value`,"
|
||||
" `pHS_value`,"
|
||||
" `pN_value`,"
|
||||
" `pHN_value`,"
|
||||
" `pSN_value`,"
|
||||
" `pHSN_value`,"
|
||||
" `pD_value`,"
|
||||
" `pHD_value`,"
|
||||
" `pSD_value`,"
|
||||
" `pHSD_value`,"
|
||||
" `pND_value`,"
|
||||
" `pHND_value`,"
|
||||
" `pSND_value`,"
|
||||
" `pHSND_value`"
|
||||
"FROM"
|
||||
" `bot_spell_casting_chances` "
|
||||
"WHERE"
|
||||
" `value` != '0'";
|
||||
" `bot_spell_casting_chances`";
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
if (!results.Success() || !results.RowCount())
|
||||
return false;
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
@ -109,18 +121,22 @@ bool BotDatabase::LoadBotSpellCastingChances()
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
continue;
|
||||
uint8 class_index = atoi(row[1]);
|
||||
if (class_index >= PLAYER_CLASS_COUNT)
|
||||
if (class_index < WARRIOR || class_index > BERSERKER)
|
||||
continue;
|
||||
--class_index;
|
||||
uint8 stance_index = atoi(row[2]);
|
||||
if (stance_index >= MaxStances)
|
||||
continue;
|
||||
uint8 conditional_index = atoi(row[3]);
|
||||
if (conditional_index >= cntHS)
|
||||
continue;
|
||||
uint8 value = atoi(row[4]);
|
||||
if (value > 100)
|
||||
value = 100;
|
||||
spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
|
||||
|
||||
for (uint8 conditional_index = nHSND; conditional_index < cntHSND; ++conditional_index) {
|
||||
uint8 value = atoi(row[3 + conditional_index]);
|
||||
if (!value)
|
||||
continue;
|
||||
if (value > 100)
|
||||
value = 100;
|
||||
|
||||
spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -2761,7 +2777,7 @@ bool BotDatabase::DeleteAllHealRotations(const uint32 owner_id)
|
||||
|
||||
|
||||
/* Bot miscellaneous functions */
|
||||
uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index)
|
||||
uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index) // class_index is 0-based
|
||||
{
|
||||
if (spell_type_index >= MaxSpellTypes)
|
||||
return 0;
|
||||
@ -2769,7 +2785,7 @@ uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_ind
|
||||
return 0;
|
||||
if (stance_index >= MaxStances)
|
||||
return 0;
|
||||
if (conditional_index >= cntHS)
|
||||
if (conditional_index >= cntHSND)
|
||||
return 0;
|
||||
|
||||
return spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
|
||||
|
||||
@ -63,6 +63,12 @@ struct BotSpell_wPriority : public BotSpell {
|
||||
struct BotCastingRoles {
|
||||
bool GroupHealer;
|
||||
bool GroupSlower;
|
||||
bool GroupNuker;
|
||||
bool GroupDoter;
|
||||
//bool RaidHealer;
|
||||
//bool RaidSlower;
|
||||
//bool RaidNuker;
|
||||
//bool RaidDoter;
|
||||
};
|
||||
|
||||
struct BotAA {
|
||||
|
||||
@ -1046,6 +1046,12 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
break;
|
||||
}
|
||||
case SpellType_PreCombatBuff: {
|
||||
break;
|
||||
}
|
||||
case SpellType_PreCombatBuffSong: {
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -1163,16 +1169,28 @@ bool Bot::AI_IdleCastCheck() {
|
||||
#endif
|
||||
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
||||
|
||||
//Ok, IdleCastCheck depends of class.
|
||||
// Healers WITHOUT pets will check if a heal is needed before buffing.
|
||||
uint8 botClass = GetClass();
|
||||
bool pre_combat = false;
|
||||
Mob* test_against = nullptr;
|
||||
|
||||
if(botClass == CLERIC || botClass == PALADIN || botClass == RANGER) {
|
||||
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient())
|
||||
test_against = GetGroup()->GetLeader();
|
||||
else if (GetOwner() && GetOwner()->IsClient())
|
||||
test_against = GetOwner();
|
||||
|
||||
if (test_against && test_against->GetTarget() && test_against->GetTarget()->IsNPC() && !test_against->GetTarget()->IsPet())
|
||||
pre_combat = true;
|
||||
|
||||
//Ok, IdleCastCheck depends of class.
|
||||
switch (GetClass()) {
|
||||
// Healers WITHOUT pets will check if a heal is needed before buffing.
|
||||
case CLERIC:
|
||||
case PALADIN:
|
||||
case RANGER: {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
//
|
||||
}
|
||||
}
|
||||
@ -1181,16 +1199,23 @@ bool Bot::AI_IdleCastCheck() {
|
||||
}
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
// Pets class will first cast their pet, then buffs
|
||||
else if(botClass == DRUID || botClass == MAGICIAN || botClass == SHADOWKNIGHT || botClass == SHAMAN || botClass == NECROMANCER || botClass == ENCHANTER || botClass == BEASTLORD || botClass == WIZARD) {
|
||||
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
case DRUID:
|
||||
case MAGICIAN:
|
||||
case SHADOWKNIGHT:
|
||||
case SHAMAN:
|
||||
case NECROMANCER:
|
||||
case ENCHANTER:
|
||||
case BEASTLORD: {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
|
||||
if(!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
//
|
||||
}
|
||||
}
|
||||
@ -1201,23 +1226,57 @@ bool Bot::AI_IdleCastCheck() {
|
||||
}
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
else if(botClass == BARD) {
|
||||
// bard bots
|
||||
bool battle_prep = false;
|
||||
Mob* prep_against = nullptr;
|
||||
|
||||
if (HasGroup() && GetGroup()->GetLeader() && GetGroup()->GetLeader()->IsClient())
|
||||
prep_against = GetGroup()->GetLeader();
|
||||
else if (GetOwner() && GetOwner()->IsClient())
|
||||
prep_against = GetOwner();
|
||||
|
||||
if (prep_against && prep_against->GetTarget() && prep_against->GetTarget()->IsNPC() && !prep_against->GetTarget()->IsPet())
|
||||
battle_prep = true;
|
||||
|
||||
if (battle_prep) {
|
||||
if (!AICastSpell(this, 100, SpellType_InCombatBuffSong)) {
|
||||
//
|
||||
case WIZARD: { // This can eventually be move into the BEASTLORD case handler once pre-combat is fully implemented
|
||||
if (pre_combat) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_PreCombatBuff)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Pet)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Heal)) {
|
||||
if (!AICastSpell(this, 100, SpellType_Buff)) {
|
||||
if (!AICastSpell(GetPet(), 100, SpellType_Heal)) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Buff)) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
case BARD: {
|
||||
if (pre_combat) {
|
||||
if (!entity_list.Bot_AICheckCloseBeneficialSpells(this, 100, BotAISpellRange, SpellType_Cure)) {
|
||||
if (!AICastSpell(this, 100, SpellType_PreCombatBuffSong)) {
|
||||
if (!AICastSpell(this, 100, SpellType_InCombatBuffSong)) {
|
||||
//
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
@ -1229,6 +1288,10 @@ bool Bot::AI_IdleCastCheck() {
|
||||
}
|
||||
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if(!AIautocastspell_timer->Enabled())
|
||||
@ -2572,6 +2635,12 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
case SpellType_OutOfCombatBuffSong:
|
||||
spell_type_index = SpellType_OutOfCombatBuffSongIndex;
|
||||
break;
|
||||
case SpellType_PreCombatBuff:
|
||||
spell_type_index = SpellType_PreCombatBuffIndex;
|
||||
break;
|
||||
case SpellType_PreCombatBuffSong:
|
||||
spell_type_index = SpellType_PreCombatBuffSongIndex;
|
||||
break;
|
||||
default:
|
||||
spell_type_index = MaxSpellTypes;
|
||||
break;
|
||||
@ -2588,12 +2657,16 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
|
||||
if (stance_index >= MaxStances)
|
||||
return 0;
|
||||
|
||||
uint8 type_index = nHS;
|
||||
uint8 type_index = nHSND;
|
||||
if (HasGroup()) {
|
||||
if (IsGroupHealer())
|
||||
if (IsGroupHealer()/* || IsRaidHealer()*/)
|
||||
type_index |= pH;
|
||||
if (IsGroupSlower())
|
||||
if (IsGroupSlower()/* || IsRaidSlower()*/)
|
||||
type_index |= pS;
|
||||
if (IsGroupNuker()/* || IsRaidNuker()*/)
|
||||
type_index |= pN;
|
||||
if (IsGroupDoter()/* || IsRaidDoter()*/)
|
||||
type_index |= pD;
|
||||
}
|
||||
|
||||
return botdb.GetSpellCastingChance(spell_type_index, class_index, stance_index, type_index);
|
||||
|
||||
@ -2606,6 +2606,7 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
uint32 tmpidle_no_sp_recast_max = atoi(row[18]);
|
||||
uint8 tmpidle_b_chance = atoi(row[19]);
|
||||
|
||||
// pulling fixed values from an auto-increment field is dangerous...
|
||||
query = StringFormat("SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, resist_adjust "
|
||||
#ifdef BOTS
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user