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 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. |
… | |
… | |
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 | int size = sizeof (cauldron_effect) / sizeof (char *); |
69 | |
69 | |
70 | return cauldron_effect[rndm (0, size - 1)]; |
70 | return cauldron_effect[rndm (0, size - 1)]; |
71 | } |
71 | } |
… | |
… | |
73 | /** |
73 | /** |
74 | * Recipe value of the entire contents of a container. |
74 | * Recipe value of the entire contents of a container. |
75 | * This appears to just generate a hash value, which I guess for now works |
75 | * This appears to just generate a hash value, which I guess for now works |
76 | * ok, but the possibility of duplicate hashes is certainly possible - msw |
76 | * ok, but the possibility of duplicate hashes is certainly possible - msw |
77 | */ |
77 | */ |
78 | int |
78 | static int |
79 | content_recipe_value (object *op) |
79 | content_recipe_value (object *op) |
80 | { |
80 | { |
81 | char name[MAX_BUF]; |
81 | char name[MAX_BUF]; |
82 | object *tmp = op->inv; |
82 | object *tmp = op->inv; |
83 | int tval = 0, formula = 0; |
83 | int tval = 0, formula = 0; |
… | |
… | |
102 | } |
102 | } |
103 | |
103 | |
104 | /** |
104 | /** |
105 | * Returns total number of items in op |
105 | * Returns total number of items in op |
106 | */ |
106 | */ |
107 | int |
107 | static int |
108 | numb_ob_inside (object *op) |
108 | numb_ob_inside (object *op) |
109 | { |
109 | { |
110 | object *tmp = op->inv; |
110 | object *tmp = op->inv; |
111 | int number = 0, o_number = 0; |
111 | int number = 0, o_number = 0; |
112 | |
112 | |
… | |
… | |
131 | * Otherwise the code returns a 'generic' item if create_item is set. -b.t. |
131 | * Otherwise the code returns a 'generic' item if create_item is set. -b.t. |
132 | * |
132 | * |
133 | * @param rp_arch_index pointer to return value; set to arch index for recipe; |
133 | * @param rp_arch_index pointer to return value; set to arch index for recipe; |
134 | * set to zero if not using a transmution formula |
134 | * set to zero if not using a transmution formula |
135 | */ |
135 | */ |
136 | object * |
136 | static object * |
137 | find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) |
137 | find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) |
138 | { |
138 | { |
139 | object *prod_item = 0; |
139 | object *prod_item = 0; |
140 | bool found = false; |
140 | bool found = false; |
141 | *rp_arch_index = 0; |
141 | *rp_arch_index = 0; |
… | |
… | |
192 | * |
192 | * |
193 | * @param rp the recipe to make the artifact from |
193 | * @param rp the recipe to make the artifact from |
194 | * |
194 | * |
195 | * @return the newly created object, NULL if something failed |
195 | * @return the newly created object, NULL if something failed |
196 | */ |
196 | */ |
197 | object * |
197 | static object * |
198 | make_item_from_recipe (object *cauldron, recipe *rp) |
198 | make_item_from_recipe (object *cauldron, recipe *rp) |
199 | { |
199 | { |
200 | artifact *art = NULL; |
200 | artifact *art = NULL; |
201 | object *item = NULL; |
201 | object *item = NULL; |
202 | size_t rp_arch_index; |
202 | size_t rp_arch_index; |
… | |
… | |
223 | |
223 | |
224 | transmute_materialname (item, art->item); |
224 | transmute_materialname (item, art->item); |
225 | give_artifact_abilities (item, art->item); |
225 | give_artifact_abilities (item, art->item); |
226 | } |
226 | } |
227 | |
227 | |
228 | if (QUERY_FLAG (cauldron, FLAG_CURSED)) |
228 | if (cauldron->flag [FLAG_CURSED]) |
229 | SET_FLAG (item, FLAG_CURSED); |
229 | item->set_flag (FLAG_CURSED); |
230 | if (QUERY_FLAG (cauldron, FLAG_DAMNED)) |
230 | if (cauldron->flag [FLAG_DAMNED]) |
231 | SET_FLAG (item, FLAG_DAMNED); |
231 | item->set_flag (FLAG_DAMNED); |
232 | |
232 | |
233 | return item; |
233 | return item; |
234 | } |
234 | } |
235 | |
235 | |
236 | /* |
236 | /* |
237 | * All but object "save_item" are elimentated from |
237 | * All but object "save_item" are elimentated from |
238 | * the container list. Note we have to becareful to remove the inventories |
238 | * the container list. Note we have to becareful to remove the inventories |
239 | * of objects in the cauldron inventory (ex icecube has stuff in it). |
239 | * of objects in the cauldron inventory (ex icecube has stuff in it). |
240 | */ |
240 | */ |
241 | void |
241 | static void |
242 | remove_contents (object *first_ob, object *save_item) |
242 | remove_contents (object *first_ob, object *save_item) |
243 | { |
243 | { |
244 | // this cries for a cleaner rewrite, removing save_item first possibly |
244 | // this cries for a cleaner rewrite, removing save_item first possibly |
245 | object *next, *tmp = first_ob; |
245 | object *next, *tmp = first_ob; |
246 | |
246 | |
… | |
… | |
266 | |
266 | |
267 | /** |
267 | /** |
268 | * We adjust the nrof, exp and level of the final product, based |
268 | * We adjust the nrof, exp and level of the final product, based |
269 | * on the item's default parameters, and the relevant caster skill level. |
269 | * on the item's default parameters, and the relevant caster skill level. |
270 | */ |
270 | */ |
271 | void |
271 | static void |
272 | adjust_product (object *item, int lvl, int yield) |
272 | adjust_product (object *item, int lvl, int yield) |
273 | { |
273 | { |
274 | int nrof = 1; |
274 | int nrof = 1; |
275 | |
275 | |
276 | if (!yield) |
276 | if (!yield) |
… | |
… | |
295 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
295 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
296 | * gain some exp from (successfull) fabrication of the product. |
296 | * gain some exp from (successfull) fabrication of the product. |
297 | * If nbatches==-1, don't give exp for this creation (random generation/ |
297 | * If nbatches==-1, don't give exp for this creation (random generation/ |
298 | * failed recipe) |
298 | * failed recipe) |
299 | */ |
299 | */ |
300 | object * |
300 | static object * |
301 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
301 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
302 | { |
302 | { |
303 | |
303 | |
304 | object *item = NULL, *skop; |
304 | object *item = NULL, *skop; |
305 | |
305 | |
… | |
… | |
364 | * This fctn simulates the backfire effects--worse effects as the level |
364 | * This fctn simulates the backfire effects--worse effects as the level |
365 | * increases. If SPELL_FAILURE_EFFECTS is defined some really evil things |
365 | * increases. If SPELL_FAILURE_EFFECTS is defined some really evil things |
366 | * can happen to the would be alchemist. This table probably needs some |
366 | * can happen to the would be alchemist. This table probably needs some |
367 | * adjustment for playbalance. -b.t. |
367 | * adjustment for playbalance. -b.t. |
368 | */ |
368 | */ |
369 | void |
369 | static void |
370 | alchemy_failure_effect (object *op, object *cauldron, recipe *rp, int danger) |
370 | alchemy_failure_effect (object *op, object *cauldron, recipe *rp, int danger) |
371 | { |
371 | { |
372 | int level = 0; |
372 | int level = 0; |
373 | |
373 | |
374 | if (!op || !cauldron) |
374 | if (!op || !cauldron) |
… | |
… | |
389 | if (rndm (0, 2)) |
389 | if (rndm (0, 2)) |
390 | { /* slag created */ |
390 | { /* slag created */ |
391 | object *tmp = cauldron->inv; |
391 | object *tmp = cauldron->inv; |
392 | int weight = 0; |
392 | int weight = 0; |
393 | |
393 | |
394 | tmp = get_archetype ("rock"); |
394 | tmp = get_archetype (shstr_rock); |
395 | tmp->weight = weight; |
395 | tmp->weight = weight; |
396 | tmp->value = 0; |
396 | tmp->value = 0; |
397 | tmp->materialname = "stone"; |
397 | tmp->material = name_to_material (shstr_stone); |
398 | tmp->name = "slag"; |
398 | tmp->name = shstr_slag; |
399 | tmp->name_pl = "slags"; |
399 | tmp->name_pl = shstr_slags; |
400 | item = insert_ob_in_ob (tmp, cauldron); |
400 | item = insert_ob_in_ob (tmp, cauldron); |
401 | CLEAR_FLAG (tmp, FLAG_CAN_ROLL); |
401 | tmp->clr_flag (FLAG_CAN_ROLL); |
402 | SET_FLAG (tmp, FLAG_NO_DROP); |
402 | tmp->set_flag (FLAG_NO_DROP); |
403 | tmp->move_block = 0; |
403 | tmp->move_block = 0; |
404 | } |
404 | } |
405 | |
405 | |
406 | remove_contents (cauldron->inv, item); |
406 | remove_contents (cauldron->inv, item); |
407 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
407 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
… | |
… | |
415 | if ((rp = get_random_recipe ((recipelist *) NULL)) == NULL) |
415 | if ((rp = get_random_recipe ((recipelist *) NULL)) == NULL) |
416 | return; |
416 | return; |
417 | |
417 | |
418 | if ((tmp = attempt_recipe (op, cauldron, 1, rp, -1))) |
418 | if ((tmp = attempt_recipe (op, cauldron, 1, rp, -1))) |
419 | { |
419 | { |
420 | if (!QUERY_FLAG (tmp, FLAG_CURSED)) /* curse it */ |
420 | if (!tmp->flag [FLAG_CURSED]) /* curse it */ |
421 | SET_FLAG (tmp, FLAG_CURSED); |
421 | tmp->set_flag (FLAG_CURSED); |
422 | |
422 | |
423 | /* the apply code for potions already deals with cursed |
423 | /* the apply code for potions already deals with cursed |
424 | * potions, so any code here is basically ignored. |
424 | * potions, so any code here is basically ignored. |
425 | */ |
425 | */ |
426 | if (tmp->type == FOOD) |
426 | if (tmp->type == FOOD) |
… | |
… | |
445 | int numb = numb_ob_inside (cauldron); |
445 | int numb = numb_ob_inside (cauldron); |
446 | |
446 | |
447 | fl = get_formulalist (numb - 1); /* take a lower recipe list */ |
447 | fl = get_formulalist (numb - 1); /* take a lower recipe list */ |
448 | if (fl && (rp = get_random_recipe (fl))) |
448 | if (fl && (rp = get_random_recipe (fl))) |
449 | /* even though random, don't grant user any EXP for it */ |
449 | /* even though random, don't grant user any EXP for it */ |
450 | (void) attempt_recipe (op, cauldron, 1, rp, -1); |
450 | attempt_recipe (op, cauldron, 1, rp, -1); |
451 | else |
451 | else |
452 | alchemy_failure_effect (op, cauldron, rp, level - 1); |
452 | alchemy_failure_effect (op, cauldron, rp, level - 1); |
453 | } |
453 | } |
454 | else if (level < 45) |
454 | else if (level < 45) |
455 | { /* INFURIATE NPC's */ |
455 | { /* INFURIATE NPC's */ |
… | |
… | |
466 | |
466 | |
467 | remove_contents (cauldron->inv, NULL); |
467 | remove_contents (cauldron->inv, NULL); |
468 | switch (rndm (0, 2)) |
468 | switch (rndm (0, 2)) |
469 | { |
469 | { |
470 | case 0: |
470 | case 0: |
471 | tmp = get_archetype ("bomb"); |
471 | tmp = get_archetype (shstr_bomb); |
472 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW); |
472 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW); |
473 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW); |
473 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW); |
474 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s creates a bomb!", &cauldron->name); |
474 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s creates a bomb!", &cauldron->name); |
475 | break; |
475 | break; |
476 | |
476 | |
477 | default: |
477 | default: |
478 | tmp = get_archetype ("fireball"); |
478 | tmp = get_archetype (shstr_fireball); |
479 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW) / 5 + 1; |
479 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW) / 5 + 1; |
480 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW) / 10 + 2; |
480 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW) / 10 + 2; |
481 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
481 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
482 | break; |
482 | break; |
483 | } |
483 | } |
… | |
… | |
498 | fb->destroy (); |
498 | fb->destroy (); |
499 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
499 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
500 | } |
500 | } |
501 | else if (level < 100) |
501 | else if (level < 100) |
502 | { /* WHAMMY the CAULDRON */ |
502 | { /* WHAMMY the CAULDRON */ |
503 | if (!QUERY_FLAG (cauldron, FLAG_CURSED)) |
503 | if (!cauldron->flag [FLAG_CURSED]) |
504 | SET_FLAG (cauldron, FLAG_CURSED); |
504 | cauldron->set_flag (FLAG_CURSED); |
505 | else |
505 | else |
506 | cauldron->magic--; |
506 | cauldron->magic--; |
507 | |
507 | |
508 | cauldron->magic -= random_roll (0, 4, op, PREFER_LOW); |
508 | cauldron->magic -= random_roll (0, 4, op, PREFER_LOW); |
509 | |
509 | |
… | |
… | |
575 | * mixing device (aka "cauldron"). Higher values of 'danger' indicate more |
575 | * mixing device (aka "cauldron"). Higher values of 'danger' indicate more |
576 | * danger. Note that we assume that we have had the caster ready the alchemy |
576 | * danger. Note that we assume that we have had the caster ready the alchemy |
577 | * skill *before* this routine is called. (no longer auto-readies that skill) |
577 | * skill *before* this routine is called. (no longer auto-readies that skill) |
578 | * -b.t. |
578 | * -b.t. |
579 | */ |
579 | */ |
580 | int |
580 | static int |
581 | calc_alch_danger (object *caster, object *cauldron, recipe *rp) |
581 | calc_alch_danger (object *caster, object *cauldron, recipe *rp) |
582 | { |
582 | { |
583 | object *item; |
583 | object *item; |
584 | char name[MAX_BUF]; |
584 | char name[MAX_BUF]; |
585 | int danger = 0, nrofi = 0; |
585 | int danger = 0, nrofi = 0; |
… | |
… | |
614 | danger += 110; |
614 | danger += 110; |
615 | else |
615 | else |
616 | danger += rp->diff * 3; |
616 | danger += rp->diff * 3; |
617 | |
617 | |
618 | /* Using a bad device is *majorly* stupid */ |
618 | /* Using a bad device is *majorly* stupid */ |
619 | if (QUERY_FLAG (cauldron, FLAG_CURSED)) |
619 | if (cauldron->flag [FLAG_CURSED]) |
620 | danger += 80; |
620 | danger += 80; |
621 | if (QUERY_FLAG (cauldron, FLAG_DAMNED)) |
621 | if (cauldron->flag [FLAG_DAMNED]) |
622 | danger += 200; |
622 | danger += 200; |
623 | |
623 | |
624 | #ifdef ALCHEMY_DEBUG |
624 | #ifdef ALCHEMY_DEBUG |
625 | LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger); |
625 | LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger); |
626 | #endif |
626 | #endif |
… | |
… | |
853 | return; |
853 | return; |
854 | |
854 | |
855 | numb = numb_ob_inside (cauldron); |
855 | numb = numb_ob_inside (cauldron); |
856 | if ((fl = get_formulalist (numb))) |
856 | if ((fl = get_formulalist (numb))) |
857 | { |
857 | { |
858 | if (QUERY_FLAG (caster, FLAG_WIZ)) |
858 | if (caster->flag [FLAG_WIZ]) |
859 | { |
859 | { |
860 | rp = find_recipe (fl, formula, cauldron->inv); |
860 | rp = find_recipe (fl, formula, cauldron->inv); |
861 | if (rp != NULL) |
861 | if (rp != NULL) |
862 | { |
862 | { |
863 | #ifdef ALCHEMY_DEBUG |
863 | #ifdef ALCHEMY_DEBUG |
… | |
… | |
883 | uint64 value_ingredients; |
883 | uint64 value_ingredients; |
884 | uint64 value_item; |
884 | uint64 value_item; |
885 | object *tmp; |
885 | object *tmp; |
886 | int attempt_shadow_alchemy; |
886 | int attempt_shadow_alchemy; |
887 | |
887 | |
888 | ave_chance = fl->total_chance / (float) fl->number; |
888 | ave_chance = fl->total_chance / (float)fl->number; |
889 | |
889 | |
890 | ability += (int) (skill->level * ((4.0 + cauldron->magic) / 4.0)); |
890 | ability += skill->level * ((4.0 + cauldron->magic) / 4.0); |
891 | |
891 | |
892 | /* determine value of ingredients */ |
892 | /* determine value of ingredients */ |
893 | value_ingredients = 0; |
893 | value_ingredients = 0; |
894 | for (tmp = cauldron->inv; tmp != NULL; tmp = tmp->below) |
894 | for (tmp = cauldron->inv; tmp; tmp = tmp->below) |
895 | value_ingredients += query_cost (tmp, NULL, F_TRUE); |
895 | value_ingredients += query_cost (tmp, NULL, F_TRUE); |
896 | |
896 | |
897 | attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster); |
897 | attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster); |
898 | |
898 | |
899 | /* create the object **FIRST**, then decide whether to keep it. */ |
899 | /* create the object **FIRST**, then decide whether to keep it. */ |
… | |
… | |
916 | "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", |
916 | "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", |
917 | value_ingredients, value_item); |
917 | value_ingredients, value_item); |
918 | #endif |
918 | #endif |
919 | } |
919 | } |
920 | /* roll the dice */ |
920 | /* roll the dice */ |
921 | else if ((float) (random_roll (0, 101, caster, PREFER_LOW)) <= 100.0 * success_chance) |
921 | else if (random_roll (0, 101, caster, PREFER_LOW) <= 100.0 * success_chance) |
922 | { |
922 | { |
923 | change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE); |
923 | change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE); |
924 | |
924 | |
925 | // let alchemy consume some time, so that exploits are less easy |
925 | // let alchemy consume some time, so that exploits are less easy |
926 | caster->speed_left -= 1.0; |
926 | caster->speed_left -= 1.0; |