ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/alchemy.C
Revision: 1.33
Committed: Sat Dec 27 02:31:19 2008 UTC (15 years, 5 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_74
Changes since 1.32: +3 -2 lines
Log Message:
implement smell member for mapspaces, remove all traces of the old RANDOM macro

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