Fandom

Wikihack

Source:NetHack 3.1.0/spell.c

2,034pages on
this wiki
Add New Page
Talk0

Ad blocker interference detected!


Wikia is a free-to-use site that makes money from advertising. We have a modified experience for viewers using ad blockers

Wikia is not accessible if you’ve made further modifications. Remove the custom ad blocker rule(s) and the page will load as expected.

Below is the full text to spell.c from the source code of NetHack 3.1.0. To link to a particular line, write [[NetHack 3.1.0/spell.c#line123]], for example.

Warning! This is the source code from an old release. For the latest release, see Source code

The NetHack General Public License applies to screenshots, source code and other content from NetHack.
1.    /*	SCCS Id: @(#)spell.c	3.1	92/12/10
2.    /*	Copyright (c) M. Stephenson 1988			  */
3.    /* NetHack may be freely redistributed.  See license for details. */
4.    
5.    #include "hack.h"
6.    
7.    static schar NEARDATA delay;		/* moves left for this spell */
8.    static struct obj NEARDATA *book;	/* last/current book being xscribed */
9.    
10.   #define spelluses(spell)	spl_book[spell-1].sp_uses
11.   #define decrnuses(spell)	spl_book[spell-1].sp_uses--
12.   #define spellev(spell)		spl_book[spell-1].sp_lev
13.   #define spellname(spell)	OBJ_NAME(objects[spl_book[spell-1].sp_id])
14.   #define spellid(spell)		spl_book[spell-1].sp_id
15.   
16.   static void FDECL(cursed_book, (int));
17.   static void FDECL(deadbook, (struct obj *));
18.   STATIC_PTR int NDECL(learn);
19.   static int NDECL(getspell);
20.   static char FDECL(spellet, (int));
21.   static char NDECL(dospellmenu);
22.   
23.   static void
24.   cursed_book(lev)
25.   	register int	lev;
26.   {
27.   	switch(rn2(lev)) {
28.   	case 0:
29.   		You("feel a wrenching sensation.");
30.   		tele();		/* teleport him */
31.   		break;
32.   	case 1:
33.   		You("feel threatened.");
34.   		aggravate();
35.   		break;
36.   	case 2:
37.   		make_blinded(Blinded + rn1(100,250),TRUE);
38.   		break;
39.   	case 3:
40.   		take_gold();
41.   		break;
42.   	case 4:
43.   		pline("These runes were just too much to comprehend.");
44.   		make_confused(HConfusion + rn1(7,16),FALSE);
45.   		break;
46.   	case 5:
47.   		pline("The book was coated with contact poison!");
48.   		if (uarmg) {
49.   		    /* Note: at this writing, there are no corrodeable
50.   		     * gloves in the game.  If no one plans on adding
51.   		     * copper gauntlets, most of this could be removed. -3.
52.   		     */
53.   		    if (uarmg->oerodeproof || !is_corrodeable(uarmg)) {
54.   			Your("gloves seem unaffected.");
55.   		    } else if (uarmg->oeroded < MAX_ERODE) {
56.   			Your("gloves corrode%s!",
57.   			     uarmg->oeroded+1 == MAX_ERODE ? " completely" :
58.   			     uarmg->oeroded ? " further" : "");
59.   			uarmg->oeroded++;
60.   		    } else
61.   			Your("gloves %s completely corroded.",
62.   			     Blind ? "feel" : "look");
63.   		    break;
64.   		}
65.   		if(Poison_resistance) {
66.   		    losestr(rn1(1,2));
67.   		    losehp(rnd(6), "contact-poisoned spellbook", KILLED_BY_AN);
68.   		} else {
69.   		    losestr(rn1(4,3));
70.   		    losehp(rnd(10), "contact-poisoned spellbook", KILLED_BY_AN);
71.   		}
72.   		break;
73.   	case 6:
74.   		if(Antimagic) {
75.   		    shieldeff(u.ux, u.uy);
76.   		    pline("The book explodes, but you are unharmed!");
77.   		} else {
78.   		    pline("As you read the book, it explodes in your %s!",
79.   			body_part(FACE));
80.   		    losehp (2*rnd(10)+5, "exploding rune", KILLED_BY_AN);
81.   		}
82.   		break;
83.   	default:
84.   		rndcurse();
85.   		break;
86.   	}
87.   	return;
88.   }
89.   
90.   /* special effects for The Book of the Dead */
91.   static void
92.   deadbook(book2)
93.   struct obj *book2;
94.   {
95.       if(invocation_pos(u.ux, u.uy) && !On_stairs(u.ux, u.uy)) {
96.   	register struct obj *otmp;
97.   	register boolean arti1_primed = FALSE, arti2_primed = FALSE,
98.   			 arti_cursed = FALSE;
99.   
100.  	if(book2->cursed) {
101.  	    pline("The runes appear scrambled.  You can't read them!");
102.  	    return;
103.  	}
104.  
105.  	if(!u.uhave.bell || !u.uhave.menorah) {
106.  	    pline("A chill runs down your spine.");
107.  	    if(!u.uhave.bell) You("hear a faint chime...");
108.  	    if(!u.uhave.menorah) pline("Vlad's doppelganger is amused.");
109.  	    return;
110.  	}
111.  
112.  	for(otmp = invent; otmp; otmp = otmp->nobj) {
113.  	    if(otmp->otyp == CANDELABRUM_OF_INVOCATION &&
114.  	       otmp->spe == 7 && otmp->lamplit) {
115.  		if(!otmp->cursed) arti1_primed = TRUE;
116.  		else arti_cursed = TRUE;
117.  	    }
118.  	    if(otmp->otyp == BELL_OF_OPENING &&
119.  	       (moves - otmp->age) < 5L) { /* you rang it recently */
120.  		if(!otmp->cursed) arti2_primed = TRUE;
121.  		else arti_cursed = TRUE;
122.  	    }
123.  	}
124.  
125.  	if(arti_cursed) {
126.  	    pline("The invocation fails!");
127.  	    pline("At least one of your artifacts is cursed...");
128.  	} else if(arti1_primed && arti2_primed) {
129.  	    mkinvokearea();
130.  	    u.uevent.invoked = 1;
131.  	} else {	/* at least one artifact not prepared properly */
132.  	    You("have a feeling that something is amiss...");
133.  	    goto raise_dead;
134.  	}
135.  	return;
136.      }
137.  
138.      /* when not an invocation situation */
139.      if(book2->cursed)
140.  raise_dead:
141.      {
142.  	register struct monst *mtmp;
143.  	coord mm;
144.  
145.  	You("raised the dead!");
146.  	mm.x = u.ux;
147.  	mm.y = u.uy;
148.  	mkundead(&mm);
149.  	if(!rn2(4))
150.  	    if(mtmp = makemon(&mons[PM_MASTER_LICH],u.ux,u.uy)) {
151.  		mtmp->mpeaceful = 0;
152.  		set_malign(mtmp);
153.  	    }
154.      } else if(book2->blessed) {
155.  	register struct monst *mtmp, *mtmp2;
156.  
157.  	for(mtmp = fmon; mtmp; mtmp = mtmp2) {
158.  	    mtmp2 = mtmp->nmon;		/* tamedog() changes chain */
159.  	    if(is_undead(mtmp->data) && cansee(mtmp->mx, mtmp->my)) {
160.  		mtmp->mpeaceful = TRUE;
161.  		if(sgn(mtmp->data->maligntyp) == sgn(u.ualign.type)
162.  		   && distu(mtmp->mx, mtmp->my) < 4)
163.  		    if (mtmp->mtame)
164.  			mtmp->mtame++;
165.  		    else
166.  			(void) tamedog(mtmp, (struct obj *)0);
167.  		else mtmp->mflee = TRUE;
168.  	    }
169.  	}
170.      } else {
171.  	switch(rn2(3)) {
172.  	case 0: 
173.  	    Your("ancestors are annoyed with you!"); 
174.  	    break;
175.  	case 1: 
176.  	    pline("The headstones in the cemetery begin to move!");
177.  	    break;
178.  	default:
179.  	    pline("Oh my!  Your name appears in the book!");
180.  	}
181.      }
182.      return;
183.  }
184.  
185.  STATIC_PTR
186.  int
187.  learn()
188.  {
189.  	register int	i;
190.  	register unsigned booktype;
191.  
192.  	if (delay) {	/* not if (delay++), so at end delay == 0 */
193.  		delay++;
194.  		return(1); /* still busy */
195.  	}
196.  	exercise(A_WIS, TRUE);		/* you're studying. */
197.  	booktype = book->otyp;
198.  	if(booktype == SPE_BOOK_OF_THE_DEAD) {
199.  	    deadbook(book);
200.  	    return(0);
201.  	}
202.  
203.  	for (i = 0; i < MAXSPELL; i++)  {
204.  		if (spl_book[i].sp_id == booktype)  {
205.  			if (book->spestudied >= rn1(1,8-spl_book[i].sp_lev)) {
206.  			    pline("This spellbook is too faint to be read anymore.");
207.  			    book->otyp = booktype = SPE_BLANK_PAPER;
208.  			    makeknown((int)booktype);
209.  			}
210.  			else if (spl_book[i].sp_uses < 11-spl_book[i].sp_lev) {
211.  			    Your("knowledge of that spell is keener.");
212.  			    spl_book[i].sp_uses += rn1(1,9-spl_book[i].sp_lev);
213.  			    book->spestudied++;
214.  			    exercise(A_WIS, TRUE);	/* extra study */
215.  			} else
216.  			    You("know that spell quite well already.");
217.  			break;
218.  		} else if (spl_book[i].sp_id == NO_SPELL)  {
219.  			spl_book[i].sp_id = booktype;
220.  			spl_book[i].sp_lev = objects[booktype].oc_level;
221.  			/* spells have 1 .. 9-level uses. */
222.  			/* ie 1 or 2 uses w/ most potent */
223.  			spl_book[i].sp_uses = rn1(1,9-spl_book[i].sp_lev);
224.  			book->spestudied++;
225.  			You("add the spell to your repertoire.");
226.  			makeknown((int)booktype);
227.  			break;
228.  		}
229.  	}
230.  	if (i == MAXSPELL) impossible("Too many spells memorized!");
231.  
232.  	if (book->cursed) {	/* maybe a demon cursed it */
233.  		cursed_book(objects[booktype].oc_level);
234.  	}
235.          check_unpaid(book);
236.  	book = 0;
237.  	return(0);
238.  }
239.  
240.  int
241.  study_book(spellbook)
242.  register struct obj *spellbook;
243.  {
244.  	register int	 booktype = spellbook->otyp;
245.  	register boolean confused = (Confusion != 0);
246.  
247.  	if (delay && spellbook == book)
248.  		You("continue your efforts to memorize the spell.");
249.  	else {
250.  		switch(booktype)  {
251.  
252.  /* blank spellbook */
253.  	case SPE_BLANK_PAPER:
254.  		pline("This spellbook is all blank.");
255.  		makeknown(SPE_BLANK_PAPER);
256.  		return(1);
257.  /* level 1 spells */
258.  	case SPE_HEALING:
259.  	case SPE_DETECT_MONSTERS:
260.  	case SPE_FORCE_BOLT:
261.  	case SPE_LIGHT:
262.  	case SPE_SLEEP:
263.  	case SPE_KNOCK:
264.  /* level 2 spells */
265.  	case SPE_MAGIC_MISSILE:
266.  	case SPE_CONFUSE_MONSTER:
267.  	case SPE_SLOW_MONSTER:
268.  	case SPE_CURE_BLINDNESS:
269.  	case SPE_CREATE_MONSTER:
270.  	case SPE_DETECT_FOOD:
271.  	case SPE_WIZARD_LOCK:
272.  		delay = -objects[booktype].oc_delay;
273.  		break;
274.  /* level 3 spells */
275.  	case SPE_HASTE_SELF:
276.  	case SPE_CAUSE_FEAR:
277.  	case SPE_CURE_SICKNESS:
278.  	case SPE_DETECT_UNSEEN:
279.  	case SPE_EXTRA_HEALING:
280.  	case SPE_CHARM_MONSTER:
281.  	case SPE_CLAIRVOYANCE:
282.  /* level 4 spells */
283.  	case SPE_LEVITATION:
284.  	case SPE_RESTORE_ABILITY:
285.  	case SPE_INVISIBILITY:
286.  	case SPE_FIREBALL:
287.  	case SPE_DETECT_TREASURE:
288.  		delay = -(objects[booktype].oc_level - 1) * objects[booktype].oc_delay;
289.  		break;
290.  /* level 5 spells */
291.  	case SPE_REMOVE_CURSE:
292.  	case SPE_MAGIC_MAPPING:
293.  	case SPE_CONE_OF_COLD:
294.  	case SPE_IDENTIFY:
295.  	case SPE_DIG:
296.  /* level 6 spells */
297.  	case SPE_TURN_UNDEAD:
298.  	case SPE_POLYMORPH:
299.  	case SPE_CREATE_FAMILIAR:
300.  	case SPE_TELEPORT_AWAY:
301.  		delay = -objects[booktype].oc_level * objects[booktype].oc_delay;
302.  		break;
303.  /* level 7 spells */
304.  	case SPE_CANCELLATION:
305.  	case SPE_FINGER_OF_DEATH:
306.  	case SPE_BOOK_OF_THE_DEAD:
307.  		delay = -8 * objects[booktype].oc_delay;
308.  		break;
309.  /* impossible */
310.  	default:
311.  		impossible("Unknown spellbook, %d;", booktype);
312.  		return(0);
313.  	}
314.  
315.  		/* Books are often wiser than their readers (Rus.) */
316.  		if(!spellbook->blessed &&
317.  		        spellbook->otyp != SPE_BOOK_OF_THE_DEAD &&
318.  			(spellbook->cursed ||
319.  			    rn2(20) > (ACURR(A_INT) + 4 + (int)(u.ulevel/2)
320.  					- 2*objects[booktype].oc_level))) {
321.  			cursed_book(objects[booktype].oc_level);
322.  			nomul(delay);			/* study time */
323.  			delay = 0;
324.  			if(!rn2(3)) {
325.  				useup(spellbook);
326.  				pline("The spellbook crumbles to dust!");
327.  			}
328.  			return(1);
329.  		}
330.  		else if(confused) {
331.  			if(!rn2(3) && 
332.  			    spellbook->otyp != SPE_BOOK_OF_THE_DEAD) {
333.  				useup(spellbook);
334.  				pline("Being confused you have difficulties in controlling your actions.");
335.  				display_nhwindow(WIN_MESSAGE, FALSE);
336.  				You("accidentally tear the spellbook to pieces.");
337.  			}
338.  			else
339.  				You("find yourself reading the first line over and over again.");
340.  			nomul(delay);
341.  			delay = 0;
342.  			return(1);
343.  		}
344.  
345.  		You("begin to %s the runes.",
346.  		    spellbook->otyp == SPE_BOOK_OF_THE_DEAD ? "recite" :
347.  		    "memorize");
348.  	}
349.  
350.  	book = spellbook;
351.  	set_occupation(learn, "studying", 0);
352.  	return(1);
353.  }
354.  
355.  static int
356.  getspell()
357.  {
358.  	register int	maxs, ilet, i;
359.  	char	 lets[BUFSZ], buf[BUFSZ], qbuf[QBUFSZ];
360.  
361.  	if (spl_book[0].sp_id == NO_SPELL)  {
362.  
363.  		You("don't know any spells right now.");
364.  		return(0);
365.  	} else  {
366.  
367.  	    for(maxs = 1; (maxs < MAXSPELL) && (spl_book[maxs].sp_id != NO_SPELL); maxs++);
368.  	    if (maxs >= MAXSPELL)  {
369.  
370.  		impossible("Too many spells memorized.");
371.  		return(0);
372.  	    }
373.  
374.  	    for(i = 0; (i < maxs) && (i < 26); buf[++i] = 0)  buf[i] = 'a' + i;
375.  	    for(i = 26; (i < maxs) && (i < 52); buf[++i] = 0) buf[i] = 'A' + i - 26;
376.  
377.  	    if (maxs == 1)  Strcpy(lets, "a");
378.  	    else if (maxs < 27)  Sprintf(lets, "a-%c", 'a' + maxs - 1);
379.  	    else if (maxs == 27)  Sprintf(lets, "a-z A");
380.  	    else Sprintf(lets, "a-z A-%c", 'A' + maxs - 27);
381.  	    for(;;)  {
382.  
383.  		Sprintf(qbuf, "Cast which spell? [%s ?]", lets);
384.  		if ((ilet = yn_function(qbuf, NULL, '\0')) == '?') {
385.  			ilet = dospellmenu();
386.  			if(!ilet)
387.  			    continue;
388.  		}
389.  		if (index(quitchars, ilet))
390.  			return(0);
391.  		else for(i = 0; buf[i] != 0; i++)
392.  		    if(ilet == buf[i])  return(++i);
393.  		You("don't know that spell.");
394.  	    }
395.  	}
396.  }
397.  
398.  int
399.  docast()
400.  {
401.  	register int	 spell;
402.  
403.  	spell = getspell();
404.  	if (!spell) return(0);
405.  
406.  	return(spelleffects(spell,FALSE));
407.  }
408.  
409.  int
410.  spelleffects(spell,atme)
411.  register int spell;
412.  boolean atme;
413.  {
414.  	register int energy, damage;
415.  	boolean confused = (Confusion != 0);
416.  	struct obj *pseudo;
417.  
418.  	/* note that trying to cast it decrements the # of uses,    */
419.  	/* even if the mage does not have enough food/energy to use */
420.  	/* the spell */
421.  	switch (spelluses(spell)) {
422.  		case 0:
423.  		    pline ("Curdled magical energy twists through you...");
424.  		    pline ("...you have overloaded and burned out this spell.");
425.  		    make_confused((long)spellev(spell) * 3, FALSE);
426.  		    return(0);
427.  		case 1:
428.  		    Your("nerves tingle warningly.");
429.  		    break;
430.  		case 2:
431.  		    pline ("This spell is starting to be over-used.");
432.  		    break;
433.  		default:
434.  		    break;
435.  	}
436.  	decrnuses(spell);
437.  	energy = spellev(spell) * 7 / 2 - 2;    /* 1 <= energy <= 22 */
438.  	if (u.uhunger <= 100 && spell != SPE_DETECT_FOOD) {
439.  		You("are too hungry to cast that spell.");
440.  		return(0);
441.  	} else if (ACURR(A_STR) < 6)  {
442.  		You("lack the strength to cast spells.");
443.  		return(0);
444.  	} else if(check_capacity(
445.  		"Your concentration falters while carrying so much stuff.")) {
446.  	    return (1);
447.  	}
448.  
449.  	if (u.uhave.amulet) {
450.  		You("feel the amulet draining your energy away.");
451.  		energy *= rnd(3);
452.  	}
453.  	if(energy > u.uen)  {
454.  		You("don't have enough energy to cast that spell.");
455.  		return(0);
456.  	} else {
457.  		if (spell != SPE_DETECT_FOOD)
458.  			morehungry(energy * 10);
459.  		u.uen -= energy;
460.  	}
461.  	flags.botl = 1;
462.  
463.  	if (confused ||
464.  	    ((int)(ACURR(A_INT) + Luck) - 3 * spellev(spell)) < 0) {
465.  
466.  		if (Hallucination)
467.  			pline("Far out... a light show!");
468.  		else	pline("The air around you crackles as you goof up.");
469.  		return(0);
470.  	}
471.  	exercise(A_WIS, TRUE);
472.  /*	pseudo is a temporary "false" object containing the spell stats. */
473.  	pseudo = mksobj(spellid(spell), FALSE, FALSE);
474.  	pseudo->blessed = pseudo->cursed = 0;
475.  	pseudo->quan = 20L;			/* do not let useup get it */
476.  	switch(pseudo->otyp)  {
477.  
478.  /* These spells are all duplicates of wand effects */
479.  	case SPE_FORCE_BOLT:
480.  	case SPE_SLEEP:
481.  	case SPE_MAGIC_MISSILE:
482.  	case SPE_KNOCK:
483.  	case SPE_SLOW_MONSTER:
484.  	case SPE_WIZARD_LOCK:
485.  	case SPE_FIREBALL:
486.  	case SPE_CONE_OF_COLD:
487.  	case SPE_DIG:
488.  	case SPE_TURN_UNDEAD:
489.  	case SPE_POLYMORPH:
490.  	case SPE_TELEPORT_AWAY:
491.  	case SPE_CANCELLATION:
492.  	case SPE_FINGER_OF_DEATH:
493.  	case SPE_LIGHT:
494.  	case SPE_DETECT_UNSEEN:
495.  		if (!(objects[pseudo->otyp].oc_dir == NODIR)) {
496.  			if (atme) u.dx = u.dy = u.dz = 0;
497.  			else (void) getdir(NULL);
498.  			if(!u.dx && !u.dy && !u.dz) {
499.  			    if((damage = zapyourself(pseudo)))
500.  				losehp(damage, 
501.  		self_pronoun("zapped %sself with a spell", "him"),
502.  					NO_KILLER_PREFIX);
503.  			} else	weffects(pseudo);
504.  		} else weffects(pseudo);
505.  		break;
506.  /* These are all duplicates of scroll effects */
507.  	case SPE_CONFUSE_MONSTER:
508.  	case SPE_DETECT_FOOD:
509.  	case SPE_CAUSE_FEAR:
510.  	case SPE_CHARM_MONSTER:
511.  	case SPE_REMOVE_CURSE:
512.  	case SPE_MAGIC_MAPPING:
513.  	case SPE_CREATE_MONSTER:
514.  	case SPE_IDENTIFY:
515.  		(void) seffects(pseudo);
516.  		break;
517.  	case SPE_HASTE_SELF:
518.  	case SPE_DETECT_TREASURE:
519.  	case SPE_DETECT_MONSTERS:
520.  	case SPE_LEVITATION:
521.  	case SPE_RESTORE_ABILITY:
522.  	case SPE_INVISIBILITY:
523.  		(void) peffects(pseudo);
524.  		break;
525.  	case SPE_HEALING:
526.  		You("feel a bit better.");
527.  		healup(rnd(8), 0, FALSE, FALSE);
528.  		break;
529.  	case SPE_CURE_BLINDNESS:
530.  		healup(0, 0, FALSE, TRUE);
531.  		break;
532.  	case SPE_CURE_SICKNESS:
533.  		if (Sick) You("are no longer ill.");
534.  		healup(0, 0, TRUE, FALSE);
535.  		break;
536.  	case SPE_EXTRA_HEALING:
537.  		You("feel a fair bit better.");
538.  		healup(d(2,8)+2, 0, FALSE, FALSE);
539.  		break;
540.  	case SPE_CREATE_FAMILIAR:
541.  		make_familiar((struct obj *)0, u.ux, u.uy);
542.  		break;
543.  	case SPE_CLAIRVOYANCE:
544.  		do_vicinity_map();
545.  		break;
546.  	default:
547.  		impossible("Unknown spell %d attempted.", spell);
548.  		obfree(pseudo, (struct obj *)0);
549.  		return(0);
550.  	}
551.  	obfree(pseudo, (struct obj *)0);	/* now, get rid of it */
552.  	return(1);
553.  }
554.  
555.  void
556.  losespells() {
557.  	register boolean confused = (Confusion != 0);
558.  	register int	 n, nzap, i;
559.  
560.  	book = 0;
561.  	for(n = 0;(spl_book[n].sp_id != NO_SPELL) && (n < MAXSPELL); n++);
562.  	if (!n) return;
563.  	if (n < MAXSPELL) {
564.  		nzap = rnd(n);
565.  		if (nzap < n) nzap += confused;
566.  		for (i = 0; i < nzap; i++) {
567.  		    spl_book[n-i-1].sp_id = NO_SPELL;
568.  		    exercise(A_WIS, FALSE);	/* ouch! */
569.  		}
570.  	} else impossible("Too many spells memorized!");
571.  	return;
572.  }
573.  
574.  static char
575.  spellet(spl)
576.  int spl;
577.  {
578.  	return (spl < 27) ? ('a' + spl - 1) : ('A' + spl - 27);
579.  }
580.  
581.  int
582.  dovspell()
583.  {
584.      (void) dospellmenu();
585.      return 0;
586.  }
587.  
588.  static char
589.  dospellmenu()
590.  {
591.  	winid tmpwin;
592.  	register int maxs, i;
593.  	char rval;
594.  	char     buf[BUFSZ];
595.  
596.  	if (spl_book[0].sp_id == NO_SPELL)  {
597.  
598.  		You("don't know any spells right now.");
599.  		return 0;
600.  	}
601.  
602.  	for(maxs = 1; (maxs < MAXSPELL) && (spl_book[maxs].sp_id != NO_SPELL); maxs++);
603.  	if (maxs >= MAXSPELL)  {
604.  
605.  		impossible("Too many spells memorized.");
606.  		return 0;
607.  	}
608.  	tmpwin = create_nhwindow(NHW_MENU);
609.  	start_menu(tmpwin);
610.  	add_menu(tmpwin, 0, 0, "Currently known spells:");
611.  	add_menu(tmpwin, 0, 0, "");
612.  
613.  	for(i = 1; i <= maxs; i++) {
614.  		Sprintf(buf, "%c %c %s (%d)",
615.  			spellet(i), (spelluses(i)) ? '-' : '*',
616.  			spellname(i), spellev(i));
617.  		add_menu(tmpwin, spellet(i), 0, buf);
618.    	}
619.  	end_menu(tmpwin, '\0', "\033 ", NULL);
620.  	rval = select_menu(tmpwin);
621.  	destroy_nhwindow(tmpwin);
622.  
623.  	return rval;
624.  }
625.  
626.  /*spell.c*/

Also on Fandom

Random Wiki