Crossfire Server, Trunk  R20911
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 
93 static float recipe_chance(const recipe *rp, const object *skill, const object *cauldron) {
94  int cauldron_add_skill;
95 
96  assert(rp);
97  assert(skill);
98  assert(cauldron);
99 
100  cauldron_add_skill = (cauldron->magic + 1) / 2;
101 
102  if (skill->level + cauldron_add_skill < rp->diff - 10)
103  return MAX(.01, .3 - (rp->diff - 10 - skill->level - cauldron_add_skill) * .03);
104 
105  if (skill->level + cauldron_add_skill <= rp->diff + 10)
106  return .5 + .02 * (float)(skill->level + cauldron_add_skill - rp->diff);
107 
108  return MIN(.95, .70 + (skill->level + cauldron_add_skill - rp->diff - 10) * .01);
109 }
110 
139 static void attempt_do_alchemy(object *caster, object *cauldron) {
140  const recipelist *fl;
141  const recipe *rp = NULL;
142  float success_chance;
143  int numb, ability = 1;
144  int formula = 0;
145  object *item, *skop;
146 
147  if (caster->type != PLAYER)
148  return; /* only players for now */
149 
150  /* if no ingredients, no formula! lets forget it */
151  if (!(formula = content_recipe_value(cauldron))) {
153  "The %s is empty.",
154  cauldron->name);
155  return;
156  }
157 
158  numb = numb_ob_inside(cauldron);
159  if ((fl = get_formulalist(numb))) {
160  if (QUERY_FLAG(caster, FLAG_WIZ)) {
161  rp = find_recipe(fl, formula, cauldron->inv);
162  if (rp != NULL) {
163 #ifdef ALCHEMY_DEBUG
164  if (strcmp(rp->title, "NONE"))
165  LOG(llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], rp->title);
166  else
167  LOG(llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula/rp->index);
168 #endif
169  attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, !is_defined_recipe(rp, cauldron, caster));
170  } else
171  LOG(llevDebug, "WIZ couldn't find formula for ingredients.\n");
172  return;
173  } /* End of WIZ alchemy */
174 
175  /* find the recipe */
176  rp = find_recipe(fl, formula, cauldron->inv);
177  if (rp != NULL) {
178  uint64_t value_ingredients;
179  uint64_t value_item;
180  int attempt_shadow_alchemy;
181 
182  /* the caster gets an increase in ability based on thier skill lvl */
183  if (rp->skill != NULL) {
184  skop = find_skill_by_name(caster, rp->skill);
185  if (!skop) {
187  "You do not have the proper skill for this recipe");
188  } else {
189  ability += skop->level*((4.0+cauldron->magic)/4.0);
190  }
191  } else {
192  LOG(llevDebug, "Recipe %s has NULL skill!\n", rp->title);
193  return;
194  }
195 
196  if (rp->cauldron == NULL) {
197  LOG(llevDebug, "Recipe %s has NULL cauldron!\n", rp->title);
198  return;
199  }
200 
201  if (rp->min_level != 0 && skop->level < rp->min_level) {
203  "You aren't skilled enough to try this recipe.");
204  return;
205  }
206 
207  /* determine value of ingredients */
208  value_ingredients = 0;
209  FOR_INV_PREPARE(cauldron, tmp)
210  value_ingredients += price_base(tmp);
211  FOR_INV_FINISH();
212 
213  attempt_shadow_alchemy = !is_defined_recipe(rp, cauldron, caster);
214 
215  /* create the object **FIRST**, then decide whether to keep it. */
216  if ((item = attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, attempt_shadow_alchemy)) != NULL) {
217  /* compute base chance of recipe success */
218  success_chance = recipe_chance(rp, skop, cauldron);
219 
220 #ifdef ALCHEMY_DEBUG
221  LOG(llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level);
222 #endif
223 
224  value_item = price_base(item);
225  if (attempt_shadow_alchemy && value_item > value_ingredients) {
226 #ifdef ALCHEMY_DEBUG
227 #ifndef WIN32
228  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);
229 #else
230  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);
231 #endif
232 #endif
233  /* roll the dice */
234  } else if (random_roll(0, 101, caster, PREFER_LOW) <= 100.0*success_chance) {
235  change_exp(caster, rp->exp, rp->skill, SK_EXP_NONE);
236  return;
237  }
238  }
239  }
240  }
241  /* if we get here, we failed!! */
242  alchemy_failure_effect(caster, cauldron, rp, calc_alch_danger(caster, cauldron, rp));
243 }
244 
255 static int content_recipe_value(object *op) {
256  char name[MAX_BUF];
257  int tval = 0, formula = 0;
258 
259  FOR_INV_PREPARE(op, tmp) {
260  tval = 0;
261  safe_strncpy(name, tmp->name, sizeof(name));
262  if (tmp->title)
263  snprintf(name, sizeof(name), "%s %s", tmp->name, tmp->title);
264  tval = (strtoint(name)*(tmp->nrof ? tmp->nrof : 1));
265 #ifdef ALCHEMY_DEBUG
266  LOG(llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval);
267 #endif
268  formula += tval;
269  } FOR_INV_FINISH();
270 #ifdef ALCHEMY_DEBUG
271  LOG(llevDebug, " Formula value=%d\n", formula);
272 #endif
273  return formula;
274 }
275 
283 static int numb_ob_inside(const object *op) {
284  int o_number = 0;
285 
286  FOR_INV_PREPARE(op, tmp)
287  o_number++;
288  FOR_INV_FINISH();
289 #ifdef ALCHEMY_DEBUG
290  LOG(llevDebug, "numb_ob_inside(%s): found %d ingredients\n", op->name, o_number);
291 #endif
292  return o_number;
293 }
294 
320 static object *attempt_recipe(object *caster, object *cauldron, int ability, const recipe *rp, int nbatches, int ignore_cauldron) {
321  object *item = NULL, *skop;
322  /* this should be passed to this fctn, not effiecent cpu use this way */
323  int batches = abs(nbatches);
324 
325  /* is the cauldron the right type? */
326  if (!ignore_cauldron && (strcmp(rp->cauldron, cauldron->arch->name) != 0)) {
328  "You are not using the proper facilities for this formula.");
329  return NULL;
330  }
331 
332  skop = find_skill_by_name(caster, rp->skill);
333  /* does the caster have the skill? */
334  if (!skop)
335  return NULL;
336 
337  /* code required for this recipe, search the caster */
338  if (rp->keycode) {
339  object *tmp;
340 
341  tmp = object_find_by_type_and_slaying(caster, FORCE, rp->keycode);
342  if (tmp == NULL) { /* failure--no code found */
344  "You know the ingredients, but not the technique. Go learn how to do this recipe.");
345  return NULL;
346  }
347  }
348 
349 #ifdef EXTREME_ALCHEMY_DEBUG
350  LOG(llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
351  LOG(llevDebug, "attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown");
352 #endif
353 
354  if ((item = make_item_from_recipe(cauldron, rp)) != NULL) {
355  remove_contents(cauldron->inv, item);
356  /* adj lvl, nrof on caster level */
357  adjust_product(item, ability, rp->yield ? (rp->yield*batches) : batches);
358 
359  if (item->type == POTION) {
360  item->level = MAX(item->level, skop->level);
361  }
362 
363  if (!item->env && (item = object_insert_in_ob(item, cauldron)) == NULL) {
365  "Nothing happened.");
366  } else {
367  draw_ext_info_format(NDI_UNIQUE, 0, caster,
369  "The %s %s.",
370  cauldron->name, cauldron_sound());
371  }
372  }
373  return item;
374 }
375 
386 static void adjust_product(object *item, int adjust, int yield) {
387  int nrof = 1;
388 
389  if (!yield)
390  yield = 1;
391  if (adjust <= 0)
392  adjust = 1; /* lets avoid div by zero! */
393  if (item->nrof) {
394  nrof = (1.0-1.0/(adjust/10.0+1.0))*(rndm(0, yield-1)+rndm(0, yield-1)+rndm(0, yield-1))+1;
395  if (nrof > yield)
396  nrof = yield;
397  item->nrof = nrof;
398  }
399 }
400 
411 static object *make_item_from_recipe(object *cauldron, const recipe *rp) {
412  const artifact *art = NULL;
413  object *item = NULL;
414  size_t rp_arch_index;
415 
416  if (rp == NULL)
417  return (object *)NULL;
418 
419  /* Find the appropriate object to transform...*/
420  if ((item = find_transmution_ob(cauldron->inv, rp, &rp_arch_index, 1)) == NULL) {
421  LOG(llevDebug, "make_alchemy_item(): failed to create alchemical object.\n");
422  return (object *)NULL;
423  }
424 
425  /* If item is already in container, we need to remove its weight, since it can change later on. */
426  if (item->env != NULL)
427  object_sub_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
428 
429  /* Find the appropriate artifact template...*/
430  if (strcmp(rp->title, "NONE")) {
431  if ((art = locate_recipe_artifact(rp, rp_arch_index)) == NULL) {
432  LOG(llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
433  LOG(llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], rp->title);
434  return (object *)NULL;
435  }
436  transmute_materialname(item, art->item);
437  give_artifact_abilities(item, art->item);
438  if (need_identify(item) && QUERY_FLAG(item, FLAG_IDENTIFIED)) {
439  /* artifacts are generated unidentified, so if the item is identified we must give it its real properties. */
441  }
442  }
443  if (item->env != NULL)
444  object_add_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
445 
446  if (QUERY_FLAG(cauldron, FLAG_CURSED))
447  SET_FLAG(item, FLAG_CURSED);
448  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
449  SET_FLAG(item, FLAG_DAMNED);
450 
451  return item;
452 }
453 
467 static object *find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item) {
468  object *item = NULL;
469 
470  *rp_arch_index = 0;
471 
472  if (rp->transmute) { /* look for matching ingredient/prod archs */
473  item = first_ingred;
475  size_t i;
476 
477  for (i = 0; i < rp->arch_names; i++) {
478  if (strcmp(item->arch->name, rp->arch_name[i]) == 0) {
479  *rp_arch_index = i;
480  break;
481  }
482  }
483  if (i < rp->arch_names)
484  break;
486  }
487 
488  /* failed, create a fresh object. Note no nrof>1 because that would
489  * allow players to create massive amounts of artifacts easily */
490  if (create_item && (!item || item->nrof > 1)) {
491  *rp_arch_index = RANDOM()%rp->arch_names;
492  item = create_archetype(rp->arch_name[*rp_arch_index]);
493  }
494 
495 #ifdef ALCHEMY_DEBUG
496  LOG(llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
497  if (item != NULL) {
498  LOG(llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp);
499  }
500 #endif
501 
502  return item;
503 }
504 
521 static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger) {
522  int level = 0;
523 
524  if (!op || !cauldron)
525  return;
526 
528  if (rp && rp->failure_arch) {
529  object *failure = create_archetype(rp->failure_arch);
530  if (!failure) {
531  LOG(llevError, "invalid failure_arch %s for recipe %s\n", rp->failure_arch, rp->title);
532  return;
533  }
534 
535  remove_contents(cauldron->inv, NULL);
536 
537  object_insert_in_ob(failure, cauldron);
538  if (rp->failure_message) {
540  }
541  return;
542  }
543 
544  if (danger > 1)
545  level = random_roll(1, danger, op, PREFER_LOW);
546 
547 #ifdef ALCHEMY_DEBUG
548  LOG(llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
549 #endif
550 
551  /* possible outcomes based on level */
552  if (level < 25) { /* INGREDIENTS USED/SLAGGED */
553  object *item = NULL;
554 
555  if (rndm(0, 2)) { /* slag created */
556  object *tmp;
557  int weight = 0;
558  uint16_t material = M_STONE;
559 
560  FOR_INV_PREPARE(cauldron, tmp) { /* slag has coadded ingredient properties */
561  weight += tmp->weight;
562  if (!(material&tmp->material))
563  material |= tmp->material;
564  } FOR_INV_FINISH();
565  tmp = create_archetype("rock");
566  tmp->weight = weight;
567  tmp->value = 0;
568  tmp->material = material;
569  tmp->materialname = add_string("stone");
570  free_string(tmp->name);
571  tmp->name = add_string("slag");
572  if (tmp->name_pl)
573  free_string(tmp->name_pl);
574  tmp->name_pl = add_string("slags");
575  item = object_insert_in_ob(tmp, cauldron);
577  CLEAR_FLAG(tmp, FLAG_NO_PICK);
578  tmp->move_block = 0;
579  }
580  remove_contents(cauldron->inv, item);
582  "The %s %s.",
583  cauldron->name, cauldron_sound());
584  return;
585  } else if (level < 40) { /* MAKE TAINTED ITEM */
586  object *tmp = NULL;
587 
588  /*
589  * Note by Nicolas Weeger 2010-09-26
590  * This is an incorrect part.
591  * Calling again attempt_recipe in case of failure will apply again the artifact
592  * combination to the item.
593  * This leads to items with eg 100% resist, or more.
594  * So use the actual item in the cauldron, don't retry the recipe.
595  * This should fix bug #2020224: buggy(?) crafting yields.
596  *
597  if (!rp)
598  if ((rp = get_random_recipe((recipelist *)NULL)) == NULL)
599  return;
600  */
601 
602  if ((tmp = cauldron->inv)) /*attempt_recipe(op, cauldron, 1, rp, -1, 0)))*/ {
603  if (!QUERY_FLAG(tmp, FLAG_CURSED)) { /* curse it */
604  SET_FLAG(tmp, FLAG_CURSED);
607  }
608 
609  /* the apply code for potions already deals with cursed
610  * potions, so any code here is basically ignored.
611  */
612  if (tmp->type == FOOD) {
613  tmp->stats.hp = random_roll(0, 149, op, PREFER_LOW);
614  }
615  tmp->value = 0; /* unsaleable item */
616 
617  /* change stats downward */
618  do {
619  change_attr_value(&tmp->stats, rndm(0, 6), -1*(rndm(1, 3)));
620  } while (rndm(0, 2));
621  }
622  return;
623  }
624 #if 0
625  /*
626  Note: this does not work as expected...
627  At this point there is only one item in the cauldron, and get_formulalist(0) will return
628  the first formula list for recipes with 1 ingredient.
629  So disable this, and just use the next case.
630  */
631 
632  if (level == 40) { /* MAKE RANDOM RECIPE */
633  recipelist *fl;
634  int numb = numb_ob_inside(cauldron);
635 
636  fl = get_formulalist(numb-1); /* take a lower recipe list */
637  if (fl && (rp = get_random_recipe(fl)))
638  /* even though random, don't grant user any EXP for it */
639  (void)attempt_recipe(op, cauldron, 1, rp, -1, 0);
640  else
641  alchemy_failure_effect(op, cauldron, rp, level-1);
642  return;
643 
644  } else
645 #endif
646  if (level < 45) { /* INFURIATE NPC's */
647  /* this is kind of kludgy I know...*/
648  object_set_enemy(cauldron, op);
649  monster_npc_call_help(cauldron);
650  object_set_enemy(cauldron, NULL);
651 
652  alchemy_failure_effect(op, cauldron, rp, level-5);
653  return;
654  } else if (level < 50) { /* MINOR EXPLOSION/FIREBALL */
655  object *tmp;
656 
657  remove_contents(cauldron->inv, NULL);
658  switch (rndm(0, 2)) {
659  case 0:
660  tmp = create_archetype("bomb");
661  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW);
662  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW);
664  "The %s creates a bomb!",
665  cauldron->name);
666  break;
667 
668  default:
669  tmp = create_archetype("fireball");
670  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW)/5+1;
671  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW)/10+2;
673  "The %s erupts in flame!",
674  cauldron->name);
675  break;
676  }
677  object_insert_in_map_at(tmp, op->map, NULL, 0, cauldron->x, cauldron->y);
678  return;
679  } else if (level < 60) { /* CREATE MONSTER */
681  "The %s %s.", cauldron->name, cauldron_sound());
682  remove_contents(cauldron->inv, NULL);
683  return;
684  } else if (level < 80) { /* MAJOR FIRE */
685  object *fb = create_archetype(SP_MED_FIREBALL);
686 
687  remove_contents(cauldron->inv, NULL);
688  fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, fb);
691  "The %s erupts in flame!",
692  cauldron->name);
693  return;
694  } else if (level < 100) { /* WHAMMY the CAULDRON */
695  if (!QUERY_FLAG(cauldron, FLAG_CURSED)) {
696  SET_FLAG(cauldron, FLAG_CURSED);
697  CLEAR_FLAG(cauldron, FLAG_KNOWN_CURSED);
698  CLEAR_FLAG(cauldron, FLAG_IDENTIFIED);
699  } else
700  cauldron->magic--;
701  cauldron->magic -= random_roll(0, 4, op, PREFER_LOW);
702  if (rndm(0, 1)) {
703  remove_contents(cauldron->inv, NULL);
705  "Your %s turns darker then makes a gulping sound!",
706  cauldron->name);
707  } else
709  "Your %s becomes darker.",
710  cauldron->name);
711  return;
712  } else if (level < 110) { /* SUMMON EVIL MONSTERS */
713  object *tmp = get_random_mon(level/5);
714 
715  remove_contents(cauldron->inv, NULL);
716  if (!tmp)
717  alchemy_failure_effect(op, cauldron, rp, level);
718  else if (summon_hostile_monsters(cauldron, random_roll(1, 10, op, PREFER_LOW), tmp->arch->name))
720  "The %s %s and then pours forth monsters!",
721  cauldron->name, cauldron_sound());
722  return;
723  } else if (level < 150) { /* COMBO EFFECT */
724  int roll = rndm(1, 3);
725  while (roll) {
726  alchemy_failure_effect(op, cauldron, rp, level-39);
727  roll--;
728  }
729  return;
730  } else if (level == 151) { /* CREATE RANDOM ARTIFACT */
731  object *tmp;
732 
733  /* this is meant to be better than prior possiblity,
734  * in this one, we allow *any *valid alchemy artifact
735  * to be made (rather than only those on the given
736  * formulalist) */
737  if (!rp)
738  rp = get_random_recipe((recipelist *)NULL);
739  if (rp && (tmp = create_archetype(rp->arch_name[RANDOM()%rp->arch_names]))) {
740  generate_artifact(tmp, random_roll(1, op->level/2+1, op, PREFER_HIGH)+1);
741  if ((tmp = object_insert_in_ob(tmp, cauldron))) {
742  remove_contents(cauldron->inv, tmp);
745  "The %s %s.",
746  cauldron->name, cauldron_sound());
747  }
748  }
749  return;
750  } else { /* MANA STORM - watch out!! */
751  object *tmp = create_archetype(LOOSE_MANA);
753  "You unwisely release potent forces!");
754  remove_contents(cauldron->inv, NULL);
755  cast_magic_storm(op, tmp, level);
756  return;
757  }
758 }
759 
769 static void remove_contents(object *first_ob, object *save_item) {
770  object *tmp;
771 
772  tmp = first_ob;
774  if (tmp != save_item) {
775  if (tmp->inv)
776  remove_contents(tmp->inv, NULL);
777  object_remove(tmp);
779  }
781 }
782 
802 static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp) {
803  int danger = 0;
804 
805  /* Knowing alchemy skill reduces yer risk */
806  danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
807 
808  /* better cauldrons reduce risk */
809  danger -= cauldron->magic;
810 
811  /* Higher Int, lower the risk */
812  danger -= 3*(caster->stats.Int-15);
813 
814  /* Ingredients. */
815  FOR_INV_PREPARE(cauldron, item) {
816  /* Nicolas Weeger: no reason why this is the case.
817  danger += item->weight / 100;
818  */
819  danger++;
820  if (QUERY_FLAG(item, FLAG_CURSED) || QUERY_FLAG(item, FLAG_DAMNED))
821  danger += 5;
822  } FOR_INV_FINISH();
823 
824  if (rp == NULL)
825  danger += 110;
826  else
827  danger += rp->diff*3;
828 
829  /* Using a bad device is *majorly *stupid */
830  if (QUERY_FLAG(cauldron, FLAG_CURSED))
831  danger += 80;
832  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
833  danger += 200;
834 
835 #ifdef ALCHEMY_DEBUG
836  LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
837 #endif
838 
839  return danger;
840 }
841 
861 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster) {
862  uint32_t batches_in_cauldron;
863  const linked_char *ingredient;
864  int number;
865 
866  /* check for matching number of ingredients */
867  number = 0;
868  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
869  number++;
870  FOR_INV_PREPARE(cauldron, ob)
871  number--;
872  FOR_INV_FINISH();
873  if (number != 0)
874  return 0;
875 
876  /* check for matching ingredients */
877  batches_in_cauldron = 0;
878  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next) {
879  uint32_t nrof;
880  const char *name;
881  int ok;
882 
883  /* determine and remove nrof from name */
884  name = ingredient->name;
885  nrof = 0;
886  while (isdigit(*name)) {
887  nrof = 10*nrof+(*name-'0');
888  name++;
889  }
890  if (nrof == 0)
891  nrof = 1;
892  while (*name == ' ')
893  name++;
894 
895  /* find the current ingredient in the cauldron */
896  ok = 0;
897  FOR_INV_PREPARE(cauldron, ob) {
898  char name_ob[MAX_BUF];
899  const char *name2;
900 
901  if (ob->title == NULL)
902  name2 = ob->name;
903  else {
904  snprintf(name_ob, sizeof(name_ob), "%s %s", ob->name, ob->title);
905  name2 = name_ob;
906  }
907 
908  if (strcmp(name2, name) == 0) {
909  if (ob->nrof%nrof == 0) {
910  uint32_t batches;
911 
912  batches = ob->nrof/nrof;
913  if (batches_in_cauldron == 0) {
914  batches_in_cauldron = batches;
915  ok = 1;
916  } else if (batches_in_cauldron == batches)
917  ok = 1;
918  }
919  break;
920  }
921  } FOR_INV_FINISH();
922  if (!ok)
923  return(0);
924  }
925 
926  return(1);
927 }
928 
943 static const recipe *find_recipe(const recipelist *fl, int formula, object *ingredients) {
944  const recipe *rp;
945  const recipe *result; /* winning recipe, or NULL if no recipe found */
946  int recipes_matching; /* total number of matching recipes so far */
947  int transmute_found; /* records whether a transmuting recipe was found so far */
948  size_t rp_arch_index;
949 
950 #ifdef EXTREME_ALCHEMY_DEBUG
951  LOG(llevDebug, "looking for formula %d:\n", formula);
952 #endif
953  result = NULL;
954  recipes_matching = 0;
955  transmute_found = 0;
956  for (rp = fl->items; rp != NULL; rp = rp->next) {
957  /* check if recipe matches at all */
958  if (formula%rp->index != 0) {
959 #ifdef EXTREME_ALCHEMY_DEBUG
960  LOG(llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index);
961 #endif
962  continue;
963  }
964 
965  if (rp->transmute && find_transmution_ob(ingredients, rp, &rp_arch_index, 0) != NULL) {
966 #ifdef EXTREME_ALCHEMY_DEBUG
967  LOG(llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index);
968 #endif
969  /* transmution recipe with matching base ingredient */
970  if (!transmute_found) {
971  transmute_found = 1;
972  recipes_matching = 0;
973  }
974  } else if (transmute_found) {
975 #ifdef EXTREME_ALCHEMY_DEBUG
976  LOG(llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, rp->index);
977 #endif
978  /* "normal" recipe found after previous transmution recipe => ignore this recipe */
979  continue;
980  }
981 #ifdef EXTREME_ALCHEMY_DEBUG
982  else {
983  LOG(llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index);
984  }
985 #endif
986 
987  if (rndm(0, recipes_matching) == 0)
988  result = rp;
989 
990  recipes_matching++;
991  }
992 
993  if (result == NULL) {
994 #ifdef ALCHEMY_DEBUG
995  LOG(llevDebug, "couldn't find formula for ingredients.\n");
996 #endif
997  return NULL;
998  }
999 
1000 #ifdef ALCHEMY_DEBUG
1001  if (strcmp(result->title, "NONE") != 0)
1002  LOG(llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula/result->index);
1003  else
1004  LOG(llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula/result->index);
1005 #endif
1006  return result;
1007 }
1008 
1020 int use_alchemy(object *op) {
1021  object *unpaid_cauldron = NULL;
1022  object *unpaid_item = NULL;
1023  int did_alchemy = 0;
1024  char name[MAX_BUF];
1025 
1026  if (QUERY_FLAG(op, FLAG_WIZ))
1027  draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_COMMAND, MSG_TYPE_COMMAND_DM, "Note: alchemy in wizard-mode.\n");
1028 
1029  FOR_MAP_PREPARE(op->map, op->x, op->y, tmp) {
1030  if (QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) {
1031  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1032  unpaid_cauldron = tmp;
1033  continue;
1034  }
1035  unpaid_item = object_find_by_flag(tmp, FLAG_UNPAID);
1036  if (unpaid_item != NULL)
1037  continue;
1038 
1039  attempt_do_alchemy(op, tmp);
1040  if (QUERY_FLAG(tmp, FLAG_APPLIED))
1041  esrv_send_inventory(op, tmp);
1042  did_alchemy = 1;
1043  }
1044  } FOR_MAP_FINISH();
1045  if (unpaid_cauldron) {
1046  query_base_name(unpaid_cauldron, 0, name, MAX_BUF);
1048  "You must pay for your %s first!",
1049  name);
1050  } else if (unpaid_item) {
1051  query_base_name(unpaid_item, 0, name, MAX_BUF);
1053  "You must pay for your %s first!",
1054  name);
1055  }
1056 
1057  return did_alchemy;
1058 }
void draw_ext_info_format(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *format,...)
Definition: main.c:315
int8_t Int
Definition: living.h:35
#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:521
#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:139
void object_give_identified_properties(object *op)
Definition: item.c:1376
#define SET_FLAG(xyz, p)
Definition: define.h:223
void object_sub_weight(object *op, signed long weight)
Definition: object.c:1625
recipe * get_random_recipe(recipelist *rpl)
Definition: recipe.c:703
uint16_t material
Definition: object.h:347
static void adjust_product(object *item, int lvl, int yield)
Definition: alchemy.c:386
object * get_random_mon(int level)
Definition: readable.c:1393
static int numb_ob_inside(const object *op)
Definition: alchemy.c:283
Definition: object.h:112
int yield
Definition: recipe.h:21
void free_string(sstring str)
Definition: shstr.c:280
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.c:723
linked_char * ingred
Definition: recipe.h:22
#define PREFER_LOW
Definition: define.h:600
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:4100
#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:4052
#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:41
static const char *const cauldron_effect[]
Definition: alchemy.c:45
#define safe_strncpy
Definition: compat.h:23
void draw_ext_info(int flags, int pri, const object *pl, uint8_t type, uint8_t subtype, const char *message)
Definition: main.c:310
void transmute_materialname(object *op, const object *change)
Definition: utils.c:267
#define FOR_OB_AND_BELOW_FINISH()
Definition: define.h:789
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Definition: living.h:39
#define FLAG_CAN_ROLL
Definition: define.h:254
#define LOOSE_MANA
Definition: spells.h:162
int rndm(int min, int max)
Definition: utils.c:162
struct obj * chosen_skill
Definition: object.h:386
void object_free_drop_inventory(object *ob)
Definition: object.c:1368
const char * title
Definition: object.h:317
int16_t y
Definition: object.h:326
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Definition: object.c:1921
#define MSG_TYPE_COMMAND
Definition: newclient.h:379
sstring title
Definition: recipe.h:11
const char * name_pl
Definition: object.h:315
object * create_archetype(const char *name)
Definition: arch.c:617
char ** arch_name
Definition: recipe.h:13
object * object_insert_in_ob(object *op, object *where)
Definition: object.c:2690
static const recipe * find_recipe(const recipelist *fl, int formula, object *ingredients)
Definition: alchemy.c:943
static int content_recipe_value(object *op)
Definition: alchemy.c:255
const char * materialname
Definition: object.h:346
int32_t weight
Definition: object.h:365
void monster_npc_call_help(object *op)
Definition: monster.c:1831
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Definition: recipe.c:643
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:320
int summon_hostile_monsters(object *op, int n, const char *monstername)
Definition: spell_util.c:1047
struct linked_char * next
Definition: global.h:88
#define FLAG_IDENTIFIED
Definition: define.h:261
#define FOR_INV_FINISH()
Definition: define.h:712
int16_t dam
Definition: living.h:45
int strtoint(const char *buf)
Definition: recipe.c:622
void change_attr_value(living *stats, int attr, int8_t value)
Definition: living.c:264
const char * name
Definition: object.h:311
struct obj * env
Definition: object.h:293
uint64_t price_base(const object *obj)
Definition: shop.c:66
uint32_t nrof
Definition: object.h:333
#define FLAG_IS_CAULDRON
Definition: define.h:339
Definition: object.h:111
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:585
void cast_magic_storm(object *op, object *tmp, int lvl)
Definition: spell_effect.c:44
sstring skill
Definition: recipe.h:26
static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp)
Definition: alchemy.c:802
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:2100
int index
Definition: recipe.h:18
#define MAX_BUF
Definition: define.h:35
void generate_artifact(object *op, int difficulty)
Definition: artifact.c:155
void give_artifact_abilities(object *op, const object *artifact)
Definition: artifact.c:203
int16_t x
Definition: object.h:326
unsigned short uint16_t
Definition: win32.h:163
#define FOR_MAP_FINISH()
Definition: define.h:765
#define FLAG_KNOWN_CURSED
Definition: define.h:321
object * ob
Definition: player.h:158
int need_identify(const object *op)
Definition: item.c:1332
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:861
const char * name
Definition: global.h:87
#define PREFER_HIGH
Definition: define.h:599
static object * make_item_from_recipe(object *cauldron, const recipe *rp)
Definition: alchemy.c:411
#define SP_MED_FIREBALL
Definition: spells.h:161
#define RANDOM()
Definition: define.h:679
living stats
Definition: object.h:368
struct archt * arch
Definition: object.h:412
size_t arch_names
Definition: recipe.h:12
uint8_t type
Definition: object.h:338
sstring failure_message
Definition: recipe.h:29
void object_set_enemy(object *op, object *enemy)
Definition: object.c:679
#define SK_EXP_NONE
Definition: skills.h:81
#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:93
int fire_arch_from_position(object *op, object *caster, int16_t x, int16_t y, int dir, object *spell)
Definition: spell_util.c:672
#define FOR_OB_AND_BELOW_PREPARE(op_)
Definition: define.h:785
sstring add_string(const char *str)
Definition: shstr.c:124
void object_add_weight(object *op, signed long weight)
Definition: object.c:2663
#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:758
MoveType move_block
Definition: object.h:425
static object * find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item)
Definition: alchemy.c:467
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:584
object * item
Definition: artifact.h:15
static const char * cauldron_sound(void)
Definition: alchemy.c:77
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:42
#define FLAG_NO_PICK
Definition: define.h:239
int16_t level
Definition: object.h:351
int use_alchemy(object *op)
Definition: alchemy.c:1020
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:213
static void remove_contents(object *first_ob, object *save_item)
Definition: alchemy.c:769
int32_t value
Definition: object.h:350
int8_t magic
Definition: object.h:348
sstring cauldron
Definition: recipe.h:27
const char * name
Definition: object.h:466
sstring keycode
Definition: recipe.h:25
struct recipestruct * items
Definition: recipe.h:40
recipelist * get_formulalist(int i)
Definition: recipe.c:96
int min_level
Definition: recipe.h:30
#define FOR_INV_PREPARE(op_, it_)
Definition: define.h:705
void object_remove(object *op)
Definition: object.c:1654
#define MSG_TYPE_COMMAND_DM
Definition: newclient.h:515
Definition: object.h:224