Trade.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599
  1. #include "Trade.h"
  2. #include "Items/Items.h"
  3. #include "Entity.h"
  4. #include "Bots/Bot.h"
  5. #include "../common/Log.h"
  6. #include "Rules/Rules.h"
  7. extern ConfigReader configReader;
  8. extern MasterItemList master_item_list;
  9. extern RuleManager rule_manager;
  10. Trade::Trade(Entity* trader1, Entity* trader2) {
  11. this->trader1 = trader1;
  12. this->trader2 = trader2;
  13. trade_max_slots = 12;
  14. if(trader1->IsPlayer()) {
  15. if(((Player*)trader1)->GetClient() && ((Player*)trader1)->GetClient()->GetVersion() <= 547) {
  16. trade_max_slots = 6;
  17. }
  18. }
  19. if(trader2->IsPlayer()) {
  20. if(((Player*)trader2)->GetClient() && ((Player*)trader2)->GetClient()->GetVersion() <= 547) {
  21. trade_max_slots = 6;
  22. }
  23. }
  24. trader1_accepted = false;
  25. trader2_accepted = false;
  26. trader1_coins = 0;
  27. trader2_coins = 0;
  28. OpenTradeWindow();
  29. }
  30. Trade::~Trade() {
  31. }
  32. int8 Trade::AddItemToTrade(Entity* character, Item* item, int8 quantity, int8 slot) {
  33. LogWrite(PLAYER__ERROR, 0, "Trade", "Player (%s) adding item (%u) to slot %u of the trade window", character->GetName(), item->details.item_id, slot);
  34. if (slot == 255)
  35. slot = GetNextFreeSlot(character);
  36. if (slot < 0) {
  37. LogWrite(PLAYER__ERROR, 0, "Trade", "Player (%s) tried to add an item to an invalid trade slot (%u)", character->GetName(), slot);
  38. return 255;
  39. }
  40. else if(slot >= trade_max_slots) {
  41. return 254;
  42. }
  43. Entity* other = GetTradee(character);
  44. int8 result = CheckItem(character, item, other);
  45. if (result == 0) {
  46. if (character == trader1) {
  47. Trader1ItemAdd(item, quantity, slot);
  48. // Only trader2 can be a bot so only
  49. // need to do the bot check here
  50. if (trader2->IsBot()) {
  51. ((Bot*)trader2)->TradeItemAdded(item);
  52. }
  53. }
  54. else if (character == trader2)
  55. Trader2ItemAdd(item, quantity, slot);
  56. else {
  57. LogWrite(PLAYER__ERROR, 0, "Trade", "Player (%s) tried to add an item to a trade but was neither trader1 or trader2", character->GetName());
  58. return 255;
  59. }
  60. }
  61. SendTradePacket();
  62. return result;
  63. }
  64. int8 Trade::CheckItem(Entity* trader, Item* item, Entity* other) {
  65. int8 ret = 0;
  66. map<int8, TradeItemInfo>* list = 0;
  67. map<int8, TradeItemInfo>::iterator itr;
  68. bool other_is_bot = false;
  69. if(other)
  70. other_is_bot = other->IsBot();
  71. if (trader == trader1)
  72. list = &trader1_items;
  73. else if (trader == trader2)
  74. list = &trader2_items;
  75. if (list) {
  76. if (trader->IsPlayer()) {
  77. // Check to see if the item is already in the trade
  78. for (itr = list->begin(); itr != list->end(); itr++) {
  79. if (itr->second.item->details.unique_id == item->details.unique_id) {
  80. ret = 1;
  81. break;
  82. }
  83. }
  84. // Only allow heirloom and no-trade items to be traded with a bot
  85. if (!other_is_bot) {
  86. if (item->CheckFlag(NO_TRADE))
  87. ret = 2;
  88. if (item->CheckFlag2(HEIRLOOM)) {
  89. if(other->IsPlayer() && item->grouped_char_ids.find(((Player*)other)->GetCharacterID()) != item->grouped_char_ids.end()) {
  90. double diffInSeconds = 0.0; std::difftime(std::time(nullptr), item->created);
  91. if(item->CheckFlag(ATTUNED) || ((diffInSeconds = std::difftime(std::time(nullptr), item->created)) && diffInSeconds >= rule_manager.GetGlobalRule(R_Player, HeirloomItemShareExpiration)->GetFloat())) {
  92. ret = 3; // denied heirloom cannot be transferred to outside of group after 48 hours (by rule setting) or if already attuned
  93. }
  94. }
  95. else {
  96. ret = 3; // not part of the group/raid
  97. }
  98. }
  99. }
  100. }
  101. }
  102. return ret;
  103. }
  104. void Trade::RemoveItemFromTrade(Entity* character, int8 slot) {
  105. map<int8, TradeItemInfo>* list = 0;
  106. if (character == trader1)
  107. list = &trader1_items;
  108. else if (character == trader2)
  109. list = &trader2_items;
  110. if (list) {
  111. if (list->count(slot) > 0) {
  112. list->erase(slot);
  113. SendTradePacket();
  114. }
  115. else
  116. LogWrite(PLAYER__ERROR, 0, "Trade", "Player (%s) tried to remove an item from a trade slot that was empty ", character->GetName());
  117. }
  118. }
  119. void Trade::AddCoinToTrade(Entity* character, int64 amount) {
  120. if (!character->IsPlayer())
  121. return;
  122. if (character == trader1) {
  123. trader1_coins += amount;
  124. if (!((Player*)character)->HasCoins(trader1_coins)) {
  125. trader1_coins -= amount;
  126. LogWrite(PLAYER__ERROR, 0, "Trade", "Player (%s) tried to add more coins then they had ", character->GetName());
  127. }
  128. }
  129. else if (character == trader2) {
  130. trader2_coins += amount;
  131. if (!((Player*)character)->HasCoins(trader2_coins)) {
  132. trader2_coins -= amount;
  133. LogWrite(PLAYER__ERROR, 0, "Trade", "Player (%s) tried to add more coins then they had ", character->GetName());
  134. }
  135. }
  136. SendTradePacket();
  137. }
  138. void Trade::RemoveCoinFromTrade(Entity* character, int64 amount) {
  139. if (character == trader1)
  140. trader1_coins = (amount >= trader1_coins) ? 0 : trader1_coins - amount;
  141. else if (character == trader2)
  142. trader2_coins = (amount >= trader2_coins) ? 0 : trader2_coins - amount;
  143. SendTradePacket();
  144. }
  145. Entity* Trade::GetTradee(Entity* character) {
  146. if (character == trader1)
  147. return trader2;
  148. else if (character == trader2)
  149. return trader1;
  150. return 0;
  151. }
  152. bool Trade::SetTradeAccepted(Entity* character) {
  153. if (character == trader1)
  154. trader1_accepted = true;
  155. else if (character == trader2)
  156. trader2_accepted = true;
  157. else
  158. return false;
  159. Entity* other = GetTradee(character);
  160. if (other) {
  161. if (other->IsPlayer()) {
  162. Client* client = ((Player*)other)->GetClient();
  163. if(client) {
  164. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  165. if (packet) {
  166. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(character));
  167. packet->setDataByName("type", 16);
  168. client->QueuePacket(packet->serialize());
  169. safe_delete(packet);
  170. }
  171. }
  172. }
  173. else if (other->IsBot()) {
  174. Client* client = ((Player*)character)->GetClient();
  175. if (trader1_coins > 0) {
  176. CancelTrade(other);
  177. if (client)
  178. client->SimpleMessage(CHANNEL_ERROR, "Bots can't take coins so the trade was canceled.");
  179. return true;
  180. }
  181. else {
  182. if (!((Bot*)other)->CheckTradeItems(&trader1_items)) {
  183. CancelTrade(other);
  184. if (client)
  185. client->SimpleMessage(CHANNEL_ERROR, "There was an item the bot could not equip so the trade was canceled.");
  186. return true;
  187. }
  188. else
  189. trader2_accepted = true;
  190. }
  191. }
  192. if (HasAcceptedTrade(other)) {
  193. CompleteTrade();
  194. return true;
  195. }
  196. }
  197. return false;
  198. }
  199. bool Trade::HasAcceptedTrade(Entity* character) {
  200. if (character == trader1)
  201. return trader1_accepted;
  202. else if (character == trader2)
  203. return trader2_accepted;
  204. return false;
  205. }
  206. void Trade::CancelTrade(Entity* character) {
  207. Entity* other = GetTradee(character);
  208. if (other){
  209. if (other->IsPlayer()) {
  210. Client* client = ((Player*)other)->GetClient();
  211. if(client) {
  212. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  213. if (packet) {
  214. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(character));
  215. packet->setDataByName("type", 2);
  216. client->QueuePacket(packet->serialize());
  217. safe_delete(packet);
  218. }
  219. }
  220. }
  221. else if (other->IsBot())
  222. ((Bot*)other)->FinishTrade();
  223. }
  224. trader1->trade = 0;
  225. trader2->trade = 0;
  226. }
  227. void Trade::Trader1ItemAdd(Item* item, int8 quantity, int8 slot) {
  228. trader1_items[slot].item = item;
  229. trader1_items[slot].quantity = quantity;
  230. }
  231. void Trade::Trader2ItemAdd(Item* item, int8 quantity, int8 slot) {
  232. trader2_items[slot].item = item;
  233. trader2_items[slot].quantity = quantity;
  234. }
  235. Item* Trade::GetTraderSlot(Entity* trader, int8 slot) {
  236. if(trader == GetTrader1()) {
  237. map<int8, TradeItemInfo>::iterator itr = trader1_items.find(slot);
  238. if(itr != trader1_items.end()) {
  239. return itr->second.item;
  240. }
  241. }
  242. else if(trader == GetTrader2()) {
  243. map<int8, TradeItemInfo>::iterator itr = trader2_items.find(slot);
  244. if(itr != trader2_items.end()) {
  245. return itr->second.item;
  246. }
  247. }
  248. return nullptr;
  249. }
  250. void Trade::CompleteTrade() {
  251. map<int8, TradeItemInfo>::iterator itr;
  252. vector<Item*> trader1_item_pass;
  253. vector<Item*>::iterator itr2;
  254. string log_string = "TradeComplete:\n";
  255. if (trader1->IsPlayer()) {
  256. Player* player = (Player*)trader1;
  257. Client* client = ((Player*)player)->GetClient();
  258. if (client) {
  259. log_string += "Trader1 = ";
  260. log_string += trader1->GetName();
  261. log_string += "(" + to_string(client->GetCharacterID()) + ")\n";
  262. log_string += "Coins: " + to_string(trader1_coins) + "\n";
  263. log_string += "Items:\n";
  264. player->RemoveCoins(trader1_coins);
  265. for (itr = trader1_items.begin(); itr != trader1_items.end(); itr++) {
  266. // client->RemoveItem can delete the item so we need to store the item id's and quantity to give to trader2
  267. Item* newitem = new Item(itr->second.item);
  268. newitem->details.count = itr->second.quantity;
  269. trader1_item_pass.push_back(newitem);
  270. log_string += itr->second.item->name + " (" + to_string(itr->second.item->details.item_id) + ") x" + to_string(itr->second.quantity) + "\n";
  271. client->RemoveItem(itr->second.item, itr->second.quantity);
  272. }
  273. player->AddCoins(trader2_coins);
  274. for (itr = trader2_items.begin(); itr != trader2_items.end(); itr++) {
  275. Item* newitem = new Item(itr->second.item);
  276. newitem->details.count = itr->second.quantity;
  277. client->AddItem(newitem, nullptr);
  278. }
  279. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  280. if (packet) {
  281. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(trader2));
  282. packet->setDataByName("type", 24);
  283. client->QueuePacket(packet->serialize());
  284. safe_delete(packet);
  285. }
  286. }
  287. }
  288. // trader1 is the player who starts the trade, will never be a bot, if trader1 is not a player something went horribly wrong.
  289. if (trader2->IsPlayer()) {
  290. Player* player = (Player*)trader2;
  291. Client* client = ((Player*)player)->GetClient();
  292. if (client) {
  293. log_string += "Trader2 = ";
  294. log_string += trader2->GetName();
  295. log_string += "(" + to_string(client->GetCharacterID()) + ")\n";
  296. log_string += "Coins: " + to_string(trader2_coins) + "\n";
  297. log_string += "Items:\n";
  298. player->RemoveCoins(trader2_coins);
  299. for (itr = trader2_items.begin(); itr != trader2_items.end(); itr++) {
  300. log_string += itr->second.item->name + " (" + to_string(itr->second.item->details.item_id) + ") x" + to_string(itr->second.quantity) + "\n";
  301. client->RemoveItem(itr->second.item, itr->second.quantity);
  302. }
  303. player->AddCoins(trader1_coins);
  304. for (itr2 = trader1_item_pass.begin(); itr2 != trader1_item_pass.end(); itr2++) {
  305. client->AddItem(*itr2, nullptr);
  306. }
  307. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  308. if (packet) {
  309. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(trader1));
  310. packet->setDataByName("type", 24);
  311. client->QueuePacket(packet->serialize());
  312. safe_delete(packet);
  313. }
  314. }
  315. }
  316. else if (trader2->IsBot()) {
  317. Bot* bot = (Bot*)trader2;
  318. log_string += "Trader2 is a bot";
  319. for (itr = trader2_items.begin(); itr != trader2_items.end(); itr++) {
  320. bot->RemoveItem(itr->second.item);
  321. }
  322. for (itr2 = trader1_item_pass.begin(); itr2 != trader1_item_pass.end(); itr2++) {
  323. bot->GiveItem(*itr2);
  324. }
  325. bot->FinishTrade();
  326. }
  327. LogWrite(PLAYER__INFO, 0, "Trade", log_string.c_str());
  328. trader1->trade = 0;
  329. trader2->trade = 0;
  330. }
  331. void Trade::OpenTradeWindow() {
  332. if (trader1->IsPlayer()) {
  333. Client* client = ((Player*)trader1)->GetClient();
  334. if(client) {
  335. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  336. if (packet) {
  337. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(trader2));
  338. packet->setDataByName("type", 1);
  339. client->QueuePacket(packet->serialize());
  340. safe_delete(packet);
  341. }
  342. }
  343. }
  344. if (trader2->IsPlayer()) {
  345. Client* client = ((Player*)trader2)->GetClient();
  346. if(client) {
  347. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  348. if (packet) {
  349. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(trader1));
  350. packet->setDataByName("type", 1);
  351. client->QueuePacket(packet->serialize());
  352. safe_delete(packet);
  353. }
  354. }
  355. }
  356. }
  357. void Trade::SendTradePacket() {
  358. if (trader1->IsPlayer()) {
  359. Client* client = ((Player*)trader1)->GetClient();
  360. if(!client) {
  361. return;
  362. }
  363. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  364. if (packet) {
  365. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(trader2));
  366. packet->setDataByName("type", 1);
  367. int8 size = (int8)(trader1_items.size());
  368. int8 i = 0;
  369. map<int8, TradeItemInfo>::iterator itr;
  370. packet->setArrayLengthByName("your_item_count", size);
  371. for (itr = trader1_items.begin(); itr != trader1_items.end(); itr++) {
  372. packet->setArrayDataByName("your_item_unknown1", 1, i);
  373. packet->setArrayDataByName("your_item_unknown2", 1, i);
  374. packet->setArrayDataByName("your_item_slot", itr->first, i);
  375. packet->setArrayDataByName("your_item_id", itr->second.item->details.item_id, i);
  376. packet->setArrayDataByName("your_item_name", itr->second.item->name.c_str(), i);
  377. packet->setArrayDataByName("your_item_quantity", itr->second.quantity, i);
  378. packet->setArrayDataByName("your_item_icon", itr->second.item->details.icon, i);
  379. packet->setArrayDataByName("your_item_background", 0, i); // No clue on this value yet
  380. i++;
  381. }
  382. int32 plat = 0;
  383. int32 gold = 0;
  384. int32 silver = 0;
  385. int32 copper = 0;
  386. CalculateCoins(trader1_coins, plat, gold, silver, copper);
  387. packet->setDataByName("your_copper", copper);
  388. packet->setDataByName("your_silver", silver);
  389. packet->setDataByName("your_gold", gold);
  390. packet->setDataByName("your_plat", plat);
  391. size = (int8)(trader2_items.size());
  392. i = 0;
  393. packet->setArrayLengthByName("their_item_count", size);
  394. for (itr = trader2_items.begin(); itr != trader2_items.end(); itr++) {
  395. packet->setArrayDataByName("their_item_unknown1", 1, i);
  396. packet->setArrayDataByName("their_item_unknown2", 1, i);
  397. packet->setArrayDataByName("their_item_slot", itr->first, i);
  398. packet->setArrayDataByName("their_item_id", itr->second.item->details.item_id, i);
  399. packet->setArrayDataByName("their_item_name", itr->second.item->name.c_str(), i);
  400. packet->setArrayDataByName("their_item_quantity", itr->second.quantity, i);
  401. packet->setArrayDataByName("their_item_icon", itr->second.item->details.icon, i);
  402. packet->setArrayDataByName("their_item_background", 0, i); // No clue on this value yet
  403. i++;
  404. }
  405. plat = 0;
  406. gold = 0;
  407. silver = 0;
  408. copper = 0;
  409. CalculateCoins(trader2_coins, plat, gold, silver, copper);
  410. packet->setDataByName("their_copper", copper);
  411. packet->setDataByName("their_silver", silver);
  412. packet->setDataByName("their_gold", gold);
  413. packet->setDataByName("their_plat", plat);
  414. LogWrite(PLAYER__ERROR, 0, "Trade", "packet sent");
  415. client->QueuePacket(packet->serialize());
  416. safe_delete(packet);
  417. }
  418. }
  419. if (trader2->IsPlayer()) {
  420. Client* client = ((Player*)trader2)->GetClient();
  421. if(!client) {
  422. return;
  423. }
  424. PacketStruct* packet = configReader.getStruct("WS_PlayerTrade", client->GetVersion());
  425. if (packet) {
  426. packet->setDataByName("spawn_id", client->GetPlayer()->GetIDWithPlayerSpawn(trader1));
  427. packet->setDataByName("type", 1);
  428. int8 size = (int8)(trader2_items.size());
  429. int8 i = 0;
  430. map<int8, TradeItemInfo>::iterator itr;
  431. packet->setArrayLengthByName("your_item_count", size);
  432. for (itr = trader2_items.begin(); itr != trader2_items.end(); itr++) {
  433. packet->setArrayDataByName("your_item_unknown1", 1, i);
  434. packet->setArrayDataByName("your_item_unknown2", 1, i);
  435. packet->setArrayDataByName("your_item_slot", itr->first, i);
  436. packet->setArrayDataByName("your_item_id", itr->second.item->details.item_id, i);
  437. packet->setArrayDataByName("your_item_quantity", itr->second.quantity, i);
  438. packet->setArrayDataByName("your_item_icon", itr->second.item->details.icon, i);
  439. packet->setArrayDataByName("your_item_background", 0, i); // No clue on this value yet
  440. i++;
  441. }
  442. int32 plat = 0;
  443. int32 gold = 0;
  444. int32 silver = 0;
  445. int32 copper = 0;
  446. CalculateCoins(trader2_coins, plat, gold, silver, copper);
  447. packet->setDataByName("your_copper", copper);
  448. packet->setDataByName("your_silver", silver);
  449. packet->setDataByName("your_gold", gold);
  450. packet->setDataByName("your_plat", plat);
  451. size = (int8)(trader1_items.size());
  452. i = 0;
  453. packet->setArrayLengthByName("their_item_count", size);
  454. for (itr = trader1_items.begin(); itr != trader1_items.end(); itr++) {
  455. packet->setArrayDataByName("their_item_unknown1", 1, i);
  456. packet->setArrayDataByName("their_item_unknown2", 1, i);
  457. packet->setArrayDataByName("their_item_slot", itr->first, i);
  458. packet->setArrayDataByName("their_item_id", itr->second.item->details.item_id, i);
  459. packet->setArrayDataByName("their_item_quantity", itr->second.quantity, i);
  460. packet->setArrayDataByName("their_item_icon", itr->second.item->details.icon, i);
  461. packet->setArrayDataByName("their_item_background", 0, i); // No clue on this value yet
  462. i++;
  463. }
  464. plat = 0;
  465. gold = 0;
  466. silver = 0;
  467. copper = 0;
  468. CalculateCoins(trader1_coins, plat, gold, silver, copper);
  469. packet->setDataByName("their_copper", copper);
  470. packet->setDataByName("their_silver", silver);
  471. packet->setDataByName("their_gold", gold);
  472. packet->setDataByName("their_plat", plat);
  473. client->QueuePacket(packet->serialize());
  474. safe_delete(packet);
  475. }
  476. }
  477. }
  478. void Trade::CalculateCoins(int64 val, int32& plat, int32& gold, int32& silver, int32& copper) {
  479. int32 tmp = 0;
  480. if (val >= 1000000) {
  481. tmp = val / 1000000;
  482. val -= tmp * 1000000;
  483. plat = tmp;
  484. }
  485. if (val >= 10000) {
  486. tmp = val / 10000;
  487. val -= tmp * 10000;
  488. gold = tmp;
  489. }
  490. if (val >= 100) {
  491. tmp = val / 100;
  492. val -= tmp * 100;
  493. silver = tmp;
  494. }
  495. if (val > 0) {
  496. copper = val;
  497. }
  498. }
  499. int8 Trade::GetNextFreeSlot(Entity* character) {
  500. map<int8, TradeItemInfo>* list = 0;
  501. if (character == trader1)
  502. list = &trader1_items;
  503. else if (character == trader2)
  504. list = &trader2_items;
  505. else
  506. return 255;
  507. int8 ret = 255;
  508. for (int8 index = 0; index < 12; index++) {
  509. if (list->count(index) == 0) {
  510. ret = index;
  511. break;
  512. }
  513. }
  514. return ret;
  515. }