2025-03-18 19:19:03 -04:00

346 lines
10 KiB
C++

/*
* Copyright (C) 2016+ AzerothCore <www.azerothcore.org>, released under GNU GPL v2 license, you may redistribute it
* and/or modify it under version 2 of the License, or (at your option), any later version.
*/
#include "TradeStatusAction.h"
#include "CraftValue.h"
#include "Event.h"
#include "GuildTaskMgr.h"
#include "ItemUsageValue.h"
#include "ItemVisitors.h"
#include "PlayerbotMgr.h"
#include "PlayerbotSecurity.h"
#include "Playerbots.h"
#include "RandomPlayerbotMgr.h"
#include "SetCraftAction.h"
bool TradeStatusAction::Execute(Event event)
{
Player* trader = bot->GetTrader();
Player* master = GetMaster();
if (!trader)
return false;
PlayerbotAI* traderBotAI = GET_PLAYERBOT_AI(trader);
if (trader != master && !traderBotAI)
{
bot->Whisper("I'm kind of busy now", LANG_UNIVERSAL, trader);
}
if ((trader != master || !botAI->GetSecurity()->CheckLevelFor(PLAYERBOT_SECURITY_ALLOW_ALL, true, master)) &&
!traderBotAI)
{
WorldPacket p;
uint32 status = 0;
p << status;
bot->GetSession()->HandleCancelTradeOpcode(p);
return false;
}
WorldPacket p(event.getPacket());
p.rpos(0);
uint32 status;
p >> status;
if (status == TRADE_STATUS_TRADE_ACCEPT)
{
WorldPacket p;
uint32 status = 0;
p << status;
uint32 discount = sRandomPlayerbotMgr->GetTradeDiscount(bot, trader);
if (CheckTrade())
{
int32 botMoney = CalculateCost(bot, true);
std::map<uint32, uint32> givenItemIds, takenItemIds;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{
Item* item = trader->GetTradeData()->GetItem((TradeSlots)slot);
if (item)
givenItemIds[item->GetTemplate()->ItemId] += item->GetCount();
item = bot->GetTradeData()->GetItem((TradeSlots)slot);
if (item)
takenItemIds[item->GetTemplate()->ItemId] += item->GetCount();
}
bot->GetSession()->HandleAcceptTradeOpcode(p);
if (bot->GetTradeData())
{
sRandomPlayerbotMgr->SetTradeDiscount(bot, trader, discount);
return false;
}
for (std::map<uint32, uint32>::iterator i = givenItemIds.begin(); i != givenItemIds.end(); ++i)
{
uint32 itemId = i->first;
uint32 count = i->second;
CraftData& craftData = AI_VALUE(CraftData&, "craft");
if (!craftData.IsEmpty() && craftData.IsRequired(itemId))
{
craftData.AddObtained(itemId, count);
}
sGuildTaskMgr->CheckItemTask(itemId, count, trader, bot);
}
for (std::map<uint32, uint32>::iterator i = takenItemIds.begin(); i != takenItemIds.end(); ++i)
{
uint32 itemId = i->first;
uint32 count = i->second;
CraftData& craftData = AI_VALUE(CraftData&, "craft");
if (!craftData.IsEmpty() && craftData.itemId == itemId)
{
craftData.Crafted(count);
}
}
return true;
}
}
else if (status == TRADE_STATUS_BEGIN_TRADE)
{
if (!bot->HasInArc(CAST_ANGLE_IN_FRONT, trader, sPlayerbotAIConfig->sightDistance))
bot->SetFacingToObject(trader);
BeginTrade();
return true;
}
return false;
}
void TradeStatusAction::BeginTrade()
{
WorldPacket p;
bot->GetSession()->HandleBeginTradeOpcode(p);
if (GET_PLAYERBOT_AI(bot->GetTrader()))
return;
ListItemsVisitor visitor;
IterateItems(&visitor);
botAI->TellMaster("=== Inventory ===");
TellItems(visitor.items, visitor.soulbound);
if (sRandomPlayerbotMgr->IsRandomBot(bot))
{
uint32 discount = sRandomPlayerbotMgr->GetTradeDiscount(bot, botAI->GetMaster());
if (discount)
{
std::ostringstream out;
out << "Discount up to: " << chat->formatMoney(discount);
botAI->TellMaster(out);
}
}
}
bool TradeStatusAction::CheckTrade()
{
Player* trader = bot->GetTrader();
if (!bot->GetTradeData() || !trader->GetTradeData())
return false;
if (!botAI->HasActivePlayerMaster() && GET_PLAYERBOT_AI(bot->GetTrader()))
{
bool isGivingItem = false;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{
Item* item = bot->GetTradeData()->GetItem((TradeSlots)slot);
if (item)
{
isGivingItem = true;
break;
}
}
bool isGettingItem = false;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{
Item* item = trader->GetTradeData()->GetItem((TradeSlots)slot);
if (item)
{
isGettingItem = true;
break;
}
}
if (isGettingItem)
{
if (bot->GetGroup() && bot->GetGroup()->IsMember(bot->GetTrader()->GetGUID()) &&
botAI->HasRealPlayerMaster())
botAI->TellMasterNoFacing("Thank you " + chat->FormatWorldobject(bot->GetTrader()));
else
bot->Say("Thank you " + chat->FormatWorldobject(bot->GetTrader()),
(bot->GetTeamId() == TEAM_ALLIANCE ? LANG_COMMON : LANG_ORCISH));
}
return isGettingItem;
}
if (!bot->GetSession())
{
return false;
}
uint32 accountId = bot->GetSession()->GetAccountId();
if (!sPlayerbotAIConfig->IsInRandomAccountList(accountId))
{
int32 botItemsMoney = CalculateCost(bot, true);
int32 botMoney = bot->GetTradeData()->GetMoney() + botItemsMoney;
int32 playerItemsMoney = CalculateCost(trader, false);
int32 playerMoney = trader->GetTradeData()->GetMoney() + playerItemsMoney;
if (playerMoney || botMoney)
botAI->PlaySound(playerMoney < botMoney ? TEXT_EMOTE_SIGH : TEXT_EMOTE_THANK);
return true;
}
int32 botItemsMoney = CalculateCost(bot, true);
int32 botMoney = bot->GetTradeData()->GetMoney() + botItemsMoney;
int32 playerItemsMoney = CalculateCost(trader, false);
int32 playerMoney = trader->GetTradeData()->GetMoney() + playerItemsMoney;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{
Item* item = bot->GetTradeData()->GetItem((TradeSlots)slot);
if (item && !item->GetTemplate()->SellPrice && !item->GetTemplate()->IsConjuredConsumable())
{
std::ostringstream out;
out << chat->FormatItem(item->GetTemplate()) << " - This is not for sale";
botAI->TellMaster(out);
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}
item = trader->GetTradeData()->GetItem((TradeSlots)slot);
if (item)
{
std::ostringstream out;
out << item->GetTemplate()->ItemId;
ItemUsage usage = AI_VALUE2(ItemUsage, "item usage", out.str());
if ((botMoney && !item->GetTemplate()->BuyPrice) || usage == ITEM_USAGE_NONE)
{
std::ostringstream out;
out << chat->FormatItem(item->GetTemplate()) << " - I don't need this";
botAI->TellMaster(out);
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}
}
}
if (!botMoney && !playerMoney)
return true;
if (!botItemsMoney && !playerItemsMoney)
{
botAI->TellError("There are no items to trade");
return false;
}
int32 discount = (int32)sRandomPlayerbotMgr->GetTradeDiscount(bot, trader);
int32 delta = playerMoney - botMoney;
int32 moneyDelta = (int32)trader->GetTradeData()->GetMoney() - (int32)bot->GetTradeData()->GetMoney();
bool success = false;
if (delta < 0)
{
if (delta + discount >= 0)
{
if (moneyDelta < 0)
{
botAI->TellError("You can use discount to buy items only");
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}
success = true;
}
}
else
{
success = true;
}
if (success)
{
sRandomPlayerbotMgr->AddTradeDiscount(bot, trader, delta);
switch (urand(0, 4))
{
case 0:
botAI->TellMaster("A pleasure doing business with you");
break;
case 1:
botAI->TellMaster("Fair trade");
break;
case 2:
botAI->TellMaster("Thanks");
break;
case 3:
botAI->TellMaster("Off with you");
break;
}
botAI->PlaySound(TEXT_EMOTE_THANK);
return true;
}
std::ostringstream out;
out << "I want " << chat->formatMoney(-(delta + discount)) << " for this";
botAI->TellMaster(out);
botAI->PlaySound(TEXT_EMOTE_NO);
return false;
}
int32 TradeStatusAction::CalculateCost(Player* player, bool sell)
{
Player* trader = bot->GetTrader();
TradeData* data = player->GetTradeData();
if (!data)
return 0;
uint32 sum = 0;
for (uint32 slot = 0; slot < TRADE_SLOT_TRADED_COUNT; ++slot)
{
Item* item = data->GetItem((TradeSlots)slot);
if (!item)
continue;
ItemTemplate const* proto = item->GetTemplate();
if (!proto)
continue;
if (proto->Quality < ITEM_QUALITY_NORMAL)
return 0;
CraftData& craftData = AI_VALUE(CraftData&, "craft");
if (!craftData.IsEmpty())
{
if (player == trader && !sell && craftData.IsRequired(proto->ItemId))
{
continue;
}
if (player == bot && sell && craftData.itemId == proto->ItemId && craftData.IsFulfilled())
{
sum += item->GetCount() * SetCraftAction::GetCraftFee(craftData);
continue;
}
}
if (sell)
{
sum += item->GetCount() * proto->SellPrice * sRandomPlayerbotMgr->GetSellMultiplier(bot);
}
else
{
sum += item->GetCount() * proto->BuyPrice * sRandomPlayerbotMgr->GetBuyMultiplier(bot);
}
}
return sum;
}