ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/recipe.C
Revision: 1.19
Committed: Mon Apr 16 06:23:40 2007 UTC (17 years, 2 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.18: +22 -33 lines
Log Message:
VERY EXPERIMENTAL

- change the way archetypes and treasurelists are being loaded:
  - referring to a nonexisting treasurelist will create an empty one
  - referring to a nonexisting archetype will create an empty one
  - archetypes/treasurelists will overwrite any existing object
    of the same name.

- net effect should be to allow reloading of archetypes and treasurelists
  at runtime at a later stage.

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