ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/alchemy.C
Revision: 1.41
Committed: Thu Oct 15 23:02:28 2009 UTC (14 years, 7 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.40: +7 -19 lines
Log Message:
improve some book messages

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