[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 // 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; using namespace EQ;
struct ClientTrade { struct ClientTrade {
const ItemInstance* tradeItemInstance; const ItemInstance* trade_item_instance;
int16 fromClientSlot; int16 from_client_slot;
int16 toBotSlot; int16 to_bot_slot;
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) { } ClientTrade(const ItemInstance* item, int16 from) : trade_item_instance(item), from_client_slot(from), to_bot_slot(invslot::SLOT_INVALID) { }
}; };
struct ClientReturn { struct ClientReturn {
const ItemInstance* returnItemInstance; const ItemInstance* return_item_instance;
int16 fromBotSlot; int16 from_bot_slot;
int16 toClientSlot; int16 to_client_slot;
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) { } 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] = { 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()) { 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(); client->ResetTrade();
return; 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(); client->ResetTrade();
return; 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(); client->ResetTrade();
return; 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(); client->ResetTrade();
return; 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(); client->ResetTrade();
return; return;
} }
if (client->IsEngaged() || IsEngaged()) { 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(); client->ResetTrade();
return; return;
} }
@ -4281,66 +4282,100 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
std::list<ClientReturn> client_return; std::list<ClientReturn> client_return;
// pre-checks for incoming illegal transfers // 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]; auto trade_instance = client->GetInv()[trade_index];
if (!trade_instance) if (!trade_instance) {
continue; continue;
}
if (!trade_instance->GetItem()) { if (!trade_instance->GetItem()) {
// TODO: add logging LogError("Bot::PerformTradeWithClient could not find item from instance in trade for {} with {} in slot {}.", client->GetCleanName(), GetCleanName(), trade_index);
client->Message(Chat::Red, "A server error was encountered while processing client slot %i - Trade Canceled.", 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(); client->ResetTrade();
return; return;
} }
if ((trade_index != invslot::slotCursor) && !trade_instance->IsDroppable()) {
// TODO: add logging EQ::SayLinkEngine linker;
client->Message(Chat::Red, "Trade hack detected - Trade Canceled."); 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(); client->ResetTrade();
return; return;
} }
if (trade_instance->IsStackable() && (trade_instance->GetCharges() < trade_instance->GetItem()->StackSize)) { // temp until partial stacks are implemented 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(); client->ResetTrade();
return; return;
} }
if (CheckLoreConflict(trade_instance->GetItem())) { 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(); client->ResetTrade();
return; return;
} }
if (!trade_instance->IsType(item::ItemClassCommon)) { if (!trade_instance->IsType(item::ItemClassCommon)) {
client_return.push_back(ClientReturn(trade_instance, trade_index, trade_instance->GetItem()->Name)); client_return.push_back(ClientReturn(trade_instance, trade_index));
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));
continue; 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 // check for incoming lore hacks
for (auto& trade_iterator : client_trade) { for (auto& trade_iterator : client_trade) {
if (!trade_iterator.tradeItemInstance->GetItem()->LoreFlag) if (!trade_iterator.trade_item_instance->GetItem()->LoreFlag) {
continue; continue;
}
for (const auto& check_iterator : client_trade) { 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.tradeItemInstance->GetItem()->LoreFlag)
continue; continue;
}
if ((trade_iterator.tradeItemInstance->GetItem()->LoreGroup == -1) && (check_iterator.tradeItemInstance->GetItem()->ID == trade_iterator.tradeItemInstance->GetItem()->ID)) { if (!check_iterator.trade_item_instance->GetItem()->LoreFlag) {
// TODO: add logging continue;
client->Message(Chat::Red, "Trade hack detected - Trade Canceled."); }
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(); client->ResetTrade();
return; return;
} }
if ((trade_iterator.tradeItemInstance->GetItem()->LoreGroup > 0) && (check_iterator.tradeItemInstance->GetItem()->LoreGroup == trade_iterator.tradeItemInstance->GetItem()->LoreGroup)) {
// TODO: add logging if ((trade_iterator.trade_item_instance->GetItem()->LoreGroup > 0) && (check_iterator.trade_item_instance->GetItem()->LoreGroup == trade_iterator.trade_item_instance->GetItem()->LoreGroup)) {
client->Message(Chat::Red, "Trade hack detected - Trade Canceled."); 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(); client->ResetTrade();
return; 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 = stageStackable; stage_loop <= stageReplaceable; ++stage_loop) { // awaiting implementation
for (unsigned stage_loop = stageEmpty; stage_loop <= stageReplaceable; ++stage_loop) { for (unsigned stage_loop = stageEmpty; stage_loop <= stageReplaceable; ++stage_loop) {
for (auto& trade_iterator : client_trade) { for (auto& trade_iterator : client_trade) {
if (trade_iterator.toBotSlot != invslot::SLOT_INVALID) if (trade_iterator.to_bot_slot != invslot::SLOT_INVALID) {
continue; continue;
}
auto trade_instance = trade_iterator.tradeItemInstance; auto trade_instance = trade_iterator.trade_item_instance;
//if ((stage_loop == stageStackable) && !trade_instance->IsStackable()) //if ((stage_loop == stageStackable) && !trade_instance->IsStackable())
// continue; // continue;
for (auto index : bot_equip_order) { for (auto index : bot_equip_order) {
if (!(trade_instance->GetItem()->Slots & (1 << index))) if (!(trade_instance->GetItem()->Slots & (1 << index))) {
continue; continue;
}
//if (stage_loop == stageStackable) { //if (stage_loop == stageStackable) {
// // TODO: implement // // TODO: implement
@ -4372,59 +4409,64 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
//} //}
if (stage_loop != stageReplaceable) { if (stage_loop != stageReplaceable) {
if (m_inv[index]) if (m_inv[index]) {
continue; continue;
}
} }
bool slot_taken = false; bool slot_taken = false;
for (const auto& check_iterator : client_trade) { 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; continue;
}
if (check_iterator.toBotSlot == index) { if (check_iterator.to_bot_slot == index) {
slot_taken = true; slot_taken = true;
break; break;
} }
} }
if (slot_taken)
if (slot_taken) {
continue; continue;
}
if (index == invslot::slotPrimary) { if (index == invslot::slotPrimary) {
if (trade_instance->GetItem()->IsType2HWeapon()) { if (trade_instance->GetItem()->IsType2HWeapon()) {
if (!melee_secondary) { if (!melee_secondary) {
melee_2h_weapon = true; melee_2h_weapon = true;
auto equipped_secondary_weapon = m_inv[invslot::slotSecondary]; 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)); client_return.push_back(ClientReturn(equipped_secondary_weapon, invslot::slotSecondary));
} }
else { } else {
continue; continue;
} }
} }
} } else if (index == invslot::slotSecondary) {
if (index == invslot::slotSecondary) {
if (!melee_2h_weapon) { 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; melee_secondary = true;
auto equipped_primary_weapon = m_inv[invslot::slotPrimary]; 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)); client_return.push_back(ClientReturn(equipped_primary_weapon, invslot::slotPrimary));
} }
else { } else {
continue; continue;
} }
} } else {
else {
continue; 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)); client_return.push_back(ClientReturn(m_inv[index], index));
}
break; break;
} }
@ -4433,8 +4475,8 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
// move unassignable items from trade list to return list // move unassignable items from trade list to return list
for (std::list<ClientTrade>::iterator trade_iterator = client_trade.begin(); trade_iterator != client_trade.end();) { for (std::list<ClientTrade>::iterator trade_iterator = client_trade.begin(); trade_iterator != client_trade.end();) {
if (trade_iterator->toBotSlot == invslot::SLOT_INVALID) { if (trade_iterator->to_bot_slot == invslot::SLOT_INVALID) {
client_return.push_back(ClientReturn(trade_iterator->tradeItemInstance, trade_iterator->fromClientSlot, trade_iterator->tradeItemInstance->GetItem()->Name)); client_return.push_back(ClientReturn(trade_iterator->trade_item_instance, trade_iterator->from_client_slot));
trade_iterator = client_trade.erase(trade_iterator); trade_iterator = client_trade.erase(trade_iterator);
continue; continue;
} }
@ -4443,31 +4485,49 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
// out-going return checks for client // out-going return checks for client
for (auto& return_iterator : client_return) { for (auto& return_iterator : client_return) {
auto return_instance = return_iterator.returnItemInstance; auto return_instance = return_iterator.return_item_instance;
if (!return_instance) if (!return_instance) {
continue; continue;
}
if (!return_instance->GetItem()) { if (!return_instance->GetItem()) {
// TODO: add logging LogError("Bot::PerformTradeWithClient error processing bot slot {} for {} in trade with {}.", return_iterator.from_bot_slot, GetCleanName(), client->GetCleanName());
client->Message(Chat::Red, "A server error was encountered while processing bot slot %i - Trade Canceled.", return_iterator.fromBotSlot); 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(); client->ResetTrade();
return; 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}) // 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) // this process is hindered by not having bots use the inventory trade method (TODO: implement bot inventory use)
if (client->CheckLoreConflict(return_instance->GetItem())) { 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(); client->ResetTrade();
return; return;
} }
if (return_iterator.fromBotSlot == invslot::slotCursor) { if (return_iterator.from_bot_slot == invslot::slotCursor) {
return_iterator.toClientSlot = invslot::slotCursor; return_iterator.to_client_slot = invslot::slotCursor;
} } else {
else {
int16 client_search_general = invslot::GENERAL_BEGIN; int16 client_search_general = invslot::GENERAL_BEGIN;
uint8 client_search_bag = invbag::SLOT_BEGIN; uint8 client_search_bag = invbag::SLOT_BEGIN;
bool run_search = true; bool run_search = true;
while (run_search) { while (run_search) {
int16 client_test_slot = client->GetInv().FindFreeSlotForTradeItem(return_instance, client_search_general, client_search_bag); 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; bool slot_taken = false;
for (const auto& check_iterator : client_return) { 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; 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; slot_taken = true;
break; break;
} }
} }
if (slot_taken) { 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_general;
client_search_bag = invbag::SLOT_BEGIN; client_search_bag = invbag::SLOT_BEGIN;
} } else {
else {
client_search_general = InventoryProfile::CalcSlotId(client_test_slot); client_search_general = InventoryProfile::CalcSlotId(client_test_slot);
client_search_bag = InventoryProfile::CalcBagIdx(client_test_slot); client_search_bag = InventoryProfile::CalcBagIdx(client_test_slot);
++client_search_bag; ++client_search_bag;
if (client_search_bag >= invbag::SLOT_COUNT) { 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 // 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; continue;
} }
return_iterator.toClientSlot = client_test_slot; return_iterator.to_client_slot = client_test_slot;
run_search = false; run_search = false;
} }
} }
if (return_iterator.toClientSlot == invslot::SLOT_INVALID) { if (return_iterator.to_client_slot == invslot::SLOT_INVALID) {
client->Message(Chat::Yellow, "You do not have room to complete this trade - Trade Canceled!"); client->Message(Chat::Yellow, "You do not have room to complete this trade, the trade has been cancelled!");
client->ResetTrade(); client->ResetTrade();
return; return;
} }
@ -4524,70 +4584,105 @@ void Bot::PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* cli
for (auto& return_iterator : client_return) { for (auto& return_iterator : client_return) {
// TODO: code for stackables // 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 // no movement action required
} } else if ((return_iterator.from_bot_slot >= invslot::TRADE_BEGIN) && (return_iterator.from_bot_slot <= invslot::TRADE_END)) { // failed trade returns
else if ((return_iterator.fromBotSlot >= invslot::TRADE_BEGIN) && (return_iterator.fromBotSlot <= invslot::TRADE_END)) { // failed trade returns client->PutItemInInventory(return_iterator.to_client_slot, *return_iterator.return_item_instance);
client->PutItemInInventory(return_iterator.toClientSlot, *return_iterator.returnItemInstance); client->SendItemPacket(return_iterator.to_client_slot, return_iterator.return_item_instance, ItemPacketTrade);
client->SendItemPacket(return_iterator.toClientSlot, return_iterator.returnItemInstance, ItemPacketTrade); client->DeleteItemInInventory(return_iterator.from_bot_slot);
client->DeleteItemInInventory(return_iterator.fromBotSlot); } else { // successful trade returns
} auto return_instance = m_inv.PopItem(return_iterator.from_bot_slot);
else { // successful trade returns //if (*return_instance != *return_iterator.return_item_instance) {
auto return_instance = m_inv.PopItem(return_iterator.fromBotSlot);
//if (*return_instance != *return_iterator.returnItemInstance) {
// // TODO: add logging // // TODO: add logging
//} //}
if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.fromBotSlot)) if (!database.botdb.DeleteItemBySlot(GetBotID(), return_iterator.from_bot_slot)) {
client->Message(Chat::Red, "%s (slot: %i, name: '%s')", BotDatabase::fail::DeleteItemBySlot(), return_iterator.fromBotSlot, (return_instance ? return_instance->GetItem()->Name : "nullptr")); 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); InventoryProfile::MarkDirty(return_instance);
} }
return_iterator.returnItemInstance = nullptr; return_iterator.return_item_instance = nullptr;
} }
// trades can now go in as empty slot inserts // trades can now go in as empty slot inserts
for (auto& trade_iterator : client_trade) { for (auto& trade_iterator : client_trade) {
// TODO: code for stackables // TODO: code for stackables
if (!database.botdb.SaveItemBySlot(this, trade_iterator.toBotSlot, trade_iterator.tradeItemInstance)) if (!database.botdb.SaveItemBySlot(this, trade_iterator.to_bot_slot, trade_iterator.trade_item_instance)) {
client->Message(Chat::Red, "%s (slot: %i, name: '%s')", BotDatabase::fail::SaveItemBySlot(), trade_iterator.toBotSlot, (trade_iterator.tradeItemInstance ? trade_iterator.tradeItemInstance->GetItem()->Name : "nullptr")); 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); EQ::SayLinkEngine linker;
BotAddEquipItem(trade_iterator.toBotSlot, (trade_iterator.tradeItemInstance ? trade_iterator.tradeItemInstance->GetID() : 0)); linker.SetLinkType(EQ::saylink::SayLinkItemInst);
trade_iterator.tradeItemInstance = nullptr; // actual deletion occurs in client delete below 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 // 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) // 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) { 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.toBotSlot); auto attune_item = m_inv.GetItem(trade_iterator.to_bot_slot);
if (attune_item && attune_item->GetItem()->Attuneable) if (attune_item && attune_item->GetItem()->Attuneable) {
attune_item->SetAttuned(true); 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 accepted_count = client_trade.size();
size_t returned_count = client_return.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)); CalcBotStats(client->GetBotOption(Client::booStatsUpdate));
}
} }
bool Bot::Death(Mob *killerMob, int32 damage, uint16 spell_id, EQ::skills::SkillType attack_skill) { 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 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 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 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); virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
BotCastingRoles& GetCastingRoles() { return m_CastingRoles; } BotCastingRoles& GetCastingRoles() { return m_CastingRoles; }