[Bots] Bot::PerformTradeWithClient Cleanup. (#2084)

* [Bots] Bot::PerformTradeWithClient Cleanup.
- Cleanups message and logic.
- Initial cleanup to eventually allow easy use with Perl/Lua quest API.

* Duplicated comment.
This commit is contained in:
Kinglykrab 2022-05-01 19:05:12 -04:00 committed by GitHub
parent 8f0b80097e
commit 759f9bd007
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 227 additions and 132 deletions

View File

@ -4206,28 +4206,24 @@ void Bot::FinishTrade(Client* client, BotTradeType tradeType)
}
// Perfoms the actual trade action with a client bot owner
void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client)
void Bot::PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client)
{
using namespace EQ;
struct ClientTrade {
const ItemInstance* tradeItemInstance;
int16 fromClientSlot;
int16 toBotSlot;
int adjustStackSize;
std::string acceptedItemName;
ClientTrade(const ItemInstance* item, int16 from, const char* name = "") : tradeItemInstance(item), fromClientSlot(from), toBotSlot(invslot::SLOT_INVALID), adjustStackSize(0), acceptedItemName(name) { }
const ItemInstance* trade_item_instance;
int16 from_client_slot;
int16 to_bot_slot;
ClientTrade(const ItemInstance* item, int16 from) : trade_item_instance(item), from_client_slot(from), to_bot_slot(invslot::SLOT_INVALID) { }
};
struct ClientReturn {
const ItemInstance* returnItemInstance;
int16 fromBotSlot;
int16 toClientSlot;
int adjustStackSize;
std::string failedItemName;
ClientReturn(const ItemInstance* item, int16 from, const char* name = "") : returnItemInstance(item), fromBotSlot(from), toClientSlot(invslot::SLOT_INVALID), adjustStackSize(0), failedItemName(name) { }
const ItemInstance* return_item_instance;
int16 from_bot_slot;
int16 to_client_slot;
ClientReturn(const ItemInstance* item, int16 from) : return_item_instance(item), from_bot_slot(from), to_client_slot(invslot::SLOT_INVALID) { }
};
static const int16 bot_equip_order[invslot::EQUIPMENT_COUNT] = {
@ -4247,32 +4243,37 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
}
if (client != GetOwner()) {
client->Message(Chat::Red, "You are not the owner of this bot - Trade Canceled.");
client->Message(Chat::Red, "You are not the owner of this bot, the trade has been cancelled.");
client->ResetTrade();
return;
}
if ((beginSlotID != invslot::TRADE_BEGIN) && (beginSlotID != invslot::slotCursor)) {
client->Message(Chat::Red, "Trade request processing from illegal 'begin' slot - Trade Canceled.");
if (begin_slot_id != invslot::TRADE_BEGIN && begin_slot_id != invslot::slotCursor) {
client->Message(Chat::Red, "Trade request processing from illegal 'begin' slot, the trade has been cancelled.");
client->ResetTrade();
return;
}
if ((endSlotID != invslot::TRADE_END) && (endSlotID != invslot::slotCursor)) {
client->Message(Chat::Red, "Trade request processing from illegal 'end' slot - Trade Canceled.");
if (end_slot_id != invslot::TRADE_END && end_slot_id != invslot::slotCursor) {
client->Message(Chat::Red, "Trade request processing from illegal 'end' slot, the trade has been cancelled.");
client->ResetTrade();
return;
}
if (((beginSlotID == invslot::slotCursor) && (endSlotID != invslot::slotCursor)) || ((beginSlotID != invslot::slotCursor) && (endSlotID == invslot::slotCursor))) {
client->Message(Chat::Red, "Trade request processing illegal slot range - Trade Canceled.");
if ((begin_slot_id == invslot::slotCursor && end_slot_id != invslot::slotCursor) || (begin_slot_id != invslot::slotCursor && end_slot_id == invslot::slotCursor)) {
client->Message(Chat::Red, "Trade request processing illegal slot range, the trade has been cancelled.");
client->ResetTrade();
return;
}
if (endSlotID < beginSlotID) {
client->Message(Chat::Red, "Trade request processing in reverse slot order - Trade Canceled.");
if (end_slot_id < begin_slot_id) {
client->Message(Chat::Red, "Trade request processing in reverse slot order, the trade has been cancelled.");
client->ResetTrade();
return;
}
if (client->IsEngaged() || IsEngaged()) {
client->Message(Chat::Yellow, "You may not perform a trade while engaged - Trade Canceled!");
client->Message(Chat::Yellow, "You may not perform a trade while engaged, the trade has been cancelled!");
client->ResetTrade();
return;
}
@ -4281,66 +4282,100 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
std::list<ClientReturn> client_return;
// pre-checks for incoming illegal transfers
for (int16 trade_index = beginSlotID; trade_index <= endSlotID; ++trade_index) {
for (int16 trade_index = begin_slot_id; trade_index <= end_slot_id; ++trade_index) {
auto trade_instance = client->GetInv()[trade_index];
if (!trade_instance)
if (!trade_instance) {
continue;
}
if (!trade_instance->GetItem()) {
// TODO: add logging
client->Message(Chat::Red, "A server error was encountered while processing client slot %i - Trade Canceled.", trade_index);
LogError("Bot::PerformTradeWithClient could not find item from instance in trade for {} with {} in slot {}.", client->GetCleanName(), GetCleanName(), trade_index);
client->Message(
Chat::Red,
fmt::format(
"A server error was encountered while processing client slot {}, the trade has been cancelled.",
trade_index
).c_str()
);
client->ResetTrade();
return;
}
if ((trade_index != invslot::slotCursor) && !trade_instance->IsDroppable()) {
// TODO: add logging
client->Message(Chat::Red, "Trade hack detected - Trade Canceled.");
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
linker.SetItemInst(trade_instance);
auto item_link = linker.GenerateLink();
if (trade_index != invslot::slotCursor && !trade_instance->IsDroppable()) {
LogError("Bot::PerformTradeWithClient trade hack detected by {} with {}.", client->GetCleanName(), GetCleanName());
client->Message(Chat::Red, "Trade hack detected, the trade has been cancelled.");
client->ResetTrade();
return;
}
if (trade_instance->IsStackable() && (trade_instance->GetCharges() < trade_instance->GetItem()->StackSize)) { // temp until partial stacks are implemented
client->Message(Chat::Yellow, "'%s' is only a partially stacked item - Trade Canceled!", trade_instance->GetItem()->Name);
client->Message(
Chat::Yellow,
fmt::format(
"{} is only a partially stacked item, the trade has been cancelled!",
item_link
).c_str()
);
client->ResetTrade();
return;
}
if (CheckLoreConflict(trade_instance->GetItem())) {
client->Message(Chat::Yellow, "This bot already has lore equipment matching the item '%s' - Trade Canceled!", trade_instance->GetItem()->Name);
client->Message(
Chat::Yellow,
fmt::format(
"This bot already has {}, the trade has been cancelled!",
item_link
).c_str()
);
client->ResetTrade();
return;
}
if (!trade_instance->IsType(item::ItemClassCommon)) {
client_return.push_back(ClientReturn(trade_instance, trade_index, trade_instance->GetItem()->Name));
continue;
}
if (!trade_instance->IsEquipable(GetBaseRace(), GetClass()) || (GetLevel() < trade_instance->GetItem()->ReqLevel)) { // deity checks will be handled within IsEquipable()
client_return.push_back(ClientReturn(trade_instance, trade_index, trade_instance->GetItem()->Name));
client_return.push_back(ClientReturn(trade_instance, trade_index));
continue;
}
client_trade.push_back(ClientTrade(trade_instance, trade_index, trade_instance->GetItem()->Name));
if (!trade_instance->IsEquipable(GetBaseRace(), GetClass()) || (GetLevel() < trade_instance->GetItem()->ReqLevel)) { // deity checks will be handled within IsEquipable()
client_return.push_back(ClientReturn(trade_instance, trade_index));
continue;
}
client_trade.push_back(ClientTrade(trade_instance, trade_index));
}
// check for incoming lore hacks
for (auto& trade_iterator : client_trade) {
if (!trade_iterator.tradeItemInstance->GetItem()->LoreFlag)
if (!trade_iterator.trade_item_instance->GetItem()->LoreFlag) {
continue;
}
for (const auto& check_iterator : client_trade) {
if (check_iterator.fromClientSlot == trade_iterator.fromClientSlot)
continue;
if (!check_iterator.tradeItemInstance->GetItem()->LoreFlag)
if (check_iterator.from_client_slot == trade_iterator.from_client_slot) {
continue;
}
if ((trade_iterator.tradeItemInstance->GetItem()->LoreGroup == -1) && (check_iterator.tradeItemInstance->GetItem()->ID == trade_iterator.tradeItemInstance->GetItem()->ID)) {
// TODO: add logging
client->Message(Chat::Red, "Trade hack detected - Trade Canceled.");
if (!check_iterator.trade_item_instance->GetItem()->LoreFlag) {
continue;
}
if (trade_iterator.trade_item_instance->GetItem()->LoreGroup == -1 && check_iterator.trade_item_instance->GetItem()->ID == trade_iterator.trade_item_instance->GetItem()->ID) {
LogError("Bot::PerformTradeWithClient trade hack detected by {} with {}.", client->GetCleanName(), GetCleanName());
client->Message(Chat::Red, "Trade hack detected, the trade has been cancelled.");
client->ResetTrade();
return;
}
if ((trade_iterator.tradeItemInstance->GetItem()->LoreGroup > 0) && (check_iterator.tradeItemInstance->GetItem()->LoreGroup == trade_iterator.tradeItemInstance->GetItem()->LoreGroup)) {
// TODO: add logging
client->Message(Chat::Red, "Trade hack detected - Trade Canceled.");
if ((trade_iterator.trade_item_instance->GetItem()->LoreGroup > 0) && (check_iterator.trade_item_instance->GetItem()->LoreGroup == trade_iterator.trade_item_instance->GetItem()->LoreGroup)) {
LogError("Bot::PerformTradeWithClient trade hack detected by {} with {}.", client->GetCleanName(), GetCleanName());
client->Message(Chat::Red, "Trade hack detected, the trade has been cancelled.");
client->ResetTrade();
return;
}
@ -4355,16 +4390,18 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
//for (unsigned stage_loop = stageStackable; stage_loop <= stageReplaceable; ++stage_loop) { // awaiting implementation
for (unsigned stage_loop = stageEmpty; stage_loop <= stageReplaceable; ++stage_loop) {
for (auto& trade_iterator : client_trade) {
if (trade_iterator.toBotSlot != invslot::SLOT_INVALID)
if (trade_iterator.to_bot_slot != invslot::SLOT_INVALID) {
continue;
}
auto trade_instance = trade_iterator.tradeItemInstance;
auto trade_instance = trade_iterator.trade_item_instance;
//if ((stage_loop == stageStackable) && !trade_instance->IsStackable())
// continue;
for (auto index : bot_equip_order) {
if (!(trade_instance->GetItem()->Slots & (1 << index)))
if (!(trade_instance->GetItem()->Slots & (1 << index))) {
continue;
}
//if (stage_loop == stageStackable) {
// // TODO: implement
@ -4372,59 +4409,64 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
//}
if (stage_loop != stageReplaceable) {
if (m_inv[index])
if (m_inv[index]) {
continue;
}
}
bool slot_taken = false;
for (const auto& check_iterator : client_trade) {
if (check_iterator.fromClientSlot == trade_iterator.fromClientSlot)
if (check_iterator.from_client_slot == trade_iterator.from_client_slot) {
continue;
}
if (check_iterator.toBotSlot == index) {
if (check_iterator.to_bot_slot == index) {
slot_taken = true;
break;
}
}
if (slot_taken)
if (slot_taken) {
continue;
}
if (index == invslot::slotPrimary) {
if (trade_instance->GetItem()->IsType2HWeapon()) {
if (!melee_secondary) {
melee_2h_weapon = true;
auto equipped_secondary_weapon = m_inv[invslot::slotSecondary];
if (equipped_secondary_weapon)
if (equipped_secondary_weapon) {
client_return.push_back(ClientReturn(equipped_secondary_weapon, invslot::slotSecondary));
}
else {
}
} else {
continue;
}
}
}
if (index == invslot::slotSecondary) {
} else if (index == invslot::slotSecondary) {
if (!melee_2h_weapon) {
if ((can_dual_wield && trade_instance->GetItem()->IsType1HWeapon()) || trade_instance->GetItem()->IsTypeShield() || !trade_instance->IsWeapon()) {
if (
(can_dual_wield && trade_instance->GetItem()->IsType1HWeapon()) ||
trade_instance->GetItem()->IsTypeShield() ||
!trade_instance->IsWeapon()
) {
melee_secondary = true;
auto equipped_primary_weapon = m_inv[invslot::slotPrimary];
if (equipped_primary_weapon && equipped_primary_weapon->GetItem()->IsType2HWeapon())
if (equipped_primary_weapon && equipped_primary_weapon->GetItem()->IsType2HWeapon()) {
client_return.push_back(ClientReturn(equipped_primary_weapon, invslot::slotPrimary));
}
else {
}
} else {
continue;
}
}
else {
} else {
continue;
}
}
trade_iterator.toBotSlot = index;
trade_iterator.to_bot_slot = index;
if (m_inv[index])
if (m_inv[index]) {
client_return.push_back(ClientReturn(m_inv[index], index));
}
break;
}
@ -4433,8 +4475,8 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
// move unassignable items from trade list to return list
for (std::list<ClientTrade>::iterator trade_iterator = client_trade.begin(); trade_iterator != client_trade.end();) {
if (trade_iterator->toBotSlot == invslot::SLOT_INVALID) {
client_return.push_back(ClientReturn(trade_iterator->tradeItemInstance, trade_iterator->fromClientSlot, trade_iterator->tradeItemInstance->GetItem()->Name));
if (trade_iterator->to_bot_slot == invslot::SLOT_INVALID) {
client_return.push_back(ClientReturn(trade_iterator->trade_item_instance, trade_iterator->from_client_slot));
trade_iterator = client_trade.erase(trade_iterator);
continue;
}
@ -4443,31 +4485,49 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
// out-going return checks for client
for (auto& return_iterator : client_return) {
auto return_instance = return_iterator.returnItemInstance;
if (!return_instance)
auto return_instance = return_iterator.return_item_instance;
if (!return_instance) {
continue;
}
if (!return_instance->GetItem()) {
// TODO: add logging
client->Message(Chat::Red, "A server error was encountered while processing bot slot %i - Trade Canceled.", return_iterator.fromBotSlot);
LogError("Bot::PerformTradeWithClient error processing bot slot {} for {} in trade with {}.", return_iterator.from_bot_slot, GetCleanName(), client->GetCleanName());
client->Message(
Chat::Red,
fmt::format(
"A server error was encountered while processing bot slot {}, the trade has been cancelled.",
return_iterator.from_bot_slot
).c_str()
);
client->ResetTrade();
return;
}
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
linker.SetItemInst(return_instance);
auto item_link = linker.GenerateLink();
// non-failing checks above are causing this to trigger (i.e., !ItemClassCommon and !IsEquipable{race, class, min_level})
// this process is hindered by not having bots use the inventory trade method (TODO: implement bot inventory use)
if (client->CheckLoreConflict(return_instance->GetItem())) {
client->Message(Chat::Yellow, "You already have lore equipment matching the item '%s' - Trade Canceled!", return_instance->GetItem()->Name);
client->Message(
Chat::Yellow,
fmt::format(
"You already have {}, the trade has been cancelled!",
item_link
).c_str()
);
client->ResetTrade();
return;
}
if (return_iterator.fromBotSlot == invslot::slotCursor) {
return_iterator.toClientSlot = invslot::slotCursor;
}
else {
if (return_iterator.from_bot_slot == invslot::slotCursor) {
return_iterator.to_client_slot = invslot::slotCursor;
} else {
int16 client_search_general = invslot::GENERAL_BEGIN;
uint8 client_search_bag = invbag::SLOT_BEGIN;
bool run_search = true;
while (run_search) {
int16 client_test_slot = client->GetInv().FindFreeSlotForTradeItem(return_instance, client_search_general, client_search_bag);
@ -4478,23 +4538,23 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
bool slot_taken = false;
for (const auto& check_iterator : client_return) {
if (check_iterator.fromBotSlot == return_iterator.fromBotSlot)
if (check_iterator.from_bot_slot == return_iterator.from_bot_slot) {
continue;
}
if ((check_iterator.toClientSlot == client_test_slot) && (client_test_slot != invslot::slotCursor)) {
if (check_iterator.to_client_slot == client_test_slot && client_test_slot != invslot::slotCursor) {
slot_taken = true;
break;
}
}
if (slot_taken) {
if ((client_test_slot >= invslot::GENERAL_BEGIN) && (client_test_slot <= invslot::GENERAL_END)) {
if (client_test_slot >= invslot::GENERAL_BEGIN && client_test_slot <= invslot::GENERAL_END) {
++client_search_general;
client_search_bag = invbag::SLOT_BEGIN;
}
else {
} else {
client_search_general = InventoryProfile::CalcSlotId(client_test_slot);
client_search_bag = InventoryProfile::CalcBagIdx(client_test_slot);
++client_search_bag;
if (client_search_bag >= invbag::SLOT_COUNT) {
// incrementing this past legacy::GENERAL_END triggers the (client_test_slot == legacy::SLOT_INVALID) at the beginning of the search loop
@ -4507,13 +4567,13 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
continue;
}
return_iterator.toClientSlot = client_test_slot;
return_iterator.to_client_slot = client_test_slot;
run_search = false;
}
}
if (return_iterator.toClientSlot == invslot::SLOT_INVALID) {
client->Message(Chat::Yellow, "You do not have room to complete this trade - Trade Canceled!");
if (return_iterator.to_client_slot == invslot::SLOT_INVALID) {
client->Message(Chat::Yellow, "You do not have room to complete this trade, the trade has been cancelled!");
client->ResetTrade();
return;
}
@ -4524,70 +4584,105 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
for (auto& return_iterator : client_return) {
// TODO: code for stackables
if (return_iterator.fromBotSlot == invslot::slotCursor) { // failed trade return
if (return_iterator.from_bot_slot == invslot::slotCursor) { // failed trade return
// no movement action required
}
else if ((return_iterator.fromBotSlot >= invslot::TRADE_BEGIN) && (return_iterator.fromBotSlot <= invslot::TRADE_END)) { // failed trade returns
client->PutItemInInventory(return_iterator.toClientSlot, *return_iterator.returnItemInstance);
client->SendItemPacket(return_iterator.toClientSlot, return_iterator.returnItemInstance, ItemPacketTrade);
client->DeleteItemInInventory(return_iterator.fromBotSlot);
}
else { // successful trade returns
auto return_instance = m_inv.PopItem(return_iterator.fromBotSlot);
//if (*return_instance != *return_iterator.returnItemInstance) {
} else if ((return_iterator.from_bot_slot >= invslot::TRADE_BEGIN) && (return_iterator.from_bot_slot <= invslot::TRADE_END)) { // failed trade returns
client->PutItemInInventory(return_iterator.to_client_slot, *return_iterator.return_item_instance);
client->SendItemPacket(return_iterator.to_client_slot, return_iterator.return_item_instance, ItemPacketTrade);
client->DeleteItemInInventory(return_iterator.from_bot_slot);
} else { // successful trade returns
auto return_instance = m_inv.PopItem(return_iterator.from_bot_slot);
//if (*return_instance != *return_iterator.return_item_instance) {
// // TODO: add logging
//}
if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.fromBotSlot))
client->Message(Chat::Red, "%s (slot: %i, name: '%s')", BotDatabase::fail::DeleteItemBySlot(), return_iterator.fromBotSlot, (return_instance ? return_instance->GetItem()->Name : "nullptr"));
if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.from_bot_slot)) {
client->Message(
Chat::Red,
fmt::format(
"Failed to delete item by slot from slot {} for {}.",
return_iterator.from_bot_slot,
GetCleanName()
).c_str()
);
}
BotRemoveEquipItem(return_iterator.from_bot_slot);
if (return_instance) {
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
linker.SetItemInst(return_instance);
auto item_link = linker.GenerateLink();
client->Message(
Chat::Tell,
fmt::format(
"{} tells you, 'I have returned {}.'",
GetCleanName(),
item_link
).c_str()
);
client->PutItemInInventory(return_iterator.to_client_slot, *return_instance, true);
}
BotRemoveEquipItem(return_iterator.fromBotSlot);
if (return_instance)
client->PutItemInInventory(return_iterator.toClientSlot, *return_instance, true);
InventoryProfile::MarkDirty(return_instance);
}
return_iterator.returnItemInstance = nullptr;
return_iterator.return_item_instance = nullptr;
}
// trades can now go in as empty slot inserts
for (auto& trade_iterator : client_trade) {
// TODO: code for stackables
if (!database.botdb.SaveItemBySlot(this, trade_iterator.toBotSlot, trade_iterator.tradeItemInstance))
client->Message(Chat::Red, "%s (slot: %i, name: '%s')", BotDatabase::fail::SaveItemBySlot(), trade_iterator.toBotSlot, (trade_iterator.tradeItemInstance ? trade_iterator.tradeItemInstance->GetItem()->Name : "nullptr"));
if (!database.botdb.SaveItemBySlot(this, trade_iterator.to_bot_slot, trade_iterator.trade_item_instance)) {
client->Message(
Chat::Red,
fmt::format(
"Failed to save item by slot to slot {} for {}.",
trade_iterator.to_bot_slot,
GetCleanName()
).c_str()
);
}
m_inv.PutItem(trade_iterator.toBotSlot, *trade_iterator.tradeItemInstance);
BotAddEquipItem(trade_iterator.toBotSlot, (trade_iterator.tradeItemInstance ? trade_iterator.tradeItemInstance->GetID() : 0));
trade_iterator.tradeItemInstance = nullptr; // actual deletion occurs in client delete below
EQ::SayLinkEngine linker;
linker.SetLinkType(EQ::saylink::SayLinkItemInst);
linker.SetItemInst(trade_iterator.trade_item_instance);
auto item_link = linker.GenerateLink();
client->DeleteItemInInventory(trade_iterator.fromClientSlot, 0, (trade_iterator.fromClientSlot == EQ::invslot::slotCursor));
client->Message(
Chat::Tell,
fmt::format(
"{} tells you, 'I have accepted {}.'",
GetCleanName(),
item_link
).c_str()
);
m_inv.PutItem(trade_iterator.to_bot_slot, *trade_iterator.trade_item_instance);
BotAddEquipItem(trade_iterator.to_bot_slot, (trade_iterator.trade_item_instance ? trade_iterator.trade_item_instance->GetID() : 0));
trade_iterator.trade_item_instance = nullptr; // actual deletion occurs in client delete below
client->DeleteItemInInventory(trade_iterator.from_client_slot, 0, (trade_iterator.from_client_slot == EQ::invslot::slotCursor));
// database currently has unattuned item saved in inventory..it will be attuned on next bot load
// this prevents unattuned item returns in the mean time (TODO: re-work process)
if (trade_iterator.toBotSlot >= invslot::EQUIPMENT_BEGIN && trade_iterator.toBotSlot <= invslot::EQUIPMENT_END) {
auto attune_item = m_inv.GetItem(trade_iterator.toBotSlot);
if (attune_item && attune_item->GetItem()->Attuneable)
if (trade_iterator.to_bot_slot >= invslot::EQUIPMENT_BEGIN && trade_iterator.to_bot_slot <= invslot::EQUIPMENT_END) {
auto attune_item = m_inv.GetItem(trade_iterator.to_bot_slot);
if (attune_item && attune_item->GetItem()->Attuneable) {
attune_item->SetAttuned(true);
}
}
}
// trade messages
for (const auto& return_iterator : client_return) {
if (return_iterator.failedItemName.size())
client->Message(Chat::Tell, "%s tells you, \"%s, I can't use this '%s.'\"", GetCleanName(), client->GetName(), return_iterator.failedItemName.c_str());
}
for (const auto& trade_iterator : client_trade) {
if (trade_iterator.acceptedItemName.size())
client->Message(Chat::Tell, "%s tells you, \"Thank you for the '%s,' %s!\"", GetCleanName(), trade_iterator.acceptedItemName.c_str(), client->GetName());
}
size_t accepted_count = client_trade.size();
size_t returned_count = client_return.size();
client->Message(Chat::Lime, "Trade with '%s' resulted in %i accepted item%s, %i returned item%s.", GetCleanName(), accepted_count, ((accepted_count == 1) ? "" : "s"), returned_count, ((returned_count == 1) ? "" : "s"));
if (accepted_count)
if (accepted_count) {
CalcBotStats(client->GetBotOption(Client::booStatsUpdate));
}
}
bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) {

View File

@ -602,7 +602,7 @@ protected:
virtual int32 GetBotFocusEffect(focusType bottype, uint16 spell_id, bool from_buff_tic = false);
virtual int32 CalcBotFocusEffect(focusType bottype, uint16 focus_id, uint16 spell_id, bool best_focus=false);
virtual int32 CalcBotAAFocus(focusType type, uint32 aa_ID, uint32 points, uint16 spell_id);
virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client);
virtual void PerformTradeWithClient(int16 begin_slot_id, int16 end_slot_id, Client* client);
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
BotCastingRoles& GetCastingRoles() { return m_CastingRoles; }