1 | /* |
1 | /* |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
3 | * |
3 | * |
4 | * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
4 | * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
5 | * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team |
5 | * Copyright (©) 2002 Mark Wedel & Crossfire Development Team |
6 | * Copyright (©) 1992,2007 Frank Tore Johansen |
6 | * Copyright (©) 1992 Frank Tore Johansen |
7 | * |
7 | * |
8 | * Deliantra is free software: you can redistribute it and/or modify it under |
8 | * 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 |
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 |
10 | * Free Software Foundation, either version 3 of the License, or (at your |
11 | * option) any later version. |
11 | * option) any later version. |
12 | * |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
16 | * GNU General Public License for more details. |
17 | * |
17 | * |
18 | * You should have received a copy of the Affero GNU General Public License |
18 | * 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 |
19 | * and the GNU General Public License along with this program. If not, see |
20 | * <http://www.gnu.org/licenses/>. |
20 | * <http://www.gnu.org/licenses/>. |
21 | * |
21 | * |
22 | * The authors can be reached via e-mail to <support@deliantra.net> |
22 | * The authors can be reached via e-mail to <support@deliantra.net> |
23 | */ |
23 | */ |
24 | |
24 | |
25 | /* March 96 - Laid down original code. -b.t. thomas@astro.psu.edu */ |
25 | /* March 96 - Laid down original code. -b.t. thomas@astro.psu.edu */ |
26 | |
26 | |
… | |
… | |
61 | static int is_defined_recipe (const recipe *rp, const object *cauldron, object *caster); |
61 | static int is_defined_recipe (const recipe *rp, const object *cauldron, object *caster); |
62 | static recipe *find_recipe (recipelist * fl, int formula, object *ingredients); |
62 | static recipe *find_recipe (recipelist * fl, int formula, object *ingredients); |
63 | |
63 | |
64 | /** Returns a random selection from cauldron_effect[] */ |
64 | /** Returns a random selection from cauldron_effect[] */ |
65 | static const char * |
65 | static const char * |
66 | cauldron_sound (void) |
66 | cauldron_sound () |
67 | { |
67 | { |
68 | int size = sizeof (cauldron_effect) / sizeof (char *); |
68 | return cauldron_effect [rndm (array_length (cauldron_effect))]; |
69 | |
|
|
70 | return cauldron_effect[rndm (0, size - 1)]; |
|
|
71 | } |
69 | } |
72 | |
70 | |
73 | /** |
71 | /** |
74 | * Recipe value of the entire contents of a container. |
72 | * Recipe value of the entire contents of a container. |
75 | * This appears to just generate a hash value, which I guess for now works |
73 | * 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 |
74 | * ok, but the possibility of duplicate hashes is certainly possible - msw |
77 | */ |
75 | */ |
78 | static int |
76 | static int |
79 | content_recipe_value (object *op) |
77 | content_recipe_value (object *op) |
80 | { |
78 | { |
81 | char name[MAX_BUF]; |
|
|
82 | object *tmp = op->inv; |
79 | object *tmp = op->inv; |
83 | int tval = 0, formula = 0; |
80 | int formula = 0; |
84 | |
81 | |
85 | while (tmp) |
82 | while (tmp) |
86 | { |
83 | { |
87 | tval = 0; |
84 | const char *name = tmp->title |
88 | assign (name, tmp->name); |
|
|
89 | if (tmp->title) |
|
|
90 | sprintf (name, "%s %s", &tmp->name, &tmp->title); |
85 | ? (const char *)format ("%s %s", &tmp->name, &tmp->title) |
|
|
86 | : tmp->name; |
|
|
87 | |
91 | tval = (strtoint (name) * (tmp->nrof ? tmp->nrof : 1)); |
88 | int tval = strtoint (name) * tmp->number_of (); |
92 | #ifdef ALCHEMY_DEBUG |
89 | #ifdef ALCHEMY_DEBUG |
93 | LOG (llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval); |
90 | LOG (llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval); |
94 | #endif |
91 | #endif |
95 | formula += tval; |
92 | formula += tval; |
96 | tmp = tmp->below; |
93 | tmp = tmp->below; |
97 | } |
94 | } |
98 | #ifdef ALCHEMY_DEBUG |
95 | #ifdef ALCHEMY_DEBUG |
99 | LOG (llevDebug, " Formula value=%d\n", formula); |
96 | LOG (llevDebug, " Formula value=%d\n", formula); |
100 | #endif |
97 | #endif |
|
|
98 | |
101 | return formula; |
99 | return formula; |
102 | } |
100 | } |
103 | |
101 | |
104 | /** |
102 | /** |
105 | * Returns total number of items in op |
103 | * Returns total number of items in op |
… | |
… | |
135 | */ |
133 | */ |
136 | static object * |
134 | static object * |
137 | find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) |
135 | find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) |
138 | { |
136 | { |
139 | object *prod_item = 0; |
137 | object *prod_item = 0; |
140 | bool found = false; |
|
|
141 | *rp_arch_index = 0; |
138 | *rp_arch_index = 0; |
142 | |
139 | |
143 | if (rp->transmute) /* look for matching ingredient/prod archs */ |
140 | if (rp->transmute) /* look for matching ingredient/prod archs */ |
144 | for (object *item = first_ingred; item; item = item->below) |
141 | for (object *item = first_ingred; item; item = item->below) |
145 | { |
142 | { |
… | |
… | |
168 | &prod_item->arch->archname, prod_item->nrof); |
165 | &prod_item->arch->archname, prod_item->nrof); |
169 | } |
166 | } |
170 | #endif |
167 | #endif |
171 | if (!prod_item) |
168 | if (!prod_item) |
172 | *rp_arch_index = rndm (rp->arch_names); |
169 | *rp_arch_index = rndm (rp->arch_names); |
173 | prod_item = get_archetype (rp->arch_name[*rp_arch_index]); |
170 | prod_item = archetype::get (rp->arch_name[*rp_arch_index]); |
174 | } |
171 | } |
175 | |
172 | |
176 | #ifdef ALCHEMY_DEBUG |
173 | #ifdef ALCHEMY_DEBUG |
177 | LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no "); |
174 | LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no "); |
178 | if (prod_item != NULL) |
175 | if (prod_item != NULL) |
… | |
… | |
223 | |
220 | |
224 | transmute_materialname (item, art->item); |
221 | transmute_materialname (item, art->item); |
225 | give_artifact_abilities (item, art->item); |
222 | give_artifact_abilities (item, art->item); |
226 | } |
223 | } |
227 | |
224 | |
228 | if (QUERY_FLAG (cauldron, FLAG_CURSED)) |
225 | if (cauldron->flag [FLAG_CURSED]) |
229 | SET_FLAG (item, FLAG_CURSED); |
226 | item->set_flag (FLAG_CURSED); |
230 | if (QUERY_FLAG (cauldron, FLAG_DAMNED)) |
227 | if (cauldron->flag [FLAG_DAMNED]) |
231 | SET_FLAG (item, FLAG_DAMNED); |
228 | item->set_flag (FLAG_DAMNED); |
232 | |
229 | |
233 | return item; |
230 | return item; |
234 | } |
231 | } |
235 | |
232 | |
236 | /* |
233 | /* |
237 | * All but object "save_item" are elimentated from |
234 | * All but object "save_item" are elimentated from |
238 | * the container list. Note we have to becareful to remove the inventories |
235 | * 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). |
236 | * of objects in the cauldron inventory (ex icecube has stuff in it). |
240 | */ |
237 | */ |
241 | static void |
238 | static void |
242 | remove_contents (object *first_ob, object *save_item) |
239 | remove_contents (object *first_ob, object *save_item) |
243 | { |
240 | { |
244 | // this cries for a cleaner rewrite, removing save_item first possibly |
241 | // this cries for a cleaner rewrite, removing save_item first possibly |
… | |
… | |
289 | item->nrof = nrof; |
286 | item->nrof = nrof; |
290 | } |
287 | } |
291 | } |
288 | } |
292 | |
289 | |
293 | /** |
290 | /** |
294 | * Essentially a wrapper for make_item_from_recipe() and |
291 | * Essentially a wrapper for make_item_from_recipe() and |
295 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
292 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
296 | * gain some exp from (successfull) fabrication of the product. |
293 | * gain some exp from (successfull) fabrication of the product. |
297 | * If nbatches==-1, don't give exp for this creation (random generation/ |
294 | * If nbatches==-1, don't give exp for this creation (random generation/ |
298 | * failed recipe) |
295 | * failed recipe) |
299 | */ |
296 | */ |
300 | static object * |
297 | static object * |
301 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
298 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
… | |
… | |
342 | { |
339 | { |
343 | remove_contents (cauldron->inv, item); |
340 | remove_contents (cauldron->inv, item); |
344 | /* Recalc carrying of the cauldron, in case recipe did not conserve mass */ |
341 | /* Recalc carrying of the cauldron, in case recipe did not conserve mass */ |
345 | cauldron->update_weight (); |
342 | cauldron->update_weight (); |
346 | /* adj lvl, nrof on caster level */ |
343 | /* adj lvl, nrof on caster level */ |
347 | adjust_product (item, ability, rp->yield ? (rp->yield * batches) : batches); |
344 | adjust_product (item, ability, rp->yield ? rp->yield * batches : batches); |
|
|
345 | |
348 | if (!item->env && (item = insert_ob_in_ob (item, cauldron)) == NULL) |
346 | if (!item->env && (item = insert_ob_in_ob (item, cauldron)) == NULL) |
349 | { |
347 | { |
350 | new_draw_info (NDI_UNIQUE, 0, caster, "Nothing happened."); |
348 | new_draw_info (NDI_UNIQUE, 0, caster, "Nothing happened."); |
351 | /* new_draw_info_format(NDI_UNIQUE, 0,caster, |
349 | /* new_draw_info_format(NDI_UNIQUE, 0,caster, |
352 | "Your spell causes the %s to explode!",&cauldron->name); */ |
350 | "Your spell causes the %s to explode!",&cauldron->name); */ |
… | |
… | |
389 | if (rndm (0, 2)) |
387 | if (rndm (0, 2)) |
390 | { /* slag created */ |
388 | { /* slag created */ |
391 | object *tmp = cauldron->inv; |
389 | object *tmp = cauldron->inv; |
392 | int weight = 0; |
390 | int weight = 0; |
393 | |
391 | |
394 | tmp = get_archetype ("rock"); |
392 | tmp = archetype::get (shstr_rock); |
395 | tmp->weight = weight; |
393 | tmp->weight = weight; |
396 | tmp->value = 0; |
394 | tmp->value = 0; |
397 | tmp->materialname = "stone"; |
395 | tmp->material = name_to_material (shstr_stone); |
398 | tmp->name = "slag"; |
396 | tmp->name = shstr_slag; |
399 | tmp->name_pl = "slags"; |
397 | tmp->name_pl = shstr_slags; |
400 | item = insert_ob_in_ob (tmp, cauldron); |
398 | item = insert_ob_in_ob (tmp, cauldron); |
401 | CLEAR_FLAG (tmp, FLAG_CAN_ROLL); |
399 | tmp->clr_flag (FLAG_CAN_ROLL); |
402 | SET_FLAG (tmp, FLAG_NO_DROP); |
400 | tmp->set_flag (FLAG_NO_DROP); |
403 | tmp->move_block = 0; |
401 | tmp->move_block = 0; |
404 | } |
402 | } |
405 | |
403 | |
406 | remove_contents (cauldron->inv, item); |
404 | remove_contents (cauldron->inv, item); |
407 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
405 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
… | |
… | |
415 | if ((rp = get_random_recipe ((recipelist *) NULL)) == NULL) |
413 | if ((rp = get_random_recipe ((recipelist *) NULL)) == NULL) |
416 | return; |
414 | return; |
417 | |
415 | |
418 | if ((tmp = attempt_recipe (op, cauldron, 1, rp, -1))) |
416 | if ((tmp = attempt_recipe (op, cauldron, 1, rp, -1))) |
419 | { |
417 | { |
420 | if (!QUERY_FLAG (tmp, FLAG_CURSED)) /* curse it */ |
418 | if (!tmp->flag [FLAG_CURSED]) /* curse it */ |
421 | SET_FLAG (tmp, FLAG_CURSED); |
419 | tmp->set_flag (FLAG_CURSED); |
422 | |
420 | |
423 | /* the apply code for potions already deals with cursed |
421 | /* the apply code for potions already deals with cursed |
424 | * potions, so any code here is basically ignored. |
422 | * potions, so any code here is basically ignored. |
425 | */ |
423 | */ |
426 | if (tmp->type == FOOD) |
424 | if (tmp->type == FOOD) |
… | |
… | |
445 | int numb = numb_ob_inside (cauldron); |
443 | int numb = numb_ob_inside (cauldron); |
446 | |
444 | |
447 | fl = get_formulalist (numb - 1); /* take a lower recipe list */ |
445 | fl = get_formulalist (numb - 1); /* take a lower recipe list */ |
448 | if (fl && (rp = get_random_recipe (fl))) |
446 | if (fl && (rp = get_random_recipe (fl))) |
449 | /* even though random, don't grant user any EXP for it */ |
447 | /* even though random, don't grant user any EXP for it */ |
450 | (void) attempt_recipe (op, cauldron, 1, rp, -1); |
448 | attempt_recipe (op, cauldron, 1, rp, -1); |
451 | else |
449 | else |
452 | alchemy_failure_effect (op, cauldron, rp, level - 1); |
450 | alchemy_failure_effect (op, cauldron, rp, level - 1); |
453 | } |
451 | } |
454 | else if (level < 45) |
452 | else if (level < 45) |
455 | { /* INFURIATE NPC's */ |
453 | { /* INFURIATE NPC's */ |
… | |
… | |
466 | |
464 | |
467 | remove_contents (cauldron->inv, NULL); |
465 | remove_contents (cauldron->inv, NULL); |
468 | switch (rndm (0, 2)) |
466 | switch (rndm (0, 2)) |
469 | { |
467 | { |
470 | case 0: |
468 | case 0: |
471 | tmp = get_archetype ("bomb"); |
469 | tmp = archetype::get (shstr_bomb); |
472 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW); |
470 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW); |
473 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW); |
471 | 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); |
472 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s creates a bomb!", &cauldron->name); |
475 | break; |
473 | break; |
476 | |
474 | |
477 | default: |
475 | default: |
478 | tmp = get_archetype ("fireball"); |
476 | tmp = archetype::get (shstr_fireball); |
479 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW) / 5 + 1; |
477 | 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; |
478 | 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); |
479 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
482 | break; |
480 | break; |
483 | } |
481 | } |
… | |
… | |
489 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
487 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
490 | remove_contents (cauldron->inv, NULL); |
488 | remove_contents (cauldron->inv, NULL); |
491 | } |
489 | } |
492 | else if (level < 80) |
490 | else if (level < 80) |
493 | { /* MAJOR FIRE */ |
491 | { /* MAJOR FIRE */ |
494 | object *fb = get_archetype (SP_MED_FIREBALL); |
492 | object *fb = archetype::get (SP_MED_FIREBALL); |
495 | |
493 | |
496 | remove_contents (cauldron->inv, NULL); |
494 | remove_contents (cauldron->inv, NULL); |
497 | fire_arch_from_position (cauldron, cauldron, cauldron->x, cauldron->y, 0, fb); |
495 | fire_arch_from_position (cauldron, cauldron, cauldron->x, cauldron->y, 0, fb); |
498 | fb->destroy (); |
496 | fb->destroy (); |
499 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
497 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
500 | } |
498 | } |
501 | else if (level < 100) |
499 | else if (level < 100) |
502 | { /* WHAMMY the CAULDRON */ |
500 | { /* WHAMMY the CAULDRON */ |
503 | if (!QUERY_FLAG (cauldron, FLAG_CURSED)) |
501 | if (!cauldron->flag [FLAG_CURSED]) |
504 | SET_FLAG (cauldron, FLAG_CURSED); |
502 | cauldron->set_flag (FLAG_CURSED); |
505 | else |
503 | else |
506 | cauldron->magic--; |
504 | cauldron->magic--; |
507 | |
505 | |
508 | cauldron->magic -= random_roll (0, 4, op, PREFER_LOW); |
506 | cauldron->magic -= random_roll (0, 4, op, PREFER_LOW); |
509 | |
507 | |
… | |
… | |
538 | } |
536 | } |
539 | else if (level == 151) |
537 | else if (level == 151) |
540 | { /* CREATE RANDOM ARTIFACT */ |
538 | { /* CREATE RANDOM ARTIFACT */ |
541 | object *tmp; |
539 | object *tmp; |
542 | |
540 | |
543 | /* this is meant to be better than prior possiblity, |
541 | /* this is meant to be better than prior possiblity, |
544 | * in this one, we allow *any* valid alchemy artifact |
542 | * in this one, we allow *any* valid alchemy artifact |
545 | * to be made (rather than only those on the given |
543 | * to be made (rather than only those on the given |
546 | * formulalist) */ |
544 | * formulalist) */ |
547 | if (!rp) |
545 | if (!rp) |
548 | rp = get_random_recipe ((recipelist *) NULL); |
546 | rp = get_random_recipe ((recipelist *) NULL); |
549 | |
547 | |
550 | if (rp && (tmp = get_archetype (rp->arch_name [rndm (rp->arch_names)]))) |
548 | if (rp && (tmp = archetype::get (rp->arch_name [rndm (rp->arch_names)]))) |
551 | { |
549 | { |
552 | generate_artifact (tmp, random_roll (1, op->level / 2 + 1, op, PREFER_HIGH) + 1); |
550 | generate_artifact (tmp, random_roll (1, op->level / 2 + 1, op, PREFER_HIGH) + 1); |
553 | if ((tmp = insert_ob_in_ob (tmp, cauldron))) |
551 | if ((tmp = insert_ob_in_ob (tmp, cauldron))) |
554 | { |
552 | { |
555 | remove_contents (cauldron->inv, tmp); |
553 | remove_contents (cauldron->inv, tmp); |
… | |
… | |
557 | } |
555 | } |
558 | } |
556 | } |
559 | } |
557 | } |
560 | else |
558 | else |
561 | { /* MANA STORM - watch out!! */ |
559 | { /* MANA STORM - watch out!! */ |
562 | object *tmp = get_archetype (LOOSE_MANA); |
560 | object *tmp = archetype::get (LOOSE_MANA); |
563 | |
561 | |
564 | new_draw_info (NDI_UNIQUE, 0, op, "You unwisely release potent forces!"); |
562 | new_draw_info (NDI_UNIQUE, 0, op, "You unwisely release potent forces!"); |
565 | remove_contents (cauldron->inv, NULL); |
563 | remove_contents (cauldron->inv, NULL); |
566 | cast_magic_storm (op, tmp, level); |
564 | cast_magic_storm (op, tmp, level); |
567 | } |
565 | } |
… | |
… | |
572 | * could be if the user fails to concoct a recipe properly. Factors include |
570 | * 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, |
571 | * 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 |
572 | * 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 |
573 | * 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 |
574 | * 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) |
575 | * skill *before* this routine is called. (no longer auto-readies that skill) |
578 | * -b.t. |
576 | * -b.t. |
579 | */ |
577 | */ |
580 | static int |
578 | static int |
581 | calc_alch_danger (object *caster, object *cauldron, recipe *rp) |
579 | calc_alch_danger (object *caster, object *cauldron, object *skill, recipe *rp) |
582 | { |
580 | { |
583 | object *item; |
|
|
584 | char name[MAX_BUF]; |
|
|
585 | int danger = 0, nrofi = 0; |
581 | int danger = 0; |
586 | |
582 | |
587 | /* Knowing alchemy skill reduces yer risk */ |
583 | /* Knowing alchemy skill reduces yer risk */ |
588 | danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level; |
584 | danger -= skill->level; |
589 | |
|
|
590 | 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 | |
585 | |
594 | /* better cauldrons reduce risk */ |
586 | /* better cauldrons reduce risk */ |
595 | danger -= cauldron->magic; |
587 | danger -= cauldron->magic; |
596 | |
588 | |
597 | /* Higher Int, lower the risk */ |
589 | /* Higher Int, lower the risk */ |
… | |
… | |
599 | |
591 | |
600 | /* Ingredients. Longer names usually mean rarer stuff. |
592 | /* Ingredients. Longer names usually mean rarer stuff. |
601 | * Thus the backfire is worse. Also, more ingredients |
593 | * Thus the backfire is worse. Also, more ingredients |
602 | * means we are attempting a more powerfull potion, |
594 | * means we are attempting a more powerfull potion, |
603 | * and thus the backfire will be worse. */ |
595 | * and thus the backfire will be worse. */ |
604 | for (item = cauldron->inv; item; item = item->below) |
596 | for (object *item = cauldron->inv; item; item = item->below) |
605 | { |
597 | { |
606 | assign (name, item->name); |
598 | const char *name = item->title |
607 | if (item->title) |
|
|
608 | sprintf (name, "%s %s", &item->name, &item->title); |
599 | ? format ("%s %s", &item->name, &item->title) |
|
|
600 | : &item->name; |
|
|
601 | |
609 | danger += (strtoint (name) / 1000) + 3; |
602 | danger += strtoint (name) / 1000 + 3; |
610 | nrofi++; |
|
|
611 | } |
603 | } |
612 | |
604 | |
613 | if (rp == NULL) |
605 | if (!rp) |
614 | danger += 110; |
606 | danger += 110; |
615 | else |
607 | else |
616 | danger += rp->diff * 3; |
608 | danger += rp->diff * 3; |
617 | |
609 | |
618 | /* Using a bad device is *majorly* stupid */ |
610 | /* Using a bad device is *majorly* stupid */ |
619 | if (QUERY_FLAG (cauldron, FLAG_CURSED)) |
611 | if (cauldron->flag [FLAG_CURSED]) danger += 80; |
620 | danger += 80; |
612 | if (cauldron->flag [FLAG_DAMNED]) danger += 200; |
621 | if (QUERY_FLAG (cauldron, FLAG_DAMNED)) |
|
|
622 | danger += 200; |
|
|
623 | |
613 | |
624 | #ifdef ALCHEMY_DEBUG |
614 | #ifdef ALCHEMY_DEBUG |
625 | LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger); |
615 | LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger); |
626 | #endif |
616 | #endif |
627 | |
617 | |
… | |
… | |
805 | return result; |
795 | return result; |
806 | } |
796 | } |
807 | |
797 | |
808 | /** |
798 | /** |
809 | * Main part of the ALCHEMY code. From this we call fctns |
799 | * 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, |
800 | * 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 |
801 | * 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). |
802 | * formula list (the formula list chosen is based on the # contents of the cauldron). |
813 | * |
803 | * |
814 | * If we get a match between the recipe indicated in cauldron contents and a |
804 | * 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 |
805 | * randomly chosen one, an item is created and experience awarded. Otherwise |
816 | * various failure effects are possible (getting worse and worse w/ # cauldron |
806 | * 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 |
807 | * 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. |
808 | * the artifacts list in lib/artifacts which has a recipe listed in lib/formulae. |
819 | * |
809 | * |
820 | * To those wondering why I am using the funky formula index method: |
810 | * To those wondering why I am using the funky formula index method: |
821 | * 1) I want to match recipe to ingredients regardless of ordering. |
811 | * 1) I want to match recipe to ingredients regardless of ordering. |
822 | * 2) I want a fast search for the 'right' recipe. |
812 | * 2) I want a fast search for the 'right' recipe. |
823 | * |
813 | * |
824 | * Note: it is just possible that a totally different combination of |
814 | * 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! |
815 | * 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 |
816 | * 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 |
817 | * unique -- such a 'feature' is one reason why players might want to experiment |
828 | * around. :) |
818 | * around. :) |
… | |
… | |
835 | recipe *rp = NULL; |
825 | recipe *rp = NULL; |
836 | float success_chance; |
826 | float success_chance; |
837 | int numb, ability = 1; |
827 | int numb, ability = 1; |
838 | int formula = 0; |
828 | int formula = 0; |
839 | float ave_chance; |
829 | float ave_chance; |
840 | object *item, *skop; |
830 | object *item; |
841 | |
831 | |
842 | if (caster->type != PLAYER) |
832 | if (caster->type != PLAYER) |
843 | return; /* only players for now */ |
833 | return; /* only players for now */ |
844 | |
834 | |
845 | if (get_map_flags (caster->map, NULL, caster->x, caster->y, NULL, NULL) & P_SAFE) |
835 | if (get_map_flags (caster->map, NULL, caster->x, caster->y, NULL, NULL) & P_SAFE) |
… | |
… | |
853 | return; |
843 | return; |
854 | |
844 | |
855 | numb = numb_ob_inside (cauldron); |
845 | numb = numb_ob_inside (cauldron); |
856 | if ((fl = get_formulalist (numb))) |
846 | if ((fl = get_formulalist (numb))) |
857 | { |
847 | { |
858 | if (QUERY_FLAG (caster, FLAG_WIZ)) |
848 | if (caster->flag [FLAG_WIZ]) |
859 | { |
849 | { |
860 | rp = find_recipe (fl, formula, cauldron->inv); |
850 | rp = find_recipe (fl, formula, cauldron->inv); |
861 | if (rp != NULL) |
851 | if (rp != NULL) |
862 | { |
852 | { |
863 | #ifdef ALCHEMY_DEBUG |
853 | #ifdef ALCHEMY_DEBUG |
… | |
… | |
883 | uint64 value_ingredients; |
873 | uint64 value_ingredients; |
884 | uint64 value_item; |
874 | uint64 value_item; |
885 | object *tmp; |
875 | object *tmp; |
886 | int attempt_shadow_alchemy; |
876 | int attempt_shadow_alchemy; |
887 | |
877 | |
888 | ave_chance = fl->total_chance / (float) fl->number; |
878 | ave_chance = fl->total_chance / (float)fl->number; |
889 | |
879 | |
890 | ability += (int) (skill->level * ((4.0 + cauldron->magic) / 4.0)); |
880 | ability += skill->level * ((4.0 + cauldron->magic) / 4.0); |
891 | |
881 | |
892 | /* determine value of ingredients */ |
882 | /* determine value of ingredients */ |
893 | value_ingredients = 0; |
883 | value_ingredients = 0; |
894 | for (tmp = cauldron->inv; tmp != NULL; tmp = tmp->below) |
884 | for (tmp = cauldron->inv; tmp; tmp = tmp->below) |
895 | value_ingredients += query_cost (tmp, NULL, F_TRUE); |
885 | value_ingredients += query_cost (tmp, NULL, F_TRUE); |
896 | |
886 | |
897 | attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster); |
887 | attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster); |
898 | |
888 | |
899 | /* create the object **FIRST**, then decide whether to keep it. */ |
889 | /* create the object **FIRST**, then decide whether to keep it. */ |
900 | if ((item = attempt_recipe (caster, cauldron, ability, rp, formula / rp->index)) != NULL) |
890 | if ((item = attempt_recipe (caster, cauldron, ability, rp, formula / rp->index)) != NULL) |
901 | { |
891 | { |
902 | /* compute base chance of recipe success */ |
892 | /* compute base chance of recipe success */ |
903 | success_chance = ((float) ability / (float) (rp->diff * (item->level + 2))); |
893 | success_chance = ((float)ability / (float)(rp->diff * (item->level + 2))); |
904 | if (ave_chance == 0) |
894 | if (ave_chance == 0) |
905 | ave_chance = 1; |
895 | ave_chance = 1; |
906 | |
896 | |
907 | #ifdef ALCHEMY_DEBUG |
897 | #ifdef ALCHEMY_DEBUG |
908 | LOG (llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level); |
898 | LOG (llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level); |
… | |
… | |
916 | "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", |
906 | "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", |
917 | value_ingredients, value_item); |
907 | value_ingredients, value_item); |
918 | #endif |
908 | #endif |
919 | } |
909 | } |
920 | /* roll the dice */ |
910 | /* roll the dice */ |
921 | else if ((float) (random_roll (0, 101, caster, PREFER_LOW)) <= 100.0 * success_chance) |
911 | else if (random_roll (0, 101, caster, PREFER_LOW) <= 100.0 * success_chance) |
922 | { |
912 | { |
923 | change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE); |
913 | change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE); |
924 | |
914 | |
925 | // let alchemy consume some time, so that exploits are less easy |
915 | // let alchemy consume some time, so that exploits are less easy |
926 | caster->speed_left -= 1.0; |
916 | caster->speed_left -= 1.0; |
… | |
… | |
930 | } |
920 | } |
931 | } |
921 | } |
932 | } |
922 | } |
933 | |
923 | |
934 | /* if we get here, we failed!! */ |
924 | /* if we get here, we failed!! */ |
935 | alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, rp)); |
925 | alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, skill, rp)); |
936 | } |
926 | } |
937 | |
927 | |