item_upgrade.cpp 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
  2. // For more information, see LICENCE in the main folder
  3. #include "item_upgrade.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. ItemUpgradeDatabase item_upgrade_db;
  10. void ItemUpgradeDatabase::clear() {
  11. TypesafeYamlDatabase::clear();
  12. }
  13. const std::string ItemUpgradeDatabase::getDefaultLocation() {
  14. return std::string(db_path) + "/item_upgrade.yml";
  15. }
  16. /**
  17. * Reads and parses an entry from the item_upgrade file.
  18. * @param node: YAML node containing the entry.
  19. * @return count of successfully parsed rows
  20. */
  21. uint64 ItemUpgradeDatabase::parseBodyNode(const YAML::Node &node) {
  22. std::string upgrade_item_name;
  23. if (!this->asString(node, "Item", upgrade_item_name))
  24. return 0;
  25. std::shared_ptr<item_data> item = item_db.search_aegisname(upgrade_item_name.c_str());
  26. if (item == nullptr) {
  27. this->invalidWarning(node["Item"], "Item name for Upgrade Box %s does not exist.\n", upgrade_item_name.c_str());
  28. return 0;
  29. }
  30. std::shared_ptr<s_item_upgrade_db> entry = this->find(item->nameid);
  31. bool exists = entry != nullptr;
  32. if (!exists) {
  33. if (!this->nodesExist(node, { "TargetItem", "Result" }))
  34. return 0;
  35. entry = std::make_shared<s_item_upgrade_db>();
  36. entry->id = item->nameid;
  37. }
  38. if (this->nodeExists(node, "Result")) {
  39. std::string script_str;
  40. script_code *code;
  41. if (!this->asString(node, "Result", script_str))
  42. return 0;
  43. if (!(code = parse_script(script_str.c_str(), this->getCurrentFile().c_str(), item->nameid, SCRIPT_IGNORE_EXTERNAL_BRACKETS))) {
  44. this->invalidWarning(node["Result"], "Invalid Result item script.\n");
  45. return 0;
  46. }
  47. if (entry->result)
  48. script_free_code(entry->result);
  49. entry->result = code;
  50. }
  51. if (exists && this->nodeExists(node, "ClearTargetItem")) {
  52. ShowNotice("item_upgrade: Cleared all items in TargetItem. Upgrade: %s (%u)\n", item->name.c_str(), item->nameid);
  53. if (!entry->targets.empty())
  54. entry->targets.clear();
  55. }
  56. if (this->nodeExists(node, "TargetItem")) {
  57. const YAML::Node& targetNode = node["TargetItem"];
  58. std::string target_item_name;
  59. for (const YAML::Node &target : targetNode) {
  60. if (!this->asString(target, "Item", target_item_name))
  61. continue;
  62. std::shared_ptr<item_data> target_item = item_db.search_aegisname(target_item_name.c_str());
  63. if (target_item == nullptr) {
  64. this->invalidWarning(node["TargetItem"], "Target item name %s does not exist, skipping.\n", target_item_name.c_str());
  65. continue;
  66. }
  67. t_itemid itemid = target_item->nameid;
  68. if (exists && this->nodeExists(target, "Remove")) {
  69. entry->targets.erase(std::remove_if(entry->targets.begin(), entry->targets.end(), [&itemid](const t_itemid &x) { return x == itemid; }));
  70. ShowNotice("item_upgrade: Removed %s (%u) from TargetItem. Upgrade: %s (%u)\n", target_item->name.c_str(), itemid, item->name.c_str(), item->nameid);
  71. continue;
  72. }
  73. entry->targets.push_back(itemid);
  74. }
  75. }
  76. if (this->nodeExists(node, "NeedRefineMin")) {
  77. if (!this->asUInt16(node, "NeedRefineMin", entry->source_refine_min))
  78. return 0;
  79. }
  80. if (this->nodeExists(node, "NeedRefineMax")) {
  81. if (!this->asUInt16(node, "NeedRefineMax", entry->source_refine_max))
  82. return 0;
  83. }
  84. if (this->nodeExists(node, "NeedOptionNumMin")) {
  85. if (!this->asUInt16(node, "NeedOptionNumMin", entry->need_option_num))
  86. return 0;
  87. }
  88. if (this->nodeExists(node, "NotSocketEnchantItem")) {
  89. if (!this->asBool(node, "NotSocketEnchantItem", entry->not_socket_enchant))
  90. return 0;
  91. }
  92. if (!exists)
  93. this->put(item->nameid, entry);
  94. return 1;
  95. }
  96. /**
  97. * Attempt to open upgrade UI for a player
  98. * @param sd: Open UI for this player
  99. * @param itemid: ID of upgrade UI
  100. * @return True on succes, false on failure
  101. */
  102. bool item_upgrade_open(map_session_data *sd, t_itemid itemid) {
  103. nullpo_retr(false, sd);
  104. if (pc_cant_act2(sd) || (sd)->chatID)
  105. return false;
  106. if (pc_is90overweight(sd) || !pc_inventoryblank(sd)) {
  107. clif_msg(sd, ITEM_CANT_OBTAIN_WEIGHT);
  108. return false;
  109. }
  110. if (!item_upgrade_db.exists(itemid))
  111. return false;
  112. if (clif_lapine_upgrade_open(sd, itemid)) {
  113. sd->last_lapine_box = itemid;
  114. sd->state.lapine_ui = 2;
  115. }
  116. return true;
  117. }
  118. /**
  119. * Process selected item from player's input
  120. * @param sd: Player
  121. * @param source_itemid: Item ID of source item to open Upgrade UI
  122. * @param target_index: Index of target item in player's inventory
  123. * @return LAPINE_UPRAGDE_SUCCESS on success. @see e_item_upgrade_result
  124. */
  125. e_item_upgrade_result item_upgrade_submit(map_session_data *sd, t_itemid source_itemid, uint16 target_index) {
  126. nullpo_retr(LAPINE_UPRAGDE_FAILURE, sd);
  127. if (!sd->state.lapine_ui || source_itemid != sd->last_lapine_box) {
  128. sd->state.lapine_ui = sd->last_lapine_box = 0;
  129. return LAPINE_UPRAGDE_FAILURE;
  130. }
  131. item *it;
  132. if (target_index >= MAX_INVENTORY || !sd->inventory_data[target_index] || !(it = &sd->inventory.u.items_inventory[target_index]))
  133. return LAPINE_UPRAGDE_FAILURE;
  134. if (it->expire_time || it->equip || it->identify != 1)
  135. return LAPINE_UPRAGDE_FAILURE;
  136. auto info = item_upgrade_db.find(source_itemid);
  137. if (!info || !info->targetExists(it->nameid) || !info->checkRequirement(it, sd->inventory_data[target_index]))
  138. return LAPINE_UPRAGDE_FAILURE;
  139. info->setPlayerInfo(sd, target_index, it);
  140. if (info->delete_target_onsuccess)
  141. pc_delitem(sd, target_index, 1, 0, 0, LOG_TYPE_OTHER);
  142. sd->state.lapine_ui = sd->last_lapine_box = 0;
  143. if (info->result)
  144. run_script(info->result, 0, sd->status.account_id, 0);
  145. return LAPINE_UPRAGDE_SUCCESS;
  146. }
  147. /**
  148. * Loads lapine upgrade database
  149. */
  150. void item_upgrade_read_db(void)
  151. {
  152. item_upgrade_db.load();
  153. }
  154. /**
  155. * Reloads the lapine upgrade database
  156. */
  157. void item_upgrade_db_reload(void)
  158. {
  159. do_final_item_upgrade();
  160. do_init_item_upgrade();
  161. }
  162. /**
  163. * Initializes the lapine upgrade database
  164. */
  165. void do_init_item_upgrade(void)
  166. {
  167. item_upgrade_db.load();
  168. }
  169. /**
  170. * Finalizes the lapine upgrade database
  171. */
  172. void do_final_item_upgrade(void) {
  173. item_upgrade_db.clear();
  174. }
  175. /**
  176. * Constructor
  177. */
  178. s_item_upgrade_db::s_item_upgrade_db()
  179. : targets()
  180. , result(nullptr)
  181. , source_refine_min(0)
  182. , source_refine_max(MAX_REFINE)
  183. , need_option_num(0)
  184. , not_socket_enchant(false)
  185. , delete_target_onsuccess(true)
  186. {}
  187. /**
  188. * Destructor
  189. */
  190. s_item_upgrade_db::~s_item_upgrade_db()
  191. {
  192. if (this->result) {
  193. script_free_code(this->result);
  194. this->result = nullptr;
  195. }
  196. }
  197. /**
  198. * Check if submitted target item is valid
  199. * @param target_id: Item ID of target item
  200. * @return True if exist, false if not
  201. */
  202. bool s_item_upgrade_db::targetExists(t_itemid target_id)
  203. {
  204. if (this->targets.empty())
  205. return false;
  206. auto target = std::find(this->targets.begin(), this->targets.end(), target_id);
  207. return (target != this->targets.end());
  208. }
  209. /**
  210. * Check if the target item is valid
  211. * @param it: Target item
  212. * @param id: Item data
  213. * @return True if valid, false if invalid
  214. */
  215. bool s_item_upgrade_db::checkRequirement(item *it, item_data *id)
  216. {
  217. if (this->source_refine_min > it->refine)
  218. return false;
  219. if (this->source_refine_max < it->refine)
  220. return false;
  221. if (this->not_socket_enchant) {
  222. for (int i = id->slots; i < MAX_SLOTS; i++) {
  223. if (it->card[i])
  224. return false;
  225. }
  226. }
  227. if (this->need_option_num) {
  228. int c = 0;
  229. for (int i = 0; i < MAX_ITEM_RDM_OPT; i++) {
  230. if (it->option[i].id)
  231. c++;
  232. }
  233. if (c < this->need_option_num)
  234. return false;
  235. }
  236. return true;
  237. }
  238. /**
  239. * Set variables for player on success upgrade process
  240. * @param sd: Player
  241. * @param target_index: Index of player's inventory items as upgrade target
  242. * @param it: Latest item data
  243. */
  244. void s_item_upgrade_db::setPlayerInfo(map_session_data * sd, uint16 target_index, item *it)
  245. {
  246. pc_setreg(sd, add_str("@last_lapine_id"), it->nameid);
  247. pc_setreg(sd, add_str("@last_lapine_idx"), target_index);
  248. pc_setreg(sd, add_str("@last_lapine_refine"), it->refine);
  249. pc_setreg(sd, add_str("@last_lapine_attribute"), it->attribute);
  250. pc_setreg(sd, add_str("@last_lapine_card1"), it->card[0]);
  251. pc_setreg(sd, add_str("@last_lapine_card2"), it->card[1]);
  252. pc_setreg(sd, add_str("@last_lapine_card3"), it->card[2]);
  253. pc_setreg(sd, add_str("@last_lapine_card4"), it->card[3]);
  254. pc_setreg(sd, add_str("@last_lapine_bound"), it->bound);
  255. char unique_id[23];
  256. memset(unique_id, '\0', sizeof(unique_id));
  257. snprintf(unique_id, sizeof(unique_id), "%llu", (unsigned long long)it->unique_id);
  258. pc_setregstr(sd, add_str("@last_lapine_uniqueid$"), unique_id);
  259. int key_opt_id = 0, key_opt_value = 0, key_opt_param = 0;
  260. script_cleararray_pc(sd, "@last_lapine_option_id");
  261. script_cleararray_pc(sd, "@last_lapine_option_value");
  262. script_cleararray_pc(sd, "@last_lapine_option_param");
  263. for (int i = 0; i < MAX_ITEM_RDM_OPT; i++) {
  264. script_setarray_pc(sd, "@last_lapine_option_id", i, (intptr_t)it->option[i].id, &key_opt_id);
  265. script_setarray_pc(sd, "@last_lapine_option_value", i, (intptr_t)it->option[i].value, &key_opt_value);
  266. script_setarray_pc(sd, "@last_lapine_option_param", i, (intptr_t)it->option[i].param, &key_opt_param);
  267. }
  268. }