item_synthesis.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "item_synthesis.hpp"
  4. #include <algorithm>
  5. #include <memory>
  6. #include "../common/nullpo.hpp"
  7. #include "../common/showmsg.hpp"
  8. #include "log.hpp" // e_log_pick_type
  9. ItemSynthesisDatabase item_synthesis_db;
  10. void ItemSynthesisDatabase::clear() {
  11. TypesafeYamlDatabase::clear();
  12. }
  13. const std::string ItemSynthesisDatabase::getDefaultLocation() {
  14. return std::string(db_path) + "/item_synthesis.yml";
  15. }
  16. /**
  17. * Reads and parses an entry from the item_synthesis file.
  18. * @param node: YAML node containing the entry.
  19. * @return count of successfully parsed rows
  20. */
  21. uint64 ItemSynthesisDatabase::parseBodyNode(const YAML::Node &node) {
  22. std::string synthesis_item_name;
  23. if (!this->asString(node, "Item", synthesis_item_name))
  24. return 0;
  25. item_data *item = itemdb_search_aegisname(synthesis_item_name.c_str());
  26. if (item == nullptr) {
  27. this->invalidWarning(node["Item"], "Item name for Synthesis Box %s does not exist.\n", synthesis_item_name.c_str());
  28. return 0;
  29. }
  30. std::shared_ptr<s_item_synthesis_db> entry = this->find(item->nameid);
  31. bool exists = entry != nullptr;
  32. if (!exists) {
  33. if (!this->nodesExist(node, { "SourceItem", "Reward" }))
  34. return 0;
  35. entry = std::make_shared<s_item_synthesis_db>();
  36. entry->id = item->nameid;
  37. }
  38. if (this->nodeExists(node, "SourceNeeded")) {
  39. if (!this->asUInt16(node, "SourceNeeded", entry->source_needed))
  40. return 0;
  41. }
  42. if (this->nodeExists(node, "NeedRefineMin")) {
  43. if (!this->asUInt16(node, "NeedRefineMin", entry->source_refine_min))
  44. return 0;
  45. }
  46. if (this->nodeExists(node, "NeedRefineMax")) {
  47. if (!this->asUInt16(node, "NeedRefineMax", entry->source_refine_max))
  48. return 0;
  49. }
  50. if (exists && this->nodeExists(node, "ClearSourceItem")) {
  51. ShowNotice("item_synthesis: Cleared all items in SourceItem. Synthesis: %s (%u)\n", item->name.c_str(), item->nameid);
  52. if (!entry->sources.empty())
  53. entry->sources.clear();
  54. }
  55. if (this->nodeExists(node, "SourceItem")) {
  56. const YAML::Node& sourceNode = node["SourceItem"];
  57. if (!exists)
  58. entry->sources.reserve(entry->source_needed);
  59. for (const YAML::Node &source : sourceNode) {
  60. std::string source_item_name;
  61. if (!this->asString(source, "Item", source_item_name))
  62. continue;
  63. item_data *source_it = itemdb_search_aegisname(source_item_name.c_str());
  64. if (source_it == nullptr) {
  65. this->invalidWarning(node["SourceItem"], "Source item name %s does not exist, skipping.\n", source_item_name.c_str());
  66. continue;
  67. }
  68. s_item_synthesis_source source_item = {};
  69. source_item.nameid = source_it->nameid;
  70. if (exists && this->nodeExists(source, "Remove")) {
  71. entry->sources.erase(std::remove_if(entry->sources.begin(), entry->sources.end(), [&source_item](const s_item_synthesis_source &x) { return x.nameid == source_item.nameid; }));
  72. ShowNotice("item_synthesis: Removed %s (%u) from SourceItem. Synthesis: %s (%u)\n", source_it->name.c_str(), source_item.nameid, item->name.c_str(), item->nameid);
  73. continue;
  74. }
  75. if (this->nodeExists(source, "Amount"))
  76. this->asUInt16(source, "Amount", source_item.amount);
  77. if (entry->sources.end() == std::find_if(
  78. entry->sources.begin(), entry->sources.end(), [&source_item](s_item_synthesis_source &x)
  79. {
  80. if (x.nameid == source_item.nameid) {
  81. x.amount = source_item.amount;
  82. return true;
  83. }
  84. return false;
  85. }
  86. ))
  87. entry->sources.push_back(source_item);
  88. }
  89. }
  90. if (this->nodeExists(node, "Reward")) {
  91. std::string script_str;
  92. script_code *code;
  93. if (!this->asString(node, "Reward", script_str))
  94. return 0;
  95. if (!(code = parse_script(script_str.c_str(), this->getCurrentFile().c_str(), item->nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS))) {
  96. this->invalidWarning(node["Reward"], "Invalid Reward item script.\n");
  97. return 0;
  98. }
  99. if (entry->reward)
  100. script_free_code(entry->reward);
  101. entry->reward = code;
  102. }
  103. if (!exists)
  104. this->put(item->nameid, entry);
  105. return 1;
  106. }
  107. /**
  108. * Attempt to open synthesis UI for a player
  109. * @param sd: Open UI for this player
  110. * @param itemid: ID of synthesis UI
  111. * @return True on succes, false on failure
  112. */
  113. bool item_synthesis_open(map_session_data *sd, t_itemid itemid) {
  114. nullpo_retr(false, sd);
  115. if (pc_cant_act2(sd) || (sd)->chatID)
  116. return false;
  117. if (pc_is90overweight(sd) || !pc_inventoryblank(sd)) {
  118. clif_msg(sd, ITEM_CANT_OBTAIN_WEIGHT);
  119. return false;
  120. }
  121. if (!item_synthesis_db.exists(itemid))
  122. return false;
  123. if (clif_synthesisui_open(sd, itemid)) {
  124. sd->last_lapine_box = itemid;
  125. sd->state.lapine_ui = 1;
  126. }
  127. return true;
  128. }
  129. /**
  130. * Process synthesis input from player
  131. * @param sd: Player who request
  132. * @param itemid: ID of synthesis UI
  133. * @param items: Item list sent by player
  134. * @return SYNTHESIS_SUCCESS on success. @see e_item_synthesis_result
  135. */
  136. e_item_synthesis_result item_synthesis_submit(map_session_data *sd, t_itemid itemid, const std::vector<s_item_synthesis_list> items) {
  137. nullpo_retr(SYNTHESIS_INVALID_ITEM, sd);
  138. if (!sd->state.lapine_ui || itemid != sd->last_lapine_box) {
  139. sd->state.lapine_ui = sd->last_lapine_box = 0;
  140. return SYNTHESIS_INVALID_ITEM;
  141. }
  142. auto info = item_synthesis_db.find(itemid);
  143. if (!info || !info->checkRequirement(sd, items))
  144. return SYNTHESIS_INSUFFICIENT_AMOUNT;
  145. if (!info->deleteRequirement(sd, items))
  146. return SYNTHESIS_INSUFFICIENT_AMOUNT;
  147. if (info->reward)
  148. run_script(info->reward, 0, sd->status.account_id, 0);
  149. sd->state.lapine_ui = sd->last_lapine_box = 0;
  150. return SYNTHESIS_SUCCESS;
  151. }
  152. /**
  153. * Loads lapine synthesis database
  154. */
  155. void item_synthesis_read_db(void)
  156. {
  157. item_synthesis_db.load();
  158. }
  159. /**
  160. * Reloads the lapine synthesis database
  161. */
  162. void item_synthesis_db_reload(void)
  163. {
  164. do_final_item_synthesis();
  165. do_init_item_synthesis();
  166. }
  167. /**
  168. * Initializes the lapine synthesis database
  169. */
  170. void do_init_item_synthesis(void)
  171. {
  172. item_synthesis_db.load();
  173. }
  174. /**
  175. * Finalizes the lapine synthesis database
  176. */
  177. void do_final_item_synthesis(void) {
  178. item_synthesis_db.clear();
  179. }
  180. /**
  181. * Constructor
  182. */
  183. s_item_synthesis_db::s_item_synthesis_db()
  184. : source_needed(1)
  185. , sources()
  186. , reward(nullptr)
  187. , source_refine_min(0)
  188. , source_refine_max(MAX_REFINE)
  189. {}
  190. /**
  191. * Destructor
  192. */
  193. s_item_synthesis_db::~s_item_synthesis_db()
  194. {
  195. if (this->reward) {
  196. script_free_code(this->reward);
  197. this->reward = nullptr;
  198. }
  199. }
  200. /**
  201. * Check if the source for synthesis item is exists
  202. * @param source_id: Item ID of source item
  203. * @return true if source exists, false if doesn't
  204. */
  205. bool s_item_synthesis_db::sourceExists(t_itemid source_id)
  206. {
  207. if (this->sources.empty())
  208. return false;
  209. auto source = std::find_if(
  210. this->sources.begin(), this->sources.end(),
  211. [&source_id](const s_item_synthesis_source &source) { return source.nameid == source_id; }
  212. );
  213. return (source != this->sources.end());
  214. }
  215. /**
  216. * Check all submitted items are valid
  217. * @param sd: Player
  218. * @param items: Submitted items by player
  219. * @return True if all items are valid
  220. */
  221. bool s_item_synthesis_db::checkRequirement(map_session_data *sd, const std::vector<s_item_synthesis_list> items)
  222. {
  223. if (items.empty() || items.size() != this->source_needed)
  224. return false;
  225. item *item = nullptr;
  226. item_data *id = nullptr;
  227. std::vector<int> indexes(this->source_needed);
  228. for (auto &it : items) {
  229. if (it.index >= MAX_INVENTORY)
  230. return false;
  231. if (!(item = &sd->inventory.u.items_inventory[it.index]) || !(id = sd->inventory_data[it.index]))
  232. return false;
  233. if (item->equip || item->expire_time || item->amount < it.amount || item->identify != 1)
  234. return false;
  235. if (!this->sourceExists(item->nameid))
  236. return false;
  237. if (item->refine < this->source_refine_min)
  238. return false;
  239. if (item->refine > this->source_refine_max)
  240. return false;
  241. if (std::find(indexes.begin(), indexes.end(), it.index) != indexes.end())
  242. return false;
  243. indexes.push_back(it.index);
  244. }
  245. return true;
  246. }
  247. /**
  248. * Delete all submitted items for synthesis
  249. * @param sd: Player
  250. * @param items: Submitted items by player
  251. * @return True if all items are deleted
  252. */
  253. bool s_item_synthesis_db::deleteRequirement(map_session_data *sd, const std::vector<s_item_synthesis_list> items)
  254. {
  255. if (items.empty() || items.size() != this->source_needed)
  256. return false;
  257. for (auto &it : items) {
  258. if (it.index >= MAX_INVENTORY)
  259. return false;
  260. if (pc_delitem(sd, it.index, it.amount, 0, 0, LOG_TYPE_OTHER) != 0)
  261. return false;
  262. }
  263. return true;
  264. }
  265. /**
  266. * Synthesis items constructor.
  267. * Set default amount to 1
  268. */
  269. s_item_synthesis_source::s_item_synthesis_source()
  270. : amount(1)
  271. {
  272. }