ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/recipe.C
Revision: 1.23
Committed: Sun Jul 1 05:00:18 2007 UTC (16 years, 10 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_2, rel-2_3
Changes since 1.22: +11 -12 lines
Log Message:
- upgrade crossfire trt to the GPL version 3 (hopefully correctly).
- add a single file covered by the GNU Affero General Public License
  (which is not yet released, so I used the current draft, which is
  legally a bit wavy, but its likely better than nothing as it expresses
  direct intent by the authors, and we can upgrade as soon as it has been
  released).
  * this should ensure availability of source code for the server at least
    and hopefully also archetypes and maps even when modified versions
    are not being distributed, in accordance of section 13 of the agplv3.

File Contents

# Content
1 /*
2 * This file is part of Crossfire TRT, the Roguelike Realtime MORPG.
3 *
4 * Copyright (©) 2005,2006,2007 Marc Alexander Lehmann / Robin Redeker / the Crossfire TRT team
5 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6 * Copyright (©) 1992,2007 Frank Tore Johansen
7 *
8 * Crossfire TRT is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 *
21 * The authors can be reached via e-mail to <crossfire@schmorp.de>
22 */
23
24 /* Basic stuff for use with the alchemy code. Clearly some of this stuff
25 * could go into server/alchemy, but I left it here just in case it proves
26 * more generally useful.
27 *
28 * Nov 1995 - file created by b.t. thomas@astro.psu.edu
29 */
30
31
32 /* Our definition of 'formula' is any product of an alchemical process.
33 * Ingredients are just comma delimited list of archetype (or object)
34 * names.
35 */
36
37 /* Example 'formula' entry in libdir/formulae:
38 * object transparency
39 * chance 10
40 * ingred dust of beholdereye,gem
41 * arch potion_generic
42 */
43
44 #include <cctype>
45
46 #include <global.h>
47 #include <object.h>
48
49 static void build_stringlist (const char *str, char ***result_list, size_t * result_size);
50
51 static recipelist *formulalist;
52
53 static recipelist *
54 init_recipelist (void)
55 {
56 recipelist *tl = new recipelist;
57
58 tl->total_chance = 0;
59 tl->number = 0;
60 tl->items = 0;
61 tl->next = 0;
62
63 return tl;
64 }
65
66 static recipe *
67 get_empty_formula (void)
68 {
69 recipe *t = new recipe;
70
71 t->chance = 0;
72 t->index = 0;
73 t->transmute = 0;
74 t->yield = 0;
75 t->diff = 0;
76 t->exp = 0;
77 t->keycode = 0;
78 t->title = NULL;
79 t->arch_names = 0;
80 t->arch_name = NULL;
81 t->skill = NULL;
82 t->cauldron = NULL;
83 t->ingred = NULL;
84 t->next = NULL;
85 return t;
86 }
87
88 /* get_formulalist() - returns pointer to the formula list */
89 recipelist *
90 get_formulalist (int i)
91 {
92 recipelist *fl = formulalist;
93 int number = i;
94
95 while (fl && number > 1)
96 {
97 if (!(fl = fl->next))
98 break;
99 number--;
100 }
101
102 return fl;
103 }
104
105 /* check_recipe() - makes sure we actually have the requested artifact
106 * and archetype. */
107 static int
108 check_recipe (const recipe *rp)
109 {
110 size_t i;
111 int result = 1;
112
113 for (i = 0; i < rp->arch_names; i++)
114 {
115 if (archetype::find (rp->arch_name[i]) != NULL)
116 {
117 artifact *art = locate_recipe_artifact (rp, i);
118
119 if (!art && strcmp (rp->title, "NONE") != 0)
120 {
121 LOG (llevError, "WARNING: Formula %s of %s has no artifact.\n", rp->arch_name[i], &rp->title);
122 result = 0;
123 }
124 }
125 else
126 {
127 LOG (llevError, "WARNING: Can't find archetype %s for formula %s\n", rp->arch_name[i], &rp->title);
128 result = 0;
129 }
130 }
131
132 return result;
133 }
134
135 /*
136 * init_formulae() - Builds up the lists of formula from the file in
137 * the libdir. -b.t.
138 */
139 void
140 init_formulae (void)
141 {
142 static int has_been_done = 0;
143 FILE *fp;
144 char filename[MAX_BUF], buf[MAX_BUF], *cp, *next;
145 recipe *formula = NULL;
146 recipelist *fl = init_recipelist ();
147 linked_char *tmp;
148 int value, comp;
149
150 if (!formulalist)
151 formulalist = fl;
152
153 if (has_been_done)
154 return;
155 else
156 has_been_done = 1;
157
158 sprintf (filename, "%s/formulae", settings.datadir);
159 LOG (llevDebug, "Reading alchemical formulae from %s...\n", filename);
160 if ((fp = open_and_uncompress (filename, 0, &comp)) == NULL)
161 {
162 LOG (llevError, "Can't open %s.\n", filename);
163 return;
164 }
165
166 while (fgets (buf, MAX_BUF, fp) != NULL)
167 {
168 if (*buf == '#')
169 continue;
170 if ((cp = strchr (buf, '\n')) != NULL)
171 *cp = '\0';
172 cp = buf;
173 while (*cp == ' ') /* Skip blanks */
174 cp++;
175
176 if (!strncmp (cp, "object", 6))
177 {
178 formula = get_empty_formula ();
179 formula->title = strchr (cp, ' ') + 1;
180 }
181 else if (!strncmp (cp, "keycode", 7))
182 formula->keycode = strchr (cp, ' ') + 1;
183 else if (sscanf (cp, "trans %d", &value))
184 formula->transmute = (uint16) value;
185 else if (sscanf (cp, "yield %d", &value))
186 formula->yield = (uint16) value;
187 else if (sscanf (cp, "chance %d", &value))
188 formula->chance = (uint16) value;
189 else if (sscanf (cp, "exp %d", &value))
190 formula->exp = (uint16) value;
191 else if (sscanf (cp, "diff %d", &value))
192 formula->diff = (uint16) value;
193 else if (!strncmp (cp, "ingred", 6))
194 {
195 int numb_ingred = 1;
196
197 cp = strchr (cp, ' ') + 1;
198 do
199 {
200 if ((next = strchr (cp, ',')) != NULL)
201 {
202 *(next++) = '\0';
203 numb_ingred++;
204 }
205
206 tmp = new linked_char;
207
208 tmp->name = cp;
209 tmp->next = formula->ingred;
210 formula->ingred = tmp;
211 /* each ingredient's ASCII value is coadded. Later on this
212 * value will be used allow us to search the formula lists
213 * quickly for the right recipe.
214 */
215 formula->index += strtoint (cp);
216 }
217 while ((cp = next) != NULL);
218
219 /* now find the correct (# of ingred ordered) formulalist */
220 fl = formulalist;
221 while (numb_ingred != 1)
222 {
223 if (!fl->next)
224 fl->next = init_recipelist ();
225
226 fl = fl->next;
227 numb_ingred--;
228 }
229
230 fl->total_chance += formula->chance;
231 fl->number++;
232 formula->next = fl->items;
233 fl->items = formula;
234 }
235 else if (!strncmp (cp, "arch", 4))
236 {
237 build_stringlist (strchr (cp, ' ') + 1, &formula->arch_name, &formula->arch_names);
238 check_recipe (formula);
239 }
240 else if (!strncmp (cp, "skill", 5))
241 formula->skill = strchr (cp, ' ') + 1;
242 else if (!strncmp (cp, "cauldron", 8))
243 formula->cauldron = strchr (cp, ' ') + 1;
244 else
245 LOG (llevError, "Unknown input in file %s: %s\n", filename, buf);
246 }
247
248 LOG (llevDebug, "done.\n");
249 close_and_delete (fp, comp);
250 /* Lastly, lets check for problems in formula we got */
251 check_formulae ();
252 }
253
254 /* check_formulae()- since we are doing a squential search on the
255 * formulae lists now, we have to be carefull that we dont have 2
256 * formula with the exact same index value. Under the new nbatches
257 * code, it is possible to have multiples of ingredients in a cauldron
258 * which could result in an index formula mismatch. We *don't* check for
259 * that possibility here. -b.t.
260 */
261 void
262 check_formulae (void)
263 {
264 recipelist *fl;
265 recipe *check, *formula;
266 int numb = 1;
267
268 LOG (llevDebug, "Checking formulae lists...\n");
269
270 for (fl = formulalist; fl; fl = fl->next)
271 {
272 for (formula = fl->items; formula; formula = formula->next)
273 for (check = formula->next; check; check = check->next)
274 if (check->index == formula->index)
275 {
276 LOG (llevError, " ERROR: On %d ingred list: ", numb);
277 LOG (llevError, "Formulae [%s] of %s and [%s] of %s have matching index id (%d)\n",
278 formula->arch_name[0], &formula->title, check->arch_name[0], &check->title, formula->index);
279 }
280 numb++;
281 }
282
283 LOG (llevDebug, "done.\n");
284
285 }
286
287 /* Borrowed (again) from the artifacts code for this */
288
289 void
290 dump_alchemy (void)
291 {
292 recipelist *fl = formulalist;
293 recipe *formula = NULL;
294 linked_char *next;
295 int num_ingred = 1;
296
297 fprintf (logfile, "\n");
298 while (fl)
299 {
300 fprintf (logfile, "\n Formulae with %d ingredient%s %d Formulae with total_chance=%d\n",
301 num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
302 for (formula = fl->items; formula; formula = formula->next)
303 {
304 artifact *art = NULL;
305 char buf[MAX_BUF];
306 size_t i;
307
308 for (i = 0; i < formula->arch_names; i++)
309 {
310 const char *string = formula->arch_name[i];
311
312 if (archetype::find (string) != NULL)
313 {
314 art = locate_recipe_artifact (formula, i);
315 if (!art && strcmp (formula->title, "NONE"))
316 LOG (llevError, "Formula %s has no artifact!\n", &formula->title);
317 else
318 {
319 if (strcmp (formula->title, "NONE"))
320 sprintf (buf, "%s of %s", string, &formula->title);
321 else
322 sprintf (buf, "%s", string);
323 fprintf (logfile, "%-30s(%d) bookchance %3d ", buf, formula->index, formula->chance);
324 fprintf (logfile, "skill %s", &formula->skill);
325 fprintf (logfile, "\n");
326 if (formula->ingred != NULL)
327 {
328 int nval = 0, tval = 0;
329
330 fprintf (logfile, "\tIngred: ");
331 for (next = formula->ingred; next != NULL; next = next->next)
332 {
333 if (nval != 0)
334 fprintf (logfile, ",");
335 fprintf (logfile, "%s(%d)", &next->name, (nval = strtoint (next->name)));
336 tval += nval;
337 }
338 fprintf (logfile, "\n");
339 if (tval != formula->index)
340 fprintf (logfile, "WARNING:ingredient list and formula values not equal.\n");
341 }
342 if (formula->skill != NULL)
343 fprintf (logfile, "\tSkill Required: %s", &formula->skill);
344 if (formula->cauldron != NULL)
345 fprintf (logfile, "\tCauldron: %s\n", &formula->cauldron);
346 fprintf (logfile, "\tDifficulty: %d\t Exp: %d\n", formula->diff, formula->exp);
347 }
348 }
349 else
350 LOG (llevError, "Can't find archetype:%s for formula %s\n", string, &formula->title);
351 }
352 }
353 fprintf (logfile, "\n");
354 fl = fl->next;
355 num_ingred++;
356 }
357 }
358
359 /* Find a treasure with a matching name. The 'depth' parameter is
360 * only there to prevent infinite loops in treasure lists (a list
361 * referencing another list pointing back to the first one). */
362 archetype *
363 find_treasure_by_name (const treasure *t, const char *name, int depth)
364 {
365 if (depth > 10)
366 return 0;
367
368 while (t)
369 {
370 if (t->name)
371 {
372 if (treasurelist *tl = treasurelist::find (t->name))
373 if (tl->items)
374 if (archetype *at = find_treasure_by_name (tl->items, name, depth + 1))
375 return at;
376 }
377 else
378 {
379 if (t->item && !strcasecmp (t->item->object::name, name))
380 return t->item;
381 }
382
383 if (t->next_yes)
384 if (archetype *at = find_treasure_by_name (t->next_yes, name, depth))
385 return at;
386
387 if (t->next_no)
388 if (archetype *at = find_treasure_by_name (t->next_no, name, depth))
389 return at;
390
391 t = t->next;
392 }
393
394 return 0;
395 }
396
397 /* If several archetypes have the same name, the value of the first
398 * one with that name will be returned. This happens for the
399 * mushrooms (mushroom_1, mushroom_2 and mushroom_3). For the
400 * monsters' body parts, there may be several monsters with the same
401 * name. This is not a problem if these monsters have the same level
402 * (e.g. sage & c_sage) or if only one of the monsters generates the
403 * body parts that we are looking for (e.g. big_dragon and
404 * big_dragon_worthless). */
405 long
406 find_ingred_cost (const char *name)
407 {
408 archetype *at2;
409 artifactlist *al;
410 artifact *art;
411 long mult;
412 char *cp;
413 char part1[100];
414 char part2[100];
415
416 /* same as atoi(), but skip number */
417 mult = 0;
418 while (isdigit (*name))
419 {
420 mult = 10 * mult + (*name - '0');
421 name++;
422 }
423
424 if (mult > 0)
425 name++;
426 else
427 mult = 1;
428
429 /* first, try to match the name of an archetype */
430 for_all_archetypes (at)
431 {
432 if (at->title != NULL)
433 {
434 /* inefficient, but who cares? */
435 sprintf (part1, "%s %s", &at->object::name, &at->title);
436 if (!strcasecmp (part1, name))
437 return mult * at->value;
438 }
439 if (!strcasecmp (at->object::name, name))
440 return mult * at->value;
441 }
442
443 /* second, try to match an artifact ("arch of something") */
444 cp = strstr (name, " of ");
445 if (cp != NULL)
446 {
447 strcpy (part1, name);
448 part1[cp - name] = '\0';
449 strcpy (part2, cp + 4);
450
451 /* find the first archetype matching the first part of the name */
452 for_all_archetypes (at)
453 if (!strcasecmp (at->object::name, part1) && at->title == NULL)
454 {
455 /* find the first artifact derived from that archetype (same type) */
456 for (al = first_artifactlist; al; al = al->next)
457 if (al->type == at->type)
458 {
459 for (art = al->items; art; art = art->next)
460 if (!strcasecmp (art->item->name, part2))
461 return mult * at->value * art->item->value;
462 }
463 }
464 }
465
466 /* third, try to match a body part ("arch's something") */
467 cp = strstr (name, "'s ");
468 if (cp)
469 {
470 strcpy (part1, name);
471 part1[cp - name] = '\0';
472 strcpy (part2, cp + 3);
473 /* examine all archetypes matching the first part of the name */
474 for_all_archetypes (at)
475 if (!strcasecmp (at->object::name, part1) && at->title == NULL)
476 {
477 if (at->randomitems)
478 {
479 at2 = find_treasure_by_name (at->randomitems->items, part2, 0);
480 if (at2)
481 return mult * at2->value * isqrt (at->level * 2);
482 }
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; 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->value * art->item->value;
546 else
547 cost = at->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 archetype *at = archetype::find (rp->arch_name [idx]);
616
617 if (at)
618 if (artifactlist *al = find_artifactlist (at->type))
619 for (artifact *art = al->items; art; art = art->next)
620 if (art->item->name == rp->title)
621 return art;
622
623 return 0;
624 }
625
626 int
627 numb_ingred (const char *buf)
628 {
629 int numb;
630
631 if ((numb = atoi (buf)))
632 return numb;
633 else
634 return 1;
635 }
636
637 recipelist *
638 get_random_recipelist (void)
639 {
640 recipelist *fl = NULL;
641 int number = 0, roll = 0;
642
643 /* first, determine # of recipelist we have */
644 for (fl = get_formulalist (1); fl; fl = fl->next)
645 number++;
646
647 /* now, randomly choose one */
648 if (number > 0)
649 roll = rndm (number);
650
651 fl = get_formulalist (1);
652 while (roll && fl)
653 {
654 if (fl->next)
655 fl = fl->next;
656 else
657 break;
658 roll--;
659 }
660 if (!fl) /* failed! */
661 LOG (llevError, "get_random_recipelist(): no recipelists found!\n");
662 else if (fl->total_chance == 0)
663 fl = get_random_recipelist ();
664
665 return fl;
666 }
667
668 recipe *
669 get_random_recipe (recipelist * rpl)
670 {
671 recipelist *fl = rpl;
672 recipe *rp = NULL;
673 int r = 0;
674
675 /* looks like we have to choose a random one */
676 if (fl == NULL)
677 if ((fl = get_random_recipelist ()) == NULL)
678 return rp;
679
680 if (fl->total_chance > 0)
681 {
682 r = rndm (fl->total_chance);
683 for (rp = fl->items; rp; rp = rp->next)
684 {
685 r -= rp->chance;
686 if (r < 0)
687 break;
688 }
689 }
690 return rp;
691 }
692
693 void
694 free_all_recipes (void)
695 {
696 recipelist *fl = formulalist, *flnext;
697 recipe *formula = NULL, *next;
698 linked_char *lchar, *charnext;
699
700 LOG (llevDebug, "Freeing all the recipes\n");
701 for (fl = formulalist; fl != NULL; fl = flnext)
702 {
703 flnext = fl->next;
704
705 for (formula = fl->items; formula != NULL; formula = next)
706 {
707 next = formula->next;
708
709 free (formula->arch_name[0]);
710 free (formula->arch_name);
711
712 for (lchar = formula->ingred; lchar; lchar = charnext)
713 {
714 charnext = lchar->next;
715 delete lchar;
716 }
717 delete formula;
718 }
719
720 delete fl;
721 }
722 }
723
724 /**
725 * Split a comma separated string list into words.
726 *
727 * @param str the string to split
728 *
729 * @param result_list pointer to return value for the newly created list; the
730 * caller is responsible for freeing both *result_list and **result_list.
731 *
732 * @param result_size pointer to return value for the size of the newly
733 * created list
734 */
735 static void
736 build_stringlist (const char *str, char ***result_list, size_t * result_size)
737 {
738 char *dup;
739 char *p;
740 size_t size;
741 size_t i;
742
743 dup = strdup (str);
744 if (dup == NULL)
745 fatal (OUT_OF_MEMORY);
746
747 size = 0;
748 for (p = strtok (dup, ","); p != NULL; p = strtok (NULL, ","))
749 size++;
750
751 *result_list = (char **) malloc (size * sizeof (*result_list));
752
753 *result_size = size;
754
755 for (i = 0; i < size; i++)
756 {
757 (*result_list)[i] = dup;
758 dup = dup + strlen (dup) + 1;
759 }
760 }