craftdef.cpp 26 KB

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