Crossfire Server, Trunk  R22047
recipe.c
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 
42 #include "object.h"
43 #include "assets.h"
44 
45 static void build_stringlist(const char *str, char ***result_list, size_t *result_size);
46 
49 
58 static recipelist *init_recipelist(void) {
59  recipelist *tl = (recipelist *)malloc(sizeof(recipelist));
60  if (tl == NULL)
62  tl->total_chance = 0;
63  tl->number = 0;
64  tl->items = NULL;
65  tl->next = NULL;
66  return tl;
67 }
68 
77 static recipe *get_empty_formula(void) {
78  // This used to be a malloc followed by setting everything to zero.
79  // So just use calloc to make it faster.
80  // SilverNexus -- 2018-10-22
81  recipe *t = (recipe *)calloc(1, sizeof(recipe));
82  if (t == NULL)
84  return t;
85 }
86 
97  recipelist *fl = formulalist;
98  int number = i;
99 
100  while (fl && number > 1) {
101  if (!(fl = fl->next))
102  break;
103  number--;
104  }
105  return fl;
106 }
107 
118 static int check_recipe(const recipe *rp) {
119  size_t i;
120  int result;
121 
122  result = 1;
123  for (i = 0; i < rp->arch_names; i++) {
124  if (try_find_archetype(rp->arch_name[i]) != NULL) {
125  const artifact *art = locate_recipe_artifact(rp, i);
126 
127  if (!art && strcmp(rp->title, "NONE") != 0) {
128  LOG(llevError, "\nWARNING: Formula %s of %s has no artifact.\n", rp->arch_name[i], rp->title);
129  result = 0;
130  }
131  } else {
132  LOG(llevError, "\nWARNING: Can't find archetype %s for formula %s\n", rp->arch_name[i], rp->title);
133  result = 0;
134  }
135  }
136 
137  return result;
138 }
139 
143 void init_formulae(BufferReader *reader, const char *filename) {
144  char *buf, *cp, *next;
145  recipe *formula = NULL;
146  recipelist *fl;
147  linked_char *tmp;
148  int value;
149 
150  if (!formulalist)
151  formulalist = init_recipelist();
152 
153  while ((buf = bufferreader_next_line(reader)) != NULL) {
154  if (*buf == '#' || *buf == '\0')
155  continue;
156  cp = buf;
157  while (*cp == ' ') /* Skip blanks */
158  cp++;
159 
160  if (!strncmp(cp, "Object", 6)) {
161  formula = get_empty_formula();
162  formula->title = add_string(strchr(cp, ' ')+1);
163  } else if (formula == NULL) {
164  LOG(llevError, "recipe.c: First key in formulae file %s is not \"Object\".\n", filename);
166  } else if (!strncmp(cp, "keycode", 7)) {
167  formula->keycode = add_string(strchr(cp, ' ')+1);
168  } else if (sscanf(cp, "trans %d", &value)) {
169  formula->transmute = value;
170  } else if (sscanf(cp, "yield %d", &value)) {
171  formula->yield = value;
172  } else if (sscanf(cp, "chance %d", &value)) {
173  formula->chance = value;
174  } else if (sscanf(cp, "exp %d", &value)) {
175  formula->exp = value;
176  } else if (sscanf(cp, "diff %d", &value)) {
177  formula->diff = value;
178  } else if (!strncmp(cp, "ingred", 6)) {
179  int numb_ingred;
180  formula->ingred_count = 1;
181  cp = strchr(cp, ' ')+1;
182  do {
183  if ((next = strchr(cp, ',')) != NULL) {
184  *(next++) = '\0';
185  formula->ingred_count++;
186  }
187  tmp = (linked_char *)malloc(sizeof(linked_char));
188  /* trim the string */
189  while (*cp == ' ')
190  cp++;
191  while (*cp != '\0' && cp[strlen(cp) - 1] == ' ')
192  cp[strlen(cp) - 1] = '\0';
193  tmp->name = add_string(cp);
194  tmp->next = formula->ingred;
195  formula->ingred = tmp;
196  /* each ingredient's ASCII value is coadded. Later on this
197  * value will be used allow us to search the formula lists
198  * quickly for the right recipe.
199  */
200  formula->index += strtoint(cp);
201  } while ((cp = next) != NULL);
202  /* now find the correct (# of ingred ordered) formulalist */
203  numb_ingred = formula->ingred_count;
204  fl = formulalist;
205  while (numb_ingred != 1) {
206  if (!fl->next)
207  fl->next = init_recipelist();
208  fl = fl->next;
209  numb_ingred--;
210  }
211  fl->total_chance += formula->chance;
212  fl->number++;
213  formula->next = fl->items;
214  fl->items = formula;
215  } else if (!strncmp(cp, "arch", 4)) {
216  build_stringlist(strchr(cp, ' ')+1, &formula->arch_name, &formula->arch_names);
217  } else if (!strncmp(cp, "skill", 5)) {
218  formula->skill = add_string(strchr(cp, ' ')+1);
219  } else if (!strncmp(cp, "cauldron", 8)) {
220  formula->cauldron = add_string(strchr(cp, ' ')+1);
221  } else if (!strncmp(cp, "failure_arch ", 13)) {
222  formula->failure_arch = add_string(strchr(cp, ' ')+1);
223  } else if (!strncmp(cp, "failure_message ", 16)) {
224  formula->failure_message = add_string(strchr(cp, ' ')+1);
225  } else if (sscanf(cp, "min_level %d", &value)) {
226  formula->min_level = value;
227  } else if (!strncmp(cp, "tool ", 5)) {
228  build_stringlist(strchr(cp, ' ')+1, &formula->tool, &formula->tool_size);
229  } else if (sscanf(cp, "combination %d", &value)) {
230  formula->is_combination = value ? 1 : 0;
231  } else
232  LOG(llevError, "Unknown input in file %s: %s\n", filename, buf);
233  }
234  LOG(llevDebug, "done.\n");
235 }
236 
251 void check_formulae(void) {
252  recipelist *fl;
253  recipe *check, *formula;
254  int numb = 1, tool_match;
255  size_t tool_i,tool_j;
256 
257  LOG(llevDebug, "Checking formulae lists...\n");
258 
259  for (fl = formulalist; fl != NULL; fl = fl->next) {
260  for (formula = fl->items; formula != NULL; formula = formula->next) {
261  for (check = formula->next; check != NULL; check = check->next)
262  /* If one recipe has a tool and another a caudron, we should be able to handle it */
263  if (check->index == formula->index &&
264  ((check->cauldron && formula->cauldron && strcmp(check->cauldron, formula->cauldron) == 0) ||
265  (check->tool_size == formula->tool_size && check->tool_size > 0))) {
266  /* Check the tool list to make sure they have no matches */
267  if (check->tool && formula->tool)
268  {
269  tool_match = 0;
270  for (tool_i = 0; tool_i < formula->tool_size; ++tool_i)
271  /* If it turns out these lists are sorted, then we could optimize this better. */
272  for (tool_j = 0; tool_j < check->tool_size; ++tool_j)
273  if (strcmp(formula->tool[tool_i], check->tool[tool_j]) == 0) {
274  tool_match = 1;
275  break; /* TODO: break out of the double loop */
276  }
277  }
278  else
279  tool_match = 1; /* If we get here, we matched on the cauldron */
280  /* Check to see if we have a denoted match */
281  if (tool_match) {
282  /* if the recipes don't have the same facility, then no issue anyway. */
283  LOG(llevError, " ERROR: On %d ingred list:\n", numb);
284  LOG(llevError, "Formulae [%s] of %s and [%s] of %s have matching index id (%d)\n",
285  formula->arch_name[0], formula->title, check->arch_name[0], check->title, formula->index);
286  }
287  }
288  for (size_t idx = 0; idx < formula->arch_names; idx++) {
289  if (try_find_archetype(formula->arch_name[idx]) == NULL) {
290  LOG(llevError, "Formulae %s of %s (%d ingredients) references non existent archetype %s\n",
291  formula->arch_name[0], formula->title, numb, formula->arch_name[idx]);
292  }
293  }
294  }
295  numb++;
296  }
297 
298  LOG(llevDebug, "done checking.\n");
299 }
300 
308 void dump_alchemy(void) {
309  recipelist *fl = formulalist;
310  recipe *formula = NULL;
311  linked_char *next;
312  int num_ingred = 1;
313 
314  fprintf(logfile, "\n");
315  while (fl) {
316  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);
317  for (formula = fl->items; formula != NULL; formula = formula->next) {
318  const artifact *art = NULL;
319  char buf[MAX_BUF];
320  size_t i;
321 
322  for (i = 0; i < formula->arch_names; i++) {
323  const char *string = formula->arch_name[i];
324 
325  if (try_find_archetype(string) != NULL) {
326  art = locate_recipe_artifact(formula, i);
327  if (!art && strcmp(formula->title, "NONE"))
328  LOG(llevError, "Formula %s has no artifact\n", formula->title);
329  else {
330  if (strcmp(formula->title, "NONE"))
331  snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
332  else
333  strlcpy(buf, string, sizeof(buf));
334  fprintf(logfile, "%-30s(%d) bookchance %3d ", buf, formula->index, formula->chance);
335  fprintf(logfile, "skill %s", formula->skill);
336  fprintf(logfile, "\n");
337  if (formula->ingred != NULL) {
338  int nval = 0, tval = 0;
339  fprintf(logfile, "\tIngred: ");
340  for (next = formula->ingred; next != NULL; next = next->next) {
341  if (nval != 0)
342  fprintf(logfile, ",");
343  fprintf(logfile, "%s(%d)", next->name, (nval = strtoint(next->name)));
344  tval += nval;
345  }
346  fprintf(logfile, "\n");
347  if (tval != formula->index)
348  fprintf(logfile, "WARNING:ingredient list and formula values not equal.\n");
349  }
350  if (formula->skill != NULL)
351  fprintf(logfile, "\tSkill Required: %s", formula->skill);
352  if (formula->cauldron != NULL)
353  fprintf(logfile, "\tCauldron: %s\n", formula->cauldron);
354  fprintf(logfile, "\tDifficulty: %d\t Exp: %d\n", formula->diff, formula->exp);
355  }
356  } else
357  LOG(llevError, "Can't find archetype:%s for formula %s\n", string, formula->title);
358  }
359  }
360  fprintf(logfile, "\n");
361  fl = fl->next;
362  num_ingred++;
363  }
364 }
365 
380 archetype *find_treasure_by_name(const treasure *t, const char *name, int depth) {
381  treasurelist *tl;
382  archetype *at;
383 
384  if (depth > 10)
385  return NULL;
386 
387  while (t != NULL) {
388  if (t->name != NULL) {
389  tl = find_treasurelist(t->name);
390  at = find_treasure_by_name(tl->items, name, depth+1);
391  if (at != NULL)
392  return at;
393  } else {
394  if (!strcasecmp(t->item->clone.name, name))
395  return t->item;
396  }
397  if (t->next_yes != NULL) {
398  at = find_treasure_by_name(t->next_yes, name, depth);
399  if (at != NULL)
400  return at;
401  }
402  if (t->next_no != NULL) {
403  at = find_treasure_by_name(t->next_no, name, depth);
404  if (at != NULL)
405  return at;
406  }
407  t = t->next;
408  }
409  return NULL;
410 }
411 
420 void dump_alchemy_costs(void) {
421  recipelist *fl = formulalist;
422  recipe *formula = NULL;
423  linked_char *next;
424  int num_ingred = 1;
425  int num_errors = 0;
426  long cost;
427  long tcost;
428 
429  fprintf(logfile, "\n");
430  while (fl) {
431  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);
432  for (formula = fl->items; formula != NULL; formula = formula->next) {
433  const artifact *art = NULL;
434  const archetype *at = NULL;
435  char buf[MAX_BUF];
436  size_t i;
437 
438  for (i = 0; i < formula->arch_names; i++) {
439  const char *string = formula->arch_name[i];
440 
441  if ((at = try_find_archetype(string)) != NULL) {
442  art = locate_recipe_artifact(formula, i);
443  if (!art && strcmp(formula->title, "NONE"))
444  LOG(llevError, "Formula %s has no artifact\n", formula->title);
445  else {
446  if (!strcmp(formula->title, "NONE"))
447  strlcpy(buf, string, sizeof(buf));
448  else
449  snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
450  fprintf(logfile, "\n%-40s bookchance %3d skill %s\n", buf, formula->chance, formula->skill);
451  if (formula->ingred != NULL) {
452  tcost = 0;
453  for (next = formula->ingred; next != NULL; next = next->next) {
454  cost = recipe_find_ingredient_cost(next->name);
455  if (cost < 0)
456  num_errors++;
457  fprintf(logfile, "\t%-33s%5ld\n", next->name, cost);
458  if (cost < 0 || tcost < 0)
459  tcost = -1;
460  else
461  tcost += cost;
462  }
463  if (art != NULL && art->item != NULL)
464  cost = at->clone.value*art->item->value;
465  else
466  cost = at->clone.value;
467  fprintf(logfile, "\t\tBuying result costs: %5ld", cost);
468  if (formula->yield > 1) {
469  fprintf(logfile, " to %ld (max %d items)\n", cost*formula->yield, formula->yield);
470  cost = cost*(formula->yield+1L)/2L;
471  } else
472  fprintf(logfile, "\n");
473  fprintf(logfile, "\t\tIngredients cost: %5ld\n\t\tComment: ", tcost);
474  if (tcost < 0)
475  fprintf(logfile, "Could not find some ingredients. Check the formula!\n");
476  else if (tcost > cost)
477  fprintf(logfile, "Ingredients are much too expensive. Useless formula.\n");
478  else if (tcost*2L > cost)
479  fprintf(logfile, "Ingredients are too expensive.\n");
480  else if (tcost*10L < cost)
481  fprintf(logfile, "Ingredients are too cheap.\n");
482  else
483  fprintf(logfile, "OK.\n");
484  }
485  }
486  } else
487  LOG(llevError, "Can't find archetype:%s for formula %s\n", string, formula->title);
488  }
489  }
490  fprintf(logfile, "\n");
491  fl = fl->next;
492  num_ingred++;
493  }
494  if (num_errors > 0)
495  fprintf(logfile, "WARNING: %d objects required by the formulae do not exist in the game.\n", num_errors);
496 }
497 
506 static const char *ingred_name(const char *name) {
507  const char *cp = name;
508 
509  if (atoi(cp))
510  cp = strchr(cp, ' ')+1;
511  return cp;
512 }
513 
522 static int numb_ingred(const char *buf) {
523  int numb;
524 
525  if ((numb = atoi(buf)))
526  return numb;
527  else
528  return 1;
529 }
530 
541 int strtoint(const char *buf) {
542  const char *cp = ingred_name(buf);
543  int val = 0, len = strlen(cp), mult = numb_ingred(buf);
544 
545  while (len) {
546  val += tolower(*cp);
547  cp++; len--;
548  }
549  return val*mult;
550 }
551 
562 const artifact *locate_recipe_artifact(const recipe *rp, size_t idx) {
563  object *item = create_archetype(rp->arch_name[idx]);
564  const artifactlist *at = NULL;
565  const artifact *art = NULL;
566 
567  if (!item)
568  return (artifact *)NULL;
569 
570  if ((at = find_artifactlist(item->type)))
571  for (art = at->items; art; art = art->next)
572  if (!strcmp(art->item->name, rp->title) && legal_artifact_combination(item, art))
573  break;
574 
576 
577  return art;
578 }
579 
587  recipelist *fl = NULL;
588  int number = 0, roll = 0;
589 
590  /* first, determine # of recipelist we have */
591  for (fl = get_formulalist(1); fl; fl = fl->next)
592  number++;
593 
594  /* now, randomly choose one */
595  if (number > 0)
596  roll = RANDOM()%number;
597 
598  fl = get_formulalist(1);
599  while (roll && fl) {
600  if (fl->next)
601  fl = fl->next;
602  else
603  break;
604  roll--;
605  }
606  if (!fl) /* failed! */
607  LOG(llevError, "get_random_recipelist(): no recipelists found!\n");
608  else if (fl->total_chance == 0)
609  fl = get_random_recipelist();
610 
611  return fl;
612 }
613 
623  recipelist *fl = rpl;
624  recipe *rp = NULL;
625  int r = 0;
626 
627  /* looks like we have to choose a random one */
628  if (fl == NULL)
629  if ((fl = get_random_recipelist()) == NULL)
630  return rp;
631 
632  if (fl->total_chance > 0) {
633  r = RANDOM()%fl->total_chance;
634  for (rp = fl->items; rp; rp = rp->next) {
635  r -= rp->chance;
636  if (r < 0)
637  break;
638  }
639  }
640  return rp;
641 }
642 
646 void free_all_recipes(void) {
647  recipelist *fl, *flnext;
648  recipe *formula = NULL, *next;
649  linked_char *lchar, *charnext;
650 
651  LOG(llevDebug, "Freeing all the recipes\n");
652  for (fl = formulalist; fl != NULL; fl = flnext) {
653  flnext = fl->next;
654 
655  for (formula = fl->items; formula != NULL; formula = next) {
656  next = formula->next;
657 
658  free(formula->arch_name[0]);
659  free(formula->arch_name);
660  if (formula->title)
661  free_string(formula->title);
662  if (formula->skill)
663  free_string(formula->skill);
664  if (formula->cauldron)
665  free_string(formula->cauldron);
666  if (formula->failure_arch)
667  free_string(formula->failure_arch);
668  if (formula->failure_message)
669  free_string(formula->failure_message);
670  for (lchar = formula->ingred; lchar; lchar = charnext) {
671  charnext = lchar->next;
672  free_string(lchar->name);
673  free(lchar);
674  }
675  if (formula->tool)
676  free(formula->tool[0]);
677  free(formula->tool);
678  free(formula);
679  }
680  free(fl);
681  }
682  formulalist = NULL;
683 }
684 
696 static void build_stringlist(const char *str, char ***result_list, size_t *result_size) {
697  char *dup;
698  char *p;
699  size_t size;
700  size_t i;
701 
702  dup = strdup_local(str);
703  if (dup == NULL)
705 
706  size = 0;
707  for (p = strtok(dup, ","); p != NULL; p = strtok(NULL, ","))
708  size++;
709 
710  assert(size > 0);
711  *result_list = malloc(sizeof(**result_list) * size);
712  if (*result_list == NULL)
714  *result_size = size;
715 
716  for (i = 0; i < size; i++) {
717  (*result_list)[i] = dup;
718  dup = dup+strlen(dup)+1;
719  }
720 }
721 
729 recipe *find_recipe_for_tool(const char *tool, recipe *from) {
730  size_t t;
731  recipelist *list = from ? get_formulalist(from->ingred_count) : formulalist;
732  recipe *test = from ? from->next : list->items;
733 
734  while (list) {
735  while (test) {
736  for (t = 0; t < test->tool_size; t++) {
737  if (strcmp(test->tool[t], tool) == 0) {
738  return test;
739  }
740  }
741  test = test->next;
742  }
743 
744  list = list->next;
745  }
746 
747  return NULL;
748 }
EXTERN FILE * logfile
Definition: global.h:137
void init_formulae(BufferReader *reader, const char *filename)
Definition: recipe.c:143
int diff
Definition: recipe.h:16
long recipe_find_ingredient_cost(const char *name)
Definition: assets.cpp:544
archetype * find_treasure_by_name(const treasure *t, const char *name, int depth)
Definition: recipe.c:380
const artifactlist * find_artifactlist(int type)
Definition: artifact.c:642
static void build_stringlist(const char *str, char ***result_list, size_t *result_size)
Definition: recipe.c:696
recipe * get_random_recipe(recipelist *rpl)
Definition: recipe.c:622
StringBuffer * buf
Definition: readable.c:1591
#define strdup_local
Definition: compat.h:25
struct recipeliststruct * next
Definition: recipe.h:41
static int check_recipe(const recipe *rp)
Definition: recipe.c:118
int yield
Definition: recipe.h:21
void fatal(enum fatal_error err)
Definition: utils.c:597
struct artifactstruct * items
Definition: artifact.h:30
treasurelist * find_treasurelist(const char *name)
Definition: assets.cpp:232
void free_string(sstring str)
Definition: shstr.c:280
linked_char * ingred
Definition: recipe.h:22
recipe * find_recipe_for_tool(const char *tool, recipe *from)
Definition: recipe.c:729
static int numb_ingred(const char *buf)
Definition: recipe.c:522
static recipelist * init_recipelist(void)
Definition: recipe.c:58
int is_combination
Definition: recipe.h:31
int transmute
Definition: recipe.h:19
static recipelist * get_random_recipelist(void)
Definition: recipe.c:586
void dump_alchemy_costs(void)
Definition: recipe.c:420
struct archt * item
Definition: treasure.h:64
archetype * try_find_archetype(const char *name)
Definition: assets.cpp:257
int ingred_count
Definition: recipe.h:23
int chance
Definition: recipe.h:14
void object_free_drop_inventory(object *ob)
Definition: object.c:1316
int legal_artifact_combination(const object *op, const artifact *art)
Definition: artifact.c:221
sstring title
Definition: recipe.h:11
char ** arch_name
Definition: recipe.h:13
static const char * ingred_name(const char *name)
Definition: recipe.c:506
int total_chance
Definition: recipe.h:38
object * create_archetype(const char *name)
Definition: arch.cpp:281
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Definition: recipe.c:562
#define snprintf
Definition: win32.h:46
int strcasecmp(const char *s1, const char *s2)
void dump_alchemy(void)
Definition: recipe.c:308
struct linked_char * next
Definition: global.h:88
int strtoint(const char *buf)
Definition: recipe.c:541
char * bufferreader_next_line(BufferReader *br)
Definition: bufferreader.c:81
struct treasurestruct * next_no
Definition: treasure.h:68
sstring skill
Definition: recipe.h:26
static recipelist * formulalist
Definition: recipe.c:48
int index
Definition: recipe.h:18
#define MAX_BUF
Definition: define.h:35
size_t tool_size
Definition: recipe.h:33
void check_formulae(void)
Definition: recipe.c:251
struct treasurestruct * items
Definition: treasure.h:89
#define tolower(C)
Definition: c_new.c:30
void free_all_recipes(void)
Definition: recipe.c:646
int exp
Definition: recipe.h:17
const char * name
Definition: global.h:87
struct treasurestruct * next_yes
Definition: treasure.h:67
#define RANDOM()
Definition: define.h:681
size_t arch_names
Definition: recipe.h:12
sstring failure_message
Definition: recipe.h:29
struct recipestruct * next
Definition: recipe.h:24
sstring add_string(const char *str)
Definition: shstr.c:124
sstring failure_arch
Definition: recipe.h:28
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
object * item
Definition: artifact.h:15
static recipe * get_empty_formula(void)
Definition: recipe.c:77
struct treasurestruct * next
Definition: treasure.h:66
const char * name
Definition: treasure.h:65
sstring cauldron
Definition: recipe.h:27
char ** tool
Definition: recipe.h:32
sstring keycode
Definition: recipe.h:25
size_t strlcpy(char *dst, const char *src, size_t size)
Definition: porting.c:370
struct recipestruct * items
Definition: recipe.h:40
recipelist * get_formulalist(int i)
Definition: recipe.c:96
int min_level
Definition: recipe.h:30
struct artifactstruct * next
Definition: artifact.h:18