ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/alchemy.C
Revision: 1.60
Committed: Fri Jan 27 22:00:40 2012 UTC (12 years, 3 months ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.59: +1 -2 lines
Log Message:
remove unused variables, kernel_panic

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