craftdef.cpp 29 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "craftdef.h"
  17. #include "irrlichttypes.h"
  18. #include "log.h"
  19. #include <sstream>
  20. #include <set>
  21. #include <algorithm>
  22. #include "gamedef.h"
  23. #include "inventory.h"
  24. #include "util/serialize.h"
  25. #include "util/string.h"
  26. #include "util/numeric.h"
  27. #include "util/strfnd.h"
  28. #include "exceptions.h"
  29. inline bool isGroupRecipeStr(const std::string &rec_name)
  30. {
  31. return str_starts_with(rec_name, std::string("group:"));
  32. }
  33. inline u64 getHashForString(const std::string &recipe_str)
  34. {
  35. /*errorstream << "Hashing craft string \"" << recipe_str << '"';*/
  36. return murmur_hash_64_ua(recipe_str.data(), recipe_str.length(), 0xdeadbeef);
  37. }
  38. static u64 getHashForGrid(CraftHashType type, const std::vector<std::string> &grid_names)
  39. {
  40. switch (type) {
  41. case CRAFT_HASH_TYPE_ITEM_NAMES: {
  42. std::ostringstream os;
  43. bool is_first = true;
  44. for (const std::string &grid_name : grid_names) {
  45. if (!grid_name.empty()) {
  46. os << (is_first ? "" : "\n") << grid_name;
  47. is_first = false;
  48. }
  49. }
  50. return getHashForString(os.str());
  51. } case CRAFT_HASH_TYPE_COUNT: {
  52. u64 cnt = 0;
  53. for (const std::string &grid_name : grid_names)
  54. if (!grid_name.empty())
  55. cnt++;
  56. return cnt;
  57. } case CRAFT_HASH_TYPE_UNHASHED:
  58. return 0;
  59. }
  60. // invalid CraftHashType
  61. assert(false);
  62. return 0;
  63. }
  64. // Check if input matches recipe
  65. // Takes recipe groups into account
  66. static bool inputItemMatchesRecipe(const std::string &inp_name,
  67. const std::string &rec_name, IItemDefManager *idef)
  68. {
  69. // Exact name
  70. if (inp_name == rec_name)
  71. return true;
  72. // Group
  73. if (isGroupRecipeStr(rec_name) && idef->isKnown(inp_name)) {
  74. const struct ItemDefinition &def = idef->get(inp_name);
  75. Strfnd f(rec_name.substr(6));
  76. bool all_groups_match = true;
  77. do {
  78. std::string check_group = f.next(",");
  79. if (itemgroup_get(def.groups, check_group) == 0) {
  80. all_groups_match = false;
  81. break;
  82. }
  83. } while (!f.at_end());
  84. if (all_groups_match)
  85. return true;
  86. }
  87. // Didn't match
  88. return false;
  89. }
  90. // Deserialize an itemstring then return the name of the item
  91. static std::string craftGetItemName(const std::string &itemstring, IGameDef *gamedef)
  92. {
  93. ItemStack item;
  94. item.deSerialize(itemstring, gamedef->idef());
  95. return item.name;
  96. }
  97. // (mapcar craftGetItemName itemstrings)
  98. static std::vector<std::string> craftGetItemNames(
  99. const std::vector<std::string> &itemstrings, IGameDef *gamedef)
  100. {
  101. std::vector<std::string> result;
  102. for (const auto &itemstring : itemstrings) {
  103. result.push_back(craftGetItemName(itemstring, gamedef));
  104. }
  105. return result;
  106. }
  107. // Get name of each item, and return them as a new list.
  108. static std::vector<std::string> craftGetItemNames(
  109. const std::vector<ItemStack> &items, IGameDef *gamedef)
  110. {
  111. std::vector<std::string> result;
  112. for (const auto &item : items) {
  113. result.push_back(item.name);
  114. }
  115. return result;
  116. }
  117. // convert a list of item names, to ItemStacks.
  118. static std::vector<ItemStack> craftGetItems(
  119. const std::vector<std::string> &items, IGameDef *gamedef)
  120. {
  121. std::vector<ItemStack> result;
  122. for (const auto &item : items) {
  123. result.emplace_back(std::string(item), (u16)1,
  124. (u16)0, gamedef->getItemDefManager());
  125. }
  126. return result;
  127. }
  128. // Compute bounding rectangle given a matrix of items
  129. // Returns false if every item is ""
  130. static bool craftGetBounds(const std::vector<std::string> &items, unsigned int width,
  131. unsigned int &min_x, unsigned int &max_x,
  132. unsigned int &min_y, unsigned int &max_y)
  133. {
  134. bool success = false;
  135. unsigned int x = 0;
  136. unsigned int y = 0;
  137. for (const std::string &item : items) {
  138. // Is this an actual item?
  139. if (!item.empty()) {
  140. if (!success) {
  141. // This is the first nonempty item
  142. min_x = max_x = x;
  143. min_y = max_y = y;
  144. success = true;
  145. } else {
  146. if (x < min_x) min_x = x;
  147. if (x > max_x) max_x = x;
  148. if (y < min_y) min_y = y;
  149. if (y > max_y) max_y = y;
  150. }
  151. }
  152. // Step coordinate
  153. x++;
  154. if (x == width) {
  155. x = 0;
  156. y++;
  157. }
  158. }
  159. return success;
  160. }
  161. // Removes 1 from each item stack
  162. static void craftDecrementInput(CraftInput &input, IGameDef *gamedef)
  163. {
  164. for (auto &item : input.items) {
  165. if (item.count != 0)
  166. item.remove(1);
  167. }
  168. }
  169. // Removes 1 from each item stack with replacement support
  170. // Example: if replacements contains the pair ("bucket:bucket_water", "bucket:bucket_empty"),
  171. // a water bucket will not be removed but replaced by an empty bucket.
  172. static void craftDecrementOrReplaceInput(CraftInput &input,
  173. std::vector<ItemStack> &output_replacements,
  174. const CraftReplacements &replacements,
  175. IGameDef *gamedef)
  176. {
  177. if (replacements.pairs.empty()) {
  178. craftDecrementInput(input, gamedef);
  179. return;
  180. }
  181. // Make a copy of the replacements pair list
  182. std::vector<std::pair<std::string, std::string> > pairs = replacements.pairs;
  183. for (auto &item : input.items) {
  184. // Find an appropriate replacement
  185. bool found_replacement = false;
  186. for (auto j = pairs.begin(); j != pairs.end(); ++j) {
  187. if (inputItemMatchesRecipe(item.name, j->first, gamedef->idef())) {
  188. if (item.count == 1) {
  189. item.deSerialize(j->second, gamedef->idef());
  190. found_replacement = true;
  191. pairs.erase(j);
  192. break;
  193. }
  194. ItemStack rep;
  195. rep.deSerialize(j->second, gamedef->idef());
  196. item.remove(1);
  197. found_replacement = true;
  198. output_replacements.push_back(rep);
  199. break;
  200. }
  201. }
  202. // No replacement was found, simply decrement count by one
  203. if (!found_replacement && item.count > 0)
  204. item.remove(1);
  205. }
  206. }
  207. // Dump an itemstring matrix
  208. static std::string craftDumpMatrix(const std::vector<std::string> &items,
  209. unsigned int width)
  210. {
  211. std::ostringstream os(std::ios::binary);
  212. os << "{ ";
  213. unsigned int x = 0;
  214. for(std::vector<std::string>::size_type i = 0;
  215. i < items.size(); i++, x++) {
  216. if (x == width) {
  217. os << "; ";
  218. x = 0;
  219. } else if (x != 0) {
  220. os << ",";
  221. }
  222. os << '"' << items[i] << '"';
  223. }
  224. os << " }";
  225. return os.str();
  226. }
  227. // Dump an item matrix
  228. std::string craftDumpMatrix(const std::vector<ItemStack> &items,
  229. unsigned int width)
  230. {
  231. std::ostringstream os(std::ios::binary);
  232. os << "{ ";
  233. unsigned int x = 0;
  234. for (std::vector<ItemStack>::size_type i = 0;
  235. i < items.size(); i++, x++) {
  236. if (x == width) {
  237. os << "; ";
  238. x = 0;
  239. } else if (x != 0) {
  240. os << ",";
  241. }
  242. os << '"' << (items[i].getItemString()) << '"';
  243. }
  244. os << " }";
  245. return os.str();
  246. }
  247. /*
  248. CraftInput
  249. */
  250. std::string CraftInput::dump() const
  251. {
  252. std::ostringstream os(std::ios::binary);
  253. os << "(method=" << ((int)method) << ", items="
  254. << craftDumpMatrix(items, width) << ")";
  255. return os.str();
  256. }
  257. /*
  258. CraftOutput
  259. */
  260. std::string CraftOutput::dump() const
  261. {
  262. std::ostringstream os(std::ios::binary);
  263. os << "(item=\"" << item << "\", time=" << time << ")";
  264. return os.str();
  265. }
  266. /*
  267. CraftReplacements
  268. */
  269. std::string CraftReplacements::dump() const
  270. {
  271. std::ostringstream os(std::ios::binary);
  272. os<<"{";
  273. const char *sep = "";
  274. for (const auto &repl_p : pairs) {
  275. os << sep
  276. << '"' << (repl_p.first)
  277. << "\"=>\"" << (repl_p.second) << '"';
  278. sep = ",";
  279. }
  280. os << "}";
  281. return os.str();
  282. }
  283. /*
  284. CraftDefinitionShaped
  285. */
  286. std::string CraftDefinitionShaped::getName() const
  287. {
  288. return "shaped";
  289. }
  290. bool CraftDefinitionShaped::check(const CraftInput &input, IGameDef *gamedef) const
  291. {
  292. if (input.method != CRAFT_METHOD_NORMAL)
  293. return false;
  294. // Get input item matrix
  295. std::vector<std::string> inp_names = craftGetItemNames(input.items, gamedef);
  296. unsigned int inp_width = input.width;
  297. if (inp_width == 0)
  298. return false;
  299. while (inp_names.size() % inp_width != 0)
  300. inp_names.emplace_back("");
  301. // Get input bounds
  302. unsigned int inp_min_x = 0, inp_max_x = 0, inp_min_y = 0, inp_max_y = 0;
  303. if (!craftGetBounds(inp_names, inp_width, inp_min_x, inp_max_x,
  304. inp_min_y, inp_max_y))
  305. return false; // it was empty
  306. std::vector<std::string> rec_names;
  307. if (hash_inited)
  308. rec_names = recipe_names;
  309. else
  310. rec_names = craftGetItemNames(recipe, gamedef);
  311. // Get recipe item matrix
  312. unsigned int rec_width = width;
  313. if (rec_width == 0)
  314. return false;
  315. while (rec_names.size() % rec_width != 0)
  316. rec_names.emplace_back("");
  317. // Get recipe bounds
  318. unsigned int rec_min_x=0, rec_max_x=0, rec_min_y=0, rec_max_y=0;
  319. if (!craftGetBounds(rec_names, rec_width, rec_min_x, rec_max_x,
  320. rec_min_y, rec_max_y))
  321. return false; // it was empty
  322. // Different sizes?
  323. if (inp_max_x - inp_min_x != rec_max_x - rec_min_x ||
  324. inp_max_y - inp_min_y != rec_max_y - rec_min_y)
  325. return false;
  326. // Verify that all item names in the bounding box are equal
  327. unsigned int w = inp_max_x - inp_min_x + 1;
  328. unsigned int h = inp_max_y - inp_min_y + 1;
  329. for (unsigned int y=0; y < h; y++) {
  330. unsigned int inp_y = (inp_min_y + y) * inp_width;
  331. unsigned int rec_y = (rec_min_y + y) * rec_width;
  332. for (unsigned int x=0; x < w; x++) {
  333. unsigned int inp_x = inp_min_x + x;
  334. unsigned int rec_x = rec_min_x + x;
  335. if (!inputItemMatchesRecipe(
  336. inp_names[inp_y + inp_x],
  337. rec_names[rec_y + rec_x], gamedef->idef())) {
  338. return false;
  339. }
  340. }
  341. }
  342. return true;
  343. }
  344. CraftOutput CraftDefinitionShaped::getOutput(const CraftInput &input, IGameDef *gamedef) const
  345. {
  346. return CraftOutput(output, 0);
  347. }
  348. CraftInput CraftDefinitionShaped::getInput(const CraftOutput &output, IGameDef *gamedef) const
  349. {
  350. return CraftInput(CRAFT_METHOD_NORMAL,width,craftGetItems(recipe,gamedef));
  351. }
  352. void CraftDefinitionShaped::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  353. IGameDef *gamedef) const
  354. {
  355. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  356. }
  357. CraftHashType CraftDefinitionShaped::getHashType() const
  358. {
  359. assert(hash_inited); // Pre-condition
  360. bool has_group = false;
  361. for (const auto &recipe_name : recipe_names) {
  362. if (isGroupRecipeStr(recipe_name)) {
  363. has_group = true;
  364. break;
  365. }
  366. }
  367. if (has_group)
  368. return CRAFT_HASH_TYPE_COUNT;
  369. return CRAFT_HASH_TYPE_ITEM_NAMES;
  370. }
  371. u64 CraftDefinitionShaped::getHash(CraftHashType type) const
  372. {
  373. assert(hash_inited); // Pre-condition
  374. assert((type == CRAFT_HASH_TYPE_ITEM_NAMES)
  375. || (type == CRAFT_HASH_TYPE_COUNT)); // Pre-condition
  376. std::vector<std::string> rec_names = recipe_names;
  377. std::sort(rec_names.begin(), rec_names.end());
  378. return getHashForGrid(type, rec_names);
  379. }
  380. void CraftDefinitionShaped::initHash(IGameDef *gamedef)
  381. {
  382. if (hash_inited)
  383. return;
  384. hash_inited = true;
  385. recipe_names = craftGetItemNames(recipe, gamedef);
  386. }
  387. std::string CraftDefinitionShaped::dump() const
  388. {
  389. std::ostringstream os(std::ios::binary);
  390. os << "(shaped, output=\"" << output
  391. << "\", recipe=" << craftDumpMatrix(recipe, width)
  392. << ", replacements=" << replacements.dump() << ")";
  393. return os.str();
  394. }
  395. /*
  396. CraftDefinitionShapeless
  397. */
  398. std::string CraftDefinitionShapeless::getName() const
  399. {
  400. return "shapeless";
  401. }
  402. bool CraftDefinitionShapeless::check(const CraftInput &input, IGameDef *gamedef) const
  403. {
  404. if (input.method != CRAFT_METHOD_NORMAL)
  405. return false;
  406. // Filter empty items out of input
  407. std::vector<std::string> input_filtered;
  408. for (const auto &item : input.items) {
  409. if (!item.name.empty())
  410. input_filtered.push_back(item.name);
  411. }
  412. // If there is a wrong number of items in input, no match
  413. if (input_filtered.size() != recipe.size()) {
  414. /*dstream<<"Number of input items ("<<input_filtered.size()
  415. <<") does not match recipe size ("<<recipe.size()<<") "
  416. <<"of recipe with output="<<output<<std::endl;*/
  417. return false;
  418. }
  419. std::vector<std::string> recipe_copy;
  420. if (hash_inited)
  421. recipe_copy = recipe_names;
  422. else {
  423. recipe_copy = craftGetItemNames(recipe, gamedef);
  424. std::sort(recipe_copy.begin(), recipe_copy.end());
  425. }
  426. // Try with all permutations of the recipe,
  427. // start from the lexicographically first permutation (=sorted),
  428. // recipe_names is pre-sorted
  429. do {
  430. // If all items match, the recipe matches
  431. bool all_match = true;
  432. //dstream<<"Testing recipe (output="<<output<<"):";
  433. for (size_t i=0; i<recipe.size(); i++) {
  434. //dstream<<" ("<<input_filtered[i]<<" == "<<recipe_copy[i]<<")";
  435. if (!inputItemMatchesRecipe(input_filtered[i], recipe_copy[i],
  436. gamedef->idef())) {
  437. all_match = false;
  438. break;
  439. }
  440. }
  441. //dstream<<" -> match="<<all_match<<std::endl;
  442. if (all_match)
  443. return true;
  444. } while (std::next_permutation(recipe_copy.begin(), recipe_copy.end()));
  445. return false;
  446. }
  447. CraftOutput CraftDefinitionShapeless::getOutput(const CraftInput &input, IGameDef *gamedef) const
  448. {
  449. return CraftOutput(output, 0);
  450. }
  451. CraftInput CraftDefinitionShapeless::getInput(const CraftOutput &output, IGameDef *gamedef) const
  452. {
  453. return CraftInput(CRAFT_METHOD_NORMAL, 0, craftGetItems(recipe, gamedef));
  454. }
  455. void CraftDefinitionShapeless::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  456. IGameDef *gamedef) const
  457. {
  458. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  459. }
  460. CraftHashType CraftDefinitionShapeless::getHashType() const
  461. {
  462. assert(hash_inited); // Pre-condition
  463. bool has_group = false;
  464. for (const auto &recipe_name : recipe_names) {
  465. if (isGroupRecipeStr(recipe_name)) {
  466. has_group = true;
  467. break;
  468. }
  469. }
  470. if (has_group)
  471. return CRAFT_HASH_TYPE_COUNT;
  472. return CRAFT_HASH_TYPE_ITEM_NAMES;
  473. }
  474. u64 CraftDefinitionShapeless::getHash(CraftHashType type) const
  475. {
  476. assert(hash_inited); // Pre-condition
  477. assert(type == CRAFT_HASH_TYPE_ITEM_NAMES
  478. || type == CRAFT_HASH_TYPE_COUNT); // Pre-condition
  479. return getHashForGrid(type, recipe_names);
  480. }
  481. void CraftDefinitionShapeless::initHash(IGameDef *gamedef)
  482. {
  483. if (hash_inited)
  484. return;
  485. hash_inited = true;
  486. recipe_names = craftGetItemNames(recipe, gamedef);
  487. std::sort(recipe_names.begin(), recipe_names.end());
  488. }
  489. std::string CraftDefinitionShapeless::dump() const
  490. {
  491. std::ostringstream os(std::ios::binary);
  492. os << "(shapeless, output=\"" << output
  493. << "\", recipe=" << craftDumpMatrix(recipe, recipe.size())
  494. << ", replacements=" << replacements.dump() << ")";
  495. return os.str();
  496. }
  497. /*
  498. CraftDefinitionToolRepair
  499. */
  500. static ItemStack craftToolRepair(
  501. const ItemStack &item1,
  502. const ItemStack &item2,
  503. float additional_wear,
  504. IGameDef *gamedef)
  505. {
  506. IItemDefManager *idef = gamedef->idef();
  507. if (item1.count != 1 || item2.count != 1 || item1.name != item2.name
  508. || idef->get(item1.name).type != ITEM_TOOL
  509. || idef->get(item2.name).type != ITEM_TOOL) {
  510. // Failure
  511. return ItemStack();
  512. }
  513. s32 item1_uses = 65536 - (u32) item1.wear;
  514. s32 item2_uses = 65536 - (u32) item2.wear;
  515. s32 new_uses = item1_uses + item2_uses;
  516. s32 new_wear = 65536 - new_uses + floor(additional_wear * 65536 + 0.5);
  517. if (new_wear >= 65536)
  518. return ItemStack();
  519. if (new_wear < 0)
  520. new_wear = 0;
  521. ItemStack repaired = item1;
  522. repaired.wear = new_wear;
  523. return repaired;
  524. }
  525. std::string CraftDefinitionToolRepair::getName() const
  526. {
  527. return "toolrepair";
  528. }
  529. bool CraftDefinitionToolRepair::check(const CraftInput &input, IGameDef *gamedef) const
  530. {
  531. if (input.method != CRAFT_METHOD_NORMAL)
  532. return false;
  533. ItemStack item1;
  534. ItemStack item2;
  535. for (const auto &item : input.items) {
  536. if (!item.empty()) {
  537. if (item1.empty())
  538. item1 = item;
  539. else if (item2.empty())
  540. item2 = item;
  541. else
  542. return false;
  543. }
  544. }
  545. ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
  546. return !repaired.empty();
  547. }
  548. CraftOutput CraftDefinitionToolRepair::getOutput(const CraftInput &input, IGameDef *gamedef) const
  549. {
  550. ItemStack item1;
  551. ItemStack item2;
  552. for (const auto &item : input.items) {
  553. if (!item.empty()) {
  554. if (item1.empty())
  555. item1 = item;
  556. else if (item2.empty())
  557. item2 = item;
  558. }
  559. }
  560. ItemStack repaired = craftToolRepair(item1, item2, additional_wear, gamedef);
  561. return CraftOutput(repaired.getItemString(), 0);
  562. }
  563. CraftInput CraftDefinitionToolRepair::getInput(const CraftOutput &output, IGameDef *gamedef) const
  564. {
  565. std::vector<ItemStack> stack;
  566. stack.emplace_back();
  567. return CraftInput(CRAFT_METHOD_COOKING, additional_wear, stack);
  568. }
  569. void CraftDefinitionToolRepair::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  570. IGameDef *gamedef) const
  571. {
  572. craftDecrementInput(input, gamedef);
  573. }
  574. std::string CraftDefinitionToolRepair::dump() const
  575. {
  576. std::ostringstream os(std::ios::binary);
  577. os << "(toolrepair, additional_wear=" << additional_wear << ")";
  578. return os.str();
  579. }
  580. /*
  581. CraftDefinitionCooking
  582. */
  583. std::string CraftDefinitionCooking::getName() const
  584. {
  585. return "cooking";
  586. }
  587. bool CraftDefinitionCooking::check(const CraftInput &input, IGameDef *gamedef) const
  588. {
  589. if (input.method != CRAFT_METHOD_COOKING)
  590. return false;
  591. // Filter empty items out of input
  592. std::vector<std::string> input_filtered;
  593. for (const auto &item : input.items) {
  594. const std::string &name = item.name;
  595. if (!name.empty())
  596. input_filtered.push_back(name);
  597. }
  598. // If there is a wrong number of items in input, no match
  599. if (input_filtered.size() != 1) {
  600. /*dstream<<"Number of input items ("<<input_filtered.size()
  601. <<") does not match recipe size (1) "
  602. <<"of cooking recipe with output="<<output<<std::endl;*/
  603. return false;
  604. }
  605. // Check the single input item
  606. return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
  607. }
  608. CraftOutput CraftDefinitionCooking::getOutput(const CraftInput &input, IGameDef *gamedef) const
  609. {
  610. return CraftOutput(output, cooktime);
  611. }
  612. CraftInput CraftDefinitionCooking::getInput(const CraftOutput &output, IGameDef *gamedef) const
  613. {
  614. std::vector<std::string> rec;
  615. rec.push_back(recipe);
  616. return CraftInput(CRAFT_METHOD_COOKING,cooktime,craftGetItems(rec,gamedef));
  617. }
  618. void CraftDefinitionCooking::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  619. IGameDef *gamedef) const
  620. {
  621. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  622. }
  623. CraftHashType CraftDefinitionCooking::getHashType() const
  624. {
  625. if (isGroupRecipeStr(recipe_name))
  626. return CRAFT_HASH_TYPE_COUNT;
  627. return CRAFT_HASH_TYPE_ITEM_NAMES;
  628. }
  629. u64 CraftDefinitionCooking::getHash(CraftHashType type) const
  630. {
  631. if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
  632. return getHashForString(recipe_name);
  633. }
  634. if (type == CRAFT_HASH_TYPE_COUNT) {
  635. return 1;
  636. }
  637. // illegal hash type for this CraftDefinition (pre-condition)
  638. assert(false);
  639. return 0;
  640. }
  641. void CraftDefinitionCooking::initHash(IGameDef *gamedef)
  642. {
  643. if (hash_inited)
  644. return;
  645. hash_inited = true;
  646. recipe_name = craftGetItemName(recipe, gamedef);
  647. }
  648. std::string CraftDefinitionCooking::dump() const
  649. {
  650. std::ostringstream os(std::ios::binary);
  651. os << "(cooking, output=\"" << output
  652. << "\", recipe=\"" << recipe
  653. << "\", cooktime=" << cooktime << ")"
  654. << ", replacements=" << replacements.dump() << ")";
  655. return os.str();
  656. }
  657. /*
  658. CraftDefinitionFuel
  659. */
  660. std::string CraftDefinitionFuel::getName() const
  661. {
  662. return "fuel";
  663. }
  664. bool CraftDefinitionFuel::check(const CraftInput &input, IGameDef *gamedef) const
  665. {
  666. if (input.method != CRAFT_METHOD_FUEL)
  667. return false;
  668. // Filter empty items out of input
  669. std::vector<std::string> input_filtered;
  670. for (const auto &item : input.items) {
  671. const std::string &name = item.name;
  672. if (!name.empty())
  673. input_filtered.push_back(name);
  674. }
  675. // If there is a wrong number of items in input, no match
  676. if (input_filtered.size() != 1) {
  677. /*dstream<<"Number of input items ("<<input_filtered.size()
  678. <<") does not match recipe size (1) "
  679. <<"of fuel recipe with burntime="<<burntime<<std::endl;*/
  680. return false;
  681. }
  682. // Check the single input item
  683. return inputItemMatchesRecipe(input_filtered[0], recipe, gamedef->idef());
  684. }
  685. CraftOutput CraftDefinitionFuel::getOutput(const CraftInput &input, IGameDef *gamedef) const
  686. {
  687. return CraftOutput("", burntime);
  688. }
  689. CraftInput CraftDefinitionFuel::getInput(const CraftOutput &output, IGameDef *gamedef) const
  690. {
  691. std::vector<std::string> rec;
  692. rec.push_back(recipe);
  693. return CraftInput(CRAFT_METHOD_COOKING,(int)burntime,craftGetItems(rec,gamedef));
  694. }
  695. void CraftDefinitionFuel::decrementInput(CraftInput &input, std::vector<ItemStack> &output_replacements,
  696. IGameDef *gamedef) const
  697. {
  698. craftDecrementOrReplaceInput(input, output_replacements, replacements, gamedef);
  699. }
  700. CraftHashType CraftDefinitionFuel::getHashType() const
  701. {
  702. if (isGroupRecipeStr(recipe_name))
  703. return CRAFT_HASH_TYPE_COUNT;
  704. return CRAFT_HASH_TYPE_ITEM_NAMES;
  705. }
  706. u64 CraftDefinitionFuel::getHash(CraftHashType type) const
  707. {
  708. if (type == CRAFT_HASH_TYPE_ITEM_NAMES) {
  709. return getHashForString(recipe_name);
  710. }
  711. if (type == CRAFT_HASH_TYPE_COUNT) {
  712. return 1;
  713. }
  714. // illegal hash type for this CraftDefinition (pre-condition)
  715. assert(false);
  716. return 0;
  717. }
  718. void CraftDefinitionFuel::initHash(IGameDef *gamedef)
  719. {
  720. if (hash_inited)
  721. return;
  722. hash_inited = true;
  723. recipe_name = craftGetItemName(recipe, gamedef);
  724. }
  725. std::string CraftDefinitionFuel::dump() const
  726. {
  727. std::ostringstream os(std::ios::binary);
  728. os << "(fuel, recipe=\"" << recipe
  729. << "\", burntime=" << burntime << ")"
  730. << ", replacements=" << replacements.dump() << ")";
  731. return os.str();
  732. }
  733. /*
  734. Craft definition manager
  735. */
  736. class CCraftDefManager: public IWritableCraftDefManager
  737. {
  738. public:
  739. CCraftDefManager()
  740. {
  741. m_craft_defs.resize(craft_hash_type_max + 1);
  742. }
  743. virtual ~CCraftDefManager()
  744. {
  745. clear();
  746. }
  747. virtual bool getCraftResult(CraftInput &input, CraftOutput &output,
  748. std::vector<ItemStack> &output_replacement, bool decrementInput,
  749. IGameDef *gamedef) const
  750. {
  751. output.item = "";
  752. output.time = 0;
  753. // If all input items are empty, abort.
  754. bool all_empty = true;
  755. for (const auto &item : input.items) {
  756. if (!item.empty()) {
  757. all_empty = false;
  758. break;
  759. }
  760. }
  761. if (all_empty)
  762. return false;
  763. std::vector<std::string> input_names;
  764. input_names = craftGetItemNames(input.items, gamedef);
  765. std::sort(input_names.begin(), input_names.end());
  766. // Try hash types with increasing collision rate, and return if found.
  767. for (int type = 0; type <= craft_hash_type_max; type++) {
  768. u64 hash = getHashForGrid((CraftHashType) type, input_names);
  769. /*errorstream << "Checking type " << type << " with hash " << hash << std::endl;*/
  770. // We'd like to do "const [...] hash_collisions = m_craft_defs[type][hash];"
  771. // but that doesn't compile for some reason. This does.
  772. auto col_iter = (m_craft_defs[type]).find(hash);
  773. if (col_iter == (m_craft_defs[type]).end())
  774. continue;
  775. const std::vector<CraftDefinition*> &hash_collisions = col_iter->second;
  776. // Walk crafting definitions from back to front, so that later
  777. // definitions can override earlier ones.
  778. for (std::vector<CraftDefinition*>::size_type
  779. i = hash_collisions.size(); i > 0; i--) {
  780. CraftDefinition *def = hash_collisions[i - 1];
  781. /*errorstream << "Checking " << input.dump() << std::endl
  782. << " against " << def->dump() << std::endl;*/
  783. if (def->check(input, gamedef)) {
  784. // Get output, then decrement input (if requested)
  785. output = def->getOutput(input, gamedef);
  786. if (decrementInput)
  787. def->decrementInput(input, output_replacement, gamedef);
  788. /*errorstream << "Check RETURNS TRUE" << std::endl;*/
  789. return true;
  790. }
  791. }
  792. }
  793. return false;
  794. }
  795. virtual std::vector<CraftDefinition*> getCraftRecipes(CraftOutput &output,
  796. IGameDef *gamedef, unsigned limit=0) const
  797. {
  798. std::vector<CraftDefinition*> recipes;
  799. auto vec_iter = m_output_craft_definitions.find(output.item);
  800. if (vec_iter == m_output_craft_definitions.end())
  801. return recipes;
  802. const std::vector<CraftDefinition*> &vec = vec_iter->second;
  803. recipes.reserve(limit ? MYMIN(limit, vec.size()) : vec.size());
  804. for (std::vector<CraftDefinition*>::size_type i = vec.size();
  805. i > 0; i--) {
  806. CraftDefinition *def = vec[i - 1];
  807. if (limit && recipes.size() >= limit)
  808. break;
  809. recipes.push_back(def);
  810. }
  811. return recipes;
  812. }
  813. virtual bool clearCraftRecipesByOutput(const CraftOutput &output, IGameDef *gamedef)
  814. {
  815. auto vec_iter = m_output_craft_definitions.find(output.item);
  816. if (vec_iter == m_output_craft_definitions.end())
  817. return false;
  818. std::vector<CraftDefinition*> &vec = vec_iter->second;
  819. for (auto def : vec) {
  820. // Recipes are not yet hashed at this point
  821. std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
  822. std::vector<CraftDefinition*> new_vec_by_input;
  823. /* We will preallocate necessary memory addresses, so we don't need to reallocate them later.
  824. This would save us some performance. */
  825. new_vec_by_input.reserve(unhashed_inputs_vec.size());
  826. for (auto &i2 : unhashed_inputs_vec) {
  827. if (def != i2) {
  828. new_vec_by_input.push_back(i2);
  829. }
  830. }
  831. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
  832. }
  833. m_output_craft_definitions.erase(output.item);
  834. return true;
  835. }
  836. virtual bool clearCraftRecipesByInput(CraftMethod craft_method, unsigned int craft_grid_width,
  837. const std::vector<std::string> &recipe, IGameDef *gamedef)
  838. {
  839. bool all_empty = true;
  840. for (const auto &i : recipe) {
  841. if (!i.empty()) {
  842. all_empty = false;
  843. break;
  844. }
  845. }
  846. if (all_empty)
  847. return false;
  848. CraftInput input(craft_method, craft_grid_width, craftGetItems(recipe, gamedef));
  849. // Recipes are not yet hashed at this point
  850. std::vector<CraftDefinition*> &unhashed_inputs_vec = m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
  851. std::vector<CraftDefinition*> new_vec_by_input;
  852. bool got_hit = false;
  853. for (std::vector<CraftDefinition*>::size_type
  854. i = unhashed_inputs_vec.size(); i > 0; i--) {
  855. CraftDefinition *def = unhashed_inputs_vec[i - 1];
  856. /* If the input doesn't match the recipe definition, this recipe definition later
  857. will be added back in source map. */
  858. if (!def->check(input, gamedef)) {
  859. new_vec_by_input.push_back(def);
  860. continue;
  861. }
  862. CraftOutput output = def->getOutput(input, gamedef);
  863. got_hit = true;
  864. auto vec_iter = m_output_craft_definitions.find(output.item);
  865. if (vec_iter == m_output_craft_definitions.end())
  866. continue;
  867. std::vector<CraftDefinition*> &vec = vec_iter->second;
  868. std::vector<CraftDefinition*> new_vec_by_output;
  869. /* We will preallocate necessary memory addresses, so we don't need
  870. to reallocate them later. This would save us some performance. */
  871. new_vec_by_output.reserve(vec.size());
  872. for (auto &vec_i : vec) {
  873. /* If pointers from map by input and output are not same,
  874. we will add 'CraftDefinition*' to a new vector. */
  875. if (def != vec_i) {
  876. /* Adding dereferenced iterator value (which are
  877. 'CraftDefinition' reference) to a new vector. */
  878. new_vec_by_output.push_back(vec_i);
  879. }
  880. }
  881. // Swaps assigned to current key value with new vector for output map.
  882. m_output_craft_definitions[output.item].swap(new_vec_by_output);
  883. }
  884. if (got_hit)
  885. // Swaps value with new vector for input map.
  886. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].swap(new_vec_by_input);
  887. return got_hit;
  888. }
  889. virtual std::string dump() const
  890. {
  891. std::ostringstream os(std::ios::binary);
  892. os << "Crafting definitions:\n";
  893. for (int type = 0; type <= craft_hash_type_max; ++type) {
  894. for (auto it = m_craft_defs[type].begin();
  895. it != m_craft_defs[type].end(); ++it) {
  896. for (std::vector<CraftDefinition*>::size_type i = 0;
  897. i < it->second.size(); i++) {
  898. os << "type " << type
  899. << " hash " << it->first
  900. << " def " << it->second[i]->dump()
  901. << "\n";
  902. }
  903. }
  904. }
  905. return os.str();
  906. }
  907. virtual void registerCraft(CraftDefinition *def, IGameDef *gamedef)
  908. {
  909. verbosestream << "registerCraft: registering craft definition: "
  910. << def->dump() << std::endl;
  911. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0].push_back(def);
  912. CraftInput input;
  913. std::string output_name = craftGetItemName(
  914. def->getOutput(input, gamedef).item, gamedef);
  915. m_output_craft_definitions[output_name].push_back(def);
  916. }
  917. virtual void clear()
  918. {
  919. for (int type = 0; type <= craft_hash_type_max; ++type) {
  920. for (auto &it : m_craft_defs[type]) {
  921. for (auto &iit : it.second) {
  922. delete iit;
  923. }
  924. it.second.clear();
  925. }
  926. m_craft_defs[type].clear();
  927. }
  928. m_output_craft_definitions.clear();
  929. }
  930. virtual void initHashes(IGameDef *gamedef)
  931. {
  932. // Move the CraftDefs from the unhashed layer into layers higher up.
  933. std::vector<CraftDefinition *> &unhashed =
  934. m_craft_defs[(int) CRAFT_HASH_TYPE_UNHASHED][0];
  935. for (auto def : unhashed) {
  936. // Initialize and get the definition's hash
  937. def->initHash(gamedef);
  938. CraftHashType type = def->getHashType();
  939. u64 hash = def->getHash(type);
  940. // Enter the definition
  941. m_craft_defs[type][hash].push_back(def);
  942. }
  943. unhashed.clear();
  944. }
  945. private:
  946. //TODO: change both maps to unordered_map when c++11 can be used
  947. std::vector<std::map<u64, std::vector<CraftDefinition*> > > m_craft_defs;
  948. std::map<std::string, std::vector<CraftDefinition*> > m_output_craft_definitions;
  949. };
  950. IWritableCraftDefManager* createCraftDefManager()
  951. {
  952. return new CCraftDefManager();
  953. }