Crossfire Server, Branch 1.12
R12190
|
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 }