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 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
4 | * Copyright (©) 2005,2006,2007,2008,2009 Marc Alexander Lehmann / Robin Redeker / the Deliantra team |
5 | * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team |
5 | * Copyright (©) 2002,2007 Mark Wedel & Crossfire Development Team |
6 | * Copyright (©) 1992,2007 Frank Tore Johansen |
6 | * Copyright (©) 1992,2007 Frank Tore Johansen |
7 | * |
7 | * |
8 | * Deliantra is free software: you can redistribute it and/or modify |
8 | * Deliantra is free software: you can redistribute it and/or modify it under |
9 | * it under the terms of the GNU General Public License as published by |
9 | * the terms of the Affero GNU General Public License as published by the |
10 | * the Free Software Foundation, either version 3 of the License, or |
10 | * Free Software Foundation, either version 3 of the License, or (at your |
11 | * (at your 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 GNU General Public License |
18 | * You should have received a copy of the Affero GNU General Public License |
19 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | * and the GNU General Public License along with this program. If not, see |
|
|
20 | * <http://www.gnu.org/licenses/>. |
20 | * |
21 | * |
21 | * 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> |
22 | */ |
23 | */ |
23 | |
24 | |
24 | /* 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 */ |
… | |
… | |
55 | "rattles", |
56 | "rattles", |
56 | "makes chugging sounds", |
57 | "makes chugging sounds", |
57 | "smokes heavily for a while" |
58 | "smokes heavily for a while" |
58 | }; |
59 | }; |
59 | |
60 | |
60 | |
|
|
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 | |
|
|
64 | |
63 | |
65 | /** Returns a random selection from cauldron_effect[] */ |
64 | /** Returns a random selection from cauldron_effect[] */ |
66 | static const char * |
65 | static const char * |
67 | cauldron_sound (void) |
66 | cauldron_sound (void) |
68 | { |
67 | { |
69 | int size = sizeof (cauldron_effect) / sizeof (char *); |
68 | int size = sizeof (cauldron_effect) / sizeof (char *); |
70 | |
69 | |
71 | return cauldron_effect[rndm (0, size - 1)]; |
70 | return cauldron_effect[rndm (0, size - 1)]; |
72 | } |
71 | } |
73 | |
72 | |
74 | /**
|
73 | /** |
75 | * Main part of the ALCHEMY code. From this we call fctns |
74 | * Main part of the ALCHEMY code. From this we call fctns |
76 | * that take a look at the contents of the 'cauldron' and, using these ingredients, |
75 | * that take a look at the contents of the 'cauldron' and, using these ingredients, |
77 | * we construct an integer formula value which is referenced (randomly) against a |
76 | * we construct an integer formula value which is referenced (randomly) against a |
78 | * formula list (the formula list chosen is based on the # contents of the cauldron). |
77 | * formula list (the formula list chosen is based on the # contents of the cauldron). |
79 | * |
78 | * |
… | |
… | |
127 | rp = find_recipe (fl, formula, cauldron->inv); |
126 | rp = find_recipe (fl, formula, cauldron->inv); |
128 | if (rp != NULL) |
127 | if (rp != NULL) |
129 | { |
128 | { |
130 | #ifdef ALCHEMY_DEBUG |
129 | #ifdef ALCHEMY_DEBUG |
131 | if (strcmp (rp->title, "NONE")) |
130 | if (strcmp (rp->title, "NONE")) |
132 | LOG (llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], rp->title); |
131 | LOG (llevDebug, "WIZ got formula: %s of %s\n", rp->arch_name[0], &rp->title); |
133 | else |
132 | else |
134 | LOG (llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula / rp->index); |
133 | LOG (llevDebug, "WIZ got formula: %s (nbatches:%d)\n", rp->arch_name[0], formula / rp->index); |
135 | #endif |
134 | #endif |
136 | attempt_recipe (caster, cauldron, ability, rp, formula / rp->index); |
135 | attempt_recipe (caster, cauldron, ability, rp, formula / rp->index); |
137 | } |
136 | } |
… | |
… | |
200 | |
199 | |
201 | /* if we get here, we failed!! */ |
200 | /* if we get here, we failed!! */ |
202 | alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, rp)); |
201 | alchemy_failure_effect (caster, cauldron, rp, calc_alch_danger (caster, cauldron, rp)); |
203 | } |
202 | } |
204 | |
203 | |
205 | /**
|
204 | /** |
206 | * Recipe value of the entire contents of a container. |
205 | * Recipe value of the entire contents of a container. |
207 | * This appears to just generate a hash value, which I guess for now works |
206 | * This appears to just generate a hash value, which I guess for now works |
208 | * ok, but the possibility of duplicate hashes is certainly possible - msw |
207 | * ok, but the possibility of duplicate hashes is certainly possible - msw |
209 | */ |
208 | */ |
210 | |
|
|
211 | int |
209 | int |
212 | content_recipe_value (object *op) |
210 | content_recipe_value (object *op) |
213 | { |
211 | { |
214 | char name[MAX_BUF]; |
212 | char name[MAX_BUF]; |
215 | object *tmp = op->inv; |
213 | object *tmp = op->inv; |
… | |
… | |
232 | LOG (llevDebug, " Formula value=%d\n", formula); |
230 | LOG (llevDebug, " Formula value=%d\n", formula); |
233 | #endif |
231 | #endif |
234 | return formula; |
232 | return formula; |
235 | } |
233 | } |
236 | |
234 | |
237 | /**
|
235 | /** |
238 | * Returns total number of items in op
|
236 | * Returns total number of items in op |
239 | */ |
237 | */ |
240 | |
238 | |
241 | int |
239 | int |
242 | numb_ob_inside (object *op) |
240 | numb_ob_inside (object *op) |
243 | { |
241 | { |
… | |
… | |
252 | number++; |
250 | number++; |
253 | o_number++; |
251 | o_number++; |
254 | tmp = tmp->below; |
252 | tmp = tmp->below; |
255 | } |
253 | } |
256 | #ifdef ALCHEMY_DEBUG |
254 | #ifdef ALCHEMY_DEBUG |
257 | LOG (llevDebug, "numb_ob_inside(%s): found %d ingredients\n", op->name, o_number); |
255 | LOG (llevDebug, "numb_ob_inside(%s): found %d ingredients\n", &op->name, o_number); |
258 | #endif |
256 | #endif |
259 | return o_number; |
257 | return o_number; |
260 | } |
258 | } |
261 | |
259 | |
262 | /** |
260 | /** |
… | |
… | |
264 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
262 | * insert_ob_in_ob. If the caster has some alchemy skill, then they might |
265 | * gain some exp from (successfull) fabrication of the product. |
263 | * gain some exp from (successfull) fabrication of the product. |
266 | * If nbatches==-1, don't give exp for this creation (random generation/ |
264 | * If nbatches==-1, don't give exp for this creation (random generation/ |
267 | * failed recipe) |
265 | * failed recipe) |
268 | */ |
266 | */ |
269 | |
|
|
270 | object * |
267 | object * |
271 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
268 | attempt_recipe (object *caster, object *cauldron, int ability, recipe *rp, int nbatches) |
272 | { |
269 | { |
273 | |
270 | |
274 | object *item = NULL, *skop; |
271 | object *item = NULL, *skop; |
… | |
… | |
303 | } |
300 | } |
304 | } |
301 | } |
305 | |
302 | |
306 | #ifdef EXTREME_ALCHEMY_DEBUG |
303 | #ifdef EXTREME_ALCHEMY_DEBUG |
307 | LOG (llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches); |
304 | LOG (llevDebug, "attempt_recipe(): got %d nbatches\n", nbatches); |
308 | LOG (llevDebug, "attempt_recipe(): using recipe %s\n", rp->title ? rp->title : "unknown"); |
305 | LOG (llevDebug, "attempt_recipe(): using recipe %s\n", *rp->title ? &rp->title : "unknown"); |
309 | #endif |
306 | #endif |
310 | |
307 | |
311 | if ((item = make_item_from_recipe (cauldron, rp)) != NULL) |
308 | if ((item = make_item_from_recipe (cauldron, rp)) != NULL) |
312 | { |
309 | { |
313 | remove_contents (cauldron->inv, item); |
310 | remove_contents (cauldron->inv, item); |
… | |
… | |
327 | } |
324 | } |
328 | |
325 | |
329 | return item; |
326 | return item; |
330 | } |
327 | } |
331 | |
328 | |
332 | |
|
|
333 | |
|
|
334 | /** |
329 | /** |
335 | * We adjust the nrof, exp and level of the final product, based |
330 | * We adjust the nrof, exp and level of the final product, based |
336 | * on the item's default parameters, and the relevant caster skill level.
|
331 | * on the item's default parameters, and the relevant caster skill level. |
337 | */ |
332 | */ |
338 | void |
333 | void |
339 | adjust_product (object *item, int lvl, int yield) |
334 | adjust_product (object *item, int lvl, int yield) |
340 | { |
335 | { |
341 | int nrof = 1; |
336 | int nrof = 1; |
… | |
… | |
354 | nrof = yield; |
349 | nrof = yield; |
355 | |
350 | |
356 | item->nrof = nrof; |
351 | item->nrof = nrof; |
357 | } |
352 | } |
358 | } |
353 | } |
359 | |
|
|
360 | |
354 | |
361 | /** |
355 | /** |
362 | * Using a list of items and a recipe to make an artifact. |
356 | * Using a list of items and a recipe to make an artifact. |
363 | * |
357 | * |
364 | * @param cauldron the cauldron (including the ingredients) used to make the item |
358 | * @param cauldron the cauldron (including the ingredients) used to make the item |
365 | * |
359 | * |
366 | * @param rp the recipe to make the artifact from |
360 | * @param rp the recipe to make the artifact from |
367 | * |
361 | * |
368 | * @return the newly created object, NULL if something failed |
362 | * @return the newly created object, NULL if something failed |
369 | */ |
363 | */ |
370 | |
|
|
371 | object * |
364 | object * |
372 | make_item_from_recipe (object *cauldron, recipe *rp) |
365 | make_item_from_recipe (object *cauldron, recipe *rp) |
373 | { |
366 | { |
374 | artifact *art = NULL; |
367 | artifact *art = NULL; |
375 | object *item = NULL; |
368 | object *item = NULL; |
… | |
… | |
405 | SET_FLAG (item, FLAG_DAMNED); |
398 | SET_FLAG (item, FLAG_DAMNED); |
406 | |
399 | |
407 | return item; |
400 | return item; |
408 | } |
401 | } |
409 | |
402 | |
410 | |
|
|
411 | /**
|
403 | /** |
412 | * Looks through the ingredient list. If we find a |
404 | * Looks through the ingredient list. If we find a |
413 | * suitable object in it - we will use that to make the requested artifact. |
405 | * suitable object in it - we will use that to make the requested artifact. |
414 | * Otherwise the code returns a 'generic' item if create_item is set. -b.t. |
406 | * Otherwise the code returns a 'generic' item if create_item is set. -b.t. |
415 | * |
407 | * |
416 | * @param rp_arch_index pointer to return value; set to arch index for recipe; |
408 | * @param rp_arch_index pointer to return value; set to arch index for recipe; |
417 | * set to zero if not using a transmution formula |
409 | * set to zero if not using a transmution formula |
418 | */ |
410 | */ |
419 | |
|
|
420 | object * |
411 | object * |
421 | find_transmution_ob (object *first_ingred, recipe *rp, size_t * rp_arch_index, int create_item) |
412 | find_transmution_ob (object *first_ingred, recipe *rp, size_t *rp_arch_index, int create_item) |
422 | { |
413 | { |
423 | object *item = NULL; |
414 | object *prod_item = 0; |
424 | |
415 | bool found = false; |
425 | *rp_arch_index = 0; |
416 | *rp_arch_index = 0; |
426 | |
417 | |
427 | if (rp->transmute) /* look for matching ingredient/prod archs */ |
418 | if (rp->transmute) /* look for matching ingredient/prod archs */ |
428 | for (item = first_ingred; item; item = item->below) |
419 | for (object *item = first_ingred; item; item = item->below) |
429 | { |
420 | { |
430 | int i; |
|
|
431 | |
|
|
432 | for (int i = 0; i < rp->arch_names; i++) |
421 | for (int i = 0; i < rp->arch_names; i++) |
433 | //TODO: should be a shstr comparison |
422 | //TODO: should be a shstr comparison |
434 | if (&item->arch->archname == rp->arch_name[i]) |
423 | if (!strcmp(&item->arch->archname,rp->arch_name[i])) |
435 | { |
424 | { |
436 | *rp_arch_index = i; |
425 | *rp_arch_index = i; |
|
|
426 | prod_item = item; |
437 | break; |
427 | break; |
438 | } |
428 | } |
439 | |
429 | |
440 | if (i < rp->arch_names) |
430 | if (prod_item) |
441 | break; |
431 | break; |
442 | } |
432 | } |
443 | |
433 | |
444 | /* failed, create a fresh object. Note no nrof>1 because that would |
434 | /* failed, create a fresh object. Note no nrof>1 because that would |
445 | * allow players to create massive amounts of artifacts easily */ |
435 | * allow players to create massive amounts of artifacts easily */ |
446 | if (create_item && (!item || item->nrof > 1)) |
436 | if (create_item && (!prod_item || prod_item->nrof > 1)) |
|
|
437 | { |
|
|
438 | #ifdef ALCHEMY_DEBUG |
|
|
439 | LOG (llevDebug, "creating a new item.\n"); |
|
|
440 | if (prod_item != NULL) |
447 | { |
441 | { |
|
|
442 | LOG (llevDebug, " had item: arch %s(nrof:%d)\n", |
|
|
443 | &prod_item->arch->archname, prod_item->nrof); |
|
|
444 | } |
|
|
445 | #endif |
|
|
446 | if (!prod_item) |
448 | *rp_arch_index = rndm (rp->arch_names); |
447 | *rp_arch_index = rndm (rp->arch_names); |
449 | item = get_archetype (rp->arch_name[*rp_arch_index]); |
448 | prod_item = get_archetype (rp->arch_name[*rp_arch_index]); |
450 | } |
449 | } |
451 | |
450 | |
452 | #ifdef ALCHEMY_DEBUG |
451 | #ifdef ALCHEMY_DEBUG |
453 | LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no "); |
452 | LOG (llevDebug, "recipe calls for%stransmution.\n", rp->transmute ? " " : " no "); |
454 | if (item != NULL) |
453 | if (prod_item != NULL) |
455 | { |
454 | { |
456 | LOG (llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", item->arch->name, item->stats.sp); |
455 | LOG (llevDebug, " find_transmutable_ob(): returns arch %s(sp:%d)\n", |
|
|
456 | &prod_item->arch->archname, prod_item->stats.sp); |
457 | } |
457 | } |
458 | #endif |
458 | #endif |
459 | |
459 | |
460 | return item; |
460 | return prod_item; |
461 | } |
461 | } |
462 | |
462 | |
463 | |
|
|
464 | /**
|
463 | /** |
465 | * Ouch. We didnt get the formula we wanted. |
464 | * Ouch. We didnt get the formula we wanted. |
466 | * This fctn simulates the backfire effects--worse effects as the level |
465 | * This fctn simulates the backfire effects--worse effects as the level |
467 | * increases. If SPELL_FAILURE_EFFECTS is defined some really evil things |
466 | * increases. If SPELL_FAILURE_EFFECTS is defined some really evil things |
468 | * can happen to the would be alchemist. This table probably needs some |
467 | * can happen to the would be alchemist. This table probably needs some |
469 | * adjustment for playbalance. -b.t. |
468 | * adjustment for playbalance. -b.t. |
470 | */ |
469 | */ |
471 | |
|
|
472 | void |
470 | void |
473 | alchemy_failure_effect (object *op, object *cauldron, recipe *rp, int danger) |
471 | alchemy_failure_effect (object *op, object *cauldron, recipe *rp, int danger) |
474 | { |
472 | { |
475 | int level = 0; |
473 | int level = 0; |
476 | |
474 | |
… | |
… | |
881 | { |
879 | { |
882 | /* check if recipe matches at all */ |
880 | /* check if recipe matches at all */ |
883 | if (formula % rp->index != 0) |
881 | if (formula % rp->index != 0) |
884 | { |
882 | { |
885 | #ifdef EXTREME_ALCHEMY_DEBUG |
883 | #ifdef EXTREME_ALCHEMY_DEBUG |
886 | LOG (llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], rp->title, rp->index); |
884 | LOG (llevDebug, " formula %s of %s (%d) does not match\n", rp->arch_name[0], &rp->title, rp->index); |
887 | #endif |
885 | #endif |
888 | continue; |
886 | continue; |
889 | } |
887 | } |
890 | |
888 | |
891 | if (rp->transmute && find_transmution_ob (ingredients, rp, &rp_arch_index, 0) != NULL) |
889 | if (rp->transmute && find_transmution_ob (ingredients, rp, &rp_arch_index, 0) != NULL) |
892 | { |
890 | { |
893 | #ifdef EXTREME_ALCHEMY_DEBUG |
891 | #ifdef EXTREME_ALCHEMY_DEBUG |
894 | LOG (llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], rp->title, rp->index); |
892 | LOG (llevDebug, " formula %s of %s (%d) is a matching transmuting formula\n", rp->arch_name[rp_arch_index], &rp->title, rp->index); |
895 | #endif |
893 | #endif |
896 | /* transmution recipe with matching base ingredient */ |
894 | /* transmution recipe with matching base ingredient */ |
897 | if (!transmute_found) |
895 | if (!transmute_found) |
898 | { |
896 | { |
899 | transmute_found = 1; |
897 | transmute_found = 1; |
… | |
… | |
901 | } |
899 | } |
902 | } |
900 | } |
903 | else if (transmute_found) |
901 | else if (transmute_found) |
904 | { |
902 | { |
905 | #ifdef EXTREME_ALCHEMY_DEBUG |
903 | #ifdef EXTREME_ALCHEMY_DEBUG |
906 | LOG (llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], rp->title, |
904 | LOG (llevDebug, " formula %s of %s (%d) matches but is not a matching transmuting formula\n", rp->arch_name[0], &rp->title, |
907 | rp->index); |
905 | rp->index); |
908 | #endif |
906 | #endif |
909 | /* "normal" recipe found after previous transmution recipe => ignore this recipe */ |
907 | /* "normal" recipe found after previous transmution recipe => ignore this recipe */ |
910 | continue; |
908 | continue; |
911 | } |
909 | } |
912 | #ifdef EXTREME_ALCHEMY_DEBUG |
910 | #ifdef EXTREME_ALCHEMY_DEBUG |
913 | else |
911 | else |
914 | { |
912 | { |
915 | LOG (llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], rp->title, rp->index); |
913 | LOG (llevDebug, " formula %s of %s (%d) matches\n", rp->arch_name[0], &rp->title, rp->index); |
916 | } |
914 | } |
917 | #endif |
915 | #endif |
918 | |
916 | |
919 | if (rndm (0, recipes_matching) == 0) |
917 | if (rndm (0, recipes_matching) == 0) |
920 | result = rp; |
918 | result = rp; |
… | |
… | |
930 | return NULL; |
928 | return NULL; |
931 | } |
929 | } |
932 | |
930 | |
933 | #ifdef ALCHEMY_DEBUG |
931 | #ifdef ALCHEMY_DEBUG |
934 | if (strcmp (result->title, "NONE") != 0) |
932 | if (strcmp (result->title, "NONE") != 0) |
935 | LOG (llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], result->title, formula / result->index); |
933 | LOG (llevDebug, "got formula: %s of %s (nbatches:%d)\n", result->arch_name[0], &result->title, formula / result->index); |
936 | else |
934 | else |
937 | LOG (llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula / result->index); |
935 | LOG (llevDebug, "got formula: %s (nbatches:%d)\n", result->arch_name[0], formula / result->index); |
938 | #endif |
936 | #endif |
939 | return result; |
937 | return result; |
940 | } |
938 | } |