ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/alchemy.C
Revision: 1.40
Committed: Mon Oct 12 14:00:58 2009 UTC (14 years, 8 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_81
Changes since 1.39: +7 -6 lines
Log Message:
clarify license

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