Crossfire Server  1.75.0
recipe.cpp
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
35 #include "global.h"
36 
37 #include <assert.h>
38 #include <ctype.h>
39 #include <stdlib.h>
40 #include <string.h>
41 #include <memory>
42 
43 #include "object.h"
44 #include "assets.h"
45 #include "AssetsManager.h"
46 
47 static void build_stringlist(const char *str, char ***result_list, size_t *result_size);
48 
51 
60 static recipelist *init_recipelist(void) {
61  recipelist *tl = (recipelist *)malloc(sizeof(recipelist));
62  if (tl == NULL)
64  tl->total_chance = 0;
65  tl->number = 0;
66  tl->items = NULL;
67  tl->next = NULL;
68  return tl;
69 }
70 
79 static recipe *get_empty_formula(void) {
80  // This used to be a malloc followed by setting everything to zero.
81  // So just use calloc to make it faster.
82  // SilverNexus -- 2018-10-22
83  recipe *t = (recipe *)calloc(1, sizeof(recipe));
84  if (t == NULL)
86  return t;
87 }
88 
99  recipelist *fl = formulalist;
100  int number = i;
101 
102  while (fl && number > 1) {
103  if (!(fl = fl->next))
104  break;
105  number--;
106  }
107  return fl;
108 }
109 
120 static int check_recipe(const recipe *rp) {
121  size_t i;
122  int result;
123 
124  result = 1;
125  for (i = 0; i < rp->arch_names; i++) {
126  if (try_find_archetype(rp->arch_name[i]) != NULL) {
127  if (strcmp(rp->title, "NONE")) {
128  const artifact *art = locate_recipe_artifact(rp, i);
129 
130  if (!art) {
131  LOG(llevError, "Formula %s of %s has no artifact.\n", rp->arch_name[i], rp->title);
132  result = 0;
133  }
134  }
135  } else {
136  LOG(llevError, "Can't find archetype %s for formula %s\n", rp->arch_name[i], rp->title);
137  result = 0;
138  }
139  }
140 
141  return result;
142 }
143 
148  const recipelist *rl = formulalist;
149  int abort = 0;
150  while (rl) {
151  const recipe *rp = rl->items;
152  while (rp) {
153  if (!check_recipe(rp)) {
154  abort = 1;
155  }
156  rp = rp->next;
157  }
158  rl = rl->next;
159  }
160  return !abort;
161 }
162 
166 void init_formulae(BufferReader *reader, const char *filename) {
167  char *buf, *cp, *next;
168  recipe *formula = NULL;
169  recipelist *fl;
170  linked_char *tmp;
171  int value;
172 
173  if (!formulalist)
174  formulalist = init_recipelist();
175 
176  while ((buf = bufferreader_next_line(reader)) != NULL) {
177  if (*buf == '#' || *buf == '\0')
178  continue;
179  cp = buf;
180  while (*cp == ' ') /* Skip blanks */
181  cp++;
182 
183  if (!strncmp(cp, "Remove ", 7)) {
184  if (strcmp(cp + 7, "*") == 0) {
186  formulalist = init_recipelist();
187  } else {
188  LOG(llevError, "Recipes: only '*' is accepted for 'Remove' at %s:%zu\n", filename, bufferreader_current_line(reader));
189  }
190  continue;
191  }
192  if (!strncmp(cp, "Object", 6)) {
193  formula = get_empty_formula();
194  formula->title = add_string(strchr(cp, ' ')+1);
195  } else if (formula == NULL) {
196  LOG(llevError, "recipe.c: First key in formulae file %s is not \"Object\".\n", filename);
198  } else if (!strncmp(cp, "keycode", 7)) {
199  formula->keycode = add_string(strchr(cp, ' ')+1);
200  } else if (sscanf(cp, "trans %d", &value)) {
201  formula->transmute = value;
202  } else if (sscanf(cp, "yield %d", &value)) {
203  formula->yield = value;
204  } else if (sscanf(cp, "chance %d", &value)) {
205  formula->chance = value;
206  } else if (sscanf(cp, "exp %d", &value)) {
207  formula->exp = value;
208  } else if (sscanf(cp, "diff %d", &value)) {
209  formula->diff = value;
210  } else if (!strncmp(cp, "ingred", 6)) {
211  int numb_ingred;
212  formula->ingred_count = 1;
213  cp = strchr(cp, ' ')+1;
214  do {
215  if ((next = strchr(cp, ',')) != NULL) {
216  *(next++) = '\0';
217  formula->ingred_count++;
218  }
219  tmp = (linked_char *)malloc(sizeof(linked_char));
220  /* trim the string */
221  while (*cp == ' ')
222  cp++;
223  while (*cp != '\0' && cp[strlen(cp) - 1] == ' ')
224  cp[strlen(cp) - 1] = '\0';
225  tmp->name = add_string(cp);
226  tmp->next = formula->ingred;
227  formula->ingred = tmp;
228  /* each ingredient's ASCII value is coadded. Later on this
229  * value will be used allow us to search the formula lists
230  * quickly for the right recipe.
231  */
232  formula->index += strtoint(cp);
233  } while ((cp = next) != NULL);
234  /* now find the correct (# of ingred ordered) formulalist */
235  numb_ingred = formula->ingred_count;
236  fl = formulalist;
237  while (numb_ingred != 1) {
238  if (!fl->next)
239  fl->next = init_recipelist();
240  fl = fl->next;
241  numb_ingred--;
242  }
243  formula->next = fl->items;
244  fl->items = formula;
245  } else if (!strncmp(cp, "arch", 4)) {
246  build_stringlist(strchr(cp, ' ')+1, &formula->arch_name, &formula->arch_names);
247  } else if (!strncmp(cp, "skill", 5)) {
248  formula->skill = add_string(strchr(cp, ' ')+1);
249  } else if (!strncmp(cp, "cauldron", 8)) {
250  formula->cauldron = add_string(strchr(cp, ' ')+1);
251  } else if (!strncmp(cp, "failure_arch ", 13)) {
252  formula->failure_arch = add_string(strchr(cp, ' ')+1);
253  } else if (!strncmp(cp, "failure_message ", 16)) {
254  formula->failure_message = add_string(strchr(cp, ' ')+1);
255  } else if (sscanf(cp, "min_level %d", &value)) {
256  formula->min_level = value;
257  } else if (!strncmp(cp, "tool ", 5)) {
258  build_stringlist(strchr(cp, ' ')+1, &formula->tool, &formula->tool_size);
259  } else if (sscanf(cp, "combination %d", &value)) {
260  formula->is_combination = value ? 1 : 0;
261  } else
262  LOG(llevError, "Unknown input in file %s: %s\n", filename, buf);
263  }
264  /* Set the total chance and count for each formula list.
265  * This needs to be done at the end to avoid dependancies on the field order in the file
266  */
267  for (fl = formulalist; fl; fl = fl->next) {
268  fl->total_chance = 0;
269  fl->number = 0;
270  for (formula = fl->items; formula; formula = formula->next) {
271  fl->total_chance += formula->chance;
272  fl->number++;
273  }
274  }
275  LOG(llevDebug, "done.\n");
276 }
277 
292 bool check_formulae(void) {
293  recipelist *fl;
294  recipe *check, *formula;
295  int numb = 1, tool_match;
296  size_t tool_i,tool_j;
297  bool success = true;
298 
299  LOG(llevDebug, "Checking formulae lists...\n");
300 
301  for (fl = formulalist; fl != NULL; fl = fl->next) {
302  for (formula = fl->items; formula != NULL; formula = formula->next) {
303  for (check = formula->next; check != NULL; check = check->next)
304  /* If one recipe has a tool and another a caudron, we should be able to handle it */
305  if (check->index == formula->index && check->cauldron == formula->cauldron && (check->tool_size == formula->tool_size)) {
306  /* Check the tool list to make sure they have no matches */
307  if (check->tool_size > 0 && check->tool && formula->tool)
308  {
309  tool_match = 0;
310  for (tool_i = 0; tool_i < formula->tool_size; ++tool_i)
311  /* If it turns out these lists are sorted, then we could optimize this better. */
312  for (tool_j = 0; tool_j < check->tool_size; ++tool_j)
313  if (strcmp(formula->tool[tool_i], check->tool[tool_j]) == 0) {
314  tool_match = 1;
315  break; /* TODO: break out of the double loop */
316  }
317  }
318  else
319  tool_match = 1; /* If we get here, we matched on the cauldron */
320  /* Check to see if we have a denoted match */
321  if (tool_match) {
322  /* if the recipes don't have the same facility, then no issue anyway. */
323  LOG(llevError, " ERROR: On %d ingred list:\n", numb);
324  LOG(llevError, "Formulae [%s] of %s and [%s] of %s have matching index id (%d)\n",
325  formula->arch_name[0], formula->title, check->arch_name[0], check->title, formula->index);
326  success = false;
327  }
328  }
329  for (size_t idx = 0; idx < formula->arch_names; idx++) {
330  if (try_find_archetype(formula->arch_name[idx]) == NULL) {
331  LOG(llevError, "Formulae %s of %s (%d ingredients) references non existent archetype %s\n",
332  formula->arch_name[0], formula->title, numb, formula->arch_name[idx]);
333  success = false;
334  }
335  }
336  }
337  numb++;
338  }
339 
340  return success;
341 }
342 
343 static long recipe_find_ingredient_cost(const char *name);
344 
352 void dump_alchemy(void) {
353  recipelist *fl = formulalist;
354  recipe *formula = NULL;
355  linked_char *next;
356  int num_ingred = 1;
357 
358  fprintf(stdout, "name,index,num_ingreds,chance,skill,difficulty,exp,cauldron,yield,ingredients,ingred_price,result_price\n");
359  while (fl) {
360  for (formula = fl->items; formula != NULL; formula = formula->next) {
361  const archetype *at = NULL;
362  const artifact *art = NULL;
363  char buf[MAX_BUF];
364  size_t i;
365 
366  for (i = 0; i < formula->arch_names; i++) {
367  const char *string = formula->arch_name[i];
368 
369  if ((at = try_find_archetype(string)) != NULL) {
370  art = locate_recipe_artifact(formula, i);
371  if (!art && strcmp(formula->title, "NONE"))
372  LOG(llevError, "Formula %s has no artifact\n", formula->title);
373  else {
374  if (strcmp(formula->title, "NONE"))
375  snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
376  else
377  strlcpy(buf, string, sizeof(buf));
378  fprintf(stdout, "%s,%d,%d,%d,%s,", buf, formula->index, num_ingred, formula->chance, formula->skill);
379  fprintf(stdout, "%d,%d,", formula->diff, formula->exp);
380  fprintf(stdout, "%s,%d,", formula->cauldron, formula->yield);
381  int ingred_cost = 0;
382  if (formula->ingred != NULL) {
383  int nval = 0, tval = 0;
384  fprintf(stdout, "\"");
385  for (next = formula->ingred; next != NULL; next = next->next) {
386  if (nval != 0)
387  fprintf(stdout, ",");
388  fprintf(stdout, "%s(%d)", next->name, (nval = strtoint(next->name)));
389  ingred_cost += recipe_find_ingredient_cost(next->name);
390  tval += nval;
391  }
392  fprintf(stdout, "\",");
393  if (tval != formula->index)
394  fprintf(stdout, "WARNING:ingredient list and formula values not equal.\n");
395  }
396  fprintf(stdout, "%d,", ingred_cost);
397  int result_price = price_base(&at->clone);
398  if (art != NULL && art->item != NULL)
399  result_price *= art->item->value;
400  fprintf(stdout, "%d\n", result_price);
401  }
402  } else
403  LOG(llevError, "Can't find archetype:%s for formula %s\n", string, formula->title);
404  }
405  }
406  fl = fl->next;
407  num_ingred++;
408  }
409 }
410 
425 archetype *find_treasure_by_name(const treasure *t, const char *name, int depth) {
426  treasurelist *tl;
427  archetype *at;
428 
429  if (depth > 10)
430  return NULL;
431 
432  while (t != NULL) {
433  if (t->name != NULL) {
434  tl = find_treasurelist(t->name);
435  at = find_treasure_by_name(tl->items, name, depth+1);
436  if (at != NULL)
437  return at;
438  } else {
439  if (!strcasecmp(t->item->clone.name, name))
440  return t->item;
441  }
442  if (t->next_yes != NULL) {
443  at = find_treasure_by_name(t->next_yes, name, depth);
444  if (at != NULL)
445  return at;
446  }
447  if (t->next_no != NULL) {
448  at = find_treasure_by_name(t->next_no, name, depth);
449  if (at != NULL)
450  return at;
451  }
452  t = t->next;
453  }
454  return NULL;
455 }
456 
476 static long recipe_find_ingredient_cost(const char *name) {
477  long mult;
478  const char *cp;
479  char part1[100];
480  char part2[100];
481 
482  /* same as atoi(), but skip number */
483  mult = 0;
484  while (isdigit(*name)) {
485  mult = 10*mult+(*name-'0');
486  name++;
487  }
488  if (mult > 0)
489  name++;
490  else
491  mult = 1;
492  /* first, try to match the name of an archetype */
493 
494  long value = 0;
495  bool found = false;
496  getManager()->archetypes()->each([&value, &found, &name, &part1] (const archetype *at) {
497  if (found) {
498  return;
499  }
500 
501  if (at->clone.title != NULL) {
502  /* inefficient, but who cares? */
503  snprintf(part1, sizeof(part1), "%s %s", at->clone.name, at->clone.title);
504  if (!strcasecmp(part1, name)) {
505  value = price_base(&at->clone);
506  found = true;
507  return;
508  }
509  }
510  if (!strcasecmp(at->clone.name, name)) {
511  value = price_base(&at->clone);
512  found = true;
513  return;
514  }
515  });
516  if (found) {
517  return mult * value;
518  }
519 
520  /* second, try to match an artifact ("arch of something") */
521  cp = strstr(name, " of ");
522  if (cp != NULL) {
523  safe_strncpy(part1, name, sizeof(part1));
524  part1[cp-name] = '\0';
525  safe_strncpy(part2, cp + 4, sizeof(part2));
526  getManager()->archetypes()->each([&value, &found, &part1, &part2] (const archetype *at) {
527  if (found) {
528  return;
529  }
530 
531  if (!strcasecmp(at->clone.name, part1) && at->clone.title == NULL) {
532  /* find the first artifact derived from that archetype (same type) */
533  for (auto al = first_artifactlist; al != NULL; al = al->next) {
534  if (al->type == at->clone.type) {
535  for (const auto art : al->items) {
536  if (!strcasecmp(art->item->name, part2)) {
537  value = price_base(&at->clone) * art->item->value;
538  found = true;
539  return;
540  }
541  }
542  }
543  }
544  }
545  });
546  }
547  if (found) {
548  return mult * value;
549  }
550 
551  /* third, try to match a body part ("arch's something") */
552  cp = strstr(name, "'s ");
553  if (cp != NULL) {
554  safe_strncpy(part1, name, sizeof(part1));
555  part1[cp-name] = '\0';
556  safe_strncpy(part2, cp + 3, sizeof(part2));
557  /* examine all archetypes matching the first part of the name */
558  getManager()->archetypes()->each([&value, &found, &part1, &part2] (const archetype *at) {
559  if (found) {
560  return;
561  }
562  if (!strcasecmp(at->clone.name, part1) && at->clone.title == NULL) {
563  if (at->clone.randomitems != NULL) {
564  auto at2 = find_treasure_by_name(at->clone.randomitems->items, part2, 0);
565  if (at2 != NULL) {
566  value = price_base(&at2->clone);
567  found = true;
568  return;
569  }
570  }
571  }
572  });
573  }
574  if (found) {
575  return mult * value;
576  }
577 
578  /* failed to find any matching items -- formula should be checked */
579  LOG(llevError, "Couldn't find cost for ingredient %s\n", name);
580  return -1;
581 }
582 
591 void dump_alchemy_costs(void) {
592  recipelist *fl = formulalist;
593  recipe *formula = NULL;
594  linked_char *next;
595  int num_ingred = 1;
596  int num_errors = 0;
597  long cost;
598  long tcost;
599 
600  fprintf(logfile, "\n");
601  while (fl) {
602  fprintf(logfile, "\n Formulae with %d ingredient%s %d Formulae with total_chance=%d\n", num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
603  for (formula = fl->items; formula != NULL; formula = formula->next) {
604  const artifact *art = NULL;
605  const archetype *at = NULL;
606  char buf[MAX_BUF];
607  size_t i;
608 
609  for (i = 0; i < formula->arch_names; i++) {
610  const char *string = formula->arch_name[i];
611 
612  if ((at = try_find_archetype(string)) != NULL) {
613  art = locate_recipe_artifact(formula, i);
614  if (!art && strcmp(formula->title, "NONE"))
615  LOG(llevError, "Formula %s has no artifact\n", formula->title);
616  else {
617  if (!strcmp(formula->title, "NONE"))
618  strlcpy(buf, string, sizeof(buf));
619  else
620  snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
621  fprintf(logfile, "\n%-40s bookchance %3d skill %s\n", buf, formula->chance, formula->skill);
622  if (formula->ingred != NULL) {
623  tcost = 0;
624  for (next = formula->ingred; next != NULL; next = next->next) {
625  cost = recipe_find_ingredient_cost(next->name);
626  if (cost < 0)
627  num_errors++;
628  fprintf(logfile, "\t%-33s%5ld\n", next->name, cost);
629  if (cost < 0 || tcost < 0)
630  tcost = -1;
631  else
632  tcost += cost;
633  }
634  if (art != NULL && art->item != NULL)
635  cost = price_base(&at->clone)*art->item->value;
636  else
637  cost = price_base(&at->clone);
638  fprintf(logfile, "\t\tBuying result costs: %5ld", cost);
639  if (formula->yield > 1) {
640  fprintf(logfile, " to %ld (max %d items)\n", cost*formula->yield, formula->yield);
641  cost = cost*(formula->yield+1L)/2L;
642  } else
643  fprintf(logfile, "\n");
644  fprintf(logfile, "\t\tIngredients cost: %5ld\n\t\tComment: ", tcost);
645  if (tcost < 0)
646  fprintf(logfile, "Could not find some ingredients. Check the formula!\n");
647  else if (tcost > cost)
648  fprintf(logfile, "Ingredients are much too expensive. Useless formula.\n");
649  else if (tcost*2L > cost)
650  fprintf(logfile, "Ingredients are too expensive.\n");
651  else if (tcost*10L < cost)
652  fprintf(logfile, "Ingredients are too cheap.\n");
653  else
654  fprintf(logfile, "OK.\n");
655  }
656  }
657  } else
658  LOG(llevError, "Can't find archetype:%s for formula %s\n", string, formula->title);
659  }
660  }
661  fprintf(logfile, "\n");
662  fl = fl->next;
663  num_ingred++;
664  }
665  if (num_errors > 0)
666  fprintf(logfile, "WARNING: %d objects required by the formulae do not exist in the game.\n", num_errors);
667 }
668 
677 static const char *ingred_name(const char *name) {
678  const char *cp = name;
679 
680  if (atoi(cp))
681  cp = strchr(cp, ' ')+1;
682  return cp;
683 }
684 
693 static int numb_ingred(const char *buf) {
694  int numb;
695 
696  if ((numb = atoi(buf)))
697  return numb;
698  else
699  return 1;
700 }
701 
712 int strtoint(const char *buf) {
713  const char *cp = ingred_name(buf);
714  int val = 0, len = strlen(cp), mult = numb_ingred(buf);
715 
716  while (len) {
717  val += tolower(*cp);
718  cp++; len--;
719  }
720  return val*mult;
721 }
722 
733 const artifact *locate_recipe_artifact(const recipe *rp, size_t idx) {
734  std::unique_ptr<object, void(*)(object *)> item(create_archetype(rp->arch_name[idx]), object_free_drop_inventory);
735  const artifactlist *at = NULL;
736 
737  if (!item)
738  return (artifact *)NULL;
739 
740  if ((at = find_artifactlist(item->type)))
741  for (auto art : at->items)
742  if (!strcmp(art->item->name, rp->title) && legal_artifact_combination(item.get(), art))
743  return art;
744 
745  return nullptr;
746 }
747 
755  recipelist *fl = NULL;
756  int number = 0, roll = 0;
757 
758  /* first, determine # of recipelist we have */
759  for (fl = get_formulalist(1); fl; fl = fl->next)
760  number++;
761 
762  /* now, randomly choose one */
763  if (number > 0)
764  roll = RANDOM()%number;
765 
766  fl = get_formulalist(1);
767  while (roll && fl) {
768  if (fl->next)
769  fl = fl->next;
770  else
771  break;
772  roll--;
773  }
774  if (!fl) /* failed! */
775  LOG(llevError, "get_random_recipelist(): no recipelists found!\n");
776  else if (fl->total_chance == 0)
777  fl = get_random_recipelist();
778 
779  return fl;
780 }
781 
791  recipelist *fl = rpl;
792  recipe *rp = NULL;
793  int r = 0;
794 
795  /* looks like we have to choose a random one */
796  if (fl == NULL)
797  if ((fl = get_random_recipelist()) == NULL)
798  return rp;
799 
800  if (fl->total_chance > 0) {
801  r = RANDOM()%fl->total_chance;
802  for (rp = fl->items; rp; rp = rp->next) {
803  r -= rp->chance;
804  if (r < 0)
805  break;
806  }
807  }
808  return rp;
809 }
810 
814 void free_all_recipes(void) {
815  recipelist *fl, *flnext;
816  recipe *formula = NULL, *next;
817  linked_char *lchar, *charnext;
818 
819  LOG(llevDebug, "Freeing all the recipes\n");
820  for (fl = formulalist; fl != NULL; fl = flnext) {
821  flnext = fl->next;
822 
823  for (formula = fl->items; formula != NULL; formula = next) {
824  next = formula->next;
825 
826  free(formula->arch_name[0]);
827  free(formula->arch_name);
828  if (formula->title)
829  free_string(formula->title);
830  if (formula->skill)
831  free_string(formula->skill);
832  if (formula->cauldron)
833  free_string(formula->cauldron);
834  if (formula->failure_arch)
835  free_string(formula->failure_arch);
836  if (formula->failure_message)
837  free_string(formula->failure_message);
838  for (lchar = formula->ingred; lchar; lchar = charnext) {
839  charnext = lchar->next;
840  free_string(lchar->name);
841  free(lchar);
842  }
843  if (formula->tool)
844  free(formula->tool[0]);
845  free(formula->tool);
846  free(formula);
847  }
848  free(fl);
849  }
850  formulalist = NULL;
851 }
852 
864 static void build_stringlist(const char *str, char ***result_list, size_t *result_size) {
865  char *dup;
866  char *p;
867  size_t size;
868  size_t i;
869 
870  dup = strdup_local(str);
871  if (dup == NULL)
873 
874  size = 0;
875  for (p = strtok(dup, ","); p != NULL; p = strtok(NULL, ","))
876  size++;
877 
878  assert(size > 0);
879  *result_list = static_cast<char **>(malloc(sizeof(**result_list) * size));
880  if (*result_list == NULL)
882  *result_size = size;
883 
884  for (i = 0; i < size; i++) {
885  (*result_list)[i] = dup;
886  dup = dup+strlen(dup)+1;
887  }
888 }
889 
897 recipe *find_recipe_for_tool(const char *tool, recipe *from) {
898  size_t t;
899  recipelist *list = from ? get_formulalist(from->ingred_count) : formulalist;
900  recipe *test = from ? from->next : list->items;
901 
902  while (list) {
903  while (test) {
904  for (t = 0; t < test->tool_size; t++) {
905  if (strcmp(test->tool[t], tool) == 0) {
906  return test;
907  }
908  }
909  test = test->next;
910  }
911 
912  list = list->next;
913  }
914 
915  return NULL;
916 }
917 
923 const Face *recipe_get_face(const recipe *rp) {
924  const artifact *art;
925  archetype *arch;
926  object *item;
927  const Face *face;
928 
929  if (rp->arch_names == 0)
930  return NULL;
931 
932  arch = try_find_archetype(rp->arch_name[0]);
933  if (arch == NULL) {
934  return NULL;
935  }
936  if (strcmp(rp->title, "NONE") == 0) {
937  return arch->clone.face;
938  }
939 
940  art = locate_recipe_artifact(rp, 0);
941  if (art == NULL)
942  return arch->clone.face;
943 
944  face = arch->clone.face;
945  item = arch_to_object(arch);
946  give_artifact_abilities(item, art->item);
948  if (item->face != NULL && item->face != blank_face)
949  face = item->face;
951 
952  return face;
953 }
954 
965 const char *recipe_get_difficulty_string(int difficulty) {
966  if (difficulty < 5)
967  return "basic";
968  if (difficulty < 10)
969  return "simple";
970  if (difficulty < 15)
971  return "advanced";
972  if (difficulty < 20)
973  return "complicated";
974  if (difficulty < 25)
975  return "difficult";
976  if (difficulty < 30)
977  return "challenging";
978  if (difficulty < 35)
979  return "frustrating";
980  return "vexatious";
981 }
Error, serious thing.
Definition: logger.h:11
int diff
Alchemical dfficulty level.
Definition: recipe.h:16
FILE * logfile
Used by server/daemon.c.
Definition: init.cpp:114
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.cpp:58
recipe * find_recipe_for_tool(const char *tool, recipe *from)
Find a recipe for a specified tool.
Definition: recipe.cpp:897
#define strdup_local
Definition: compat.h:29
recipelist * next
Pointer to next recipe list.
Definition: recipe.h:41
recipe * get_random_recipe(recipelist *rpl)
Gets a random recipe from a list, based on chance.
Definition: recipe.cpp:790
int total_chance
Total chance of the recipes in this list.
Definition: recipe.h:38
static const char * ingred_name(const char *name)
Extracts the name from an ingredient.
Definition: recipe.cpp:677
New face structure - this enforces the notion that data is face by face only - you can not change the...
Definition: face.h:14
void dump_alchemy_costs(void)
Dumps to output all costs of recipes.
Definition: recipe.cpp:591
sstring name
If non null, name of list to use instead.
Definition: treasure.h:66
sstring add_string(const char *str)
Share a string.
Definition: shstr.cpp:137
int32_t value
How much money it is worth (or contains)
Definition: object.h:360
int is_combination
Whather this is an alchemy recipe, or an item transformation description.
Definition: recipe.h:31
size_t bufferreader_current_line(BufferReader *br)
Return the index of the last line returned by bufferreader_next_line().
sstring title
Distinguishing name of product.
Definition: recipe.h:11
#define FREE_OBJ_NO_DESTROY_CALLBACK
Do not run the destroy callback.
Definition: object.h:547
linked_char * ingred
List of ingredients.
Definition: recipe.h:22
void dump_alchemy(void)
Dumps alchemy recipes to output.
Definition: recipe.cpp:352
recipe * items
Pointer to first recipe in this list.
Definition: recipe.h:40
artifactlist * find_artifactlist(int type)
Finds the artifact list for a certain item type.
Definition: artifact.cpp:570
int index
Index value derived from formula ingredients.
Definition: recipe.h:18
const char * recipe_get_difficulty_string(int difficulty)
A method to produce a difficulty adjective to describe alchemy projects.
Definition: recipe.cpp:965
static recipe * get_empty_formula(void)
Allocates a new recipe.
Definition: recipe.cpp:79
Global type definitions and header inclusions.
#define safe_strncpy
Definition: compat.h:27
sstring skill
Skill name used to make this recipe.
Definition: recipe.h:26
int transmute
If defined, one of the formula ingredients is used as the basis for the product object.
Definition: recipe.h:19
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:274
const Face * blank_face
Following can just as easily be pointers, but it is easier to keep them like this.
Definition: image.cpp:36
AssetsManager * getManager()
Definition: assets.cpp:309
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.cpp:294
One alchemy recipe.
Definition: recipe.h:10
bool check_formulae(void)
Check if formula don&#39;t have the same index.
Definition: recipe.cpp:292
void object_free(object *ob, int flags)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.cpp:1577
sstring failure_message
Specific failure message.
Definition: recipe.h:29
int legal_artifact_combination(const object *op, const artifact *art)
Checks if op can be combined with art.
Definition: artifact.cpp:252
#define FREE_OBJ_FREE_INVENTORY
Free inventory objects; if not set, drop inventory.
Definition: object.h:546
static long recipe_find_ingredient_cost(const char *name)
Try to find an ingredient with specified name.
Definition: recipe.cpp:476
std::vector< artifact * > items
Artifacts for this type.
Definition: artifact.h:28
bool check_recipes()
Ensure that all recipes have a valid artifact, and that archetypes are correct.
Definition: recipe.cpp:147
The archetype structure is a set of rules on how to generate and manipulate objects which point to ar...
Definition: object.h:483
static recipelist * init_recipelist(void)
Allocates a new recipelist.
Definition: recipe.cpp:60
int exp
How much exp to give for this formulae.
Definition: recipe.h:17
size_t arch_names
Size of the arch_name[] array.
Definition: recipe.h:12
object * arch_to_object(archetype *at)
Creates and returns a new object which is a copy of the given archetype.
Definition: arch.cpp:227
object * create_archetype(const char *name)
Finds which archetype matches the given name, and returns a new object containing a copy of the arche...
Definition: arch.cpp:276
size_t tool_size
Length of tool.
Definition: recipe.h:33
void give_artifact_abilities(object *op, const object *artifact)
Fixes the given object, giving it the abilities and titles it should have due to the second artifact-...
Definition: artifact.cpp:230
void object_give_identified_properties(object *op)
Ensure op has all its "identified" properties set.
Definition: item.cpp:1365
int strcasecmp(const char *s1, const char *s2)
sstring keycode
Optional keycode needed to use the recipe.
Definition: recipe.h:25
struct linked_char * next
Definition: global.h:100
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.cpp:1545
static int check_recipe(const recipe *rp)
Makes sure we actually have the requested artifact and archetype.
Definition: recipe.cpp:120
List of recipes with a certain number of ingredients.
Definition: recipe.h:37
char ** tool
Tool(s) for item transformation.
Definition: recipe.h:32
Archetypes * archetypes()
Get archetypes.
Definition: AssetsManager.h:44
int yield
Maximum number of items produced by the recipe.
Definition: recipe.h:21
const Face * face
Face with colors.
Definition: object.h:341
This represents all archetypes for one particular object type.
Definition: artifact.h:24
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Finds an artifact for a recipe.
Definition: recipe.cpp:733
recipelist * get_formulalist(int i)
Gets a formula list by ingredients count.
Definition: recipe.cpp:98
treasure * next_no
If this item was not generated, then continue here.
Definition: treasure.h:71
void fatal(enum fatal_error err)
fatal() is meant to be called whenever a fatal signal is intercepted.
Definition: utils.cpp:590
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
int strtoint(const char *buf)
Convert buf into an integer equal to the coadded sum of the (lowercase) character.
Definition: recipe.cpp:712
uint64_t price_base(const object *obj)
Price an item based on its value or archetype value, type, identification/BUC status, and other heuristics.
Definition: item.cpp:1507
size_t strlcpy(char *dst, const char *src, size_t size)
Portable implementation of strlcpy(3).
Definition: porting.cpp:222
treasurelist represents one logical group of items to be generated together.
Definition: treasure.h:85
treasure * next_yes
If this item was generated, use this link instead of ->next.
Definition: treasure.h:70
const char * name
Definition: global.h:99
Object structure, the core of Crossfire.
#define RANDOM()
Definition: define.h:667
treasure * next
Next treasure-item in a linked list.
Definition: treasure.h:69
int chance
Chance that recipe for this item will appear in an alchemical grimore.
Definition: recipe.h:14
sstring name
The name of the object, obviously...
Definition: object.h:319
sstring title
Of foo, etc.
Definition: object.h:325
Only for debugging purposes.
Definition: logger.h:13
sstring failure_arch
Arch of the item to generate on failure, instead of blowing up stuff.
Definition: recipe.h:28
int min_level
Minimum level to have in the skill to be able to use the formulae.
Definition: recipe.h:30
treasure * items
Items in this list, linked.
Definition: treasure.h:92
treasurelist * find_treasurelist(const char *name)
Search for the given treasurelist by name.
Definition: assets.cpp:253
archetype * find_treasure_by_name(const treasure *t, const char *name, int depth)
Find a treasure with a matching name.
Definition: recipe.cpp:425
treasure is one element in a linked list, which together consist of a complete treasure-list.
Definition: treasure.h:63
recipe * next
Next recipe with the same number of ingredients.
Definition: recipe.h:24
char ** arch_name
Possible archetypes of the final product made.
Definition: recipe.h:13
static void build_stringlist(const char *str, char ***result_list, size_t *result_size)
Split a comma separated string list into words.
Definition: recipe.cpp:864
const Face * recipe_get_face(const recipe *rp)
Return the best face associated with a recipe.
Definition: recipe.cpp:923
C function wrappers to interact with assets.
StringBuffer * buf
Definition: readable.cpp:1563
static recipelist * get_random_recipelist(void)
Gets a random recipe list.
Definition: recipe.cpp:754
char * bufferreader_next_line(BufferReader *br)
Return the next line in the buffer, as separated by a newline.
struct archetype * item
Which item this link can be.
Definition: treasure.h:64
void init_formulae(BufferReader *reader, const char *filename)
Builds up the lists of formula from the file in the libdir.
Definition: recipe.cpp:166
void free_all_recipes(void)
Frees all memory allocated to recipes and recipes lists.
Definition: recipe.cpp:814
int ingred_count
Number of items in ingred.
Definition: recipe.h:23
#define tolower(C)
Simple macro to convert a letter to lowercase.
Definition: c_new.cpp:30
sstring cauldron
Arch of the cauldron/workbench used to house the formulae.
Definition: recipe.h:27
This is one artifact, ie one special item.
Definition: artifact.h:14
int number
Number of recipes in this list.
Definition: recipe.h:39
object clone
An object from which to do object_copy()
Definition: object.h:487
static int numb_ingred(const char *buf)
Extracts the number part of an ingredient.
Definition: recipe.cpp:693
object * item
Special values of the artifact.
Definition: artifact.h:15
void each(std::function< void(T *)> op)
Apply a function to each asset.
static recipelist * formulalist
Pointer to first recipelist.
Definition: recipe.cpp:50