Atrinik Server 2.5
server/alchemy.c
Go to the documentation of this file.
00001 /************************************************************************
00002 *            Atrinik, a Multiplayer Online Role Playing Game            *
00003 *                                                                       *
00004 *    Copyright (C) 2009-2011 Alex Tokar and Atrinik Development Team    *
00005 *                                                                       *
00006 * Fork from Daimonin (Massive Multiplayer Online Role Playing Game)     *
00007 * and Crossfire (Multiplayer game for X-windows).                       *
00008 *                                                                       *
00009 * This program is free software; you can redistribute it and/or modify  *
00010 * it under the terms of the GNU General Public License as published by  *
00011 * the Free Software Foundation; either version 2 of the License, or     *
00012 * (at your option) any later version.                                   *
00013 *                                                                       *
00014 * This program is distributed in the hope that it will be useful,       *
00015 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00017 * GNU General Public License for more details.                          *
00018 *                                                                       *
00019 * You should have received a copy of the GNU General Public License     *
00020 * along with this program; if not, write to the Free Software           *
00021 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.             *
00022 *                                                                       *
00023 * The author can be reached at admin@atrinik.org                        *
00024 ************************************************************************/
00025 
00030 #include <global.h>
00031 
00033 #define ALCHEMY_DEBUG
00034 
00036 #define EXTREME_ALCHEMY_DEBUG
00037 
00039 static char *cauldron_effect[] =
00040 {
00041     "vibrates briefly",
00042     "produces a cloud of steam",
00043     "emits bright flames",
00044     "pours forth heavy black smoke",
00045     "emits sparks",
00046     "shoots out small flames",
00047     "whines painfully",
00048     "hiccups loudly",
00049     "wheezes",
00050     "burps",
00051     "shakes",
00052     "rattles",
00053     "makes chugging sounds",
00054     "smokes heavily for a while"
00055 };
00056 
00057 static const char *cauldron_sound();
00058 static int content_recipe_value(object *op);
00059 static int numb_ob_inside(object *op);
00060 static void adjust_product(object *item, int lvl, int yield);
00061 static object *make_item_from_recipe(object *cauldron, recipe *rp);
00062 static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger);
00063 static void remove_contents(object *first_ob, object *save_item);
00064 static int calc_alch_danger(object *caster, object *cauldron);
00065 static object *find_transmution_ob(object *first_ingred, recipe *rp);
00066 static object *attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches);
00067 
00070 static const char *cauldron_sound()
00071 {
00072     int size = sizeof(cauldron_effect) / sizeof(char *);
00073 
00074     return cauldron_effect[rndm(1, size) - 1];
00075 }
00076 
00102 static void attempt_do_alchemy(object *caster, object *cauldron)
00103 {
00104     recipelist *fl;
00105     recipe *rp = NULL;
00106     float success_chance;
00107     int numb, ability = 1;
00108     int formula = 0;
00109 
00110     /* Only players for now */
00111     if (caster->type != PLAYER)
00112     {
00113         return;
00114     }
00115 
00116     /* If no ingredients, no formula! Let's forget it */
00117     if (!(formula = content_recipe_value(cauldron)))
00118     {
00119         return;
00120     }
00121 
00122     numb = numb_ob_inside(cauldron);
00123 
00124     if ((fl = get_formulalist(numb)))
00125     {
00126         /* The caster only gets an increase in ability if they know
00127          * alchemy skill */
00128         if (find_skill(caster, SK_ALCHEMY) != NULL)
00129         {
00130             change_skill(caster, SK_ALCHEMY);
00131             ability += (int) (SK_level(caster) * (float) ((float) (4 + cauldron->magic) / 4.0f));
00132         }
00133 
00134 #ifdef ALCHEMY_DEBUG
00135         LOG(llevDebug, "Got alchemy ability lvl = %d\n", ability);
00136 #endif
00137 
00138         if (QUERY_FLAG(caster, FLAG_WIZ))
00139         {
00140             rp = fl->items;
00141 
00142             while (rp && (formula % rp->index) != 0)
00143             {
00144 #ifdef EXTREME_ALCHEMY_DEBUG
00145                 LOG(llevDebug, "found list %d formula: %s of %s (%d)\n", numb, rp->arch_name, rp->title, rp->index);
00146 #endif
00147                 rp = rp->next;
00148             }
00149 
00150             if (rp)
00151             {
00152 #ifdef ALCHEMY_DEBUG
00153                 if (rp->title != shstr_cons.NONE)
00154                 {
00155                     LOG(llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name, rp->title);
00156                 }
00157                 else
00158                 {
00159                     LOG(llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name, formula / rp->index);
00160                 }
00161 #endif
00162                 attempt_recipe(caster, cauldron, ability, rp, formula / rp->index);
00163             }
00164             else
00165             {
00166                 LOG(llevDebug, "WIZ couldn't find formula for ingredients.\n");
00167             }
00168 
00169             return;
00170         }
00171 
00172         /* Find the recipe */
00173         for (rp = fl->items; rp != NULL && (formula % rp->index) != 0; rp = rp->next)
00174         {
00175         }
00176 
00177         /* If we found a recipe */
00178         if (rp)
00179         {
00180             float ave_chance = fl->total_chance / (float) fl->number;
00181             object *item;
00182 
00183             /* Create the object **FIRST**, then decide whether to keep it. */
00184             if ((item = attempt_recipe(caster, cauldron, ability, rp, formula / rp->index)) != NULL)
00185             {
00186                 /* Compute base chance of recipe success */
00187                 success_chance = ((float) (15 * ability) / (float) (15 * ability + numb * item->level * (numb + item->level + formula / rp->index)));
00188 
00189                 if (ave_chance == 0)
00190                 {
00191                     ave_chance = 1;
00192                 }
00193 
00194                 /* Adjust the success chance by the chance from the recipe list */
00195                 if (ave_chance > rp->chance)
00196                 {
00197                     success_chance *= ((float) rp->chance + ave_chance) / (2.0f * ave_chance);
00198                 }
00199                 else
00200                 {
00201                     success_chance = 1.0f - ((1.0f - success_chance) * ((float) rp->chance + ave_chance) / (2.0f * (float) rp->chance));
00202                 }
00203 
00204 #ifdef ALCHEMY_DEBUG
00205                 LOG(llevDebug, "percent success chance =  %f\n", success_chance);
00206 #endif
00207 
00208                 /* Roll the dice */
00209                 if (rndm(0, 99) <= 100.0 * success_chance)
00210                 {
00211                     /* We learn from our experience IF we know something of the alchemical arts */
00212                     if (caster->chosen_skill && caster->chosen_skill->stats.sp == SK_ALCHEMY)
00213                     {
00214                         /* More exp is given for higher ingred number recipes */
00215                         sint64 amount = numb * numb * calc_skill_exp(caster, item, -1);
00216                         add_exp(caster, amount, SK_ALCHEMY, 0);
00217                         /* So when skill id this item, less xp is awarded */
00218                         item->stats.exp = 0;
00219 #ifdef EXTREME_ALCHEMY_DEBUG
00220                         LOG(llevDebug, "%s gains %"FMT64" experience points.\n", caster->name, amount);
00221 #endif
00222                     }
00223 
00224                     return;
00225                 }
00226             }
00227         }
00228     }
00229 
00230     /* If we get here, we failed! */
00231     alchemy_failure_effect(caster, cauldron, rp, calc_alch_danger(caster, cauldron));
00232 }
00233 
00242 static int content_recipe_value(object *op)
00243 {
00244     char name[MAX_BUF];
00245     object *tmp = op->inv;
00246     int tval = 0, formula = 0;
00247 
00248     while (tmp)
00249     {
00250         tval = 0;
00251         strcpy(name, tmp->name);
00252 
00253         if (tmp->title)
00254         {
00255             snprintf(name, sizeof(name), "%s %s", tmp->name, tmp->title);
00256         }
00257 
00258         tval = (strtoint(name) * (tmp->nrof ? tmp->nrof : 1));
00259 
00260 #ifdef ALCHEMY_DEBUG
00261         LOG(llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval);
00262 #endif
00263 
00264         formula += tval;
00265         tmp = tmp->below;
00266     }
00267 
00268 #ifdef ALCHEMY_DEBUG
00269     LOG(llevDebug, "Formula value=%d\n", formula);
00270 #endif
00271 
00272     return formula;
00273 }
00274 
00280 static int numb_ob_inside(object *op)
00281 {
00282     object *tmp = op->inv;
00283     int o_number = 0;
00284 
00285     while (tmp)
00286     {
00287         o_number++;
00288         tmp = tmp->below;
00289     }
00290 
00291 #ifdef ALCHEMY_DEBUG
00292     LOG(llevDebug, "numb_ob_inside(%s): found %d ingredients\n", op->name, o_number);
00293 #endif
00294 
00295     return o_number;
00296 }
00297 
00310 static object *attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches)
00311 {
00312     object *item = NULL;
00313     /* This should be passed to this function, not too efficient CPU use this way */
00314     int batches = abs(nbatches);
00315 
00316     /* Code required for this recipe, search the caster */
00317     if (rp->keycode)
00318     {
00319         object *tmp;
00320 
00321         for (tmp = caster->inv; tmp != NULL; tmp = tmp->below)
00322         {
00323             if (tmp->type == FORCE && tmp->slaying && !strcmp(rp->keycode, tmp->slaying))
00324             {
00325                 break;
00326             }
00327         }
00328 
00329         /* Failure - no code found */
00330         if (tmp == NULL)
00331         {
00332             new_draw_info(0, COLOR_WHITE, caster, "You know the ingredients, but not the technique. Go learn how to do this recipe.");
00333             return NULL;
00334         }
00335     }
00336 
00337 #ifdef EXTREME_ALCHEMY_DEBUG
00338     LOG(llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
00339     LOG(llevDebug, "attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown");
00340 #endif
00341 
00342     if ((item = make_item_from_recipe(cauldron, rp)) != NULL)
00343     {
00344         remove_contents(cauldron->inv, item);
00345         /* adj lvl, nrof on caster level */
00346         adjust_product(item, ability, rp->yield ? (rp->yield * batches) : batches);
00347 
00348         if (!item->env && (item = insert_ob_in_ob(item, cauldron)) == NULL)
00349         {
00350             new_draw_info(0, COLOR_WHITE, caster, "Nothing happened.");
00351         }
00352         else
00353         {
00354             new_draw_info_format(0, COLOR_WHITE, caster, "The %s %s.", cauldron->name, cauldron_sound());
00355         }
00356     }
00357 
00358     return item;
00359 }
00360 
00367 static void adjust_product(object *item, int lvl, int yield)
00368 {
00369     int nrof = 1;
00370 
00371     if (!yield)
00372     {
00373         yield = 1;
00374     }
00375 
00376     /* Avoid division by zero */
00377     if (lvl <= 0)
00378     {
00379         lvl = 1;
00380     }
00381 
00382     if (item->nrof)
00383     {
00384         nrof = (int) ((1.0f - 1.0f / ((float) lvl / 10.0f + 1.0f)) * (float) (rndm(0, yield - 1) + rndm(0, yield - 1) + rndm(0, yield - 1)) + 1.0f);
00385 
00386         if (nrof > yield)
00387         {
00388             nrof = yield;
00389         }
00390 
00391         item->nrof = nrof;
00392     }
00393 
00394     /* Item exp. This will be used later for experience calculation */
00395     item->stats.exp += lvl * lvl * nrof;
00396 
00397 #if 0
00398     /* avg between default and caster levels */
00399     item->level = (lvl + item->level) / 2;
00400 #endif
00401 }
00402 
00409 static object *make_item_from_recipe(object *cauldron, recipe *rp)
00410 {
00411     artifact *art = NULL;
00412     object *item = NULL;
00413 
00414     if (rp == NULL)
00415     {
00416         return NULL;
00417     }
00418 
00419     /* Find the appropriate object to transform...*/
00420     if ((item = find_transmution_ob(cauldron->inv, rp)) == NULL)
00421     {
00422         LOG(llevDebug, "make_alchemy_item(): Failed to create alchemical object.\n");
00423         return NULL;
00424     }
00425 
00426     /* Find the appropriate artifact template...*/
00427     if (rp->title != shstr_cons.NONE)
00428     {
00429         if ((art = locate_recipe_artifact(rp)) == NULL)
00430         {
00431             LOG(llevBug, "make_alchemy_item(): failed to locate recipe artifact.\n");
00432             LOG(llevBug, "--requested recipe: %s of %s.\n", rp->arch_name, rp->title);
00433             return NULL;
00434         }
00435 
00436         give_artifact_abilities(item, art);
00437     }
00438 
00439     if (QUERY_FLAG(cauldron, FLAG_CURSED))
00440     {
00441         SET_FLAG(item, FLAG_CURSED);
00442     }
00443 
00444     if (QUERY_FLAG(cauldron, FLAG_DAMNED))
00445     {
00446         SET_FLAG(item, FLAG_DAMNED);
00447     }
00448 
00449     return item;
00450 }
00451 
00459 static object *find_transmution_ob(object *first_ingred, recipe *rp)
00460 {
00461     object *item = NULL;
00462 
00463     /* Look for matching ingredient/prod archs */
00464     if (rp->transmute)
00465     {
00466         for (item = first_ingred; item; item = item->below)
00467         {
00468             if (!strcmp(item->arch->name, rp->arch_name))
00469             {
00470                 break;
00471             }
00472         }
00473     }
00474 
00475     /* Failed, create a fresh object. Note no nrof > 1 because that would
00476      * allow players to create massive amounts of artifacts easily. */
00477     if (!item || item->nrof > 1)
00478     {
00479         item = get_archetype(rp->arch_name);
00480     }
00481 
00482 #ifdef ALCHEMY_DEBUG
00483     LOG(llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
00484 
00485     if (strcmp(item->arch->name, rp->arch_name))
00486     {
00487         LOG(llevDebug, "recipe calls for arch: %s\n", rp->arch_name);
00488     }
00489 
00490     LOG(llevDebug, "find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp);
00491 #endif
00492 
00493     return item;
00494 }
00495 
00506 static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger)
00507 {
00508     int level = 0;
00509 
00510     if (!op || !cauldron)
00511     {
00512         return;
00513     }
00514 
00515     if (danger > 1)
00516     {
00517         level = rndm(1, danger);
00518     }
00519 
00520 #ifdef ALCHEMY_DEBUG
00521     LOG(llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
00522 #endif
00523 
00524     /* possible outcomes based on level */
00525 
00526     /* Ingredients destroyed, and possibly create slag. */
00527     if (level < 25)
00528     {
00529         object *item = NULL;
00530 
00531         /* Slag created */
00532         if (rndm(0, 2))
00533         {
00534             object *tmp = cauldron->inv;
00535             int weight = 0;
00536             uint16 material = M_STONE;
00537 
00538             /* Slag has coadded ingredient properties */
00539             while (tmp)
00540             {
00541                 weight += tmp->weight;
00542 
00543                 if (!(material & tmp->material))
00544                 {
00545                     material = material | tmp->material;
00546                 }
00547 
00548                 tmp = tmp->below;
00549             }
00550 
00551             tmp = get_archetype("rock");
00552             tmp->weight = weight;
00553             tmp->value = 0;
00554             tmp->material = material;
00555             FREE_AND_COPY_HASH(tmp->name, "slag");
00556             item = insert_ob_in_ob(tmp, cauldron);
00557             CLEAR_FLAG(tmp, FLAG_CAN_ROLL);
00558             CLEAR_FLAG(tmp, FLAG_NO_PICK);
00559             CLEAR_FLAG(tmp, FLAG_NO_PASS);
00560         }
00561 
00562         remove_contents(cauldron->inv, item);
00563         new_draw_info_format(0, COLOR_WHITE, op, "The %s %s.", cauldron->name, cauldron_sound());
00564         return;
00565     }
00566     /* Make tained item. */
00567     else if (level < 40)
00568     {
00569         object *tmp = NULL;
00570 
00571         if (!rp)
00572         {
00573             if ((rp = get_random_recipe(NULL)) == NULL)
00574             {
00575                 return;
00576             }
00577         }
00578 
00579         if ((tmp = attempt_recipe(op, cauldron, 1, rp, -1)))
00580         {
00581             /* Curse it */
00582             if (!QUERY_FLAG(tmp, FLAG_CURSED))
00583             {
00584                 SET_FLAG(tmp, FLAG_CURSED);
00585             }
00586 
00587             /* Special stuff for consumables */
00588             if (tmp->type == FOOD)
00589             {
00590                 tmp->stats.sp = 0;
00591 
00592                 /* Poisonous */
00593                 if (rndm(0, 1))
00594                 {
00595                     tmp->type = FOOD;
00596                     tmp->stats.hp = rndm(0, 149);
00597                 }
00598             }
00599 
00600             /* Unsaleable item */
00601             tmp->value = 0;
00602 
00603             /* Change stats downward */
00604             do
00605             {
00606                 change_attr_value(&tmp->stats, rndm(0, 6), (signed char) (-1 * (rndm(1, 3))));
00607             }
00608             while (rndm(0, 2));
00609         }
00610 
00611         return;
00612     }
00613 
00614     /* Make random recipe. */
00615     if (level == 40)
00616     {
00617         recipelist *fl;
00618         int numb = numb_ob_inside(cauldron);
00619 
00620         /* Take a lower recipe list */
00621         fl = get_formulalist(numb - 1);
00622 
00623         if (fl && (rp = get_random_recipe(fl)))
00624         {
00625             /* Even though random, don't grant user any EXP for it */
00626             (void) attempt_recipe(op, cauldron, 1, rp, -1);
00627         }
00628         else
00629         {
00630             alchemy_failure_effect(op, cauldron, rp, level - 1);
00631         }
00632 
00633         return;
00634     }
00635     /* Infuriate NPCs */
00636     else if (level < 45)
00637     {
00638         alchemy_failure_effect(op, cauldron, rp, level - 5);
00639         return;
00640     }
00641     /* Minor explosion/fireball */
00642     else if (level < 50)
00643     {
00644         object *tmp;
00645 
00646         remove_contents(cauldron->inv, NULL);
00647 
00648         switch (rndm(0, 2))
00649         {
00650             case 0:
00651                 tmp = get_archetype("bomb");
00652                 tmp->stats.dam = rndm(1, level);
00653                 tmp->stats.hp = rndm(1, level);
00654                 new_draw_info_format(0, COLOR_WHITE, op, "The %s creates a bomb!", cauldron->name);
00655                 break;
00656 
00657             default:
00658                 tmp = get_archetype("fireball");
00659                 tmp->stats.dam = rndm(1, level) / 5 + 1;
00660                 tmp->stats.hp = rndm(1, level) / 10 + 2;
00661                 new_draw_info_format(0, COLOR_WHITE, op, "The %s erupts in flame!", cauldron->name);
00662                 break;
00663         }
00664 
00665         tmp->x = cauldron->x, tmp->y = cauldron->y;
00666         insert_ob_in_map(tmp, op->map, NULL, 0);
00667         return;
00668     }
00669     /* Create monster */
00670     else if (level < 60)
00671     {
00672         new_draw_info_format(0, COLOR_WHITE, op, "The %s %s.", cauldron->name, cauldron_sound());
00673         remove_contents(cauldron->inv, NULL);
00674         return;
00675     }
00676     /* Major fire */
00677     else if (level < 80)
00678     {
00679         remove_contents(cauldron->inv, NULL);
00680 #if 0
00681         fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, spellarch[SP_L_FIREBALL], SP_L_FIREBALL, NULL);
00682 #endif
00683         new_draw_info_format(0, COLOR_WHITE, op, "The %s erupts in flame!", cauldron->name);
00684         return;
00685     }
00686     /* Whammy the cauldron */
00687     else if (level < 100)
00688     {
00689         if (!QUERY_FLAG(cauldron, FLAG_CURSED))
00690         {
00691             SET_FLAG(cauldron, FLAG_CURSED);
00692         }
00693         else
00694         {
00695             cauldron->magic--;
00696         }
00697 
00698         cauldron->magic -= rndm(0, 4);
00699 
00700         if (rndm(0, 1))
00701         {
00702             remove_contents(cauldron->inv, NULL);
00703             new_draw_info_format(0, COLOR_WHITE, op, "Your %s turns darker then makes a gulping sound!", cauldron->name);
00704         }
00705         else
00706         {
00707             new_draw_info_format(0, COLOR_WHITE, op, "Your %s becomes darker.", cauldron->name);
00708         }
00709 
00710         return;
00711     }
00712     /* Summon evil monsters */
00713     else if (level < 110)
00714     {
00715         object *tmp = get_random_mon();
00716 
00717         remove_contents(cauldron->inv, NULL);
00718 
00719         if (!tmp)
00720         {
00721             alchemy_failure_effect(op, cauldron, rp, level);
00722         }
00723 
00724         return;
00725     }
00726     /* Combo effect */
00727     else if (level < 150)
00728     {
00729         int roll = rndm(1, 3);
00730 
00731         while (roll)
00732         {
00733             alchemy_failure_effect(op, cauldron, rp, level - 39);
00734             roll--;
00735         }
00736 
00737         return;
00738     }
00739     /* Create random artifact */
00740     else if (level == 151)
00741     {
00742         object *tmp;
00743 
00744         /* this is meant to be better than prior possibility,
00745          * in this one, we allow *any* valid alchemy artifact
00746          * to be made (rather than only those on the given
00747          * formulalist) */
00748         if (!rp)
00749         {
00750             rp = get_random_recipe((recipelist *) NULL);
00751         }
00752 
00753         if (rp && (tmp = get_archetype(rp->arch_name)))
00754         {
00755             generate_artifact(tmp, rndm(1, op->level / 2 + 1) + 1, 0, 99);
00756 
00757             if ((tmp = insert_ob_in_ob(tmp, cauldron)))
00758             {
00759                 remove_contents(cauldron->inv, tmp);
00760                 new_draw_info_format(0, COLOR_WHITE, op, "The %s %s.", cauldron->name, cauldron_sound());
00761             }
00762         }
00763 
00764         return;
00765     }
00766     /* Mana storm -- watch out! */
00767     else if (level < 200)
00768     {
00769         new_draw_info(0, COLOR_WHITE, op, "You unwisely release potent forces!");
00770         remove_contents(cauldron->inv, NULL);
00771         cast_magic_storm(op, get_archetype("loose_magic"), level);
00772         return;
00773     }
00774 }
00775 
00782 static void remove_contents(object *first_ob, object *save_item)
00783 {
00784     object *next, *tmp = first_ob;
00785 
00786     while (tmp)
00787     {
00788         next = tmp->below;
00789 
00790         if (tmp == save_item)
00791         {
00792             if (!(tmp = next))
00793             {
00794                 break;
00795             }
00796             else
00797             {
00798                 next = next->below;
00799             }
00800         }
00801 
00802         if (tmp->inv)
00803         {
00804             remove_contents(tmp->inv, NULL);
00805         }
00806 
00807         remove_ob(tmp);
00808         tmp = next;
00809     }
00810 }
00811 
00824 static int calc_alch_danger(object *caster, object *cauldron)
00825 {
00826     object *item;
00827     char name[MAX_BUF];
00828     int danger = 0, nrofi = 0;
00829 
00830     /* Knowing alchemy skill reduces yer risk */
00831     if (caster->chosen_skill && caster->chosen_skill->stats.sp == SK_ALCHEMY)
00832     {
00833         danger -= SK_level(caster);
00834     }
00835 
00836     /* Better cauldrons reduce risk */
00837     danger -= cauldron->magic;
00838 
00839     /* Higher Int, lower the risk */
00840     danger -= 3 * (caster->stats.Int - 15);
00841 
00842     /* Ingredients. Longer names usually mean rarer stuff. Thus the
00843      * backfire is worse. Also, more ingredients means we are attempting
00844      * a more powerful potion, and thus the backfire will be worse. */
00845     for (item = cauldron->inv; item; item = item->below)
00846     {
00847         strcpy(name, item->name);
00848 
00849         if (item->title)
00850         {
00851             snprintf(name, sizeof(name), "%s %s", item->name, item->title);
00852         }
00853 
00854         danger += (strtoint(name) / 1000) + 3;
00855         nrofi++;
00856     }
00857 
00858     if (nrofi > 1)
00859     {
00860         danger *= nrofi;
00861     }
00862 
00863     /* Using a bad device is *very* stupid */
00864     if (QUERY_FLAG(cauldron, FLAG_CURSED))
00865     {
00866         danger += 80;
00867     }
00868 
00869     if (QUERY_FLAG(cauldron, FLAG_DAMNED))
00870     {
00871         danger += 200;
00872     }
00873 
00874 #ifdef ALCHEMY_DEBUG
00875     LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
00876 #endif
00877 
00878     return danger;
00879 }
00880 
00887 int use_alchemy(object *op)
00888 {
00889     object *tmp, *item, *next;
00890     object *unpaid_cauldron = NULL;
00891     object *unpaid_item = NULL;
00892     int did_alchemy = 0;
00893 
00894     for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = next)
00895     {
00896         next = tmp->above;
00897 
00898         if (QUERY_FLAG(tmp, FLAG_IS_CAULDRON))
00899         {
00900             if (QUERY_FLAG(tmp, FLAG_UNPAID))
00901             {
00902                 unpaid_cauldron = tmp;
00903                 continue;
00904             }
00905 
00906             unpaid_item = NULL;
00907 
00908             for (item = tmp->inv; item; item = item->below)
00909             {
00910                 if (QUERY_FLAG(item, FLAG_UNPAID))
00911                 {
00912                     unpaid_item = item;
00913                     break;
00914                 }
00915             }
00916 
00917             if (unpaid_item != NULL)
00918             {
00919                 continue;
00920             }
00921 
00922             attempt_do_alchemy(op, tmp);
00923 
00924             if (QUERY_FLAG(tmp, FLAG_APPLIED))
00925             {
00926                 esrv_send_inventory(op, tmp);
00927             }
00928 
00929             did_alchemy = 1;
00930         }
00931     }
00932 
00933     if (unpaid_cauldron)
00934     {
00935         new_draw_info_format(0, COLOR_WHITE, op, "You must pay for your %s first!", query_base_name(unpaid_cauldron, NULL));
00936     }
00937     else if (unpaid_item)
00938     {
00939         new_draw_info_format(0, COLOR_WHITE, op, "You must pay for your %s first!", query_base_name(unpaid_item, NULL));
00940     }
00941 
00942     return did_alchemy;
00943 }