Crossfire Server, Trunk
alchemy.cpp
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);
64 static const recipe *find_recipe(const recipelist *fl, int formula, object *cauldron);
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);
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));
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);
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);
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 
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 
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 
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  int item_power_delta = 0;
452  if ((art = locate_recipe_artifact(rp, rp_arch_index)) == NULL) {
453  LOG(llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
454  LOG(llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], rp->title);
455  return (object *)NULL;
456  }
458  if (rp->transmute) {
459  /* If we change an item, then we must keep the item power.
460  * Else this allows for instance using a helmet+5 (item power 5) for a
461  * helmet of Xebinon, giving a helmet of Xebinon+5 with item power 0. */
462  item_power_delta = item->item_power - item->arch->clone.item_power;
463  }
465  item->item_power += item_power_delta;
466  if (is_identified(item)) {
467  /* artifacts are generated unidentified, so if the item is identified we must give it its real properties. */
469  }
470  }
471  if (item->env != NULL)
472  object_add_weight(cauldron, item->weight*NROF(item));
473 
474  if (QUERY_FLAG(cauldron, FLAG_CURSED))
476  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
478 
479  return item;
480 }
481 
495 static object *find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item) {
496  object *item = NULL;
497 
498  *rp_arch_index = 0;
499 
500  if (rp->transmute) { /* look for matching ingredient/prod archs */
501  item = first_ingred;
503  size_t i;
504 
505  for (i = 0; i < rp->arch_names; i++) {
506  /* Items with titles are skipped to avoid shadow alchemy ability stacking. */
507  if (strcmp(item->arch->name, rp->arch_name[i]) == 0 && !(item->title)) {
508  *rp_arch_index = i;
509  break;
510  }
511  }
512  if (i < rp->arch_names)
513  break;
515  }
516 
517  /* failed, create a fresh object. Note no nrof>1 because that would
518  * allow players to create massive amounts of artifacts easily */
519  if (create_item && (!item || item->nrof > 1)) {
520  *rp_arch_index = RANDOM()%rp->arch_names;
521  item = create_archetype(rp->arch_name[*rp_arch_index]);
522  }
523 
524 #ifdef ALCHEMY_DEBUG
525  LOG(llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
526  if (item != NULL) {
527  LOG(llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp);
528  }
529 #endif
530 
531  return item;
532 }
533 
550 static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger) {
551  int level = 0;
552 
553  if (!op || !cauldron)
554  return;
555 
557  if (rp && rp->failure_arch) {
558  object *failure = create_archetype(rp->failure_arch);
559  if (!failure) {
560  LOG(llevError, "invalid failure_arch %s for recipe %s\n", rp->failure_arch, rp->title);
561  return;
562  }
563 
564  remove_contents(cauldron->inv, NULL);
565 
566  object_insert_in_ob(failure, cauldron);
567  if (rp->failure_message) {
569  }
570  return;
571  }
572 
573  if (danger > 1)
574  level = random_roll(1, danger, op, PREFER_LOW);
575 
576 #ifdef ALCHEMY_DEBUG
577  LOG(llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
578 #endif
579 
580  /* possible outcomes based on level */
581  if (level < 25) { /* INGREDIENTS USED/SLAGGED */
582  object *item = NULL;
583 
584  if (rndm(0, 2)) { /* slag created */
585  object *tmp;
586  int weight = 0;
587  uint16_t material = M_STONE;
588 
589  FOR_INV_PREPARE(cauldron, inv) { /* slag has coadded ingredient properties */
590  weight += inv->weight;
591  if (!(material&inv->material))
592  material |= inv->material;
593  } FOR_INV_FINISH();
594  tmp = create_archetype("rock");
595  tmp->weight = weight;
596  tmp->value = 0;
597  tmp->material = material;
598  tmp->materialname = add_string("stone");
599  free_string(tmp->name);
600  tmp->name = add_string("slag");
601  if (tmp->name_pl)
602  free_string(tmp->name_pl);
603  tmp->name_pl = add_string("slags");
604  item = object_insert_in_ob(tmp, cauldron);
607  tmp->move_block = 0;
608  }
609  remove_contents(cauldron->inv, item);
611  "The %s %s.",
612  cauldron->name, cauldron_sound());
613  return;
614  } else if (level < 40) { /* MAKE TAINTED ITEM */
615  object *tmp = NULL;
616 
617  /*
618  * Note by Nicolas Weeger 2010-09-26
619  * This is an incorrect part.
620  * Calling again attempt_recipe in case of failure will apply again the artifact
621  * combination to the item.
622  * This leads to items with eg 100% resist, or more.
623  * So use the actual item in the cauldron, don't retry the recipe.
624  * This should fix bug #2020224: buggy(?) crafting yields.
625  *
626  if (!rp)
627  if ((rp = get_random_recipe((recipelist *)NULL)) == NULL)
628  return;
629  */
630 
631  if ((tmp = cauldron->inv)) /*attempt_recipe(op, cauldron, 1, rp, -1, 0)))*/ {
632  if (!QUERY_FLAG(tmp, FLAG_CURSED)) { /* curse it */
636  }
637 
638  /* the apply code for potions already deals with cursed
639  * potions, so any code here is basically ignored.
640  */
641  if (tmp->type == FOOD) {
642  tmp->stats.hp = random_roll(0, 149, op, PREFER_LOW);
643  }
644  tmp->value = 0; /* unsaleable item */
645 
646  /* change stats downward */
647  do {
648  change_attr_value(&tmp->stats, rndm(0, 6), -1*(rndm(1, 3)));
649  } while (rndm(0, 2));
650  }
651  return;
652  }
653 #if 0
654  /*
655  Note: this does not work as expected...
656  At this point there is only one item in the cauldron, and get_formulalist(0) will return
657  the first formula list for recipes with 1 ingredient.
658  So disable this, and just use the next case.
659  */
660 
661  if (level == 40) { /* MAKE RANDOM RECIPE */
662  recipelist *fl;
663  int numb = numb_ob_inside(cauldron);
664 
665  fl = get_formulalist(numb-1); /* take a lower recipe list */
666  if (fl && (rp = get_random_recipe(fl)))
667  /* even though random, don't grant user any EXP for it */
668  (void)attempt_recipe(op, cauldron, 1, rp, -1, 0);
669  else
670  alchemy_failure_effect(op, cauldron, rp, level-1);
671  return;
672 
673  } else
674 #endif
675  if (level < 45) { /* INFURIATE NPC's */
676  /* this is kind of kludgy I know...*/
677  object_set_enemy(cauldron, op);
678  monster_npc_call_help(cauldron);
679  object_set_enemy(cauldron, NULL);
680 
681  alchemy_failure_effect(op, cauldron, rp, level-5);
682  return;
683  } else if (level < 50) { /* MINOR EXPLOSION/FIREBALL */
684  object *tmp;
685 
686  remove_contents(cauldron->inv, NULL);
687  switch (rndm(0, 2)) {
688  case 0:
689  tmp = create_archetype("bomb");
690  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW);
691  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW);
693  "The %s creates a bomb!",
694  cauldron->name);
695  break;
696 
697  default:
698  tmp = create_archetype("fireball");
699  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW)/5+1;
700  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW)/10+2;
702  "The %s erupts in flame!",
703  cauldron->name);
704  break;
705  }
706  object_insert_in_map_at(tmp, op->map, NULL, 0, cauldron->x, cauldron->y);
707  return;
708  } else if (level < 60) { /* CREATE MONSTER */
710  "The %s %s.", cauldron->name, cauldron_sound());
711  remove_contents(cauldron->inv, NULL);
712  return;
713  } else if (level < 80) { /* MAJOR FIRE */
714  object *fb = create_archetype(SP_MED_FIREBALL);
715 
716  remove_contents(cauldron->inv, NULL);
717  fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, fb);
720  "The %s erupts in flame!",
721  cauldron->name);
722  return;
723  } else if (level < 100) { /* WHAMMY the CAULDRON */
724  if (!QUERY_FLAG(cauldron, FLAG_CURSED)) {
725  SET_FLAG(cauldron, FLAG_CURSED);
726  CLEAR_FLAG(cauldron, FLAG_KNOWN_CURSED);
727  CLEAR_FLAG(cauldron, FLAG_IDENTIFIED);
728  } else
729  cauldron->magic--;
730  cauldron->magic -= random_roll(0, 4, op, PREFER_LOW);
731  if (rndm(0, 1)) {
732  remove_contents(cauldron->inv, NULL);
734  "Your %s turns darker then makes a gulping sound!",
735  cauldron->name);
736  } else
738  "Your %s becomes darker.",
739  cauldron->name);
740  return;
741  } else if (level < 110) { /* SUMMON EVIL MONSTERS */
742  object *tmp = get_random_mon(level/5);
743 
744  remove_contents(cauldron->inv, NULL);
745  if (!tmp)
746  alchemy_failure_effect(op, cauldron, rp, level);
747  else if (summon_hostile_monsters(cauldron, random_roll(1, 10, op, PREFER_LOW), tmp->arch->name))
749  "The %s %s and then pours forth monsters!",
750  cauldron->name, cauldron_sound());
751  return;
752  } else if (level < 150) { /* COMBO EFFECT */
753  int roll = rndm(1, 3);
754  while (roll) {
755  alchemy_failure_effect(op, cauldron, rp, level-39);
756  roll--;
757  }
758  return;
759  } else if (level == 151) { /* CREATE RANDOM ARTIFACT */
760  object *tmp;
761 
762  /* this is meant to be better than prior possiblity,
763  * in this one, we allow *any *valid alchemy artifact
764  * to be made (rather than only those on the given
765  * formulalist) */
766  if (!rp)
767  rp = get_random_recipe((recipelist *)NULL);
768  if (rp && (tmp = create_archetype(rp->arch_name[RANDOM()%rp->arch_names]))) {
769  generate_artifact(tmp, random_roll(1, op->level/2+1, op, PREFER_HIGH)+1);
770  if ((tmp = object_insert_in_ob(tmp, cauldron))) {
771  remove_contents(cauldron->inv, tmp);
774  "The %s %s.",
775  cauldron->name, cauldron_sound());
776  }
777  }
778  return;
779  } else { /* MANA STORM - watch out!! */
780  object *tmp = create_archetype(LOOSE_MANA);
782  "You unwisely release potent forces!");
783  remove_contents(cauldron->inv, NULL);
785  return;
786  }
787 }
788 
798 static void remove_contents(object *first_ob, object *save_item) {
799  object *tmp;
800 
801  tmp = first_ob;
803  if (tmp != save_item) {
804  if (tmp->inv)
805  remove_contents(tmp->inv, NULL);
808  }
810 }
811 
830 static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp) {
831  int danger = 0;
832 
833  /* Get the recipe's skill if it has one. Otherwise, use your overall level.
834  * Note that this isn't inherently your chosen skill, so we go find it.
835  */
836  object *skop = rp && rp->skill ? find_skill_by_name(caster, rp->skill) : NULL;
837  danger -= skop ? skop->level : caster->level;
838 
839  /* better cauldrons reduce risk */
840  danger -= cauldron->magic;
841 
842  /* Higher Int, lower the risk */
843  danger -= 3*(caster->stats.Int-15);
844 
845  /* Ingredients. */
846  FOR_INV_PREPARE(cauldron, item) {
847  /* Nicolas Weeger: no reason why this is the case.
848  danger += item->weight / 100;
849  */
850  danger++;
852  danger += 5;
853  } FOR_INV_FINISH();
854 
855  if (rp == NULL)
856  danger += 110;
857  else
858  danger += rp->diff*3;
859 
860  /* Using a bad device is *majorly *stupid */
861  if (QUERY_FLAG(cauldron, FLAG_CURSED))
862  danger += 80;
863  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
864  danger += 200;
865 
866 #ifdef ALCHEMY_DEBUG
867  LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
868 #endif
869 
870  return danger;
871 }
872 
890 static int is_defined_recipe(const recipe *rp, const object *cauldron) {
891  uint32_t batches_in_cauldron;
892  const linked_char *ingredient;
893  int number;
894 
895  /* check for matching number of ingredients */
896  number = 0;
897  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
898  number++;
899  FOR_INV_PREPARE(cauldron, ob) {
900 #ifdef ALCHEMY_DEBUG
901  LOG(llevDebug, "cauldron %s, item %s, number %d->%d\n", cauldron->name, ob->name, number, number - 1);
902 #endif
903  number--;
904  } FOR_INV_FINISH();
905  if (number != 0)
906  return 0;
907 
908  /* check for matching ingredients */
909  batches_in_cauldron = 0;
910  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next) {
911  uint32_t nrof;
912  const char *name;
913  int ok;
914 
915  /* determine and remove nrof from name */
916  name = ingredient->name;
917  nrof = 0;
918  while (isdigit(*name)) {
919  nrof = 10*nrof+(*name-'0');
920  name++;
921  }
922  if (nrof == 0)
923  nrof = 1;
924  while (*name == ' ')
925  name++;
926 
927  /* find the current ingredient in the cauldron */
928  ok = 0;
929  FOR_INV_PREPARE(cauldron, ob) {
930  char name_ob[MAX_BUF];
931  const char *name2;
932 
933  if (ob->title == NULL)
934  name2 = ob->name;
935  else {
936  snprintf(name_ob, sizeof(name_ob), "%s %s", ob->name, ob->title);
937  name2 = name_ob;
938  }
939 
940  if (strcmp(name2, name) == 0) {
941  if (ob->nrof%nrof == 0) {
942  uint32_t batches;
943 
944  // Make sure we handle non-stacking ingredients such as icecubes.
945  batches = (ob->nrof ? ob->nrof : 1)/nrof;
946 #ifdef ALCHEMY_DEBUG
947  LOG(llevDebug, "batches of ingred %s: %d; batches prior: %d\n", name, batches, batches_in_cauldron);
948 #endif
949  if (batches_in_cauldron == 0) {
950  batches_in_cauldron = batches;
951  ok = 1;
952  } else if (batches_in_cauldron == batches)
953  ok = 1;
954  }
955  break;
956  }
957  } FOR_INV_FINISH();
958  if (!ok)
959  return(0);
960  }
961 
962  return(1);
963 }
964 
979 static const recipe *find_recipe(const recipelist *fl, int formula, object *cauldron) {
980  const recipe *rp;
981  const recipe *result; /* winning recipe, or NULL if no recipe found */
982  int recipes_matching; /* total number of matching recipes so far */
983  int transmute_found; /* records whether a transmuting recipe was found so far */
984  size_t rp_arch_index;
985 
986 #ifdef EXTREME_ALCHEMY_DEBUG
987  LOG(llevDebug, "looking for formula %d:\n", formula);
988 #endif
989  result = NULL;
990  recipes_matching = 0;
991  transmute_found = 0;
992  for (rp = fl->items; rp != NULL; rp = rp->next) {
993  /* check if recipe matches at all */
994  if (rp->cauldron != cauldron->arch->name || formula%rp->index != 0) {
995 #ifdef EXTREME_ALCHEMY_DEBUG
996  LOG(llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index);
997 #endif
998  continue;
999  }
1000 
1001  object *ingredients = cauldron->inv;
1002  if (rp->transmute && find_transmution_ob(ingredients, rp, &rp_arch_index, 0) != NULL) {
1003 #ifdef EXTREME_ALCHEMY_DEBUG
1004  LOG(llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index);
1005 #endif
1006  /* transmution recipe with matching base ingredient */
1007  if (!transmute_found) {
1008  transmute_found = 1;
1009  recipes_matching = 0;
1010  }
1011  } else if (transmute_found) {
1012 #ifdef EXTREME_ALCHEMY_DEBUG
1013  LOG(llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, rp->index);
1014 #endif
1015  /* "normal" recipe found after previous transmution recipe => ignore this recipe */
1016  continue;
1017  }
1018 #ifdef EXTREME_ALCHEMY_DEBUG
1019  else {
1020  LOG(llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index);
1021  }
1022 #endif
1023 
1024  if (rndm(0, recipes_matching) == 0)
1025  result = rp;
1026 
1027  recipes_matching++;
1028  }
1029 
1030  if (result == NULL) {
1031 #ifdef ALCHEMY_DEBUG
1032  LOG(llevDebug, "couldn't find formula for ingredients.\n");
1033 #endif
1034  return NULL;
1035  }
1036 
1037 #ifdef ALCHEMY_DEBUG
1038  if (strcmp(result->title, "NONE") != 0)
1039  LOG(llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula/result->index);
1040  else
1041  LOG(llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula/result->index);
1042 #endif
1043  return result;
1044 }
1045 
1057 int use_alchemy(object *op) {
1058  object *unpaid_cauldron = NULL;
1059  object *unpaid_item = NULL;
1060  int did_alchemy = 0;
1061  char name[MAX_BUF];
1062 
1063  if (QUERY_FLAG(op, FLAG_WIZ))
1064  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, "Note: alchemy in wizard-mode.\n");
1065 
1066  FOR_MAP_PREPARE(op->map, op->x, op->y, tmp) {
1068  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1069  unpaid_cauldron = tmp;
1070  continue;
1071  }
1072  unpaid_item = object_find_by_flag(tmp, FLAG_UNPAID);
1073  if (unpaid_item != NULL)
1074  continue;
1075 
1077  if (QUERY_FLAG(tmp, FLAG_APPLIED))
1079  did_alchemy = 1;
1080  }
1081  } FOR_MAP_FINISH();
1082  if (unpaid_cauldron) {
1083  query_base_name(unpaid_cauldron, 0, name, MAX_BUF);
1085  "You must pay for your %s first!",
1086  name);
1087  } else if (unpaid_item) {
1088  query_base_name(unpaid_item, 0, name, MAX_BUF);
1090  "You must pay for your %s first!",
1091  name);
1092  }
1093 
1094  return did_alchemy;
1095 }
PLAYER
@ PLAYER
Definition: object.h:112
global.h
calc_alch_danger
static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp)
Definition: alchemy.cpp:830
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
get_formulalist
recipelist * get_formulalist(int i)
Definition: recipe.cpp:98
llevError
@ llevError
Definition: logger.h:11
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.cpp:58
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
cauldron_sound
static const char * cauldron_sound(void)
Definition: alchemy.cpp:77
object::inv
object * inv
Definition: object.h:298
MSG_TYPE_SKILL
#define MSG_TYPE_SKILL
Definition: newclient.h:396
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
recipe::arch_names
size_t arch_names
Definition: recipe.h:12
recipe::yield
int yield
Definition: recipe.h:21
price_base
uint64_t price_base(const object *obj)
Definition: shop.cpp:75
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Definition: item.cpp:316
object::arch
struct archetype * arch
Definition: object.h:424
object_set_enemy
void object_set_enemy(object *op, object *enemy)
Definition: object.cpp:915
find_recipe
static const recipe * find_recipe(const recipelist *fl, int formula, object *cauldron)
Definition: alchemy.cpp:979
object::x
int16_t x
Definition: object.h:335
recipe::failure_arch
sstring failure_arch
Definition: recipe.h:28
give_artifact_abilities
void give_artifact_abilities(object *op, const object *artifact)
Definition: artifact.cpp:230
adjust_product
static void adjust_product(object *item, int lvl, int yield)
Definition: alchemy.cpp:406
PREFER_LOW
#define PREFER_LOW
Definition: define.h:564
LOOSE_MANA
#define LOOSE_MANA
Definition: spells.h:162
guildjoin.ob
ob
Definition: guildjoin.py:42
cauldron_effect
static const char *const cauldron_effect[]
Definition: alchemy.cpp:45
artifact::item
object * item
Definition: artifact.h:15
commongive.inv
inv
Definition: commongive.py:29
recipelist::items
recipe * items
Definition: recipe.h:40
draw_ext_info_format
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...) PRINTF_ARGS(6
MIN
#define MIN(x, y)
Definition: compat.h:21
alchemy_failure_effect
static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger)
Definition: alchemy.cpp:550
M_STONE
#define M_STONE
Definition: material.h:20
recipe::failure_message
sstring failure_message
Definition: recipe.h:29
Ice.tmp
int tmp
Definition: Ice.py:207
recipe::arch_name
char ** arch_name
Definition: recipe.h:13
attempt_recipe
static object * attempt_recipe(object *caster, object *cauldron, int ability, const recipe *rp, int nbatches, int ignore_cauldron)
Definition: alchemy.cpp:340
recipe::transmute
int transmute
Definition: recipe.h:19
rndm
int rndm(int min, int max)
Definition: utils.cpp:162
skills.h
fire_arch_from_position
int fire_arch_from_position(object *op, object *caster, int16_t x, int16_t y, int dir, object *spell)
Definition: spell_util.cpp:629
SK_EXP_NONE
#define SK_EXP_NONE
Definition: skills.h:80
recipe::exp
int exp
Definition: recipe.h:17
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
object::level
int16_t level
Definition: object.h:361
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.cpp:2853
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:393
MAX
#define MAX(x, y)
Definition: compat.h:24
FLAG_NO_PICK
#define FLAG_NO_PICK
Definition: define.h:239
cast_magic_storm
void cast_magic_storm(object *op, object *tmp, int lvl)
Definition: spell_effect.cpp:49
linked_char
Definition: global.h:96
object::y
int16_t y
Definition: object.h:335
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.cpp:1560
NROF
static uint32_t NROF(const object *const ob)
Definition: object.h:625
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
find_transmution_ob
static object * find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item)
Definition: alchemy.cpp:495
linked_char::name
const char * name
Definition: global.h:97
recipelist
Definition: recipe.h:37
rotate-tower.result
bool result
Definition: rotate-tower.py:13
POTION
@ POTION
Definition: object.h:116
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
MSG_TYPE_COMMAND_DM
#define MSG_TYPE_COMMAND_DM
Definition: newclient.h:524
add_string
sstring add_string(const char *str)
Definition: shstr.cpp:124
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:754
use_alchemy
int use_alchemy(object *op)
Definition: alchemy.cpp:1057
numb_ob_inside
static int numb_ob_inside(const object *op)
Definition: alchemy.cpp:303
SP_MED_FIREBALL
#define SP_MED_FIREBALL
Definition: spells.h:161
is_identified
int is_identified(const object *op)
Definition: item.cpp:1348
object::type
uint8_t type
Definition: object.h:348
recipe::index
int index
Definition: recipe.h:18
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
object::magic
int8_t magic
Definition: object.h:358
content_recipe_value
static int content_recipe_value(object *op)
Definition: alchemy.cpp:275
object_find_by_flag
object * object_find_by_flag(const object *who, int flag)
Definition: object.cpp:4202
linked_char::next
struct linked_char * next
Definition: global.h:98
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
FLAG_CAN_ROLL
#define FLAG_CAN_ROLL
Definition: define.h:254
MSG_TYPE_SKILL_MISSING
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:576
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.cpp:2168
remove_contents
static void remove_contents(object *first_ob, object *save_item)
Definition: alchemy.cpp:798
sproto.h
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:750
living::Int
int8_t Int
Definition: living.h:36
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.cpp:42
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
object_insert_in_map_at
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.cpp:2100
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.cpp:211
MAX_BUF
#define MAX_BUF
Definition: define.h:35
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:278
attempt_do_alchemy
static void attempt_do_alchemy(object *caster, object *cauldron)
Definition: alchemy.cpp:159
free_string
void free_string(sstring str)
Definition: shstr.cpp:280
RANDOM
#define RANDOM()
Definition: define.h:644
make_item_from_recipe
static object * make_item_from_recipe(object *cauldron, const recipe *rp)
Definition: alchemy.cpp:431
recipe
Definition: recipe.h:10
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
object_add_weight
void object_add_weight(object *op, signed long weight)
Definition: object.cpp:2829
object_find_by_type_and_slaying
object * object_find_by_type_and_slaying(const object *who, int type, const char *slaying)
Definition: object.cpp:4154
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
MSG_TYPE_SKILL_ERROR
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:577
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:251
spells.h
transmute_materialname
void transmute_materialname(object *op, const object *change)
Definition: utils.cpp:263
object::name
sstring name
Definition: object.h:319
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:579
get_random_recipe
recipe * get_random_recipe(recipelist *rpl)
Definition: recipe.cpp:787
locate_recipe_artifact
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Definition: recipe.cpp:730
item
Definition: item.py:1
change_attr_value
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.cpp:264
give.op
op
Definition: give.py:33
monster_npc_call_help
void monster_npc_call_help(object *op)
Definition: monster.cpp:2012
shop.h
summon_hostile_monsters
int summon_hostile_monsters(object *op, int n, const char *monstername)
Definition: spell_util.cpp:1005
is_defined_recipe
static int is_defined_recipe(const recipe *rp, const object *cauldron)
Definition: alchemy.cpp:890
strtoint
int strtoint(const char *buf)
Definition: recipe.cpp:709
recipe::diff
int diff
Definition: recipe.h:16
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
object_give_identified_properties
void object_give_identified_properties(object *op)
Definition: item.cpp:1356
recipe::keycode
sstring keycode
Definition: recipe.h:25
recipe::min_level
int min_level
Definition: recipe.h:30
draw_ext_info
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.cpp:308
get_random_mon
object * get_random_mon(int level)
Definition: readable.cpp:1269
FOOD
@ FOOD
Definition: object.h:117
object_remove
void object_remove(object *op)
Definition: object.cpp:1833
recipe::ingred
linked_char * ingred
Definition: recipe.h:22
chance_fn
static float chance_fn(int diff)
Definition: alchemy.cpp:89
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
archetype::name
sstring name
Definition: object.h:484
recipe::next
recipe * next
Definition: recipe.h:24
recipe::skill
sstring skill
Definition: recipe.h:26
say.item
dictionary item
Definition: say.py:149
query_base_name
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.cpp:689
object::stats
living stats
Definition: object.h:378
artifact
Definition: artifact.h:14
MSG_TYPE_SKILL_SUCCESS
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:578
FLAG_IS_CAULDRON
#define FLAG_IS_CAULDRON
Definition: define.h:338
object_sub_weight
void object_sub_weight(object *op, signed long weight)
Definition: object.cpp:1807
recipe_chance
static float recipe_chance(const recipe *rp, const object *skill, const object *cauldron)
Definition: alchemy.cpp:121
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
generate_artifact
void generate_artifact(object *op, int difficulty)
Definition: artifact.cpp:177
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
FORCE
@ FORCE
Definition: object.h:229
object.h
llevDebug
@ llevDebug
Definition: logger.h:13
FLAG_IDENTIFIED
#define FLAG_IDENTIFIED
Definition: define.h:261
give.name
name
Definition: give.py:27
recipe::cauldron
sstring cauldron
Definition: recipe.h:27
recipe::title
sstring title
Definition: recipe.h:11
level
Definition: level.py:1