ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/recipe.C
Revision: 1.14
Committed: Wed Jan 3 02:30:51 2007 UTC (17 years, 4 months ago) by elmex
Content type: text/plain
Branch: MAIN
Changes since 1.13: +1 -1 lines
Log Message:
implemented proper support for empty treasures, which
sadly occur in empty treasure lists. fixing treasurelists
to have no entries at all would be even more complicated,
but even when this is fixed, the current changes only make the
server more crash robust to bad treasures.
Also removed the 'NONE' specialcase for treasure lists. Developers
should use 'none' instead now.

File Contents

# Content
1 /* Basic stuff for use with the alchemy code. Clearly some of this stuff
2 * could go into server/alchemy, but I left it here just in case it proves
3 * more generally useful.
4 *
5 * Nov 1995 - file created by b.t. thomas@astro.psu.edu
6 */
7
8
9 /* Our definition of 'formula' is any product of an alchemical process.
10 * Ingredients are just comma delimited list of archetype (or object)
11 * names.
12 */
13
14 /* Example 'formula' entry in libdir/formulae:
15 * Object transparency
16 * chance 10
17 * ingred dust of beholdereye,gem
18 * arch potion_generic
19 */
20
21 #include <cctype>
22
23 #include <global.h>
24 #include <object.h>
25
26 static void build_stringlist (const char *str, char ***result_list, size_t * result_size);
27
28 static recipelist *formulalist;
29
30 static recipelist *
31 init_recipelist (void)
32 {
33 recipelist *tl = new recipelist;
34
35 tl->total_chance = 0;
36 tl->number = 0;
37 tl->items = NULL;
38 tl->next = NULL;
39 return tl;
40 }
41
42 static recipe *
43 get_empty_formula (void)
44 {
45 recipe *t = new recipe;
46
47 t->chance = 0;
48 t->index = 0;
49 t->transmute = 0;
50 t->yield = 0;
51 t->diff = 0;
52 t->exp = 0;
53 t->keycode = 0;
54 t->title = NULL;
55 t->arch_names = 0;
56 t->arch_name = NULL;
57 t->skill = NULL;
58 t->cauldron = NULL;
59 t->ingred = NULL;
60 t->next = NULL;
61 return t;
62 }
63
64 /* get_formulalist() - returns pointer to the formula list */
65
66 recipelist *
67 get_formulalist (int i)
68 {
69 recipelist *fl = formulalist;
70 int number = i;
71
72 while (fl && number > 1)
73 {
74 if (!(fl = fl->next))
75 break;
76 number--;
77 }
78 return fl;
79 }
80
81 /* check_recipe() - makes sure we actually have the requested artifact
82 * and archetype. */
83
84 static int
85 check_recipe (const recipe *rp)
86 {
87 size_t i;
88 int result;
89
90 result = 1;
91 for (i = 0; i < rp->arch_names; i++)
92 {
93 if (archetype::find (rp->arch_name[i]) != NULL)
94 {
95 artifact *art = locate_recipe_artifact (rp, i);
96
97 if (!art && strcmp (rp->title, "NONE") != 0)
98 {
99 LOG (llevError, "WARNING: Formula %s of %s has no artifact.\n", rp->arch_name[i], &rp->title);
100 result = 0;
101 }
102 }
103 else
104 {
105 LOG (llevError, "WARNING: Can't find archetype %s for formula %s\n", rp->arch_name[i], &rp->title);
106 result = 0;
107 }
108 }
109
110 return result;
111 }
112
113
114 /*
115 * init_formulae() - Builds up the lists of formula from the file in
116 * the libdir. -b.t.
117 */
118
119 void
120 init_formulae (void)
121 {
122 static int has_been_done = 0;
123 FILE *fp;
124 char filename[MAX_BUF], buf[MAX_BUF], *cp, *next;
125 recipe *formula = NULL;
126 recipelist *fl = init_recipelist ();
127 linked_char *tmp;
128 int value, comp;
129
130 if (!formulalist)
131 formulalist = fl;
132
133 if (has_been_done)
134 return;
135 else
136 has_been_done = 1;
137
138 sprintf (filename, "%s/formulae", settings.datadir);
139 LOG (llevDebug, "Reading alchemical formulae from %s...\n", filename);
140 if ((fp = open_and_uncompress (filename, 0, &comp)) == NULL)
141 {
142 LOG (llevError, "Can't open %s.\n", filename);
143 return;
144 }
145
146 while (fgets (buf, MAX_BUF, fp) != NULL)
147 {
148 if (*buf == '#')
149 continue;
150 if ((cp = strchr (buf, '\n')) != NULL)
151 *cp = '\0';
152 cp = buf;
153 while (*cp == ' ') /* Skip blanks */
154 cp++;
155
156 if (!strncmp (cp, "Object", 6))
157 {
158 formula = get_empty_formula ();
159 formula->title = strchr (cp, ' ') + 1;
160 }
161 else if (!strncmp (cp, "keycode", 7))
162 {
163 formula->keycode = strchr (cp, ' ') + 1;
164 }
165 else if (sscanf (cp, "trans %d", &value))
166 {
167 formula->transmute = (uint16) value;
168 }
169 else if (sscanf (cp, "yield %d", &value))
170 {
171 formula->yield = (uint16) value;
172 }
173 else if (sscanf (cp, "chance %d", &value))
174 {
175 formula->chance = (uint16) value;
176 }
177 else if (sscanf (cp, "exp %d", &value))
178 {
179 formula->exp = (uint16) value;
180 }
181 else if (sscanf (cp, "diff %d", &value))
182 {
183 formula->diff = (uint16) value;
184 }
185 else if (!strncmp (cp, "ingred", 6))
186 {
187 int numb_ingred = 1;
188
189 cp = strchr (cp, ' ') + 1;
190 do
191 {
192 if ((next = strchr (cp, ',')) != NULL)
193 {
194 *(next++) = '\0';
195 numb_ingred++;
196 }
197 tmp = new linked_char;
198
199 tmp->name = cp;
200 tmp->next = formula->ingred;
201 formula->ingred = tmp;
202 /* each ingredient's ASCII value is coadded. Later on this
203 * value will be used allow us to search the formula lists
204 * quickly for the right recipe.
205 */
206 formula->index += strtoint (cp);
207 }
208 while ((cp = next) != NULL);
209 /* now find the correct (# of ingred ordered) formulalist */
210 fl = formulalist;
211 while (numb_ingred != 1)
212 {
213 if (!fl->next)
214 fl->next = init_recipelist ();
215 fl = fl->next;
216 numb_ingred--;
217 }
218 fl->total_chance += formula->chance;
219 fl->number++;
220 formula->next = fl->items;
221 fl->items = formula;
222 }
223 else if (!strncmp (cp, "arch", 4))
224 {
225 build_stringlist (strchr (cp, ' ') + 1, &formula->arch_name, &formula->arch_names);
226 check_recipe (formula);
227 }
228 else if (!strncmp (cp, "skill", 5))
229 {
230 formula->skill = strchr (cp, ' ') + 1;
231 }
232 else if (!strncmp (cp, "cauldron", 8))
233 {
234 formula->cauldron = strchr (cp, ' ') + 1;
235 }
236 else
237 LOG (llevError, "Unknown input in file %s: %s\n", filename, buf);
238 }
239 LOG (llevDebug, "done.\n");
240 close_and_delete (fp, comp);
241 /* Lastly, lets check for problems in formula we got */
242 check_formulae ();
243 }
244
245 /* check_formulae()- since we are doing a squential search on the
246 * formulae lists now, we have to be carefull that we dont have 2
247 * formula with the exact same index value. Under the new nbatches
248 * code, it is possible to have multiples of ingredients in a cauldron
249 * which could result in an index formula mismatch. We *don't* check for
250 * that possibility here. -b.t.
251 */
252 void
253 check_formulae (void)
254 {
255 recipelist *fl;
256 recipe *check, *formula;
257 int numb = 1;
258
259 LOG (llevDebug, "Checking formulae lists...\n");
260
261 for (fl = formulalist; fl != NULL; fl = fl->next)
262 {
263 for (formula = fl->items; formula != NULL; formula = formula->next)
264 for (check = formula->next; check != NULL; check = check->next)
265 if (check->index == formula->index)
266 {
267 LOG (llevError, " ERROR: On %d ingred list: ", numb);
268 LOG (llevError, "Formulae [%s] of %s and [%s] of %s have matching index id (%d)\n",
269 formula->arch_name[0], &formula->title, check->arch_name[0], &check->title, formula->index);
270 }
271 numb++;
272 }
273
274 LOG (llevDebug, "done.\n");
275
276 }
277
278 /* Borrowed (again) from the artifacts code for this */
279
280 void
281 dump_alchemy (void)
282 {
283 recipelist *fl = formulalist;
284 recipe *formula = NULL;
285 linked_char *next;
286 int num_ingred = 1;
287
288 fprintf (logfile, "\n");
289 while (fl)
290 {
291 fprintf (logfile, "\n Formulae with %d ingredient%s %d Formulae with total_chance=%d\n",
292 num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
293 for (formula = fl->items; formula != NULL; formula = formula->next)
294 {
295 artifact *art = NULL;
296 char buf[MAX_BUF];
297 size_t i;
298
299 for (i = 0; i < formula->arch_names; i++)
300 {
301 const char *string = formula->arch_name[i];
302
303 if (archetype::find (string) != NULL)
304 {
305 art = locate_recipe_artifact (formula, i);
306 if (!art && strcmp (formula->title, "NONE"))
307 LOG (llevError, "Formula %s has no artifact\n", &formula->title);
308 else
309 {
310 if (strcmp (formula->title, "NONE"))
311 sprintf (buf, "%s of %s", string, &formula->title);
312 else
313 sprintf (buf, "%s", string);
314 fprintf (logfile, "%-30s(%d) bookchance %3d ", buf, formula->index, formula->chance);
315 fprintf (logfile, "skill %s", &formula->skill);
316 fprintf (logfile, "\n");
317 if (formula->ingred != NULL)
318 {
319 int nval = 0, tval = 0;
320
321 fprintf (logfile, "\tIngred: ");
322 for (next = formula->ingred; next != NULL; next = next->next)
323 {
324 if (nval != 0)
325 fprintf (logfile, ",");
326 fprintf (logfile, "%s(%d)", &next->name, (nval = strtoint (next->name)));
327 tval += nval;
328 }
329 fprintf (logfile, "\n");
330 if (tval != formula->index)
331 fprintf (logfile, "WARNING:ingredient list and formula values not equal.\n");
332 }
333 if (formula->skill != NULL)
334 fprintf (logfile, "\tSkill Required: %s", &formula->skill);
335 if (formula->cauldron != NULL)
336 fprintf (logfile, "\tCauldron: %s\n", &formula->cauldron);
337 fprintf (logfile, "\tDifficulty: %d\t Exp: %d\n", formula->diff, formula->exp);
338 }
339 }
340 else
341 LOG (llevError, "Can't find archetype:%s for formula %s\n", string, &formula->title);
342 }
343 }
344 fprintf (logfile, "\n");
345 fl = fl->next;
346 num_ingred++;
347 }
348 }
349
350 /* Find a treasure with a matching name. The 'depth' parameter is
351 * only there to prevent infinite loops in treasure lists (a list
352 * referencing another list pointing back to the first one). */
353 archetype *
354 find_treasure_by_name (const treasure *t, const char *name, int depth)
355 {
356 treasurelist *tl;
357 archetype *at;
358
359 if (depth > 10)
360 return 0;
361
362 while (t)
363 {
364 if (t->name)
365 {
366 tl = find_treasurelist (t->name);
367
368 if (tl)
369 {
370 at = find_treasure_by_name (tl->items, name, depth + 1);
371
372 if (at)
373 return at;
374 }
375 }
376 else
377 {
378 if (t->item && !strcasecmp (t->item->clone.name, name))
379 return t->item;
380 }
381
382 if (t->next_yes)
383 {
384 at = find_treasure_by_name (t->next_yes, name, depth);
385 if (at)
386 return at;
387 }
388
389 if (t->next_no)
390 {
391 at = find_treasure_by_name (t->next_no, name, depth);
392 if (at)
393 return at;
394 }
395 t = t->next;
396 }
397 return 0;
398 }
399
400 /* If several archetypes have the same name, the value of the first
401 * one with that name will be returned. This happens for the
402 * mushrooms (mushroom_1, mushroom_2 and mushroom_3). For the
403 * monsters' body parts, there may be several monsters with the same
404 * name. This is not a problem if these monsters have the same level
405 * (e.g. sage & c_sage) or if only one of the monsters generates the
406 * body parts that we are looking for (e.g. big_dragon and
407 * big_dragon_worthless). */
408 long
409 find_ingred_cost (const char *name)
410 {
411 archetype *at;
412 archetype *at2;
413 artifactlist *al;
414 artifact *art;
415 long mult;
416 char *cp;
417 char part1[100];
418 char part2[100];
419
420 /* same as atoi(), but skip number */
421 mult = 0;
422 while (isdigit (*name))
423 {
424 mult = 10 * mult + (*name - '0');
425 name++;
426 }
427 if (mult > 0)
428 name++;
429 else
430 mult = 1;
431 /* first, try to match the name of an archetype */
432 for (at = first_archetype; at != NULL; at = at->next)
433 {
434 if (at->clone.title != NULL)
435 {
436 /* inefficient, but who cares? */
437 sprintf (part1, "%s %s", &at->clone.name, &at->clone.title);
438 if (!strcasecmp (part1, name))
439 return mult * at->clone.value;
440 }
441 if (!strcasecmp (at->clone.name, name))
442 return mult * at->clone.value;
443 }
444 /* second, try to match an artifact ("arch of something") */
445 cp = strstr (name, " of ");
446 if (cp != NULL)
447 {
448 strcpy (part1, name);
449 part1[cp - name] = '\0';
450 strcpy (part2, cp + 4);
451 /* find the first archetype matching the first part of the name */
452 for (at = first_archetype; at != NULL; at = at->next)
453 if (!strcasecmp (at->clone.name, part1) && at->clone.title == NULL)
454 break;
455 if (at != NULL)
456 {
457 /* find the first artifact derived from that archetype (same type) */
458 for (al = first_artifactlist; al != NULL; al = al->next)
459 if (al->type == at->clone.type)
460 {
461 for (art = al->items; art != NULL; art = art->next)
462 if (!strcasecmp (art->item->name, part2))
463 return mult * at->clone.value * art->item->value;
464 }
465 }
466 }
467 /* third, try to match a body part ("arch's something") */
468 cp = strstr (name, "'s ");
469 if (cp != NULL)
470 {
471 strcpy (part1, name);
472 part1[cp - name] = '\0';
473 strcpy (part2, cp + 3);
474 /* examine all archetypes matching the first part of the name */
475 for (at = first_archetype; at != NULL; at = at->next)
476 if (!strcasecmp (at->clone.name, part1) && at->clone.title == NULL)
477 {
478 if (at->clone.randomitems != NULL)
479 {
480 at2 = find_treasure_by_name (at->clone.randomitems->items, part2, 0);
481 if (at2)
482 return mult * at2->clone.value * isqrt (at->clone.level * 2);
483 }
484 }
485 }
486 /* failed to find any matching items -- formula should be checked */
487 return -1;
488 }
489
490 /* code copied from dump_alchemy() and modified by Raphael Quinet */
491 void
492 dump_alchemy_costs (void)
493 {
494 recipelist *fl = formulalist;
495 recipe *formula = NULL;
496 linked_char *next;
497 int num_ingred = 1;
498 int num_errors = 0;
499 long cost;
500 long tcost;
501
502 fprintf (logfile, "\n");
503 while (fl)
504 {
505 fprintf (logfile, "\n Formulae with %d ingredient%s %d Formulae with total_chance=%d\n",
506 num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
507 for (formula = fl->items; formula != NULL; formula = formula->next)
508 {
509 artifact *art = NULL;
510 archetype *at = NULL;
511 char buf[MAX_BUF];
512 size_t i;
513
514 for (i = 0; i < formula->arch_names; i++)
515 {
516 const char *string = formula->arch_name[i];
517
518 if ((at = archetype::find (string)) != NULL)
519 {
520 art = locate_recipe_artifact (formula, i);
521 if (!art && strcmp (formula->title, "NONE"))
522 LOG (llevError, "Formula %s has no artifact\n", &formula->title);
523 else
524 {
525 if (!strcmp (formula->title, "NONE"))
526 sprintf (buf, "%s", string);
527 else
528 sprintf (buf, "%s of %s", string, &formula->title);
529 fprintf (logfile, "\n%-40s bookchance %3d skill %s\n", buf, formula->chance, &(formula->skill));
530 if (formula->ingred != NULL)
531 {
532 tcost = 0;
533 for (next = formula->ingred; next != NULL; next = next->next)
534 {
535 cost = find_ingred_cost (next->name);
536 if (cost < 0)
537 num_errors++;
538 fprintf (logfile, "\t%-33s%5ld\n", &next->name, cost);
539 if (cost < 0 || tcost < 0)
540 tcost = -1;
541 else
542 tcost += cost;
543 }
544 if (art != NULL && art->item != NULL)
545 cost = at->clone.value * art->item->value;
546 else
547 cost = at->clone.value;
548 fprintf (logfile, "\t\tBuying result costs: %5ld", cost);
549 if (formula->yield > 1)
550 {
551 fprintf (logfile, " to %ld (max %d items)\n", cost * formula->yield, formula->yield);
552 cost = cost * (formula->yield + 1L) / 2L;
553 }
554 else
555 fprintf (logfile, "\n");
556 fprintf (logfile, "\t\tIngredients cost: %5ld\n\t\tComment: ", tcost);
557 if (tcost < 0)
558 fprintf (logfile, "Could not find some ingredients. Check the formula!\n");
559 else if (tcost > cost)
560 fprintf (logfile, "Ingredients are much too expensive. Useless formula.\n");
561 else if (tcost * 2L > cost)
562 fprintf (logfile, "Ingredients are too expensive.\n");
563 else if (tcost * 10L < cost)
564 fprintf (logfile, "Ingredients are too cheap.\n");
565 else
566 fprintf (logfile, "OK.\n");
567 }
568 }
569 }
570 else
571 LOG (llevError, "Can't find archetype:%s for formula %s\n", string, &formula->title);
572 }
573 }
574 fprintf (logfile, "\n");
575 fl = fl->next;
576 num_ingred++;
577 }
578 if (num_errors > 0)
579 fprintf (logfile, "WARNING: %d objects required by the formulae do not exist in the game.\n", num_errors);
580 }
581
582 const char *
583 ingred_name (const char *name)
584 {
585 const char *cp = name;
586
587 if (atoi (cp))
588 cp = strchr (cp, ' ') + 1;
589 return cp;
590 }
591
592 /* strtoint() - we use this to convert buf into an integer
593 * equal to the coadded sum of the (lowercase) character
594 * ASCII values in buf (times prepended integers).
595 */
596
597 int
598 strtoint (const char *buf)
599 {
600 const char *cp = ingred_name (buf);
601 int val = 0, len = strlen (cp), mult = numb_ingred (buf);
602
603 while (len)
604 {
605 val += tolower (*cp);
606 cp++;
607 len--;
608 }
609 return val * mult;
610 }
611
612 artifact *
613 locate_recipe_artifact (const recipe *rp, size_t idx)
614 {
615 object *item = get_archetype (rp->arch_name[idx]);
616 artifactlist *at = NULL;
617 artifact *art = NULL;
618
619 if (!item)
620 return (artifact *) NULL;
621
622 if ((at = find_artifactlist (item->type)))
623 for (art = at->items; art; art = art->next)
624 if (!strcmp (art->item->name, rp->title))
625 break;
626
627 item->destroy ();
628
629 return art;
630 }
631
632 int
633 numb_ingred (const char *buf)
634 {
635 int numb;
636
637 if ((numb = atoi (buf)))
638 return numb;
639 else
640 return 1;
641 }
642
643 recipelist *
644 get_random_recipelist (void)
645 {
646 recipelist *fl = NULL;
647 int number = 0, roll = 0;
648
649 /* first, determine # of recipelist we have */
650 for (fl = get_formulalist (1); fl; fl = fl->next)
651 number++;
652
653 /* now, randomly choose one */
654 if (number > 0)
655 roll = RANDOM () % number;
656
657 fl = get_formulalist (1);
658 while (roll && fl)
659 {
660 if (fl->next)
661 fl = fl->next;
662 else
663 break;
664 roll--;
665 }
666 if (!fl) /* failed! */
667 LOG (llevError, "get_random_recipelist(): no recipelists found!\n");
668 else if (fl->total_chance == 0)
669 fl = get_random_recipelist ();
670
671 return fl;
672 }
673
674 recipe *
675 get_random_recipe (recipelist * rpl)
676 {
677 recipelist *fl = rpl;
678 recipe *rp = NULL;
679 int r = 0;
680
681 /* looks like we have to choose a random one */
682 if (fl == NULL)
683 if ((fl = get_random_recipelist ()) == NULL)
684 return rp;
685
686 if (fl->total_chance > 0)
687 {
688 r = RANDOM () % fl->total_chance;
689 for (rp = fl->items; rp; rp = rp->next)
690 {
691 r -= rp->chance;
692 if (r < 0)
693 break;
694 }
695 }
696 return rp;
697 }
698
699 void
700 free_all_recipes (void)
701 {
702 recipelist *fl = formulalist, *flnext;
703 recipe *formula = NULL, *next;
704 linked_char *lchar, *charnext;
705
706 LOG (llevDebug, "Freeing all the recipes\n");
707 for (fl = formulalist; fl != NULL; fl = flnext)
708 {
709 flnext = fl->next;
710
711 for (formula = fl->items; formula != NULL; formula = next)
712 {
713 next = formula->next;
714
715 free (formula->arch_name[0]);
716 free (formula->arch_name);
717
718 for (lchar = formula->ingred; lchar; lchar = charnext)
719 {
720 charnext = lchar->next;
721 delete lchar;
722 }
723 delete formula;
724 }
725
726 delete fl;
727 }
728 }
729
730 /**
731 * Split a comma separated string list into words.
732 *
733 * @param str the string to split
734 *
735 * @param result_list pointer to return value for the newly created list; the
736 * caller is responsible for freeing both *result_list and **result_list.
737 *
738 * @param result_size pointer to return value for the size of the newly
739 * created list
740 */
741 static void
742 build_stringlist (const char *str, char ***result_list, size_t * result_size)
743 {
744 char *dup;
745 char *p;
746 size_t size;
747 size_t i;
748
749 dup = strdup (str);
750 if (dup == NULL)
751 fatal (OUT_OF_MEMORY);
752
753 size = 0;
754 for (p = strtok (dup, ","); p != NULL; p = strtok (NULL, ","))
755 size++;
756
757 *result_list = (char **) malloc (size * sizeof (*result_list));
758
759 *result_size = size;
760
761 for (i = 0; i < size; i++)
762 {
763 (*result_list)[i] = dup;
764 dup = dup + strlen (dup) + 1;
765 }
766 }