Crossfire Server, Trunk  R21670
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 
89 static float chance_fn(int diff) {
90  if (diff > 10)
91  return MAX(.01, .3 - (diff - 10) * .03);
92 
93  if (diff > -10)
94  return .5 + .02 * (float)(-diff);
95 
96  return MIN(.95, .70 + (-diff - 10) * .01);
97 }
98 
121 static float recipe_chance(const recipe *rp, const object *skill, const object *cauldron) {
122  assert(rp);
123  assert(skill);
124  assert(cauldron);
125 
126  const int cauldron_add_skill = (cauldron->magic + 1) / 2;
127  const int eff_skill = skill->level + cauldron_add_skill;
128  return chance_fn(rp->diff - eff_skill);
129 }
130 
159 static void attempt_do_alchemy(object *caster, object *cauldron) {
160  const recipelist *fl;
161  const recipe *rp = NULL;
162  float success_chance;
163  int numb, ability = 1;
164  int formula = 0;
165  object *item, *skop;
166 
167  if (caster->type != PLAYER)
168  return; /* only players for now */
169 
170  /* if no ingredients, no formula! lets forget it */
171  if (!(formula = content_recipe_value(cauldron))) {
173  "The %s is empty.",
174  cauldron->name);
175  return;
176  }
177 
178  numb = numb_ob_inside(cauldron);
179  if ((fl = get_formulalist(numb))) {
180  if (QUERY_FLAG(caster, FLAG_WIZ)) {
181  rp = find_recipe(fl, formula, cauldron->inv);
182  if (rp != NULL) {
183 #ifdef ALCHEMY_DEBUG
184  if (strcmp(rp->title, "NONE"))
185  LOG(llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], rp->title);
186  else
187  LOG(llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula/rp->index);
188 #endif
189  attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, !is_defined_recipe(rp, cauldron, caster));
190  } else
191  LOG(llevDebug, "WIZ couldn't find formula for ingredients.\n");
192  return;
193  } /* End of WIZ alchemy */
194 
195  /* find the recipe */
196  rp = find_recipe(fl, formula, cauldron->inv);
197  if (rp != NULL) {
198  uint64_t value_ingredients;
199  uint64_t value_item;
200  int attempt_shadow_alchemy;
201 
202  /* the caster gets an increase in ability based on thier skill lvl */
203  if (rp->skill != NULL) {
204  skop = find_skill_by_name(caster, rp->skill);
205  if (!skop) {
207  "You do not have the proper skill for this recipe");
208  } else {
209  ability += skop->level*((4.0+cauldron->magic)/4.0);
210  }
211  } else {
212  LOG(llevDebug, "Recipe %s has NULL skill!\n", rp->title);
213  return;
214  }
215 
216  if (rp->cauldron == NULL) {
217  LOG(llevDebug, "Recipe %s has NULL cauldron!\n", rp->title);
218  return;
219  }
220 
221  if (rp->min_level != 0 && skop->level < rp->min_level) {
223  "You aren't skilled enough to try this recipe.");
224  return;
225  }
226 
227  /* determine value of ingredients */
228  value_ingredients = 0;
229  FOR_INV_PREPARE(cauldron, tmp)
230  value_ingredients += price_base(tmp);
231  FOR_INV_FINISH();
232 
233  attempt_shadow_alchemy = !is_defined_recipe(rp, cauldron, caster);
234 
235  /* create the object **FIRST**, then decide whether to keep it. */
236  if ((item = attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, attempt_shadow_alchemy)) != NULL) {
237  /* compute base chance of recipe success */
238  success_chance = recipe_chance(rp, skop, cauldron);
239 
240 #ifdef ALCHEMY_DEBUG
241  LOG(llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level);
242 #endif
243 
244  value_item = price_base(item);
245  if (attempt_shadow_alchemy && value_item > value_ingredients) {
246 #ifdef ALCHEMY_DEBUG
247 #ifndef WIN32
248  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);
249 #else
250  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);
251 #endif
252 #endif
253  /* roll the dice */
254  } else if (random_roll(0, 101, caster, PREFER_LOW) <= 100.0*success_chance) {
255  change_exp(caster, rp->exp, rp->skill, SK_EXP_NONE);
256  return;
257  }
258  }
259  }
260  }
261  /* if we get here, we failed!! */
262  alchemy_failure_effect(caster, cauldron, rp, calc_alch_danger(caster, cauldron, rp));
263 }
264 
275 static int content_recipe_value(object *op) {
276  char name[MAX_BUF];
277  int tval = 0, formula = 0;
278 
279  FOR_INV_PREPARE(op, tmp) {
280  tval = 0;
281  safe_strncpy(name, tmp->name, sizeof(name));
282  if (tmp->title)
283  snprintf(name, sizeof(name), "%s %s", tmp->name, tmp->title);
284  tval = (strtoint(name)*NROF(tmp));
285 #ifdef ALCHEMY_DEBUG
286  LOG(llevDebug, "Got ingredient %d %s(%d)\n", NROF(tmp), name, tval);
287 #endif
288  formula += tval;
289  } FOR_INV_FINISH();
290 #ifdef ALCHEMY_DEBUG
291  LOG(llevDebug, " Formula value=%d\n", formula);
292 #endif
293  return formula;
294 }
295 
303 static int numb_ob_inside(const object *op) {
304  int o_number = 0;
305 
306  FOR_INV_PREPARE(op, tmp)
307  o_number++;
308  FOR_INV_FINISH();
309 #ifdef ALCHEMY_DEBUG
310  LOG(llevDebug, "numb_ob_inside(%s): found %d ingredients\n", op->name, o_number);
311 #endif
312  return o_number;
313 }
314 
340 static object *attempt_recipe(object *caster, object *cauldron, int ability, const recipe *rp, int nbatches, int ignore_cauldron) {
341  object *item = NULL, *skop;
342  /* this should be passed to this fctn, not effiecent cpu use this way */
343  int batches = abs(nbatches);
344 
345  /* is the cauldron the right type? */
346  if (!ignore_cauldron && (strcmp(rp->cauldron, cauldron->arch->name) != 0)) {
348  "You are not using the proper facilities for this formula.");
349  return NULL;
350  }
351 
352  skop = find_skill_by_name(caster, rp->skill);
353  /* does the caster have the skill? */
354  if (!skop)
355  return NULL;
356 
357  /* code required for this recipe, search the caster */
358  if (rp->keycode) {
359  object *tmp;
360 
361  tmp = object_find_by_type_and_slaying(caster, FORCE, rp->keycode);
362  if (tmp == NULL) { /* failure--no code found */
364  "You know the ingredients, but not the technique. Go learn how to do this recipe.");
365  return NULL;
366  }
367  }
368 
369 #ifdef EXTREME_ALCHEMY_DEBUG
370  LOG(llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
371  LOG(llevDebug, "attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown");
372 #endif
373 
374  if ((item = make_item_from_recipe(cauldron, rp)) != NULL) {
375  remove_contents(cauldron->inv, item);
376  /* adj lvl, nrof on caster level */
377  adjust_product(item, ability, rp->yield ? (rp->yield*batches) : batches);
378 
379  if (item->type == POTION) {
380  item->level = MAX(item->level, skop->level);
381  }
382 
383  if (!item->env && (item = object_insert_in_ob(item, cauldron)) == NULL) {
385  "Nothing happened.");
386  } else {
387  draw_ext_info_format(NDI_UNIQUE, 0, caster,
389  "The %s %s.",
390  cauldron->name, cauldron_sound());
391  }
392  }
393  return item;
394 }
395 
406 static void adjust_product(object *item, int adjust, int yield) {
407  int nrof = 1;
408 
409  if (!yield)
410  yield = 1;
411  if (adjust <= 0)
412  adjust = 1; /* lets avoid div by zero! */
413  if (item->nrof) {
414  nrof = (1.0-1.0/(adjust/10.0+1.0))*(rndm(0, yield-1)+rndm(0, yield-1)+rndm(0, yield-1))+1;
415  if (nrof > yield)
416  nrof = yield;
417  item->nrof = nrof;
418  }
419 }
420 
431 static object *make_item_from_recipe(object *cauldron, const recipe *rp) {
432  const artifact *art = NULL;
433  object *item = NULL;
434  size_t rp_arch_index;
435 
436  if (rp == NULL)
437  return (object *)NULL;
438 
439  /* Find the appropriate object to transform...*/
440  if ((item = find_transmution_ob(cauldron->inv, rp, &rp_arch_index, 1)) == NULL) {
441  LOG(llevDebug, "make_alchemy_item(): failed to create alchemical object.\n");
442  return (object *)NULL;
443  }
444 
445  /* If item is already in container, we need to remove its weight, since it can change later on. */
446  if (item->env != NULL)
447  object_sub_weight(cauldron, item->weight*NROF(item));
448 
449  /* Find the appropriate artifact template...*/
450  if (strcmp(rp->title, "NONE")) {
451  if ((art = locate_recipe_artifact(rp, rp_arch_index)) == NULL) {
452  LOG(llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
453  LOG(llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], rp->title);
454  return (object *)NULL;
455  }
456  transmute_materialname(item, art->item);
457  give_artifact_abilities(item, art->item);
458  if (is_identified(item)) {
459  /* artifacts are generated unidentified, so if the item is identified we must give it its real properties. */
461  }
462  }
463  if (item->env != NULL)
464  object_add_weight(cauldron, item->weight*NROF(item));
465 
466  if (QUERY_FLAG(cauldron, FLAG_CURSED))
467  SET_FLAG(item, FLAG_CURSED);
468  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
469  SET_FLAG(item, FLAG_DAMNED);
470 
471  return item;
472 }
473 
487 static object *find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item) {
488  object *item = NULL;
489 
490  *rp_arch_index = 0;
491 
492  if (rp->transmute) { /* look for matching ingredient/prod archs */
493  item = first_ingred;
495  size_t i;
496 
497  for (i = 0; i < rp->arch_names; i++) {
498  /* Items with titles are skipped to avoid shadow alchemy ability stacking. */
499  if (strcmp(item->arch->name, rp->arch_name[i]) == 0 && !(item->title)) {
500  *rp_arch_index = i;
501  break;
502  }
503  }
504  if (i < rp->arch_names)
505  break;
507  }
508 
509  /* failed, create a fresh object. Note no nrof>1 because that would
510  * allow players to create massive amounts of artifacts easily */
511  if (create_item && (!item || item->nrof > 1)) {
512  *rp_arch_index = RANDOM()%rp->arch_names;
513  item = create_archetype(rp->arch_name[*rp_arch_index]);
514  }
515 
516 #ifdef ALCHEMY_DEBUG
517  LOG(llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
518  if (item != NULL) {
519  LOG(llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp);
520  }
521 #endif
522 
523  return item;
524 }
525 
542 static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger) {
543  int level = 0;
544 
545  if (!op || !cauldron)
546  return;
547 
549  if (rp && rp->failure_arch) {
550  object *failure = create_archetype(rp->failure_arch);
551  if (!failure) {
552  LOG(llevError, "invalid failure_arch %s for recipe %s\n", rp->failure_arch, rp->title);
553  return;
554  }
555 
556  remove_contents(cauldron->inv, NULL);
557 
558  object_insert_in_ob(failure, cauldron);
559  if (rp->failure_message) {
561  }
562  return;
563  }
564 
565  if (danger > 1)
566  level = random_roll(1, danger, op, PREFER_LOW);
567 
568 #ifdef ALCHEMY_DEBUG
569  LOG(llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
570 #endif
571 
572  /* possible outcomes based on level */
573  if (level < 25) { /* INGREDIENTS USED/SLAGGED */
574  object *item = NULL;
575 
576  if (rndm(0, 2)) { /* slag created */
577  object *tmp;
578  int weight = 0;
579  uint16_t material = M_STONE;
580 
581  FOR_INV_PREPARE(cauldron, tmp) { /* slag has coadded ingredient properties */
582  weight += tmp->weight;
583  if (!(material&tmp->material))
584  material |= tmp->material;
585  } FOR_INV_FINISH();
586  tmp = create_archetype("rock");
587  tmp->weight = weight;
588  tmp->value = 0;
589  tmp->material = material;
590  tmp->materialname = add_string("stone");
591  free_string(tmp->name);
592  tmp->name = add_string("slag");
593  if (tmp->name_pl)
594  free_string(tmp->name_pl);
595  tmp->name_pl = add_string("slags");
596  item = object_insert_in_ob(tmp, cauldron);
598  CLEAR_FLAG(tmp, FLAG_NO_PICK);
599  tmp->move_block = 0;
600  }
601  remove_contents(cauldron->inv, item);
603  "The %s %s.",
604  cauldron->name, cauldron_sound());
605  return;
606  } else if (level < 40) { /* MAKE TAINTED ITEM */
607  object *tmp = NULL;
608 
609  /*
610  * Note by Nicolas Weeger 2010-09-26
611  * This is an incorrect part.
612  * Calling again attempt_recipe in case of failure will apply again the artifact
613  * combination to the item.
614  * This leads to items with eg 100% resist, or more.
615  * So use the actual item in the cauldron, don't retry the recipe.
616  * This should fix bug #2020224: buggy(?) crafting yields.
617  *
618  if (!rp)
619  if ((rp = get_random_recipe((recipelist *)NULL)) == NULL)
620  return;
621  */
622 
623  if ((tmp = cauldron->inv)) /*attempt_recipe(op, cauldron, 1, rp, -1, 0)))*/ {
624  if (!QUERY_FLAG(tmp, FLAG_CURSED)) { /* curse it */
625  SET_FLAG(tmp, FLAG_CURSED);
628  }
629 
630  /* the apply code for potions already deals with cursed
631  * potions, so any code here is basically ignored.
632  */
633  if (tmp->type == FOOD) {
634  tmp->stats.hp = random_roll(0, 149, op, PREFER_LOW);
635  }
636  tmp->value = 0; /* unsaleable item */
637 
638  /* change stats downward */
639  do {
640  change_attr_value(&tmp->stats, rndm(0, 6), -1*(rndm(1, 3)));
641  } while (rndm(0, 2));
642  }
643  return;
644  }
645 #if 0
646  /*
647  Note: this does not work as expected...
648  At this point there is only one item in the cauldron, and get_formulalist(0) will return
649  the first formula list for recipes with 1 ingredient.
650  So disable this, and just use the next case.
651  */
652 
653  if (level == 40) { /* MAKE RANDOM RECIPE */
654  recipelist *fl;
655  int numb = numb_ob_inside(cauldron);
656 
657  fl = get_formulalist(numb-1); /* take a lower recipe list */
658  if (fl && (rp = get_random_recipe(fl)))
659  /* even though random, don't grant user any EXP for it */
660  (void)attempt_recipe(op, cauldron, 1, rp, -1, 0);
661  else
662  alchemy_failure_effect(op, cauldron, rp, level-1);
663  return;
664 
665  } else
666 #endif
667  if (level < 45) { /* INFURIATE NPC's */
668  /* this is kind of kludgy I know...*/
669  object_set_enemy(cauldron, op);
670  monster_npc_call_help(cauldron);
671  object_set_enemy(cauldron, NULL);
672 
673  alchemy_failure_effect(op, cauldron, rp, level-5);
674  return;
675  } else if (level < 50) { /* MINOR EXPLOSION/FIREBALL */
676  object *tmp;
677 
678  remove_contents(cauldron->inv, NULL);
679  switch (rndm(0, 2)) {
680  case 0:
681  tmp = create_archetype("bomb");
682  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW);
683  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW);
685  "The %s creates a bomb!",
686  cauldron->name);
687  break;
688 
689  default:
690  tmp = create_archetype("fireball");
691  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW)/5+1;
692  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW)/10+2;
694  "The %s erupts in flame!",
695  cauldron->name);
696  break;
697  }
698  object_insert_in_map_at(tmp, op->map, NULL, 0, cauldron->x, cauldron->y);
699  return;
700  } else if (level < 60) { /* CREATE MONSTER */
702  "The %s %s.", cauldron->name, cauldron_sound());
703  remove_contents(cauldron->inv, NULL);
704  return;
705  } else if (level < 80) { /* MAJOR FIRE */
706  object *fb = create_archetype(SP_MED_FIREBALL);
707 
708  remove_contents(cauldron->inv, NULL);
709  fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, fb);
712  "The %s erupts in flame!",
713  cauldron->name);
714  return;
715  } else if (level < 100) { /* WHAMMY the CAULDRON */
716  if (!QUERY_FLAG(cauldron, FLAG_CURSED)) {
717  SET_FLAG(cauldron, FLAG_CURSED);
718  CLEAR_FLAG(cauldron, FLAG_KNOWN_CURSED);
719  CLEAR_FLAG(cauldron, FLAG_IDENTIFIED);
720  } else
721  cauldron->magic--;
722  cauldron->magic -= random_roll(0, 4, op, PREFER_LOW);
723  if (rndm(0, 1)) {
724  remove_contents(cauldron->inv, NULL);
726  "Your %s turns darker then makes a gulping sound!",
727  cauldron->name);
728  } else
730  "Your %s becomes darker.",
731  cauldron->name);
732  return;
733  } else if (level < 110) { /* SUMMON EVIL MONSTERS */
734  object *tmp = get_random_mon(level/5);
735 
736  remove_contents(cauldron->inv, NULL);
737  if (!tmp)
738  alchemy_failure_effect(op, cauldron, rp, level);
739  else if (summon_hostile_monsters(cauldron, random_roll(1, 10, op, PREFER_LOW), tmp->arch->name))
741  "The %s %s and then pours forth monsters!",
742  cauldron->name, cauldron_sound());
743  return;
744  } else if (level < 150) { /* COMBO EFFECT */
745  int roll = rndm(1, 3);
746  while (roll) {
747  alchemy_failure_effect(op, cauldron, rp, level-39);
748  roll--;
749  }
750  return;
751  } else if (level == 151) { /* CREATE RANDOM ARTIFACT */
752  object *tmp;
753 
754  /* this is meant to be better than prior possiblity,
755  * in this one, we allow *any *valid alchemy artifact
756  * to be made (rather than only those on the given
757  * formulalist) */
758  if (!rp)
759  rp = get_random_recipe((recipelist *)NULL);
760  if (rp && (tmp = create_archetype(rp->arch_name[RANDOM()%rp->arch_names]))) {
761  generate_artifact(tmp, random_roll(1, op->level/2+1, op, PREFER_HIGH)+1);
762  if ((tmp = object_insert_in_ob(tmp, cauldron))) {
763  remove_contents(cauldron->inv, tmp);
766  "The %s %s.",
767  cauldron->name, cauldron_sound());
768  }
769  }
770  return;
771  } else { /* MANA STORM - watch out!! */
772  object *tmp = create_archetype(LOOSE_MANA);
774  "You unwisely release potent forces!");
775  remove_contents(cauldron->inv, NULL);
776  cast_magic_storm(op, tmp, level);
777  return;
778  }
779 }
780 
790 static void remove_contents(object *first_ob, object *save_item) {
791  object *tmp;
792 
793  tmp = first_ob;
795  if (tmp != save_item) {
796  if (tmp->inv)
797  remove_contents(tmp->inv, NULL);
798  object_remove(tmp);
800  }
802 }
803 
823 static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp) {
824  int danger = 0;
825 
826  /* Knowing alchemy skill reduces yer risk */
827  danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
828 
829  /* better cauldrons reduce risk */
830  danger -= cauldron->magic;
831 
832  /* Higher Int, lower the risk */
833  danger -= 3*(caster->stats.Int-15);
834 
835  /* Ingredients. */
836  FOR_INV_PREPARE(cauldron, item) {
837  /* Nicolas Weeger: no reason why this is the case.
838  danger += item->weight / 100;
839  */
840  danger++;
841  if (QUERY_FLAG(item, FLAG_CURSED) || QUERY_FLAG(item, FLAG_DAMNED))
842  danger += 5;
843  } FOR_INV_FINISH();
844 
845  if (rp == NULL)
846  danger += 110;
847  else
848  danger += rp->diff*3;
849 
850  /* Using a bad device is *majorly *stupid */
851  if (QUERY_FLAG(cauldron, FLAG_CURSED))
852  danger += 80;
853  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
854  danger += 200;
855 
856 #ifdef ALCHEMY_DEBUG
857  LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
858 #endif
859 
860  return danger;
861 }
862 
882 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster) {
883  uint32_t batches_in_cauldron;
884  const linked_char *ingredient;
885  int number;
886 
887  /* check for matching number of ingredients */
888  number = 0;
889  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
890  number++;
891  FOR_INV_PREPARE(cauldron, ob)
892  number--;
893  FOR_INV_FINISH();
894  if (number != 0)
895  return 0;
896 
897  /* check for matching ingredients */
898  batches_in_cauldron = 0;
899  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next) {
900  uint32_t nrof;
901  const char *name;
902  int ok;
903 
904  /* determine and remove nrof from name */
905  name = ingredient->name;
906  nrof = 0;
907  while (isdigit(*name)) {
908  nrof = 10*nrof+(*name-'0');
909  name++;
910  }
911  if (nrof == 0)
912  nrof = 1;
913  while (*name == ' ')
914  name++;
915 
916  /* find the current ingredient in the cauldron */
917  ok = 0;
918  FOR_INV_PREPARE(cauldron, ob) {
919  char name_ob[MAX_BUF];
920  const char *name2;
921 
922  if (ob->title == NULL)
923  name2 = ob->name;
924  else {
925  snprintf(name_ob, sizeof(name_ob), "%s %s", ob->name, ob->title);
926  name2 = name_ob;
927  }
928 
929  if (strcmp(name2, name) == 0) {
930  if (ob->nrof%nrof == 0) {
931  uint32_t batches;
932 
933  batches = ob->nrof/nrof;
934  if (batches_in_cauldron == 0) {
935  batches_in_cauldron = batches;
936  ok = 1;
937  } else if (batches_in_cauldron == batches)
938  ok = 1;
939  }
940  break;
941  }
942  } FOR_INV_FINISH();
943  if (!ok)
944  return(0);
945  }
946 
947  return(1);
948 }
949 
964 static const recipe *find_recipe(const recipelist *fl, int formula, object *ingredients) {
965  const recipe *rp;
966  const recipe *result; /* winning recipe, or NULL if no recipe found */
967  int recipes_matching; /* total number of matching recipes so far */
968  int transmute_found; /* records whether a transmuting recipe was found so far */
969  size_t rp_arch_index;
970 
971 #ifdef EXTREME_ALCHEMY_DEBUG
972  LOG(llevDebug, "looking for formula %d:\n", formula);
973 #endif
974  result = NULL;
975  recipes_matching = 0;
976  transmute_found = 0;
977  for (rp = fl->items; rp != NULL; rp = rp->next) {
978  /* check if recipe matches at all */
979  if (formula%rp->index != 0) {
980 #ifdef EXTREME_ALCHEMY_DEBUG
981  LOG(llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index);
982 #endif
983  continue;
984  }
985 
986  if (rp->transmute && find_transmution_ob(ingredients, rp, &rp_arch_index, 0) != NULL) {
987 #ifdef EXTREME_ALCHEMY_DEBUG
988  LOG(llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index);
989 #endif
990  /* transmution recipe with matching base ingredient */
991  if (!transmute_found) {
992  transmute_found = 1;
993  recipes_matching = 0;
994  }
995  } else if (transmute_found) {
996 #ifdef EXTREME_ALCHEMY_DEBUG
997  LOG(llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, rp->index);
998 #endif
999  /* "normal" recipe found after previous transmution recipe => ignore this recipe */
1000  continue;
1001  }
1002 #ifdef EXTREME_ALCHEMY_DEBUG
1003  else {
1004  LOG(llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index);
1005  }
1006 #endif
1007 
1008  if (rndm(0, recipes_matching) == 0)
1009  result = rp;
1010 
1011  recipes_matching++;
1012  }
1013 
1014  if (result == NULL) {
1015 #ifdef ALCHEMY_DEBUG
1016  LOG(llevDebug, "couldn't find formula for ingredients.\n");
1017 #endif
1018  return NULL;
1019  }
1020 
1021 #ifdef ALCHEMY_DEBUG
1022  if (strcmp(result->title, "NONE") != 0)
1023  LOG(llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula/result->index);
1024  else
1025  LOG(llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula/result->index);
1026 #endif
1027  return result;
1028 }
1029 
1041 int use_alchemy(object *op) {
1042  object *unpaid_cauldron = NULL;
1043  object *unpaid_item = NULL;
1044  int did_alchemy = 0;
1045  char name[MAX_BUF];
1046 
1047  if (QUERY_FLAG(op, FLAG_WIZ))
1048  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, "Note: alchemy in wizard-mode.\n");
1049 
1050  FOR_MAP_PREPARE(op->map, op->x, op->y, tmp) {
1051  if (QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) {
1052  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1053  unpaid_cauldron = tmp;
1054  continue;
1055  }
1056  unpaid_item = object_find_by_flag(tmp, FLAG_UNPAID);
1057  if (unpaid_item != NULL)
1058  continue;
1059 
1060  attempt_do_alchemy(op, tmp);
1061  if (QUERY_FLAG(tmp, FLAG_APPLIED))
1062  esrv_send_inventory(op, tmp);
1063  did_alchemy = 1;
1064  }
1065  } FOR_MAP_FINISH();
1066  if (unpaid_cauldron) {
1067  query_base_name(unpaid_cauldron, 0, name, MAX_BUF);
1069  "You must pay for your %s first!",
1070  name);
1071  } else if (unpaid_item) {
1072  query_base_name(unpaid_item, 0, name, MAX_BUF);
1074  "You must pay for your %s first!",
1075  name);
1076  }
1077 
1078  return did_alchemy;
1079 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:316
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:542
#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:159
void object_give_identified_properties(object *op)
Definition: item.c:1324
#define SET_FLAG(xyz, p)
Definition: define.h:223
void object_sub_weight(object *op, signed long weight)
Definition: object.c:1551
recipe * get_random_recipe(recipelist *rpl)
Definition: recipe.c:725
uint16_t material
Definition: object.h:349
static void adjust_product(object *item, int lvl, int yield)
Definition: alchemy.c:406
object * get_random_mon(int level)
Definition: readable.c:1399
static int numb_ob_inside(const object *op)
Definition: alchemy.c:303
Definition: object.h:112
int yield
Definition: recipe.h:21
void free_string(sstring str)
Definition: shstr.c:280
static uint32_t NROF(const object *const ob)
Definition: object.h:612
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.c:680
linked_char * ingred
Definition: recipe.h:22
#define PREFER_LOW
Definition: define.h:601
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:3985
#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:3937
#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:311
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:389
void object_free_drop_inventory(object *ob)
Definition: object.c:1316
const char * title
Definition: object.h:317
int16_t y
Definition: object.h:327
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:1838
#define MSG_TYPE_COMMAND
Definition: newclient.h:379
static float chance_fn(int diff)
Definition: alchemy.c:89
int is_identified(const object *op)
Definition: item.c:1316
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:2591
static const recipe * find_recipe(const recipelist *fl, int formula, object *ingredients)
Definition: alchemy.c:964
static int content_recipe_value(object *op)
Definition: alchemy.c:275
const char * materialname
Definition: object.h:348
int32_t weight
Definition: object.h:367
void monster_npc_call_help(object *op)
Definition: monster.c:1840
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Definition: recipe.c:665
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:340
int summon_hostile_monsters(object *op, int n, const char *monstername)
Definition: spell_util.c:1043
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:644
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:265
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:334
#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:45
sstring skill
Definition: recipe.h:26
static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp)
Definition: alchemy.c:823
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:2162
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:327
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 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:882
const char * name
Definition: global.h:87
#define PREFER_HIGH
Definition: define.h:600
static object * make_item_from_recipe(object *cauldron, const recipe *rp)
Definition: alchemy.c:431
#define SP_MED_FIREBALL
Definition: spells.h:161
#define RANDOM()
Definition: define.h:681
living stats
Definition: object.h:370
struct archt * arch
Definition: object.h:415
size_t arch_names
Definition: recipe.h:12
uint8_t type
Definition: object.h:340
sstring failure_message
Definition: recipe.h:29
void object_set_enemy(object *op, object *enemy)
Definition: object.c:679
#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:121
int fire_arch_from_position(object *op, object *caster, int16_t x, int16_t y, int dir, object *spell)
Definition: spell_util.c:668
#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:2567
#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:428
static object * find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item)
Definition: alchemy.c:487
#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:353
int use_alchemy(object *op)
Definition: alchemy.c:1041
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:192
static void remove_contents(object *first_ob, object *save_item)
Definition: alchemy.c:790
int32_t value
Definition: object.h:352
int8_t magic
Definition: object.h:350
sstring cauldron
Definition: recipe.h:27
const char * name
Definition: object.h:468
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:1577
#define MSG_TYPE_COMMAND_DM
Definition: newclient.h:515
Definition: object.h:224