1 | /* |
1 | /* |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
2 | * This file is part of Deliantra, the Roguelike Realtime MMORPG. |
3 | * |
3 | * |
4 | * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
4 | * Copyright (©) 2005,2006,2007,2008,2009,2010,2011,2012,2013,2014,2015,2016 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
5 | * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team |
5 | * Copyright (©) 2002 Mark Wedel & Crossfire Development Team |
6 | * Copyright (©) 1992,2007 Frank Tore Johansen |
6 | * Copyright (©) 1992 Frank Tore Johansen |
7 | * |
7 | * |
8 | * Deliantra is free software: you can redistribute it and/or modify it under |
8 | * Deliantra is free software: you can redistribute it and/or modify it under |
9 | * the terms of the Affero GNU General Public License as published by the |
9 | * the terms of the Affero GNU General Public License as published by the |
10 | * Free Software Foundation, either version 3 of the License, or (at your |
10 | * Free Software Foundation, either version 3 of the License, or (at your |
11 | * option) any later version. |
11 | * option) any later version. |
12 | * |
12 | * |
13 | * This program is distributed in the hope that it will be useful, |
13 | * This program is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | * GNU General Public License for more details. |
16 | * GNU General Public License for more details. |
17 | * |
17 | * |
18 | * You should have received a copy of the Affero GNU General Public License |
18 | * You should have received a copy of the Affero GNU General Public License |
19 | * and the GNU General Public License along with this program. If not, see |
19 | * and the GNU General Public License along with this program. If not, see |
20 | * <http://www.gnu.org/licenses/>. |
20 | * <http://www.gnu.org/licenses/>. |
21 | * |
21 | * |
22 | * The authors can be reached via e-mail to <support@deliantra.net> |
22 | * The authors can be reached via e-mail to <support@deliantra.net> |
23 | */ |
23 | */ |
24 | |
24 | |
25 | /* March 96 - Laid down original code. -b.t. thomas@astro.psu.edu */ |
25 | /* March 96 - Laid down original code. -b.t. thomas@astro.psu.edu */ |
26 | |
26 | |
… | |
… | |
61 | static int is_defined_recipe (const recipe *rp, const object *cauldron, object *caster); |
61 | static int is_defined_recipe (const recipe *rp, const object *cauldron, object *caster); |
62 | static recipe *find_recipe (recipelist * fl, int formula, object *ingredients); |
62 | static recipe *find_recipe (recipelist * fl, int formula, object *ingredients); |
63 | |
63 | |
64 | /** Returns a random selection from cauldron_effect[] */ |
64 | /** Returns a random selection from cauldron_effect[] */ |
65 | static const char * |
65 | static const char * |
66 | cauldron_sound (void) |
66 | cauldron_sound () |
67 | { |
67 | { |
68 | int size = sizeof (cauldron_effect) / sizeof (char *); |
68 | return cauldron_effect [rndm (array_length (cauldron_effect))]; |
69 | |
|
|
70 | return cauldron_effect[rndm (0, size - 1)]; |
|
|
71 | } |
|
|
72 | |
|
|
73 | /** |
|
|
74 | * Main part of the ALCHEMY code. From this we call fctns |
|
|
75 | * that take a look at the contents of the 'cauldron' and, using these ingredients, |
|
|
76 | * we construct an integer formula value which is referenced (randomly) against a |
|
|
77 | * formula list (the formula list chosen is based on the # contents of the cauldron). |
|
|
78 | * |
|
|
79 | * If we get a match between the recipe indicated in cauldron contents and a |
|
|
80 | * randomly chosen one, an item is created and experience awarded. Otherwise |
|
|
81 | * various failure effects are possible (getting worse and worse w/ # cauldron |
|
|
82 | * ingredients). Note that the 'item' to be made can be *anything* listed on |
|
|
83 | * the artifacts list in lib/artifacts which has a recipe listed in lib/formulae. |
|
|
84 | * |
|
|
85 | * To those wondering why I am using the funky formula index method: |
|
|
86 | * 1) I want to match recipe to ingredients regardless of ordering. |
|
|
87 | * 2) I want a fast search for the 'right' recipe. |
|
|
88 | * |
|
|
89 | * Note: it is just possible that a totally different combination of |
|
|
90 | * ingredients will result in a match with a given recipe. This is not a bug! |
|
|
91 | * There is no good reason (in my mind) why alchemical processes have to be |
|
|
92 | * unique -- such a 'feature' is one reason why players might want to experiment |
|
|
93 | * around. :) |
|
|
94 | * -b.t. |
|
|
95 | */ |
|
|
96 | |
|
|
97 | void |
|
|
98 | attempt_do_alchemy (object *caster, object *cauldron, object *skill) |
|
|
99 | { |
|
|
100 | recipelist *fl; |
|
|
101 | recipe *rp = NULL; |
|
|
102 | float success_chance; |
|
|
103 | int numb, ability = 1; |
|
|
104 | int formula = 0; |
|
|
105 | float ave_chance; |
|
|
106 | object *item, *skop; |
|
|
107 | |
|
|
108 | if (caster->type != PLAYER) |
|
|
109 | return; /* only players for now */ |
|
|
110 | |
|
|
111 | if (get_map_flags (caster->map, NULL, caster->x, caster->y, NULL, NULL) & P_SAFE) |
|
|
112 | { |
|
|
113 | new_draw_info (NDI_UNIQUE, 0, caster, "This is sacred ground, the gods prevent you from using this device."); |
|
|
114 | return; |
|
|
115 | } |
|
|
116 | |
|
|
117 | /* if no ingredients, no formula! lets forget it */ |
|
|
118 | if (!(formula = content_recipe_value (cauldron))) |
|
|
119 | return; |
|
|
120 | |
|
|
121 | numb = numb_ob_inside (cauldron); |
|
|
122 | if ((fl = get_formulalist (numb))) |
|
|
123 | { |
|
|
124 | if (QUERY_FLAG (caster, FLAG_WIZ)) |
|
|
125 | { |
|
|
126 | rp = find_recipe (fl, formula, cauldron->inv); |
|
|
127 | if (rp != NULL) |
|
|
128 | { |
|
|
129 | #ifdef ALCHEMY_DEBUG |
|
|
130 | if (strcmp (rp->title, "NONE")) |
|
|
131 | LOG (llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], &rp->title); |
|
|
132 | else |
|
|
133 | LOG (llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula / rp->index); |
|
|
134 | #endif |
|
|
135 | attempt_recipe (caster, cauldron, ability, rp, formula / rp->index); |
|
|
136 | } |
|
|
137 | else |
|
|
138 | LOG (llevDebug, "WIZ couldn't find formula for ingredients.\n"); |
|
|
139 | return; |
|
|
140 | } /* End of WIZ alchemy */ |
|
|
141 | |
|
|
142 | /* find the recipe */ |
|
|
143 | rp = find_recipe (fl, formula, cauldron->inv); |
|
|
144 | if (rp |
|
|
145 | && rp->skill == skill->skill |
|
|
146 | && (!rp->cauldron |
|
|
147 | || rp->cauldron == cauldron->arch->archname)) |
|
|
148 | { |
|
|
149 | uint64 value_ingredients; |
|
|
150 | uint64 value_item; |
|
|
151 | object *tmp; |
|
|
152 | int attempt_shadow_alchemy; |
|
|
153 | |
|
|
154 | ave_chance = fl->total_chance / (float) fl->number; |
|
|
155 | |
|
|
156 | ability += (int) (skill->level * ((4.0 + cauldron->magic) / 4.0)); |
|
|
157 | |
|
|
158 | /* determine value of ingredients */ |
|
|
159 | value_ingredients = 0; |
|
|
160 | for (tmp = cauldron->inv; tmp != NULL; tmp = tmp->below) |
|
|
161 | value_ingredients += query_cost (tmp, NULL, F_TRUE); |
|
|
162 | |
|
|
163 | attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster); |
|
|
164 | |
|
|
165 | /* create the object **FIRST**, then decide whether to keep it. */ |
|
|
166 | if ((item = attempt_recipe (caster, cauldron, ability, rp, formula / rp->index)) != NULL) |
|
|
167 | { |
|
|
168 | /* compute base chance of recipe success */ |
|
|
169 | success_chance = ((float) ability / (float) (rp->diff * (item->level + 2))); |
|
|
170 | if (ave_chance == 0) |
|
|
171 | ave_chance = 1; |
|
|
172 | |
|
|
173 | #ifdef ALCHEMY_DEBUG |
|
|
174 | LOG (llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level); |
|
|
175 | #endif |
|
|
176 | |
|
|
177 | value_item = query_cost (item, NULL, F_TRUE | F_IDENTIFIED | F_NOT_CURSED); |
|
|
178 | if (attempt_shadow_alchemy && value_item > value_ingredients) |
|
|
179 | { |
|
|
180 | #ifdef ALCHEMY_DEBUG |
|
|
181 | LOG (llevDebug, |
|
|
182 | "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", |
|
|
183 | value_ingredients, value_item); |
|
|
184 | #endif |
|
|
185 | } |
|
|
186 | /* roll the dice */ |
|
|
187 | else if ((float) (random_roll (0, 101, caster, PREFER_LOW)) <= 100.0 * success_chance) |
|
|
188 | { |
|
|
189 | change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE); |
|
|
190 | |
|
|
191 | // let alchemy consume some time, so that exploits are less easy |
|
|
192 | caster->speed_left -= 1.0; |
|
|
193 | |
|
|
194 | return; |
|
|
195 | } |
|
|
196 | } |
|
|
197 | } |
|
|
198 | } |
|
|
199 | |
|
|
200 | /* if we get here, we failed!! */ |
|
|
201 | alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, rp)); |
|
|
202 | } |
69 | } |
203 | |
70 | |
204 | /** |
71 | /** |
205 | * Recipe value of the entire contents of a container. |
72 | * Recipe value of the entire contents of a container. |
206 | * This appears to just generate a hash value, which I guess for now works |
73 | * This appears to just generate a hash value, which I guess for now works |
207 | * ok, but the possibility of duplicate hashes is certainly possible - msw |
74 | * ok, but the possibility of duplicate hashes is certainly possible - msw |
208 | */ |
75 | */ |
209 | int |
76 | static int |
210 | content_recipe_value (object *op) |
77 | content_recipe_value (object *op) |
211 | { |
78 | { |
212 | char name[MAX_BUF]; |
|
|
213 | object *tmp = op->inv; |
79 | object *tmp = op->inv; |
214 | int tval = 0, formula = 0; |
80 | int formula = 0; |
215 | |
81 | |
216 | while (tmp) |
82 | while (tmp) |
217 | { |
83 | { |
218 | tval = 0; |
84 | const char *name = tmp->title |
219 | assign (name, tmp->name); |
|
|
220 | if (tmp->title) |
|
|
221 | sprintf (name, "%s %s", &tmp->name, &tmp->title); |
85 | ? (const char *)format ("%s %s", &tmp->name, &tmp->title) |
|
|
86 | : tmp->name; |
|
|
87 | |
222 | tval = (strtoint (name) * (tmp->nrof ? tmp->nrof : 1)); |
88 | int tval = strtoint (name) * tmp->number_of (); |
223 | #ifdef ALCHEMY_DEBUG |
89 | #ifdef ALCHEMY_DEBUG |
224 | LOG (llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval); |
90 | LOG (llevDebug, "Got ingredient %d %s(%d)\n", tmp->nrof ? tmp->nrof : 1, name, tval); |
225 | #endif |
91 | #endif |
226 | formula += tval; |
92 | formula += tval; |
227 | tmp = tmp->below; |
93 | tmp = tmp->below; |
228 | } |
94 | } |
229 | #ifdef ALCHEMY_DEBUG |
95 | #ifdef ALCHEMY_DEBUG |
230 | LOG (llevDebug, " Formula value=%d\n", formula); |
96 | LOG (llevDebug, " Formula value=%d\n", formula); |
231 | #endif |
97 | #endif |
|
|
98 | |
232 | return formula; |
99 | return formula; |
233 | } |
100 | } |
234 | |
101 | |
235 | /** |
102 | /** |
236 | * Returns total number of items in op |
103 | * Returns total number of items in op |
237 | */ |
104 | */ |
238 | |
105 | static int |
239 | int |
|
|
240 | numb_ob_inside (object *op) |
106 | numb_ob_inside (object *op) |
241 | { |
107 | { |
242 | object *tmp = op->inv; |
108 | object *tmp = op->inv; |
243 | int number = 0, o_number = 0; |
109 | int number = 0, o_number = 0; |
244 | |
110 | |
… | |
… | |
256 | #endif |
122 | #endif |
257 | return o_number; |
123 | return o_number; |
258 | } |
124 | } |
259 | |
125 | |
260 | /** |
126 | /** |
261 | * Essentially a wrapper for make_item_from_recipe() and |
|
|
262 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
|
|
263 | * gain some exp from (successfull) fabrication of the product. |
|
|
264 | * If nbatches==-1, don't give exp for this creation (random generation/ |
|
|
265 | * failed recipe) |
|
|
266 | */ |
|
|
267 | object * |
|
|
268 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
|
|
269 | { |
|
|
270 | |
|
|
271 | object *item = NULL, *skop; |
|
|
272 | |
|
|
273 | /* this should be passed to this fctn, not effiecent cpu use this way */ |
|
|
274 | int batches = abs (nbatches); |
|
|
275 | |
|
|
276 | /* is the cauldron the right type? */ |
|
|
277 | if (rp->cauldron && rp->cauldron != cauldron->arch->archname) |
|
|
278 | return 0; |
|
|
279 | |
|
|
280 | skop = find_skill_by_name (caster, rp->skill); |
|
|
281 | /* does the caster have the skill? */ |
|
|
282 | if (!skop) |
|
|
283 | return 0; |
|
|
284 | |
|
|
285 | /* code required for this recipe, search the caster */ |
|
|
286 | if (rp->keycode) |
|
|
287 | { |
|
|
288 | object *tmp; |
|
|
289 | |
|
|
290 | for (tmp = caster->inv; tmp; tmp = tmp->below) |
|
|
291 | { |
|
|
292 | if (tmp->type == FORCE && tmp->slaying && rp->keycode == tmp->slaying) |
|
|
293 | break; |
|
|
294 | } |
|
|
295 | |
|
|
296 | if (!tmp) |
|
|
297 | { /* failure--no code found */ |
|
|
298 | new_draw_info (NDI_UNIQUE, 0, caster, "You know the ingredients, but not the technique. Go learn how to do this recipe."); |
|
|
299 | return 0; |
|
|
300 | } |
|
|
301 | } |
|
|
302 | |
|
|
303 | #ifdef EXTREME_ALCHEMY_DEBUG |
|
|
304 | LOG (llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches); |
|
|
305 | LOG (llevDebug, "attempt_recipe(): using recipe %s\n", *rp->title ? &rp->title : "unknown"); |
|
|
306 | #endif |
|
|
307 | |
|
|
308 | if ((item = make_item_from_recipe (cauldron, rp)) != NULL) |
|
|
309 | { |
|
|
310 | remove_contents (cauldron->inv, item); |
|
|
311 | /* Recalc carrying of the cauldron, in case recipe did not conserve mass */ |
|
|
312 | cauldron->update_weight (); |
|
|
313 | /* adj lvl, nrof on caster level */ |
|
|
314 | adjust_product (item, ability, rp->yield ? (rp->yield * batches) : batches); |
|
|
315 | if (!item->env && (item = insert_ob_in_ob (item, cauldron)) == NULL) |
|
|
316 | { |
|
|
317 | new_draw_info (NDI_UNIQUE, 0, caster, "Nothing happened."); |
|
|
318 | /* new_draw_info_format(NDI_UNIQUE, 0,caster, |
|
|
319 | "Your spell causes the %s to explode!",&cauldron->name); */ |
|
|
320 | /* kaboom_cauldron(); */ |
|
|
321 | } |
|
|
322 | else |
|
|
323 | new_draw_info_format (NDI_UNIQUE, 0, caster, "The %s %s.", &cauldron->name, cauldron_sound ()); |
|
|
324 | } |
|
|
325 | |
|
|
326 | return item; |
|
|
327 | } |
|
|
328 | |
|
|
329 | /** |
|
|
330 | * We adjust the nrof, exp and level of the final product, based |
|
|
331 | * on the item's default parameters, and the relevant caster skill level. |
|
|
332 | */ |
|
|
333 | void |
|
|
334 | adjust_product (object *item, int lvl, int yield) |
|
|
335 | { |
|
|
336 | int nrof = 1; |
|
|
337 | |
|
|
338 | if (!yield) |
|
|
339 | yield = 1; |
|
|
340 | |
|
|
341 | if (lvl <= 0) |
|
|
342 | lvl = 1; /* lets avoid div by zero! */ |
|
|
343 | |
|
|
344 | if (item->nrof) |
|
|
345 | { |
|
|
346 | nrof = (int) ((1.0 - 1.0 / (lvl / 10.0 + 1.0)) * (rndm (0, yield - 1) + rndm (0, yield - 1) + rndm (0, yield - 1)) + 1); |
|
|
347 | |
|
|
348 | if (nrof > yield) |
|
|
349 | nrof = yield; |
|
|
350 | |
|
|
351 | item->nrof = nrof; |
|
|
352 | } |
|
|
353 | } |
|
|
354 | |
|
|
355 | /** |
|
|
356 | * Using a list of items and a recipe to make an artifact. |
|
|
357 | * |
|
|
358 | * @param cauldron the cauldron (including the ingredients) used to make the item |
|
|
359 | * |
|
|
360 | * @param rp the recipe to make the artifact from |
|
|
361 | * |
|
|
362 | * @return the newly created object, NULL if something failed |
|
|
363 | */ |
|
|
364 | object * |
|
|
365 | make_item_from_recipe (object *cauldron, recipe *rp) |
|
|
366 | { |
|
|
367 | artifact *art = NULL; |
|
|
368 | object *item = NULL; |
|
|
369 | size_t rp_arch_index; |
|
|
370 | |
|
|
371 | if (rp == NULL) |
|
|
372 | return (object *) NULL; |
|
|
373 | |
|
|
374 | /* Find the appropriate object to transform... */ |
|
|
375 | if ((item = find_transmution_ob (cauldron->inv, rp, &rp_arch_index, 1)) == NULL) |
|
|
376 | { |
|
|
377 | LOG (llevDebug, "make_alchemy_item(): failed to create alchemical object.\n"); |
|
|
378 | return (object *) NULL; |
|
|
379 | } |
|
|
380 | |
|
|
381 | /* Find the appropriate artifact template... */ |
|
|
382 | if (rp->title != shstr_NONE) |
|
|
383 | { |
|
|
384 | if ((art = locate_recipe_artifact (rp, rp_arch_index)) == NULL) |
|
|
385 | { |
|
|
386 | LOG (llevError, "make_alchemy_item(): failed to locate recipe artifact.\n"); |
|
|
387 | LOG (llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], &rp->title); |
|
|
388 | return (object *) NULL; |
|
|
389 | } |
|
|
390 | |
|
|
391 | transmute_materialname (item, art->item); |
|
|
392 | give_artifact_abilities (item, art->item); |
|
|
393 | } |
|
|
394 | |
|
|
395 | if (QUERY_FLAG (cauldron, FLAG_CURSED)) |
|
|
396 | SET_FLAG (item, FLAG_CURSED); |
|
|
397 | if (QUERY_FLAG (cauldron, FLAG_DAMNED)) |
|
|
398 | SET_FLAG (item, FLAG_DAMNED); |
|
|
399 | |
|
|
400 | return item; |
|
|
401 | } |
|
|
402 | |
|
|
403 | /** |
|
|
404 | * Looks through the ingredient list. If we find a |
127 | * Looks through the ingredient list. If we find a |
405 | * suitable object in it - we will use that to make the requested artifact. |
128 | * suitable object in it - we will use that to make the requested artifact. |
406 | * Otherwise the code returns a 'generic' item if create_item is set. -b.t. |
129 | * Otherwise the code returns a 'generic' item if create_item is set. -b.t. |
407 | * |
130 | * |
408 | * @param rp_arch_index pointer to return value; set to arch index for recipe; |
131 | * @param rp_arch_index pointer to return value; set to arch index for recipe; |
409 | * set to zero if not using a transmution formula |
132 | * set to zero if not using a transmution formula |
410 | */ |
133 | */ |
411 | object * |
134 | static object * |
412 | find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) |
135 | find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) |
413 | { |
136 | { |
414 | object *prod_item = 0; |
137 | object *prod_item = 0; |
415 | bool found = false; |
|
|
416 | *rp_arch_index = 0; |
138 | *rp_arch_index = 0; |
417 | |
139 | |
418 | if (rp->transmute) /* look for matching ingredient/prod archs */ |
140 | if (rp->transmute) /* look for matching ingredient/prod archs */ |
419 | for (object *item = first_ingred; item; item = item->below) |
141 | for (object *item = first_ingred; item; item = item->below) |
420 | { |
142 | { |
… | |
… | |
443 | &prod_item->arch->archname, prod_item->nrof); |
165 | &prod_item->arch->archname, prod_item->nrof); |
444 | } |
166 | } |
445 | #endif |
167 | #endif |
446 | if (!prod_item) |
168 | if (!prod_item) |
447 | *rp_arch_index = rndm (rp->arch_names); |
169 | *rp_arch_index = rndm (rp->arch_names); |
448 | prod_item = get_archetype (rp->arch_name[*rp_arch_index]); |
170 | prod_item = archetype::get (rp->arch_name[*rp_arch_index]); |
449 | } |
171 | } |
450 | |
172 | |
451 | #ifdef ALCHEMY_DEBUG |
173 | #ifdef ALCHEMY_DEBUG |
452 | LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no "); |
174 | LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no "); |
453 | if (prod_item != NULL) |
175 | if (prod_item != NULL) |
… | |
… | |
456 | &prod_item->arch->archname, prod_item->stats.sp); |
178 | &prod_item->arch->archname, prod_item->stats.sp); |
457 | } |
179 | } |
458 | #endif |
180 | #endif |
459 | |
181 | |
460 | return prod_item; |
182 | return prod_item; |
|
|
183 | } |
|
|
184 | |
|
|
185 | /** |
|
|
186 | * Using a list of items and a recipe to make an artifact. |
|
|
187 | * |
|
|
188 | * @param cauldron the cauldron (including the ingredients) used to make the item |
|
|
189 | * |
|
|
190 | * @param rp the recipe to make the artifact from |
|
|
191 | * |
|
|
192 | * @return the newly created object, NULL if something failed |
|
|
193 | */ |
|
|
194 | static object * |
|
|
195 | make_item_from_recipe (object *cauldron, recipe *rp) |
|
|
196 | { |
|
|
197 | artifact *art = NULL; |
|
|
198 | object *item = NULL; |
|
|
199 | size_t rp_arch_index; |
|
|
200 | |
|
|
201 | if (rp == NULL) |
|
|
202 | return (object *) NULL; |
|
|
203 | |
|
|
204 | /* Find the appropriate object to transform... */ |
|
|
205 | if ((item = find_transmution_ob (cauldron->inv, rp, &rp_arch_index, 1)) == NULL) |
|
|
206 | { |
|
|
207 | LOG (llevDebug, "make_alchemy_item(): failed to create alchemical object.\n"); |
|
|
208 | return (object *) NULL; |
|
|
209 | } |
|
|
210 | |
|
|
211 | /* Find the appropriate artifact template... */ |
|
|
212 | if (rp->title != shstr_NONE) |
|
|
213 | { |
|
|
214 | if ((art = locate_recipe_artifact (rp, rp_arch_index)) == NULL) |
|
|
215 | { |
|
|
216 | LOG (llevError, "make_alchemy_item(): failed to locate recipe artifact.\n"); |
|
|
217 | LOG (llevDebug, " --requested recipe: %s of %s.\n", rp->arch_name[0], &rp->title); |
|
|
218 | return (object *) NULL; |
|
|
219 | } |
|
|
220 | |
|
|
221 | transmute_materialname (item, art->item); |
|
|
222 | give_artifact_abilities (item, art->item); |
|
|
223 | } |
|
|
224 | |
|
|
225 | if (cauldron->flag [FLAG_CURSED]) |
|
|
226 | item->set_flag (FLAG_CURSED); |
|
|
227 | if (cauldron->flag [FLAG_DAMNED]) |
|
|
228 | item->set_flag (FLAG_DAMNED); |
|
|
229 | |
|
|
230 | return item; |
|
|
231 | } |
|
|
232 | |
|
|
233 | /* |
|
|
234 | * All but object "save_item" are elimentated from |
|
|
235 | * the container list. Note we have to becareful to remove the inventories |
|
|
236 | * of objects in the cauldron inventory (ex icecube has stuff in it). |
|
|
237 | */ |
|
|
238 | static void |
|
|
239 | remove_contents (object *first_ob, object *save_item) |
|
|
240 | { |
|
|
241 | // this cries for a cleaner rewrite, removing save_item first possibly |
|
|
242 | object *next, *tmp = first_ob; |
|
|
243 | |
|
|
244 | while (tmp) |
|
|
245 | { |
|
|
246 | next = tmp->below; |
|
|
247 | |
|
|
248 | if (tmp == save_item) |
|
|
249 | { |
|
|
250 | if (!(tmp = next)) |
|
|
251 | break; |
|
|
252 | else |
|
|
253 | next = next->below; |
|
|
254 | } |
|
|
255 | |
|
|
256 | if (tmp->inv) |
|
|
257 | remove_contents (tmp->inv, NULL); |
|
|
258 | |
|
|
259 | tmp->destroy (); |
|
|
260 | tmp = next; |
|
|
261 | } |
|
|
262 | } |
|
|
263 | |
|
|
264 | /** |
|
|
265 | * We adjust the nrof, exp and level of the final product, based |
|
|
266 | * on the item's default parameters, and the relevant caster skill level. |
|
|
267 | */ |
|
|
268 | static void |
|
|
269 | adjust_product (object *item, int lvl, int yield) |
|
|
270 | { |
|
|
271 | int nrof = 1; |
|
|
272 | |
|
|
273 | if (!yield) |
|
|
274 | yield = 1; |
|
|
275 | |
|
|
276 | if (lvl <= 0) |
|
|
277 | lvl = 1; /* lets avoid div by zero! */ |
|
|
278 | |
|
|
279 | if (item->nrof) |
|
|
280 | { |
|
|
281 | nrof = (int) ((1.0 - 1.0 / (lvl / 10.0 + 1.0)) * (rndm (0, yield - 1) + rndm (0, yield - 1) + rndm (0, yield - 1)) + 1); |
|
|
282 | |
|
|
283 | if (nrof > yield) |
|
|
284 | nrof = yield; |
|
|
285 | |
|
|
286 | item->nrof = nrof; |
|
|
287 | } |
|
|
288 | } |
|
|
289 | |
|
|
290 | /** |
|
|
291 | * Essentially a wrapper for make_item_from_recipe() and |
|
|
292 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
|
|
293 | * gain some exp from (successfull) fabrication of the product. |
|
|
294 | * If nbatches==-1, don't give exp for this creation (random generation/ |
|
|
295 | * failed recipe) |
|
|
296 | */ |
|
|
297 | static object * |
|
|
298 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
|
|
299 | { |
|
|
300 | |
|
|
301 | object *item = NULL, *skop; |
|
|
302 | |
|
|
303 | /* this should be passed to this fctn, not effiecent cpu use this way */ |
|
|
304 | int batches = abs (nbatches); |
|
|
305 | |
|
|
306 | /* is the cauldron the right type? */ |
|
|
307 | if (rp->cauldron && rp->cauldron != cauldron->arch->archname) |
|
|
308 | return 0; |
|
|
309 | |
|
|
310 | skop = find_skill_by_name (caster, rp->skill); |
|
|
311 | /* does the caster have the skill? */ |
|
|
312 | if (!skop) |
|
|
313 | return 0; |
|
|
314 | |
|
|
315 | /* code required for this recipe, search the caster */ |
|
|
316 | if (rp->keycode) |
|
|
317 | { |
|
|
318 | object *tmp; |
|
|
319 | |
|
|
320 | for (tmp = caster->inv; tmp; tmp = tmp->below) |
|
|
321 | { |
|
|
322 | if (tmp->type == FORCE && tmp->slaying && rp->keycode == tmp->slaying) |
|
|
323 | break; |
|
|
324 | } |
|
|
325 | |
|
|
326 | if (!tmp) |
|
|
327 | { /* failure--no code found */ |
|
|
328 | new_draw_info (NDI_UNIQUE, 0, caster, "You know the ingredients, but not the technique. Go learn how to do this recipe."); |
|
|
329 | return 0; |
|
|
330 | } |
|
|
331 | } |
|
|
332 | |
|
|
333 | #ifdef EXTREME_ALCHEMY_DEBUG |
|
|
334 | LOG (llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches); |
|
|
335 | LOG (llevDebug, "attempt_recipe(): using recipe %s\n", *rp->title ? &rp->title : "unknown"); |
|
|
336 | #endif |
|
|
337 | |
|
|
338 | if ((item = make_item_from_recipe (cauldron, rp)) != NULL) |
|
|
339 | { |
|
|
340 | remove_contents (cauldron->inv, item); |
|
|
341 | /* Recalc carrying of the cauldron, in case recipe did not conserve mass */ |
|
|
342 | cauldron->update_weight (); |
|
|
343 | /* adj lvl, nrof on caster level */ |
|
|
344 | adjust_product (item, ability, rp->yield ? rp->yield * batches : batches); |
|
|
345 | |
|
|
346 | if (!item->env && (item = insert_ob_in_ob (item, cauldron)) == NULL) |
|
|
347 | { |
|
|
348 | new_draw_info (NDI_UNIQUE, 0, caster, "Nothing happened."); |
|
|
349 | /* new_draw_info_format(NDI_UNIQUE, 0,caster, |
|
|
350 | "Your spell causes the %s to explode!",&cauldron->name); */ |
|
|
351 | /* kaboom_cauldron(); */ |
|
|
352 | } |
|
|
353 | else |
|
|
354 | new_draw_info_format (NDI_UNIQUE, 0, caster, "The %s %s.", &cauldron->name, cauldron_sound ()); |
|
|
355 | } |
|
|
356 | |
|
|
357 | return item; |
461 | } |
358 | } |
462 | |
359 | |
463 | /** |
360 | /** |
464 | * Ouch. We didnt get the formula we wanted. |
361 | * Ouch. We didnt get the formula we wanted. |
465 | * This fctn simulates the backfire effects--worse effects as the level |
362 | * This fctn simulates the backfire effects--worse effects as the level |
466 | * increases. If SPELL_FAILURE_EFFECTS is defined some really evil things |
363 | * increases. If SPELL_FAILURE_EFFECTS is defined some really evil things |
467 | * can happen to the would be alchemist. This table probably needs some |
364 | * can happen to the would be alchemist. This table probably needs some |
468 | * adjustment for playbalance. -b.t. |
365 | * adjustment for playbalance. -b.t. |
469 | */ |
366 | */ |
470 | void |
367 | static void |
471 | alchemy_failure_effect (object *op, object *cauldron, recipe *rp, int danger) |
368 | alchemy_failure_effect (object *op, object *cauldron, recipe *rp, int danger) |
472 | { |
369 | { |
473 | int level = 0; |
370 | int level = 0; |
474 | |
371 | |
475 | if (!op || !cauldron) |
372 | if (!op || !cauldron) |
… | |
… | |
490 | if (rndm (0, 2)) |
387 | if (rndm (0, 2)) |
491 | { /* slag created */ |
388 | { /* slag created */ |
492 | object *tmp = cauldron->inv; |
389 | object *tmp = cauldron->inv; |
493 | int weight = 0; |
390 | int weight = 0; |
494 | |
391 | |
495 | tmp = get_archetype ("rock"); |
392 | tmp = archetype::get (shstr_rock); |
496 | tmp->weight = weight; |
393 | tmp->weight = weight; |
497 | tmp->value = 0; |
394 | tmp->value = 0; |
498 | tmp->materialname = "stone"; |
395 | tmp->material = name_to_material (shstr_stone); |
499 | tmp->name = "slag"; |
396 | tmp->name = shstr_slag; |
500 | tmp->name_pl = "slags"; |
397 | tmp->name_pl = shstr_slags; |
501 | item = insert_ob_in_ob (tmp, cauldron); |
398 | item = insert_ob_in_ob (tmp, cauldron); |
502 | CLEAR_FLAG (tmp, FLAG_CAN_ROLL); |
399 | tmp->clr_flag (FLAG_CAN_ROLL); |
503 | SET_FLAG (tmp, FLAG_NO_DROP); |
400 | tmp->set_flag (FLAG_NO_DROP); |
504 | tmp->move_block = 0; |
401 | tmp->move_block = 0; |
505 | } |
402 | } |
506 | |
403 | |
507 | remove_contents (cauldron->inv, item); |
404 | remove_contents (cauldron->inv, item); |
508 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
405 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
… | |
… | |
516 | if ((rp = get_random_recipe ((recipelist *) NULL)) == NULL) |
413 | if ((rp = get_random_recipe ((recipelist *) NULL)) == NULL) |
517 | return; |
414 | return; |
518 | |
415 | |
519 | if ((tmp = attempt_recipe (op, cauldron, 1, rp, -1))) |
416 | if ((tmp = attempt_recipe (op, cauldron, 1, rp, -1))) |
520 | { |
417 | { |
521 | if (!QUERY_FLAG (tmp, FLAG_CURSED)) /* curse it */ |
418 | if (!tmp->flag [FLAG_CURSED]) /* curse it */ |
522 | SET_FLAG (tmp, FLAG_CURSED); |
419 | tmp->set_flag (FLAG_CURSED); |
523 | |
420 | |
524 | /* the apply code for potions already deals with cursed |
421 | /* the apply code for potions already deals with cursed |
525 | * potions, so any code here is basically ignored. |
422 | * potions, so any code here is basically ignored. |
526 | */ |
423 | */ |
527 | if (tmp->type == FOOD) |
424 | if (tmp->type == FOOD) |
… | |
… | |
546 | int numb = numb_ob_inside (cauldron); |
443 | int numb = numb_ob_inside (cauldron); |
547 | |
444 | |
548 | fl = get_formulalist (numb - 1); /* take a lower recipe list */ |
445 | fl = get_formulalist (numb - 1); /* take a lower recipe list */ |
549 | if (fl && (rp = get_random_recipe (fl))) |
446 | if (fl && (rp = get_random_recipe (fl))) |
550 | /* even though random, don't grant user any EXP for it */ |
447 | /* even though random, don't grant user any EXP for it */ |
551 | (void) attempt_recipe (op, cauldron, 1, rp, -1); |
448 | attempt_recipe (op, cauldron, 1, rp, -1); |
552 | else |
449 | else |
553 | alchemy_failure_effect (op, cauldron, rp, level - 1); |
450 | alchemy_failure_effect (op, cauldron, rp, level - 1); |
554 | } |
451 | } |
555 | else if (level < 45) |
452 | else if (level < 45) |
556 | { /* INFURIATE NPC's */ |
453 | { /* INFURIATE NPC's */ |
… | |
… | |
567 | |
464 | |
568 | remove_contents (cauldron->inv, NULL); |
465 | remove_contents (cauldron->inv, NULL); |
569 | switch (rndm (0, 2)) |
466 | switch (rndm (0, 2)) |
570 | { |
467 | { |
571 | case 0: |
468 | case 0: |
572 | tmp = get_archetype ("bomb"); |
469 | tmp = archetype::get (shstr_bomb); |
573 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW); |
470 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW); |
574 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW); |
471 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW); |
575 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s creates a bomb!", &cauldron->name); |
472 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s creates a bomb!", &cauldron->name); |
576 | break; |
473 | break; |
577 | |
474 | |
578 | default: |
475 | default: |
579 | tmp = get_archetype ("fireball"); |
476 | tmp = archetype::get (shstr_fireball); |
580 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW) / 5 + 1; |
477 | tmp->stats.dam = random_roll (1, level, op, PREFER_LOW) / 5 + 1; |
581 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW) / 10 + 2; |
478 | tmp->stats.hp = random_roll (1, level, op, PREFER_LOW) / 10 + 2; |
582 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
479 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
583 | break; |
480 | break; |
584 | } |
481 | } |
… | |
… | |
590 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
487 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s %s.", &cauldron->name, cauldron_sound ()); |
591 | remove_contents (cauldron->inv, NULL); |
488 | remove_contents (cauldron->inv, NULL); |
592 | } |
489 | } |
593 | else if (level < 80) |
490 | else if (level < 80) |
594 | { /* MAJOR FIRE */ |
491 | { /* MAJOR FIRE */ |
595 | object *fb = get_archetype (SP_MED_FIREBALL); |
492 | object *fb = archetype::get (SP_MED_FIREBALL); |
596 | |
493 | |
597 | remove_contents (cauldron->inv, NULL); |
494 | remove_contents (cauldron->inv, NULL); |
598 | fire_arch_from_position (cauldron, cauldron, cauldron->x, cauldron->y, 0, fb); |
495 | fire_arch_from_position (cauldron, cauldron, cauldron->x, cauldron->y, 0, fb); |
599 | fb->destroy (); |
496 | fb->destroy (); |
600 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
497 | new_draw_info_format (NDI_UNIQUE, 0, op, "The %s erupts in flame!", &cauldron->name); |
601 | } |
498 | } |
602 | else if (level < 100) |
499 | else if (level < 100) |
603 | { /* WHAMMY the CAULDRON */ |
500 | { /* WHAMMY the CAULDRON */ |
604 | if (!QUERY_FLAG (cauldron, FLAG_CURSED)) |
501 | if (!cauldron->flag [FLAG_CURSED]) |
605 | SET_FLAG (cauldron, FLAG_CURSED); |
502 | cauldron->set_flag (FLAG_CURSED); |
606 | else |
503 | else |
607 | cauldron->magic--; |
504 | cauldron->magic--; |
608 | |
505 | |
609 | cauldron->magic -= random_roll (0, 4, op, PREFER_LOW); |
506 | cauldron->magic -= random_roll (0, 4, op, PREFER_LOW); |
610 | |
507 | |
… | |
… | |
639 | } |
536 | } |
640 | else if (level == 151) |
537 | else if (level == 151) |
641 | { /* CREATE RANDOM ARTIFACT */ |
538 | { /* CREATE RANDOM ARTIFACT */ |
642 | object *tmp; |
539 | object *tmp; |
643 | |
540 | |
644 | /* this is meant to be better than prior possiblity, |
541 | /* this is meant to be better than prior possiblity, |
645 | * in this one, we allow *any* valid alchemy artifact |
542 | * in this one, we allow *any* valid alchemy artifact |
646 | * to be made (rather than only those on the given |
543 | * to be made (rather than only those on the given |
647 | * formulalist) */ |
544 | * formulalist) */ |
648 | if (!rp) |
545 | if (!rp) |
649 | rp = get_random_recipe ((recipelist *) NULL); |
546 | rp = get_random_recipe ((recipelist *) NULL); |
650 | |
547 | |
651 | if (rp && (tmp = get_archetype (rp->arch_name [rndm (rp->arch_names)]))) |
548 | if (rp && (tmp = archetype::get (rp->arch_name [rndm (rp->arch_names)]))) |
652 | { |
549 | { |
653 | generate_artifact (tmp, random_roll (1, op->level / 2 + 1, op, PREFER_HIGH) + 1); |
550 | generate_artifact (tmp, random_roll (1, op->level / 2 + 1, op, PREFER_HIGH) + 1); |
654 | if ((tmp = insert_ob_in_ob (tmp, cauldron))) |
551 | if ((tmp = insert_ob_in_ob (tmp, cauldron))) |
655 | { |
552 | { |
656 | remove_contents (cauldron->inv, tmp); |
553 | remove_contents (cauldron->inv, tmp); |
… | |
… | |
658 | } |
555 | } |
659 | } |
556 | } |
660 | } |
557 | } |
661 | else |
558 | else |
662 | { /* MANA STORM - watch out!! */ |
559 | { /* MANA STORM - watch out!! */ |
663 | object *tmp = get_archetype (LOOSE_MANA); |
560 | object *tmp = archetype::get (LOOSE_MANA); |
664 | |
561 | |
665 | new_draw_info (NDI_UNIQUE, 0, op, "You unwisely release potent forces!"); |
562 | new_draw_info (NDI_UNIQUE, 0, op, "You unwisely release potent forces!"); |
666 | remove_contents (cauldron->inv, NULL); |
563 | remove_contents (cauldron->inv, NULL); |
667 | cast_magic_storm (op, tmp, level); |
564 | cast_magic_storm (op, tmp, level); |
668 | } |
|
|
669 | } |
|
|
670 | |
|
|
671 | /* |
|
|
672 | * All but object "save_item" are elimentated from |
|
|
673 | * the container list. Note we have to becareful to remove the inventories |
|
|
674 | * of objects in the cauldron inventory (ex icecube has stuff in it). |
|
|
675 | */ |
|
|
676 | void |
|
|
677 | remove_contents (object *first_ob, object *save_item) |
|
|
678 | { |
|
|
679 | // this cries for a cleaner rewrite, removing save_item first possibly |
|
|
680 | object *next, *tmp = first_ob; |
|
|
681 | |
|
|
682 | while (tmp) |
|
|
683 | { |
|
|
684 | next = tmp->below; |
|
|
685 | |
|
|
686 | if (tmp == save_item) |
|
|
687 | { |
|
|
688 | if (!(tmp = next)) |
|
|
689 | break; |
|
|
690 | else |
|
|
691 | next = next->below; |
|
|
692 | } |
|
|
693 | |
|
|
694 | if (tmp->inv) |
|
|
695 | remove_contents (tmp->inv, NULL); |
|
|
696 | |
|
|
697 | tmp->destroy (); |
|
|
698 | tmp = next; |
|
|
699 | } |
565 | } |
700 | } |
566 | } |
701 | |
567 | |
702 | /** |
568 | /** |
703 | *"Danger" level, will determine how bad the backfire |
569 | *"Danger" level, will determine how bad the backfire |
704 | * could be if the user fails to concoct a recipe properly. Factors include |
570 | * could be if the user fails to concoct a recipe properly. Factors include |
705 | * the number of ingredients, the length of the name of each ingredient, |
571 | * the number of ingredients, the length of the name of each ingredient, |
706 | * the user's effective level, the user's Int and the enchantment on the |
572 | * the user's effective level, the user's Int and the enchantment on the |
707 | * mixing device (aka "cauldron"). Higher values of 'danger' indicate more |
573 | * mixing device (aka "cauldron"). Higher values of 'danger' indicate more |
708 | * danger. Note that we assume that we have had the caster ready the alchemy |
574 | * danger. Note that we assume that we have had the caster ready the alchemy |
709 | * skill *before* this routine is called. (no longer auto-readies that skill) |
575 | * skill *before* this routine is called. (no longer auto-readies that skill) |
710 | * -b.t. |
576 | * -b.t. |
711 | */ |
577 | */ |
712 | int |
578 | static int |
713 | calc_alch_danger (object *caster, object *cauldron, recipe *rp) |
579 | calc_alch_danger (object *caster, object *cauldron, object *skill, recipe *rp) |
714 | { |
580 | { |
715 | object *item; |
|
|
716 | char name[MAX_BUF]; |
|
|
717 | int danger = 0, nrofi = 0; |
581 | int danger = 0; |
718 | |
582 | |
719 | /* Knowing alchemy skill reduces yer risk */ |
583 | /* Knowing alchemy skill reduces yer risk */ |
720 | danger -= caster->chosen_skill ? caster->chosen_skill->level : caster->level; |
584 | danger -= skill->level; |
721 | |
|
|
722 | if (!caster->chosen_skill) |
|
|
723 | LOG (llevError | logBacktrace, "calc_alch_danger called without a chosen skill, caster %s, cauldron %s\n", |
|
|
724 | caster->debug_desc (), cauldron->debug_desc ()); |
|
|
725 | |
585 | |
726 | /* better cauldrons reduce risk */ |
586 | /* better cauldrons reduce risk */ |
727 | danger -= cauldron->magic; |
587 | danger -= cauldron->magic; |
728 | |
588 | |
729 | /* Higher Int, lower the risk */ |
589 | /* Higher Int, lower the risk */ |
… | |
… | |
731 | |
591 | |
732 | /* Ingredients. Longer names usually mean rarer stuff. |
592 | /* Ingredients. Longer names usually mean rarer stuff. |
733 | * Thus the backfire is worse. Also, more ingredients |
593 | * Thus the backfire is worse. Also, more ingredients |
734 | * means we are attempting a more powerfull potion, |
594 | * means we are attempting a more powerfull potion, |
735 | * and thus the backfire will be worse. */ |
595 | * and thus the backfire will be worse. */ |
736 | for (item = cauldron->inv; item; item = item->below) |
596 | for (object *item = cauldron->inv; item; item = item->below) |
737 | { |
597 | { |
738 | assign (name, item->name); |
598 | const char *name = item->title |
739 | if (item->title) |
|
|
740 | sprintf (name, "%s %s", &item->name, &item->title); |
599 | ? format ("%s %s", &item->name, &item->title) |
|
|
600 | : &item->name; |
|
|
601 | |
741 | danger += (strtoint (name) / 1000) + 3; |
602 | danger += strtoint (name) / 1000 + 3; |
742 | nrofi++; |
|
|
743 | } |
603 | } |
744 | |
604 | |
745 | if (rp == NULL) |
605 | if (!rp) |
746 | danger += 110; |
606 | danger += 110; |
747 | else |
607 | else |
748 | danger += rp->diff * 3; |
608 | danger += rp->diff * 3; |
749 | |
609 | |
750 | /* Using a bad device is *majorly* stupid */ |
610 | /* Using a bad device is *majorly* stupid */ |
751 | if (QUERY_FLAG (cauldron, FLAG_CURSED)) |
611 | if (cauldron->flag [FLAG_CURSED]) danger += 80; |
752 | danger += 80; |
612 | if (cauldron->flag [FLAG_DAMNED]) danger += 200; |
753 | if (QUERY_FLAG (cauldron, FLAG_DAMNED)) |
|
|
754 | danger += 200; |
|
|
755 | |
613 | |
756 | #ifdef ALCHEMY_DEBUG |
614 | #ifdef ALCHEMY_DEBUG |
757 | LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger); |
615 | LOG (llevDebug, "calc_alch_danger() returned danger=%d\n", danger); |
758 | #endif |
616 | #endif |
759 | |
617 | |
… | |
… | |
934 | else |
792 | else |
935 | LOG (llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula / result->index); |
793 | LOG (llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula / result->index); |
936 | #endif |
794 | #endif |
937 | return result; |
795 | return result; |
938 | } |
796 | } |
|
|
797 | |
|
|
798 | /** |
|
|
799 | * Main part of the ALCHEMY code. From this we call fctns |
|
|
800 | * that take a look at the contents of the 'cauldron' and, using these ingredients, |
|
|
801 | * we construct an integer formula value which is referenced (randomly) against a |
|
|
802 | * formula list (the formula list chosen is based on the # contents of the cauldron). |
|
|
803 | * |
|
|
804 | * If we get a match between the recipe indicated in cauldron contents and a |
|
|
805 | * randomly chosen one, an item is created and experience awarded. Otherwise |
|
|
806 | * various failure effects are possible (getting worse and worse w/ # cauldron |
|
|
807 | * ingredients). Note that the 'item' to be made can be *anything* listed on |
|
|
808 | * the artifacts list in lib/artifacts which has a recipe listed in lib/formulae. |
|
|
809 | * |
|
|
810 | * To those wondering why I am using the funky formula index method: |
|
|
811 | * 1) I want to match recipe to ingredients regardless of ordering. |
|
|
812 | * 2) I want a fast search for the 'right' recipe. |
|
|
813 | * |
|
|
814 | * Note: it is just possible that a totally different combination of |
|
|
815 | * ingredients will result in a match with a given recipe. This is not a bug! |
|
|
816 | * There is no good reason (in my mind) why alchemical processes have to be |
|
|
817 | * unique -- such a 'feature' is one reason why players might want to experiment |
|
|
818 | * around. :) |
|
|
819 | * -b.t. |
|
|
820 | */ |
|
|
821 | void |
|
|
822 | attempt_do_alchemy (object *caster, object *cauldron, object *skill) |
|
|
823 | { |
|
|
824 | recipelist *fl; |
|
|
825 | recipe *rp = NULL; |
|
|
826 | float success_chance; |
|
|
827 | int numb, ability = 1; |
|
|
828 | int formula = 0; |
|
|
829 | float ave_chance; |
|
|
830 | object *item; |
|
|
831 | |
|
|
832 | if (caster->type != PLAYER) |
|
|
833 | return; /* only players for now */ |
|
|
834 | |
|
|
835 | if (get_map_flags (caster->map, NULL, caster->x, caster->y, NULL, NULL) & P_SAFE) |
|
|
836 | { |
|
|
837 | new_draw_info (NDI_UNIQUE, 0, caster, "This is sacred ground, the gods prevent you from using this device."); |
|
|
838 | return; |
|
|
839 | } |
|
|
840 | |
|
|
841 | /* if no ingredients, no formula! lets forget it */ |
|
|
842 | if (!(formula = content_recipe_value (cauldron))) |
|
|
843 | return; |
|
|
844 | |
|
|
845 | numb = numb_ob_inside (cauldron); |
|
|
846 | if ((fl = get_formulalist (numb))) |
|
|
847 | { |
|
|
848 | if (caster->flag [FLAG_WIZ]) |
|
|
849 | { |
|
|
850 | rp = find_recipe (fl, formula, cauldron->inv); |
|
|
851 | if (rp != NULL) |
|
|
852 | { |
|
|
853 | #ifdef ALCHEMY_DEBUG |
|
|
854 | if (strcmp (rp->title, "NONE")) |
|
|
855 | LOG (llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], &rp->title); |
|
|
856 | else |
|
|
857 | LOG (llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula / rp->index); |
|
|
858 | #endif |
|
|
859 | attempt_recipe (caster, cauldron, ability, rp, formula / rp->index); |
|
|
860 | } |
|
|
861 | else |
|
|
862 | LOG (llevDebug, "WIZ couldn't find formula for ingredients.\n"); |
|
|
863 | return; |
|
|
864 | } /* End of WIZ alchemy */ |
|
|
865 | |
|
|
866 | /* find the recipe */ |
|
|
867 | rp = find_recipe (fl, formula, cauldron->inv); |
|
|
868 | if (rp |
|
|
869 | && rp->skill == skill->skill |
|
|
870 | && (!rp->cauldron |
|
|
871 | || rp->cauldron == cauldron->arch->archname)) |
|
|
872 | { |
|
|
873 | uint64 value_ingredients; |
|
|
874 | uint64 value_item; |
|
|
875 | object *tmp; |
|
|
876 | int attempt_shadow_alchemy; |
|
|
877 | |
|
|
878 | ave_chance = fl->total_chance / (float)fl->number; |
|
|
879 | |
|
|
880 | ability += skill->level * ((4.0 + cauldron->magic) / 4.0); |
|
|
881 | |
|
|
882 | /* determine value of ingredients */ |
|
|
883 | value_ingredients = 0; |
|
|
884 | for (tmp = cauldron->inv; tmp; tmp = tmp->below) |
|
|
885 | value_ingredients += query_cost (tmp, NULL, F_TRUE); |
|
|
886 | |
|
|
887 | attempt_shadow_alchemy = !is_defined_recipe (rp, cauldron, caster); |
|
|
888 | |
|
|
889 | /* create the object **FIRST**, then decide whether to keep it. */ |
|
|
890 | if ((item = attempt_recipe (caster, cauldron, ability, rp, formula / rp->index)) != NULL) |
|
|
891 | { |
|
|
892 | /* compute base chance of recipe success */ |
|
|
893 | success_chance = ((float)ability / (float)(rp->diff * (item->level + 2))); |
|
|
894 | if (ave_chance == 0) |
|
|
895 | ave_chance = 1; |
|
|
896 | |
|
|
897 | #ifdef ALCHEMY_DEBUG |
|
|
898 | LOG (llevDebug, "percent success chance = %f ab%d / diff%d*lev%d\n", success_chance, ability, rp->diff, item->level); |
|
|
899 | #endif |
|
|
900 | |
|
|
901 | value_item = query_cost (item, NULL, F_TRUE | F_IDENTIFIED | F_NOT_CURSED); |
|
|
902 | if (attempt_shadow_alchemy && value_item > value_ingredients) |
|
|
903 | { |
|
|
904 | #ifdef ALCHEMY_DEBUG |
|
|
905 | LOG (llevDebug, |
|
|
906 | "Forcing failure for shadow alchemy recipe because price of ingredients (%llu) is less than price of result (%llu).\n", |
|
|
907 | value_ingredients, value_item); |
|
|
908 | #endif |
|
|
909 | } |
|
|
910 | /* roll the dice */ |
|
|
911 | else if (random_roll (0, 101, caster, PREFER_LOW) <= 100.0 * success_chance) |
|
|
912 | { |
|
|
913 | change_exp (caster, rp->exp, rp->skill, SK_EXP_NONE); |
|
|
914 | |
|
|
915 | // let alchemy consume some time, so that exploits are less easy |
|
|
916 | caster->speed_left -= 1.0; |
|
|
917 | |
|
|
918 | return; |
|
|
919 | } |
|
|
920 | } |
|
|
921 | } |
|
|
922 | } |
|
|
923 | |
|
|
924 | /* if we get here, we failed!! */ |
|
|
925 | alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, skill, rp)); |
|
|
926 | } |
|
|
927 | |