ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/deliantra/server/server/alchemy.C
Revision: 1.54
Committed: Fri Apr 30 20:43:18 2010 UTC (14 years, 1 month ago) by root
Content type: text/plain
Branch: MAIN
Changes since 1.53: +1 -3 lines
Log Message:
indent

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.50 * Copyright (©) 2005,2006,2007,2008,2009,2010 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     bool found = false;
139     *rp_arch_index = 0;
140 root 1.7
141 root 1.43 if (rp->transmute) /* look for matching ingredient/prod archs */
142     for (object *item = first_ingred; item; item = item->below)
143     {
144     for (int i = 0; i < rp->arch_names; i++)
145     //TODO: should be a shstr comparison
146     if (!strcmp(&item->arch->archname,rp->arch_name[i]))
147     {
148     *rp_arch_index = i;
149     prod_item = item;
150     break;
151     }
152 root 1.7
153 root 1.43 if (prod_item)
154     break;
155     }
156 root 1.7
157 root 1.43 /* failed, create a fresh object. Note no nrof>1 because that would
158     * allow players to create massive amounts of artifacts easily */
159     if (create_item && (!prod_item || prod_item->nrof > 1))
160 root 1.7 {
161 root 1.43 #ifdef ALCHEMY_DEBUG
162     LOG (llevDebug, "creating a new item.\n");
163     if (prod_item != NULL)
164     {
165     LOG (llevDebug, " had item: arch %s(nrof:%d)\n",
166     &prod_item->arch->archname, prod_item->nrof);
167     }
168     #endif
169     if (!prod_item)
170     *rp_arch_index = rndm (rp->arch_names);
171     prod_item = get_archetype (rp->arch_name[*rp_arch_index]);
172 elmex 1.1 }
173    
174 root 1.43 #ifdef ALCHEMY_DEBUG
175     LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
176     if (prod_item != NULL)
177 root 1.7 {
178 root 1.43 LOG (llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n",
179     &prod_item->arch->archname, prod_item->stats.sp);
180 elmex 1.1 }
181 root 1.43 #endif
182 root 1.17
183 root 1.43 return prod_item;
184 elmex 1.1 }
185    
186     /**
187     * Using a list of items and a recipe to make an artifact.
188     *
189     * @param cauldron the cauldron (including the ingredients) used to make the item
190     *
191     * @param rp the recipe to make the artifact from
192     *
193     * @return the newly created object, NULL if something failed
194     */
195 root 1.45 static object *
196 root 1.7 make_item_from_recipe (object *cauldron, recipe *rp)
197     {
198     artifact *art = NULL;
199     object *item = NULL;
200     size_t rp_arch_index;
201    
202     if (rp == NULL)
203     return (object *) NULL;
204    
205     /* Find the appropriate object to transform... */
206     if ((item = find_transmution_ob (cauldron->inv, rp, &rp_arch_index, 1)) == NULL)
207     {
208     LOG (llevDebug, "make_alchemy_item(): failed to create alchemical object.\n");
209     return (object *) NULL;
210     }
211    
212     /* Find the appropriate artifact template... */
213 root 1.34 if (rp->title != shstr_NONE)
214 root 1.7 {
215     if ((art = locate_recipe_artifact (rp, rp_arch_index)) == NULL)
216     {
217     LOG (llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
218     LOG (llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], &rp->title);
219     return (object *) NULL;
220     }
221 root 1.34
222 root 1.7 transmute_materialname (item, art->item);
223     give_artifact_abilities (item, art->item);
224     }
225    
226 root 1.51 if (cauldron->flag [FLAG_CURSED])
227     item->set_flag (FLAG_CURSED);
228     if (cauldron->flag [FLAG_DAMNED])
229     item->set_flag (FLAG_DAMNED);
230 root 1.7
231     return item;
232 elmex 1.1 }
233    
234 root 1.43 /*
235     * All but object "save_item" are elimentated from
236     * the container list. Note we have to becareful to remove the inventories
237     * of objects in the cauldron inventory (ex icecube has stuff in it).
238     */
239 root 1.45 static void
240 root 1.43 remove_contents (object *first_ob, object *save_item)
241     {
242     // this cries for a cleaner rewrite, removing save_item first possibly
243     object *next, *tmp = first_ob;
244    
245     while (tmp)
246     {
247     next = tmp->below;
248    
249     if (tmp == save_item)
250     {
251     if (!(tmp = next))
252     break;
253     else
254     next = next->below;
255     }
256    
257     if (tmp->inv)
258     remove_contents (tmp->inv, NULL);
259    
260     tmp->destroy ();
261     tmp = next;
262     }
263     }
264    
265 root 1.41 /**
266 root 1.43 * We adjust the nrof, exp and level of the final product, based
267     * on the item's default parameters, and the relevant caster skill level.
268     */
269 root 1.44 static void
270 root 1.43 adjust_product (object *item, int lvl, int yield)
271     {
272     int nrof = 1;
273    
274     if (!yield)
275     yield = 1;
276    
277     if (lvl <= 0)
278     lvl = 1; /* lets avoid div by zero! */
279    
280     if (item->nrof)
281     {
282     nrof = (int) ((1.0 - 1.0 / (lvl / 10.0 + 1.0)) * (rndm (0, yield - 1) + rndm (0, yield - 1) + rndm (0, yield - 1)) + 1);
283    
284     if (nrof > yield)
285     nrof = yield;
286    
287     item->nrof = nrof;
288     }
289     }
290    
291     /**
292     * Essentially a wrapper for make_item_from_recipe() and
293     * insert_ob_in_ob. If the caster has some alchemy skill, then they might
294     * gain some exp from (successfull) fabrication of the product.
295     * If nbatches==-1, don't give exp for this creation (random generation/
296     * failed recipe)
297 elmex 1.1 */
298 root 1.44 static object *
299 root 1.43 attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches)
300 root 1.7 {
301    
302 root 1.43 object *item = NULL, *skop;
303    
304     /* this should be passed to this fctn, not effiecent cpu use this way */
305     int batches = abs (nbatches);
306    
307     /* is the cauldron the right type? */
308     if (rp->cauldron && rp->cauldron != cauldron->arch->archname)
309     return 0;
310 root 1.20
311 root 1.43 skop = find_skill_by_name (caster, rp->skill);
312     /* does the caster have the skill? */
313     if (!skop)
314     return 0;
315 root 1.7
316 root 1.43 /* code required for this recipe, search the caster */
317     if (rp->keycode)
318 root 1.7 {
319 root 1.43 object *tmp;
320    
321     for (tmp = caster->inv; tmp; tmp = tmp->below)
322     {
323     if (tmp->type == FORCE && tmp->slaying && rp->keycode == tmp->slaying)
324     break;
325     }
326    
327     if (!tmp)
328     { /* failure--no code found */
329     new_draw_info (NDI_UNIQUE, 0, caster, "You know the ingredients, but not the technique. Go learn how to do this recipe.");
330     return 0;
331     }
332     }
333    
334     #ifdef EXTREME_ALCHEMY_DEBUG
335     LOG (llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
336     LOG (llevDebug, "attempt_recipe(): using recipe %s\n", *rp->title ? &rp->title : "unknown");
337 sf-marcmagus 1.38 #endif
338 elmex 1.1
339 root 1.43 if ((item = make_item_from_recipe (cauldron, rp)) != NULL)
340 root 1.7 {
341 root 1.43 remove_contents (cauldron->inv, item);
342     /* Recalc carrying of the cauldron, in case recipe did not conserve mass */
343     cauldron->update_weight ();
344     /* adj lvl, nrof on caster level */
345     adjust_product (item, ability, rp->yield ? (rp->yield * batches) : batches);
346     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.46 tmp = get_archetype (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.46 tmp = get_archetype (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.46 tmp = get_archetype (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     object *fb = get_archetype (SP_MED_FIREBALL);
493    
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     if (rp && (tmp = get_archetype (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     object *tmp = get_archetype (LOOSE_MANA);
561    
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.7 calc_alch_danger (object *caster, object *cauldron, recipe *rp)
580     {
581     object *item;
582     char name[MAX_BUF];
583     int danger = 0, nrofi = 0;
584    
585     /* Knowing alchemy skill reduces yer risk */
586     danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
587    
588 root 1.23 if (!caster->chosen_skill)
589     LOG (llevError | logBacktrace, "calc_alch_danger called without a chosen skill, caster %s, cauldron %s\n",
590     caster->debug_desc (), cauldron->debug_desc ());
591    
592 root 1.7 /* better cauldrons reduce risk */
593     danger -= cauldron->magic;
594    
595     /* Higher Int, lower the risk */
596     danger -= 3 * (caster->stats.Int - 15);
597    
598     /* Ingredients. Longer names usually mean rarer stuff.
599     * Thus the backfire is worse. Also, more ingredients
600     * means we are attempting a more powerfull potion,
601     * and thus the backfire will be worse. */
602     for (item = cauldron->inv; item; item = item->below)
603     {
604 root 1.19 assign (name, item->name);
605 root 1.7 if (item->title)
606     sprintf (name, "%s %s", &item->name, &item->title);
607     danger += (strtoint (name) / 1000) + 3;
608     nrofi++;
609     }
610 root 1.23
611 root 1.7 if (rp == NULL)
612     danger += 110;
613     else
614     danger += rp->diff * 3;
615    
616     /* Using a bad device is *majorly* stupid */
617 root 1.51 if (cauldron->flag [FLAG_CURSED])
618 root 1.7 danger += 80;
619 root 1.51 if (cauldron->flag [FLAG_DAMNED])
620 root 1.7 danger += 200;
621 elmex 1.1
622     #ifdef ALCHEMY_DEBUG
623 root 1.7 LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
624 elmex 1.1 #endif
625    
626 root 1.7 return danger;
627 elmex 1.1 }
628    
629 root 1.23 /**
630 elmex 1.1 * Determines if ingredients in a container match the
631     * proper ingredients for a recipe.
632     *
633     * rp is the recipe to check
634     * cauldron is the container that holds the ingredients
635     * returns 1 if the ingredients match the recipe, 0 if not
636     *
637     * This functions tries to find each defined ingredient in the container. It is
638     * the defined recipe iff
639     * - the number of ingredients of the recipe and in the container is equal
640     * - all ingredients of the recipe are found in the container
641     * - the number of batches is the same for all ingredients
642     */
643 root 1.7 static int
644     is_defined_recipe (const recipe *rp, const object *cauldron, object *caster)
645 elmex 1.1 {
646 root 1.7 uint32 batches_in_cauldron;
647     const linked_char *ingredient;
648     int number;
649     const object *ob;
650    
651     /* check for matching number of ingredients */
652     number = 0;
653     for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
654     number++;
655     for (ob = cauldron->inv; ob != NULL; ob = ob->below)
656     number--;
657     if (number != 0)
658     return 0;
659    
660     /* check for matching ingredients */
661     batches_in_cauldron = 0;
662     for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
663     {
664     uint32 nrof;
665     const char *name;
666     int ok;
667    
668     /* determine and remove nrof from name */
669     name = ingredient->name;
670     nrof = 0;
671     while (isdigit (*name))
672     {
673     nrof = 10 * nrof + (*name - '0');
674     name++;
675     }
676     if (nrof == 0)
677     nrof = 1;
678     while (*name == ' ')
679     name++;
680    
681     /* find the current ingredient in the cauldron */
682     ok = 0;
683     for (ob = cauldron->inv; ob != NULL; ob = ob->below)
684     {
685     char name_ob[MAX_BUF];
686     const char *name2;
687    
688 root 1.34 if (!ob->title)
689 root 1.7 name2 = ob->name;
690     else
691     {
692     snprintf (name_ob, sizeof (name_ob), "%s %s", &ob->name, &ob->title);
693     name2 = name_ob;
694     }
695    
696     if (strcmp (name2, name) == 0)
697     {
698     if (ob->nrof % nrof == 0)
699     {
700     uint32 batches;
701    
702     batches = ob->nrof / nrof;
703     if (batches_in_cauldron == 0)
704     {
705     batches_in_cauldron = batches;
706     ok = 1;
707     }
708     else if (batches_in_cauldron == batches)
709     ok = 1;
710 root 1.4 }
711 root 1.7 break;
712 root 1.4 }
713     }
714 root 1.7 if (!ok)
715     return (0);
716 elmex 1.1 }
717    
718 root 1.7 return (1);
719 elmex 1.1 }
720    
721     /**
722     * Find a recipe from a recipe list that matches the given formula. If there
723     * is more than one matching recipe, it selects a random one. If at least one
724     * transmuting recipe matches, it only considers matching transmuting recipes.
725     *
726     * @return one matching recipe, or NULL if no recipe matches
727     */
728 root 1.7 static recipe *
729     find_recipe (recipelist * fl, int formula, object *ingredients)
730 elmex 1.1 {
731 root 1.7 recipe *rp;
732     recipe *result; /* winning recipe, or NULL if no recipe found */
733     int recipes_matching; /* total number of matching recipes so far */
734     int transmute_found; /* records whether a transmuting recipe was found so far */
735     size_t rp_arch_index;
736 elmex 1.1
737     #ifdef EXTREME_ALCHEMY_DEBUG
738 root 1.7 LOG (llevDebug, "looking for formula %d:\n", formula);
739 elmex 1.1 #endif
740 root 1.7 result = NULL;
741     recipes_matching = 0;
742     transmute_found = 0;
743 root 1.22
744     for (rp = fl->items; rp; rp = rp->next)
745 root 1.7 {
746     /* check if recipe matches at all */
747     if (formula % rp->index != 0)
748     {
749 elmex 1.1 #ifdef EXTREME_ALCHEMY_DEBUG
750 sf-marcmagus 1.38 LOG (llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], &rp->title, rp->index);
751 elmex 1.1 #endif
752 root 1.7 continue;
753 elmex 1.1 }
754    
755 root 1.7 if (rp->transmute && find_transmution_ob (ingredients, rp, &rp_arch_index, 0) != NULL)
756     {
757 elmex 1.1 #ifdef EXTREME_ALCHEMY_DEBUG
758 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);
759 elmex 1.1 #endif
760 root 1.7 /* transmution recipe with matching base ingredient */
761     if (!transmute_found)
762     {
763     transmute_found = 1;
764     recipes_matching = 0;
765 elmex 1.1 }
766 root 1.7 }
767     else if (transmute_found)
768     {
769 elmex 1.1 #ifdef EXTREME_ALCHEMY_DEBUG
770 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,
771 root 1.7 rp->index);
772 elmex 1.1 #endif
773 root 1.7 /* "normal" recipe found after previous transmution recipe => ignore this recipe */
774     continue;
775 elmex 1.1 }
776     #ifdef EXTREME_ALCHEMY_DEBUG
777 root 1.7 else
778     {
779 sf-marcmagus 1.38 LOG (llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], &rp->title, rp->index);
780 elmex 1.1 }
781     #endif
782    
783 root 1.7 if (rndm (0, recipes_matching) == 0)
784     result = rp;
785 elmex 1.1
786 root 1.7 recipes_matching++;
787 elmex 1.1 }
788    
789 root 1.7 if (result == NULL)
790     {
791 elmex 1.1 #ifdef ALCHEMY_DEBUG
792 root 1.7 LOG (llevDebug, "couldn't find formula for ingredients.\n");
793 elmex 1.1 #endif
794 root 1.7 return NULL;
795 elmex 1.1 }
796    
797     #ifdef ALCHEMY_DEBUG
798 root 1.7 if (strcmp (result->title, "NONE") != 0)
799 sf-marcmagus 1.38 LOG (llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], &result->title, formula / result->index);
800 root 1.7 else
801     LOG (llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula / result->index);
802 elmex 1.1 #endif
803 root 1.7 return result;
804 elmex 1.1 }
805 root 1.43
806     /**
807     * Main part of the ALCHEMY code. From this we call fctns
808     * that take a look at the contents of the 'cauldron' and, using these ingredients,
809     * we construct an integer formula value which is referenced (randomly) against a
810     * formula list (the formula list chosen is based on the # contents of the cauldron).
811     *
812     * If we get a match between the recipe indicated in cauldron contents and a
813     * randomly chosen one, an item is created and experience awarded. Otherwise
814     * various failure effects are possible (getting worse and worse w/ # cauldron
815     * ingredients). Note that the 'item' to be made can be *anything* listed on
816     * the artifacts list in lib/artifacts which has a recipe listed in lib/formulae.
817     *
818     * To those wondering why I am using the funky formula index method:
819     * 1) I want to match recipe to ingredients regardless of ordering.
820     * 2) I want a fast search for the 'right' recipe.
821     *
822     * Note: it is just possible that a totally different combination of
823     * ingredients will result in a match with a given recipe. This is not a bug!
824     * There is no good reason (in my mind) why alchemical processes have to be
825     * unique -- such a 'feature' is one reason why players might want to experiment
826     * around. :)
827     * -b.t.
828     */
829     void
830     attempt_do_alchemy (object *caster, object *cauldron, object *skill)
831     {
832     recipelist *fl;
833     recipe *rp = NULL;
834     float success_chance;
835     int numb, ability = 1;
836     int formula = 0;
837     float ave_chance;
838     object *item, *skop;
839    
840     if (caster->type != PLAYER)
841     return; /* only players for now */
842    
843     if (get_map_flags (caster->map, NULL, caster->x, caster->y, NULL, NULL) & P_SAFE)
844     {
845     new_draw_info (NDI_UNIQUE, 0, caster, "This is sacred ground, the gods prevent you from using this device.");
846     return;
847     }
848    
849     /* if no ingredients, no formula! lets forget it */
850     if (!(formula = content_recipe_value (cauldron)))
851     return;
852    
853     numb = numb_ob_inside (cauldron);
854     if ((fl = get_formulalist (numb)))
855     {
856 root 1.51 if (caster->flag [FLAG_WIZ])
857 root 1.43 {
858     rp = find_recipe (fl, formula, cauldron->inv);
859     if (rp != NULL)
860     {
861     #ifdef ALCHEMY_DEBUG
862     if (strcmp (rp->title, "NONE"))
863     LOG (llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], &rp->title);
864     else
865     LOG (llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula / rp->index);
866     #endif
867     attempt_recipe (caster, cauldron, ability, rp, formula / rp->index);
868     }
869     else
870     LOG (llevDebug, "WIZ couldn't find formula for ingredients.\n");
871     return;
872     } /* End of WIZ alchemy */
873    
874     /* find the recipe */
875     rp = find_recipe (fl, formula, cauldron->inv);
876     if (rp
877     && rp->skill == skill->skill
878     && (!rp->cauldron
879     || rp->cauldron == cauldron->arch->archname))
880     {
881     uint64 value_ingredients;
882     uint64 value_item;
883     object *tmp;
884     int attempt_shadow_alchemy;
885    
886 root 1.46 ave_chance = fl->total_chance / (float)fl->number;
887 root 1.43
888 root 1.52 ability += skill->level * ((4.0 + cauldron->magic) / 4.0);
889 root 1.43
890     /* determine value of ingredients */
891     value_ingredients = 0;
892 root 1.46 for (tmp = cauldron->inv; tmp; tmp = tmp->below)
893 root 1.43 value_ingredients += query_cost (tmp, NULL, F_TRUE);
894    
895     attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster);
896    
897     /* create the object **FIRST**, then decide whether to keep it. */
898     if ((item = attempt_recipe (caster, cauldron, ability, rp, formula / rp->index)) != NULL)
899     {
900     /* compute base chance of recipe success */
901     success_chance = ((float) ability / (float) (rp->diff * (item->level + 2)));
902     if (ave_chance == 0)
903     ave_chance = 1;
904    
905     #ifdef ALCHEMY_DEBUG
906     LOG (llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level);
907     #endif
908    
909     value_item = query_cost (item, NULL, F_TRUE | F_IDENTIFIED | F_NOT_CURSED);
910     if (attempt_shadow_alchemy && value_item > value_ingredients)
911     {
912     #ifdef ALCHEMY_DEBUG
913     LOG (llevDebug,
914     "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n",
915     value_ingredients, value_item);
916     #endif
917     }
918     /* roll the dice */
919 root 1.52 else if (random_roll (0, 101, caster, PREFER_LOW) <= 100.0 * success_chance)
920 root 1.43 {
921     change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE);
922    
923     // let alchemy consume some time, so that exploits are less easy
924     caster->speed_left -= 1.0;
925    
926     return;
927     }
928     }
929     }
930     }
931    
932     /* if we get here, we failed!! */
933     alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, rp));
934     }
935