00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00036 #include <global.h>
00037 #include <object.h>
00038 #ifndef __CEXTRACT__
00039 #include <sproto.h>
00040 #endif
00041 #include <skills.h>
00042 #include <spells.h>
00043
00045 #if 0
00046 #define ALCHEMY_DEBUG
00047 #endif
00048
00050 #if 0
00051 #define EXTREME_ALCHEMY_DEBUG
00052 #endif
00053
00055 static const char *const cauldron_effect [] = {
00056 "vibrates briefly",
00057 "produces a cloud of steam",
00058 "emits bright flames",
00059 "pours forth heavy black smoke",
00060 "emits sparks",
00061 "shoots out small flames",
00062 "whines painfully",
00063 "hiccups loudly",
00064 "wheezes",
00065 "burps",
00066 "shakes",
00067 "rattles",
00068 "makes chugging sounds",
00069 "smokes heavily for a while"
00070 };
00071
00072
00073 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster);
00074 static recipe *find_recipe(recipelist *fl, int formula, object *ingredients);
00075 static int content_recipe_value(object *op);
00076 static int numb_ob_inside(object *op);
00077 static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger);
00078 static object *attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches, int ignore_cauldron);
00079 static int calc_alch_danger(object *caster, object *cauldron, recipe *rp);
00080 static object *make_item_from_recipe(object *cauldron, recipe *rp);
00081 static void remove_contents(object *first_ob, object *save_item);
00082 static void adjust_product(object *item, int lvl, int yield);
00083 static object *find_transmution_ob(object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item);
00084 static void attempt_do_alchemy(object *caster, object *cauldron);
00085
00087 static const char *cauldron_sound(void) {
00088 int size = sizeof(cauldron_effect)/sizeof(char *);
00089
00090 return cauldron_effect[rndm(0, size-1)];
00091 }
00092
00121 static void attempt_do_alchemy(object *caster, object *cauldron) {
00122 recipelist *fl;
00123 recipe *rp = NULL;
00124 float success_chance;
00125 int numb, ability = 1;
00126 int formula = 0;
00127 float ave_chance;
00128 object *item, *skop;
00129
00130 if (caster->type != PLAYER)
00131 return;
00132
00133
00134 if (!(formula = content_recipe_value(cauldron))) {
00135 draw_ext_info_format(NDI_UNIQUE, 0, caster, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00136 "The %s is empty.",
00137 "The %s is empty.",
00138 cauldron->name);
00139 return;
00140 }
00141
00142 numb = numb_ob_inside(cauldron);
00143 if ((fl = get_formulalist(numb))) {
00144 if (QUERY_FLAG(caster, FLAG_WIZ)) {
00145 rp = find_recipe(fl, formula, cauldron->inv);
00146 if (rp != NULL) {
00147 #ifdef ALCHEMY_DEBUG
00148 if (strcmp(rp->title, "NONE"))
00149 LOG(llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], rp->title);
00150 else
00151 LOG(llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula/rp->index);
00152 #endif
00153 attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, !is_defined_recipe(rp, cauldron, caster));
00154 } else
00155 LOG(llevDebug, "WIZ couldn't find formula for ingredients.\n");
00156 return;
00157 }
00158
00159
00160 rp = find_recipe(fl, formula, cauldron->inv);
00161 if (rp != NULL) {
00162 uint64 value_ingredients;
00163 uint64 value_item;
00164 object *tmp;
00165 int attempt_shadow_alchemy;
00166
00167 ave_chance = fl->total_chance/(float)fl->number;
00168
00169 if (rp->skill != NULL) {
00170 skop = find_skill_by_name(caster, rp->skill);
00171 if (!skop) {
00172 draw_ext_info(NDI_UNIQUE, 0, caster, MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
00173 "You do not have the proper skill for this recipe", NULL);
00174 } else {
00175 ability += skop->level*((4.0+cauldron->magic)/4.0);
00176 }
00177 } else {
00178 LOG(llevDebug, "Recipe %s has NULL skill!\n", rp->title);
00179 return;
00180 }
00181
00182 if (rp->cauldron == NULL) {
00183 LOG(llevDebug, "Recipe %s has NULL cauldron!\n", rp->title);
00184 return;
00185 }
00186
00187
00188 value_ingredients = 0;
00189 for (tmp = cauldron->inv; tmp != NULL; tmp = tmp->below)
00190 value_ingredients += query_cost(tmp, NULL, F_TRUE);
00191
00192 attempt_shadow_alchemy = !is_defined_recipe(rp, cauldron, caster);
00193
00194
00195 if ((item = attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, attempt_shadow_alchemy)) != NULL) {
00196
00197 success_chance = ((float)ability/(float)(rp->diff*(item->level+2)));
00198 if (ave_chance == 0)
00199 ave_chance = 1;
00200
00201 #ifdef ALCHEMY_DEBUG
00202 LOG(llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level);
00203 #endif
00204
00205 value_item = query_cost(item, NULL, F_TRUE|F_IDENTIFIED|F_NOT_CURSED);
00206 if (attempt_shadow_alchemy && value_item > value_ingredients) {
00207 #ifdef ALCHEMY_DEBUG
00208 #ifndef WIN32
00209 LOG(llevDebug, "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", value_ingredients, value_item);
00210 #else
00211 LOG(llevDebug, "Forcing failure for shadow alchemy recipe because price of ingredients (%I64d) is less than price of result (%I64d).\n", value_ingredients, value_item);
00212 #endif
00213 #endif
00214
00215 } else if ((float)(random_roll(0, 101, caster, PREFER_LOW)) <= 100.0*success_chance) {
00216 change_exp(caster, rp->exp, rp->skill, SK_EXP_NONE);
00217 return;
00218 }
00219 }
00220 }
00221 }
00222
00223 alchemy_failure_effect(caster, cauldron, rp, calc_alch_danger(caster, cauldron, rp));
00224 }
00225
00236 static int content_recipe_value(object *op) {
00237 char name[MAX_BUF];
00238 object *tmp = op->inv;
00239 int tval = 0, formula = 0;
00240
00241 while (tmp) {
00242 tval = 0;
00243 strcpy(name, tmp->name);
00244 if (tmp->title)
00245 snprintf(name, sizeof(name), "%s %s", tmp->name, tmp->title);
00246 tval = (strtoint(name)*(tmp->nrof ? tmp->nrof : 1));
00247 #ifdef ALCHEMY_DEBUG
00248 LOG(llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval);
00249 #endif
00250 formula += tval;
00251 tmp = tmp->below;
00252 }
00253 #ifdef ALCHEMY_DEBUG
00254 LOG(llevDebug, " Formula value=%d\n", formula);
00255 #endif
00256 return formula;
00257 }
00258
00266 static int numb_ob_inside(object *op) {
00267 object *tmp = op->inv;
00268 int o_number = 0;
00269
00270 while (tmp) {
00271 o_number++;
00272 tmp = tmp->below;
00273 }
00274 #ifdef ALCHEMY_DEBUG
00275 LOG(llevDebug, "numb_ob_inside(%s): found %d ingredients\n", op->name, o_number);
00276 #endif
00277 return o_number;
00278 }
00279
00305 static object *attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches, int ignore_cauldron) {
00306 object *item = NULL, *skop;
00307
00308 int batches = abs(nbatches);
00309
00310
00311 if (!ignore_cauldron && (strcmp(rp->cauldron, cauldron->arch->name) != 0)) {
00312 draw_ext_info(NDI_UNIQUE, 0, caster, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00313 "You are not using the proper facilities for this formula.", NULL);
00314 return NULL;
00315 }
00316
00317 skop = find_skill_by_name(caster, rp->skill);
00318
00319 if (!skop)
00320 return NULL;
00321
00322
00323 if (rp->keycode) {
00324 object *tmp;
00325
00326 for (tmp = caster->inv; tmp != NULL; tmp = tmp->below) {
00327 if (tmp->type == FORCE
00328 && tmp->slaying
00329 && !strcmp(rp->keycode, tmp->slaying))
00330 break;
00331 }
00332 if (tmp == NULL) {
00333 draw_ext_info(NDI_UNIQUE, 0, caster, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
00334 "You know the ingredients, but not the technique. Go learn how to do this recipe.",
00335 NULL);
00336 return NULL;
00337 }
00338 }
00339
00340 #ifdef EXTREME_ALCHEMY_DEBUG
00341 LOG(llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
00342 LOG(llevDebug, "attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown");
00343 #endif
00344
00345 if ((item = make_item_from_recipe(cauldron, rp)) != NULL) {
00346 remove_contents(cauldron->inv, item);
00347
00348 adjust_product(item, ability, rp->yield ? (rp->yield*batches) : batches);
00349 if (!item->env && (item = insert_ob_in_ob(item, cauldron)) == NULL) {
00350 draw_ext_info(NDI_UNIQUE, 0, caster, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00351 "Nothing happened.", NULL);
00352 } else {
00353 draw_ext_info_format(NDI_UNIQUE, 0, caster,
00354 MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00355 "The %s %s.",
00356 "The %s %s.",
00357 cauldron->name, cauldron_sound());
00358 }
00359 }
00360 return item;
00361 }
00362
00373 static void adjust_product(object *item, int lvl, int yield) {
00374 int nrof = 1;
00375
00376 if (!yield)
00377 yield = 1;
00378 if (lvl <= 0)
00379 lvl = 1;
00380 if (item->nrof) {
00381 nrof = (1.0-1.0/(lvl/10.0+1.0))*(rndm(0, yield-1)+rndm(0, yield-1)+rndm(0, yield-1))+1;
00382 if (nrof > yield)
00383 nrof = yield;
00384 item->nrof = nrof;
00385 }
00386 }
00387
00398 static object *make_item_from_recipe(object *cauldron, recipe *rp) {
00399 artifact *art = NULL;
00400 object *item = NULL;
00401 size_t rp_arch_index;
00402
00403 if (rp == NULL)
00404 return (object *)NULL;
00405
00406
00407 if ((item = find_transmution_ob(cauldron->inv, rp, &rp_arch_index, 1)) == NULL) {
00408 LOG(llevDebug, "make_alchemy_item(): failed to create alchemical object.\n");
00409 return (object *)NULL;
00410 }
00411
00412
00413 if (item->env != NULL)
00414 sub_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
00415
00416
00417 if (strcmp(rp->title, "NONE")) {
00418 if ((art = locate_recipe_artifact(rp, rp_arch_index)) == NULL) {
00419 LOG(llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
00420 LOG(llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], rp->title);
00421 return (object *)NULL;
00422 }
00423 transmute_materialname(item, art->item);
00424 give_artifact_abilities(item, art->item);
00425 }
00426 if (item->env != NULL)
00427 add_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
00428
00429 if (QUERY_FLAG(cauldron, FLAG_CURSED))
00430 SET_FLAG(item, FLAG_CURSED);
00431 if (QUERY_FLAG(cauldron, FLAG_DAMNED))
00432 SET_FLAG(item, FLAG_DAMNED);
00433
00434 return item;
00435 }
00436
00450 static object *find_transmution_ob(object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) {
00451 object *item = NULL;
00452
00453 *rp_arch_index = 0;
00454
00455 if (rp->transmute)
00456 for (item = first_ingred; item; item = item->below) {
00457 size_t i;
00458
00459 for (i = 0; i < rp->arch_names; i++) {
00460 if (strcmp(item->arch->name, rp->arch_name[i]) == 0) {
00461 *rp_arch_index = i;
00462 break;
00463 }
00464 }
00465 if (i < rp->arch_names)
00466 break;
00467 }
00468
00469
00470
00471 if (create_item && (!item || item->nrof > 1)) {
00472 *rp_arch_index = RANDOM()%rp->arch_names;
00473 item = create_archetype(rp->arch_name[*rp_arch_index]);
00474 }
00475
00476 #ifdef ALCHEMY_DEBUG
00477 LOG(llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
00478 if (item != NULL) {
00479 LOG(llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp);
00480 }
00481 #endif
00482
00483 return item;
00484 }
00485
00502 static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger) {
00503 int level = 0;
00504
00505 if (!op || !cauldron)
00506 return;
00507
00508 if (danger > 1)
00509 level = random_roll(1, danger, op, PREFER_LOW);
00510
00511 #ifdef ALCHEMY_DEBUG
00512 LOG(llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
00513 #endif
00514
00515
00516 if (level < 25) {
00517 object *item = NULL;
00518
00519 if (rndm(0, 2)) {
00520 object *tmp = cauldron->inv;
00521 int weight = 0;
00522 uint16 material = M_STONE;
00523
00524 while (tmp) {
00525 weight += tmp->weight;
00526 if (!(material&tmp->material))
00527 material |= tmp->material;
00528 tmp = tmp->below;
00529 }
00530 tmp = create_archetype("rock");
00531 tmp->weight = weight;
00532 tmp->value = 0;
00533 tmp->material = material;
00534 tmp->materialname = add_string("stone");
00535 free_string(tmp->name);
00536 tmp->name = add_string("slag");
00537 if (tmp->name_pl)
00538 free_string(tmp->name_pl);
00539 tmp->name_pl = add_string("slags");
00540 item = insert_ob_in_ob(tmp, cauldron);
00541 CLEAR_FLAG(tmp, FLAG_CAN_ROLL);
00542 CLEAR_FLAG(tmp, FLAG_NO_PICK);
00543 tmp->move_block = 0;
00544 }
00545 remove_contents(cauldron->inv, item);
00546 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00547 "The %s %s.",
00548 "The %s %s.",
00549 cauldron->name, cauldron_sound());
00550 return;
00551 } else if (level < 40) {
00552 object *tmp = NULL;
00553
00554 if (!rp)
00555 if ((rp = get_random_recipe((recipelist *)NULL)) == NULL)
00556 return;
00557
00558 if ((tmp = attempt_recipe(op, cauldron, 1, rp, -1, 0))) {
00559 if (!QUERY_FLAG(tmp, FLAG_CURSED)) {
00560 SET_FLAG(tmp, FLAG_CURSED);
00561 CLEAR_FLAG(tmp, FLAG_KNOWN_CURSED);
00562 CLEAR_FLAG(tmp, FLAG_IDENTIFIED);
00563 }
00564
00565
00566
00567
00568 if (tmp->type == FOOD) {
00569 tmp->stats.hp = random_roll(0, 149, op, PREFER_LOW);
00570 }
00571 tmp->value = 0;
00572
00573
00574 do {
00575 change_attr_value(&tmp->stats, rndm(0, 6), -1*(rndm(1, 3)));
00576 } while (rndm(0, 2));
00577 }
00578 return;
00579 }
00580 if (level == 40) {
00581 recipelist *fl;
00582 int numb = numb_ob_inside(cauldron);
00583
00584 fl = get_formulalist(numb-1);
00585 if (fl &&(rp = get_random_recipe(fl)))
00586
00587 (void)attempt_recipe(op, cauldron, 1, rp, -1, 0);
00588 else
00589 alchemy_failure_effect(op, cauldron, rp, level-1);
00590 return;
00591
00592 } else if (level < 45) {
00593
00594 cauldron->enemy = op;
00595 npc_call_help(cauldron);
00596 cauldron->enemy = NULL;
00597
00598 alchemy_failure_effect(op, cauldron, rp, level-5);
00599 return;
00600 } else if (level < 50) {
00601 object *tmp;
00602
00603 remove_contents(cauldron->inv, NULL);
00604 switch (rndm(0, 2)) {
00605 case 0:
00606 tmp = create_archetype("bomb");
00607 tmp->stats.dam = random_roll(1, level, op, PREFER_LOW);
00608 tmp->stats.hp = random_roll(1, level, op, PREFER_LOW);
00609 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00610 "The %s creates a bomb!",
00611 "The %s creates a bomb!",
00612 cauldron->name);
00613 break;
00614
00615 default:
00616 tmp = create_archetype("fireball");
00617 tmp->stats.dam = random_roll(1, level, op, PREFER_LOW)/5+1;
00618 tmp->stats.hp = random_roll(1, level, op, PREFER_LOW)/10+2;
00619 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00620 "The %s erupts in flame!",
00621 "The %s erupts in flame!",
00622 cauldron->name);
00623 break;
00624 }
00625 tmp->x = cauldron->x,
00626 tmp->y = cauldron->y;
00627 insert_ob_in_map(tmp, op->map, NULL, 0);
00628 return;
00629 } else if (level < 60) {
00630 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00631 "The %s %s.", NULL, cauldron->name, cauldron_sound());
00632 remove_contents(cauldron->inv, NULL);
00633 return;
00634 } else if (level < 80) {
00635 object *fb = create_archetype(SP_MED_FIREBALL);
00636
00637 remove_contents(cauldron->inv, NULL);
00638 fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, fb);
00639 free_object(fb);
00640 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00641 "The %s erupts in flame!",
00642 "The %s erupts in flame!",
00643 cauldron->name);
00644 return;
00645 } else if (level < 100) {
00646 if (!QUERY_FLAG(cauldron, FLAG_CURSED)) {
00647 SET_FLAG(cauldron, FLAG_CURSED);
00648 CLEAR_FLAG(cauldron, FLAG_KNOWN_CURSED);
00649 CLEAR_FLAG(cauldron, FLAG_IDENTIFIED);
00650 } else
00651 cauldron->magic--;
00652 cauldron->magic -= random_roll(0, 4, op, PREFER_LOW);
00653 if (rndm(0, 1)) {
00654 remove_contents(cauldron->inv, NULL);
00655 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00656 "Your %s turns darker then makes a gulping sound!",
00657 "Your %s turns darker then makes a gulping sound!",
00658 cauldron->name);
00659 } else
00660 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00661 "Your %s becomes darker.",
00662 "Your %s becomes darker.",
00663 cauldron->name);
00664 return;
00665
00666 } else if (level < 110) {
00667 object *tmp = get_random_mon(level/5);
00668
00669 remove_contents(cauldron->inv, NULL);
00670 if (!tmp)
00671 alchemy_failure_effect(op, cauldron, rp, level);
00672 else if (summon_hostile_monsters(cauldron, random_roll(1, 10, op, PREFER_LOW), tmp->arch->name))
00673 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00674 "The %s %s and then pours forth monsters!",
00675 "The %s %s and then pours forth monsters!",
00676 cauldron->name, cauldron_sound());
00677 return;
00678 } else if (level < 150) {
00679 int roll = rndm(1, 3);
00680 while (roll) {
00681 alchemy_failure_effect(op, cauldron, rp, level-39);
00682 roll--;
00683 }
00684 return;
00685 } else if (level == 151) {
00686 object *tmp;
00687
00688
00689
00690
00691
00692 if (!rp)
00693 rp = get_random_recipe((recipelist *)NULL);
00694 if (rp && (tmp = create_archetype(rp->arch_name[RANDOM()%rp->arch_names]))) {
00695 generate_artifact(tmp, random_roll(1, op->level/2+1, op, PREFER_HIGH)+1);
00696 if ((tmp = insert_ob_in_ob(tmp, cauldron))) {
00697 remove_contents(cauldron->inv, tmp);
00698 draw_ext_info_format(NDI_UNIQUE, 0, op,
00699 MSG_TYPE_SKILL, MSG_TYPE_SKILL_SUCCESS,
00700 "The %s %s.",
00701 "The %s %s.",
00702 cauldron->name, cauldron_sound());
00703 }
00704 }
00705 return;
00706 } else {
00707 object *tmp = create_archetype(LOOSE_MANA);
00708 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
00709 "You unwisely release potent forces!", NULL);
00710 remove_contents(cauldron->inv, NULL);
00711 cast_magic_storm(op, tmp, level);
00712 return;
00713 }
00714 }
00715
00725 static void remove_contents(object *first_ob, object *save_item) {
00726 object *next, *tmp = first_ob;
00727
00728 while (tmp) {
00729 next = tmp->below;
00730 if (tmp == save_item) {
00731 if (!(tmp = next))
00732 break;
00733 else
00734 next = next->below;
00735 }
00736 if (tmp->inv)
00737 remove_contents(tmp->inv, NULL);
00738 remove_ob(tmp);
00739 free_object(tmp);
00740 tmp = next;
00741 }
00742 }
00743
00763 static int calc_alch_danger(object *caster, object *cauldron, recipe *rp) {
00764 object *item;
00765 char name[MAX_BUF];
00766 int danger = 0, nrofi = 0;
00767
00768
00769 danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
00770
00771
00772 danger -= cauldron->magic;
00773
00774
00775 danger -= 3*(caster->stats.Int-15);
00776
00777
00778
00779
00780
00781 for (item = cauldron->inv; item; item = item->below) {
00782 strcpy(name, item->name);
00783 if (item->title)
00784 snprintf(name, sizeof(name), "%s %s", item->name, item->title);
00785 danger += (strtoint(name)/1000)+3;
00786 nrofi++;
00787 }
00788 if (rp == NULL)
00789 danger += 110;
00790 else
00791 danger += rp->diff*3;
00792
00793
00794 if (QUERY_FLAG(cauldron, FLAG_CURSED))
00795 danger += 80;
00796 if (QUERY_FLAG(cauldron, FLAG_DAMNED))
00797 danger += 200;
00798
00799 #ifdef ALCHEMY_DEBUG
00800 LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
00801 #endif
00802
00803 return danger;
00804 }
00805
00825 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster) {
00826 uint32 batches_in_cauldron;
00827 const linked_char *ingredient;
00828 int number;
00829 const object *ob;
00830
00831
00832 number = 0;
00833 for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
00834 number++;
00835 for (ob = cauldron->inv; ob != NULL; ob = ob->below)
00836 number--;
00837 if (number != 0)
00838 return 0;
00839
00840
00841 batches_in_cauldron = 0;
00842 for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next) {
00843 uint32 nrof;
00844 const char *name;
00845 int ok;
00846
00847
00848 name = ingredient->name;
00849 nrof = 0;
00850 while (isdigit(*name)) {
00851 nrof = 10*nrof+(*name-'0');
00852 name++;
00853 }
00854 if (nrof == 0)
00855 nrof = 1;
00856 while (*name == ' ')
00857 name++;
00858
00859
00860 ok = 0;
00861 for (ob = cauldron->inv; ob != NULL; ob = ob->below) {
00862 char name_ob[MAX_BUF];
00863 const char *name2;
00864
00865 if (ob->title == NULL)
00866 name2 = ob->name;
00867 else {
00868 snprintf(name_ob, sizeof(name_ob), "%s %s", ob->name, ob->title);
00869 name2 = name_ob;
00870 }
00871
00872 if (strcmp(name2, name) == 0) {
00873 if (ob->nrof%nrof == 0) {
00874 uint32 batches;
00875
00876 batches = ob->nrof/nrof;
00877 if (batches_in_cauldron == 0) {
00878 batches_in_cauldron = batches;
00879 ok = 1;
00880 } else if (batches_in_cauldron == batches)
00881 ok = 1;
00882 }
00883 break;
00884 }
00885 }
00886 if (!ok)
00887 return(0);
00888 }
00889
00890 return(1);
00891 }
00892
00909 static recipe *find_recipe(recipelist *fl, int formula, object *ingredients) {
00910 recipe *rp;
00911 recipe *result;
00912 int recipes_matching;
00913 int transmute_found;
00914 size_t rp_arch_index;
00915
00916 #ifdef EXTREME_ALCHEMY_DEBUG
00917 LOG(llevDebug, "looking for formula %d:\n", formula);
00918 #endif
00919 result = NULL;
00920 recipes_matching = 0;
00921 transmute_found = 0;
00922 for (rp = fl->items; rp != NULL; rp = rp->next) {
00923
00924 if (formula%rp->index != 0) {
00925 #ifdef EXTREME_ALCHEMY_DEBUG
00926 LOG(llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index);
00927 #endif
00928 continue;
00929 }
00930
00931 if (rp->transmute && find_transmution_ob(ingredients, rp, &rp_arch_index, 0) != NULL) {
00932 #ifdef EXTREME_ALCHEMY_DEBUG
00933 LOG(llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index);
00934 #endif
00935
00936 if (!transmute_found) {
00937 transmute_found = 1;
00938 recipes_matching = 0;
00939 }
00940 } else if (transmute_found) {
00941 #ifdef EXTREME_ALCHEMY_DEBUG
00942 LOG(llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, rp->index);
00943 #endif
00944
00945 continue;
00946 }
00947 #ifdef EXTREME_ALCHEMY_DEBUG
00948 else {
00949 LOG(llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index);
00950 }
00951 #endif
00952
00953 if (rndm(0, recipes_matching) == 0)
00954 result = rp;
00955
00956 recipes_matching++;
00957 }
00958
00959 if (result == NULL) {
00960 #ifdef ALCHEMY_DEBUG
00961 LOG(llevDebug, "couldn't find formula for ingredients.\n");
00962 #endif
00963 return NULL;
00964 }
00965
00966 #ifdef ALCHEMY_DEBUG
00967 if (strcmp(result->title, "NONE") != 0)
00968 LOG(llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula/result->index);
00969 else
00970 LOG(llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula/result->index);
00971 #endif
00972 return result;
00973 }
00974
00986 int use_alchemy(object *op) {
00987 object *tmp, *item, *next;
00988 object *unpaid_cauldron = NULL;
00989 object *unpaid_item = NULL;
00990 int did_alchemy = 0;
00991 char name[MAX_BUF];
00992
00993 for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = next) {
00994 next = tmp->above;
00995 if (QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) {
00996 if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
00997 unpaid_cauldron = tmp;
00998 continue;
00999 }
01000 unpaid_item = NULL;
01001 for (item = tmp->inv; item; item = item->below) {
01002 if (QUERY_FLAG(item, FLAG_UNPAID)) {
01003 unpaid_item = item;
01004 break;
01005 }
01006 }
01007 if (unpaid_item != NULL)
01008 continue;
01009
01010 attempt_do_alchemy(op, tmp);
01011 if (QUERY_FLAG(tmp, FLAG_APPLIED))
01012 esrv_send_inventory(op, tmp);
01013 did_alchemy = 1;
01014 }
01015 }
01016 if (unpaid_cauldron) {
01017 query_base_name(unpaid_cauldron, 0, name, MAX_BUF);
01018 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01019 "You must pay for your %s first!",
01020 "You must pay for your %s first!",
01021 name);
01022 } else if (unpaid_item) {
01023 query_base_name(unpaid_item, 0, name, MAX_BUF);
01024 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_ERROR,
01025 "You must pay for your %s first!",
01026 "You must pay for your %s first!",
01027 name);
01028 }
01029
01030 return did_alchemy;
01031 }