1/*	$NetBSD: fight.c,v 1.12 2009/08/12 08:21:41 dholland Exp $	*/
2
3/*
4 * fight.c   Phantasia monster fighting routines
5 */
6
7#include <math.h>
8#include <setjmp.h>
9#include <stdio.h>
10#include <string.h>
11
12#include "macros.h"
13#include "phantdefs.h"
14#include "phantstruct.h"
15#include "phantglobs.h"
16
17#undef bool
18#include <curses.h>
19
20static void awardtreasure(void);
21static void callmonster(int);
22static void cancelmonster(void);
23static void cursedtreasure(void);
24static void hitmonster(double);
25static void monsthits(void);
26static int pickmonster(void);
27static void playerhits(void);
28static void scramblestats(void);
29static void throwspell(void);
30
31void
32encounter(int particular)
33{
34	volatile bool    firsthit = Player.p_blessing;	/* set if player gets
35							 * the first hit */
36	volatile int     flockcnt = 1;	/* how many time flocked */
37
38	/* let others know what we are doing */
39	Player.p_status = S_MONSTER;
40	writerecord(&Player, Fileloc);
41
42#ifdef SYS5
43	flushinp();
44#endif
45
46	Shield = 0.0;		/* no shield up yet */
47
48	if (particular >= 0)
49		/* monster is specified */
50		Whichmonster = particular;
51	else
52		/* pick random monster */
53		Whichmonster = pickmonster();
54
55	setjmp(Fightenv);	/* this is to enable changing fight state */
56
57	move(6, 0);
58	clrtobot();		/* clear bottom area of screen */
59
60	Lines = 9;
61	callmonster(Whichmonster);	/* set up monster to fight */
62
63	Luckout = FALSE;	/* haven't tried to luckout yet */
64
65	if (Curmonster.m_type == SM_MORGOTH)
66		mvprintw(4, 0, "You've encountered %s, Bane of the Council and Valar.\n",
67		    Enemyname);
68
69	if (Curmonster.m_type == SM_UNICORN) {
70		if (Player.p_virgin) {
71			printw("You just subdued %s, thanks to the virgin.\n", Enemyname);
72			Player.p_virgin = FALSE;
73		} else {
74			printw("You just saw %s running away!\n", Enemyname);
75			Curmonster.m_experience = 0.0;
76			Curmonster.m_treasuretype = 0;
77		}
78	} else
79		/* not a special monster */
80		for (;;)
81			/* print header, and arbitrate between player and
82			 * monster */
83		{
84			mvprintw(6, 0, "You are being attacked by %s,   EXP: %.0f   (Size: %.0f)\n",
85			    Enemyname, Curmonster.m_experience, Circle);
86
87			displaystats();
88			mvprintw(1, 26, "%20.0f", Player.p_energy + Shield);	/* overprint energy */
89			readmessage();
90
91			if (Curmonster.m_type == SM_DARKLORD
92			    && Player.p_blessing
93			    && Player.p_charms > 0)
94				/* overpower Dark Lord with blessing and charm */
95			{
96				mvprintw(7, 0, "You just overpowered %s!", Enemyname);
97				Lines = 8;
98				Player.p_blessing = FALSE;
99				--Player.p_charms;
100				break;
101			}
102			/* allow paralyzed monster to wake up */
103			Curmonster.m_speed = MIN(Curmonster.m_speed + 1.0, Curmonster.m_maxspeed);
104
105			if (drandom() * Curmonster.m_speed > drandom() * Player.p_speed
106			/* monster is faster */
107			    && Curmonster.m_type != SM_DARKLORD
108			/* not D. L. */
109			    && Curmonster.m_type != SM_SHRIEKER
110			/* not mimic */
111			    && !firsthit)
112				/* monster gets a hit */
113				monsthits();
114			else
115				/* player gets a hit */
116			{
117				firsthit = FALSE;
118				playerhits();
119			}
120
121			refresh();
122
123			if (Lines > LINES - 2)
124				/* near bottom of screen - pause */
125			{
126				more(Lines);
127				move(Lines = 8, 0);
128				clrtobot();
129			}
130			if (Player.p_energy <= 0.0)
131				/* player died */
132			{
133				more(Lines);
134				death(Enemyname);
135				cancelmonster();
136				break;	/* fight ends if the player is saved
137					 * from death */
138			}
139			if (Curmonster.m_energy <= 0.0)
140				/* monster died */
141				break;
142		}
143
144	/* give player credit for killing monster */
145	Player.p_experience += Curmonster.m_experience;
146
147	if (drandom() < Curmonster.m_flock / 100.0)
148		/* monster flocks */
149	{
150		more(Lines);
151		++flockcnt;
152		longjmp(Fightenv, 0);
153		/* NOTREACHED */
154	} else
155		if (Circle > 1.0
156		    && Curmonster.m_treasuretype > 0
157		    && drandom() > 0.2 + pow(0.4, (double) (flockcnt / 3 + Circle / 3.0)))
158			/* monster has treasure; this takes # of flocks and
159			 * size into account */
160		{
161			more(Lines);
162			awardtreasure();
163		}
164	/* pause before returning */
165	getyx(stdscr, Lines, flockcnt);
166	more(Lines + 1);
167
168	Player.p_ring.ring_inuse = FALSE;	/* not using ring */
169
170	/* clean up the screen */
171	move(4, 0);
172	clrtobot();
173}
174
175static int
176pickmonster(void)
177{
178	if (Player.p_specialtype == SC_VALAR)
179		/* even chance of any monster */
180		return ((int) ROLL(0.0, 100.0));
181
182	if (Marsh)
183		/* water monsters */
184		return ((int) ROLL(0.0, 15.0));
185
186	else
187		if (Circle > 24)
188			/* even chance of all non-water monsters */
189			return ((int) ROLL(14.0, 86.0));
190
191		else
192			if (Circle > 15)
193				/* chance of all non-water monsters, weighted
194				 * toward middle */
195				return ((int) (ROLL(0.0, 50.0) + ROLL(14.0, 37.0)));
196
197			else
198				if (Circle > 8)
199					/* not all non-water monsters,
200					 * weighted toward middle */
201					return ((int) (ROLL(0.0, 50.0) + ROLL(14.0, 26.0)));
202
203				else
204					if (Circle > 3)
205						/* even chance of some tamer
206						 * non-water monsters */
207						return ((int) ROLL(14.0, 50.0));
208
209					else
210						/* even chance of some of the
211						 * tamest non-water monsters */
212						return ((int) ROLL(14.0, 25.0));
213}
214
215static void
216playerhits(void)
217{
218	double  inflict;	/* damage inflicted */
219	int     ch;		/* input */
220
221	mvaddstr(7, 0, "1:Melee  2:Skirmish  3:Evade  4:Spell  5:Nick  ");
222
223	if (!Luckout) {
224		/* haven't tried to luckout yet */
225		if (Curmonster.m_type == SM_MORGOTH)
226			/* cannot luckout against Morgoth */
227			addstr("6:Ally  ");
228		else
229			addstr("6:Luckout  ");
230	}
231
232	if (Player.p_ring.ring_type != R_NONE)
233		/* player has a ring */
234		addstr("7:Use Ring  ");
235	else
236		clrtoeol();
237
238	ch = inputoption();
239
240	move(8, 0);
241	clrtobot();		/* clear any messages from before */
242	Lines = 9;
243	mvaddstr(4, 0, "\n\n");	/* clear status area */
244
245	switch (ch) {
246	case 'T':		/* timeout; lose turn */
247		break;
248
249	case ' ':
250	case '1':		/* melee */
251		/* melee affects monster's energy and strength */
252		inflict = ROLL(Player.p_might / 2.0 + 5.0, 1.3 * Player.p_might)
253		    + (Player.p_ring.ring_inuse ? Player.p_might : 0.0);
254
255		Curmonster.m_melee += inflict;
256		Curmonster.m_strength = Curmonster.m_o_strength
257		    - Curmonster.m_melee / Curmonster.m_o_energy
258		    * Curmonster.m_o_strength / 4.0;
259		hitmonster(inflict);
260		break;
261
262	case '2':		/* skirmish */
263		/* skirmish affects monter's energy and speed */
264		inflict = ROLL(Player.p_might / 3.0 + 3.0, 1.1 * Player.p_might)
265		    + (Player.p_ring.ring_inuse ? Player.p_might : 0.0);
266
267		Curmonster.m_skirmish += inflict;
268		Curmonster.m_maxspeed = Curmonster.m_o_speed
269		    - Curmonster.m_skirmish / Curmonster.m_o_energy
270		    * Curmonster.m_o_speed / 4.0;
271		hitmonster(inflict);
272		break;
273
274	case '3':		/* evade */
275		/* use brains and speed to try to evade */
276		if ((Curmonster.m_type == SM_DARKLORD
277			|| Curmonster.m_type == SM_SHRIEKER
278		/* can always run from D. L. and shrieker */
279			|| drandom() * Player.p_speed * Player.p_brains
280			> drandom() * Curmonster.m_speed * Curmonster.m_brains)
281		    && (Curmonster.m_type != SM_MIMIC))
282			/* cannot run from mimic */
283		{
284			mvaddstr(Lines++, 0, "You got away!");
285			cancelmonster();
286			altercoordinates(0.0, 0.0, A_NEAR);
287		} else
288			mvprintw(Lines++, 0, "%s is still after you!", Enemyname);
289
290		break;
291
292	case 'M':
293	case '4':		/* magic spell */
294		throwspell();
295		break;
296
297	case '5':		/* nick */
298		/* hit 1 plus sword; give some experience */
299		inflict = 1.0 + Player.p_sword;
300		Player.p_experience += floor(Curmonster.m_experience / 10.0);
301		Curmonster.m_experience *= 0.92;
302		/* monster gets meaner */
303		Curmonster.m_maxspeed += 2.0;
304		Curmonster.m_speed = (Curmonster.m_speed < 0.0) ? 0.0 : Curmonster.m_speed + 2.0;
305		if (Curmonster.m_type == SM_DARKLORD)
306			/* Dark Lord; doesn't like to be nicked */
307		{
308			mvprintw(Lines++, 0,
309			    "You hit %s %.0f times, and made him mad!", Enemyname, inflict);
310			Player.p_quickness /= 2.0;
311			altercoordinates(0.0, 0.0, A_FAR);
312			cancelmonster();
313		} else
314			hitmonster(inflict);
315		break;
316
317	case 'B':
318	case '6':		/* luckout */
319		if (Luckout)
320			mvaddstr(Lines++, 0, "You already tried that.");
321		else {
322			Luckout = TRUE;
323			if (Curmonster.m_type == SM_MORGOTH)
324				/* Morgoth; ally */
325			{
326				if (drandom() < Player.p_sin / 100.0) {
327					mvprintw(Lines++, 0, "%s accepted!", Enemyname);
328					cancelmonster();
329				} else
330					mvaddstr(Lines++, 0, "Nope, he's not interested.");
331			} else
332				/* normal monster; use brains for success */
333			{
334				if ((drandom() + 0.333) * Player.p_brains
335				    < (drandom() + 0.333) * Curmonster.m_brains)
336					mvprintw(Lines++, 0, "You blew it, %s.", Player.p_name);
337				else {
338					mvaddstr(Lines++, 0, "You made it!");
339					Curmonster.m_energy = 0.0;
340				}
341			}
342		}
343		break;
344
345	case '7':		/* use ring */
346		if (Player.p_ring.ring_type != R_NONE) {
347			mvaddstr(Lines++, 0, "Now using ring.");
348			Player.p_ring.ring_inuse = TRUE;
349			if (Player.p_ring.ring_type != R_DLREG)
350				/* age ring */
351				--Player.p_ring.ring_duration;
352		}
353		break;
354	}
355
356}
357
358static void
359monsthits(void)
360{
361	double  inflict;	/* damage inflicted */
362	int     ch;		/* input */
363
364	switch (Curmonster.m_type)
365		/* may be a special monster */
366	{
367	case SM_DARKLORD:
368		/* hits just enough to kill player */
369		inflict = (Player.p_energy + Shield) * 1.02;
370		goto SPECIALHIT;
371
372	case SM_SHRIEKER:
373		/* call a big monster */
374		mvaddstr(Lines++, 0,
375		    "Shrieeeek!!  You scared it, and it called one of its friends.");
376		more(Lines);
377		Whichmonster = (int) ROLL(70.0, 30.0);
378		longjmp(Fightenv, 0);
379		/* NOTREACHED */
380
381	case SM_BALROG:
382		/* take experience away */
383		inflict = ROLL(10.0, Curmonster.m_strength);
384		inflict = MIN(Player.p_experience, inflict);
385		mvprintw(Lines++, 0,
386		    "%s took away %.0f experience points.", Enemyname, inflict);
387		Player.p_experience -= inflict;
388		return;
389
390	case SM_FAERIES:
391		if (Player.p_holywater > 0)
392			/* holy water kills when monster tries to hit */
393		{
394			mvprintw(Lines++, 0, "Your holy water killed it!");
395			--Player.p_holywater;
396			Curmonster.m_energy = 0.0;
397			return;
398		}
399		break;
400
401	case SM_NONE:
402		/* normal hit */
403		break;
404
405	default:
406		if (drandom() > 0.2)
407			/* normal hit */
408			break;
409
410		/* else special things */
411		switch (Curmonster.m_type) {
412		case SM_LEANAN:
413			/* takes some of the player's strength */
414			inflict = ROLL(1.0, (Circle - 1.0) / 2.0);
415			inflict = MIN(Player.p_strength, inflict);
416			mvprintw(Lines++, 0, "%s sapped %.0f of your strength!",
417			    Enemyname, inflict);
418			Player.p_strength -= inflict;
419			Player.p_might -= inflict;
420			break;
421
422		case SM_SARUMAN:
423			if (Player.p_palantir)
424				/* take away palantir */
425			{
426				mvprintw(Lines++, 0, "Wormtongue stole your palantir!");
427				Player.p_palantir = FALSE;
428			} else
429				if (drandom() > 0.5)
430					/* gems turn to gold */
431				{
432					mvprintw(Lines++, 0,
433					    "%s transformed your gems into gold!", Enemyname);
434					Player.p_gold += Player.p_gems;
435					Player.p_gems = 0.0;
436				} else
437					/* scramble some stats */
438				{
439					mvprintw(Lines++, 0, "%s scrambled your stats!", Enemyname);
440					scramblestats();
441				}
442			break;
443
444		case SM_THAUMATURG:
445			/* transport player */
446			mvprintw(Lines++, 0, "%s transported you!", Enemyname);
447			altercoordinates(0.0, 0.0, A_FAR);
448			cancelmonster();
449			break;
450
451		case SM_VORTEX:
452			/* suck up some mana */
453			inflict = ROLL(0, 7.5 * Circle);
454			inflict = MIN(Player.p_mana, floor(inflict));
455			mvprintw(Lines++, 0,
456			    "%s sucked up %.0f of your mana!", Enemyname, inflict);
457			Player.p_mana -= inflict;
458			break;
459
460		case SM_NAZGUL:
461			/* try to take ring if player has one */
462			if (Player.p_ring.ring_type != R_NONE)
463				/* player has a ring */
464			{
465				mvaddstr(Lines++, 0, "Will you relinguish your ring ? ");
466				ch = getanswer("YN", FALSE);
467				if (ch == 'Y')
468					/* take ring away */
469				{
470					Player.p_ring.ring_type = R_NONE;
471					Player.p_ring.ring_inuse = FALSE;
472					cancelmonster();
473					break;
474				}
475			}
476			/* otherwise, take some brains */
477			mvprintw(Lines++, 0,
478			    "%s neutralized 1/5 of your brain!", Enemyname);
479			Player.p_brains *= 0.8;
480			break;
481
482		case SM_TIAMAT:
483			/* take some gold and gems */
484			mvprintw(Lines++, 0,
485			    "%s took half your gold and gems and flew off.", Enemyname);
486			Player.p_gold /= 2.0;
487			Player.p_gems /= 2.0;
488			cancelmonster();
489			break;
490
491		case SM_KOBOLD:
492			/* steal a gold piece and run */
493			mvprintw(Lines++, 0,
494			    "%s stole one gold piece and ran away.", Enemyname);
495			Player.p_gold = MAX(0.0, Player.p_gold - 1.0);
496			cancelmonster();
497			break;
498
499		case SM_SHELOB:
500			/* bite and (medium) poison */
501			mvprintw(Lines++, 0,
502			    "%s has bitten and poisoned you!", Enemyname);
503			Player.p_poison -= 1.0;
504			break;
505
506		case SM_LAMPREY:
507			/* bite and (small) poison */
508			mvprintw(Lines++, 0, "%s bit and poisoned you!", Enemyname);
509			Player.p_poison += 0.25;
510			break;
511
512		case SM_BONNACON:
513			/* fart and run */
514			mvprintw(Lines++, 0, "%s farted and scampered off.", Enemyname);
515			Player.p_energy /= 2.0;	/* damage from fumes */
516			cancelmonster();
517			break;
518
519		case SM_SMEAGOL:
520			if (Player.p_ring.ring_type != R_NONE)
521				/* try to steal ring */
522			{
523				mvprintw(Lines++, 0,
524				    "%s tried to steal your ring, ", Enemyname);
525				if (drandom() > 0.1)
526					addstr("but was unsuccessful.");
527				else {
528					addstr("and ran away with it!");
529					Player.p_ring.ring_type = R_NONE;
530					cancelmonster();
531				}
532			}
533			break;
534
535		case SM_SUCCUBUS:
536			/* inflict damage through shield */
537			inflict = ROLL(15.0, Circle * 10.0);
538			inflict = MIN(inflict, Player.p_energy);
539			mvprintw(Lines++, 0, "%s sapped %.0f of your energy.",
540			    Enemyname, inflict);
541			Player.p_energy -= inflict;
542			break;
543
544		case SM_CERBERUS:
545			/* take all metal treasures */
546			mvprintw(Lines++, 0,
547			    "%s took all your metal treasures!", Enemyname);
548			Player.p_crowns = 0;
549			Player.p_sword =
550			    Player.p_shield =
551			    Player.p_gold = 0.0;
552			cancelmonster();
553			break;
554
555		case SM_UNGOLIANT:
556			/* (large) poison and take a quickness */
557			mvprintw(Lines++, 0,
558			    "%s poisoned you, and took one quik.", Enemyname);
559			Player.p_poison += 5.0;
560			Player.p_quickness -= 1.0;
561			break;
562
563		case SM_JABBERWOCK:
564			/* fly away, and leave either a Jubjub bird or
565			 * Bonnacon */
566			mvprintw(Lines++, 0,
567			    "%s flew away, and left you to contend with one of its friends.",
568			    Enemyname);
569			Whichmonster = 55 + ((drandom() > 0.5) ? 22 : 0);
570			longjmp(Fightenv, 0);
571			/* NOTREACHED */
572
573		case SM_TROLL:
574			/* partially regenerate monster */
575			mvprintw(Lines++, 0,
576			    "%s partially regenerated his energy.!", Enemyname);
577			Curmonster.m_energy +=
578			    floor((Curmonster.m_o_energy - Curmonster.m_energy) / 2.0);
579			Curmonster.m_strength = Curmonster.m_o_strength;
580			Curmonster.m_melee = Curmonster.m_skirmish = 0.0;
581			Curmonster.m_maxspeed = Curmonster.m_o_speed;
582			break;
583
584		case SM_WRAITH:
585			if (!Player.p_blindness)
586				/* make blind */
587			{
588				mvprintw(Lines++, 0, "%s blinded you!", Enemyname);
589				Player.p_blindness = TRUE;
590				Enemyname = "A monster";
591			}
592			break;
593		}
594		return;
595	}
596
597	/* fall through to here if monster inflicts a normal hit */
598	inflict = drandom() * Curmonster.m_strength + 0.5;
599SPECIALHIT:
600	mvprintw(Lines++, 0, "%s hit you %.0f times!", Enemyname, inflict);
601
602	if ((Shield -= inflict) < 0) {
603		Player.p_energy += Shield;
604		Shield = 0.0;
605	}
606}
607
608static void
609cancelmonster(void)
610{
611	Curmonster.m_energy = 0.0;
612	Curmonster.m_experience = 0.0;
613	Curmonster.m_treasuretype = 0;
614	Curmonster.m_flock = 0.0;
615}
616
617static void
618hitmonster(double inflict)
619{
620	mvprintw(Lines++, 0, "You hit %s %.0f times!", Enemyname, inflict);
621	Curmonster.m_energy -= inflict;
622	if (Curmonster.m_energy > 0.0) {
623		if (Curmonster.m_type == SM_DARKLORD || Curmonster.m_type == SM_SHRIEKER)
624			/* special monster didn't die */
625			monsthits();
626	} else
627		/* monster died.  print message. */
628	{
629		if (Curmonster.m_type == SM_MORGOTH)
630			mvaddstr(Lines++, 0, "You have defeated Morgoth, but he may return. . .");
631		else
632			/* all other types of monsters */
633		{
634			mvprintw(Lines++, 0, "You killed it.  Good work, %s.", Player.p_name);
635
636			if (Curmonster.m_type == SM_MIMIC
637			    && strcmp(Curmonster.m_name, "A Mimic") != 0
638			    && !Player.p_blindness)
639				mvaddstr(Lines++, 0, "The body slowly changes into the form of a mimic.");
640		}
641	}
642}
643
644static void
645throwspell(void)
646{
647	double  inflict;	/* damage inflicted */
648	double  dtemp;		/* for dtemporary calculations */
649	int     ch;		/* input */
650
651	inflict = 0;
652	mvaddstr(7, 0, "\n\n");	/* clear menu area */
653
654	if (Player.p_magiclvl >= ML_ALLORNOTHING)
655		mvaddstr(7, 0, "1:All or Nothing  ");
656	if (Player.p_magiclvl >= ML_MAGICBOLT)
657		addstr("2:Magic Bolt  ");
658	if (Player.p_magiclvl >= ML_FORCEFIELD)
659		addstr("3:Force Field  ");
660	if (Player.p_magiclvl >= ML_XFORM)
661		addstr("4:Transform  ");
662	if (Player.p_magiclvl >= ML_INCRMIGHT)
663		addstr("5:Increase Might\n");
664	if (Player.p_magiclvl >= ML_INVISIBLE)
665		mvaddstr(8, 0, "6:Invisibility  ");
666	if (Player.p_magiclvl >= ML_XPORT)
667		addstr("7:Transport  ");
668	if (Player.p_magiclvl >= ML_PARALYZE)
669		addstr("8:Paralyze  ");
670	if (Player.p_specialtype >= SC_COUNCIL)
671		addstr("9:Specify");
672	mvaddstr(4, 0, "Spell ? ");
673
674	ch = getanswer(" ", TRUE);
675
676	mvaddstr(7, 0, "\n\n");	/* clear menu area */
677
678	if (Curmonster.m_type == SM_MORGOTH && ch != '3')
679		/* can only throw force field against Morgoth */
680		ILLSPELL();
681	else
682		switch (ch) {
683		case '1':	/* all or nothing */
684			if (drandom() < 0.25)
685				/* success */
686			{
687				inflict = Curmonster.m_energy * 1.01 + 1.0;
688
689				if (Curmonster.m_type == SM_DARKLORD)
690					/* all or nothing doesn't quite work
691					 * against D. L. */
692					inflict *= 0.9;
693			} else
694				/* failure -- monster gets stronger and
695				 * quicker */
696			{
697				Curmonster.m_o_strength = Curmonster.m_strength *= 2.0;
698				Curmonster.m_maxspeed *= 2.0;
699				Curmonster.m_o_speed *= 2.0;
700
701				/* paralyzed monsters wake up a bit */
702				Curmonster.m_speed = MAX(1.0, Curmonster.m_speed * 2.0);
703			}
704
705			if (Player.p_mana >= MM_ALLORNOTHING)
706				/* take a mana if player has one */
707				Player.p_mana -= MM_ALLORNOTHING;
708
709			hitmonster(inflict);
710			break;
711
712		case '2':	/* magic bolt */
713			if (Player.p_magiclvl < ML_MAGICBOLT)
714				ILLSPELL();
715			else {
716				do
717					/* prompt for amount to expend */
718				{
719					mvaddstr(4, 0, "How much mana for bolt? ");
720					dtemp = floor(infloat());
721				}
722				while (dtemp < 0.0 || dtemp > Player.p_mana);
723
724				Player.p_mana -= dtemp;
725
726				if (Curmonster.m_type == SM_DARKLORD)
727					/* magic bolts don't work against D.
728					 * L. */
729					inflict = 0.0;
730				else
731					inflict = dtemp * ROLL(15.0, sqrt(Player.p_magiclvl / 3.0 + 1.0));
732				mvaddstr(5, 0, "Magic Bolt fired!\n");
733				hitmonster(inflict);
734			}
735			break;
736
737		case '3':	/* force field */
738			if (Player.p_magiclvl < ML_FORCEFIELD)
739				ILLSPELL();
740			else
741				if (Player.p_mana < MM_FORCEFIELD)
742					NOMANA();
743				else {
744					Player.p_mana -= MM_FORCEFIELD;
745					Shield = (Player.p_maxenergy + Player.p_shield) * 4.2 + 45.0;
746					mvaddstr(5, 0, "Force Field up.\n");
747				}
748			break;
749
750		case '4':	/* transform */
751			if (Player.p_magiclvl < ML_XFORM)
752				ILLSPELL();
753			else
754				if (Player.p_mana < MM_XFORM)
755					NOMANA();
756				else {
757					Player.p_mana -= MM_XFORM;
758					Whichmonster = (int) ROLL(0.0, 100.0);
759					longjmp(Fightenv, 0);
760					/* NOTREACHED */
761				}
762			break;
763
764		case '5':	/* increase might */
765			if (Player.p_magiclvl < ML_INCRMIGHT)
766				ILLSPELL();
767			else
768				if (Player.p_mana < MM_INCRMIGHT)
769					NOMANA();
770				else {
771					Player.p_mana -= MM_INCRMIGHT;
772					Player.p_might +=
773					    (1.2 * (Player.p_strength + Player.p_sword)
774					    + 5.0 - Player.p_might) / 2.0;
775					mvprintw(5, 0, "New strength:  %.0f\n", Player.p_might);
776				}
777			break;
778
779		case '6':	/* invisible */
780			if (Player.p_magiclvl < ML_INVISIBLE)
781				ILLSPELL();
782			else
783				if (Player.p_mana < MM_INVISIBLE)
784					NOMANA();
785				else {
786					Player.p_mana -= MM_INVISIBLE;
787					Player.p_speed +=
788					    (1.2 * (Player.p_quickness + Player.p_quksilver)
789					    + 5.0 - Player.p_speed) / 2.0;
790					mvprintw(5, 0, "New quickness:  %.0f\n", Player.p_speed);
791				}
792			break;
793
794		case '7':	/* transport */
795			if (Player.p_magiclvl < ML_XPORT)
796				ILLSPELL();
797			else
798				if (Player.p_mana < MM_XPORT)
799					NOMANA();
800				else {
801					Player.p_mana -= MM_XPORT;
802					if (Player.p_brains + Player.p_magiclvl
803					    < Curmonster.m_experience / 200.0 * drandom()) {
804						mvaddstr(5, 0, "Transport backfired!\n");
805						altercoordinates(0.0, 0.0, A_FAR);
806						cancelmonster();
807					} else {
808						mvprintw(5, 0, "%s is transported.\n", Enemyname);
809						if (drandom() < 0.3)
810							/* monster didn't drop
811							 * its treasure */
812							Curmonster.m_treasuretype = 0;
813
814						Curmonster.m_energy = 0.0;
815					}
816				}
817			break;
818
819		case '8':	/* paralyze */
820			if (Player.p_magiclvl < ML_PARALYZE)
821				ILLSPELL();
822			else
823				if (Player.p_mana < MM_PARALYZE)
824					NOMANA();
825				else {
826					Player.p_mana -= MM_PARALYZE;
827					if (Player.p_magiclvl >
828					    Curmonster.m_experience / 1000.0 * drandom()) {
829						mvprintw(5, 0, "%s is held.\n", Enemyname);
830						Curmonster.m_speed = -2.0;
831					} else
832						mvaddstr(5, 0, "Monster unaffected.\n");
833				}
834			break;
835
836		case '9':	/* specify */
837			if (Player.p_specialtype < SC_COUNCIL)
838				ILLSPELL();
839			else
840				if (Player.p_mana < MM_SPECIFY)
841					NOMANA();
842				else {
843					Player.p_mana -= MM_SPECIFY;
844					mvaddstr(5, 0, "Which monster do you want [0-99] ? ");
845					Whichmonster = (int) infloat();
846					Whichmonster = MAX(0, MIN(99, Whichmonster));
847					longjmp(Fightenv, 0);
848					/* NOTREACHED */
849				}
850			break;
851		}
852}
853
854static void
855callmonster(int which)
856{
857	struct monster Othermonster;	/* to find a name for mimics */
858
859	which = MIN(which, 99);	/* make sure within range */
860
861	/* fill structure */
862	fseek(Monstfp, (long) which * (long) SZ_MONSTERSTRUCT, SEEK_SET);
863	fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
864
865	/* handle some special monsters */
866	if (Curmonster.m_type == SM_MODNAR) {
867		if (Player.p_specialtype < SC_COUNCIL)
868			/* randomize some stats */
869		{
870			Curmonster.m_strength *= drandom() + 0.5;
871			Curmonster.m_brains *= drandom() + 0.5;
872			Curmonster.m_speed *= drandom() + 0.5;
873			Curmonster.m_energy *= drandom() + 0.5;
874			Curmonster.m_experience *= drandom() + 0.5;
875			Curmonster.m_treasuretype =
876			    (int) ROLL(0.0, (double) Curmonster.m_treasuretype);
877		} else
878			/* make Modnar into Morgoth */
879		{
880			strcpy(Curmonster.m_name, "Morgoth");
881			Curmonster.m_strength = drandom() * (Player.p_maxenergy + Player.p_shield) / 1.4
882			    + drandom() * (Player.p_maxenergy + Player.p_shield) / 1.5;
883			Curmonster.m_brains = Player.p_brains;
884			Curmonster.m_energy = Player.p_might * 30.0;
885			Curmonster.m_type = SM_MORGOTH;
886			Curmonster.m_speed = Player.p_speed * 1.1
887			    + ((Player.p_specialtype == SC_EXVALAR) ? Player.p_speed : 0.0);
888			Curmonster.m_flock = 0.0;
889			Curmonster.m_treasuretype = 0;
890			Curmonster.m_experience = 0.0;
891		}
892	} else
893		if (Curmonster.m_type == SM_MIMIC)
894			/* pick another name */
895		{
896			which = (int) ROLL(0.0, 100.0);
897			fseek(Monstfp, (long) which * (long) SZ_MONSTERSTRUCT, SEEK_SET);
898			fread(&Othermonster, SZ_MONSTERSTRUCT, 1, Monstfp);
899			strcpy(Curmonster.m_name, Othermonster.m_name);
900		}
901	truncstring(Curmonster.m_name);
902
903	if (Curmonster.m_type != SM_MORGOTH)
904		/* adjust stats based on which circle player is in */
905	{
906		Curmonster.m_strength *= (1.0 + Circle / 2.0);
907		Curmonster.m_brains *= Circle;
908		Curmonster.m_speed += Circle * 1.e-9;
909		Curmonster.m_energy *= Circle;
910		Curmonster.m_experience *= Circle;
911	}
912	if (Player.p_blindness)
913		/* cannot see monster if blind */
914		Enemyname = "A monster";
915	else
916		Enemyname = Curmonster.m_name;
917
918	if (Player.p_speed <= 0.0)
919		/* make Player.p_speed positive */
920	{
921		Curmonster.m_speed += -Player.p_speed;
922		Player.p_speed = 1.0;
923	}
924	/* fill up the rest of the structure */
925	Curmonster.m_o_strength = Curmonster.m_strength;
926	Curmonster.m_o_speed = Curmonster.m_maxspeed = Curmonster.m_speed;
927	Curmonster.m_o_energy = Curmonster.m_energy;
928	Curmonster.m_melee = Curmonster.m_skirmish = 0.0;
929}
930
931static void
932awardtreasure(void)
933{
934	int     whichtreasure;	/* calculated treasure to grant */
935	int     temp;		/* temporary */
936	int     ch;		/* input */
937	double  treasuretype;	/* monster's treasure type */
938	double  gold = 0.0;	/* gold awarded */
939	double  gems = 0.0;	/* gems awarded */
940	double  dtemp;		/* for temporary calculations */
941
942	whichtreasure = (int) ROLL(1.0, 3.0);	/* pick a treasure */
943	treasuretype = (double) Curmonster.m_treasuretype;
944
945	move(4, 0);
946	clrtobot();
947	move(6, 0);
948
949	if (drandom() > 0.65)
950		/* gold and gems */
951	{
952		if (Curmonster.m_treasuretype > 7)
953			/* gems */
954		{
955			gems = ROLL(1.0, (treasuretype - 7.0)
956			    * (treasuretype - 7.0) * (Circle - 1.0) / 4.0);
957			printw("You have discovered %.0f gems!", gems);
958		} else
959			/* gold */
960		{
961			gold = ROLL(treasuretype * 10.0, treasuretype
962			    * treasuretype * 10.0 * (Circle - 1.0));
963			printw("You have found %.0f gold pieces.", gold);
964		}
965
966		addstr("  Do you want to pick them up ? ");
967		ch = getanswer("NY", FALSE);
968		addstr("\n\n");
969
970		if (ch == 'Y') {
971			if (drandom() < treasuretype / 35.0 + 0.04)
972				/* cursed */
973			{
974				addstr("They were cursed!\n");
975				cursedtreasure();
976			} else
977				collecttaxes(gold, gems);
978		}
979
980		return;
981	} else
982		/* other treasures */
983	{
984		addstr("You have found some treasure.  Do you want to inspect it ? ");
985		ch = getanswer("NY", FALSE);
986		addstr("\n\n");
987
988		if (ch != 'Y')
989			return;
990		else
991			if (drandom() < 0.08 && Curmonster.m_treasuretype != 4) {
992				addstr("It was cursed!\n");
993				cursedtreasure();
994				return;
995			} else
996				switch (Curmonster.m_treasuretype) {
997				case 1:	/* treasure type 1 */
998					switch (whichtreasure) {
999					case 1:
1000						addstr("You've discovered a power booster!\n");
1001						Player.p_mana += ROLL(Circle * 4.0, Circle * 30.0);
1002						break;
1003
1004					case 2:
1005						addstr("You have encountered a druid.\n");
1006						Player.p_experience +=
1007						    ROLL(0.0, 2000.0 + Circle * 400.0);
1008						break;
1009
1010					case 3:
1011						addstr("You have found a holy orb.\n");
1012						Player.p_sin = MAX(0.0, Player.p_sin - 0.25);
1013						break;
1014					}
1015					break;
1016					/* end treasure type 1 */
1017
1018				case 2:	/* treasure type 2 */
1019					switch (whichtreasure) {
1020					case 1:
1021						addstr("You have found an amulet.\n");
1022						++Player.p_amulets;
1023						break;
1024
1025					case 2:
1026						addstr("You've found some holy water!\n");
1027						++Player.p_holywater;
1028						break;
1029
1030					case 3:
1031						addstr("You've met a hermit!\n");
1032						Player.p_sin *= 0.75;
1033						Player.p_mana += 12.0 * Circle;
1034						break;
1035					}
1036					break;
1037					/* end treasure type 2 */
1038
1039				case 3:	/* treasure type 3 */
1040					switch (whichtreasure) {
1041					case 1:
1042						dtemp = ROLL(7.0, 30.0 + Circle / 10.0);
1043						printw("You've found a +%.0f shield!\n", dtemp);
1044						if (dtemp >= Player.p_shield)
1045							Player.p_shield = dtemp;
1046						else
1047							SOMEBETTER();
1048						break;
1049
1050					case 2:
1051						addstr("You have rescued a virgin.  Will you be honorable ? ");
1052						ch = getanswer("NY", FALSE);
1053						addstr("\n\n");
1054						if (ch == 'Y')
1055							Player.p_virgin = TRUE;
1056						else {
1057							Player.p_experience += 2000.0 * Circle;
1058							++Player.p_sin;
1059						}
1060						break;
1061
1062					case 3:
1063						addstr("You've discovered some athelas!\n");
1064						--Player.p_poison;
1065						break;
1066					}
1067					break;
1068					/* end treasure type 3 */
1069
1070				case 4:	/* treasure type 4 */
1071					addstr("You've found a scroll.  Will you read it ? ");
1072					ch = getanswer("NY", FALSE);
1073					addstr("\n\n");
1074
1075					if (ch == 'Y')
1076						switch ((int) ROLL(1, 6)) {
1077						case 1:
1078							addstr("It throws up a shield for you next monster.\n");
1079							getyx(stdscr, whichtreasure, ch);
1080							more(whichtreasure);
1081							Shield =
1082							    (Player.p_maxenergy + Player.p_energy) * 5.5 + Circle * 50.0;
1083							Whichmonster = pickmonster();
1084							longjmp(Fightenv, 0);
1085							/* NOTREACHED */
1086
1087						case 2:
1088							addstr("It makes you invisible for you next monster.\n");
1089							getyx(stdscr, whichtreasure, ch);
1090							more(whichtreasure);
1091							Player.p_speed = 1e6;
1092							Whichmonster = pickmonster();
1093							longjmp(Fightenv, 0);
1094							/* NOTREACHED */
1095
1096						case 3:
1097							addstr("It increases your strength ten fold to fight your next monster.\n");
1098							getyx(stdscr, whichtreasure, ch);
1099							more(whichtreasure);
1100							Player.p_might *= 10.0;
1101							Whichmonster = pickmonster();
1102							longjmp(Fightenv, 0);
1103							/* NOTREACHED */
1104
1105						case 4:
1106							addstr("It is a general knowledge scroll.\n");
1107							Player.p_brains += ROLL(2.0, Circle);
1108							Player.p_magiclvl += ROLL(1.0, Circle / 2.0);
1109							break;
1110
1111						case 5:
1112							addstr("It tells you how to pick your next monster.\n");
1113							addstr("Which monster do you want [0-99] ? ");
1114							Whichmonster = (int) infloat();
1115							Whichmonster = MIN(99, MAX(0, Whichmonster));
1116							longjmp(Fightenv, 0);
1117
1118						case 6:
1119							addstr("It was cursed!\n");
1120							cursedtreasure();
1121							break;
1122						}
1123					break;
1124					/* end treasure type 4 */
1125
1126				case 5:	/* treasure type 5 */
1127					switch (whichtreasure) {
1128					case 1:
1129						dtemp = ROLL(Circle / 4.0 + 5.0, Circle / 2.0 + 9.0);
1130						printw("You've discovered a +%.0f dagger.\n", dtemp);
1131						if (dtemp >= Player.p_sword)
1132							Player.p_sword = dtemp;
1133						else
1134							SOMEBETTER();
1135						break;
1136
1137					case 2:
1138						dtemp = ROLL(7.5 + Circle * 3.0, Circle * 2.0 + 160.0);
1139						printw("You have found some +%.0f armour!\n", dtemp);
1140						if (dtemp >= Player.p_shield)
1141							Player.p_shield = dtemp;
1142						else
1143							SOMEBETTER();
1144						break;
1145
1146					case 3:
1147						addstr("You've found a tablet.\n");
1148						Player.p_brains += 4.5 * Circle;
1149						break;
1150					}
1151					break;
1152					/* end treasure type 5 */
1153
1154				case 6:	/* treasure type 6 */
1155					switch (whichtreasure) {
1156					case 1:
1157						addstr("You've found a priest.\n");
1158						Player.p_energy = Player.p_maxenergy + Player.p_shield;
1159						Player.p_sin /= 2.0;
1160						Player.p_mana += 24.0 * Circle;
1161						Player.p_brains += Circle;
1162						break;
1163
1164					case 2:
1165						addstr("You have come upon Robin Hood!\n");
1166						Player.p_shield += Circle * 2.0;
1167						Player.p_strength += Circle / 2.5 + 1.0;
1168						break;
1169
1170					case 3:
1171						dtemp = ROLL(2.0 + Circle / 4.0, Circle / 1.2 + 10.0);
1172						printw("You have found a +%.0f axe!\n", dtemp);
1173						if (dtemp >= Player.p_sword)
1174							Player.p_sword = dtemp;
1175						else
1176							SOMEBETTER();
1177						break;
1178					}
1179					break;
1180					/* end treasure type 6 */
1181
1182				case 7:	/* treasure type 7 */
1183					switch (whichtreasure) {
1184					case 1:
1185						addstr("You've discovered a charm!\n");
1186						++Player.p_charms;
1187						break;
1188
1189					case 2:
1190						addstr("You have encountered Merlyn!\n");
1191						Player.p_brains += Circle + 5.0;
1192						Player.p_magiclvl += Circle / 3.0 + 5.0;
1193						Player.p_mana += Circle * 10.0;
1194						break;
1195
1196					case 3:
1197						dtemp = ROLL(5.0 + Circle / 3.0, Circle / 1.5 + 20.0);
1198						printw("You have found a +%.0f war hammer!\n", dtemp);
1199						if (dtemp >= Player.p_sword)
1200							Player.p_sword = dtemp;
1201						else
1202							SOMEBETTER();
1203						break;
1204					}
1205					break;
1206					/* end treasure type 7 */
1207
1208				case 8:	/* treasure type 8 */
1209					switch (whichtreasure) {
1210					case 1:
1211						addstr("You have found a healing potion.\n");
1212						Player.p_poison = MIN(-2.0, Player.p_poison - 2.0);
1213						break;
1214
1215					case 2:
1216						addstr("You have discovered a transporter.  Do you wish to go anywhere ? ");
1217						ch = getanswer("NY", FALSE);
1218						addstr("\n\n");
1219						if (ch == 'Y') {
1220							double  x, y;
1221
1222							addstr("X Y Coordinates ? ");
1223							getstring(Databuf, SZ_DATABUF);
1224							sscanf(Databuf, "%lf %lf", &x, &y);
1225							altercoordinates(x, y, A_FORCED);
1226						}
1227						break;
1228
1229					case 3:
1230						dtemp = ROLL(10.0 + Circle / 1.2, Circle * 3.0 + 30.0);
1231						printw("You've found a +%.0f sword!\n", dtemp);
1232						if (dtemp >= Player.p_sword)
1233							Player.p_sword = dtemp;
1234						else
1235							SOMEBETTER();
1236						break;
1237					}
1238					break;
1239					/* end treasure type 8 */
1240
1241				case 10:
1242				case 11:
1243				case 12:
1244				case 13:	/* treasure types 10 - 13 */
1245					if (drandom() < 0.33) {
1246						if (Curmonster.m_treasuretype == 10) {
1247							addstr("You've found a pair of elven boots!\n");
1248							Player.p_quickness += 2.0;
1249							break;
1250						} else
1251							if (Curmonster.m_treasuretype == 11
1252							    && !Player.p_palantir) {
1253								addstr("You've acquired Saruman's palantir.\n");
1254								Player.p_palantir = TRUE;
1255								break;
1256							} else
1257								if (Player.p_ring.ring_type == R_NONE
1258								    && Player.p_specialtype < SC_COUNCIL
1259								    && (Curmonster.m_treasuretype == 12
1260									|| Curmonster.m_treasuretype == 13))
1261									/* roll
1262									 *  up
1263									 * a
1264									 * ring
1265									 *  */
1266								{
1267									if (drandom() < 0.8)
1268										/* r
1269										 * e
1270										 * g
1271										 * u
1272										 * l
1273										 * a
1274										 * r
1275										 *
1276										 * ri
1277										 * n
1278										 * g
1279										 * s
1280										 *  */
1281									{
1282										if (Curmonster.m_treasuretype == 12) {
1283											whichtreasure = R_NAZREG;
1284											temp = 35;
1285										} else {
1286											whichtreasure = R_DLREG;
1287											temp = 0;
1288										}
1289									} else
1290										/* b
1291										 * a
1292										 * d
1293										 *
1294										 * ri
1295										 * n
1296										 * g
1297										 * s
1298										 *  */
1299									{
1300										whichtreasure = R_BAD;
1301										temp = 15 + Statptr->c_ringduration + (int) ROLL(0, 5);
1302									}
1303
1304									addstr("You've discovered a ring.  Will you pick it up ? ");
1305									ch = getanswer("NY", FALSE);
1306									addstr("\n\n");
1307
1308									if (ch == 'Y') {
1309										Player.p_ring.ring_type = whichtreasure;
1310										Player.p_ring.ring_duration = temp;
1311									}
1312									break;
1313								}
1314					}
1315					/* end treasure types 10 - 13 */
1316					/* fall through to treasure type 9 if
1317					 * no treasure from above */
1318
1319				case 9:	/* treasure type 9 */
1320					switch (whichtreasure) {
1321					case 1:
1322						if (Player.p_level <= 1000.0
1323						    && Player.p_crowns <= 3
1324						    && Player.p_level >= 10.0) {
1325							addstr("You have found a golden crown!\n");
1326							++Player.p_crowns;
1327							break;
1328						}
1329						/* fall through otherwise */
1330
1331					case 2:
1332						addstr("You've been blessed!\n");
1333						Player.p_blessing = TRUE;
1334						Player.p_sin /= 3.0;
1335						Player.p_energy = Player.p_maxenergy + Player.p_shield;
1336						Player.p_mana += 100.0 * Circle;
1337						break;
1338
1339					case 3:
1340						dtemp = ROLL(1.0, Circle / 5.0 + 5.0);
1341						dtemp = MIN(dtemp, 99.0);
1342						printw("You have discovered some +%.0f quicksilver!\n", dtemp);
1343						if (dtemp >= Player.p_quksilver)
1344							Player.p_quksilver = dtemp;
1345						else
1346							SOMEBETTER();
1347						break;
1348					}
1349					break;
1350					/* end treasure type 9 */
1351				}
1352	}
1353}
1354
1355static void
1356cursedtreasure(void)
1357{
1358	if (Player.p_charms > 0) {
1359		addstr("But your charm saved you!\n");
1360		--Player.p_charms;
1361	} else
1362		if (Player.p_amulets > 0) {
1363			addstr("But your amulet saved you!\n");
1364			--Player.p_amulets;
1365		} else {
1366			Player.p_energy =
1367			    (Player.p_maxenergy + Player.p_shield) / 10.0;
1368			Player.p_poison += 0.25;
1369		}
1370}
1371
1372static void
1373scramblestats(void)
1374{
1375	double  dbuf[6];	/* to put statistic in */
1376	double  dtemp1, dtemp2;	/* for swapping values */
1377	int first, second;	/* indices for swapping */
1378	double *dptr;		/* pointer for filling and emptying buf[] */
1379
1380	/* fill buffer */
1381	dptr = &dbuf[0];
1382	*dptr++ = Player.p_strength;
1383	*dptr++ = Player.p_mana;
1384	*dptr++ = Player.p_brains;
1385	*dptr++ = Player.p_magiclvl;
1386	*dptr++ = Player.p_energy;
1387	*dptr = Player.p_sin;
1388
1389	/* pick values to swap */
1390	first = (int) ROLL(0, 5);
1391	second = (int) ROLL(0, 5);
1392
1393	/* swap values */
1394	dptr = &dbuf[0];
1395	dtemp1 = dptr[first];
1396	/* this expression is split to prevent a compiler loop on some
1397	 * compilers */
1398	dtemp2 = dptr[second];
1399	dptr[first] = dtemp2;
1400	dptr[second] = dtemp1;
1401
1402	/* empty buffer */
1403	Player.p_strength = *dptr++;
1404	Player.p_mana = *dptr++;
1405	Player.p_brains = *dptr++;
1406	Player.p_magiclvl = *dptr++;
1407	Player.p_energy = *dptr++;
1408	Player.p_sin = *dptr;
1409}
1410