ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/alchemy.C
Revision: 1.45
Committed: Fri Nov 6 13:03:34 2009 UTC (14 years, 6 months ago) by root
Content type: text/plain
Branch: MAIN
CVS Tags: rel-2_90
Changes since 1.44: +4 -4 lines
Log Message:
make effectively static symbols actually static, part 2

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