ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/common/recipe.C
Revision: 1.3
Committed: Sun Sep 3 00:18:40 2006 UTC (17 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.2: +23 -30 lines
Log Message:
THIS CODE WILL NOT COMPILE
use the STABLE tag instead.

- major changes in object lifetime and memory management
- replaced manual refcounting by shstr class
- removed quest system
- many optimisations
- major changes

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