Trade.cpp 16 KB

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