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