00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00035 #include <global.h>
00036 #include <spells.h>
00037 #include <object.h>
00038 #include <errno.h>
00039 #ifndef __CEXTRACT__
00040 #include <sproto.h>
00041 #endif
00042 #include <sounds.h>
00043 #include <assert.h>
00044
00045 extern const char *const spell_mapping[];
00046
00060 object *find_random_spell_in_ob(object *ob, const char *skill) {
00061 int k = 0, s;
00062 object *tmp;
00063
00064 for (tmp = ob->inv; tmp; tmp = tmp->below)
00065 if (tmp->type == SPELL && (!skill || tmp->skill == skill))
00066 k++;
00067
00068
00069 if (!k)
00070 return NULL;
00071
00072 s = RANDOM()%k;
00073
00074 for (tmp = ob->inv; tmp; tmp = tmp->below)
00075 if (tmp->type == SPELL && (!skill || tmp->skill == skill)) {
00076 if (!s)
00077 return tmp;
00078 else
00079 s--;
00080 }
00081
00082 return NULL;
00083 }
00084
00105 void set_spell_skill(object *op, object *caster, object *spob, object *dest) {
00106 if (dest->skill)
00107 FREE_AND_CLEAR_STR(dest->skill);
00108 if (caster == op && spob->skill)
00109 dest->skill = add_refcount(spob->skill);
00110 else if (caster->skill)
00111 dest->skill = add_refcount(caster->skill);
00112 }
00113
00120 void check_spells(void) {
00121 #ifdef SPELL_DEBUG
00122 int i;
00123 archetype *at;
00124
00125 LOG(llevDebug, "Checking spells...\n");
00126
00127 for (at = first_archetype; at; at = at->next) {
00128 if (at->clone.type == SPELL) {
00129 if (at->clone.skill) {
00130 for (i = 1; i < NUM_SKILLS; i++)
00131 if (!strcmp(skill_names[i], at->clone.skill))
00132 break;
00133 if (i == NUM_SKILLS) {
00134 LOG(llevError, "Spell %s has improper associated skill %s\n", at->name, at->clone.skill);
00135 }
00136 }
00137
00138 }
00139 }
00140
00141 i = 0;
00142 while (spell_mapping[i]) {
00143 if (!find_archetype(spell_mapping[i])) {
00144 LOG(llevError, "Unable to find spell mapping %s (%i)\n", spell_mapping[i], i);
00145 }
00146 i++;
00147 }
00148 LOG(llevDebug, "Checking spells completed.\n");
00149 #endif
00150 }
00151
00157 void dump_spells(void) {
00158 archetype *at;
00159
00160 for (at = first_archetype; at; at = at->next) {
00161 if (at->clone.type == SPELL) {
00162 fprintf(stderr, "%s:%s:%s:%s:%d\n", at->clone.name ? at->clone.name : "null",
00163 at->name, at->clone.other_arch ? at->clone.other_arch->name : "null",
00164 at->clone.skill ? at->clone.skill : "null", at->clone.level);
00165 }
00166 }
00167 }
00168
00182 void spell_effect(object *spob, int x, int y, mapstruct *map, object *originator) {
00183
00184 if (spob->other_arch != NULL) {
00185 object *effect = arch_to_object(spob->other_arch);
00186
00187 effect->x = x;
00188 effect->y = y;
00189
00190 insert_ob_in_map(effect, map, originator, 0);
00191 }
00192 }
00193
00207 int min_casting_level(const object *caster, const object *spell) {
00208 int new_level;
00209
00210 if (caster->path_denied&spell->path_attuned) {
00211
00212
00213
00214 return 1;
00215 }
00216 new_level = spell->level
00217 +((caster->path_repelled&spell->path_attuned) ? +2 : 0)
00218 +((caster->path_attuned&spell->path_attuned) ? -2 : 0);
00219 return MAX(new_level, 1);
00220 }
00221
00222
00237 int caster_level(const object *caster, const object *spell) {
00238 int level = caster->level;
00239
00240
00241 if (caster->type == PLAYER && spell->skill) {
00242 int i;
00243
00244 for (i = 0; i < NUM_SKILLS; i++)
00245 if (caster->contr->last_skill_ob[i]
00246 && caster->contr->last_skill_ob[i]->skill == spell->skill) {
00247 level = caster->contr->last_skill_ob[i]->level;
00248 break;
00249 }
00250 }
00251
00252 level += ((caster->path_repelled&spell->path_attuned) ? -2 : 0)
00253 +((caster->path_attuned&spell->path_attuned) ? 2 : 0);
00254
00255
00256
00257
00258 if (level < 1)
00259 level = 1;
00260 return level;
00261 }
00262
00281 sint16 SP_level_spellpoint_cost(object *caster, object *spell, int flags) {
00282 int sp, grace, level = caster_level(caster, spell);
00283
00284 if (settings.spellpoint_level_depend == TRUE) {
00285 if (spell->stats.sp && spell->stats.maxsp) {
00286 sp = (int)(spell->stats.sp*(1.0+MAX(0, (float)(level-spell->level)/(float)spell->stats.maxsp)));
00287 } else
00288 sp = spell->stats.sp;
00289
00290 sp *= PATH_SP_MULT(caster, spell);
00291 if (!sp && spell->stats.sp)
00292 sp = 1;
00293
00294 if (spell->stats.grace && spell->stats.maxgrace) {
00295 grace = (int)(spell->stats.grace*(1.0+MAX(0, (float)(level-spell->level)/(float)spell->stats.maxgrace)));
00296 } else
00297 grace = spell->stats.grace;
00298
00299 grace *= PATH_SP_MULT(caster, spell);
00300 if (spell->stats.grace && !grace)
00301 grace = 1;
00302 } else {
00303 sp = spell->stats.sp*PATH_SP_MULT(caster, spell);
00304 if (spell->stats.sp && !sp)
00305 sp = 1;
00306 grace = spell->stats.grace*PATH_SP_MULT(caster, spell);
00307 if (spell->stats.grace && !grace)
00308 grace = 1;
00309 }
00310 if (flags == SPELL_HIGHEST)
00311 return MAX(sp, grace);
00312 else if (flags == SPELL_GRACE)
00313 return grace;
00314 else if (flags == SPELL_MANA)
00315 return sp;
00316 else {
00317 LOG(llevError, "SP_level_spellpoint_cost: Unknown flags passed: %d\n", flags);
00318 return 0;
00319 }
00320 }
00321
00332 int SP_level_dam_adjust(const object *caster, const object *spob) {
00333 int level = caster_level(caster, spob);
00334 int adj = level-min_casting_level(caster, spob);
00335
00336 if (adj < 0)
00337 adj = 0;
00338 if (spob->dam_modifier)
00339 adj /= spob->dam_modifier;
00340 else
00341 adj = 0;
00342 return adj;
00343 }
00344
00357 int SP_level_duration_adjust(const object *caster, const object *spob) {
00358 int level = caster_level(caster, spob);
00359 int adj = level-min_casting_level(caster, spob);
00360
00361 if (adj < 0)
00362 adj = 0;
00363 if (spob->duration_modifier)
00364 adj /= spob->duration_modifier;
00365 else
00366 adj = 0;
00367
00368 return adj;
00369 }
00370
00383 int SP_level_range_adjust(const object *caster, const object *spob) {
00384 int level = caster_level(caster, spob);
00385 int adj = level-min_casting_level(caster, spob);
00386
00387 if (adj < 0)
00388 adj = 0;
00389 if (spob->range_modifier)
00390 adj /= spob->range_modifier;
00391 else
00392 adj = 0;
00393
00394 return adj;
00395 }
00396
00408 object *check_spell_known(object *op, const char *name) {
00409 object *spop;
00410
00411 for (spop = op->inv; spop; spop = spop->below)
00412 if (spop->type == SPELL && !strcmp(spop->name, name))
00413 return spop;
00414
00415 return NULL;
00416 }
00417
00430 object *lookup_spell_by_name(object *op, const char *spname) {
00431 object *spob1 = NULL, *spob2 = NULL, *spob;
00432 int nummatch = 0;
00433
00434 if (spname == NULL)
00435 return NULL;
00436
00437
00438
00439
00440
00441 for (spob = op->inv; spob; spob = spob->below) {
00442 if (spob->type == SPELL) {
00443 if (!strncmp(spob->name, spname, strlen(spname))) {
00444 if (strlen(spname) == strlen(spob->name))
00445
00446 return spob;
00447 nummatch++;
00448 spob1 = spob;
00449 } else if (!strncmp(spob->name, spname, strlen(spob->name))) {
00450
00451
00452
00453
00454
00455 if (spob2)
00456 LOG(llevError, "Found multiple spells with overlapping base names: %s, %s\n", spob2->name, spob->name);
00457 spob2 = spob;
00458 }
00459 }
00460 }
00461
00462
00463
00464 if (spob2)
00465 return spob2;
00466 if (spob1 && nummatch == 1)
00467 return spob1;
00468 return NULL;
00469 }
00470
00490 int reflwall(mapstruct *m, int x, int y, object *sp_op) {
00491 object *op;
00492
00493 if (OUT_OF_REAL_MAP(m, x, y))
00494 return 0;
00495 for (op = GET_MAP_OB(m, x, y); op != NULL; op = op->above)
00496 if (QUERY_FLAG(op, FLAG_REFL_SPELL)
00497 && (!QUERY_FLAG(op, FLAG_ALIVE) || (rndm(0, 99)) < 90-(sp_op->level/10)))
00498 return 1;
00499
00500 return 0;
00501 }
00502
00517 int cast_create_obj(object *op, object *new_op, int dir) {
00518 mapstruct *m;
00519 sint16 sx, sy;
00520
00521 if (dir
00522 && ((get_map_flags(op->map, &m, op->x+freearr_x[dir], op->y+freearr_y[dir], &sx, &sy)&P_OUT_OF_MAP)
00523 || OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(m, sx, sy)))) {
00524 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_INFO,
00525 "Something is in the way. You cast it at your feet.", NULL);
00526 dir = 0;
00527 }
00528 new_op->x = op->x+freearr_x[dir];
00529 new_op->y = op->y+freearr_y[dir];
00530 if (dir == 0)
00531 insert_ob_in_map(new_op, op->map, op, INS_BELOW_ORIGINATOR);
00532 else
00533 insert_ob_in_map(new_op, op->map, op, 0);
00534 return dir;
00535 }
00536
00555 int ok_to_put_more(mapstruct *m, sint16 x, sint16 y, object *op, uint32 immune_stop) {
00556 object *tmp;
00557 int mflags;
00558 mapstruct *mp;
00559
00560 mp = m;
00561 mflags = get_map_flags(m, &mp, x, y, &x, &y);
00562
00563 if (mflags&P_OUT_OF_MAP)
00564 return 0;
00565
00566 if (OB_TYPE_MOVE_BLOCK(op, GET_MAP_MOVE_BLOCK(mp, x, y)))
00567 return 0;
00568
00569 for (tmp = GET_MAP_OB(mp, x, y); tmp != NULL; tmp = tmp->above) {
00570
00571
00572
00573
00574
00575
00576
00577
00578
00579 if ((tmp->attacktype&AT_COUNTERSPELL)
00580 && (tmp->type != PLAYER)
00581 && !QUERY_FLAG(tmp, FLAG_MONSTER)
00582 && (tmp->type != WEAPON)
00583 && (tmp->type != BOW)
00584 && (tmp->type != ARROW)
00585 && (tmp->type != GOLEM)
00586 && (immune_stop&AT_MAGIC))
00587 return 0;
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598
00599 if (tmp->type == op->type
00600 && tmp->subtype == op->subtype
00601 && tmp->stats.maxhp
00602 && op->stats.maxhp) {
00603 if ((tmp->stats.maxhp == op->stats.maxhp)
00604 || (tmp->spell_tags && OB_SPELL_TAG_MATCH(tmp, op->stats.maxhp))
00605 || (op->spell_tags && OB_SPELL_TAG_MATCH(op, tmp->stats.maxhp))) {
00606 statistics.spell_suppressions++;
00607 return 0;
00608 }
00609
00610
00611
00612
00613
00614 if (op->spell_tags && tmp->spell_tags) {
00615 int i;
00616
00617 for (i = 0; i < SPELL_TAG_SIZE; i++) {
00618 if (op->spell_tags[i] && op->spell_tags[i] == tmp->spell_tags[i])
00619 statistics.spell_suppressions++;
00620 return 0;
00621 }
00622 }
00623 }
00624
00625
00626
00627 }
00628
00629 return 1;
00630 }
00631
00653 int fire_arch_from_position(object *op, object *caster, sint16 x, sint16 y, int dir, object *spell) {
00654 object *tmp;
00655 int mflags;
00656 mapstruct *m;
00657
00658 if (spell->other_arch == NULL)
00659 return 0;
00660
00661 m = op->map;
00662 mflags = get_map_flags(m, &m, x, y, &x, &y);
00663 if (mflags&P_OUT_OF_MAP) {
00664 return 0;
00665 }
00666
00667 tmp = arch_to_object(spell->other_arch);
00668 if (tmp == NULL)
00669 return 0;
00670
00671 if (OB_TYPE_MOVE_BLOCK(tmp, GET_MAP_MOVE_BLOCK(m, x, y))) {
00672 if (caster->type == PLAYER)
00673
00674 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR,
00675 "You can't cast the spell on top of a wall!", NULL);
00676 free_object(tmp);
00677 return 0;
00678 }
00679
00680
00681
00682 tmp->stats.dam = spell->stats.dam+SP_level_dam_adjust(caster, spell);
00683 tmp->duration = spell->duration+SP_level_duration_adjust(caster, spell);
00684
00685 tmp->stats.food = tmp->duration;
00686 tmp->range = spell->range+SP_level_range_adjust(caster, spell);
00687 tmp->attacktype = spell->attacktype;
00688 tmp->x = x;
00689 tmp->y = y;
00690 tmp->direction = dir;
00691 if (get_owner(op) != NULL)
00692 copy_owner(tmp, op);
00693 else
00694 set_owner(tmp, op);
00695 tmp->level = caster_level(caster, spell);
00696 set_spell_skill(op, caster, spell, tmp);
00697
00698
00699 if (tmp->attacktype&AT_HOLYWORD || tmp->attacktype&AT_GODPOWER) {
00700 if (!tailor_god_spell(tmp, op))
00701 return 0;
00702 }
00703 if (QUERY_FLAG(tmp, FLAG_IS_TURNABLE))
00704 SET_ANIMATION(tmp, dir);
00705
00706 if ((tmp = insert_ob_in_map(tmp, m, op, 0)) == NULL)
00707 return 1;
00708
00709 ob_process(tmp);
00710
00711 return 1;
00712 }
00713
00714
00715
00716
00717
00718
00719
00720
00721
00728 void regenerate_rod(object *rod) {
00729 if (rod->stats.hp < rod->stats.maxhp) {
00730 rod->stats.hp += 1+rod->stats.maxhp/10;
00731
00732 if (rod->stats.hp > rod->stats.maxhp)
00733 rod->stats.hp = rod->stats.maxhp;
00734 }
00735 }
00736
00743 void drain_rod_charge(object *rod) {
00744 rod->stats.hp -= SP_level_spellpoint_cost(rod, rod->inv, SPELL_HIGHEST);
00745 }
00746
00753 void drain_wand_charge(object *wand) {
00754 assert(wand->type == WAND);
00755
00756 if (!(--wand->stats.food)) {
00757 object *tmp;
00758 if (wand->arch) {
00759 CLEAR_FLAG(wand, FLAG_ANIMATE);
00760 wand->face = wand->arch->clone.face;
00761 wand->speed = 0;
00762 update_ob_speed(wand);
00763 }
00764 if ((tmp = get_player_container(wand)))
00765 esrv_update_item(UPD_ANIM, tmp, wand);
00766 }
00767 }
00768
00779 object *find_target_for_friendly_spell(object *op, int dir) {
00780 object *tmp;
00781 mapstruct *m;
00782 sint16 x, y;
00783 int mflags;
00784
00785
00786
00787
00788
00789 if (op->type != PLAYER && op->type != RUNE) {
00790 tmp = get_owner(op);
00791
00792
00793
00794 if (!tmp || !QUERY_FLAG(tmp, FLAG_MONSTER))
00795 tmp = op;
00796 } else {
00797 m = op->map;
00798 x = op->x+freearr_x[dir];
00799 y = op->y+freearr_y[dir];
00800
00801 mflags = get_map_flags(m, &m, x, y, &x, &y);
00802
00803 if (mflags&P_OUT_OF_MAP)
00804 tmp = NULL;
00805 else {
00806 for (tmp = GET_MAP_OB(m, x, y); tmp != NULL; tmp = tmp->above) {
00807 if (tmp->type == PLAYER)
00808 break;
00809 }
00810 }
00811 }
00812
00813 if (tmp == NULL)
00814 for (tmp = GET_MAP_OB(op->map, op->x, op->y); tmp != NULL; tmp = tmp->above) {
00815 if (tmp->type == PLAYER)
00816 break;
00817
00818 if (tmp->type == TRANSPORT) {
00819 object *inv;
00820
00821 for (inv = tmp->inv; inv; inv = inv->below) {
00822 if ((inv->type == PLAYER) && (op == inv))
00823 return inv;
00824 }
00825 }
00826 }
00827 return tmp;
00828 }
00829
00830
00831
00852 int spell_find_dir(mapstruct *m, int x, int y, object *exclude) {
00853 int i, max = SIZEOFFREE;
00854 sint16 nx, ny;
00855 int owner_type = 0, mflags;
00856 object *tmp;
00857 mapstruct *mp;
00858
00859 if (exclude && exclude->head)
00860 exclude = exclude->head;
00861 if (exclude && exclude->type)
00862 owner_type = exclude->type;
00863
00864 for (i = rndm(1, 8); i < max; i++) {
00865 nx = x+freearr_x[i];
00866 ny = y+freearr_y[i];
00867 mp = m;
00868 mflags = get_map_flags(m, &mp, nx, ny, &nx, &ny);
00869 if (mflags&(P_OUT_OF_MAP|P_BLOCKSVIEW))
00870 continue;
00871
00872 tmp = GET_MAP_OB(mp, nx, ny);
00873
00874 while (tmp != NULL
00875 && (((owner_type == PLAYER && !QUERY_FLAG(tmp, FLAG_MONSTER) && !QUERY_FLAG(tmp, FLAG_GENERATOR) && !(tmp->type == PLAYER && op_on_battleground(tmp, NULL, NULL, NULL)))
00876 || (owner_type != PLAYER && tmp->type != PLAYER))
00877 || (tmp == exclude || (tmp->head && tmp->head == exclude))))
00878 tmp = tmp->above;
00879
00880 if (tmp != NULL && can_see_monsterP(m, x, y, i))
00881 return freedir[i];
00882 }
00883 return -1;
00884 }
00885
00886
00887
00901 static int put_a_monster(object *op, const char *monstername) {
00902 object *tmp, *head = NULL, *prev = NULL;
00903 archetype *at;
00904 int dir;
00905
00906
00907
00908 if ((at = find_archetype(monstername)) == NULL)
00909 return 0;
00910
00911
00912
00913
00914
00915 dir = find_first_free_spot(&at->clone, op->map, op->x, op->y);
00916 if (dir != -1) {
00917
00918
00919
00920 while (at != NULL) {
00921 tmp = arch_to_object(at);
00922 tmp->x = op->x+freearr_x[dir]+at->clone.x;
00923 tmp->y = op->y+freearr_y[dir]+at->clone.y;
00924 tmp->map = op->map;
00925 if (head) {
00926 tmp->head = head;
00927 prev->more = tmp;
00928 }
00929 if (!head)
00930 head = tmp;
00931 prev = tmp;
00932 at = at->more;
00933 }
00934
00935 if (head->randomitems)
00936 create_treasure(head->randomitems, head, GT_INVISIBLE, op->map->difficulty, 0);
00937
00938 insert_ob_in_map(head, op->map, op, 0);
00939
00940
00941 tmp = create_archetype("burnout");
00942 tmp->map = op->map;
00943 tmp->x = op->x+freearr_x[dir];
00944 tmp->y = op->y+freearr_y[dir];
00945 insert_ob_in_map(tmp, op->map, op, 0);
00946 return 1;
00947 } else {
00948 return 0;
00949 }
00950 }
00951
00970 int summon_hostile_monsters(object *op, int n, const char *monstername) {
00971 int i, put = 0;
00972
00973 for (i = 0; i < n; i++)
00974 put += put_a_monster(op, monstername);
00975
00976 return put;
00977 }
00978
00979
01004 void shuffle_attack(object *op, int change_face) {
01005 int i;
01006
01007 i = rndm(0, 21);
01008 op->attacktype = ATTACKS[i].attacktype|AT_MAGIC;
01009 if (change_face) {
01010 SET_ANIMATION(op, ATTACKS[i].face);
01011 }
01012 }
01013
01014
01025 static void prayer_failure(object *op, int failure, int power) {
01026 const char *godname;
01027 object *tmp;
01028
01029 if (!strcmp((godname = determine_god(op)), "none"))
01030 godname = "Your spirit";
01031
01032 if (failure <= -20 && failure > -40) {
01033 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01034 "%s gives a sign to renew your faith.",
01035 "%s gives a sign to renew your faith.",
01036 godname);
01037 tmp = create_archetype(SPELL_WONDER);
01038 cast_cone(op, op, 0, tmp);
01039 free_object(tmp);
01040 } else if (failure <= -40 && failure > -60) {
01041 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01042 "Your diety touches your mind!", NULL);
01043 confuse_living(op, op, 99);
01044 } else if (failure <= -60 && failure > -150) {
01045 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01046 "%s requires you to pray NOW. You comply, ignoring all else.",
01047 "%s requires you to pray NOW. You comply, ignoring all else.",
01048 godname);
01049
01050 paralyze_living(op, op, 99);
01051 } else if (failure <= -150) {
01052 tmp = create_archetype(GOD_POWER);
01053 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01054 "%s smites you!",
01055 "%s smites you!",
01056 godname);
01057
01058
01059
01060
01061
01062 cast_magic_storm(op, tmp, power>50?50:power);
01063 }
01064 }
01065
01078 void spell_failure(object *op, int failure, int power, object *skill) {
01079 object *tmp;
01080
01081 if (settings.spell_failure_effects == FALSE)
01082 return;
01083
01084 if (failure <= -20 && failure > -40) {
01085 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01086 "Your spell causes an unexpected effect.", NULL);
01087 tmp = create_archetype(SPELL_WONDER);
01088 cast_cone(op, op, 0, tmp);
01089 free_object(tmp);
01090 } else if (failure <= -40 && failure > -60) {
01091 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01092 "Your magic recoils on you, making you confused!", NULL);
01093 confuse_living(op, op, 99);
01094 } else if (failure <= -60 && failure > -80) {
01095 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01096 "Your magic stuns you!", NULL);
01097 paralyze_living(op, op, 99);
01098 } else if (failure <= -80) {
01099 object *tmp;
01100
01101
01102 if (get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL)&P_NO_MAGIC) {
01103 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01104 "The magic warps and you are turned inside out!", NULL);
01105 hit_player(op, 9998, op, AT_INTERNAL, 1);
01106 } else {
01107 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01108 "You lose control of the mana! The uncontrolled magic blasts you!",
01109 NULL);
01110 tmp = create_archetype(LOOSE_MANA);
01111 tmp->level = skill->level;
01112 tmp->x = op->x;
01113 tmp->y = op->y;
01114
01115
01116 tmp->range += isqrt(power);
01117
01118 if (power > 25)
01119 tmp->stats.dam = 25+isqrt(power);
01120 else
01121 tmp->stats.dam = power;
01122
01123 tmp->stats.maxhp = tmp->count;
01124 insert_ob_in_map(tmp, op->map, NULL, 0);
01125 }
01126 }
01127 }
01128
01134 static int can_be_transmuted(object *op) {
01135 if (op->invisible)
01136 return 0;
01137
01138 if (op->type == POTION || op->type == SCROLL || op->type == WAND || op->type == ROD || op->type == WEAPON)
01139 return 1;
01140
01141 return 0;
01142 }
01143
01156 static void transmute_item_to_flower(object *op) {
01157 object *force;
01158 object *item;
01159 object *flower;
01160 object *first = NULL;
01161 int count = 0;
01162 char name[HUGE_BUF];
01163
01164 for (item = op->inv; item; item = item->below) {
01165 if (can_be_transmuted(item)) {
01166 if (!first)
01167 first = item;
01168 count++;
01169 }
01170 }
01171
01172 if (count == 0)
01173 return;
01174
01175 count = rndm(0, count-1);
01176 for (item = first; item; item = item->below) {
01177 if (can_be_transmuted(item)) {
01178 count--;
01179 if (count < 0)
01180 break;
01181 }
01182 }
01183
01184 if (!item)
01185 return;
01186
01187 force = create_archetype(FORCE_NAME);
01188 force->duration = 100+rndm(0, 10)*100;
01189 force->subtype = FORCE_TRANSFORMED_ITEM;
01190 force->speed = 1;
01191 update_ob_speed(force);
01192
01193 flower = create_archetype("flowers_permanent");
01194
01195 if (QUERY_FLAG(item, FLAG_APPLIED))
01196 manual_apply(op, item, AP_NOPRINT|AP_IGNORE_CURSE|AP_UNAPPLY);
01197 remove_ob(item);
01198 flower->weight = item->nrof ? item->nrof*item->weight : item->weight;
01199 item->weight = 0;
01200 esrv_del_item(op->contr, item->count);
01201 insert_ob_in_ob(item, force);
01202
01203 query_short_name(item, name, HUGE_BUF);
01204 draw_ext_info_format(NDI_UNIQUE, 0, op,
01205 MSG_TYPE_ITEM, MSG_TYPE_ITEM_CHANGE,
01206 "Your %s turns to a flower!",
01207 "Your %s turns to a flower!",
01208 name);
01209
01210 insert_ob_in_ob(force, flower);
01211 flower = insert_ob_in_ob(flower, op);
01212 esrv_send_item(op, flower);
01213 }
01214
01225 static void swap_random_stats(object *op) {
01226 object *force;
01227 int first, second;
01228
01229 first = RANDOM()%NUM_STATS;
01230 second = RANDOM()%(NUM_STATS-1);
01231 if (second >= first)
01232 second++;
01233
01234 draw_ext_info_format(NDI_UNIQUE, 0, op,
01235 MSG_TYPE_VICTIM, MSG_TYPE_VICTIM_SPELL,
01236 "You suddenly feel really weird!",
01237 "You suddenly feel really weird!");
01238
01239 force = create_archetype(FORCE_NAME);
01240 force->duration = 100+rndm(0, 10)*100;
01241 force->speed = 1;
01242 SET_FLAG(force, FLAG_APPLIED);
01243 set_attr_value(&force->stats, second, get_attr_value(&op->stats, first)-get_attr_value(&op->stats, second));
01244 set_attr_value(&force->stats, first, get_attr_value(&op->stats, second)-get_attr_value(&op->stats, first));
01245 update_ob_speed(force);
01246 insert_ob_in_ob(force, op);
01247 change_abil(op, force);
01248 fix_object(op);
01249 }
01250
01261 static void handle_spell_confusion(object *op) {
01262 switch (RANDOM()%2) {
01263 case 0:
01264 transmute_item_to_flower(op);
01265 break;
01266
01267 case 1:
01268 swap_random_stats(op);
01269 break;
01270 }
01271 }
01272
01308 int cast_spell(object *op, object *caster, int dir, object *spell_ob, char *stringarg) {
01309
01310 const char *godname;
01311 int success = 0, mflags, cast_level = 0, old_shoottype;
01312 object *skill = NULL;
01313 int confusion_effect = 0;
01314
01315 old_shoottype = op->contr ? op->contr->shoottype : 0;
01316
01317 if (!spell_ob) {
01318 LOG(llevError, "cast_spell: null spell object passed\n");
01319 return 0;
01320 }
01321 if (!strcmp((godname = determine_god(op)), "none"))
01322 godname = "A random spirit";
01323
01324
01325 if (!caster) {
01326 LOG(llevError, "cast_spell: null caster object passed\n");
01327 return 0;
01328 }
01329 if (spell_ob->anim_suffix)
01330 apply_anim_suffix(caster, spell_ob->anim_suffix);
01331
01332
01333 if (QUERY_FLAG(op, FLAG_CONFUSED) && caster == op && op->type == PLAYER) {
01334 if (rndm(0, 5) < 4) {
01335 spell_ob = find_random_spell_in_ob(op, NULL);
01336 draw_ext_info_format(NDI_UNIQUE, 0, op,
01337 MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01338 "In your confused state, you're not sure of what you cast!",
01339 "In your confused state, you're not sure of what you cast!");
01340 } else
01341
01342 confusion_effect = 1;
01343 }
01344
01345
01346
01347
01348 if ((caster->path_denied&spell_ob->path_attuned) && !QUERY_FLAG(caster, FLAG_WIZ)) {
01349 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR,
01350 "That spell path is denied to you.", NULL);
01351 return 0;
01352 }
01353
01354
01355
01356
01357
01358
01359
01360
01361
01362
01363 if (op->type == PLAYER && op == caster) {
01364 cast_level = caster_level(caster, spell_ob);
01365 if (spell_ob->skill) {
01366 skill = find_skill_by_name(op, spell_ob->skill);
01367 if (!skill) {
01368 draw_ext_info_format(NDI_UNIQUE, 0, op,
01369 MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
01370 "You need the skill %s to cast %s.",
01371 "You need the skill %s to cast %s.",
01372 spell_ob->skill, spell_ob->name);
01373 return 0;
01374 }
01375 if (min_casting_level(op, spell_ob) > cast_level && !QUERY_FLAG(op, FLAG_WIZ)) {
01376 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SKILL, MSG_TYPE_SKILL_FAILURE,
01377 "You lack enough skill to cast that spell.", NULL);
01378 return 0;
01379 }
01380 }
01381
01382
01383
01384 if (!QUERY_FLAG(op, FLAG_WIZ)) {
01385 if (SP_level_spellpoint_cost(caster, spell_ob, SPELL_MANA)
01386 && SP_level_spellpoint_cost(caster, spell_ob, SPELL_MANA) > op->stats.sp) {
01387 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR,
01388 "You don't have enough mana.", NULL);
01389 return 0;
01390 }
01391 if (SP_level_spellpoint_cost(caster, spell_ob, SPELL_GRACE)
01392 && SP_level_spellpoint_cost(caster, spell_ob, SPELL_GRACE) > op->stats.grace) {
01393 if (random_roll(0, op->stats.Wis-1, op, PREFER_HIGH)+op->stats.grace-10*SP_level_spellpoint_cost(caster, spell_ob, SPELL_GRACE)/op->stats.maxgrace > 0) {
01394 draw_ext_info_format(NDI_UNIQUE, 0, op,
01395 MSG_TYPE_SPELL, MSG_TYPE_SPELL_INFO,
01396 "%s grants your prayer, though you are unworthy.",
01397 "%s grants your prayer, though you are unworthy.",
01398 godname);
01399 } else {
01400 prayer_failure(op, op->stats.grace, SP_level_spellpoint_cost(caster, spell_ob, SPELL_GRACE)-op->stats.grace);
01401 draw_ext_info_format(NDI_UNIQUE, 0, op,
01402 MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01403 "%s ignores your prayer.",
01404 "%s ignores your prayer.",
01405 godname);
01406 return 0;
01407 }
01408 }
01409
01410
01411 if (spell_ob->stats.grace
01412 && random_roll(0, 99, op, PREFER_HIGH) < (spell_ob->level/(float)MAX(1, op->level)*cleric_chance[op->stats.Wis])) {
01413 play_sound_player_only(op->contr, SOUND_TYPE_SPELL, spell_ob, 0, "fumble");
01414 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01415 "You fumble the spell.", NULL);
01416 if (settings.casting_time == TRUE) {
01417 op->casting_time = -1;
01418 }
01419 op->stats.grace -= random_roll(1, SP_level_spellpoint_cost(caster, spell_ob, SPELL_GRACE), op, PREFER_LOW);
01420 return 0;
01421 } else if (spell_ob->stats.sp) {
01422 int failure = random_roll(0, 199, op, PREFER_HIGH)-op->contr->encumbrance+op->level-spell_ob->level+35;
01423
01424 if (failure < 0) {
01425 draw_ext_info(NDI_UNIQUE, 0, op,
01426 MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01427 "You bungle the spell because you have too much heavy equipment in use.",
01428 NULL);
01429 if (settings.spell_failure_effects == TRUE)
01430 spell_failure(op, failure, SP_level_spellpoint_cost(caster, spell_ob, SPELL_MANA), skill);
01431 op->contr->shoottype = old_shoottype;
01432 op->stats.sp -= random_roll(0, SP_level_spellpoint_cost(caster, spell_ob, SPELL_MANA), op, PREFER_LOW);
01433 return 0;
01434 }
01435 }
01436 }
01437 }
01438
01439 mflags = get_map_flags(op->map, NULL, op->x, op->y, NULL, NULL);
01440
01441
01442
01443
01444
01445
01446 if (spell_ob->type == SPELL
01447 && caster->type != POTION
01448 && !QUERY_FLAG(op, FLAG_WIZCAST)
01449 && (QUERY_FLAG(caster, FLAG_ALIVE) || QUERY_FLAG(op, FLAG_ALIVE))
01450 && !QUERY_FLAG(op, FLAG_MONSTER)
01451 && (((mflags&P_NO_MAGIC) && spell_ob->stats.sp) || ((mflags&P_NO_CLERIC) && spell_ob->stats.grace))) {
01452
01453 if (op->type != PLAYER)
01454 return 0;
01455
01456 if ((mflags&P_NO_CLERIC) && spell_ob->stats.grace)
01457 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR,
01458 "This ground is unholy! %s ignores you.",
01459 "This ground is unholy! %s ignores you.",
01460 godname);
01461 else
01462 switch (op->contr->shoottype) {
01463 case range_magic:
01464 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR,
01465 "Something blocks your spellcasting.", NULL);
01466 break;
01467
01468 case range_misc:
01469 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
01470 "Something blocks the magic of your item.", NULL);
01471 break;
01472 case range_golem:
01473 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_APPLY, MSG_TYPE_APPLY_ERROR,
01474 "Something blocks the magic of your scroll.", NULL);
01475 break;
01476
01477 default:
01478 break;
01479 }
01480 return 0;
01481 }
01482
01483 if (caster == op && settings.casting_time == TRUE && spell_ob->type == SPELL) {
01484 if (op->casting_time == -1) {
01485 op->casting_time = spell_ob->casting_time*PATH_TIME_MULT(op, spell_ob);
01486 op->spell = spell_ob;
01487
01488
01489
01490
01491 if (stringarg) {
01492 op->spellarg = strdup_local(stringarg);
01493 } else
01494 op->spellarg = NULL;
01495 return 0;
01496 } else if (op->casting_time != 0) {
01497 if (op->type == PLAYER)
01498 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_INFO,
01499 "You are casting!", NULL);
01500 return 0;
01501 } else {
01502 op->casting_time = -1;
01503 spell_ob = op->spell;
01504 stringarg = op->spellarg;
01505 }
01506 } else {
01507 if (!QUERY_FLAG(caster, FLAG_WIZ)) {
01508
01509
01510
01511
01512
01513
01514
01515
01516 if (caster == op && caster->type != FIREWALL) {
01517 op->speed_left -= spell_ob->casting_time*PATH_TIME_MULT(op, spell_ob)*FABS(op->speed);
01518
01519
01520
01521 if ((spell_ob->casting_time > 0)
01522 && op->speed_left < -spell_ob->casting_time*PATH_TIME_MULT(op, spell_ob)*FABS(op->speed))
01523 op->speed_left = -spell_ob->casting_time*PATH_TIME_MULT(op, spell_ob)*FABS(op->speed);
01524 } else if (caster->type == WAND
01525 || caster->type == HORN
01526 || caster->type == ROD
01527 || caster->type == POTION
01528 || caster->type == SCROLL) {
01529 op->speed_left -= 2*FABS(op->speed);
01530 }
01531 }
01532 }
01533
01534 if (op->type == PLAYER && op == caster && !QUERY_FLAG(caster, FLAG_WIZ)) {
01535 op->stats.grace -= SP_level_spellpoint_cost(caster, spell_ob, SPELL_GRACE);
01536 op->stats.sp -= SP_level_spellpoint_cost(caster, spell_ob, SPELL_MANA);
01537 }
01538
01539
01540
01541
01542
01543 if (op != caster && !skill && caster->skill) {
01544 skill = find_skill_by_name(op, caster->skill);
01545 if (!skill) {
01546 char name[MAX_BUF];
01547
01548 query_name(caster, name, MAX_BUF);
01549 draw_ext_info_format(NDI_UNIQUE, 0, op,
01550 MSG_TYPE_SKILL, MSG_TYPE_SKILL_MISSING,
01551 "You lack the skill %s to use the %s",
01552 "You lack the skill %s to use the %s",
01553 caster->skill, name);
01554 return 0;
01555 }
01556 change_skill(op, skill, 0);
01557 }
01558
01559
01560
01561
01562
01563 if (caster->type == RUNE) {
01564 object *owner = get_owner(caster);
01565
01566 if (owner)
01567 skill = find_skill_by_name(owner, caster->skill);
01568 }
01569
01570 if (confusion_effect) {
01571
01572 draw_ext_info_format(NDI_UNIQUE, 0, op,
01573 MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01574 "In your confused state, you can't control the magic!",
01575 "In your confused state, you can't control the magic!");
01576 handle_spell_confusion(op);
01577 return 0;
01578 }
01579
01580 play_sound_map(SOUND_TYPE_SPELL, caster, dir, spell_ob->name);
01581
01582 switch (spell_ob->subtype) {
01583
01584
01585
01586 case SP_RAISE_DEAD:
01587 success = cast_raise_dead_spell(op, caster, spell_ob, dir, stringarg);
01588 break;
01589
01590 case SP_RUNE:
01591 success = write_rune(op, caster, spell_ob, dir, stringarg);
01592 break;
01593
01594 case SP_MAKE_MARK:
01595 success = write_mark(op, spell_ob, stringarg);
01596 break;
01597
01598 case SP_BOLT:
01599 success = fire_bolt(op, caster, dir, spell_ob, skill);
01600 break;
01601
01602 case SP_BULLET:
01603 success = fire_bullet(op, caster, dir, spell_ob);
01604 break;
01605
01606 case SP_CONE:
01607 success = cast_cone(op, caster, dir, spell_ob);
01608 break;
01609
01610 case SP_BOMB:
01611 success = create_bomb(op, caster, dir, spell_ob);
01612 break;
01613
01614 case SP_WONDER:
01615 success = cast_wonder(op, caster, dir, spell_ob);
01616 break;
01617
01618 case SP_SMITE:
01619 success = cast_smite_spell(op, caster, dir, spell_ob);
01620 break;
01621
01622 case SP_MAGIC_MISSILE:
01623 success = fire_arch_from_position(op, caster, op->x+freearr_x[dir], op->y+freearr_y[dir], dir, spell_ob);
01624 break;
01625
01626 case SP_SUMMON_GOLEM:
01627 success = summon_golem(op, caster, dir, spell_ob);
01628 old_shoottype = range_golem;
01629 break;
01630
01631 case SP_DIMENSION_DOOR:
01632
01633
01634
01635 success = dimension_door(op, caster, spell_ob, dir);
01636 break;
01637
01638 case SP_MAGIC_MAPPING:
01639 if (op->type == PLAYER) {
01640 spell_effect(spell_ob, op->x, op->y, op->map, op);
01641 draw_magic_map(op);
01642 success = 1;
01643 } else
01644 success = 0;
01645 break;
01646
01647 case SP_MAGIC_WALL:
01648 success = magic_wall(op, caster, dir, spell_ob);
01649 break;
01650
01651 case SP_DESTRUCTION:
01652 success = cast_destruction(op, caster, spell_ob);
01653 break;
01654
01655 case SP_PERCEIVE_SELF:
01656 success = perceive_self(op);
01657 break;
01658
01659 case SP_WORD_OF_RECALL:
01660 success = cast_word_of_recall(op, caster, spell_ob);
01661 break;
01662
01663 case SP_INVISIBLE:
01664 success = cast_invisible(op, caster, spell_ob);
01665 break;
01666
01667 case SP_PROBE:
01668 success = probe(op, caster, spell_ob, dir);
01669 break;
01670
01671 case SP_HEALING:
01672 success = cast_heal(op, caster, spell_ob, dir);
01673 break;
01674
01675 case SP_CREATE_FOOD:
01676 success = cast_create_food(op, caster, spell_ob, dir, stringarg);
01677 break;
01678
01679 case SP_EARTH_TO_DUST:
01680 success = cast_earth_to_dust(op, caster, spell_ob);
01681 break;
01682
01683 case SP_CHANGE_ABILITY:
01684 success = cast_change_ability(op, caster, spell_ob, dir, 0);
01685 break;
01686
01687 case SP_BLESS:
01688 success = cast_bless(op, caster, spell_ob, dir);
01689 break;
01690
01691 case SP_CURSE:
01692 success = cast_curse(op, caster, spell_ob, dir);
01693 break;
01694
01695 case SP_SUMMON_MONSTER:
01696 success = summon_object(op, caster, spell_ob, dir, stringarg);
01697 break;
01698
01699 case SP_CHARGING:
01700 success = recharge(op, caster, spell_ob);
01701 break;
01702
01703 case SP_POLYMORPH:
01704 #if 0
01705
01706
01707
01708
01709 draw_ext_info(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_FAILURE,
01710 "The spell fizzles", NULL);
01711 success = 0;
01712 #else
01713 success = cast_polymorph(op, caster, spell_ob, dir);
01714 #endif
01715 break;
01716
01717 case SP_ALCHEMY:
01718 success = alchemy(op, caster, spell_ob);
01719 break;
01720
01721 case SP_REMOVE_CURSE:
01722 success = remove_curse(op, caster, spell_ob);
01723 break;
01724
01725 case SP_IDENTIFY:
01726 success = cast_identify(op, caster, spell_ob);
01727 break;
01728
01729 case SP_DETECTION:
01730 success = cast_detection(op, caster, spell_ob);
01731 break;
01732
01733 case SP_MOOD_CHANGE:
01734 success = mood_change(op, caster, spell_ob);
01735 break;
01736
01737 case SP_MOVING_BALL:
01738 if (spell_ob->path_repelled
01739 && (spell_ob->path_repelled&caster->path_attuned) != spell_ob->path_repelled) {
01740 draw_ext_info_format(NDI_UNIQUE, 0, op, MSG_TYPE_SPELL, MSG_TYPE_SPELL_ERROR,
01741 "You lack the proper attunement to cast %s",
01742 "You lack the proper attunement to cast %s",
01743 spell_ob->name);
01744 success = 0;
01745 } else
01746 success = fire_arch_from_position(op, caster, op->x+freearr_x[dir], op->y+freearr_y[dir], dir, spell_ob);
01747 break;
01748
01749 case SP_SWARM:
01750 success = fire_swarm(op, caster, spell_ob, dir);
01751 break;
01752
01753 case SP_CHANGE_MANA:
01754 success = cast_transfer(op, caster, spell_ob, dir);
01755 break;
01756
01757 case SP_DISPEL_RUNE:
01758
01759 success = dispel_rune(op, caster, spell_ob, skill, dir);
01760 break;
01761
01762 case SP_CREATE_MISSILE:
01763 success = cast_create_missile(op, caster, spell_ob, dir, stringarg);
01764 break;
01765
01766 case SP_CONSECRATE:
01767 success = cast_consecrate(op, caster, spell_ob);
01768 break;
01769
01770 case SP_ANIMATE_WEAPON:
01771 success = animate_weapon(op, caster, spell_ob, dir);
01772 old_shoottype = range_golem;
01773 break;
01774
01775 case SP_LIGHT:
01776 success = cast_light(op, caster, spell_ob, dir);
01777 break;
01778
01779 case SP_CHANGE_MAP_LIGHT:
01780 success = cast_change_map_lightlevel(op, caster, spell_ob);
01781 break;
01782
01783 case SP_FAERY_FIRE:
01784 success = cast_destruction(op, caster, spell_ob);
01785 break;
01786
01787 case SP_CAUSE_DISEASE:
01788 success = cast_cause_disease(op, caster, spell_ob, dir);
01789 break;
01790
01791 case SP_AURA:
01792 success = create_aura(op, caster, spell_ob);
01793 break;
01794
01795 case SP_TOWN_PORTAL:
01796 success = cast_create_town_portal(op, caster, spell_ob, dir);
01797 break;
01798
01799 case SP_ITEM_CURSE_BLESS:
01800 success = cast_item_curse_or_curse(op, caster, spell_ob);
01801 break;
01802
01803 default:
01804 LOG(llevError, "cast_spell: Unhandled spell subtype %d\n", spell_ob->subtype);
01805 }
01806
01807
01808
01809 if (settings.casting_time == TRUE && stringarg) {
01810 free(stringarg);
01811 stringarg = NULL;
01812 }
01813
01814
01815
01816
01817 if (op->contr)
01818 op->contr->shoottype = old_shoottype;
01819
01820 return success;
01821 }
01822
01829 void store_spell_expiry(object *spell) {
01830
01831 char dur[10];
01832 int i = spell->duration/5;
01833
01834 if (!i)
01835 i = 1;
01836 snprintf(dur, sizeof(dur), "%d", i);
01837 set_ob_key_value(spell, "spell_expiry_warn_1", dur, 1);
01838 i = i/5;
01839 if (i > 0) {
01840 snprintf(dur, sizeof(dur), "%d", i);
01841 set_ob_key_value(spell, "spell_expiry_warn_2", dur, 1);
01842 }
01843 }
01844
01854 void check_spell_expiry(object *spell) {
01855 const char *key;
01856
01857 if (!spell->env || !spell->env->type == PLAYER)
01858 return;
01859
01860 if ((key = get_ob_key_value(spell, "spell_expiry_warn_1")) != NULL) {
01861 if (spell->duration == atoi(key)) {
01862 draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, spell->env, MSG_TYPE_SPELL, MSG_TYPE_SPELL_INFO,
01863 "The effects of your %s are draining out.", NULL, spell->name);
01864 return;
01865 }
01866 }
01867 if ((key = get_ob_key_value(spell, "spell_expiry_warn_2")) != NULL) {
01868 if (spell->duration == atoi(key)) {
01869 draw_ext_info_format(NDI_UNIQUE|NDI_NAVY, 0, spell->env, MSG_TYPE_SPELL, MSG_TYPE_SPELL_INFO,
01870 "The effects of your %s are about to expire.", NULL, spell->name);
01871 return;
01872 }
01873 }
01874 }