Crossfire Server, Branches 1.12  R18729
alchemy.c
Go to the documentation of this file.
1 /*
2  * static char *rcsid_alchemy_c =
3  * "$Id: alchemy.c 13894 2010-09-26 13:28:50Z ryo_saeba $";
4  */
5 
6 /*
7  CrossFire, A Multiplayer game for X-windows
8 
9  Copyright (C) 2002-2006 Mark Wedel & Crossfire Development Team
10  Copyright (C) 1992 Frank Tore Johansen
11 
12  This program is free software; you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation; either version 2 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program; if not, write to the Free Software
24  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
25 
26  The authors can be reached via e-mail at crossfire-devel@real-time.com
27 */
28 
29 /* March 96 - Laid down original code. -b.t. thomas@astro.psu.edu */
30 
36 #include <global.h>
37 #include <object.h>
38 #ifndef __CEXTRACT__
39 #include <sproto.h>
40 #endif
41 #include <skills.h>
42 #include <spells.h>
43 
45 #if 0
46 #define ALCHEMY_DEBUG
47 #endif
48 
50 #if 0
51 #define EXTREME_ALCHEMY_DEBUG
52 #endif
53 
55 static const char *const cauldron_effect [] = {
56  "vibrates briefly",
57  "produces a cloud of steam",
58  "emits bright flames",
59  "pours forth heavy black smoke",
60  "emits sparks",
61  "shoots out small flames",
62  "whines painfully",
63  "hiccups loudly",
64  "wheezes",
65  "burps",
66  "shakes",
67  "rattles",
68  "makes chugging sounds",
69  "smokes heavily for a while"
70 };
71 
72 
73 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster);
74 static recipe *find_recipe(recipelist *fl, int formula, object *ingredients);
75 static int content_recipe_value(object *op);
76 static int numb_ob_inside(object *op);
77 static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger);
78 static object *attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches, int ignore_cauldron);
79 static int calc_alch_danger(object *caster, object *cauldron, recipe *rp);
80 static object *make_item_from_recipe(object *cauldron, recipe *rp);
81 static void remove_contents(object *first_ob, object *save_item);
82 static void adjust_product(object *item, int lvl, int yield);
83 static object *find_transmution_ob(object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item);
84 static void attempt_do_alchemy(object *caster, object *cauldron);
85 
87 static const char *cauldron_sound(void) {
88  int size = sizeof(cauldron_effect)/sizeof(char *);
89 
90  return cauldron_effect[rndm(0, size-1)];
91 }
92 
121 static void attempt_do_alchemy(object *caster, object *cauldron) {
122  recipelist *fl;
123  recipe *rp = NULL;
124  float success_chance;
125  int numb, ability = 1;
126  int formula = 0;
127  float ave_chance;
128  object *item, *skop;
129 
130  if (caster->type != PLAYER)
131  return; /* only players for now */
132 
133  /* if no ingredients, no formula! lets forget it */
134  if (!(formula = content_recipe_value(cauldron))) {
136  "The %s is empty.",
137  "The %s is empty.",
138  cauldron->name);
139  return;
140  }
141 
142  numb = numb_ob_inside(cauldron);
143  if ((fl = get_formulalist(numb))) {
144  if (QUERY_FLAG(caster, FLAG_WIZ)) {
145  rp = find_recipe(fl, formula, cauldron->inv);
146  if (rp != NULL) {
147 #ifdef ALCHEMY_DEBUG
148  if (strcmp(rp->title, "NONE"))
149  LOG(llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], rp->title);
150  else
151  LOG(llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula/rp->index);
152 #endif
153  attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, !is_defined_recipe(rp, cauldron, caster));
154  } else
155  LOG(llevDebug, "WIZ couldn't find formula for ingredients.\n");
156  return;
157  } /* End of WIZ alchemy */
158 
159  /* find the recipe */
160  rp = find_recipe(fl, formula, cauldron->inv);
161  if (rp != NULL) {
162  uint64 value_ingredients;
163  uint64 value_item;
164  object *tmp;
165  int attempt_shadow_alchemy;
166 
167  ave_chance = fl->total_chance/(float)fl->number;
168  /* the caster gets an increase in ability based on thier skill lvl */
169  if (rp->skill != NULL) {
170  skop = find_skill_by_name(caster, rp->skill);
171  if (!skop) {
173  "You do not have the proper skill for this recipe", NULL);
174  } else {
175  ability += skop->level*((4.0+cauldron->magic)/4.0);
176  }
177  } else {
178  LOG(llevDebug, "Recipe %s has NULL skill!\n", rp->title);
179  return;
180  }
181 
182  if (rp->cauldron == NULL) {
183  LOG(llevDebug, "Recipe %s has NULL cauldron!\n", rp->title);
184  return;
185  }
186 
187  /* determine value of ingredients */
188  value_ingredients = 0;
189  for (tmp = cauldron->inv; tmp != NULL; tmp = tmp->below)
190  value_ingredients += query_cost(tmp, NULL, F_TRUE);
191 
192  attempt_shadow_alchemy = !is_defined_recipe(rp, cauldron, caster);
193 
194  /* create the object **FIRST**, then decide whether to keep it. */
195  if ((item = attempt_recipe(caster, cauldron, ability, rp, formula/rp->index, attempt_shadow_alchemy)) != NULL) {
196  /* compute base chance of recipe success */
197  success_chance = ((float)ability/(float)(rp->diff*(item->level+2)));
198  if (ave_chance == 0)
199  ave_chance = 1;
200 
201 #ifdef ALCHEMY_DEBUG
202  LOG(llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level);
203 #endif
204 
205  value_item = query_cost(item, NULL, F_TRUE|F_IDENTIFIED|F_NOT_CURSED);
206  if (attempt_shadow_alchemy && value_item > value_ingredients) {
207 #ifdef ALCHEMY_DEBUG
208 #ifndef WIN32
209  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);
210 #else
211  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);
212 #endif
213 #endif
214  /* roll the dice */
215  } else if ((float)(random_roll(0, 101, caster, PREFER_LOW)) <= 100.0*success_chance) {
216  change_exp(caster, rp->exp, rp->skill, SK_EXP_NONE);
217  return;
218  }
219  }
220  }
221  }
222  /* if we get here, we failed!! */
223  alchemy_failure_effect(caster, cauldron, rp, calc_alch_danger(caster, cauldron, rp));
224 }
225 
236 static int content_recipe_value(object *op) {
237  char name[MAX_BUF];
238  object *tmp = op->inv;
239  int tval = 0, formula = 0;
240 
241  while (tmp) {
242  tval = 0;
243  strcpy(name, tmp->name);
244  if (tmp->title)
245  snprintf(name, sizeof(name), "%s %s", tmp->name, tmp->title);
246  tval = (strtoint(name)*(tmp->nrof ? tmp->nrof : 1));
247 #ifdef ALCHEMY_DEBUG
248  LOG(llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval);
249 #endif
250  formula += tval;
251  tmp = tmp->below;
252  }
253 #ifdef ALCHEMY_DEBUG
254  LOG(llevDebug, " Formula value=%d\n", formula);
255 #endif
256  return formula;
257 }
258 
266 static int numb_ob_inside(object *op) {
267  object *tmp = op->inv;
268  int o_number = 0;
269 
270  while (tmp) {
271  o_number++;
272  tmp = tmp->below;
273  }
274 #ifdef ALCHEMY_DEBUG
275  LOG(llevDebug, "numb_ob_inside(%s): found %d ingredients\n", op->name, o_number);
276 #endif
277  return o_number;
278 }
279 
305 static object *attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches, int ignore_cauldron) {
306  object *item = NULL, *skop;
307  /* this should be passed to this fctn, not effiecent cpu use this way */
308  int batches = abs(nbatches);
309 
310  /* is the cauldron the right type? */
311  if (!ignore_cauldron && (strcmp(rp->cauldron, cauldron->arch->name) != 0)) {
313  "You are not using the proper facilities for this formula.", NULL);
314  return NULL;
315  }
316 
317  skop = find_skill_by_name(caster, rp->skill);
318  /* does the caster have the skill? */
319  if (!skop)
320  return NULL;
321 
322  /* code required for this recipe, search the caster */
323  if (rp->keycode) {
324  object *tmp;
325 
326  for (tmp = caster->inv; tmp != NULL; tmp = tmp->below) {
327  if (tmp->type == FORCE
328  && tmp->slaying
329  && !strcmp(rp->keycode, tmp->slaying))
330  break;
331  }
332  if (tmp == NULL) { /* failure--no code found */
334  "You know the ingredients, but not the technique. Go learn how to do this recipe.",
335  NULL);
336  return NULL;
337  }
338  }
339 
340 #ifdef EXTREME_ALCHEMY_DEBUG
341  LOG(llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches);
342  LOG(llevDebug, "attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown");
343 #endif
344 
345  if ((item = make_item_from_recipe(cauldron, rp)) != NULL) {
346  remove_contents(cauldron->inv, item);
347  /* adj lvl, nrof on caster level */
348  adjust_product(item, ability, rp->yield ? (rp->yield*batches) : batches);
349  if (!item->env && (item = insert_ob_in_ob(item, cauldron)) == NULL) {
351  "Nothing happened.", NULL);
352  } else {
353  draw_ext_info_format(NDI_UNIQUE, 0, caster,
355  "The %s %s.",
356  "The %s %s.",
357  cauldron->name, cauldron_sound());
358  }
359  }
360  return item;
361 }
362 
373 static void adjust_product(object *item, int lvl, int yield) {
374  int nrof = 1;
375 
376  if (!yield)
377  yield = 1;
378  if (lvl <= 0)
379  lvl = 1; /* lets avoid div by zero! */
380  if (item->nrof) {
381  nrof = (1.0-1.0/(lvl/10.0+1.0))*(rndm(0, yield-1)+rndm(0, yield-1)+rndm(0, yield-1))+1;
382  if (nrof > yield)
383  nrof = yield;
384  item->nrof = nrof;
385  }
386 }
387 
398 static object *make_item_from_recipe(object *cauldron, recipe *rp) {
399  artifact *art = NULL;
400  object *item = NULL;
401  size_t rp_arch_index;
402 
403  if (rp == NULL)
404  return (object *)NULL;
405 
406  /* Find the appropriate object to transform...*/
407  if ((item = find_transmution_ob(cauldron->inv, rp, &rp_arch_index, 1)) == NULL) {
408  LOG(llevDebug, "make_alchemy_item(): failed to create alchemical object.\n");
409  return (object *)NULL;
410  }
411 
412  /* If item is already in container, we need to remove its weight, since it can change later on. */
413  if (item->env != NULL)
414  sub_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
415 
416  /* Find the appropriate artifact template...*/
417  if (strcmp(rp->title, "NONE")) {
418  if ((art = locate_recipe_artifact(rp, rp_arch_index)) == NULL) {
419  LOG(llevError, "make_alchemy_item(): failed to locate recipe artifact.\n");
420  LOG(llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], rp->title);
421  return (object *)NULL;
422  }
423  transmute_materialname(item, art->item);
424  give_artifact_abilities(item, art->item);
425  }
426  if (item->env != NULL)
427  add_weight(cauldron, item->weight*(item->nrof != 0 ? item->nrof : 1));
428 
429  if (QUERY_FLAG(cauldron, FLAG_CURSED))
430  SET_FLAG(item, FLAG_CURSED);
431  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
432  SET_FLAG(item, FLAG_DAMNED);
433 
434  return item;
435 }
436 
450 static object *find_transmution_ob(object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) {
451  object *item = NULL;
452 
453  *rp_arch_index = 0;
454 
455  if (rp->transmute) /* look for matching ingredient/prod archs */
456  for (item = first_ingred; item; item = item->below) {
457  size_t i;
458 
459  for (i = 0; i < rp->arch_names; i++) {
460  if (strcmp(item->arch->name, rp->arch_name[i]) == 0) {
461  *rp_arch_index = i;
462  break;
463  }
464  }
465  if (i < rp->arch_names)
466  break;
467  }
468 
469  /* failed, create a fresh object. Note no nrof>1 because that would
470  * allow players to create massive amounts of artifacts easily */
471  if (create_item && (!item || item->nrof > 1)) {
472  *rp_arch_index = RANDOM()%rp->arch_names;
473  item = create_archetype(rp->arch_name[*rp_arch_index]);
474  }
475 
476 #ifdef ALCHEMY_DEBUG
477  LOG(llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no ");
478  if (item != NULL) {
479  LOG(llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp);
480  }
481 #endif
482 
483  return item;
484 }
485 
502 static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger) {
503  int level = 0;
504 
505  if (!op || !cauldron)
506  return;
507 
508  if (danger > 1)
509  level = random_roll(1, danger, op, PREFER_LOW);
510 
511 #ifdef ALCHEMY_DEBUG
512  LOG(llevDebug, "Alchemy_failure_effect(): using level=%d\n", level);
513 #endif
514 
515  /* possible outcomes based on level */
516  if (level < 25) { /* INGREDIENTS USED/SLAGGED */
517  object *item = NULL;
518 
519  if (rndm(0, 2)) { /* slag created */
520  object *tmp = cauldron->inv;
521  int weight = 0;
522  uint16 material = M_STONE;
523 
524  while (tmp) { /* slag has coadded ingredient properties */
525  weight += tmp->weight;
526  if (!(material&tmp->material))
527  material |= tmp->material;
528  tmp = tmp->below;
529  }
530  tmp = create_archetype("rock");
531  tmp->weight = weight;
532  tmp->value = 0;
533  tmp->material = material;
534  tmp->materialname = add_string("stone");
535  free_string(tmp->name);
536  tmp->name = add_string("slag");
537  if (tmp->name_pl)
538  free_string(tmp->name_pl);
539  tmp->name_pl = add_string("slags");
540  item = insert_ob_in_ob(tmp, cauldron);
542  CLEAR_FLAG(tmp, FLAG_NO_PICK);
543  tmp->move_block = 0;
544  }
545  remove_contents(cauldron->inv, item);
547  "The %s %s.",
548  "The %s %s.",
549  cauldron->name, cauldron_sound());
550  return;
551  } else if (level < 40) { /* MAKE TAINTED ITEM */
552  object *tmp = NULL;
553 
554  /*
555  * Note by Nicolas Weeger 2010-09-26
556  * This is an incorrect part.
557  * Calling again attempt_recipe in case of failure will apply again the artifact
558  * combination to the item.
559  * This leads to items with eg 100% resist, or more.
560  * So use the actual item in the cauldron, don't retry the recipe.
561  * This should fix bug #2020224: buggy(?) crafting yields.
562  *
563  if (!rp)
564  if ((rp = get_random_recipe((recipelist *)NULL)) == NULL)
565  return;
566  */
567 
568  if ((tmp = cauldron->inv)) /*attempt_recipe(op, cauldron, 1, rp, -1, 0)))*/ {
569  if (!QUERY_FLAG(tmp, FLAG_CURSED)) { /* curse it */
570  SET_FLAG(tmp, FLAG_CURSED);
573  }
574 
575  /* the apply code for potions already deals with cursed
576  * potions, so any code here is basically ignored.
577  */
578  if (tmp->type == FOOD) {
579  tmp->stats.hp = random_roll(0, 149, op, PREFER_LOW);
580  }
581  tmp->value = 0; /* unsaleable item */
582 
583  /* change stats downward */
584  do {
585  change_attr_value(&tmp->stats, rndm(0, 6), -1*(rndm(1, 3)));
586  } while (rndm(0, 2));
587  }
588  return;
589  }
590 #if 0
591  /*
592  Note: this does not work as expected...
593  At this point there is only one item in the cauldron, and get_formulalist(0) will return
594  the first formula list for recipes with 1 ingredient.
595  So disable this, and just use the next case.
596  */
597 
598  if (level == 40) { /* MAKE RANDOM RECIPE */
599  recipelist *fl;
600  int numb = numb_ob_inside(cauldron);
601 
602  fl = get_formulalist(numb-1); /* take a lower recipe list */
603  if (fl &&(rp = get_random_recipe(fl)))
604  /* even though random, don't grant user any EXP for it */
605  (void)attempt_recipe(op, cauldron, 1, rp, -1, 0);
606  else
607  alchemy_failure_effect(op, cauldron, rp, level-1);
608  return;
609 
610  } else
611 #endif
612  if (level < 45) { /* INFURIATE NPC's */
613  /* this is kind of kludgy I know...*/
614  cauldron->enemy = op;
615  npc_call_help(cauldron);
616  cauldron->enemy = NULL;
617 
618  alchemy_failure_effect(op, cauldron, rp, level-5);
619  return;
620  } else if (level < 50) { /* MINOR EXPLOSION/FIREBALL */
621  object *tmp;
622 
623  remove_contents(cauldron->inv, NULL);
624  switch (rndm(0, 2)) {
625  case 0:
626  tmp = create_archetype("bomb");
627  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW);
628  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW);
630  "The %s creates a bomb!",
631  "The %s creates a bomb!",
632  cauldron->name);
633  break;
634 
635  default:
636  tmp = create_archetype("fireball");
637  tmp->stats.dam = random_roll(1, level, op, PREFER_LOW)/5+1;
638  tmp->stats.hp = random_roll(1, level, op, PREFER_LOW)/10+2;
640  "The %s erupts in flame!",
641  "The %s erupts in flame!",
642  cauldron->name);
643  break;
644  }
645  tmp->x = cauldron->x,
646  tmp->y = cauldron->y;
647  insert_ob_in_map(tmp, op->map, NULL, 0);
648  return;
649  } else if (level < 60) { /* CREATE MONSTER */
651  "The %s %s.", NULL, cauldron->name, cauldron_sound());
652  remove_contents(cauldron->inv, NULL);
653  return;
654  } else if (level < 80) { /* MAJOR FIRE */
655  object *fb = create_archetype(SP_MED_FIREBALL);
656 
657  remove_contents(cauldron->inv, NULL);
658  fire_arch_from_position(cauldron, cauldron, cauldron->x, cauldron->y, 0, fb);
659  free_object(fb);
661  "The %s erupts in flame!",
662  "The %s erupts in flame!",
663  cauldron->name);
664  return;
665  } else if (level < 100) { /* WHAMMY the CAULDRON */
666  if (!QUERY_FLAG(cauldron, FLAG_CURSED)) {
667  SET_FLAG(cauldron, FLAG_CURSED);
668  CLEAR_FLAG(cauldron, FLAG_KNOWN_CURSED);
669  CLEAR_FLAG(cauldron, FLAG_IDENTIFIED);
670  } else
671  cauldron->magic--;
672  cauldron->magic -= random_roll(0, 4, op, PREFER_LOW);
673  if (rndm(0, 1)) {
674  remove_contents(cauldron->inv, NULL);
676  "Your %s turns darker then makes a gulping sound!",
677  "Your %s turns darker then makes a gulping sound!",
678  cauldron->name);
679  } else
681  "Your %s becomes darker.",
682  "Your %s becomes darker.",
683  cauldron->name);
684  return;
685 
686  } else if (level < 110) { /* SUMMON EVIL MONSTERS */
687  object *tmp = get_random_mon(level/5);
688 
689  remove_contents(cauldron->inv, NULL);
690  if (!tmp)
691  alchemy_failure_effect(op, cauldron, rp, level);
692  else if (summon_hostile_monsters(cauldron, random_roll(1, 10, op, PREFER_LOW), tmp->arch->name))
694  "The %s %s and then pours forth monsters!",
695  "The %s %s and then pours forth monsters!",
696  cauldron->name, cauldron_sound());
697  return;
698  } else if (level < 150) { /* COMBO EFFECT */
699  int roll = rndm(1, 3);
700  while (roll) {
701  alchemy_failure_effect(op, cauldron, rp, level-39);
702  roll--;
703  }
704  return;
705  } else if (level == 151) { /* CREATE RANDOM ARTIFACT */
706  object *tmp;
707 
708  /* this is meant to be better than prior possiblity,
709  * in this one, we allow *any *valid alchemy artifact
710  * to be made (rather than only those on the given
711  * formulalist) */
712  if (!rp)
713  rp = get_random_recipe((recipelist *)NULL);
714  if (rp && (tmp = create_archetype(rp->arch_name[RANDOM()%rp->arch_names]))) {
715  generate_artifact(tmp, random_roll(1, op->level/2+1, op, PREFER_HIGH)+1);
716  if ((tmp = insert_ob_in_ob(tmp, cauldron))) {
717  remove_contents(cauldron->inv, tmp);
720  "The %s %s.",
721  "The %s %s.",
722  cauldron->name, cauldron_sound());
723  }
724  }
725  return;
726  } else { /* MANA STORM - watch out!! */
727  object *tmp = create_archetype(LOOSE_MANA);
729  "You unwisely release potent forces!", NULL);
730  remove_contents(cauldron->inv, NULL);
731  cast_magic_storm(op, tmp, level);
732  return;
733  }
734 }
735 
745 static void remove_contents(object *first_ob, object *save_item) {
746  object *next, *tmp = first_ob;
747 
748  while (tmp) {
749  next = tmp->below;
750  if (tmp == save_item) {
751  if (!(tmp = next))
752  break;
753  else
754  next = next->below;
755  }
756  if (tmp->inv)
757  remove_contents(tmp->inv, NULL);
758  remove_ob(tmp);
759  free_object(tmp);
760  tmp = next;
761  }
762 }
763 
783 static int calc_alch_danger(object *caster, object *cauldron, recipe *rp) {
784  object *item;
785  char name[MAX_BUF];
786  int danger = 0, nrofi = 0;
787 
788  /* Knowing alchemy skill reduces yer risk */
789  danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level;
790 
791  /* better cauldrons reduce risk */
792  danger -= cauldron->magic;
793 
794  /* Higher Int, lower the risk */
795  danger -= 3*(caster->stats.Int-15);
796 
797  /* Ingredients. Longer names usually mean rarer stuff.
798  * Thus the backfire is worse. Also, more ingredients
799  * means we are attempting a more powerfull potion,
800  * and thus the backfire will be worse. */
801  for (item = cauldron->inv; item; item = item->below) {
802  strcpy(name, item->name);
803  if (item->title)
804  snprintf(name, sizeof(name), "%s %s", item->name, item->title);
805  danger += (strtoint(name)/1000)+3;
806  nrofi++;
807  }
808  if (rp == NULL)
809  danger += 110;
810  else
811  danger += rp->diff*3;
812 
813  /* Using a bad device is *majorly *stupid */
814  if (QUERY_FLAG(cauldron, FLAG_CURSED))
815  danger += 80;
816  if (QUERY_FLAG(cauldron, FLAG_DAMNED))
817  danger += 200;
818 
819 #ifdef ALCHEMY_DEBUG
820  LOG(llevDebug, "calc_alch_danger() returned danger=%d\n", danger);
821 #endif
822 
823  return danger;
824 }
825 
845 static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster) {
846  uint32 batches_in_cauldron;
847  const linked_char *ingredient;
848  int number;
849  const object *ob;
850 
851  /* check for matching number of ingredients */
852  number = 0;
853  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next)
854  number++;
855  for (ob = cauldron->inv; ob != NULL; ob = ob->below)
856  number--;
857  if (number != 0)
858  return 0;
859 
860  /* check for matching ingredients */
861  batches_in_cauldron = 0;
862  for (ingredient = rp->ingred; ingredient != NULL; ingredient = ingredient->next) {
863  uint32 nrof;
864  const char *name;
865  int ok;
866 
867  /* determine and remove nrof from name */
868  name = ingredient->name;
869  nrof = 0;
870  while (isdigit(*name)) {
871  nrof = 10*nrof+(*name-'0');
872  name++;
873  }
874  if (nrof == 0)
875  nrof = 1;
876  while (*name == ' ')
877  name++;
878 
879  /* find the current ingredient in the cauldron */
880  ok = 0;
881  for (ob = cauldron->inv; ob != NULL; ob = ob->below) {
882  char name_ob[MAX_BUF];
883  const char *name2;
884 
885  if (ob->title == NULL)
886  name2 = ob->name;
887  else {
888  snprintf(name_ob, sizeof(name_ob), "%s %s", ob->name, ob->title);
889  name2 = name_ob;
890  }
891 
892  if (strcmp(name2, name) == 0) {
893  if (ob->nrof%nrof == 0) {
894  uint32 batches;
895 
896  batches = ob->nrof/nrof;
897  if (batches_in_cauldron == 0) {
898  batches_in_cauldron = batches;
899  ok = 1;
900  } else if (batches_in_cauldron == batches)
901  ok = 1;
902  }
903  break;
904  }
905  }
906  if (!ok)
907  return(0);
908  }
909 
910  return(1);
911 }
912 
929 static recipe *find_recipe(recipelist *fl, int formula, object *ingredients) {
930  recipe *rp;
931  recipe *result; /* winning recipe, or NULL if no recipe found */
932  int recipes_matching; /* total number of matching recipes so far */
933  int transmute_found; /* records whether a transmuting recipe was found so far */
934  size_t rp_arch_index;
935 
936 #ifdef EXTREME_ALCHEMY_DEBUG
937  LOG(llevDebug, "looking for formula %d:\n", formula);
938 #endif
939  result = NULL;
940  recipes_matching = 0;
941  transmute_found = 0;
942  for (rp = fl->items; rp != NULL; rp = rp->next) {
943  /* check if recipe matches at all */
944  if (formula%rp->index != 0) {
945 #ifdef EXTREME_ALCHEMY_DEBUG
946  LOG(llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index);
947 #endif
948  continue;
949  }
950 
951  if (rp->transmute && find_transmution_ob(ingredients, rp, &rp_arch_index, 0) != NULL) {
952 #ifdef EXTREME_ALCHEMY_DEBUG
953  LOG(llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index);
954 #endif
955  /* transmution recipe with matching base ingredient */
956  if (!transmute_found) {
957  transmute_found = 1;
958  recipes_matching = 0;
959  }
960  } else if (transmute_found) {
961 #ifdef EXTREME_ALCHEMY_DEBUG
962  LOG(llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, rp->index);
963 #endif
964  /* "normal" recipe found after previous transmution recipe => ignore this recipe */
965  continue;
966  }
967 #ifdef EXTREME_ALCHEMY_DEBUG
968  else {
969  LOG(llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index);
970  }
971 #endif
972 
973  if (rndm(0, recipes_matching) == 0)
974  result = rp;
975 
976  recipes_matching++;
977  }
978 
979  if (result == NULL) {
980 #ifdef ALCHEMY_DEBUG
981  LOG(llevDebug, "couldn't find formula for ingredients.\n");
982 #endif
983  return NULL;
984  }
985 
986 #ifdef ALCHEMY_DEBUG
987  if (strcmp(result->title, "NONE") != 0)
988  LOG(llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula/result->index);
989  else
990  LOG(llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula/result->index);
991 #endif
992  return result;
993 }
994 
1006 int use_alchemy(object *op) {
1007  object *tmp, *item, *next;
1008  object *unpaid_cauldron = NULL;
1009  object *unpaid_item = NULL;
1010  int did_alchemy = 0;
1011  char name[MAX_BUF];
1012 
1013  for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = next) {
1014  next = tmp->above;
1015  if (QUERY_FLAG(tmp, FLAG_IS_CAULDRON)) {
1016  if (QUERY_FLAG(tmp, FLAG_UNPAID)) {
1017  unpaid_cauldron = tmp;
1018  continue;
1019  }
1020  unpaid_item = NULL;
1021  for (item = tmp->inv; item; item = item->below) {
1022  if (QUERY_FLAG(item, FLAG_UNPAID)) {
1023  unpaid_item = item;
1024  break;
1025  }
1026  }
1027  if (unpaid_item != NULL)
1028  continue;
1029 
1030  attempt_do_alchemy(op, tmp);
1031  if (QUERY_FLAG(tmp, FLAG_APPLIED))
1032  esrv_send_inventory(op, tmp);
1033  did_alchemy = 1;
1034  }
1035  }
1036  if (unpaid_cauldron) {
1037  query_base_name(unpaid_cauldron, 0, name, MAX_BUF);
1039  "You must pay for your %s first!",
1040  "You must pay for your %s first!",
1041  name);
1042  } else if (unpaid_item) {
1043  query_base_name(unpaid_item, 0, name, MAX_BUF);
1045  "You must pay for your %s first!",
1046  "You must pay for your %s first!",
1047  name);
1048  }
1049 
1050  return did_alchemy;
1051 }
const char * title
Definition: recipe.h:11
#define MSG_TYPE_SKILL_MISSING
Definition: newclient.h:501
#define FOOD
Definition: define.h:118
sint8 Int
Definition: living.h:78
#define M_STONE
Definition: material.h:50
#define FLAG_DAMNED
Definition: define.h:614
#define FLAG_UNPAID
Definition: define.h:532
int diff
Definition: recipe.h:16
static void attempt_do_alchemy(object *caster, object *cauldron)
Definition: alchemy.c:121
void change_exp(object *op, sint64 exp, const char *skill_name, int flag)
Definition: living.c:2015
#define SET_FLAG(xyz, p)
Definition: define.h:510
recipe * get_random_recipe(recipelist *rpl)
Definition: recipe.c:697
uint16 material
Definition: object.h:198
static void adjust_product(object *item, int lvl, int yield)
Definition: alchemy.c:373
object * get_random_mon(int level)
Definition: readable.c:1316
int fire_arch_from_position(object *op, object *caster, sint16 x, sint16 y, int dir, object *spell)
Definition: spell_util.c:653
int yield
Definition: recipe.h:21
void free_string(sstring str)
Definition: shstr.c:272
#define MSG_TYPE_SKILL_FAILURE
Definition: newclient.h:504
void query_base_name(const object *op, int plural, char *buf, size_t size)
Definition: item.c:732
linked_char * ingred
Definition: recipe.h:22
#define PREFER_LOW
Definition: define.h:909
void draw_ext_info(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *message, const char *oldmessage)
Definition: standalone.c:171
const char * slaying
Definition: object.h:172
void esrv_send_inventory(object *pl, object *op)
Definition: item.c:307
struct obj * above
Definition: object.h:146
sint16 x
Definition: object.h:179
int transmute
Definition: recipe.h:19
#define F_TRUE
Definition: define.h:777
sint16 sp
Definition: living.h:83
void change_attr_value(living *stats, int attr, sint8 value)
Definition: living.c:336
static const char *const cauldron_effect[]
Definition: alchemy.c:55
void draw_ext_info_format(int flags, int pri, const object *pl, uint8 type, uint8 subtype, const char *new_format, const char *old_format,...)
Definition: standalone.c:175
struct obj * enemy
Definition: object.h:232
void transmute_materialname(object *op, const object *change)
Definition: utils.c:281
#define F_NOT_CURSED
Definition: define.h:780
#define PLAYER
Definition: define.h:113
sint16 hp
Definition: living.h:81
#define FLAG_CAN_ROLL
Definition: define.h:550
#define LOOSE_MANA
Definition: spells.h:189
#define MSG_TYPE_SKILL_ERROR
Definition: newclient.h:502
static object * find_transmution_ob(object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item)
Definition: alchemy.c:450
int rndm(int min, int max)
Definition: utils.c:174
struct obj * chosen_skill
Definition: object.h:237
const char * title
Definition: object.h:170
void remove_ob(object *op)
Definition: object.c:1515
#define F_IDENTIFIED
Definition: define.h:779
#define MSG_TYPE_SKILL_SUCCESS
Definition: newclient.h:503
const char * name_pl
Definition: object.h:168
object * create_archetype(const char *name)
Definition: arch.c:625
char ** arch_name
Definition: recipe.h:13
int total_chance
Definition: recipe.h:31
static int content_recipe_value(object *op)
Definition: alchemy.c:236
const char * materialname
Definition: object.h:197
sint32 weight
Definition: object.h:216
struct mapdef * map
Definition: object.h:155
int summon_hostile_monsters(object *op, int n, const char *monstername)
Definition: spell_util.c:970
struct linked_char * next
Definition: global.h:161
#define FLAG_IDENTIFIED
Definition: define.h:557
sint16 dam
Definition: living.h:87
int strtoint(const char *buf)
Definition: recipe.c:616
static int numb_ob_inside(object *op)
Definition: alchemy.c:266
const char * name
Definition: object.h:167
struct obj * env
Definition: object.h:151
struct obj * below
Definition: object.h:145
void generate_artifact(object *op, int difficulty)
Definition: treasure.c:1906
uint32 nrof
Definition: object.h:184
#define FLAG_IS_CAULDRON
Definition: define.h:635
sint16 y
Definition: object.h:179
void cast_magic_storm(object *op, object *tmp, int lvl)
Definition: spell_effect.c:55
void add_weight(object *op, signed long weight)
Definition: object.c:2486
#define MSG_TYPE_SKILL
Definition: newclient.h:329
#define QUERY_FLAG(xyz, p)
Definition: define.h:514
#define CLEAR_FLAG(xyz, p)
Definition: define.h:512
#define FLAG_WIZ
Definition: define.h:527
int index
Definition: recipe.h:18
object * insert_ob_in_ob(object *op, object *where)
Definition: object.c:2510
#define MAX_BUF
Definition: define.h:81
const char * keycode
Definition: recipe.h:24
object * insert_ob_in_map(object *op, mapstruct *m, object *originator, int flag)
Definition: object.c:1992
unsigned short uint16
Definition: global.h:67
void npc_call_help(object *op)
Definition: monster.c:1632
static int calc_alch_danger(object *caster, object *cauldron, recipe *rp)
Definition: alchemy.c:783
#define FLAG_KNOWN_CURSED
Definition: define.h:617
int exp
Definition: recipe.h:17
#define FLAG_CURSED
Definition: define.h:613
const char * cauldron
Definition: recipe.h:26
uint64 query_cost(const object *tmp, object *who, int flag)
Definition: shop.c:121
int snprintf(char *dest, int max, const char *format,...)
Definition: porting.c:498
#define FORCE
Definition: define.h:296
static int is_defined_recipe(const recipe *rp, const object *cauldron, object *caster)
Definition: alchemy.c:845
const char * name
Definition: global.h:160
#define PREFER_HIGH
Definition: define.h:908
#define SP_MED_FIREBALL
Definition: spells.h:188
living stats
Definition: object.h:219
struct archt * arch
Definition: object.h:263
const char * skill
Definition: recipe.h:25
size_t arch_names
Definition: recipe.h:12
#define SK_EXP_NONE
Definition: skills.h:105
#define FLAG_APPLIED
Definition: define.h:531
struct recipestruct * next
Definition: recipe.h:23
static object * attempt_recipe(object *caster, object *cauldron, int ability, recipe *rp, int nbatches, int ignore_cauldron)
Definition: alchemy.c:305
sstring add_string(const char *str)
Definition: shstr.c:116
#define GET_MAP_OB(M, X, Y)
Definition: map.h:193
struct obj * inv
Definition: object.h:148
#define NDI_UNIQUE
Definition: newclient.h:219
void give_artifact_abilities(object *op, object *artifact)
Definition: treasure.c:1884
void LOG(LogLevel logLevel, const char *format,...)
Definition: logger.c:63
artifact * locate_recipe_artifact(const recipe *rp, size_t idx)
Definition: recipe.c:637
void sub_weight(object *op, signed long weight)
Definition: object.c:1489
unsigned int uint32
Definition: global.h:58
MoveType move_block
Definition: object.h:278
static object * make_item_from_recipe(object *cauldron, recipe *rp)
Definition: alchemy.c:398
object * item
Definition: artifact.h:38
void free_object(object *ob)
Definition: object.c:1238
static const char * cauldron_sound(void)
Definition: alchemy.c:87
int random_roll(int min, int max, const object *op, int goodbad)
Definition: utils.c:51
#define FLAG_NO_PICK
Definition: define.h:535
static recipe * find_recipe(recipelist *fl, int formula, object *ingredients)
Definition: alchemy.c:929
sint16 level
Definition: object.h:202
int use_alchemy(object *op)
Definition: alchemy.c:1006
object * find_skill_by_name(object *who, const char *name)
Definition: skill_util.c:207
static void remove_contents(object *first_ob, object *save_item)
Definition: alchemy.c:745
sint32 value
Definition: object.h:201
sint8 magic
Definition: object.h:199
const char * name
Definition: object.h:322
static void alchemy_failure_effect(object *op, object *cauldron, recipe *rp, int danger)
Definition: alchemy.c:502
struct recipestruct * items
Definition: recipe.h:33
recipelist * get_formulalist(int i)
Definition: recipe.c:112
uint8 type
Definition: object.h:189