Crossfire Server, Trunk
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);
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));
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);
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 
831 static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp) {
832  int danger = 0;
833 
834  /* Knowing alchemy skill reduces yer risk */
835  danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
836 
837  /* better cauldrons reduce risk */
838  danger -= cauldron->magic;
839 
840  /* Higher Int, lower the risk */
841  danger -= 3*(caster->stats.Int-15);
842 
843  /* Ingredients. */
844  FOR_INV_PREPARE(cauldron, item) {
845  /* Nicolas Weeger: no reason why this is the case.
846  danger += item->weight / 100;
847  */
848  danger++;
850  danger += 5;
851  } FOR_INV_FINISH();
852 
853  if (rp == NULL)
854  danger += 110;
855  else
856  danger += rp->diff*3;
857 
858  /* Using a bad device is *majorly *stupid */
859  if (QUERY_FLAG(cauldron, FLAG_CURSED))
860  danger += 80;
861  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
862  danger += 200;
863 
864 #ifdef ALCHEMY_DEBUG
865  LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
866 #endif
867 
868  return danger;
869 }
870 
888 static int is_defined_recipe(const recipe *rp, const object *cauldron) {
889  uint32_t batches_in_cauldron;
890  const linked_char *ingredient;
891  int number;
892 
893  /* check for matching number of ingredients */
894  number = 0;
895  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
896  number++;
897  FOR_INV_PREPARE(cauldron, ob)
898  number--;
899  FOR_INV_FINISH();
900  if (number != 0)
901  return 0;
902 
903  /* check for matching ingredients */
904  batches_in_cauldron = 0;
905  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next) {
906  uint32_t nrof;
907  const char *name;
908  int ok;
909 
910  /* determine and remove nrof from name */
911  name = ingredient->name;
912  nrof = 0;
913  while (isdigit(*name)) {
914  nrof = 10*nrof+(*name-'0');
915  name++;
916  }
917  if (nrof == 0)
918  nrof = 1;
919  while (*name == ' ')
920  name++;
921 
922  /* find the current ingredient in the cauldron */
923  ok = 0;
924  FOR_INV_PREPARE(cauldron, ob) {
925  char name_ob[MAX_BUF];
926  const char *name2;
927 
928  if (ob->title == NULL)
929  name2 = ob->name;
930  else {
931  snprintf(name_ob, sizeof(name_ob), "%s %s", ob->name, ob->title);
932  name2 = name_ob;
933  }
934 
935  if (strcmp(name2, name) == 0) {
936  if (ob->nrof%nrof == 0) {
937  uint32_t batches;
938 
939  batches = ob->nrof/nrof;
940  if (batches_in_cauldron == 0) {
941  batches_in_cauldron = batches;
942  ok = 1;
943  } else if (batches_in_cauldron == batches)
944  ok = 1;
945  }
946  break;
947  }
948  } FOR_INV_FINISH();
949  if (!ok)
950  return(0);
951  }
952 
953  return(1);
954 }
955 
970 static const recipe *find_recipe(const recipelist *fl, int formula, object *ingredients) {
971  const recipe *rp;
972  const recipe *result; /* winning recipe, or NULL if no recipe found */
973  int recipes_matching; /* total number of matching recipes so far */
974  int transmute_found; /* records whether a transmuting recipe was found so far */
975  size_t rp_arch_index;
976 
977 #ifdef EXTREME_ALCHEMY_DEBUG
978  LOG(llevDebug, "looking for formula %d:\n", formula);
979 #endif
980  result = NULL;
981  recipes_matching = 0;
982  transmute_found = 0;
983  for (rp = fl->items; rp != NULL; rp = rp->next) {
984  /* check if recipe matches at all */
985  if (formula%rp->index != 0) {
986 #ifdef EXTREME_ALCHEMY_DEBUG
987  LOG(llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index);
988 #endif
989  continue;
990  }
991 
992  if (rp->transmute && find_transmution_ob(ingredients, rp, &rp_arch_index, 0) != NULL) {
993 #ifdef EXTREME_ALCHEMY_DEBUG
994  LOG(llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index);
995 #endif
996  /* transmution recipe with matching base ingredient */
997  if (!transmute_found) {
998  transmute_found = 1;
999  recipes_matching = 0;
1000  }
1001  } else if (transmute_found) {
1002 #ifdef EXTREME_ALCHEMY_DEBUG
1003  LOG(llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, rp->index);
1004 #endif
1005  /* "normal" recipe found after previous transmution recipe => ignore this recipe */
1006  continue;
1007  }
1008 #ifdef EXTREME_ALCHEMY_DEBUG
1009  else {
1010  LOG(llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index);
1011  }
1012 #endif
1013 
1014  if (rndm(0, recipes_matching) == 0)
1015  result = rp;
1016 
1017  recipes_matching++;
1018  }
1019 
1020  if (result == NULL) {
1021 #ifdef ALCHEMY_DEBUG
1022  LOG(llevDebug, "couldn't find formula for ingredients.\n");
1023 #endif
1024  return NULL;
1025  }
1026 
1027 #ifdef ALCHEMY_DEBUG
1028  if (strcmp(result->title, "NONE") != 0)
1029  LOG(llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula/result->index);
1030  else
1031  LOG(llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula/result->index);
1032 #endif
1033  return result;
1034 }
1035 
1047 int use_alchemy(object *op) {
1048  object *unpaid_cauldron = NULL;
1049  object *unpaid_item = NULL;
1050  int did_alchemy = 0;
1051  char name[MAX_BUF];
1052 
1053  if (QUERY_FLAG(op, FLAG_WIZ))
1054  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, "Note: alchemy in wizard-mode.\n");
1055 
1056  FOR_MAP_PREPARE(op->map, op->x, op->y, tmp) {
1058  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1059  unpaid_cauldron = tmp;
1060  continue;
1061  }
1062  unpaid_item = object_find_by_flag(tmp, FLAG_UNPAID);
1063  if (unpaid_item != NULL)
1064  continue;
1065 
1067  if (QUERY_FLAG(tmp, FLAG_APPLIED))
1069  did_alchemy = 1;
1070  }
1071  } FOR_MAP_FINISH();
1072  if (unpaid_cauldron) {
1073  query_base_name(unpaid_cauldron, 0, name, MAX_BUF);
1075  "You must pay for your %s first!",
1076  name);
1077  } else if (unpaid_item) {
1078  query_base_name(unpaid_item, 0, name, MAX_BUF);
1080  "You must pay for your %s first!",
1081  name);
1082  }
1083 
1084  return did_alchemy;
1085 }
recipestruct::arch_names
size_t arch_names
Definition: recipe.h:12
remove_contents
static void remove_contents(object *first_ob, object *save_item)
Definition: alchemy.c:798
PLAYER
@ PLAYER
Definition: object.h:107
global.h
get_formulalist
recipelist * get_formulalist(int i)
Definition: recipe.c:96
add_string
sstring add_string(const char *str)
Definition: shstr.c:124
object_remove
void object_remove(object *op)
Definition: object.c:1819
safe_strncpy
#define safe_strncpy
Definition: compat.h:27
FOR_MAP_FINISH
#define FOR_MAP_FINISH()
Definition: define.h:730
object_set_enemy
void object_set_enemy(object *op, object *enemy)
Definition: object.c:919
llevError
@ llevError
Definition: logger.h:11
give_artifact_abilities
void give_artifact_abilities(object *op, const object *artifact)
Definition: artifact.c:238
recipestruct::yield
int yield
Definition: recipe.h:21
numb_ob_inside
static int numb_ob_inside(const object *op)
Definition: alchemy.c:303
SET_FLAG
#define SET_FLAG(xyz, p)
Definition: define.h:224
object_add_weight
void object_add_weight(object *op, signed long weight)
Definition: object.c:2809
MSG_TYPE_SKILL
#define MSG_TYPE_SKILL
Definition: newclient.h:407
QUERY_FLAG
#define QUERY_FLAG(xyz, p)
Definition: define.h:226
attempt_do_alchemy
static void attempt_do_alchemy(object *caster, object *cauldron)
Definition: alchemy.c:159
price_base
uint64_t price_base(const object *obj)
Definition: shop.c:75
esrv_send_inventory
void esrv_send_inventory(object *pl, object *op)
Definition: item.c:315
adjust_product
static void adjust_product(object *item, int lvl, int yield)
Definition: alchemy.c: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
locate_recipe_artifact
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Definition: recipe.c:604
commongive.inv
inv
Definition: commongive.py:28
recipestruct::title
sstring title
Definition: recipe.h:11
MIN
#define MIN(x, y)
Definition: compat.h:21
M_STONE
#define M_STONE
Definition: material.h:20
Ice.tmp
int tmp
Definition: Ice.py:207
get_random_recipe
recipe * get_random_recipe(recipelist *rpl)
Definition: recipe.c:664
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.c:628
SK_EXP_NONE
#define SK_EXP_NONE
Definition: skills.h:80
FLAG_APPLIED
#define FLAG_APPLIED
Definition: define.h:235
MSG_TYPE_COMMAND
#define MSG_TYPE_COMMAND
Definition: newclient.h:404
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.c:46
linked_char
Definition: global.h:86
cauldron_effect
static const char *const cauldron_effect[]
Definition: alchemy.c:45
obj::chosen_skill
struct obj * chosen_skill
Definition: object.h:391
free_string
void free_string(sstring str)
Definition: shstr.c:280
strtoint
int strtoint(const char *buf)
Definition: recipe.c:583
recipestruct
Definition: recipe.h:10
NROF
static uint32_t NROF(const object *const ob)
Definition: object.h:611
PREFER_HIGH
#define PREFER_HIGH
Definition: define.h:563
object_sub_weight
void object_sub_weight(object *op, signed long weight)
Definition: object.c:1793
object_find_by_flag
object * object_find_by_flag(const object *who, int flag)
Definition: object.c:4177
is_defined_recipe
static int is_defined_recipe(const recipe *rp, const object *cauldron)
Definition: alchemy.c:888
find_recipe
static const recipe * find_recipe(const recipelist *fl, int formula, object *ingredients)
Definition: alchemy.c:970
object_find_by_type_and_slaying
object * object_find_by_type_and_slaying(const object *who, int type, const char *slaying)
Definition: object.c:4129
obj::name
sstring name
Definition: object.h:314
linked_char::name
const char * name
Definition: global.h:87
content_recipe_value
static int content_recipe_value(object *op)
Definition: alchemy.c:275
rotate-tower.result
bool result
Definition: rotate-tower.py:13
POTION
@ POTION
Definition: object.h:111
FLAG_KNOWN_CURSED
#define FLAG_KNOWN_CURSED
Definition: define.h:320
attempt_recipe
static object * attempt_recipe(object *caster, object *cauldron, int ability, const recipe *rp, int nbatches, int ignore_cauldron)
Definition: alchemy.c:340
MSG_TYPE_COMMAND_DM
#define MSG_TYPE_COMMAND_DM
Definition: newclient.h:535
FOR_OB_AND_BELOW_FINISH
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:754
recipestruct::cauldron
sstring cauldron
Definition: recipe.h:27
recipestruct::arch_name
char ** arch_name
Definition: recipe.h:13
chance_fn
static float chance_fn(int diff)
Definition: alchemy.c:89
SP_MED_FIREBALL
#define SP_MED_FIREBALL
Definition: spells.h:161
generate_artifact
void generate_artifact(object *op, int difficulty)
Definition: artifact.c:187
transmute_materialname
void transmute_materialname(object *op, const object *change)
Definition: utils.c:267
obj::x
int16_t x
Definition: object.h:330
FLAG_DAMNED
#define FLAG_DAMNED
Definition: define.h:317
recipestruct::exp
int exp
Definition: recipe.h:17
linked_char::next
struct linked_char * next
Definition: global.h:88
FOR_INV_FINISH
#define FOR_INV_FINISH()
Definition: define.h:677
FLAG_CAN_ROLL
#define FLAG_CAN_ROLL
Definition: define.h:254
recipestruct::keycode
sstring keycode
Definition: recipe.h:25
MSG_TYPE_SKILL_MISSING
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:587
rndm
int rndm(int min, int max)
Definition: utils.c:162
object_give_identified_properties
void object_give_identified_properties(object *op)
Definition: item.c:1344
sproto.h
recipeliststruct
Definition: recipe.h:37
FOR_OB_AND_BELOW_PREPARE
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:750
liv::Int
int8_t Int
Definition: living.h:36
nlohmann::detail::void
j template void())
Definition: json.hpp:4099
recipestruct::failure_message
sstring failure_message
Definition: recipe.h:29
recipestruct::ingred
linked_char * ingred
Definition: recipe.h:22
find_skill_by_name
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:202
MAX_BUF
#define MAX_BUF
Definition: define.h:35
artifactstruct
Definition: artifact.h:14
get_random_mon
object * get_random_mon(int level)
Definition: readable.c:1268
create_archetype
object * create_archetype(const char *name)
Definition: arch.cpp:281
RANDOM
#define RANDOM()
Definition: define.h:644
recipestruct::skill
sstring skill
Definition: recipe.h:26
random_roll
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
FOR_MAP_PREPARE
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Definition: define.h:723
obj::y
int16_t y
Definition: object.h:330
obj::arch
struct archt * arch
Definition: object.h:417
calc_alch_danger
static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp)
Definition: alchemy.c:831
FLAG_WIZ
#define FLAG_WIZ
Definition: define.h:231
MSG_TYPE_SKILL_ERROR
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:588
obj::type
uint8_t type
Definition: object.h:343
NDI_UNIQUE
#define NDI_UNIQUE
Definition: newclient.h:262
spells.h
make_item_from_recipe
static object * make_item_from_recipe(object *cauldron, const recipe *rp)
Definition: alchemy.c:431
obj::stats
living stats
Definition: object.h:373
MSG_TYPE_SKILL_FAILURE
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:590
item
Definition: item.py:1
LOG
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:51
recipestruct::min_level
int min_level
Definition: recipe.h:30
give.op
op
Definition: give.py:33
monster_npc_call_help
void monster_npc_call_help(object *op)
Definition: monster.c:2004
shop.h
summon_hostile_monsters
int summon_hostile_monsters(object *op, int n, const char *monstername)
Definition: spell_util.c:1004
query_base_name
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.c:686
CLEAR_FLAG
#define CLEAR_FLAG(xyz, p)
Definition: define.h:225
change_exp
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Definition: living.c:2168
recipestruct::next
struct recipestruct * next
Definition: recipe.h:24
object_insert_in_ob
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2833
recipestruct::diff
int diff
Definition: recipe.h:16
recipestruct::transmute
int transmute
Definition: recipe.h:19
artifactstruct::item
object * item
Definition: artifact.h:15
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.c:2080
recipeliststruct::items
struct recipestruct * items
Definition: recipe.h:40
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.c:309
object_free_drop_inventory
void object_free_drop_inventory(object *ob)
Definition: object.c:1546
FOOD
@ FOOD
Definition: object.h:112
recipestruct::index
int index
Definition: recipe.h:18
find_transmution_ob
static object * find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item)
Definition: alchemy.c:495
FLAG_UNPAID
#define FLAG_UNPAID
Definition: define.h:236
recipe_chance
static float recipe_chance(const recipe *rp, const object *skill, const object *cauldron)
Definition: alchemy.c:121
archt::name
sstring name
Definition: object.h:470
recipestruct::failure_arch
sstring failure_arch
Definition: recipe.h:28
say.item
dictionary item
Definition: say.py:149
MSG_TYPE_SKILL_SUCCESS
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:589
FLAG_IS_CAULDRON
#define FLAG_IS_CAULDRON
Definition: define.h:338
FLAG_CURSED
#define FLAG_CURSED
Definition: define.h:316
obj::magic
int8_t magic
Definition: object.h:353
change_attr_value
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:265
FOR_INV_PREPARE
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:670
FORCE
@ FORCE
Definition: object.h:224
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,...)
Definition: main.c:319
cauldron_sound
static const char * cauldron_sound(void)
Definition: alchemy.c:77
object.h
obj::level
int16_t level
Definition: object.h:356
llevDebug
@ llevDebug
Definition: logger.h:13
alchemy_failure_effect
static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger)
Definition: alchemy.c:550
obj::inv
struct obj * inv
Definition: object.h:293
FLAG_IDENTIFIED
#define FLAG_IDENTIFIED
Definition: define.h:261
give.name
name
Definition: give.py:27
use_alchemy
int use_alchemy(object *op)
Definition: alchemy.c:1047
is_identified
int is_identified(const object *op)
Definition: item.c:1336
level
Definition: level.py:1