Crossfire Server, Branch 1.12  R12190
recipe.c
Go to the documentation of this file.
00001 /*
00002     CrossFire, A Multiplayer game for X-windows
00003 
00004     Copyright (C) 2001-2006 Crossfire Development Team
00005 
00006     This program is free software; you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation; either version 2 of the License, or
00009     (at your option) any later version.
00010 
00011     This program is distributed in the hope that it will be useful,
00012     but WITHOUT ANY WARRANTY; without even the implied warranty of
00013     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014     GNU General Public License for more details.
00015 
00016     You should have received a copy of the GNU General Public License
00017     along with this program; if not, write to the Free Software
00018     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
00019 
00020     The authors can be reached via e-mail at crossfire-devel@real-time.com
00021 */
00022 
00044 #include <stdlib.h>
00045 #include <global.h>
00046 #include <object.h>
00047 #include <ctype.h>
00048 
00049 static void build_stringlist(const char *str, char ***result_list, size_t *result_size);
00050 static void check_formulae(void);
00051 
00053 static recipelist *formulalist;
00054 
00063 static recipelist *init_recipelist(void) {
00064     recipelist *tl = (recipelist *)malloc(sizeof(recipelist));
00065     if (tl == NULL)
00066         fatal(OUT_OF_MEMORY);
00067     tl->total_chance = 0;
00068     tl->number = 0;
00069     tl->items = NULL;
00070     tl->next = NULL;
00071     return tl;
00072 }
00073 
00082 static recipe *get_empty_formula(void) {
00083     recipe *t = (recipe *)malloc(sizeof(recipe));
00084     if (t == NULL)
00085         fatal(OUT_OF_MEMORY);
00086     t->chance = 0;
00087     t->index = 0;
00088     t->transmute = 0;
00089     t->yield = 0;
00090     t->diff = 0;
00091     t->exp = 0;
00092     t->keycode = NULL;
00093     t->title = NULL;
00094     t->arch_names = 0;
00095     t->arch_name = NULL;
00096     t->skill = NULL;
00097     t->cauldron = NULL;
00098     t->ingred = NULL;
00099     t->next = NULL;
00100     return t;
00101 }
00102 
00112 recipelist *get_formulalist(int i) {
00113     recipelist *fl = formulalist;
00114     int number = i;
00115 
00116     while (fl && number > 1) {
00117         if (!(fl = fl->next))
00118             break;
00119         number--;
00120     }
00121     return fl;
00122 }
00123 
00134 static int check_recipe(const recipe *rp) {
00135     size_t i;
00136     int result;
00137 
00138     result = 1;
00139     for (i = 0; i < rp->arch_names; i++) {
00140         if (find_archetype(rp->arch_name[i]) != NULL) {
00141             artifact *art = locate_recipe_artifact(rp, i);
00142 
00143             if (!art && strcmp(rp->title, "NONE") != 0) {
00144                 LOG(llevError, "\nWARNING: Formula %s of %s has no artifact.\n", rp->arch_name[i], rp->title);
00145                 result = 0;
00146             }
00147         } else {
00148             LOG(llevError, "\nWARNING: Can't find archetype %s for formula %s\n", rp->arch_name[i], rp->title);
00149             result = 0;
00150         }
00151     }
00152 
00153     return result;
00154 }
00155 
00159 void init_formulae(void) {
00160     static int has_been_done = 0;
00161     FILE *fp;
00162     char filename[MAX_BUF], buf[MAX_BUF], *cp, *next;
00163     recipe *formula = NULL;
00164     recipelist *fl = init_recipelist();
00165     linked_char *tmp;
00166     int value, comp;
00167 
00168     if (!formulalist)
00169         formulalist = fl;
00170 
00171     if (has_been_done)
00172         return;
00173     else
00174         has_been_done = 1;
00175 
00176     snprintf(filename, sizeof(filename), "%s/formulae", settings.datadir);
00177     LOG(llevDebug, "Reading alchemical formulae from %s...\n", filename);
00178     if ((fp = open_and_uncompress(filename, 0, &comp)) == NULL) {
00179         LOG(llevError, "Can't open %s.\n", filename);
00180         return;
00181     }
00182 
00183     while (fgets(buf, MAX_BUF, fp) != NULL) {
00184         if (*buf == '#')
00185             continue;
00186         if ((cp = strchr(buf, '\n')) != NULL)
00187             *cp = '\0';
00188         cp = buf;
00189         while (*cp == ' ') /* Skip blanks */
00190             cp++;
00191 
00192         if (!strncmp(cp, "Object", 6)) {
00193             formula = get_empty_formula();
00194             formula->title = add_string(strchr(cp, ' ')+1);
00195         } else if (!strncmp(cp, "keycode", 7)) {
00196             formula->keycode = add_string(strchr(cp, ' ')+1);
00197         } else if (sscanf(cp, "trans %d", &value)) {
00198             formula->transmute = value;
00199         } else if (sscanf(cp, "yield %d", &value)) {
00200             formula->yield = value;
00201         } else if (sscanf(cp, "chance %d", &value)) {
00202             formula->chance = value;
00203         } else if (sscanf(cp, "exp %d", &value)) {
00204             formula->exp = value;
00205         } else if (sscanf(cp, "diff %d", &value)) {
00206             formula->diff = value;
00207         } else if (!strncmp(cp, "ingred", 6)) {
00208             int numb_ingred = 1;
00209             cp = strchr(cp, ' ')+1;
00210             do {
00211                 if ((next = strchr(cp, ',')) != NULL) {
00212                     *(next++) = '\0';
00213                     numb_ingred++;
00214                 }
00215                 tmp = (linked_char *)malloc(sizeof(linked_char));
00216                 tmp->name = add_string(cp);
00217                 tmp->next = formula->ingred;
00218                 formula->ingred = tmp;
00219                 /* each ingredient's ASCII value is coadded. Later on this
00220                  * value will be used allow us to search the formula lists
00221                  * quickly for the right recipe.
00222                  */
00223                 formula->index += strtoint(cp);
00224             } while ((cp = next) != NULL);
00225             /* now find the correct (# of ingred ordered) formulalist */
00226             fl = formulalist;
00227             while (numb_ingred != 1) {
00228                 if (!fl->next)
00229                     fl->next = init_recipelist();
00230                 fl = fl->next;
00231                 numb_ingred--;
00232             }
00233             fl->total_chance += formula->chance;
00234             fl->number++;
00235             formula->next = fl->items;
00236             fl->items = formula;
00237         } else if (!strncmp(cp, "arch", 4)) {
00238             build_stringlist(strchr(cp, ' ')+1, &formula->arch_name, &formula->arch_names);
00239             check_recipe(formula);
00240         } else if (!strncmp(cp, "skill", 5)) {
00241             formula->skill = add_string(strchr(cp, ' ')+1);
00242         } else if (!strncmp(cp, "cauldron", 8)) {
00243             formula->cauldron = add_string(strchr(cp, ' ')+1);
00244         } else
00245             LOG(llevError, "Unknown input in file %s: %s\n", filename, buf);
00246     }
00247     LOG(llevDebug, "done.\n");
00248     close_and_delete(fp, comp);
00249     /* Lastly, lets check for problems in formula we got */
00250     check_formulae();
00251 }
00252 
00265 static void check_formulae(void) {
00266     recipelist *fl;
00267     recipe *check, *formula;
00268     int numb = 1;
00269 
00270     LOG(llevDebug, "Checking formulae lists...\n");
00271 
00272     for (fl = formulalist; fl != NULL; fl = fl->next) {
00273         for (formula = fl->items; formula != NULL; formula = formula->next)
00274             for (check = formula->next; check != NULL; check = check->next)
00275                 if (check->index == formula->index && strcmp(check->cauldron, formula->cauldron) == 0) {
00276                     /* if the recipes don't have the same facility, then no issue anyway. */
00277                     LOG(llevError, " ERROR: On %d ingred list:\n", numb);
00278                     LOG(llevError, "Formulae [%s] of %s and [%s] of %s have matching index id (%d)\n", formula->arch_name[0], formula->title, check->arch_name[0], check->title, formula->index);
00279                 }
00280         numb++;
00281     }
00282 
00283     LOG(llevDebug, "done checking.\n");
00284 }
00285 
00293 void dump_alchemy(void) {
00294     recipelist *fl = formulalist;
00295     recipe *formula = NULL;
00296     linked_char *next;
00297     int num_ingred = 1;
00298 
00299     fprintf(logfile, "\n");
00300     while (fl) {
00301         fprintf(logfile, "\n Formulae with %d ingredient%s  %d Formulae with total_chance=%d\n", num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
00302         for (formula = fl->items; formula != NULL; formula = formula->next) {
00303             artifact *art = NULL;
00304             char buf[MAX_BUF];
00305             size_t i;
00306 
00307             for (i = 0; i < formula->arch_names; i++) {
00308                 const char *string = formula->arch_name[i];
00309 
00310                 if (find_archetype(string) != NULL) {
00311                     art = locate_recipe_artifact(formula, i);
00312                     if (!art && strcmp(formula->title, "NONE"))
00313                         LOG(llevError, "Formula %s has no artifact\n", formula->title);
00314                     else {
00315                         if (strcmp(formula->title, "NONE"))
00316                             snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
00317                         else
00318                             snprintf(buf, sizeof(buf), "%s", string);
00319                         fprintf(logfile, "%-30s(%d) bookchance %3d  ", buf, formula->index, formula->chance);
00320                         fprintf(logfile, "skill %s", formula->skill);
00321                         fprintf(logfile, "\n");
00322                         if (formula->ingred != NULL) {
00323                             int nval = 0, tval = 0;
00324                             fprintf(logfile, "\tIngred: ");
00325                             for (next = formula->ingred; next != NULL; next = next->next) {
00326                                 if (nval != 0)
00327                                     fprintf(logfile, ",");
00328                                 fprintf(logfile, "%s(%d)", next->name, (nval = strtoint(next->name)));
00329                                 tval += nval;
00330                             }
00331                             fprintf(logfile, "\n");
00332                             if (tval != formula->index)
00333                                 fprintf(logfile, "WARNING:ingredient list and formula values not equal.\n");
00334                         }
00335                         if (formula->skill != NULL)
00336                             fprintf(logfile, "\tSkill Required: %s", formula->skill);
00337                         if (formula->cauldron != NULL)
00338                             fprintf(logfile, "\tCauldron: %s\n", formula->cauldron);
00339                         fprintf(logfile, "\tDifficulty: %d\t Exp: %d\n", formula->diff, formula->exp);
00340                     }
00341                 } else
00342                     LOG(llevError, "Can't find archetype:%s for formula %s\n", string, formula->title);
00343             }
00344         }
00345         fprintf(logfile, "\n");
00346         fl = fl->next;
00347         num_ingred++;
00348     }
00349 }
00350 
00365 archetype *find_treasure_by_name(const treasure *t, const char *name, int depth) {
00366     treasurelist *tl;
00367     archetype *at;
00368 
00369     if (depth > 10)
00370         return NULL;
00371 
00372     while (t != NULL) {
00373         if (t->name != NULL) {
00374             tl = find_treasurelist(t->name);
00375             at = find_treasure_by_name(tl->items, name, depth+1);
00376             if (at != NULL)
00377                 return at;
00378         } else {
00379             if (!strcasecmp(t->item->clone.name, name))
00380                 return t->item;
00381         }
00382         if (t->next_yes != NULL) {
00383             at = find_treasure_by_name(t->next_yes, name, depth);
00384             if (at != NULL)
00385                 return at;
00386         }
00387         if (t->next_no != NULL) {
00388             at = find_treasure_by_name(t->next_no, name, depth);
00389             if (at != NULL)
00390                 return at;
00391         }
00392         t = t->next;
00393     }
00394     return NULL;
00395 }
00396 
00416 static long find_ingred_cost(const char *name) {
00417     archetype *at;
00418     archetype *at2;
00419     artifactlist *al;
00420     artifact *art;
00421     long mult;
00422     char *cp;
00423     char part1[100];
00424     char part2[100];
00425 
00426     /* same as atoi(), but skip number */
00427     mult = 0;
00428     while (isdigit(*name)) {
00429         mult = 10*mult+(*name-'0');
00430         name++;
00431     }
00432     if (mult > 0)
00433         name++;
00434     else
00435         mult = 1;
00436     /* first, try to match the name of an archetype */
00437     for (at = first_archetype; at != NULL; at = at->next) {
00438         if (at->clone.title != NULL) {
00439             /* inefficient, but who cares? */
00440             snprintf(part1, sizeof(part1), "%s %s", at->clone.name, at->clone.title);
00441             if (!strcasecmp(part1, name))
00442                 return mult*at->clone.value;
00443         }
00444         if (!strcasecmp(at->clone.name, name))
00445             return mult*at->clone.value;
00446     }
00447     /* second, try to match an artifact ("arch of something") */
00448     cp = strstr(name, " of ");
00449     if (cp != NULL) {
00450         strcpy(part1, name);
00451         part1[cp-name] = '\0';
00452         strcpy(part2, cp+4);
00453         /* find the first archetype matching the first part of the name */
00454         for (at = first_archetype; at != NULL; at = at->next)
00455             if (!strcasecmp(at->clone.name, part1) && at->clone.title == NULL)
00456                 break;
00457         if (at != NULL) {
00458             /* find the first artifact derived from that archetype (same type) */
00459             for (al = first_artifactlist; al != NULL; al = al->next)
00460                 if (al->type == at->clone.type) {
00461                     for (art = al->items; art != NULL; art = art->next)
00462                         if (!strcasecmp(art->item->name, part2))
00463                             return mult*at->clone.value*art->item->value;
00464                 }
00465         }
00466     }
00467     /* third, try to match a body part ("arch's something") */
00468     cp = strstr(name, "'s ");
00469     if (cp != NULL) {
00470         strcpy(part1, name);
00471         part1[cp-name] = '\0';
00472         strcpy(part2, cp+3);
00473         /* examine all archetypes matching the first part of the name */
00474         for (at = first_archetype; at != NULL; at = at->next)
00475             if (!strcasecmp(at->clone.name, part1) && at->clone.title == NULL) {
00476                 if (at->clone.randomitems != NULL) {
00477                     at2 = find_treasure_by_name(at->clone.randomitems->items, part2, 0);
00478                     if (at2 != NULL)
00479                         return mult*at2->clone.value*isqrt(at->clone.level*2);
00480                 }
00481             }
00482     }
00483     /* failed to find any matching items -- formula should be checked */
00484     return -1;
00485 }
00486 
00495 void dump_alchemy_costs(void) {
00496     recipelist *fl = formulalist;
00497     recipe *formula = NULL;
00498     linked_char *next;
00499     int num_ingred = 1;
00500     int num_errors = 0;
00501     long cost;
00502     long tcost;
00503 
00504     fprintf(logfile, "\n");
00505     while (fl) {
00506         fprintf(logfile, "\n Formulae with %d ingredient%s  %d Formulae with total_chance=%d\n", num_ingred, num_ingred > 1 ? "s." : ".", fl->number, fl->total_chance);
00507         for (formula = fl->items; formula != NULL; formula = formula->next) {
00508             artifact *art = NULL;
00509             archetype *at = NULL;
00510             char buf[MAX_BUF];
00511             size_t i;
00512 
00513             for (i = 0; i < formula->arch_names; i++) {
00514                 const char *string = formula->arch_name[i];
00515 
00516                 if ((at = find_archetype(string)) != NULL) {
00517                     art = locate_recipe_artifact(formula, i);
00518                     if (!art && strcmp(formula->title, "NONE"))
00519                         LOG(llevError, "Formula %s has no artifact\n", formula->title);
00520                     else {
00521                         if (!strcmp(formula->title, "NONE"))
00522                             snprintf(buf, sizeof(buf), "%s", string);
00523                         else
00524                             snprintf(buf, sizeof(buf), "%s of %s", string, formula->title);
00525                         fprintf(logfile, "\n%-40s bookchance %3d  skill %s\n", buf, formula->chance, formula->skill);
00526                         if (formula->ingred != NULL) {
00527                             tcost = 0;
00528                             for (next = formula->ingred; next != NULL; next = next->next) {
00529                                 cost = find_ingred_cost(next->name);
00530                                 if (cost < 0)
00531                                     num_errors++;
00532                                 fprintf(logfile, "\t%-33s%5ld\n", next->name, cost);
00533                                 if (cost < 0 || tcost < 0)
00534                                     tcost = -1;
00535                                 else
00536                                     tcost += cost;
00537                             }
00538                             if (art != NULL && art->item != NULL)
00539                                 cost = at->clone.value*art->item->value;
00540                             else
00541                                 cost = at->clone.value;
00542                             fprintf(logfile, "\t\tBuying result costs: %5ld", cost);
00543                             if (formula->yield > 1) {
00544                                 fprintf(logfile, " to %ld (max %d items)\n", cost*formula->yield, formula->yield);
00545                                 cost = cost*(formula->yield+1L)/2L;
00546                             } else
00547                                 fprintf(logfile, "\n");
00548                             fprintf(logfile, "\t\tIngredients cost:    %5ld\n\t\tComment: ", tcost);
00549                             if (tcost < 0)
00550                                 fprintf(logfile, "Could not find some ingredients.  Check the formula!\n");
00551                             else if (tcost > cost)
00552                                 fprintf(logfile, "Ingredients are much too expensive.  Useless formula.\n");
00553                             else if (tcost*2L > cost)
00554                                 fprintf(logfile, "Ingredients are too expensive.\n");
00555                             else if (tcost*10L < cost)
00556                                 fprintf(logfile, "Ingredients are too cheap.\n");
00557                             else
00558                                 fprintf(logfile, "OK.\n");
00559                         }
00560                     }
00561                 } else
00562                     LOG(llevError, "Can't find archetype:%s for formula %s\n", string, formula->title);
00563             }
00564         }
00565         fprintf(logfile, "\n");
00566         fl = fl->next;
00567         num_ingred++;
00568     }
00569     if (num_errors > 0)
00570         fprintf(logfile, "WARNING: %d objects required by the formulae do not exist in the game.\n", num_errors);
00571 }
00572 
00581 static const char *ingred_name(const char *name) {
00582     const char *cp = name;
00583 
00584     if (atoi(cp))
00585         cp = strchr(cp, ' ')+1;
00586     return cp;
00587 }
00588 
00597 static int numb_ingred(const char *buf) {
00598     int numb;
00599 
00600     if ((numb = atoi(buf)))
00601         return numb;
00602     else
00603         return 1;
00604 }
00605 
00616 int strtoint(const char *buf) {
00617     const char *cp = ingred_name(buf);
00618     int val = 0, len = strlen(cp), mult = numb_ingred(buf);
00619 
00620     while (len) {
00621         val += tolower(*cp);
00622         cp++; len--;
00623     }
00624     return val*mult;
00625 }
00626 
00637 artifact *locate_recipe_artifact(const recipe *rp, size_t idx) {
00638     object *item = create_archetype(rp->arch_name[idx]);
00639     artifactlist *at = NULL;
00640     artifact *art = NULL;
00641 
00642     if (!item)
00643         return (artifact *)NULL;
00644 
00645     if ((at = find_artifactlist(item->type)))
00646         for (art = at->items; art; art = art->next)
00647             if (!strcmp(art->item->name, rp->title) && legal_artifact_combination(item, art))
00648                 break;
00649 
00650     free_object(item);
00651 
00652     return art;
00653 }
00654 
00661 static recipelist *get_random_recipelist(void) {
00662     recipelist *fl = NULL;
00663     int number = 0, roll = 0;
00664 
00665     /* first, determine # of recipelist we have */
00666     for (fl = get_formulalist(1); fl; fl = fl->next)
00667         number++;
00668 
00669     /* now, randomly choose one */
00670     if (number > 0)
00671         roll = RANDOM()%number;
00672 
00673     fl = get_formulalist(1);
00674     while (roll && fl) {
00675         if (fl->next)
00676             fl = fl->next;
00677         else
00678             break;
00679         roll--;
00680     }
00681     if (!fl) /* failed! */
00682         LOG(llevError, "get_random_recipelist(): no recipelists found!\n");
00683     else if (fl->total_chance == 0)
00684         fl = get_random_recipelist();
00685 
00686     return fl;
00687 }
00688 
00697 recipe *get_random_recipe(recipelist *rpl) {
00698     recipelist *fl = rpl;
00699     recipe *rp = NULL;
00700     int r = 0;
00701 
00702     /* looks like we have to choose a random one */
00703     if (fl == NULL)
00704         if ((fl = get_random_recipelist()) == NULL)
00705             return rp;
00706 
00707     if (fl->total_chance > 0) {
00708         r = RANDOM()%fl->total_chance;
00709         for (rp = fl->items; rp; rp = rp->next) {
00710             r -= rp->chance;
00711             if (r < 0)
00712                 break;
00713         }
00714     }
00715     return rp;
00716 }
00717 
00721 void free_all_recipes(void) {
00722     recipelist *fl = formulalist, *flnext;
00723     recipe *formula = NULL, *next;
00724     linked_char *lchar, *charnext;
00725 
00726     LOG(llevDebug, "Freeing all the recipes\n");
00727     for (fl = formulalist; fl != NULL; fl = flnext) {
00728         flnext = fl->next;
00729 
00730         for (formula = fl->items; formula != NULL; formula = next) {
00731             next = formula->next;
00732 
00733             free(formula->arch_name[0]);
00734             free(formula->arch_name);
00735             if (formula->title)
00736                 free_string(formula->title);
00737             if (formula->skill)
00738                 free_string(formula->skill);
00739             if (formula->cauldron)
00740                 free_string(formula->cauldron);
00741             for (lchar = formula->ingred; lchar; lchar = charnext) {
00742                 charnext = lchar->next;
00743                 free_string(lchar->name);
00744                 free(lchar);
00745             }
00746             free(formula);
00747         }
00748         free(fl);
00749     }
00750     formulalist = NULL;
00751 }
00752 
00764 static void build_stringlist(const char *str, char ***result_list, size_t *result_size) {
00765     char *dup;
00766     char *p;
00767     size_t size;
00768     size_t i;
00769 
00770     dup = strdup_local(str);
00771     if (dup == NULL)
00772         fatal(OUT_OF_MEMORY);
00773 
00774     size = 0;
00775     for (p = strtok(dup, ","); p != NULL; p = strtok(NULL, ","))
00776         size++;
00777 
00778     *result_list = malloc(size*sizeof(*result_list));
00779     if (*result_list == NULL)
00780         fatal(OUT_OF_MEMORY);
00781     *result_size = size;
00782 
00783     for (i = 0; i < size; i++) {
00784         (*result_list)[i] = dup;
00785         dup = dup+strlen(dup)+1;
00786     }
00787 }