TradeskillsPackets.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. EQ2Emulator: Everquest II Server Emulator
  3. Copyright (C) 2007 EQ2EMulator Development Team (http://www.eq2emulator.net)
  4. This file is part of EQ2Emulator.
  5. EQ2Emulator is free software: you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation, either version 3 of the License, or
  8. (at your option) any later version.
  9. EQ2Emulator is distributed in the hope that it will be useful,
  10. but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. GNU General Public License for more details.
  13. You should have received a copy of the GNU General Public License
  14. along with EQ2Emulator. If not, see <http://www.gnu.org/licenses/>.
  15. */
  16. #include <algorithm>
  17. #include "../ClientPacketFunctions.h"
  18. #include "../client.h"
  19. #include "../../common/ConfigReader.h"
  20. #include "../../common/PacketStruct.h"
  21. #include "../Recipes/Recipe.h"
  22. #include "../../common/Log.h"
  23. #include "../Spells.h"
  24. #include "../../common/MiscFunctions.h"
  25. extern ConfigReader configReader;
  26. extern MasterRecipeList master_recipe_list;
  27. extern MasterSpellList master_spell_list;
  28. void ClientPacketFunctions::SendCreateFromRecipe(Client* client, int32 recipeID) {
  29. // if recipeID is 0 we are repeating the last recipe, if not set the players current recipe to the new one
  30. if (recipeID == 0)
  31. recipeID = client->GetPlayer()->GetCurrentRecipe();
  32. else
  33. client->GetPlayer()->SetCurrentRecipe(recipeID);
  34. // Get the recipe
  35. Recipe* recipe = master_recipe_list.GetRecipe(recipeID);
  36. if (!recipe) {
  37. LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error loading recipe (%u) in ClientPacketFunctions::SendCreateFromRecipe()", recipeID);
  38. return;
  39. }
  40. // Create the packet
  41. PacketStruct* packet = configReader.getStruct("WS_CreateFromRecipe", client->GetVersion());
  42. if (!packet) {
  43. LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error loading WS_CreateFromRecipe in ClientPacketFunctions::SendCreateFromRecipe()");
  44. return;
  45. }
  46. vector<int32>::iterator itr;
  47. int8 i = 0;
  48. int32 firstID = 0;
  49. int32 primary_comp_id = 0;
  50. Item* item = 0;
  51. // Recipe and crafting table info
  52. packet->setDataByName("crafting_station", recipe->GetDevice());
  53. packet->setDataByName("recipe_name", recipe->GetName());
  54. packet->setDataByName("tier", recipe->GetTier());
  55. // Product info
  56. item = master_item_list.GetItem(recipe->GetProductID());
  57. packet->setDataByName("product_name", item->name.c_str());
  58. packet->setDataByName("icon", item->details.icon);
  59. packet->setDataByName("product_qty", recipe->GetProductQuantity());
  60. packet->setDataByName("primary_title", recipe->GetPrimaryBuildComponentTitle());
  61. packet->setDataByName("primary_qty", 1);
  62. // Reset item to 0
  63. item = 0;
  64. // Check to see if we have a primary component (slot = 0)
  65. if (recipe->components.count(0) > 0) {
  66. vector<int32> rc = recipe->components[0];
  67. packet->setArrayLengthByName("num_primary_choices", rc.size());
  68. for (itr = rc.begin(); itr != rc.end(); itr++, i++) {
  69. if (firstID == 0)
  70. firstID = *itr;
  71. item = master_item_list.GetItem(*itr);
  72. packet->setArrayDataByName("primary_component", item->name.c_str(), i);
  73. packet->setArrayDataByName("primary_item_id", (*itr), i);
  74. packet->setArrayDataByName("primary_icon", item->details.icon, i);
  75. item = 0;
  76. item = client->GetPlayer()->item_list.GetItemFromID((*itr));
  77. if (item)
  78. packet->setArrayDataByName("primary_total_quantity", item->details.count, i);
  79. }
  80. // store the id of the primary comp
  81. primary_comp_id = firstID;
  82. // Set the default item id to the first component id
  83. packet->setDataByName("primary_item_selected", 1);
  84. packet->setDataByName("primary_default_selected_id", firstID);
  85. item = 0;
  86. item = client->GetPlayer()->item_list.GetItemFromID(firstID);
  87. if (item)
  88. packet->setDataByName("primary_selected_item_qty", min((int8)1, (int8)item->details.count));
  89. // Reset the variables we will reuse
  90. i = 0;
  91. firstID = 0;
  92. item = 0;
  93. }
  94. else {
  95. LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Recipe has no primary component");
  96. return;
  97. }
  98. // Check to see if we have build components (slot = 1 - 4)
  99. int8 total_build_components = 0;
  100. if (recipe->components.count(1) > 0)
  101. total_build_components++;
  102. if (recipe->components.count(2))
  103. total_build_components++;
  104. if (recipe->components.count(3))
  105. total_build_components++;
  106. if (recipe->components.count(4))
  107. total_build_components++;
  108. if (total_build_components > 0) {
  109. packet->setArrayLengthByName("num_build_components", total_build_components);
  110. for (int8 index = 0; index < 4; index++) {
  111. if (recipe->components.count(index + 1) == 0)
  112. continue;
  113. packet->setArrayDataByName("build_slot", index, index);
  114. vector<int32> rc = recipe->components[index + 1];
  115. packet->setSubArrayLengthByName("num_build_choices", rc.size(), index);
  116. for (itr = rc.begin(); itr != rc.end(); itr++, i++) {
  117. if (firstID == 0)
  118. firstID = *itr;
  119. item = master_item_list.GetItem(*itr);
  120. packet->setSubArrayDataByName("build_component", item->name.c_str(), index, i);
  121. packet->setSubArrayDataByName("build_item_id", (*itr), index, i);
  122. packet->setSubArrayDataByName("build_icon", item->details.icon, index, i);
  123. item = 0;
  124. item = client->GetPlayer()->item_list.GetItemFromID((*itr));
  125. if (item)
  126. packet->setSubArrayDataByName("build_total_quantity", item->details.count, index, i);
  127. }
  128. // Set the default item id to the first component id
  129. packet->setArrayDataByName("build_item_selected", 1, index);
  130. packet->setArrayDataByName("build_selected_item_id", firstID, index);
  131. item = 0;
  132. item = client->GetPlayer()->item_list.GetItemFromID(firstID);
  133. int8 qty = 0;
  134. if (item) {
  135. qty = (int8)item->details.count;
  136. if (qty > 0 && firstID == primary_comp_id)
  137. qty -= 1;
  138. }
  139. if (index == 0) {
  140. packet->setArrayDataByName("build_title", recipe->GetBuild1ComponentTitle(), index);
  141. packet->setArrayDataByName("build_qty", recipe->GetBuild1ComponentQuantity(), index);
  142. if (item)
  143. packet->setArrayDataByName("build_selected_item_qty", min(qty, recipe->GetBuild1ComponentQuantity()), index);
  144. }
  145. else if (index == 1) {
  146. packet->setArrayDataByName("build_title", recipe->GetBuild2ComponentTitle(), index);
  147. packet->setArrayDataByName("build_qty", recipe->GetBuild2ComponentQuantity(), index);
  148. if (item)
  149. packet->setArrayDataByName("build_selected_item_qty", min(qty, recipe->GetBuild2ComponentQuantity()), index);
  150. }
  151. else if (index == 2) {
  152. packet->setArrayDataByName("build_title", recipe->GetBuild3ComponentTitle(), index);
  153. packet->setArrayDataByName("build_qty", recipe->GetBuild3ComponentQuantity(), index);
  154. if (item)
  155. packet->setArrayDataByName("build_selected_item_qty", min(qty, recipe->GetBuild3ComponentQuantity()), index);
  156. }
  157. else {
  158. packet->setArrayDataByName("build_title", recipe->GetBuild4ComponentTitle(), index);
  159. packet->setArrayDataByName("build_qty", recipe->GetBuild4ComponentQuantity(), index);
  160. if (item)
  161. packet->setArrayDataByName("build_selected_item_qty", min(qty, recipe->GetBuild4ComponentQuantity()), index);
  162. }
  163. // Reset the variables we will reuse
  164. i = 0;
  165. firstID = 0;
  166. item = 0;
  167. }
  168. }
  169. // Check to see if we have a fuel component (slot = 5)
  170. if (recipe->components.count(5) > 0) {
  171. vector<int32> rc = recipe->components[5];
  172. packet->setArrayLengthByName("num_fuel_choices", rc.size());
  173. for (itr = rc.begin(); itr != rc.end(); itr++, i++) {
  174. if (firstID == 0)
  175. firstID = *itr;
  176. item = master_item_list.GetItem(*itr);
  177. packet->setArrayDataByName("fuel_component", item->name.c_str(), i);
  178. packet->setArrayDataByName("fuel_item_id", (*itr), i);
  179. packet->setArrayDataByName("fuel_icon", item->details.icon, i);
  180. item = 0;
  181. item = client->GetPlayer()->item_list.GetItemFromID((*itr));
  182. if (item)
  183. packet->setArrayDataByName("fuel_total_quantity", item->details.count, i);
  184. }
  185. // Set the default item id to the first component id
  186. packet->setDataByName("fuel_selected_item_id", firstID);
  187. packet->setDataByName("fuel_item_selected", 1);
  188. item = 0;
  189. item = client->GetPlayer()->item_list.GetItemFromID(firstID);
  190. if (item)
  191. packet->setDataByName("fuel_selected_item_qty", min(recipe->GetFuelComponentQuantity(), (int8)item->details.count));
  192. packet->setDataByName("fuel_title",recipe->GetFuelComponentTitle());
  193. packet->setDataByName("fuel_qty",recipe->GetFuelComponentQuantity());
  194. // Reset the variables we will reuse
  195. i = 0;
  196. firstID = 0;
  197. item = 0;
  198. }
  199. else {
  200. LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Recipe has no fuel component");
  201. return;
  202. }
  203. packet->setDataByName("unknown1", recipeID);
  204. packet->setDataByName("unknown8", 1); // Possible array for amount you can craft
  205. packet->setDataByName("unknown8", 1, 1); // amounts you can craft
  206. //packet->PrintPacket();
  207. // Send the packet
  208. client->QueuePacket(packet->serialize());
  209. safe_delete(packet);
  210. }
  211. void ClientPacketFunctions::SendItemCreationUI(Client* client, Recipe* recipe) {
  212. // Check for valid recipe
  213. if (!recipe) {
  214. LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Recipe = null in ClientPacketFunctions::SendItemCreationUI()");
  215. return;
  216. }
  217. // Load the packet
  218. PacketStruct* packet = configReader.getStruct("WS_ShowItemCreation", client->GetVersion());
  219. if (!packet) {
  220. LogWrite(TRADESKILL__ERROR, 0, "Recipes", "Error loading WS_ShowItemCreation in ClientPacketFunctions::SendItemCreationUI()");
  221. return;
  222. }
  223. int16 item_version = GetItemPacketType(packet->GetVersion());
  224. Item* item = 0;
  225. RecipeProducts* rp = 0;
  226. packet->setDataByName("max_possible_durability", 1000);
  227. packet->setDataByName("max_possible_progress", 1000);
  228. // All the packets I have looked at these unknowns are always the same
  229. // so hardcoding them until they are identified.
  230. packet->setDataByName("unknown2", 1045220557, 0);
  231. packet->setDataByName("unknown2", 1061997773, 1);
  232. // Highest stage the player has been able to complete
  233. // TODO: store this for the player, for now use 0 (none known)
  234. packet->setDataByName("progress_levels_known", client->GetPlayer()->GetRecipeList()->GetRecipe(recipe->GetID())->GetHighestStage());
  235. packet->setArrayLengthByName("num_process", 4);
  236. for (int8 i = 0; i < 4; i++) {
  237. // Don't like this code but need to change the IfVariableNotSet value on unknown3 element
  238. // to point to the currect progress_needed
  239. vector<DataStruct*> dataStructs = packet->GetDataStructs();
  240. vector<DataStruct*>::iterator itr;
  241. for (itr = dataStructs.begin(); itr != dataStructs.end(); itr++) {
  242. DataStruct* data = *itr;
  243. char tmp[20] = {0};
  244. sprintf(tmp,"_%i",i);
  245. string name = "unknown3";
  246. name.append(tmp);
  247. if (strcmp(data->GetName(), name.c_str()) == 0) {
  248. name = "progress_needed";
  249. name.append(tmp);
  250. data->SetIfNotSetVariable(name.c_str());
  251. }
  252. }
  253. if (i == 1)
  254. packet->setArrayDataByName("progress_needed", 400, i);
  255. else if (i == 2)
  256. packet->setArrayDataByName("progress_needed", 600, i);
  257. else if (i == 3)
  258. packet->setArrayDataByName("progress_needed", 800, i);
  259. // get the product for this stage, if none found default to fuel
  260. if (recipe->products.count(i) > 0)
  261. rp = recipe->products[i];
  262. if (!rp) {
  263. rp = new RecipeProducts;
  264. rp->product_id = recipe->components[5].front();
  265. rp->product_qty = recipe->GetFuelComponentQuantity();
  266. rp->byproduct_id = 0;
  267. rp->byproduct_qty = 0;
  268. recipe->products[i] = rp;
  269. }
  270. item = master_item_list.GetItem(rp->product_id);
  271. if (!item) {
  272. LogWrite(TRADESKILL__ERROR, 0, "Recipe", "Error loading item (%u) in ClientPacketFunctions::SendItemCreationUI()", rp->product_id);
  273. return;
  274. }
  275. packet->setArrayDataByName("item_name", item->name.c_str(), i);
  276. packet->setArrayDataByName("item_icon", item->details.icon, i);
  277. if(client->GetVersion() < 860)
  278. packet->setItemArrayDataByName("item", item, client->GetPlayer(), i, 0, -1);
  279. else if (client->GetVersion() < 1193)
  280. packet->setItemArrayDataByName("item", item, client->GetPlayer(), i);
  281. else
  282. packet->setItemArrayDataByName("item", item, client->GetPlayer(), i, 0, 2);
  283. if (rp->byproduct_id > 0) {
  284. item = 0;
  285. item = master_item_list.GetItem(rp->byproduct_id);
  286. if (item) {
  287. packet->setArrayDataByName("item_byproduct_name", item->name.c_str(), i);
  288. packet->setArrayDataByName("item_byproduct_icon", item->details.icon, i);
  289. }
  290. }
  291. packet->setArrayDataByName("packettype", item_version, i);
  292. packet->setArrayDataByName("packetsubtype", 0xFF, i);
  293. item = 0;
  294. rp = 0;
  295. }
  296. packet->setDataByName("product_progress_needed", 1000);
  297. rp = recipe->products[4];
  298. item = master_item_list.GetItem(rp->product_id);
  299. packet->setDataByName("product_item_name", item->name.c_str());
  300. packet->setDataByName("product_item_icon", item->details.icon);
  301. if(client->GetVersion() < 860)
  302. packet->setItemByName("product_item", item, client->GetPlayer(), 0, -1);
  303. else if (client->GetVersion() < 1193)
  304. packet->setItemByName("product_item", item, client->GetPlayer());
  305. else
  306. packet->setItemByName("product_item", item, client->GetPlayer(), 0, 2);
  307. //packet->setItemByName("product_item", item, client->GetPlayer());
  308. if (rp->byproduct_id > 0) {
  309. item = 0;
  310. item = master_item_list.GetItem(rp->byproduct_id);
  311. if (item) {
  312. packet->setDataByName("product_byproduct_name", item->name.c_str());
  313. packet->setDataByName("product_byproduct_icon", item->details.icon);
  314. }
  315. }
  316. packet->setDataByName("packettype", item_version);
  317. packet->setDataByName("packetsubtype", 0xFF);
  318. // Start of basic work to get the skills to show on the tradeskill window
  319. // not even close to accurate but skills do get put on the ui
  320. int8 index = 0;
  321. int8 size = 0;
  322. vector<int32>::iterator itr;
  323. vector<int32> spells = client->GetPlayer()->GetSpellBookSpellIDBySkill(recipe->GetTechnique());
  324. for (itr = spells.begin(); itr != spells.end(); itr++) {
  325. size++;
  326. Spell* spell = master_spell_list.GetSpell(*itr,1);
  327. if (size > 6) {
  328. // only 6 slots for skills on the ui
  329. break;
  330. }
  331. packet->setDataByName("skill_id", *itr ,spell->GetSpellData()->ts_loc_index -1);
  332. }
  333. EQ2Packet* outapp = packet->serialize();
  334. //DumpPacket(outapp);
  335. client->QueuePacket(outapp);
  336. safe_delete(packet);
  337. }
  338. void ClientPacketFunctions::StopCrafting(Client* client) {
  339. client->QueuePacket(new EQ2Packet(OP_StopItemCreationMsg, 0, 0));
  340. }
  341. void ClientPacketFunctions::CounterReaction(Client* client, bool countered) {
  342. PacketStruct* packet = configReader.getStruct("WS_TSEventReaction", client->GetVersion());
  343. if (packet) {
  344. packet->setDataByName("counter_reaction", countered ? 1 : 0);
  345. client->QueuePacket(packet->serialize());
  346. }
  347. safe_delete(packet);
  348. }