1 /*
2  *  File:       spl-util.h                                           *
3  *  Summary:    data handlers for player-avilable spell list         *
4  *  Written by: don brodale <dbrodale@bigfootinteractive.com>        *
5  *                                                                   *
6  *  Changelog(most recent first):                                    *
7  *
8  *  <3>     04oct2001     bwr     absorbed spells0.cc
9  *  <2>     24jun2000     jmf     changed to use new data structure
10  *  <1>     12jun2000     dlb     created after much thought
11  */
12 
13 #include "AppHdr.h"
14 #include "spl-util.h"
15 
16 #include <stdlib.h>
17 #include <stdio.h>
18 #include <ctype.h>
19 #include <string.h>
20 #include <limits.h>
21 
22 #include "externs.h"
23 
24 #include "direct.h"
25 #include "debug.h"
26 #include "stuff.h"
27 #include "itemname.h"
28 #include "macro.h"
29 #include "monstuff.h"
30 #include "player.h"
31 #include "spl-book.h"
32 #include "view.h"
33 
34 
35 #ifdef DOS
36 #include <conio.h>
37 #endif
38 
39 
40 static struct playerspell spelldata[] = {
41 #include "spl-data.h"
42 };
43 
44 static int plyrspell_list[NUM_SPELLS];
45 
46 #define PLYRSPELLDATASIZE (sizeof(spelldata)/sizeof(struct playerspell))
47 
48 static struct playerspell *seekspell(int spellid);
49 static bool cloud_helper( int (*func) (int, int, int, int), int x, int y,
50                           int pow, int ctype );
51 
52 /*
53  *             BEGIN PUBLIC FUNCTIONS
54  */
55 
56 // all this does is merely refresh the internal spell list {dlb}:
init_playerspells(void)57 void init_playerspells(void)
58 {
59     unsigned int x = 0;
60 
61     for (x = 0; x < NUM_SPELLS; x++)
62         plyrspell_list[x] = -1;
63 
64     // can only use up to PLYRSPELLDATASIZE _MINUS ONE_,  or the
65     // last entry tries to set plyrspell_list[SPELL_NO_SPELL]
66     // which corrupts the heap.
67     for (x = 0; x < PLYRSPELLDATASIZE - 1; x++)
68         plyrspell_list[spelldata[x].id] = x;
69 
70     for (x = 0; x < NUM_SPELLS; x++)
71     {
72         if (plyrspell_list[x] == -1)
73             plyrspell_list[x] = plyrspell_list[SPELL_NO_SPELL];
74     }
75 
76     return;                     // return value should not matter here {dlb}
77 };                              // end init_playerspells()
78 
get_spell_slot_by_letter(char letter)79 int get_spell_slot_by_letter( char letter )
80 {
81     ASSERT( isalpha( letter ) );
82 
83     const int index = letter_to_index( letter );
84 
85     if (you.spell_letter_table[ index ] == -1)
86         return (-1);
87 
88     return (you.spell_letter_table[index]);
89 }
90 
get_spell_by_letter(char letter)91 int get_spell_by_letter( char letter )
92 {
93     ASSERT( isalpha( letter ) );
94 
95     const int slot = get_spell_slot_by_letter( letter );
96 
97     return ((slot == -1) ? SPELL_NO_SPELL : you.spells[slot]);
98 }
99 
add_spell_to_memory(int spell)100 bool add_spell_to_memory( int spell )
101 {
102     int i, j;
103 
104     // first we find a slot in our head:
105     for (i = 0; i < 25; i++)
106     {
107         if (you.spells[i] == SPELL_NO_SPELL)
108             break;
109     }
110 
111     you.spells[i] = spell;
112 
113     // now we find an available label:
114     for (j = 0; j < 52; j++)
115     {
116         if (you.spell_letter_table[j] == -1)
117             break;
118     }
119 
120     you.spell_letter_table[j] = i;
121 
122     you.spell_no++;
123 
124     return (true);
125 }
126 
del_spell_from_memory_by_slot(int slot)127 bool del_spell_from_memory_by_slot( int slot )
128 {
129     int j;
130 
131     you.spells[ slot ] = SPELL_NO_SPELL;
132 
133     for (j = 0; j < 52; j++)
134     {
135         if (you.spell_letter_table[j] == slot)
136             you.spell_letter_table[j] = -1;
137 
138     }
139 
140     you.spell_no--;
141 
142     return (true);
143 }
144 
145 
spell_hunger(int which_spell)146 int spell_hunger(int which_spell)
147 {
148     int level = seekspell(which_spell)->level;
149 
150     switch (level)
151     {
152     case  1: return   50;
153     case  2: return   95;
154     case  3: return  160;
155     case  4: return  250;
156     case  5: return  350;
157     case  6: return  550;
158     case  7: return  700;
159     case  8: return  850;
160     case  9: return 1000;
161     case 10: return 1000;
162     case 11: return 1100;
163     case 12: return 1250;
164     case 13: return 1380;
165     case 14: return 1500;
166     case 15: return 1600;
167     default: return 1600 + (20 * level);
168     }
169 }                               // end spell_hunger();
170 
171 // applied to spell misfires (more power = worse) and triggers
172 // for Xom acting (more power = more likely to grab his attention) {dlb}
spell_mana(int which_spell)173 int spell_mana(int which_spell)
174 {
175     return (seekspell(which_spell)->level);
176 }
177 
178 // applied in naughties (more difficult = higher level knowledge = worse)
179 // and triggers for Sif acting (same reasoning as above, just good) {dlb}
spell_difficulty(int which_spell)180 int spell_difficulty(int which_spell)
181 {
182     return (seekspell(which_spell)->level);
183 }
184 
spell_levels_required(int which_spell)185 int spell_levels_required( int which_spell )
186 {
187     int levels = spell_difficulty( which_spell );
188 
189     if (which_spell == SPELL_DELAYED_FIREBALL
190         && player_has_spell( SPELL_FIREBALL ))
191     {
192         levels -= spell_difficulty( SPELL_FIREBALL );
193     }
194     else if (which_spell == SPELL_FIREBALL
195             && player_has_spell( SPELL_DELAYED_FIREBALL ))
196     {
197         levels = 0;
198     }
199 
200     return (levels);
201 }
202 
spell_typematch(int which_spell,unsigned int which_discipline)203 bool spell_typematch(int which_spell, unsigned int which_discipline)
204 {
205     return (seekspell(which_spell)->disciplines & which_discipline);
206 }
207 
208 //jmf: next two for simple bit handling
spell_type(int spell)209 unsigned int spell_type(int spell)
210 {
211     return (seekspell(spell)->disciplines);
212 }
213 
count_bits(unsigned int bits)214 int count_bits(unsigned int bits)
215 {
216     unsigned int n;
217     int c = 0;
218 
219     for (n = 1; n < INT_MAX; n <<= 1)
220     {
221         if (n & bits)
222             c++;
223     }
224 
225     return (c);
226 }
227 
228 // this will probably be used often, so rather than use malloc/free
229 // (which may lead to memory fragmentation) I'll just use a static
230 // array of characters -- if/when the String changeover takes place,
231 // this will all shift, no doubt {dlb}
232 /*
233   const char *spell_title( int which_spell )
234   {
235   static char this_title[41] = ""; // this is generous, to say the least {dlb}
236   strncpy(this_title, seekspell(which_spell)->title, 41);
237   // truncation better than overrun {dlb}
238   return ( this_title );
239   }         // end spell_title()
240 */
241 
spell_title(int spell)242 const char *spell_title(int spell)      //jmf: ah the joys of driving ms. data
243 {
244     return (seekspell(spell)->title);
245 }
246 
247 
248 // FUNCTION APPLICATORS: Idea from Juho Snellman <jsnell@lyseo.edu.ouka.fi>
249 //                       on the Roguelike News pages, Development section.
250 //                       <URL:http://www.skoardy.demon.co.uk/rlnews/>
251 // Here are some function applicators: sort of like brain-dead,
252 // home-grown iterators for the container "dungeon".
253 
254 // Apply a function-pointer to all visible squares
255 // Returns summation of return values from passed in function.
apply_area_visible(int (* func)(int,int,int,int),int power)256 int apply_area_visible( int (*func) (int, int, int, int), int power )
257 {
258     int x, y;
259     int rv = 0;
260 
261     //jmf: FIXME: randomly start from other quadrants, like raise_dead?
262     for (x = you.x_pos - 8; x <= you.x_pos + 8; x++)
263     {
264         for (y = you.y_pos - 8; y <= you.y_pos + 8; y++)
265         {
266             if (see_grid(x, y))
267                 rv += func(x, y, power, 0);
268         }
269     }
270 
271     return (rv);
272 }                               // end apply_area_visible()
273 
274 // Applies the effect to all nine squares around/including the target.
275 // Returns summation of return values from passed in function.
apply_area_square(int (* func)(int,int,int,int),int cx,int cy,int power)276 int apply_area_square( int (*func) (int, int, int, int), int cx, int cy,
277                         int power )
278 {
279     int x, y;
280     int rv = 0;
281 
282     for (x = cx - 1; x <= cx + 1; x++)
283     {
284         for (y = cy - 1; y <= cy + 1; y++)
285         {
286             rv += func(x, y, power, 0);
287         }
288     }
289 
290     return (rv);
291 }                               // end apply_area_square()
292 
293 
294 // Applies the effect to the eight squares beside the target.
295 // Returns summation of return values from passed in function.
apply_area_around_square(int (* func)(int,int,int,int),int targ_x,int targ_y,int power)296 int apply_area_around_square( int (*func) (int, int, int, int),
297                               int targ_x, int targ_y, int power)
298 {
299     int x, y;
300     int rv = 0;
301 
302     for (x = targ_x - 1; x <= targ_x + 1; x++)
303     {
304         for (y = targ_y - 1; y <= targ_y + 1; y++)
305         {
306             if (x == targ_x && y == targ_y)
307                 continue;
308             else
309                 rv += func(x, y, power, 0);
310         }
311     }
312     return (rv);
313 }                               // end apply_area_around_square()
314 
315 // Effect up to max_targs monsters around a point, chosen randomly
316 // Return varies with the function called;  return values will be added up.
apply_random_around_square(int (* func)(int,int,int,int),int targ_x,int targ_y,bool hole_in_middle,int power,int max_targs)317 int apply_random_around_square( int (*func) (int, int, int, int),
318                                 int targ_x, int targ_y,
319                                 bool hole_in_middle, int power, int max_targs )
320 {
321     int rv = 0;
322 
323     if (max_targs <= 0)
324         return 0;
325 
326     if (max_targs >= 9 && !hole_in_middle)
327     {
328         return (apply_area_square( func, targ_x, targ_y, power ));
329     }
330 
331     if (max_targs >= 8 && hole_in_middle)
332     {
333         return (apply_area_around_square( func, targ_x, targ_y, power ));
334     }
335 
336     FixedVector< coord_def, 8 > targs;
337     int count = 0;
338 
339     for (int x = targ_x - 1; x <= targ_x + 1; x++)
340     {
341         for (int y = targ_y - 1; y <= targ_y + 1; y++)
342         {
343             if (hole_in_middle && (x == targ_x && y == targ_y))
344                 continue;
345 
346             if (mgrd[x][y] == NON_MONSTER
347                 && !(x == you.x_pos && y == you.y_pos))
348             {
349                 continue;
350             }
351 
352             // Found target
353             count++;
354 
355             // Slight differece here over the basic algorithm...
356             //
357             // For cases where the number of choices <= max_targs it's
358             // obvious (all available choices will be selected).
359             //
360             // For choices > max_targs, here's a brief proof:
361             //
362             // Let m = max_targs, k = choices - max_targs, k > 0.
363             //
364             // Proof, by induction (over k):
365             //
366             // 1) Show n = m + 1 (k = 1) gives uniform distribution,
367             //    P(new one not chosen) = 1 / (m + 1).
368             //                                         m     1     1
369             //    P(specific previous one replaced) = --- * --- = ---
370             //                                        m+1    m    m+1
371             //
372             //    So the probablity is uniform (ie. any element has
373             //    a 1/(m+1) chance of being in the unchosen slot).
374             //
375             // 2) Assume the distribution is uniform at n = m+k.
376             //    (ie. the probablity that any of the found elements
377             //     was chosen = m / (m+k) (the slots are symetric,
378             //     so it's the sum of the probabilities of being in
379             //     any of them)).
380             //
381             // 3) Show n = m + k + 1 gives a uniform distribution.
382             //    P(new one chosen) = m / (m + k + 1)
383             //    P(any specific previous choice remaining chosen)
384             //    = [1 - P(swaped into m+k+1 position)] * P(prev. chosen)
385             //              m      1       m
386             //    = [ 1 - ----- * --- ] * ---
387             //            m+k+1    m      m+k
388             //
389             //       m+k     m       m
390             //    = ----- * ---  = -----
391             //      m+k+1   m+k    m+k+1
392             //
393             // Therefore, it's uniform for n = m + k + 1.  QED
394             //
395             // The important thing to note in calculating the last
396             // probability is that the chosen elements have already
397             // passed tests which verify that they *don't* belong
398             // in slots m+1...m+k, so the only positions an already
399             // chosen element can end up in are it's original
400             // position (in one of the chosen slots), or in the
401             // new slot.
402             //
403             // The new item can, of course, be placed in any slot,
404             // swapping the value there into the new slot... we
405             // just don't care about the non-chosen slots enough
406             // to store them, so it might look like the item
407             // automatically takes the new slot when not chosen
408             // (although, by symetry all the non-chosen slots are
409             // the same... and similarly, by symetry, all chosen
410             // slots are the same).
411             //
412             // Yes, that's a long comment for a short piece of
413             // code, but I want people to have an understanding
414             // of why this works (or at least make them wary about
415             // changing it without proof and breaking this code). -- bwr
416 
417             // Accept the first max_targs choices, then when
418             // new choices come up, replace one of the choices
419             // at random, max_targs/count of the time (the rest
420             // of the time it replaces an element in an unchosen
421             // slot -- but we don't care about them).
422             if (count <= max_targs)
423             {
424                 targs[ count - 1 ].x = x;
425                 targs[ count - 1 ].y = y;
426             }
427             else if (random2( count ) < max_targs)
428             {
429                 const int pick = random2( max_targs );
430                 targs[ pick ].x = x;
431                 targs[ pick ].y = y;
432             }
433         }
434     }
435 
436     const int targs_found = (count < max_targs) ? count : max_targs;
437 
438     if (targs_found)
439     {
440         // Used to divide the power up among the targets here, but
441         // it's probably better to allow the full power through and
442         // balance the called function. -- bwr
443         for (int i = 0; i < targs_found; i++)
444         {
445             ASSERT( targs[i].x && targs[i].y );
446             rv += func( targs[i].x, targs[i].y, power, 0 );
447         }
448     }
449 
450     return (rv);
451 }                               // end apply_random_around_square()
452 
453 // apply func to one square of player's choice beside the player
apply_one_neighbouring_square(int (* func)(int,int,int,int),int power)454 int apply_one_neighbouring_square(int (*func) (int, int, int, int), int power)
455 {
456     struct dist bmove;
457 
458     mpr("Which direction? [ESC to cancel]", MSGCH_PROMPT);
459     direction( bmove, DIR_DIR, TARG_ENEMY );
460 
461     if (!bmove.isValid)
462     {
463         canned_msg(MSG_SPELL_FIZZLES);
464         return (0);
465     }
466 
467     int rv = func(you.x_pos + bmove.dx, you.y_pos + bmove.dy, power, 1);
468 
469     if (rv == 0)
470         canned_msg(MSG_NOTHING_HAPPENS);
471 
472     return (rv);
473 }                               // end apply_one_neighbouring_square()
474 
apply_area_within_radius(int (* func)(int,int,int,int),int x,int y,int pow,int radius,int ctype)475 int apply_area_within_radius( int (*func) (int, int, int, int),
476                               int x, int y, int pow, int radius, int ctype )
477 {
478     int ix, iy;
479     int sq_radius = radius * radius;
480     int sx, sy, ex, ey;       // start and end x, y - bounds checked
481     int rv = 0;
482 
483     // begin x,y
484     sx = x - radius;
485     sy = y - radius;
486     if (sx < 0) sx = 0;
487     if (sy < 0) sy = 0;
488 
489     // end x,y
490     ex = x + radius;
491     ey = y + radius;
492     if (ex > GXM) ex = GXM;
493     if (ey > GYM) ey = GYM;
494 
495     for (ix = sx; ix < ex; ix++)
496     {
497         for (iy = sy; iy < ey; iy++)
498         {
499             if (distance(x, y, ix, iy) <= sq_radius)
500                 rv += func(ix, iy, pow, ctype);
501         }
502     }
503 
504     return (rv);
505 }                               // end apply_area_within_radius()
506 
507 // apply_area_cloud:
508 // Try to make a realistic cloud by expanding from a point, filling empty
509 // floor tiles until we run out of material (passed in as number).
510 // We really need some sort of a queue structure, since ideally I'd like
511 // to do a (shallow) breadth-first-search of the dungeon floor.
512 // This ought to work okay for small clouds.
apply_area_cloud(int (* func)(int,int,int,int),int x,int y,int pow,int number,int ctype)513 void apply_area_cloud( int (*func) (int, int, int, int), int x, int y,
514                        int pow, int number, int ctype )
515 {
516     int spread, clouds_left = number;
517     int good_squares = 0, neighbours[8] = { 0, 0, 0, 0, 0, 0, 0, 0 };
518     int dx = 1, dy = 1;
519     bool x_first;
520 
521     if (clouds_left && cloud_helper(func, x, y, pow, ctype))
522         clouds_left--;
523 
524     if (!clouds_left)
525         return;
526 
527     if (coinflip())
528         dx *= -1;
529     if (coinflip())
530         dy *= -1;
531 
532     x_first = coinflip();
533 
534     if (x_first)
535     {
536         if (clouds_left && cloud_helper(func, x + dx, y, pow, ctype))
537         {
538             clouds_left--;
539             good_squares++;
540             neighbours[0]++;
541         }
542 
543         if (clouds_left && cloud_helper(func, x - dx, y, pow, ctype))
544         {
545             clouds_left--;
546             good_squares++;
547             neighbours[1]++;
548         }
549 
550         if (clouds_left && cloud_helper(func, x, y + dy, pow, ctype))
551         {
552             clouds_left--;
553             good_squares++;
554             neighbours[2]++;
555         }
556 
557         if (clouds_left && cloud_helper(func, x, y - dy, pow, ctype))
558         {
559             clouds_left--;
560             good_squares++;
561             neighbours[3]++;
562         }
563     }
564     else
565     {
566         if (clouds_left && cloud_helper(func, x, y + dy, pow, ctype))
567         {
568             clouds_left--;
569             good_squares++;
570             neighbours[2]++;
571         }
572 
573         if (clouds_left && cloud_helper(func, x, y - dy, pow, ctype))
574         {
575             clouds_left--;
576             good_squares++;
577             neighbours[3]++;
578         }
579 
580         if (clouds_left && cloud_helper(func, x + dx, y, pow, ctype))
581         {
582             clouds_left--;
583             good_squares++;
584             neighbours[0]++;
585         }
586 
587         if (clouds_left && cloud_helper(func, x - dx, y, pow, ctype))
588         {
589             clouds_left--;
590             good_squares++;
591             neighbours[1]++;
592         }
593     }
594 
595     // now diagonals; we could randomize dx & dy again here
596     if (clouds_left && cloud_helper(func, x + dx, y + dy, pow, ctype))
597     {
598         clouds_left--;
599         good_squares++;
600         neighbours[4]++;
601     }
602 
603     if (clouds_left && cloud_helper(func, x - dx, y + dy, pow, ctype))
604     {
605         clouds_left--;
606         good_squares++;
607         neighbours[5]++;
608     }
609 
610     if (clouds_left && cloud_helper(func, x + dx, y - dy, pow, ctype))
611     {
612         clouds_left--;
613         good_squares++;
614         neighbours[6]++;
615     }
616 
617     if (clouds_left && cloud_helper(func, x - dx, y - dy, pow, ctype))
618     {
619         clouds_left--;
620         good_squares++;
621         neighbours[7]++;
622     }
623 
624     if (!(clouds_left && good_squares))
625         return;
626 
627     for (int i = 0; i < 8 && clouds_left; i++)
628     {
629         if (neighbours[i] == 0)
630             continue;
631 
632         spread = clouds_left / good_squares;
633         clouds_left -= spread;
634         good_squares--;
635 
636         switch (i)
637         {
638         case 0:
639             apply_area_cloud(func, x + dx, y, pow, spread, ctype);
640             break;
641         case 1:
642             apply_area_cloud(func, x - dx, y, pow, spread, ctype);
643             break;
644         case 2:
645             apply_area_cloud(func, x, y + dy, pow, spread, ctype);
646             break;
647         case 3:
648             apply_area_cloud(func, x, y - dy, pow, spread, ctype);
649             break;
650         case 4:
651             apply_area_cloud(func, x + dx, y + dy, pow, spread, ctype);
652             break;
653         case 5:
654             apply_area_cloud(func, x - dx, y + dy, pow, spread, ctype);
655             break;
656         case 6:
657             apply_area_cloud(func, x + dx, y - dy, pow, spread, ctype);
658             break;
659         case 7:
660             apply_area_cloud(func, x - dx, y - dy, pow, spread, ctype);
661             break;
662         }
663     }
664 }                               // end apply_area_cloud()
665 
spell_direction(struct dist & spelld,struct bolt & pbolt,int restrict,int mode)666 char spell_direction( struct dist &spelld, struct bolt &pbolt,
667                       int restrict, int mode )
668 {
669     if (restrict == DIR_TARGET)
670         mpr( "Choose a target (+/- for next/prev monster)", MSGCH_PROMPT );
671     else
672         mpr( STD_DIRECTION_PROMPT, MSGCH_PROMPT );
673 
674     message_current_target();
675 
676     direction( spelld, restrict, mode );
677 
678     if (!spelld.isValid)
679     {
680         // check for user cancel
681         canned_msg(MSG_SPELL_FIZZLES);
682         return -1;
683     }
684 
685     pbolt.target_x = spelld.tx;
686     pbolt.target_y = spelld.ty;
687     pbolt.source_x = you.x_pos;
688     pbolt.source_y = you.y_pos;
689 
690     return 1;
691 }                               // end spell_direction()
692 
spelltype_name(unsigned int which_spelltype)693 const char *spelltype_name(unsigned int which_spelltype)
694 {
695     static char bug_string[80];
696 
697     switch (which_spelltype)
698     {
699     case SPTYP_CONJURATION:
700         return ("Conjuration");
701     case SPTYP_ENCHANTMENT:
702         return ("Enchantment");
703     case SPTYP_FIRE:
704         return ("Fire");
705     case SPTYP_ICE:
706         return ("Ice");
707     case SPTYP_TRANSMIGRATION:
708         return ("Transmigration");
709     case SPTYP_NECROMANCY:
710         return ("Necromancy");
711     case SPTYP_HOLY:
712         return ("Holy");
713     case SPTYP_SUMMONING:
714         return ("Summoning");
715     case SPTYP_DIVINATION:
716         return ("Divination");
717     case SPTYP_TRANSLOCATION:
718         return ("Translocation");
719     case SPTYP_POISON:
720         return ("Poison");
721     case SPTYP_EARTH:
722         return ("Earth");
723     case SPTYP_AIR:
724         return ("Air");
725     default:
726         snprintf( bug_string, sizeof(bug_string),
727                   "invalid(%d)", which_spelltype );
728 
729         return (bug_string);
730     }
731 }                               // end spelltype_name()
732 
spell_type2skill(unsigned int spelltype)733 int spell_type2skill(unsigned int spelltype)
734 {
735     char buffer[80];
736 
737     switch (spelltype)
738     {
739     case SPTYP_CONJURATION:    return (SK_CONJURATIONS);
740     case SPTYP_ENCHANTMENT:    return (SK_ENCHANTMENTS);
741     case SPTYP_FIRE:           return (SK_FIRE_MAGIC);
742     case SPTYP_ICE:            return (SK_ICE_MAGIC);
743     case SPTYP_TRANSMIGRATION: return (SK_TRANSMIGRATION);
744     case SPTYP_NECROMANCY:     return (SK_NECROMANCY);
745     case SPTYP_SUMMONING:      return (SK_SUMMONINGS);
746     case SPTYP_DIVINATION:     return (SK_DIVINATIONS);
747     case SPTYP_TRANSLOCATION:  return (SK_TRANSLOCATIONS);
748     case SPTYP_POISON:         return (SK_POISON_MAGIC);
749     case SPTYP_EARTH:          return (SK_EARTH_MAGIC);
750     case SPTYP_AIR:            return (SK_AIR_MAGIC);
751 
752     default:
753     case SPTYP_HOLY:
754         snprintf( buffer, sizeof(buffer),
755                   "spell_type2skill: called with spelltype %d", spelltype );
756 
757         mpr( buffer );
758         return (-1);
759     }
760 }                               // end spell_type2skill()
761 
762 /*
763  **************************************************
764  *                                                *
765  *              END PUBLIC FUNCTIONS              *
766  *                                                *
767  **************************************************
768  */
769 
770 //jmf: simplified; moved init code to top function, init_playerspells()
seekspell(int spell)771 static struct playerspell *seekspell(int spell)
772 {
773     return (&spelldata[plyrspell_list[spell]]);
774 }
775 
cloud_helper(int (* func)(int,int,int,int),int x,int y,int pow,int ctype)776 static bool cloud_helper( int (*func) (int, int, int, int), int x, int y,
777                           int pow, int ctype )
778 {
779     if (grd[x][y] > DNGN_LAST_SOLID_TILE && env.cgrid[x][y] == EMPTY_CLOUD)
780     {
781         func(x, y, pow, ctype);
782         return true;
783     }
784 
785     return false;
786 }
787