Crossfire Server, Trunk  R20513
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,...)
Sends message to player(s).
Definition: main.c:315
Error, serious thing.
Definition: logger.h:11
int8_t Int
Definition: living.h:35
#define M_STONE
Stone.
Definition: material.h:20
#define FLAG_DAMNED
The object is very cursed.
Definition: define.h:318
static void alchemy_failure_effect(object *op, object *cauldron, const recipe *rp, int danger)
Ouch.
Definition: alchemy.c:521
#define FLAG_UNPAID
Object hasn&#39;t been paid for yet.
Definition: define.h:236
List of recipes with a certain number of ingredients.
Definition: recipe.h:37
int diff
Alchemical dfficulty level.
Definition: recipe.h:16
Spell-related defines: spellpath, subtypes, ...
static void attempt_do_alchemy(object *caster, object *cauldron)
Main part of the ALCHEMY code.
Definition: alchemy.c:139
void object_give_identified_properties(object *op)
Ensure op has all its "identified" properties set.
Definition: item.c:1375
#define SET_FLAG(xyz, p)
Definition: define.h:223
void object_sub_weight(object *op, signed long weight)
Recursively (outwards) subtracts a number from the weight of an object (and what is carried by it&#39;s e...
Definition: object.c:1625
recipe * get_random_recipe(recipelist *rpl)
Gets a random recipe from a list, based on chance.
Definition: recipe.c:721
uint16_t material
What materials this object consist of.
Definition: object.h:347
object * get_random_mon(int level)
Returns a random monster selected from linked list of all monsters in the current game...
Definition: readable.c:1371
static int numb_ob_inside(const object *op)
Returns the total number of items in op, excluding ones in item&#39;s items.
Definition: alchemy.c:283
See Food.
Definition: object.h:112
int yield
Maximum number of items produced by the recipe.
Definition: recipe.h:21
void free_string(sstring str)
This will reduce the refcount, and if it has reached 0, str will be freed.
Definition: shstr.c:280
void query_base_name(const object *op, int plural, char *buf, size_t size)
Query a short name for the item.
Definition: item.c:722
linked_char * ingred
List of ingredients.
Definition: recipe.h:22
#define PREFER_LOW
Definition: define.h:600
void esrv_send_inventory(object *pl, object *op)
Sends inventory of a container.
Definition: item.c:307
object * object_find_by_flag(const object *who, int flag)
Find object in inventory by flag.
Definition: object.c:4097
#define MSG_TYPE_SKILL_MISSING
Don&#39;t have the skill.
Definition: newclient.h:582
object * object_find_by_type_and_slaying(const object *who, int type, const char *slaying)
Find object in inventory by type and slaying.
Definition: object.c:4049
#define MSG_TYPE_SKILL_ERROR
Doing something wrong.
Definition: newclient.h:583
int transmute
If defined, one of the formula ingredients is used as the basis for the product object.
Definition: recipe.h:19
#define MAX(x, y)
Definition: compat.h:20
int16_t sp
Spell points.
Definition: living.h:41
static const char *const cauldron_effect[]
define this for some helpful debuging information
Definition: alchemy.c:45
Global type definitions and header inclusions.
#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)
Sends message to player(s).
Definition: main.c:310
void transmute_materialname(object *op, const object *change)
When doing transmutation of objects, we have to recheck the resistances, as some that did not apply p...
Definition: utils.c:266
#define FOR_OB_AND_BELOW_FINISH()
Finishes FOR_OB_AND_BELOW_PREPARE().
Definition: define.h:789
#define MIN(x, y)
Definition: compat.h:17
int16_t hp
Hit Points.
Definition: living.h:39
#define FLAG_CAN_ROLL
Object can be rolled.
Definition: define.h:254
#define LOOSE_MANA
Definition: spells.h:162
int rndm(int min, int max)
Returns a number between min and max.
Definition: utils.c:161
struct obj * chosen_skill
The skill chosen to use.
Definition: object.h:386
void object_free_drop_inventory(object *ob)
Frees everything allocated by an object, removes it from the list of used objects, and puts it on the list of free objects.
Definition: object.c:1368
int16_t y
Position in the map for this object.
Definition: object.h:326
object * object_insert_in_map_at(object *op, mapstruct *m, object *originator, int flag, int x, int y)
Same as object_insert_in_map() except it handle separate coordinates and do a clean job preparing mul...
Definition: object.c:1921
#define MSG_TYPE_COMMAND
Responses to commands, eg, who.
Definition: newclient.h:379
sstring title
Distinguishing name of product.
Definition: recipe.h:11
const char * name_pl
The plural name of the object.
Definition: object.h:315
object * create_archetype(const char *name)
Finds which archetype matches the given name, and returns a new object containing a copy of the arche...
Definition: arch.c:620
char ** arch_name
Possible archetypes of the final product made.
Definition: recipe.h:13
object * object_insert_in_ob(object *op, object *where)
This function inserts the object op in the linked list inside the object environment.
Definition: object.c:2690
static const recipe * find_recipe(const recipelist *fl, int formula, object *ingredients)
Find a recipe from a recipe list that matches the given formula.
Definition: alchemy.c:943
static int content_recipe_value(object *op)
Recipe value of the entire contents of a container.
Definition: alchemy.c:255
const char * materialname
Specific material name.
Definition: object.h:346
int32_t weight
Attributes of the object.
Definition: object.h:365
void monster_npc_call_help(object *op)
A monster calls for help against its enemy.
Definition: monster.c:1767
const artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Finds an artifact for a recipe.
Definition: recipe.c:661
struct mapdef * map
Pointer to the map in which this object is present.
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)
Essentially a wrapper for make_item_from_recipe() and object_insert_in_ob().
Definition: alchemy.c:320
int summon_hostile_monsters(object *op, int n, const char *monstername)
Summons hostile monsters and places them in nearby squares.
Definition: spell_util.c:1047
struct linked_char * next
Definition: global.h:88
#define FLAG_IDENTIFIED
Player knows full info about item.
Definition: define.h:261
#define FOR_INV_FINISH()
Finishes FOR_INV_PREPARE().
Definition: define.h:712
int16_t dam
How much damage this object does when hitting.
Definition: living.h:45
int strtoint(const char *buf)
Convert buf into an integer equal to the coadded sum of the (lowercase) character.
Definition: recipe.c:640
void change_attr_value(living *stats, int attr, int8_t value)
Like set_attr_value(), but instead the value (which can be negative) is added to the specified stat...
Definition: living.c:264
const char * name
The name of the object, obviously...
Definition: object.h:311
struct obj * env
Pointer to the object which is the environment.
Definition: object.h:293
uint64_t price_base(const object *obj)
Determine the base (intrinsic) value of an item.
Definition: shop.c:66
uint32_t nrof
How many of the objects.
Definition: object.h:333
#define FLAG_IS_CAULDRON
container can make alchemical stuff
Definition: define.h:339
See Potion.
Definition: object.h:111
#define MSG_TYPE_SKILL_FAILURE
Failure in using skill.
Definition: newclient.h:585
void cast_magic_storm(object *op, object *tmp, int lvl)
This is really used mostly for spell fumbles and the like.
Definition: spell_effect.c:44
sstring skill
Skill name used to make this recipe.
Definition: recipe.h:26
static int calc_alch_danger(object *caster, object *cauldron, const recipe *rp)
"Danger" level, will determine how bad the backfire could be if the user fails to concoct a recipe pr...
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
Object has special privilegies.
Definition: define.h:231
void change_exp(object *op, int64_t exp, const char *skill_name, int flag)
Changes experience to a player/monster.
Definition: living.c:2076
int index
Index value derived from formula ingredients.
Definition: recipe.h:18
#define MAX_BUF
Used for all kinds of things.
Definition: define.h:35
static void adjust_product(object *item, int lvl, int yield)
We adjust the nrof of the final product, based on the item&#39;s default parameters, and the relevant cas...
Definition: alchemy.c:386
void generate_artifact(object *op, int difficulty)
Decides randomly which artifact the object should be turned into.
Definition: artifact.c:155
This is one artifact, ie one special item.
Definition: artifact.h:14
void give_artifact_abilities(object *op, const object *artifact)
Fixes the given object, giving it the abilities and titles it should have due to the second artifact-...
Definition: artifact.c:203
int16_t x
Definition: object.h:326
Skill-related defines, including subtypes.
unsigned short uint16_t
Definition: win32.h:163
#define FOR_MAP_FINISH()
Finishes FOR_MAP_PREPARE().
Definition: define.h:765
#define FLAG_KNOWN_CURSED
The object is known to be cursed.
Definition: define.h:321
int need_identify(const object *op)
This function really should not exist - by default, any item not identified should need it...
Definition: item.c:1331
int exp
How much exp to give for this formulae.
Definition: recipe.h:17
#define FLAG_CURSED
The object is cursed.
Definition: define.h:317
unsigned int uint32_t
Definition: win32.h:162
See Player.
Definition: object.h:107
static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster)
Determines if ingredients in a container match the proper ingredients for a recipe.
Definition: alchemy.c:861
const char * name
Definition: global.h:87
Object structure, the core of Crossfire.
#define PREFER_HIGH
Definition: define.h:599
static object * make_item_from_recipe(object *cauldron, const recipe *rp)
Using a list of items and a recipe to make an artifact.
Definition: alchemy.c:411
#define SP_MED_FIREBALL
These are some hard coded values that are used within the code for spell failure effects or pieces of...
Definition: spells.h:161
#define RANDOM()
Definition: define.h:679
living stats
Str, Con, Dex, etc.
Definition: object.h:368
struct archt * arch
Pointer to archetype.
Definition: object.h:412
size_t arch_names
Size of the arch_name[] array.
Definition: recipe.h:12
Only for debugging purposes.
Definition: logger.h:13
uint8_t type
PLAYER, BULLET, etc.
Definition: object.h:338
sstring failure_message
Specific failure message.
Definition: recipe.h:29
void object_set_enemy(object *op, object *enemy)
Sets the enemy of an object.
Definition: object.c:679
#define SK_EXP_NONE
Player gets nothing.
Definition: skills.h:81
#define FLAG_APPLIED
Object is ready for use by living.
Definition: define.h:235
struct recipestruct * next
Next recipe with the same number of ingredients.
Definition: recipe.h:24
static float recipe_chance(const recipe *rp, const object *skill, const object *cauldron)
Compute the success probability of a recipe.
Definition: alchemy.c:93
int fire_arch_from_position(object *op, object *caster, int16_t x, int16_t y, int dir, object *spell)
Fires an archetype.
Definition: spell_util.c:672
#define FOR_OB_AND_BELOW_PREPARE(op_)
Constructs a loop iterating over an object and all objects below it in the same pile.
Definition: define.h:785
sstring add_string(const char *str)
This will add &#39;str&#39; to the hash table.
Definition: shstr.c:124
void object_add_weight(object *op, signed long weight)
object_add_weight(object, weight) adds the specified weight to an object, and also updates how much t...
Definition: object.c:2663
#define MSG_TYPE_SKILL
Messages related to skill use.
Definition: newclient.h:383
struct obj * inv
Pointer to the first object in the inventory.
Definition: object.h:290
#define NDI_UNIQUE
Print immediately, don&#39;t buffer.
Definition: newclient.h:245
char * name
Account name.
Definition: account.c:77
sstring failure_arch
Arch of the item to generate on failure, instead of blowing up stuff.
Definition: recipe.h:28
void LOG(LogLevel logLevel, const char *format,...)
Logs a message to stderr, or to file.
Definition: logger.c:51
#define FOR_MAP_PREPARE(map_, mx_, my_, it_)
Constructs a loop iterating over all objects of a map tile.
Definition: define.h:758
MoveType move_block
What movement types this blocks.
Definition: object.h:425
static object * find_transmution_ob(object *first_ingred, const recipe *rp, size_t *rp_arch_index, int create_item)
Looks through the ingredient list.
Definition: alchemy.c:467
#define MSG_TYPE_SKILL_SUCCESS
Successfully used skill.
Definition: newclient.h:584
object * item
Special values of the artifact.
Definition: artifact.h:15
static const char * cauldron_sound(void)
Returns a random selection from cauldron_effect[].
Definition: alchemy.c:77
int random_roll(int min, int max, const object *op, int goodbad)
Roll a random number between min and max.
Definition: utils.c:42
#define FLAG_NO_PICK
Object can&#39;t be picked up.
Definition: define.h:239
int16_t level
Level of creature or object.
Definition: object.h:351
int use_alchemy(object *op)
Handle use_skill for alchemy-like items.
Definition: alchemy.c:1020
object * find_skill_by_name(object *who, const char *name)
This returns the skill pointer of the given name (the one that accumulates exp, has the level...
Definition: skill_util.c:213
static void remove_contents(object *first_ob, object *save_item)
All but object "save_item" are elimentated from the container list.
Definition: alchemy.c:769
int32_t value
How much money it is worth (or contains)
Definition: object.h:350
int8_t magic
Any magical bonuses to this item.
Definition: object.h:348
sstring cauldron
Arch of the cauldron/workbench used to house the formulae.
Definition: recipe.h:27
const char * name
More definite name, like "generate_kobold".
Definition: object.h:466
sstring keycode
Optional keycode needed to use the recipe.
Definition: recipe.h:25
struct recipestruct * items
Pointer to first recipe in this list.
Definition: recipe.h:40
recipelist * get_formulalist(int i)
Gets a formula list by ingredients count.
Definition: recipe.c:114
int min_level
Minimum level to have in the skill to be able to use the formulae.
Definition: recipe.h:30
#define FOR_INV_PREPARE(op_, it_)
Constructs a loop iterating over the inventory of an object.
Definition: define.h:705
void object_remove(object *op)
This function removes the object op from the linked list of objects which it is currently tied to...
Definition: object.c:1654
#define MSG_TYPE_COMMAND_DM
DM related commands.
Definition: newclient.h:515
One alchemy recipe.
Definition: recipe.h:10
Definition: object.h:224