ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/recipe.C
Revision: 1.1
Committed: Sun Aug 13 17:16:00 2006 UTC (17 years, 9 months ago) by elmex
Content type: text/plain
Branch: MAIN
Log Message:
Made server compile with C++.
Removed cfanim plugin and crossedit.
C++ here we come.

File Contents

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