mirror of
https://github.com/EQEmu/Server.git
synced 2026-06-10 15:00:25 +00:00
[Bots] [Quest API] Add ^clickitem, ^timer, fix GetBestBotSpellForCure (#3755)
* [Bots][Quest API] Add ^clickitem, ^timer, revamp bot timers, fix GetBestBotSpellForCure This adds the command **^clickitem** for bots. Bots can click items they are wearing with the provided slot ID, players can use **^invlist** on their bots to see items and slot IDs. This supports actionables. **^itemclick 13 byclass 11** would command all Necromancer bots to attempt to click their Primary item. This adds and supports charges for items to bots, when an item is used, it will lose a charge and cannot be clicked once no charges remain. This adds the following rules: **Bots, BotsClickItemsMinLvl** - Minimum level bots can use **^clickitem**. **Bots, BotsCanClickItems** - Whether or not **^clickitem** is allowed for bots. **Bots, CanClickMageEpicV1** - Whether or not players are allowed to command their bots to use the Magician Epic 1.0 This adds quest methods to Perl/Lua for: ClearDisciplineReuseTimer, ClearItemReuseTimer, ClearSpellRecastTimer GetDisciplineReuseTimer, GetItemReuseTimer, GetSpellRecastTimer SetDisciplineReuseTimer, SetItemReuseTimer, SetSpellRecastTimer Discipline and Spell methods use the spell_id to check, get and set. Item uses the item_id. Clear and Get support wildcards (no spell/item id) to clear all timers of the type or get the first timer of the type. Get will return the remaining time on the chosen timer, if any. Set supports a wildcard (no recast/reuse provided) to use the default of the provided type, you can also specify a recast/reuse timer to set that timer to the chosen value. **^timer** has been added as a bot command, defaulted for GM access. This can be used to set, get and clear timers of different types. Use **^timer help** for info. This revamps the way timers are set, stored, loaded for bots. **GetBestBotSpellForCure** was previously checking only the first spell found and not properly iterating through the checks. This requires modifications to the **bot_timers** table and is included in this commit. * Rebase Conflicts * Update queries to use repositories * Minor adjustment * Formatting * Handle delete as well * Cleanup. * Adjust primary keys to prevent conflicts --------- Co-authored-by: Akkadius <akkadius1@gmail.com> Co-authored-by: Kinglykrab <kinglykrab@gmail.com>
This commit is contained in:
+638
-15
@@ -94,6 +94,7 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
SetPullFlag(false);
|
||||
SetPullingFlag(false);
|
||||
SetReturningFlag(false);
|
||||
SetIsUsingItemClick(false);
|
||||
m_previous_pet_order = SPO_Guard;
|
||||
|
||||
rest_timer.Disable();
|
||||
@@ -107,6 +108,8 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
// Do this once and only in this constructor
|
||||
GenerateAppearance();
|
||||
GenerateBaseStats();
|
||||
bot_timers.clear();
|
||||
|
||||
// Calculate HitPoints Last As It Uses Base Stats
|
||||
current_hp = GenerateBaseHitPoints();
|
||||
current_mana = GenerateBaseManaPoints();
|
||||
@@ -114,8 +117,6 @@ Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm
|
||||
hp_regen = CalcHPRegen();
|
||||
mana_regen = CalcManaRegen();
|
||||
end_regen = CalcEnduranceRegen();
|
||||
for (int i = 0; i < MaxTimer; i++)
|
||||
timers[i] = 0;
|
||||
|
||||
strcpy(name, GetCleanName());
|
||||
memset(&_botInspectMessage, 0, sizeof(InspectMessage_Struct));
|
||||
@@ -213,6 +214,7 @@ Bot::Bot(
|
||||
SetPullFlag(false);
|
||||
SetPullingFlag(false);
|
||||
SetReturningFlag(false);
|
||||
SetIsUsingItemClick(false);
|
||||
m_previous_pet_order = SPO_Guard;
|
||||
|
||||
rest_timer.Disable();
|
||||
@@ -238,9 +240,6 @@ Bot::Bot(
|
||||
error_message.clear();
|
||||
}
|
||||
|
||||
for (int i = 0; i < MaxTimer; i++)
|
||||
timers[i] = 0;
|
||||
|
||||
if (GetClass() == Class::Rogue) {
|
||||
m_evade_timer.Start();
|
||||
}
|
||||
@@ -252,8 +251,10 @@ Bot::Bot(
|
||||
|
||||
GenerateBaseStats();
|
||||
|
||||
if (!database.botdb.LoadTimers(this) && bot_owner)
|
||||
bot_timers.clear();
|
||||
if (!database.botdb.LoadTimers(this) && bot_owner) {
|
||||
bot_owner->Message(Chat::White, "%s for '%s'", BotDatabase::fail::LoadTimers(), GetCleanName());
|
||||
}
|
||||
|
||||
LoadAAs();
|
||||
|
||||
@@ -7916,18 +7917,17 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
return false;
|
||||
|
||||
if (spell.recast_time > 0) {
|
||||
if (CheckDisciplineRecastTimers(this, spells[spell_id].timer_id)) {
|
||||
if (spells[spell_id].timer_id > 0 && spells[spell_id].timer_id < MAX_DISCIPLINE_TIMERS)
|
||||
SetDisciplineRecastTimer(spells[spell_id].timer_id, spell.recast_time);
|
||||
if (CheckDisciplineReuseTimer(spell_id)) {
|
||||
if (spells[spell_id].timer_id > 0) {
|
||||
SetDisciplineReuseTimer(spell_id);
|
||||
}
|
||||
} else {
|
||||
uint32 remaining_time = (GetDisciplineRemainingTime(this, spells[spell_id].timer_id) / 1000);
|
||||
GetOwner()->Message(
|
||||
Chat::White,
|
||||
uint32 remaining_time = (GetDisciplineReuseRemainingTime(spell_id) / 1000);
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} can use this discipline in {}.",
|
||||
GetCleanName(),
|
||||
"I can use this discipline in {}.",
|
||||
Strings::SecondsToTime(remaining_time)
|
||||
).c_str()
|
||||
)
|
||||
);
|
||||
return false;
|
||||
}
|
||||
@@ -8810,4 +8810,627 @@ void Bot::AddBotStartingItems(uint16 race_id, uint8 class_id)
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::SetSpellRecastTimer(uint16 spell_id, int32 recast_delay) {
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to set spell recast timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!recast_delay) {
|
||||
recast_delay = CalcSpellRecastTimer(spell_id);
|
||||
}
|
||||
|
||||
if (CheckSpellRecastTimer(spell_id)) {
|
||||
BotTimer_Struct t;
|
||||
|
||||
t.timer_id = spells[ spell_id ].timer_id;
|
||||
t.timer_value = (Timer::GetCurrentTime() + recast_delay);
|
||||
t.recast_time = recast_delay;
|
||||
t.is_spell = true;
|
||||
t.is_disc = false;
|
||||
t.spell_id = spells[ spell_id ].id;
|
||||
t.is_item = false;
|
||||
t.item_id = 0;
|
||||
|
||||
bot_timers.push_back(t);
|
||||
} else {
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_spell &&
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[ i ].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = (Timer::GetCurrentTime() + recast_delay);
|
||||
bot_timers[i].recast_time = recast_delay;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Bot::GetSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to get spell recast timer.");
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_spell &&
|
||||
(
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
)
|
||||
) {
|
||||
result = bot_timers[i].timer_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 Bot::GetSpellRecastRemainingTime(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (GetSpellRecastTimer(spell_id) > Timer::GetCurrentTime()) {
|
||||
result = (GetSpellRecastTimer(spell_id) - Timer::GetCurrentTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
ClearExpiredTimers();
|
||||
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to check spell recast timer.");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (GetSpellRecastTimer(spell_id) < Timer::GetCurrentTime()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::SetDisciplineReuseTimer(uint16 spell_id, int32 reuse_timer)
|
||||
{
|
||||
if (!IsValidSpell(spell_id)) {
|
||||
OwnerMessage("Failed to set discipline reuse timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!reuse_timer) {
|
||||
reuse_timer = CalcSpellRecastTimer(spell_id);
|
||||
}
|
||||
|
||||
if (CheckDisciplineReuseTimer(spell_id)) {
|
||||
BotTimer_Struct t;
|
||||
|
||||
t.timer_id = spells[ spell_id ].timer_id;
|
||||
t.timer_value = (Timer::GetCurrentTime() + reuse_timer);
|
||||
t.recast_time = reuse_timer;
|
||||
t.is_spell = false;
|
||||
t.is_disc = true;
|
||||
t.spell_id = spells[ spell_id ].id;
|
||||
t.is_item = false;
|
||||
t.item_id = 0;
|
||||
|
||||
bot_timers.push_back(t);
|
||||
} else {
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_disc &&
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = (Timer::GetCurrentTime() + reuse_timer);
|
||||
bot_timers[i].recast_time = reuse_timer;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Bot::GetDisciplineReuseTimer(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_disc &&
|
||||
(
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
)
|
||||
) {
|
||||
result = bot_timers[i].timer_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 Bot::GetDisciplineReuseRemainingTime(uint16 spell_id) {
|
||||
uint32 result = 0;
|
||||
|
||||
if (GetDisciplineReuseTimer(spell_id) > Timer::GetCurrentTime()) {
|
||||
result = (GetDisciplineReuseTimer(spell_id) - Timer::GetCurrentTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckDisciplineReuseTimer(uint16 spell_id)
|
||||
{
|
||||
ClearExpiredTimers();
|
||||
|
||||
if (GetDisciplineReuseTimer(spell_id) < Timer::GetCurrentTime()) { //checks for spells on the same timer
|
||||
return true; //can cast spell
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Bot::SetItemReuseTimer(uint32 item_id, uint32 reuse_timer)
|
||||
{
|
||||
const auto *item = database.GetItem(item_id);
|
||||
|
||||
if (!item) {
|
||||
OwnerMessage("Failed to set item reuse timer.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (item->RecastDelay <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (CheckItemReuseTimer(item_id)) {
|
||||
BotTimer_Struct t;
|
||||
|
||||
t.timer_id = (item->RecastType == NegativeItemReuse ? item->ID : item->RecastType);
|
||||
t.timer_value = (
|
||||
reuse_timer != 0 ?
|
||||
(Timer::GetCurrentTime() + reuse_timer) :
|
||||
(Timer::GetCurrentTime() + (item->RecastDelay * 1000))
|
||||
);
|
||||
t.recast_time = (reuse_timer != 0 ? reuse_timer : (item->RecastDelay * 1000));
|
||||
t.is_spell = false;
|
||||
t.is_disc = false;
|
||||
t.spell_id = 0;
|
||||
t.is_item = true;
|
||||
t.item_id = item->ID;
|
||||
|
||||
bot_timers.push_back(t);
|
||||
}
|
||||
else {
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_item &&
|
||||
(
|
||||
(
|
||||
item->RecastType != 0 &&
|
||||
item->RecastType == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].item_id == item_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = (
|
||||
reuse_timer != 0 ?
|
||||
(Timer::GetCurrentTime() + reuse_timer) :
|
||||
(Timer::GetCurrentTime() + (item->RecastDelay * 1000))
|
||||
);
|
||||
bot_timers[i].recast_time = (
|
||||
reuse_timer != 0 ?
|
||||
reuse_timer :
|
||||
(item->RecastDelay * 1000)
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint32 Bot::GetItemReuseTimer(uint32 item_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
const EQ::ItemData* item;
|
||||
|
||||
if (item_id) {
|
||||
item = database.GetItem(item_id);
|
||||
|
||||
if (!item) {
|
||||
OwnerMessage("Failed to get item reuse timer.");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_item &&
|
||||
(
|
||||
!item_id ||
|
||||
(
|
||||
(
|
||||
item->RecastType != 0 &&
|
||||
item->RecastType == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].item_id == item_id
|
||||
)
|
||||
)
|
||||
) {
|
||||
result = bot_timers[i].timer_value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Bot::CheckItemReuseTimer(uint32 item_id)
|
||||
{
|
||||
ClearExpiredTimers();
|
||||
|
||||
if (GetItemReuseTimer(item_id) < Timer::GetCurrentTime()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32 Bot::GetItemReuseRemainingTime(uint32 item_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (GetItemReuseTimer(item_id) > Timer::GetCurrentTime()) {
|
||||
result = (GetItemReuseTimer(item_id) - Timer::GetCurrentTime());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32 Bot::CalcSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
uint32 result = 0;
|
||||
|
||||
if (spells[spell_id].recast_time == 0 && spells[spell_id].recovery_time == 0) {
|
||||
return result;
|
||||
} else {
|
||||
if (spells[spell_id].recovery_time > spells[spell_id].recast_time) {
|
||||
result = spells[spell_id].recovery_time;
|
||||
} else {
|
||||
result = spells[spell_id].recast_time;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Bot::ClearDisciplineReuseTimer(uint16 spell_id)
|
||||
{
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is not a valid spell ID.'",
|
||||
spell_id
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (
|
||||
bot_timers[i].is_disc &&
|
||||
bot_timers[i].timer_value >= Timer::GetCurrentTime()
|
||||
) {
|
||||
if (
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
}
|
||||
|
||||
void Bot::ClearItemReuseTimer(uint32 item_id)
|
||||
{
|
||||
const EQ::ItemData* item;
|
||||
|
||||
if (item_id) {
|
||||
item = database.GetItem(item_id);
|
||||
|
||||
if (!item) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is not a valid item ID.",
|
||||
item_id
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (bot_timers[i].is_item && bot_timers[i].timer_value >= Timer::GetCurrentTime()) {
|
||||
if (
|
||||
!item_id ||
|
||||
(
|
||||
(
|
||||
item->RecastType != 0 &&
|
||||
item->RecastType == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].item_id == item_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
}
|
||||
|
||||
void Bot::ClearSpellRecastTimer(uint16 spell_id)
|
||||
{
|
||||
if (spell_id && !IsValidSpell(spell_id)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is not a valid spell ID.",
|
||||
spell_id
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!bot_timers.empty()) {
|
||||
for (int i = 0; i < bot_timers.size(); i++) {
|
||||
if (bot_timers[i].is_spell && bot_timers[i].timer_value >= Timer::GetCurrentTime()) {
|
||||
if (
|
||||
!spell_id ||
|
||||
(
|
||||
(
|
||||
spells[spell_id].timer_id != 0 &&
|
||||
spells[spell_id].timer_id == bot_timers[i].timer_id
|
||||
) ||
|
||||
bot_timers[i].spell_id == spell_id
|
||||
)
|
||||
) {
|
||||
bot_timers[i].timer_value = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ClearExpiredTimers();
|
||||
}
|
||||
|
||||
|
||||
void Bot::ClearExpiredTimers()
|
||||
{
|
||||
if (!bot_timers.empty()) {
|
||||
int current = 0;
|
||||
int end = bot_timers.size();
|
||||
|
||||
while (current < end) {
|
||||
if (bot_timers[current].timer_value < Timer::GetCurrentTime()) {
|
||||
bot_timers.erase(bot_timers.begin() + current);
|
||||
} else {
|
||||
current++;
|
||||
}
|
||||
|
||||
end = bot_timers.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Bot::TryItemClick(uint16 slot_id)
|
||||
{
|
||||
if (!GetOwner()) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto *inst = GetClickItem(slot_id);
|
||||
|
||||
if (!inst) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto *item = inst->GetItem();
|
||||
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!CheckItemReuseTimer(item->ID)) {
|
||||
uint32 remaining_time = (GetItemReuseRemainingTime(item->ID) / 1000);
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"I can use this item in {}.",
|
||||
Strings::SecondsToTime(remaining_time)
|
||||
)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
DoItemClick(item, slot_id);
|
||||
}
|
||||
|
||||
EQ::ItemInstance *Bot::GetClickItem(uint16 slot_id)
|
||||
{
|
||||
EQ::ItemInstance* inst = nullptr;
|
||||
const EQ::ItemData* item = nullptr;
|
||||
|
||||
inst = GetBotItem(slot_id);
|
||||
|
||||
if (!inst || !inst->GetItem()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
item = inst->GetItem();
|
||||
|
||||
if (item->ID == MAG_EPIC_1_0 && !RuleB(Bots, CanClickMageEpicV1)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is currently disabled for bots to click.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item->Click.Effect <= 0) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} does not have a clickable effect.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!IsValidSpell(item->Click.Effect)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} does not have a valid clickable effect.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item->ReqLevel > GetLevel()) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"I am below the level requirement of {} for {}.",
|
||||
item->ReqLevel,
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (item->Click.Level2 > GetLevel()) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"I must be level {} to use {}.",
|
||||
item->Click.Level2,
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (inst->GetCharges() == 0) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"{} is out of charges.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
void Bot::DoItemClick(const EQ::ItemData *item, uint16 slot_id)
|
||||
{
|
||||
bool is_casting_bard_song = false;
|
||||
Mob* tar = (GetOwner()->GetTarget() ? GetOwner()->GetTarget() : this);
|
||||
|
||||
if (IsCasting()) {
|
||||
InterruptSpell();
|
||||
}
|
||||
|
||||
SetIsUsingItemClick(true);
|
||||
|
||||
BotGroupSay(
|
||||
this,
|
||||
fmt::format(
|
||||
"Attempting to cast [{}] on {}.",
|
||||
spells[item->Click.Effect].name,
|
||||
tar->GetCleanName()
|
||||
).c_str()
|
||||
);
|
||||
|
||||
if (!IsCastWhileInvisibleSpell(item->Click.Effect)) {
|
||||
CommonBreakInvisible();
|
||||
}
|
||||
|
||||
if (GetClass() == Class::Bard && IsCasting() && casting_spell_slot < EQ::spells::CastingSlot::MaxGems) {
|
||||
is_casting_bard_song = true;
|
||||
}
|
||||
|
||||
if (GetClass() == Class::Bard) {
|
||||
DoBardCastingFromItemClick(is_casting_bard_song, item->CastTime, item->Click.Effect, tar->GetID(), EQ::spells::CastingSlot::Item, slot_id, item->RecastType, item->RecastDelay);
|
||||
} else {
|
||||
if (!CastSpell(item->Click.Effect, tar->GetID(), EQ::spells::CastingSlot::Item, item->CastTime, 0, 0, slot_id)) {
|
||||
OwnerMessage(
|
||||
fmt::format(
|
||||
"Casting failed for {}. This could be due to zone restrictions, target restrictions or other limiting factors.",
|
||||
item->Name
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][Class::PLAYER_CLASS_COUNT][EQ::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
|
||||
|
||||
Reference in New Issue
Block a user