ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/alchemy.C
Revision: 1.38
Committed: Fri Oct 9 22:46:39 2009 UTC (14 years, 7 months ago) by sf-marcmagus
Content type: text/plain
Branch: MAIN
Changes since 1.37: +17 -9 lines
Log Message:
Fix alchemy debug logging to support shstrs.

File Contents

# User Rev Content
1 elmex 1.1 /*
2 root 1.27 * This file is part of Deliantra, the Roguelike Realtime MMORPG.
3 pippijn 1.18 *
4 elmex 1.36 * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team
5 root 1.24 * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team
6     * Copyright (©) 1992,2007 Frank Tore Johansen
7 pippijn 1.18 *
8 root 1.27 * Deliantra is free software: you can redistribute it and/or modify
9 root 1.26 * it under the terms of the GNU General Public License as published by
10     * the Free Software Foundation, either version 3 of the License, or
11     * (at your option) any later version.
12 pippijn 1.18 *
13 root 1.26 * This program is distributed in the hope that it will be useful,
14     * but WITHOUT ANY WARRANTY; without even the implied warranty of
15     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16     * GNU General Public License for more details.
17 pippijn 1.18 *
18 root 1.26 * You should have received a copy of the GNU General Public License
19     * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 root 1.24 *
21 root 1.27 * The authors can be reached via e-mail to <support@deliantra.net>
22 pippijn 1.18 */
23 elmex 1.1
24     /* March 96 - Laid down original code. -b.t. thomas@astro.psu.edu */
25    
26     #include <global.h>
27     #include <object.h>
28 root 1.14 #include <sproto.h>
29 elmex 1.1 #include <skills.h>
30     #include <spells.h>
31    
32     /** define this for some helpful debuging information */
33     #if 0
34 root 1.7 # define ALCHEMY_DEBUG
35 elmex 1.1 #endif
36    
37     /** define this for loads of (marginal) debuging information */
38     #if 0
39 root 1.7 # define EXTREME_ALCHEMY_DEBUG
40 elmex 1.1 #endif
41    
42     /** Random cauldrons effects */
43 root 1.7 static const char *const cauldron_effect[] = {
44     "vibrates briefly",
45     "produces a cloud of steam",
46     "emits bright flames",
47     "pours forth heavy black smoke",
48     "emits sparks",
49     "shoots out small flames",
50     "whines painfully",
51     "hiccups loudly",
52     "wheezes",
53     "burps",
54     "shakes",
55     "rattles",
56     "makes chugging sounds",
57     "smokes heavily for a while"
58     };
59 elmex 1.1
60    
61 root 1.7 static int is_defined_recipe (const recipe *rp, const object *cauldron, object *caster);
62     static recipe *find_recipe (recipelist * fl, int formula, object *ingredients);
63 elmex 1.1
64    
65     /** Returns a random selection from cauldron_effect[] */
66 root 1.7 static const char *
67     cauldron_sound (void)
68     {
69     int size = sizeof (cauldron_effect) / sizeof (char *);
70 elmex 1.1
71 root 1.7 return cauldron_effect[rndm (0, size - 1)];
72 elmex 1.1 }
73    
74     /**
75     * Main part of the ALCHEMY code. From this we call fctns
76     * that take a look at the contents of the 'cauldron' and, using these ingredients,
77     * we construct an integer formula value which is referenced (randomly) against a
78     * formula list (the formula list chosen is based on the # contents of the cauldron).
79     *
80     * If we get a match between the recipe indicated in cauldron contents and a
81     * randomly chosen one, an item is created and experience awarded. Otherwise
82     * various failure effects are possible (getting worse and worse w/ # cauldron
83     * ingredients). Note that the 'item' to be made can be *anything* listed on
84     * the artifacts list in lib/artifacts which has a recipe listed in lib/formulae.
85     *
86     * To those wondering why I am using the funky formula index method:
87     * 1) I want to match recipe to ingredients regardless of ordering.
88     * 2) I want a fast search for the 'right' recipe.
89     *
90     * Note: it is just possible that a totally different combination of
91     * ingredients will result in a match with a given recipe. This is not a bug!
92     * There is no good reason (in my mind) why alchemical processes have to be
93     * unique -- such a 'feature' is one reason why players might want to experiment
94     * around. :)
95     * -b.t.
96     */
97    
98 root 1.7 void
99 elmex 1.35 attempt_do_alchemy (object *caster, object *cauldron, object *skill)
100 root 1.7 {
101     recipelist *fl;
102     recipe *rp = NULL;
103     float success_chance;
104     int numb, ability = 1;
105     int formula = 0;
106     float ave_chance;
107     object *item, *skop;
108    
109     if (caster->type != PLAYER)
110     return; /* only players for now */
111    
112     if (get_map_flags (caster->map, NULL, caster->x, caster->y, NULL, NULL) & P_SAFE)
113     {
114     new_draw_info (NDI_UNIQUE, 0, caster, "This is sacred ground, the gods prevent you from using this device.");
115     return;
116     }
117    
118     /* if no ingredients, no formula! lets forget it */
119     if (!(formula = content_recipe_value (cauldron)))
120     return;
121    
122     numb = numb_ob_inside (cauldron);
123     if ((fl = get_formulalist (numb)))
124     {
125     if (QUERY_FLAG (caster, FLAG_WIZ))
126     {
127     rp = find_recipe (fl, formula, cauldron->inv);
128     if (rp != NULL)
129     {
130     #ifdef ALCHEMY_DEBUG
131     if (strcmp (rp->title, "NONE"))
132 sf-marcmagus 1.38 LOG (llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], &rp->title);
133 root 1.7 else
134     LOG (llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula / rp->index);
135     #endif
136     attempt_recipe (caster, cauldron, ability, rp, formula / rp->index);
137     }
138     else
139     LOG (llevDebug, "WIZ couldn't find formula for ingredients.\n");
140     return;
141     } /* End of WIZ alchemy */
142    
143     /* find the recipe */
144     rp = find_recipe (fl, formula, cauldron->inv);
145 elmex 1.35 if (rp
146     && rp->skill == skill->skill
147     && (!rp->cauldron
148     || rp->cauldron == cauldron->arch->archname))
149 root 1.7 {
150     uint64 value_ingredients;
151     uint64 value_item;
152     object *tmp;
153     int attempt_shadow_alchemy;
154    
155     ave_chance = fl->total_chance / (float) fl->number;
156 root 1.4
157 elmex 1.35 ability += (int) (skill->level * ((4.0 + cauldron->magic) / 4.0));
158 root 1.4
159 root 1.7 /* determine value of ingredients */
160     value_ingredients = 0;
161     for (tmp = cauldron->inv; tmp != NULL; tmp = tmp->below)
162     value_ingredients += query_cost (tmp, NULL, F_TRUE);
163    
164     attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster);
165    
166     /* create the object **FIRST**, then decide whether to keep it. */
167     if ((item = attempt_recipe (caster, cauldron, ability, rp, formula / rp->index)) != NULL)
168     {
169     /* compute base chance of recipe success */
170     success_chance = ((float) ability / (float) (rp->diff * (item->level + 2)));
171     if (ave_chance == 0)
172     ave_chance = 1;
173    
174     #ifdef ALCHEMY_DEBUG
175     LOG (llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level);
176     #endif
177    
178     value_item = query_cost (item, NULL, F_TRUE | F_IDENTIFIED | F_NOT_CURSED);
179     if (attempt_shadow_alchemy && value_item > value_ingredients)
180     {
181     #ifdef ALCHEMY_DEBUG
182     LOG (llevDebug,
183     "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n",
184     value_ingredients, value_item);
185 elmex 1.1 #endif
186 root 1.4 }
187 root 1.7 /* roll the dice */
188     else if ((float) (random_roll (0, 101, caster, PREFER_LOW)) <= 100.0 * success_chance)
189     {
190     change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE);
191 root 1.10
192     // let alchemy consume some time, so that exploits are less easy
193     caster->speed_left -= 1.0;
194    
195 root 1.7 return;
196 root 1.4 }
197     }
198     }
199 elmex 1.1 }
200 root 1.10
201 root 1.7 /* if we get here, we failed!! */
202     alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, rp));
203 elmex 1.1 }
204    
205     /**
206     * Recipe value of the entire contents of a container.
207     * This appears to just generate a hash value, which I guess for now works
208     * ok, but the possibility of duplicate hashes is certainly possible - msw
209     */
210    
211 root 1.7 int
212     content_recipe_value (object *op)
213     {
214 elmex 1.1 char name[MAX_BUF];
215 root 1.7 object *tmp = op->inv;
216     int tval = 0, formula = 0;
217 elmex 1.1
218 root 1.7 while (tmp)
219     {
220     tval = 0;
221 root 1.19 assign (name, tmp->name);
222 root 1.7 if (tmp->title)
223     sprintf (name, "%s %s", &tmp->name, &tmp->title);
224     tval = (strtoint (name) * (tmp->nrof ? tmp->nrof : 1));
225     #ifdef ALCHEMY_DEBUG
226     LOG (llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval);
227     #endif
228     formula += tval;
229     tmp = tmp->below;
230     }
231 elmex 1.1 #ifdef ALCHEMY_DEBUG
232 root 1.7 LOG (llevDebug, " Formula value=%d\n", formula);
233 elmex 1.1 #endif
234 root 1.7 return formula;
235 elmex 1.1 }
236    
237     /**
238     * Returns total number of items in op
239     */
240    
241 root 1.7 int
242     numb_ob_inside (object *op)
243     {
244     object *tmp = op->inv;
245     int number = 0, o_number = 0;
246    
247     while (tmp)
248     {
249     if (tmp->nrof)
250     number += tmp->nrof;
251     else
252     number++;
253     o_number++;
254     tmp = tmp->below;
255     }
256     #ifdef ALCHEMY_DEBUG
257 sf-marcmagus 1.38 LOG (llevDebug, "numb_ob_inside(%s): found %d ingredients\n", &op->name, o_number);
258 root 1.7 #endif
259     return o_number;
260 elmex 1.1 }
261 root 1.7
262 elmex 1.6 /**
263 elmex 1.1 * Essentially a wrapper for make_item_from_recipe() and
264     * insert_ob_in_ob. If the caster has some alchemy skill, then they might
265     * gain some exp from (successfull) fabrication of the product.
266     * If nbatches==-1, don't give exp for this creation (random generation/
267     * failed recipe)
268 root 1.7 */
269    
270     object *
271     attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches)
272     {
273    
274     object *item = NULL, *skop;
275    
276     /* this should be passed to this fctn, not effiecent cpu use this way */
277     int batches = abs (nbatches);
278    
279     /* is the cauldron the right type? */
280 elmex 1.35 if (rp->cauldron && rp->cauldron != cauldron->arch->archname)
281     return 0;
282 root 1.7
283     skop = find_skill_by_name (caster, rp->skill);
284     /* does the caster have the skill? */
285     if (!skop)
286     return 0;
287    
288     /* code required for this recipe, search the caster */
289     if (rp->keycode)
290     {
291     object *tmp;
292    
293 root 1.17 for (tmp = caster->inv; tmp; tmp = tmp->below)
294 root 1.7 {
295 root 1.20 if (tmp->type == FORCE && tmp->slaying && rp->keycode == tmp->slaying)
296 root 1.7 break;
297     }
298 root 1.17
299     if (!tmp)
300 root 1.7 { /* failure--no code found */
301 root 1.28 new_draw_info (NDI_UNIQUE, 0, caster, "You know the ingredients, but not the technique. Go learn how to do this recipe.");
302 root 1.7 return 0;
303 root 1.4 }
304 elmex 1.1 }
305    
306     #ifdef EXTREME_ALCHEMY_DEBUG
307 root 1.7 LOG (llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
308 sf-marcmagus 1.38 LOG (llevDebug, "attempt_recipe(): using recipe %s\n", *rp->title ? &rp->title : "unknown");
309 root 1.7 #endif
310    
311     if ((item = make_item_from_recipe (cauldron, rp)) != NULL)
312     {
313     remove_contents (cauldron->inv, item);
314     /* Recalc carrying of the cauldron, in case recipe did not conserve mass */
315 root 1.28 cauldron->update_weight ();
316 root 1.7 /* adj lvl, nrof on caster level */
317     adjust_product (item, ability, rp->yield ? (rp->yield * batches) : batches);
318     if (!item->env && (item = insert_ob_in_ob (item, cauldron)) == NULL)
319     {
320     new_draw_info (NDI_UNIQUE, 0, caster, "Nothing happened.");
321     /* new_draw_info_format(NDI_UNIQUE, 0,caster,
322     "Your spell causes the %s to explode!",&cauldron->name); */
323     /* kaboom_cauldron(); */
324     }
325     else
326 root 1.17 new_draw_info_format (NDI_UNIQUE, 0, caster, "The %s %s.", &cauldron->name, cauldron_sound ());
327 elmex 1.1 }
328 root 1.17
329 root 1.7 return item;
330 elmex 1.1 }
331    
332    
333    
334 root 1.17 /**
335 elmex 1.1 * We adjust the nrof, exp and level of the final product, based
336     * on the item's default parameters, and the relevant caster skill level.
337     */
338 root 1.17 void
339 root 1.7 adjust_product (object *item, int lvl, int yield)
340     {
341     int nrof = 1;
342    
343     if (!yield)
344     yield = 1;
345 root 1.17
346 root 1.7 if (lvl <= 0)
347     lvl = 1; /* lets avoid div by zero! */
348 root 1.17
349 root 1.7 if (item->nrof)
350     {
351     nrof = (int) ((1.0 - 1.0 / (lvl / 10.0 + 1.0)) * (rndm (0, yield - 1) + rndm (0, yield - 1) + rndm (0, yield - 1)) + 1);
352 root 1.17
353 root 1.7 if (nrof > yield)
354     nrof = yield;
355 root 1.17
356 root 1.7 item->nrof = nrof;
357 elmex 1.1 }
358     }
359    
360    
361     /**
362     * Using a list of items and a recipe to make an artifact.
363     *
364     * @param cauldron the cauldron (including the ingredients) used to make the item
365     *
366     * @param rp the recipe to make the artifact from
367     *
368     * @return the newly created object, NULL if something failed
369     */
370    
371 root 1.7 object *
372     make_item_from_recipe (object *cauldron, recipe *rp)
373     {
374     artifact *art = NULL;
375     object *item = NULL;
376     size_t rp_arch_index;
377    
378     if (rp == NULL)
379     return (object *) NULL;
380    
381     /* Find the appropriate object to transform... */
382     if ((item = find_transmution_ob (cauldron->inv, rp, &rp_arch_index, 1)) == NULL)
383     {
384     LOG (llevDebug, "make_alchemy_item(): failed to create alchemical object.\n");
385     return (object *) NULL;
386     }
387    
388     /* Find the appropriate artifact template... */
389 root 1.34 if (rp->title != shstr_NONE)
390 root 1.7 {
391     if ((art = locate_recipe_artifact (rp, rp_arch_index)) == NULL)
392     {
393     LOG (llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
394     LOG (llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], &rp->title);
395     return (object *) NULL;
396     }
397 root 1.34
398 root 1.7 transmute_materialname (item, art->item);
399     give_artifact_abilities (item, art->item);
400     }
401    
402     if (QUERY_FLAG (cauldron, FLAG_CURSED))
403     SET_FLAG (item, FLAG_CURSED);
404     if (QUERY_FLAG (cauldron, FLAG_DAMNED))
405     SET_FLAG (item, FLAG_DAMNED);
406    
407     return item;
408 elmex 1.1 }
409    
410    
411     /**
412     * Looks through the ingredient list. If we find a
413     * suitable object in it - we will use that to make the requested artifact.
414     * Otherwise the code returns a 'generic' item if create_item is set. -b.t.
415     *
416     * @param rp_arch_index pointer to return value; set to arch index for recipe;
417     * set to zero if not using a transmution formula
418     */
419 root 1.7
420     object *
421 elmex 1.37 find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item)
422 root 1.7 {
423 elmex 1.37 object *prod_item = 0;
424     bool found = false;
425     *rp_arch_index = 0;
426 root 1.7
427 elmex 1.37 if (rp->transmute) /* look for matching ingredient/prod archs */
428     for (object *item = first_ingred; item; item = item->below)
429 root 1.7 {
430 root 1.34 for (int i = 0; i < rp->arch_names; i++)
431     //TODO: should be a shstr comparison
432     if (&item->arch->archname == rp->arch_name[i])
433     {
434     *rp_arch_index = i;
435 elmex 1.37 prod_item = item;
436 root 1.34 break;
437     }
438 root 1.20
439 elmex 1.37 if (prod_item)
440 root 1.7 break;
441     }
442    
443     /* failed, create a fresh object. Note no nrof>1 because that would
444     * allow players to create massive amounts of artifacts easily */
445 elmex 1.37 if (create_item && (!prod_item || prod_item->nrof > 1))
446 root 1.7 {
447 sf-marcmagus 1.38 #ifdef ALCHEMY_DEBUG
448     LOG (llevDebug, "creating a new item.\n");
449     if (prod_item != NULL)
450     {
451     LOG (llevDebug, " had item: arch %s(nrof:%d)\n",
452     &prod_item->arch->archname, prod_item->nrof);
453     }
454     #endif
455 root 1.33 *rp_arch_index = rndm (rp->arch_names);
456 elmex 1.37 prod_item = get_archetype (rp->arch_name[*rp_arch_index]);
457 elmex 1.1 }
458    
459     #ifdef ALCHEMY_DEBUG
460 root 1.7 LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
461 elmex 1.37 if (prod_item != NULL)
462 root 1.7 {
463 elmex 1.37 LOG (llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n",
464 sf-marcmagus 1.38 &prod_item->arch->archname, prod_item->stats.sp);
465 elmex 1.1 }
466     #endif
467 root 1.7
468 elmex 1.37 return prod_item;
469 elmex 1.1 }
470    
471    
472     /**
473     * Ouch. We didnt get the formula we wanted.
474     * This fctn simulates the backfire effects--worse effects as the level
475     * increases. If SPELL_FAILURE_EFFECTS is defined some really evil things
476     * can happen to the would be alchemist. This table probably needs some
477     * adjustment for playbalance. -b.t.
478     */
479 root 1.7
480     void
481     alchemy_failure_effect (object *op, object *cauldron, recipe *rp, int danger)
482     {
483     int level = 0;
484    
485     if (!op || !cauldron)
486     return;
487    
488     if (danger > 1)
489     level = random_roll (1, danger, op, PREFER_LOW);
490    
491     #ifdef ALCHEMY_DEBUG
492     LOG (llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
493     #endif
494    
495     /* possible outcomes based on level */
496     if (level < 25)
497     { /* INGREDIENTS USED/SLAGGED */
498     object *item = NULL;
499    
500     if (rndm (0, 2))
501     { /* slag created */
502     object *tmp = cauldron->inv;
503     int weight = 0;
504    
505     tmp = get_archetype ("rock");
506     tmp->weight = weight;
507     tmp->value = 0;
508     tmp->materialname = "stone";
509     tmp->name = "slag";
510     tmp->name_pl = "slags";
511     item = insert_ob_in_ob (tmp, cauldron);
512     CLEAR_FLAG (tmp, FLAG_CAN_ROLL);
513     CLEAR_FLAG (tmp, FLAG_NO_PICK);
514     tmp->move_block = 0;
515     }
516 root 1.21
517 root 1.7 remove_contents (cauldron->inv, item);
518     new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ());
519     return;
520     }
521     else if (level < 40)
522     { /* MAKE TAINTED ITEM */
523     object *tmp = NULL;
524    
525     if (!rp)
526     if ((rp = get_random_recipe ((recipelist *) NULL)) == NULL)
527     return;
528    
529     if ((tmp = attempt_recipe (op, cauldron, 1, rp, -1)))
530     {
531     if (!QUERY_FLAG (tmp, FLAG_CURSED)) /* curse it */
532     SET_FLAG (tmp, FLAG_CURSED);
533    
534     /* the apply code for potions already deals with cursed
535     * potions, so any code here is basically ignored.
536     */
537     if (tmp->type == FOOD)
538     {
539     tmp->stats.hp = random_roll (0, 149, op, PREFER_LOW);
540     }
541     tmp->value = 0; /* unsaleable item */
542    
543     /* change stats downward */
544     do
545     {
546     change_attr_value (&tmp->stats, rndm (0, 6), -1 * (rndm (1, 3)));
547     }
548     while (rndm (0, 2));
549     }
550     return;
551     }
552 root 1.30
553 root 1.7 if (level == 40)
554     { /* MAKE RANDOM RECIPE */
555     recipelist *fl;
556     int numb = numb_ob_inside (cauldron);
557    
558     fl = get_formulalist (numb - 1); /* take a lower recipe list */
559     if (fl && (rp = get_random_recipe (fl)))
560     /* even though random, don't grant user any EXP for it */
561     (void) attempt_recipe (op, cauldron, 1, rp, -1);
562     else
563     alchemy_failure_effect (op, cauldron, rp, level - 1);
564     }
565     else if (level < 45)
566     { /* INFURIATE NPC's */
567     /* this is kind of kludgy I know... */
568     cauldron->enemy = op;
569     npc_call_help (cauldron);
570     cauldron->enemy = NULL;
571    
572     alchemy_failure_effect (op, cauldron, rp, level - 5);
573     }
574     else if (level < 50)
575     { /* MINOR EXPLOSION/FIREBALL */
576     object *tmp;
577    
578     remove_contents (cauldron->inv, NULL);
579     switch (rndm (0, 2))
580     {
581 root 1.15 case 0:
582     tmp = get_archetype ("bomb");
583     tmp->stats.dam = random_roll (1, level, op, PREFER_LOW);
584     tmp->stats.hp = random_roll (1, level, op, PREFER_LOW);
585     new_draw_info_format (NDI_UNIQUE, 0, op, "The %s creates a bomb!", &cauldron->name);
586     break;
587 root 1.4
588 root 1.15 default:
589     tmp = get_archetype ("fireball");
590     tmp->stats.dam = random_roll (1, level, op, PREFER_LOW) / 5 + 1;
591     tmp->stats.hp = random_roll (1, level, op, PREFER_LOW) / 10 + 2;
592     new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name);
593     break;
594 root 1.7 }
595 root 1.15
596 root 1.30 tmp->insert_at (cauldron);
597 root 1.7 }
598     else if (level < 60)
599     { /* CREATE MONSTER */
600     new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ());
601     remove_contents (cauldron->inv, NULL);
602     }
603     else if (level < 80)
604     { /* MAJOR FIRE */
605     object *fb = get_archetype (SP_MED_FIREBALL);
606    
607     remove_contents (cauldron->inv, NULL);
608     fire_arch_from_position (cauldron, cauldron, cauldron->x, cauldron->y, 0, fb);
609 root 1.32 fb->destroy ();
610 root 1.7 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name);
611     }
612     else if (level < 100)
613     { /* WHAMMY the CAULDRON */
614     if (!QUERY_FLAG (cauldron, FLAG_CURSED))
615     SET_FLAG (cauldron, FLAG_CURSED);
616     else
617     cauldron->magic--;
618 root 1.30
619 root 1.7 cauldron->magic -= random_roll (0, 4, op, PREFER_LOW);
620 root 1.30
621 root 1.7 if (rndm (0, 1))
622     {
623     remove_contents (cauldron->inv, NULL);
624     new_draw_info_format (NDI_UNIQUE, 0, op, "Your %s turns darker then makes a gulping sound!", &cauldron->name);
625     }
626     else
627     new_draw_info_format (NDI_UNIQUE, 0, op, "Your %s becomes darker.", &cauldron->name);
628     }
629     else if (level < 110)
630     { /* SUMMON EVIL MONSTERS */
631     object *tmp = get_random_mon (level / 5);
632    
633     remove_contents (cauldron->inv, NULL);
634 root 1.30
635 root 1.7 if (!tmp)
636     alchemy_failure_effect (op, cauldron, rp, level);
637 root 1.25 else if (summon_hostile_monsters (cauldron, random_roll (1, 10, op, PREFER_LOW), tmp->arch->archname))
638 root 1.7 new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s and then pours forth monsters!", &cauldron->name, cauldron_sound ());
639     }
640     else if (level < 150)
641     { /* COMBO EFFECT */
642     int roll = rndm (1, 3);
643    
644     while (roll)
645     {
646     alchemy_failure_effect (op, cauldron, rp, level - 39);
647     roll--;
648     }
649     }
650     else if (level == 151)
651     { /* CREATE RANDOM ARTIFACT */
652     object *tmp;
653    
654     /* this is meant to be better than prior possiblity,
655     * in this one, we allow *any* valid alchemy artifact
656     * to be made (rather than only those on the given
657     * formulalist) */
658     if (!rp)
659     rp = get_random_recipe ((recipelist *) NULL);
660 root 1.33
661     if (rp && (tmp = get_archetype (rp->arch_name [rndm (rp->arch_names)])))
662 root 1.7 {
663     generate_artifact (tmp, random_roll (1, op->level / 2 + 1, op, PREFER_HIGH) + 1);
664     if ((tmp = insert_ob_in_ob (tmp, cauldron)))
665     {
666     remove_contents (cauldron->inv, tmp);
667     new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ());
668     }
669 root 1.4 }
670 root 1.7 }
671     else
672     { /* MANA STORM - watch out!! */
673     object *tmp = get_archetype (LOOSE_MANA);
674    
675     new_draw_info (NDI_UNIQUE, 0, op, "You unwisely release potent forces!");
676     remove_contents (cauldron->inv, NULL);
677     cast_magic_storm (op, tmp, level);
678     }
679 elmex 1.1 }
680    
681 root 1.13 /*
682 elmex 1.1 * All but object "save_item" are elimentated from
683     * the container list. Note we have to becareful to remove the inventories
684     * of objects in the cauldron inventory (ex icecube has stuff in it).
685     */
686 root 1.7 void
687     remove_contents (object *first_ob, object *save_item)
688     {
689 root 1.31 // this cries for a cleaner rewrite, removing save_item first possibly
690 root 1.7 object *next, *tmp = first_ob;
691    
692     while (tmp)
693     {
694     next = tmp->below;
695 root 1.13
696 root 1.7 if (tmp == save_item)
697     {
698     if (!(tmp = next))
699     break;
700     else
701     next = next->below;
702     }
703 root 1.13
704 root 1.7 if (tmp->inv)
705     remove_contents (tmp->inv, NULL);
706 root 1.13
707 root 1.32 tmp->destroy ();
708 root 1.7 tmp = next;
709 elmex 1.1 }
710     }
711    
712 root 1.23 /**
713 elmex 1.1 *"Danger" level, will determine how bad the backfire
714     * could be if the user fails to concoct a recipe properly. Factors include
715     * the number of ingredients, the length of the name of each ingredient,
716     * the user's effective level, the user's Int and the enchantment on the
717     * mixing device (aka "cauldron"). Higher values of 'danger' indicate more
718     * danger. Note that we assume that we have had the caster ready the alchemy
719     * skill *before* this routine is called. (no longer auto-readies that skill)
720     * -b.t.
721     */
722 root 1.7 int
723     calc_alch_danger (object *caster, object *cauldron, recipe *rp)
724     {
725     object *item;
726     char name[MAX_BUF];
727     int danger = 0, nrofi = 0;
728    
729     /* Knowing alchemy skill reduces yer risk */
730     danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
731    
732 root 1.23 if (!caster->chosen_skill)
733     LOG (llevError | logBacktrace, "calc_alch_danger called without a chosen skill, caster %s, cauldron %s\n",
734     caster->debug_desc (), cauldron->debug_desc ());
735    
736 root 1.7 /* better cauldrons reduce risk */
737     danger -= cauldron->magic;
738    
739     /* Higher Int, lower the risk */
740     danger -= 3 * (caster->stats.Int - 15);
741    
742     /* Ingredients. Longer names usually mean rarer stuff.
743     * Thus the backfire is worse. Also, more ingredients
744     * means we are attempting a more powerfull potion,
745     * and thus the backfire will be worse. */
746     for (item = cauldron->inv; item; item = item->below)
747     {
748 root 1.19 assign (name, item->name);
749 root 1.7 if (item->title)
750     sprintf (name, "%s %s", &item->name, &item->title);
751     danger += (strtoint (name) / 1000) + 3;
752     nrofi++;
753     }
754 root 1.23
755 root 1.7 if (rp == NULL)
756     danger += 110;
757     else
758     danger += rp->diff * 3;
759    
760     /* Using a bad device is *majorly* stupid */
761     if (QUERY_FLAG (cauldron, FLAG_CURSED))
762     danger += 80;
763     if (QUERY_FLAG (cauldron, FLAG_DAMNED))
764     danger += 200;
765 elmex 1.1
766     #ifdef ALCHEMY_DEBUG
767 root 1.7 LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
768 elmex 1.1 #endif
769    
770 root 1.7 return danger;
771 elmex 1.1 }
772    
773 root 1.23 /**
774 elmex 1.1 * Determines if ingredients in a container match the
775     * proper ingredients for a recipe.
776     *
777     * rp is the recipe to check
778     * cauldron is the container that holds the ingredients
779     * returns 1 if the ingredients match the recipe, 0 if not
780     *
781     * This functions tries to find each defined ingredient in the container. It is
782     * the defined recipe iff
783     * - the number of ingredients of the recipe and in the container is equal
784     * - all ingredients of the recipe are found in the container
785     * - the number of batches is the same for all ingredients
786     */
787 root 1.7 static int
788     is_defined_recipe (const recipe *rp, const object *cauldron, object *caster)
789 elmex 1.1 {
790 root 1.7 uint32 batches_in_cauldron;
791     const linked_char *ingredient;
792     int number;
793     const object *ob;
794    
795     /* check for matching number of ingredients */
796     number = 0;
797     for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
798     number++;
799     for (ob = cauldron->inv; ob != NULL; ob = ob->below)
800     number--;
801     if (number != 0)
802     return 0;
803    
804     /* check for matching ingredients */
805     batches_in_cauldron = 0;
806     for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
807     {
808     uint32 nrof;
809     const char *name;
810     int ok;
811    
812     /* determine and remove nrof from name */
813     name = ingredient->name;
814     nrof = 0;
815     while (isdigit (*name))
816     {
817     nrof = 10 * nrof + (*name - '0');
818     name++;
819     }
820     if (nrof == 0)
821     nrof = 1;
822     while (*name == ' ')
823     name++;
824    
825     /* find the current ingredient in the cauldron */
826     ok = 0;
827     for (ob = cauldron->inv; ob != NULL; ob = ob->below)
828     {
829     char name_ob[MAX_BUF];
830     const char *name2;
831    
832 root 1.34 if (!ob->title)
833 root 1.7 name2 = ob->name;
834     else
835     {
836     snprintf (name_ob, sizeof (name_ob), "%s %s", &ob->name, &ob->title);
837     name2 = name_ob;
838     }
839    
840     if (strcmp (name2, name) == 0)
841     {
842     if (ob->nrof % nrof == 0)
843     {
844     uint32 batches;
845    
846     batches = ob->nrof / nrof;
847     if (batches_in_cauldron == 0)
848     {
849     batches_in_cauldron = batches;
850     ok = 1;
851     }
852     else if (batches_in_cauldron == batches)
853     ok = 1;
854 root 1.4 }
855 root 1.7 break;
856 root 1.4 }
857     }
858 root 1.7 if (!ok)
859     return (0);
860 elmex 1.1 }
861    
862 root 1.7 return (1);
863 elmex 1.1 }
864    
865     /**
866     * Find a recipe from a recipe list that matches the given formula. If there
867     * is more than one matching recipe, it selects a random one. If at least one
868     * transmuting recipe matches, it only considers matching transmuting recipes.
869     *
870     * @return one matching recipe, or NULL if no recipe matches
871     */
872 root 1.7 static recipe *
873     find_recipe (recipelist * fl, int formula, object *ingredients)
874 elmex 1.1 {
875 root 1.7 recipe *rp;
876     recipe *result; /* winning recipe, or NULL if no recipe found */
877     int recipes_matching; /* total number of matching recipes so far */
878     int transmute_found; /* records whether a transmuting recipe was found so far */
879     size_t rp_arch_index;
880 elmex 1.1
881     #ifdef EXTREME_ALCHEMY_DEBUG
882 root 1.7 LOG (llevDebug, "looking for formula %d:\n", formula);
883 elmex 1.1 #endif
884 root 1.7 result = NULL;
885     recipes_matching = 0;
886     transmute_found = 0;
887 root 1.22
888     for (rp = fl->items; rp; rp = rp->next)
889 root 1.7 {
890     /* check if recipe matches at all */
891     if (formula % rp->index != 0)
892     {
893 elmex 1.1 #ifdef EXTREME_ALCHEMY_DEBUG
894 sf-marcmagus 1.38 LOG (llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], &rp->title, rp->index);
895 elmex 1.1 #endif
896 root 1.7 continue;
897 elmex 1.1 }
898    
899 root 1.7 if (rp->transmute && find_transmution_ob (ingredients, rp, &rp_arch_index, 0) != NULL)
900     {
901 elmex 1.1 #ifdef EXTREME_ALCHEMY_DEBUG
902 sf-marcmagus 1.38 LOG (llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], &rp->title, rp->index);
903 elmex 1.1 #endif
904 root 1.7 /* transmution recipe with matching base ingredient */
905     if (!transmute_found)
906     {
907     transmute_found = 1;
908     recipes_matching = 0;
909 elmex 1.1 }
910 root 1.7 }
911     else if (transmute_found)
912     {
913 elmex 1.1 #ifdef EXTREME_ALCHEMY_DEBUG
914 sf-marcmagus 1.38 LOG (llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], &rp->title,
915 root 1.7 rp->index);
916 elmex 1.1 #endif
917 root 1.7 /* "normal" recipe found after previous transmution recipe => ignore this recipe */
918     continue;
919 elmex 1.1 }
920     #ifdef EXTREME_ALCHEMY_DEBUG
921 root 1.7 else
922     {
923 sf-marcmagus 1.38 LOG (llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], &rp->title, rp->index);
924 elmex 1.1 }
925     #endif
926    
927 root 1.7 if (rndm (0, recipes_matching) == 0)
928     result = rp;
929 elmex 1.1
930 root 1.7 recipes_matching++;
931 elmex 1.1 }
932    
933 root 1.7 if (result == NULL)
934     {
935 elmex 1.1 #ifdef ALCHEMY_DEBUG
936 root 1.7 LOG (llevDebug, "couldn't find formula for ingredients.\n");
937 elmex 1.1 #endif
938 root 1.7 return NULL;
939 elmex 1.1 }
940    
941     #ifdef ALCHEMY_DEBUG
942 root 1.7 if (strcmp (result->title, "NONE") != 0)
943 sf-marcmagus 1.38 LOG (llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], &result->title, formula / result->index);
944 root 1.7 else
945     LOG (llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula / result->index);
946 elmex 1.1 #endif
947 root 1.7 return result;
948 elmex 1.1 }