Crossfire Server, Trunk  R21226
alchemy.c
Go to the documentation of this file.
1 /*
2  * Crossfire -- cooperative multi-player graphical RPG and adventure game
3  *
4  * Copyright (c) 1999-2014 Mark Wedel and the Crossfire Development Team
5  * Copyright (c) 1992 Frank Tore Johansen
6  *
7  * Crossfire is free software and comes with ABSOLUTELY NO WARRANTY. You are
8  * welcome to redistribute it under certain conditions. For details, please
9  * see COPYING and LICENSE.
10  *
11  * The authors can be reached via e-mail at <crossfire@metalforge.org>.
12  */
13 
14 /* March 96 - Laid down original code. -b.t. thomas@astro.psu.edu */
15 
21 #include "global.h"
22 
23 #include <assert.h>
24 #include <ctype.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "object.h"
29 #include "shop.h"
30 #include "skills.h"
31 #include "spells.h"
32 #include "sproto.h"
33 
35 #if 0
36 #define ALCHEMY_DEBUG
37 #endif
38 
40 #if 0
41 #define EXTREME_ALCHEMY_DEBUG
42 #endif
43 
45 static const char *const cauldron_effect [] = {
46  "vibrates briefly",
47  "produces a cloud of steam",
48  "emits bright flames",
49  "pours forth heavy black smoke",
50  "emits sparks",
51  "shoots out small flames",
52  "whines painfully",
53  "hiccups loudly",
54  "wheezes",
55  "burps",
56  "shakes",
57  "rattles",
58  "makes chugging sounds",
59  "smokes heavily for a while"
60 };
61 
62 
63 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster);
64 static const recipe *find_recipe(const recipelist *fl, int formula, object *ingredients);
65 static int content_recipe_value(object *op);
66 static int numb_ob_inside(const object *op);
67 static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger);
68 static object *attempt_recipe(object *caster, object *cauldron, int ability, const recipe *rp, int nbatches, int ignore_cauldron);
69 static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp);
70 static object *make_item_from_recipe(object *cauldron, const recipe *rp);
71 static void remove_contents(object *first_ob, object *save_item);
72 static void adjust_product(object *item, int lvl, int yield);
73 static object *find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item);
74 static void attempt_do_alchemy(object *caster, object *cauldron);
75 
77 static const char *cauldron_sound(void) {
78  int size = sizeof(cauldron_effect)/sizeof(char *);
79 
80  return cauldron_effect[rndm(0, size-1)];
81 }
82 
105 static float chance_fn(int diff) {
106  if (diff > 10)
107  return MAX(.01, .3 - (diff - 10) * .03);
108 
109  if (diff > -10)
110  return .5 + .02 * (float)(-diff);
111 
112  return MIN(.95, .70 + (-diff - 10) * .01);
113 }
114 
115 static float recipe_chance(const recipe *rp, const object *skill, const object *cauldron) {
116  assert(rp);
117  assert(skill);
118  assert(cauldron);
119 
120  const int cauldron_add_skill = (cauldron->magic + 1) / 2;
121  const int eff_skill = skill->level + cauldron_add_skill;
122  return chance_fn(rp->diff - eff_skill);
123 }
124 
153 static void attempt_do_alchemy(object *caster, object *cauldron) {
154  const recipelist *fl;
155  const recipe *rp = NULL;
156  float success_chance;
157  int numb, ability = 1;
158  int formula = 0;
159  object *item, *skop;
160 
161  if (caster->type != PLAYER)
162  return; /* only players for now */
163 
164  /* if no ingredients, no formula! lets forget it */
165  if (!(formula = content_recipe_value(cauldron))) {
167  "The %s is empty.",
168  cauldron->name);
169  return;
170  }
171 
172  numb = numb_ob_inside(cauldron);
173  if ((fl = get_formulalist(numb))) {
174  if (QUERY_FLAG(caster, FLAG_WIZ)) {
175  rp = find_recipe(fl, formula, cauldron->inv);
176  if (rp != NULL) {
177 #ifdef ALCHEMY_DEBUG
178  if (strcmp(rp->title, "NONE"))
179  LOG(llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], rp->title);
180  else
181  LOG(llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula/rp->index);
182 #endif
183  attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, !is_defined_recipe(rp, cauldron, caster));
184  } else
185  LOG(llevDebug, "WIZ couldn't find formula for ingredients.\n");
186  return;
187  } /* End of WIZ alchemy */
188 
189  /* find the recipe */
190  rp = find_recipe(fl, formula, cauldron->inv);
191  if (rp != NULL) {
192  uint64_t value_ingredients;
193  uint64_t value_item;
194  int attempt_shadow_alchemy;
195 
196  /* the caster gets an increase in ability based on thier skill lvl */
197  if (rp->skill != NULL) {
198  skop = find_skill_by_name(caster, rp->skill);
199  if (!skop) {
201  "You do not have the proper skill for this recipe");
202  } else {
203  ability += skop->level*((4.0+cauldron->magic)/4.0);
204  }
205  } else {
206  LOG(llevDebug, "Recipe %s has NULL skill!\n", rp->title);
207  return;
208  }
209 
210  if (rp->cauldron == NULL) {
211  LOG(llevDebug, "Recipe %s has NULL cauldron!\n", rp->title);
212  return;
213  }
214 
215  if (rp->min_level != 0 && skop->level < rp->min_level) {
217  "You aren't skilled enough to try this recipe.");
218  return;
219  }
220 
221  /* determine value of ingredients */
222  value_ingredients = 0;
223  FOR_INV_PREPARE(cauldron, tmp)
224  value_ingredients += price_base(tmp);
225  FOR_INV_FINISH();
226 
227  attempt_shadow_alchemy = !is_defined_recipe(rp, cauldron, caster);
228 
229  /* create the object **FIRST**, then decide whether to keep it. */
230  if ((item = attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, attempt_shadow_alchemy)) != NULL) {
231  /* compute base chance of recipe success */
232  success_chance = recipe_chance(rp, skop, cauldron);
233 
234 #ifdef ALCHEMY_DEBUG
235  LOG(llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level);
236 #endif
237 
238  value_item = price_base(item);
239  if (attempt_shadow_alchemy && value_item > value_ingredients) {
240 #ifdef ALCHEMY_DEBUG
241 #ifndef WIN32
242  LOG(llevDebug, "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", value_ingredients, value_item);
243 #else
244  LOG(llevDebug, "Forcing failure for shadow alchemy recipe because price of ingredients (%I64d) is less than price of result (%I64d).\n", value_ingredients, value_item);
245 #endif
246 #endif
247  /* roll the dice */
248  } else if (random_roll(0, 101, caster, PREFER_LOW) <= 100.0*success_chance) {
249  change_exp(caster, rp->exp, rp->skill, SK_EXP_NONE);
250  return;
251  }
252  }
253  }
254  }
255  /* if we get here, we failed!! */
256  alchemy_failure_effect(caster, cauldron, rp, calc_alch_danger(caster, cauldron, rp));
257 }
258 
269 static int content_recipe_value(object *op) {
270  char name[MAX_BUF];
271  int tval = 0, formula = 0;
272 
273  FOR_INV_PREPARE(op, tmp) {
274  tval = 0;
275  safe_strncpy(name, tmp->name, sizeof(name));
276  if (tmp->title)
277  snprintf(name, sizeof(name), "%s %s", tmp->name, tmp->title);
278  tval = (strtoint(name)*(tmp->nrof ? tmp->nrof : 1));
279 #ifdef ALCHEMY_DEBUG
280  LOG(llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval);
281 #endif
282  formula += tval;
283  } FOR_INV_FINISH();
284 #ifdef ALCHEMY_DEBUG
285  LOG(llevDebug, " Formula value=%d\n", formula);
286 #endif
287  return formula;
288 }
289 
297 static int numb_ob_inside(const object *op) {
298  int o_number = 0;
299 
300  FOR_INV_PREPARE(op, tmp)
301  o_number++;
302  FOR_INV_FINISH();
303 #ifdef ALCHEMY_DEBUG
304  LOG(llevDebug, "numb_ob_inside(%s): found %d ingredients\n", op->name, o_number);
305 #endif
306  return o_number;
307 }
308 
334 static object *attempt_recipe(object *caster, object *cauldron, int ability, const recipe *rp, int nbatches, int ignore_cauldron) {
335  object *item = NULL, *skop;
336  /* this should be passed to this fctn, not effiecent cpu use this way */
337  int batches = abs(nbatches);
338 
339  /* is the cauldron the right type? */
340  if (!ignore_cauldron && (strcmp(rp->cauldron, cauldron->arch->name) != 0)) {
342  "You are not using the proper facilities for this formula.");
343  return NULL;
344  }
345 
346  skop = find_skill_by_name(caster, rp->skill);
347  /* does the caster have the skill? */
348  if (!skop)
349  return NULL;
350 
351  /* code required for this recipe, search the caster */
352  if (rp->keycode) {
353  object *tmp;
354 
355  tmp = object_find_by_type_and_slaying(caster, FORCE, rp->keycode);
356  if (tmp == NULL) { /* failure--no code found */
358  "You know the ingredients, but not the technique. Go learn how to do this recipe.");
359  return NULL;
360  }
361  }
362 
363 #ifdef EXTREME_ALCHEMY_DEBUG
364  LOG(llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
365  LOG(llevDebug, "attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown");
366 #endif
367 
368  if ((item = make_item_from_recipe(cauldron, rp)) != NULL) {
369  remove_contents(cauldron->inv, item);
370  /* adj lvl, nrof on caster level */
371  adjust_product(item, ability, rp->yield ? (rp->yield*batches) : batches);
372 
373  if (item->type == POTION) {
374  item->level = MAX(item->level, skop->level);
375  }
376 
377  if (!item->env && (item = object_insert_in_ob(item, cauldron)) == NULL) {
379  "Nothing happened.");
380  } else {
381  draw_ext_info_format(NDI_UNIQUE, 0, caster,
383  "The %s %s.",
384  cauldron->name, cauldron_sound());
385  }
386  }
387  return item;
388 }
389 
400 static void adjust_product(object *item, int adjust, int yield) {
401  int nrof = 1;
402 
403  if (!yield)
404  yield = 1;
405  if (adjust <= 0)
406  adjust = 1; /* lets avoid div by zero! */
407  if (item->nrof) {
408  nrof = (1.0-1.0/(adjust/10.0+1.0))*(rndm(0, yield-1)+rndm(0, yield-1)+rndm(0, yield-1))+1;
409  if (nrof > yield)
410  nrof = yield;
411  item->nrof = nrof;
412  }
413 }
414 
425 static object *make_item_from_recipe(object *cauldron, const recipe *rp) {
426  const artifact *art = NULL;
427  object *item = NULL;
428  size_t rp_arch_index;
429 
430  if (rp == NULL)
431  return (object *)NULL;
432 
433  /* Find the appropriate object to transform...*/
434  if ((item = find_transmution_ob(cauldron->inv, rp, &rp_arch_index, 1)) == NULL) {
435  LOG(llevDebug, "make_alchemy_item(): failed to create alchemical object.\n");
436  return (object *)NULL;
437  }
438 
439  /* If item is already in container, we need to remove its weight, since it can change later on. */
440  if (item->env != NULL)
441  object_sub_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
442 
443  /* Find the appropriate artifact template...*/
444  if (strcmp(rp->title, "NONE")) {
445  if ((art = locate_recipe_artifact(rp, rp_arch_index)) == NULL) {
446  LOG(llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
447  LOG(llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], rp->title);
448  return (object *)NULL;
449  }
450  transmute_materialname(item, art->item);
451  give_artifact_abilities(item, art->item);
452  if (need_identify(item) && QUERY_FLAG(item, FLAG_IDENTIFIED)) {
453  /* artifacts are generated unidentified, so if the item is identified we must give it its real properties. */
455  }
456  }
457  if (item->env != NULL)
458  object_add_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
459 
460  if (QUERY_FLAG(cauldron, FLAG_CURSED))
461  SET_FLAG(item, FLAG_CURSED);
462  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
463  SET_FLAG(item, FLAG_DAMNED);
464 
465  return item;
466 }
467 
481 static object *find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item) {
482  object *item = NULL;
483 
484  *rp_arch_index = 0;
485 
486  if (rp->transmute) { /* look for matching ingredient/prod archs */
487  item = first_ingred;
489  size_t i;
490 
491  for (i = 0; i < rp->arch_names; i++) {
492  if (strcmp(item->arch->name, rp->arch_name[i]) == 0) {
493  *rp_arch_index = i;
494  break;
495  }
496  }
497  if (i < rp->arch_names)
498  break;
500  }
501 
502  /* failed, create a fresh object. Note no nrof>1 because that would
503  * allow players to create massive amounts of artifacts easily */
504  if (create_item && (!item || item->nrof > 1)) {
505  *rp_arch_index = RANDOM()%rp->arch_names;
506  item = create_archetype(rp->arch_name[*rp_arch_index]);
507  }
508 
509 #ifdef ALCHEMY_DEBUG
510  LOG(llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
511  if (item != NULL) {
512  LOG(llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp);
513  }
514 #endif
515 
516  return item;
517 }
518 
535 static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger) {
536  int level = 0;
537 
538  if (!op || !cauldron)
539  return;
540 
542  if (rp && rp->failure_arch) {
543  object *failure = create_archetype(rp->failure_arch);
544  if (!failure) {
545  LOG(llevError, "invalid failure_arch %s for recipe %s\n", rp->failure_arch, rp->title);
546  return;
547  }
548 
549  remove_contents(cauldron->inv, NULL);
550 
551  object_insert_in_ob(failure, cauldron);
552  if (rp->failure_message) {
554  }
555  return;
556  }
557 
558  if (danger > 1)
559  level = random_roll(1, danger, op, PREFER_LOW);
560 
561 #ifdef ALCHEMY_DEBUG
562  LOG(llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
563 #endif
564 
565  /* possible outcomes based on level */
566  if (level < 25) { /* INGREDIENTS USED/SLAGGED */
567  object *item = NULL;
568 
569  if (rndm(0, 2)) { /* slag created */
570  object *tmp;
571  int weight = 0;
572  uint16_t material = M_STONE;
573 
574  FOR_INV_PREPARE(cauldron, tmp) { /* slag has coadded ingredient properties */
575  weight += tmp->weight;
576  if (!(material&tmp->material))
577  material |= tmp->material;
578  } FOR_INV_FINISH();
579  tmp = create_archetype("rock");
580  tmp->weight = weight;
581  tmp->value = 0;
582  tmp->material = material;
583  tmp->materialname = add_string("stone");
584  free_string(tmp->name);
585  tmp->name = add_string("slag");
586  if (tmp->name_pl)
587  free_string(tmp->name_pl);
588  tmp->name_pl = add_string("slags");
589  item = object_insert_in_ob(tmp, cauldron);
591  CLEAR_FLAG(tmp, FLAG_NO_PICK);
592  tmp->move_block = 0;
593  }
594  remove_contents(cauldron->inv, item);
596  "The %s %s.",
597  cauldron->name, cauldron_sound());
598  return;
599  } else if (level < 40) { /* MAKE TAINTED ITEM */
600  object *tmp = NULL;
601 
602  /*
603  * Note by Nicolas Weeger 2010-09-26
604  * This is an incorrect part.
605  * Calling again attempt_recipe in case of failure will apply again the artifact
606  * combination to the item.
607  * This leads to items with eg 100% resist, or more.
608  * So use the actual item in the cauldron, don't retry the recipe.
609  * This should fix bug #2020224: buggy(?) crafting yields.
610  *
611  if (!rp)
612  if ((rp = get_random_recipe((recipelist *)NULL)) == NULL)
613  return;
614  */
615 
616  if ((tmp = cauldron->inv)) /*attempt_recipe(op, cauldron, 1, rp, -1, 0)))*/ {
617  if (!QUERY_FLAG(tmp, FLAG_CURSED)) { /* curse it */
618  SET_FLAG(tmp, FLAG_CURSED);
621  }
622 
623  /* the apply code for potions already deals with cursed
624  * potions, so any code here is basically ignored.
625  */
626  if (tmp->type == FOOD) {
627  tmp->stats.hp = random_roll(0, 149, op, PREFER_LOW);
628  }
629  tmp->value = 0; /* unsaleable item */
630 
631  /* change stats downward */
632  do {
633  change_attr_value(&tmp->stats, rndm(0, 6), -1*(rndm(1, 3)));
634  } while (rndm(0, 2));
635  }
636  return;
637  }
638 #if 0
639  /*
640  Note: this does not work as expected...
641  At this point there is only one item in the cauldron, and get_formulalist(0) will return
642  the first formula list for recipes with 1 ingredient.
643  So disable this, and just use the next case.
644  */
645 
646  if (level == 40) { /* MAKE RANDOM RECIPE */
647  recipelist *fl;
648  int numb = numb_ob_inside(cauldron);
649 
650  fl = get_formulalist(numb-1); /* take a lower recipe list */
651  if (fl && (rp = get_random_recipe(fl)))
652  /* even though random, don't grant user any EXP for it */
653  (void)attempt_recipe(op, cauldron, 1, rp, -1, 0);
654  else
655  alchemy_failure_effect(op, cauldron, rp, level-1);
656  return;
657 
658  } else
659 #endif
660  if (level < 45) { /* INFURIATE NPC's */
661  /* this is kind of kludgy I know...*/
662  object_set_enemy(cauldron, op);
663  monster_npc_call_help(cauldron);
664  object_set_enemy(cauldron, NULL);
665 
666  alchemy_failure_effect(op, cauldron, rp, level-5);
667  return;
668  } else if (level < 50) { /* MINOR EXPLOSION/FIREBALL */
669  object *tmp;
670 
671  remove_contents(cauldron->inv, NULL);
672  switch (rndm(0, 2)) {
673  case 0:
674  tmp = create_archetype("bomb");
675  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW);
676  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW);
678  "The %s creates a bomb!",
679  cauldron->name);
680  break;
681 
682  default:
683  tmp = create_archetype("fireball");
684  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW)/5+1;
685  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW)/10+2;
687  "The %s erupts in flame!",
688  cauldron->name);
689  break;
690  }
691  object_insert_in_map_at(tmp, op->map, NULL, 0, cauldron->x, cauldron->y);
692  return;
693  } else if (level < 60) { /* CREATE MONSTER */
695  "The %s %s.", cauldron->name, cauldron_sound());
696  remove_contents(cauldron->inv, NULL);
697  return;
698  } else if (level < 80) { /* MAJOR FIRE */
699  object *fb = create_archetype(SP_MED_FIREBALL);
700 
701  remove_contents(cauldron->inv, NULL);
702  fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, fb);
705  "The %s erupts in flame!",
706  cauldron->name);
707  return;
708  } else if (level < 100) { /* WHAMMY the CAULDRON */
709  if (!QUERY_FLAG(cauldron, FLAG_CURSED)) {
710  SET_FLAG(cauldron, FLAG_CURSED);
711  CLEAR_FLAG(cauldron, FLAG_KNOWN_CURSED);
712  CLEAR_FLAG(cauldron, FLAG_IDENTIFIED);
713  } else
714  cauldron->magic--;
715  cauldron->magic -= random_roll(0, 4, op, PREFER_LOW);
716  if (rndm(0, 1)) {
717  remove_contents(cauldron->inv, NULL);
719  "Your %s turns darker then makes a gulping sound!",
720  cauldron->name);
721  } else
723  "Your %s becomes darker.",
724  cauldron->name);
725  return;
726  } else if (level < 110) { /* SUMMON EVIL MONSTERS */
727  object *tmp = get_random_mon(level/5);
728 
729  remove_contents(cauldron->inv, NULL);
730  if (!tmp)
731  alchemy_failure_effect(op, cauldron, rp, level);
732  else if (summon_hostile_monsters(cauldron, random_roll(1, 10, op, PREFER_LOW), tmp->arch->name))
734  "The %s %s and then pours forth monsters!",
735  cauldron->name, cauldron_sound());
736  return;
737  } else if (level < 150) { /* COMBO EFFECT */
738  int roll = rndm(1, 3);
739  while (roll) {
740  alchemy_failure_effect(op, cauldron, rp, level-39);
741  roll--;
742  }
743  return;
744  } else if (level == 151) { /* CREATE RANDOM ARTIFACT */
745  object *tmp;
746 
747  /* this is meant to be better than prior possiblity,
748  * in this one, we allow *any *valid alchemy artifact
749  * to be made (rather than only those on the given
750  * formulalist) */
751  if (!rp)
752  rp = get_random_recipe((recipelist *)NULL);
753  if (rp && (tmp = create_archetype(rp->arch_name[RANDOM()%rp->arch_names]))) {
754  generate_artifact(tmp, random_roll(1, op->level/2+1, op, PREFER_HIGH)+1);
755  if ((tmp = object_insert_in_ob(tmp, cauldron))) {
756  remove_contents(cauldron->inv, tmp);
759  "The %s %s.",
760  cauldron->name, cauldron_sound());
761  }
762  }
763  return;
764  } else { /* MANA STORM - watch out!! */
765  object *tmp = create_archetype(LOOSE_MANA);
767  "You unwisely release potent forces!");
768  remove_contents(cauldron->inv, NULL);
769  cast_magic_storm(op, tmp, level);
770  return;
771  }
772 }
773 
783 static void remove_contents(object *first_ob, object *save_item) {
784  object *tmp;
785 
786  tmp = first_ob;
788  if (tmp != save_item) {
789  if (tmp->inv)
790  remove_contents(tmp->inv, NULL);
791  object_remove(tmp);
793  }
795 }
796 
816 static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp) {
817  int danger = 0;
818 
819  /* Knowing alchemy skill reduces yer risk */
820  danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
821 
822  /* better cauldrons reduce risk */
823  danger -= cauldron->magic;
824 
825  /* Higher Int, lower the risk */
826  danger -= 3*(caster->stats.Int-15);
827 
828  /* Ingredients. */
829  FOR_INV_PREPARE(cauldron, item) {
830  /* Nicolas Weeger: no reason why this is the case.
831  danger += item->weight / 100;
832  */
833  danger++;
834  if (QUERY_FLAG(item, FLAG_CURSED) || QUERY_FLAG(item, FLAG_DAMNED))
835  danger += 5;
836  } FOR_INV_FINISH();
837 
838  if (rp == NULL)
839  danger += 110;
840  else
841  danger += rp->diff*3;
842 
843  /* Using a bad device is *majorly *stupid */
844  if (QUERY_FLAG(cauldron, FLAG_CURSED))
845  danger += 80;
846  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
847  danger += 200;
848 
849 #ifdef ALCHEMY_DEBUG
850  LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
851 #endif
852 
853  return danger;
854 }
855 
875 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster) {
876  uint32_t batches_in_cauldron;
877  const linked_char *ingredient;
878  int number;
879 
880  /* check for matching number of ingredients */
881  number = 0;
882  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
883  number++;
884  FOR_INV_PREPARE(cauldron, ob)
885  number--;
886  FOR_INV_FINISH();
887  if (number != 0)
888  return 0;
889 
890  /* check for matching ingredients */
891  batches_in_cauldron = 0;
892  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next) {
893  uint32_t nrof;
894  const char *name;
895  int ok;
896 
897  /* determine and remove nrof from name */
898  name = ingredient->name;
899  nrof = 0;
900  while (isdigit(*name)) {
901  nrof = 10*nrof+(*name-'0');
902  name++;
903  }
904  if (nrof == 0)
905  nrof = 1;
906  while (*name == ' ')
907  name++;
908 
909  /* find the current ingredient in the cauldron */
910  ok = 0;
911  FOR_INV_PREPARE(cauldron, ob) {
912  char name_ob[MAX_BUF];
913  const char *name2;
914 
915  if (ob->title == NULL)
916  name2 = ob->name;
917  else {
918  snprintf(name_ob, sizeof(name_ob), "%s %s", ob->name, ob->title);
919  name2 = name_ob;
920  }
921 
922  if (strcmp(name2, name) == 0) {
923  if (ob->nrof%nrof == 0) {
924  uint32_t batches;
925 
926  batches = ob->nrof/nrof;
927  if (batches_in_cauldron == 0) {
928  batches_in_cauldron = batches;
929  ok = 1;
930  } else if (batches_in_cauldron == batches)
931  ok = 1;
932  }
933  break;
934  }
935  } FOR_INV_FINISH();
936  if (!ok)
937  return(0);
938  }
939 
940  return(1);
941 }
942 
957 static const recipe *find_recipe(const recipelist *fl, int formula, object *ingredients) {
958  const recipe *rp;
959  const recipe *result; /* winning recipe, or NULL if no recipe found */
960  int recipes_matching; /* total number of matching recipes so far */
961  int transmute_found; /* records whether a transmuting recipe was found so far */
962  size_t rp_arch_index;
963 
964 #ifdef EXTREME_ALCHEMY_DEBUG
965  LOG(llevDebug, "looking for formula %d:\n", formula);
966 #endif
967  result = NULL;
968  recipes_matching = 0;
969  transmute_found = 0;
970  for (rp = fl->items; rp != NULL; rp = rp->next) {
971  /* check if recipe matches at all */
972  if (formula%rp->index != 0) {
973 #ifdef EXTREME_ALCHEMY_DEBUG
974  LOG(llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index);
975 #endif
976  continue;
977  }
978 
979  if (rp->transmute && find_transmution_ob(ingredients, rp, &rp_arch_index, 0) != NULL) {
980 #ifdef EXTREME_ALCHEMY_DEBUG
981  LOG(llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index);
982 #endif
983  /* transmution recipe with matching base ingredient */
984  if (!transmute_found) {
985  transmute_found = 1;
986  recipes_matching = 0;
987  }
988  } else if (transmute_found) {
989 #ifdef EXTREME_ALCHEMY_DEBUG
990  LOG(llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, rp->index);
991 #endif
992  /* "normal" recipe found after previous transmution recipe => ignore this recipe */
993  continue;
994  }
995 #ifdef EXTREME_ALCHEMY_DEBUG
996  else {
997  LOG(llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index);
998  }
999 #endif
1000 
1001  if (rndm(0, recipes_matching) == 0)
1002  result = rp;
1003 
1004  recipes_matching++;
1005  }
1006 
1007  if (result == NULL) {
1008 #ifdef ALCHEMY_DEBUG
1009  LOG(llevDebug, "couldn't find formula for ingredients.\n");
1010 #endif
1011  return NULL;
1012  }
1013 
1014 #ifdef ALCHEMY_DEBUG
1015  if (strcmp(result->title, "NONE") != 0)
1016  LOG(llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula/result->index);
1017  else
1018  LOG(llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula/result->index);
1019 #endif
1020  return result;
1021 }
1022 
1034 int use_alchemy(object *op) {
1035  object *unpaid_cauldron = NULL;
1036  object *unpaid_item = NULL;
1037  int did_alchemy = 0;
1038  char name[MAX_BUF];
1039 
1040  if (QUERY_FLAG(op, FLAG_WIZ))
1041  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, "Note: alchemy in wizard-mode.\n");
1042 
1043  FOR_MAP_PREPARE(op->map, op->x, op->y, tmp) {
1044  if (QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) {
1045  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1046  unpaid_cauldron = tmp;
1047  continue;
1048  }
1049  unpaid_item = object_find_by_flag(tmp, FLAG_UNPAID);
1050  if (unpaid_item != NULL)
1051  continue;
1052 
1053  attempt_do_alchemy(op, tmp);
1054  if (QUERY_FLAG(tmp, FLAG_APPLIED))
1055  esrv_send_inventory(op, tmp);
1056  did_alchemy = 1;
1057  }
1058  } FOR_MAP_FINISH();
1059  if (unpaid_cauldron) {
1060  query_base_name(unpaid_cauldron, 0, name, MAX_BUF);
1062  "You must pay for your %s first!",
1063  name);
1064  } else if (unpaid_item) {
1065  query_base_name(unpaid_item, 0, name, MAX_BUF);
1067  "You must pay for your %s first!",
1068  name);
1069  }
1070 
1071  return did_alchemy;
1072 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:315
int8_t Int
Definition: living.h:36
#define M_STONE
Definition: material.h:20
#define FLAG_DAMNED
Definition: define.h:318
static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger)
Definition: alchemy.c:535
#define FLAG_UNPAID
Definition: define.h:236
int diff
Definition: recipe.h:16
static void attempt_do_alchemy(object *caster, object *cauldron)
Definition: alchemy.c:153
void object_give_identified_properties(object *op)
Definition: item.c:1370
#define SET_FLAG(xyz, p)
Definition: define.h:223
void object_sub_weight(object *op, signed long weight)
Definition: object.c:1637
recipe * get_random_recipe(recipelist *rpl)
Definition: recipe.c:724
uint16_t material
Definition: object.h:347
static void adjust_product(object *item, int lvl, int yield)
Definition: alchemy.c:400
object * get_random_mon(int level)
Definition: readable.c:1393
static int numb_ob_inside(const object *op)
Definition: alchemy.c:297
Definition: object.h:112
int yield
Definition: recipe.h:21
void free_string(sstring str)
Definition: shstr.c:280
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.c:720
linked_char * ingred
Definition: recipe.h:22
#define PREFER_LOW
Definition: define.h:602
void esrv_send_inventory(object *pl, object *op)
Definition: item.c:307
object * object_find_by_flag(const object *who, int flag)
Definition: object.c:4117
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:582
object * object_find_by_type_and_slaying(const object *who, int type, const char *slaying)
Definition: object.c:4069
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:583
int transmute
Definition: recipe.h:19
#define MAX(x, y)
Definition: compat.h:20
int16_t sp
Definition: living.h:42
static const char *const cauldron_effect[]
Definition: alchemy.c:45
#define safe_strncpy
Definition: compat.h:23
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:310
void transmute_materialname(object *op, const object *change)
Definition: utils.c:267
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:791
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Definition: living.h:40
#define FLAG_CAN_ROLL
Definition: define.h:254
#define LOOSE_MANA
Definition: spells.h:162
int rndm(int min, int max)
Definition: utils.c:162
struct obj * chosen_skill
Definition: object.h:386
void object_free_drop_inventory(object *ob)
Definition: object.c:1389
const char * title
Definition: object.h:317
int16_t y
Definition: object.h:326
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:1933
#define MSG_TYPE_COMMAND
Definition: newclient.h:379
static float chance_fn(int diff)
Definition: alchemy.c:105
sstring title
Definition: recipe.h:11
const char * name_pl
Definition: object.h:315
object * create_archetype(const char *name)
Definition: arch.c:620
char ** arch_name
Definition: recipe.h:13
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2707
static const recipe * find_recipe(const recipelist *fl, int formula, object *ingredients)
Definition: alchemy.c:957
static int content_recipe_value(object *op)
Definition: alchemy.c:269
const char * materialname
Definition: object.h:346
int32_t weight
Definition: object.h:365
void monster_npc_call_help(object *op)
Definition: monster.c:1831
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Definition: recipe.c:664
struct mapdef * map
Definition: object.h:297
#define snprintf
Definition: win32.h:46
static object * attempt_recipe(object *caster, object *cauldron, int ability, const recipe *rp, int nbatches, int ignore_cauldron)
Definition: alchemy.c:334
int summon_hostile_monsters(object *op, int n, const char *monstername)
Definition: spell_util.c:1045
struct linked_char * next
Definition: global.h:88
#define FLAG_IDENTIFIED
Definition: define.h:261
#define FOR_INV_FINISH()
Definition: define.h:714
int16_t dam
Definition: living.h:46
int strtoint(const char *buf)
Definition: recipe.c:643
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:264
const char * name
Definition: object.h:311
struct obj * env
Definition: object.h:293
uint64_t price_base(const object *obj)
Definition: shop.c:70
uint32_t nrof
Definition: object.h:333
#define FLAG_IS_CAULDRON
Definition: define.h:339
Definition: object.h:111
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:585
void cast_magic_storm(object *op, object *tmp, int lvl)
Definition: spell_effect.c:44
sstring skill
Definition: recipe.h:26
static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp)
Definition: alchemy.c:816
unsigned __int64 uint64_t
Definition: win32.h:167
#define QUERY_FLAG(xyz, p)
Definition: define.h:225
#define CLEAR_FLAG(xyz, p)
Definition: define.h:224
#define FLAG_WIZ
Definition: define.h:231
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.c:2126
int index
Definition: recipe.h:18
#define MAX_BUF
Definition: define.h:35
void generate_artifact(object *op, int difficulty)
Definition: artifact.c:155
void give_artifact_abilities(object *op, const object *artifact)
Definition: artifact.c:203
int16_t x
Definition: object.h:326
unsigned short uint16_t
Definition: win32.h:163
#define FOR_MAP_FINISH()
Definition: define.h:767
#define FLAG_KNOWN_CURSED
Definition: define.h:321
object * ob
Definition: player.h:158
int need_identify(const object *op)
Definition: item.c:1326
int exp
Definition: recipe.h:17
#define FLAG_CURSED
Definition: define.h:317
unsigned int uint32_t
Definition: win32.h:162
Definition: object.h:107
static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster)
Definition: alchemy.c:875
const char * name
Definition: global.h:87
#define PREFER_HIGH
Definition: define.h:601
static object * make_item_from_recipe(object *cauldron, const recipe *rp)
Definition: alchemy.c:425
#define SP_MED_FIREBALL
Definition: spells.h:161
#define RANDOM()
Definition: define.h:681
living stats
Definition: object.h:368
struct archt * arch
Definition: object.h:412
size_t arch_names
Definition: recipe.h:12
uint8_t type
Definition: object.h:338
sstring failure_message
Definition: recipe.h:29
void object_set_enemy(object *op, object *enemy)
Definition: object.c:710
#define SK_EXP_NONE
Definition: skills.h:80
#define FLAG_APPLIED
Definition: define.h:235
struct recipestruct * next
Definition: recipe.h:24
static float recipe_chance(const recipe *rp, const object *skill, const object *cauldron)
Definition: alchemy.c:115
int fire_arch_from_position(object *op, object *caster, int16_t x, int16_t y, int dir, object *spell)
Definition: spell_util.c:670
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:787
sstring add_string(const char *str)
Definition: shstr.c:124
void object_add_weight(object *op, signed long weight)
Definition: object.c:2680
#define MSG_TYPE_SKILL
Definition: newclient.h:383
struct obj * inv
Definition: object.h:290
#define NDI_UNIQUE
Definition: newclient.h:245
sstring failure_arch
Definition: recipe.h:28
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:760
MoveType move_block
Definition: object.h:425
static object * find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item)
Definition: alchemy.c:481
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:584
object * item
Definition: artifact.h:15
static const char * cauldron_sound(void)
Definition: alchemy.c:77
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
#define FLAG_NO_PICK
Definition: define.h:239
int16_t level
Definition: object.h:351
int use_alchemy(object *op)
Definition: alchemy.c:1034
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:191
static void remove_contents(object *first_ob, object *save_item)
Definition: alchemy.c:783
int32_t value
Definition: object.h:350
int8_t magic
Definition: object.h:348
sstring cauldron
Definition: recipe.h:27
const char * name
Definition: object.h:466
sstring keycode
Definition: recipe.h:25
struct recipestruct * items
Definition: recipe.h:40
recipelist * get_formulalist(int i)
Definition: recipe.c:96
int min_level
Definition: recipe.h:30
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:707
void object_remove(object *op)
Definition: object.c:1666
#define MSG_TYPE_COMMAND_DM
Definition: newclient.h:515
Definition: object.h:224