xref: /dragonfly/games/phantasia/interplayer.c (revision 36a3d1d6)
1 /*
2  * interplayer.c - player to player routines for Phantasia
3  *
4  * $FreeBSD: src/games/phantasia/interplayer.c,v 1.6 1999/11/16 02:57:33 billf Exp $
5  * $DragonFly: src/games/phantasia/interplayer.c,v 1.3 2005/05/31 00:06:26 swildner Exp $
6  */
7 
8 #include <string.h>
9 #include "include.h"
10 
11 /* functions which we need to know about */
12 /* fight.c */
13 extern	void	encounter(int);
14 /* io.c */
15 extern	int	getanswer(const char *, bool);
16 extern	void	getstring(char *, int);
17 extern	double	infloat(void);
18 extern	int	inputoption(void);
19 extern	void	more(int);
20 /* misc.c */
21 extern	void	altercoordinates(double, double, int);
22 extern	void	collecttaxes(double, double);
23 extern	void	death(const char *);
24 extern	const char	*descrlocation(struct player *, bool);
25 extern	const char	*descrstatus(struct player *);
26 extern	const char	*descrtype(struct player *, bool);
27 extern	void	displaystats(void);
28 extern	double	distance(double, double, double, double);
29 extern	long	findname(char *, struct player *);
30 extern	void	readmessage(void);
31 extern	void	readrecord(struct player *, long);
32 extern	void	truncstring(char *);
33 extern	void	writerecord(struct player *, long);
34 /* phantglobs.c */
35 extern	double	drandom(void);
36 
37 size_t	allocvoid(void);
38 void	battleplayer(long);
39 void	checkbattle(void);
40 void	checktampered(void);
41 void	dotampered(void);
42 void	myturn(void);
43 void	tampered(int, double, double);
44 void	throneroom(void);
45 void	userlist(bool);
46 void	writevoid(struct energyvoid *, long);
47 
48 /*
49  * FUNCTION: check to see if current player should battle another
50  *
51  * GLOBAL INPUTS: Other, Users, Player, Fileloc, *Playersfp
52  *
53  * GLOBAL OUTPUTS: Users
54  *
55  * DESCRIPTION:
56  *	Seach player file for a foe at the same coordinates as the
57  *	current player.
58  *	Also update user count.
59  */
60 
61 void
62 checkbattle(void)
63 {
64 	long foeloc = 0L;	/* location in file of person to fight */
65 
66 	Users = 0;
67 	fseek(Playersfp, 0L, SEEK_SET);
68 
69 	while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
70 		if (Other.p_status != S_OFF
71 		    && Other.p_status != S_NOTUSED
72 		    && Other.p_status != S_HUNGUP
73 		    && (Other.p_status != S_CLOAKED || Other.p_specialtype != SC_VALAR)) {
74 			/* player is on and not a cloaked valar */
75 			++Users;
76 
77 			if (Player.p_x == Other.p_x
78 			    && Player.p_y == Other.p_y
79 			/* same coordinates */
80 			    && foeloc != Fileloc
81 			/* not self */
82 			    && Player.p_status == S_PLAYING
83 			    && (Other.p_status == S_PLAYING || Other.p_status == S_INBATTLE)
84 			/* both are playing */
85 			    && Other.p_specialtype != SC_VALAR
86 			    && Player.p_specialtype != SC_VALAR) {
87 				/* neither is valar */
88 				battleplayer(foeloc);
89 				return;
90 			}
91 		}
92 		foeloc += SZ_PLAYERSTRUCT;
93 	}
94 }
95 
96 /*
97  * FUNCTION: inter-terminal battle with another player
98  *
99  * ARGUMENTS:
100  *	long foeplace - location in player file of person to battle
101  *
102  * GLOBAL INPUTS: Foestrikes, LINES, Lines, Other, Shield, Player, *stdscr,
103  *	Fileloc, *Enemyname
104  *
105  * GLOBAL OUTPUTS: Foestrikes, Lines, Shield, Player, Luckout, *Enemyname
106  *
107  * DESCRIPTION:
108  *	Inter-terminal battle is a very fragile and slightly klugy thing.
109  *	At any time, one player is master and the other is slave.
110  *	We pick who is master first by speed and level.  After that,
111  *	the slave waits for the master to relinquish its turn, and
112  *	the slave becomes master, and so on.
113  *
114  *	The items in the player structure which control the handshake are:
115  *	    p_tampered:
116  *		master increments this to relinquish control
117  *	    p_istat:
118  *		master sets this to specify particular action
119  *	    p_1scratch:
120  *		set to total damage inflicted so far; changes to indicate action
121  */
122 
123 void
124 battleplayer(long foeplace)
125 {
126 	double dtemp;		/* for temporary calculations */
127 	double oldhits = 0.0;	/* previous damage inflicted by foe */
128 	int loop;		/* for timing out */
129 	int ch;			/* input */
130 	short oldtampered;	/* old value of foe's p_tampered */
131 
132 	Lines = 8;
133 	Luckout = FALSE;
134 	mvaddstr(4, 0, "Preparing for battle!\n");
135 	refresh();
136 
137 #ifdef SYS5
138 	flushinp();
139 #endif
140 
141 	/* set up variables, file, etc. */
142 	Player.p_status = S_INBATTLE;
143 	Shield = Player.p_energy;
144 
145 	/* if p_tampered is not 0, someone else may try to change it (king, etc.) */
146 	Player.p_tampered = oldtampered = 1;
147 	Player.p_1scratch = 0.0;
148 	Player.p_istat = I_OFF;
149 
150 	readrecord(&Other, foeplace);
151 	if (fabs(Player.p_level - Other.p_level) > 20.0) {
152 		/* see if players are greatly mismatched */
153 		dtemp = (Player.p_level - Other.p_level) / MAX(Player.p_level, Other.p_level);
154 		if (dtemp < -0.5)
155 			/* foe outweighs this one */
156 			Player.p_speed *= 2.0;
157 	}
158 	writerecord(&Player, Fileloc);	/* write out all our info */
159 
160 	if (Player.p_blindness)
161 		Enemyname = "someone";
162 	else
163 		Enemyname = Other.p_name;
164 
165 	mvprintw(6, 0, "You have encountered %s   Level: %.0f\n", Enemyname, Other.p_level);
166 	refresh();
167 
168 	for (loop = 0; Other.p_status != S_INBATTLE && loop < 30; ++loop) {
169 		/* wait for foe to respond */
170 		readrecord(&Other, foeplace);
171 		sleep(1);
172 	}
173 
174 	if (Other.p_status != S_INBATTLE) {
175 		/* foe did not respond */
176 		mvprintw(5, 0, "%s is not responding.\n", Enemyname);
177 		goto LEAVE;
178 	}
179 	/* else, we are ready to battle */
180 
181 	move(4, 0);
182 	clrtoeol();
183 
184 	/*
185 	 * determine who is first master
186 	 * if neither player is faster, check level
187 	 * if neither level is greater, battle is not allowed
188 	 * (this should never happen, but we have to handle it)
189 	 */
190 	if (Player.p_speed > Other.p_speed)
191 		Foestrikes = FALSE;
192 	else if (Other.p_speed > Player.p_speed)
193 		Foestrikes = TRUE;
194 	else if (Player.p_level > Other.p_level)
195 		Foestrikes = FALSE;
196 	else if (Other.p_level > Player.p_level)
197 		Foestrikes = TRUE;
198 	else {
199 		/* no one is faster */
200 		printw("You can't fight %s yet.", Enemyname);
201 		goto LEAVE;
202 	}
203 
204 	for (;;) {
205 		displaystats();
206 		readmessage();
207 		mvprintw(1, 26, "%20.0f", Shield);	/* overprint energy */
208 
209 		if (!Foestrikes)
210 			/* take action against foe */
211 			myturn();
212 		else {
213 			/* wait for foe to take action */
214 			mvaddstr(4, 0, "Waiting...\n");
215 			clrtoeol();
216 			refresh();
217 
218 			for (loop = 0; loop < 20; ++loop) {
219 				/* wait for foe to act */
220 				readrecord(&Other, foeplace);
221 				if (Other.p_1scratch != oldhits)
222 					/* p_1scratch changes to indicate action */
223 					break;
224 				else {
225 					/* wait and try again */
226 					sleep(1);
227 					addch('.');
228 					refresh();
229 				}
230 			}
231 
232 			if (Other.p_1scratch == oldhits) {
233 				/* timeout */
234 				mvaddstr(22, 0, "Timeout: waiting for response.  Do you want to wait ? ");
235 				ch = getanswer("NY", FALSE);
236 				move(22, 0);
237 				clrtobot();
238 				if (ch == 'Y')
239 					continue;
240 				else
241 					break;
242 			} else {
243 				/* foe took action */
244 				switch (Other.p_istat) {
245 				case I_RAN:	/* foe ran away */
246 					mvprintw(Lines++, 0, "%s ran away!", Enemyname);
247 					break;
248 
249 				case I_STUCK:	/* foe tried to run, but couldn't */
250 					mvprintw(Lines++, 0, "%s tried to run away.", Enemyname);
251 					break;
252 
253 				case I_BLEWIT:	/* foe tried to luckout, but didn't */
254 					mvprintw(Lines++, 0, "%s tried to luckout!", Enemyname);
255 					break;
256 
257 				default:
258 					dtemp = Other.p_1scratch - oldhits;
259 					mvprintw(Lines++, 0, "%s hit you %.0f times!", Enemyname, dtemp);
260 					Shield -= dtemp;
261 					break;
262 				}
263 
264 				oldhits = Other.p_1scratch;	/* keep track of old hits */
265 
266 				if (Other.p_tampered != oldtampered) {
267 					/* p_tampered changes to relinquish turn */
268 					oldtampered = Other.p_tampered;
269 					Foestrikes = FALSE;
270 				}
271 			}
272 		}
273 
274 		/* decide what happens next */
275 		refresh();
276 		if (Lines > LINES - 2) {
277 			more(Lines);
278 			move(Lines = 8, 0);
279 			clrtobot();
280 		}
281 
282 		if (Other.p_istat == I_KILLED || Shield < 0.0) {
283 			/* we died */
284 			Shield = -2.0;	/* insure this value is negative */
285 			break;
286 		}
287 
288 		if (Player.p_istat == I_KILLED) {
289 			/* we killed foe; award treasre */
290 			mvprintw(Lines++, 0, "You killed %s!", Enemyname);
291 			Player.p_experience += Other.p_experience;
292 			Player.p_crowns += (Player.p_level < 1000.0) ? Other.p_crowns : 0;
293 			Player.p_amulets += Other.p_amulets;
294 			Player.p_charms += Other.p_charms;
295 			collecttaxes(Other.p_gold, Other.p_gems);
296 			Player.p_sword = MAX(Player.p_sword, Other.p_sword);
297 			Player.p_shield = MAX(Player.p_shield, Other.p_shield);
298 			Player.p_quksilver = MAX(Player.p_quksilver, Other.p_quksilver);
299 			if (Other.p_virgin && !Player.p_virgin) {
300 				mvaddstr(Lines++, 0, "You have rescued a virgin.  Will you be honorable ? ");
301 				if ((ch = getanswer("YN", FALSE)) == 'Y')
302 					Player.p_virgin = TRUE;
303 				else {
304 					++Player.p_sin;
305 					Player.p_experience += 8000.0;
306 				}
307 			}
308 			sleep(3);	/* give other person time to die */
309 			break;
310 		} else if (Player.p_istat == I_RAN || Other.p_istat == I_RAN)
311 			/* either player ran away */
312 			break;
313 	}
314 
315 LEAVE:
316 	/* clean up things and leave */
317 	writerecord(&Player, Fileloc);	/* update a final time */
318 	altercoordinates(0.0, 0.0, A_NEAR);	/* move away from battle site */
319 	Player.p_energy = Shield;	/* set energy to actual value */
320 	Player.p_tampered = T_OFF;	/* clear p_tampered */
321 
322 	more(Lines);		/* pause */
323 
324 	move(4, 0);
325 	clrtobot();		/* clear bottom area of screen */
326 
327 	if (Player.p_energy < 0.0)
328 		/* we are dead */
329 		death("Interterminal battle");
330 }
331 
332 /*
333  * FUNCTION: process players action against foe in battle
334  *
335  * GLOBAL INPUTS: Lines, Other, Player, *stdscr, Fileloc, Luckout,
336  *	*Enemyname
337  *
338  * GLOBAL OUTPUTS: Foestrikes, Lines, Player, Luckout
339  *
340  * DESCRIPTION:
341  *	Take action action against foe, and decide who is master
342  *	for next iteration.
343  */
344 
345 void
346 myturn(void)
347 {
348 	double dtemp;		/* for temporary calculations */
349 	int ch;			/* input */
350 
351 	mvaddstr(7, 0, "1:Fight  2:Run Away!  3:Power Blast  ");
352 	if (Luckout)
353 		clrtoeol();
354 	else
355 		addstr("4:Luckout  ");
356 
357 	ch = inputoption();
358 	move(Lines = 8, 0);
359 	clrtobot();
360 
361 	switch (ch) {
362 	default:		/* fight */
363 		dtemp = ROLL(2.0, Player.p_might);
364 HIT:
365 		mvprintw(Lines++, 0, "You hit %s %.0f times!", Enemyname, dtemp);
366 		Player.p_sin += 0.5;
367 		Player.p_1scratch += dtemp;
368 		Player.p_istat = I_OFF;
369 		break;
370 
371 	case '2':		/* run away */
372 		/* change this to indicate action */
373 		Player.p_1scratch -= 1.0;
374 		if (drandom() > 0.25) {
375 			mvaddstr(Lines++, 0, "You got away!");
376 			Player.p_istat = I_RAN;
377 		} else {
378 			mvprintw(Lines++, 0, "%s is still after you!", Enemyname);
379 			Player.p_istat = I_STUCK;
380 		}
381 		break;
382 
383 	case '3':		/* power blast */
384 		dtemp = MIN(Player.p_mana, Player.p_level * 5.0);
385 		Player.p_mana -= dtemp;
386 		dtemp *= (drandom() + 0.5) * Player.p_magiclvl * 0.2 + 2.0;
387 		mvprintw(Lines++, 0, "You blasted %s !", Enemyname);
388 		goto HIT;
389 
390 	case '4':		/* luckout */
391 		if (Luckout || drandom() > 0.1) {
392 			if (Luckout)
393 				mvaddstr(Lines++, 0, "You already tried that!");
394 			else {
395 				mvaddstr(Lines++, 0, "Not this time . . .");
396 				Luckout = TRUE;
397 			}
398 
399 			Player.p_1scratch -= 1.0;
400 			Player.p_istat = I_BLEWIT;
401 		} else {
402 			mvaddstr(Lines++, 0, "You just lucked out!");
403 			Player.p_1scratch = Other.p_energy * 1.1;
404 		}
405 		break;
406 	}
407 
408 	refresh();
409 	Player.p_1scratch = floor(Player.p_1scratch);	/* clean up any mess */
410 
411 	if (Player.p_1scratch > Other.p_energy)
412 		Player.p_istat = I_KILLED;
413 	else if (drandom() * Player.p_speed < drandom() * Other.p_speed) {
414 		/* relinquish control */
415 		++Player.p_tampered;
416 		Foestrikes = TRUE;
417 	}
418 
419 	writerecord(&Player, Fileloc);	/* let foe know what we did */
420 }
421 
422 /*
423  * FUNCTION: check if current player has been tampered with
424  *
425  * GLOBAL INPUTS: *Energyvoidfp, Other, Player, Fileloc, Enrgyvoid
426  *
427  * GLOBAL OUTPUTS: Enrgyvoid
428  *
429  * DESCRIPTION:
430  *	Check for energy voids, holy grail, and tampering by other
431  *	players.
432  */
433 
434 void
435 checktampered(void)
436 {
437 	long loc = 0L;	/* location in energy void file */
438 
439 	/* first check for energy voids */
440 	fseek(Energyvoidfp, 0L, SEEK_SET);
441 	while (fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp) == 1)
442 		if (Enrgyvoid.ev_active
443 		    && Enrgyvoid.ev_x == Player.p_x
444 		    && Enrgyvoid.ev_y == Player.p_y) {
445 			/* sitting on one */
446 			if (loc > 0L) {
447 				/* not the holy grail; inactivate energy void */
448 				Enrgyvoid.ev_active = FALSE;
449 				writevoid(&Enrgyvoid, loc);
450 				tampered(T_NRGVOID, 0.0, 0.0);
451 			} else if (Player.p_status != S_CLOAKED)
452 				/* holy grail */
453 				tampered(T_GRAIL, 0.0, 0.0);
454 			break;
455 		} else
456 			loc += SZ_VOIDSTRUCT;
457 
458 	/* now check for other things */
459 	readrecord(&Other, Fileloc);
460 	if (Other.p_tampered != T_OFF)
461 		tampered(Other.p_tampered, Other.p_1scratch, Other.p_2scratch);
462 }
463 
464 /*
465  * FUNCTION: take care of tampering by other players
466  *
467  * ARGUMENTS:
468  *	int what - what type of tampering
469  *	double arg1, arg2 - rest of tampering info
470  *
471  * GLOBAL INPUTS: Other, Player, *stdscr, Enrgyvoid, *Playersfp
472  *
473  * GLOBAL OUTPUTS: Other, Player, Changed, Enrgyvoid
474  *
475  * DESCRIPTION:
476  *	Take care of energy voids, holy grail, decree and intervention
477  *	action on current player.
478  */
479 
480 void
481 tampered(int what, double arg1, double arg2)
482 {
483 	long loc;		/* location in file of other players */
484 
485 	Changed = TRUE;
486 	move(4, 0);
487 
488 	Player.p_tampered = T_OFF;	/* no longer tampered with */
489 
490 	switch (what) {
491 	case T_NRGVOID:
492 		addstr("You've hit an energy void !\n");
493 		Player.p_mana /= 3.0;
494 		Player.p_energy /= 2.0;
495 		Player.p_gold = floor(Player.p_gold / 1.25) + 0.1;
496 		altercoordinates(0.0, 0.0, A_NEAR);
497 		break;
498 
499 	case T_TRANSPORT:
500 		addstr("The king transported you !  ");
501 		if (Player.p_charms > 0) {
502 			addstr("But your charm saved you. . .\n");
503 			--Player.p_charms;
504 		} else {
505 			altercoordinates(0.0, 0.0, A_FAR);
506 			addch('\n');
507 		}
508 		break;
509 
510 	case T_BESTOW:
511 		printw("The king has bestowed %.0f gold pieces on you !\n", arg1);
512 		Player.p_gold += arg1;
513 		break;
514 
515 	case T_CURSED:
516 		addstr("You've been cursed !  ");
517 		if (Player.p_blessing) {
518 			addstr("But your blessing saved you. . .\n");
519 			Player.p_blessing = FALSE;
520 		} else {
521 			addch('\n');
522 			Player.p_poison += 2.0;
523 			Player.p_energy = 10.0;
524 			Player.p_maxenergy *= 0.95;
525 			Player.p_status = S_PLAYING;	/* no longer cloaked */
526 		}
527 		break;
528 
529 	case T_VAPORIZED:
530 		addstr("You have been vaporized!\n");
531 		more(7);
532 		death("Vaporization");
533 		break;
534 
535 	case T_MONSTER:
536 		addstr("The Valar zapped you with a monster!\n");
537 		more(7);
538 		encounter((int)arg1);
539 		return;
540 
541 	case T_BLESSED:
542 		addstr("The Valar has blessed you!\n");
543 		Player.p_energy = (Player.p_maxenergy *= 1.05) + Player.p_shield;
544 		Player.p_mana += 500.0;
545 		Player.p_strength += 0.5;
546 		Player.p_brains += 0.5;
547 		Player.p_magiclvl += 0.5;
548 		Player.p_poison = MIN(0.5, Player.p_poison);
549 		break;
550 
551 	case T_RELOCATE:
552 		addstr("You've been relocated. . .\n");
553 		altercoordinates(arg1, arg2, A_FORCED);
554 		break;
555 
556 	case T_HEAL:
557 		addstr("You've been healed!\n");
558 		Player.p_poison -= 0.25;
559 		Player.p_energy = Player.p_maxenergy + Player.p_shield;
560 		break;
561 
562 	case T_EXVALAR:
563 		addstr("You are no longer Valar!\n");
564 		Player.p_specialtype = SC_COUNCIL;
565 		break;
566 
567 	case T_GRAIL:
568 		addstr("You have found The Holy Grail!!\n");
569 		if (Player.p_specialtype < SC_COUNCIL) {
570 			/* must be council of wise to behold grail */
571 			addstr("However, you are not experienced enough to behold it.\n");
572 			Player.p_sin *= Player.p_sin;
573 			Player.p_mana += 1000;
574 		} else if (Player.p_specialtype == SC_VALAR ||
575 			   Player.p_specialtype == SC_EXVALAR) {
576 			addstr("You have made it to the position of Valar once already.\n");
577 			addstr("The Grail is of no more use to you now.\n");
578 		} else {
579 			addstr("It is now time to see if you are worthy to behold it. . .\n");
580 			refresh();
581 			sleep(4);
582 
583 			if (drandom() / 2.0 < Player.p_sin) {
584 				addstr("You have failed!\n");
585 				Player.p_strength =
586 				Player.p_mana =
587 				Player.p_energy =
588 				Player.p_maxenergy =
589 				Player.p_magiclvl =
590 				Player.p_brains =
591 				Player.p_experience =
592 				Player.p_quickness = 1.0;
593 
594 				altercoordinates(1.0, 1.0, A_FORCED);
595 				Player.p_level = 0.0;
596 			} else {
597 				addstr("You made to position of Valar!\n");
598 				Player.p_specialtype = SC_VALAR;
599 				Player.p_lives = 5;
600 				fseek(Playersfp, 0L, SEEK_SET);
601 				loc = 0L;
602 				while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
603 					/* search for existing valar */
604 					if (Other.p_specialtype == SC_VALAR &&
605 					    Other.p_status != S_NOTUSED) {
606 						/* found old valar */
607 						Other.p_tampered = T_EXVALAR;
608 						writerecord(&Other, loc);
609 						break;
610 					} else
611 						loc += SZ_PLAYERSTRUCT;
612 			}
613 		}
614 
615 		/* move grail to new location */
616 		Enrgyvoid.ev_active = TRUE;
617 		Enrgyvoid.ev_x = ROLL(-1.0e6, 2.0e6);
618 		Enrgyvoid.ev_y = ROLL(-1.0e6, 2.0e6);
619 		writevoid(&Enrgyvoid, 0L);
620 		break;
621 	}
622 	refresh();
623 	sleep(2);
624 }
625 
626 /*
627  * FUNCTION: print list of players and locations
628  *
629  * ARGUMENTS:
630  *	bool ingameflag - set if called while playing
631  *
632  * GLOBAL INPUTS: LINES, Other, Circle, Wizard, Player, *stdscr, *Playersfp
633  *
634  * DESCRIPTION:
635  *	We can only see the coordinate of those closer to the origin
636  *	from us.
637  *	Kings and council of the wise can see and can be seen by everyone.
638  *	Palantirs are good for seeing everyone; and the valar can use
639  *	one to see through a 'cloak' spell.
640  *	The valar has no coordinates, and is completely invisible if
641  *	cloaked.
642  */
643 
644 void
645 userlist(bool ingameflag)
646 {
647 	int numusers = 0;	/* number of users on file */
648 
649 	if (ingameflag && Player.p_blindness) {
650 		mvaddstr(8, 0, "You cannot see anyone.\n");
651 		return;
652 	}
653 
654 	fseek(Playersfp, 0L, SEEK_SET);
655 	mvaddstr(8, 0,
656 	    "Name                         X         Y    Lvl Type Login    Status\n");
657 
658 	while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
659 		if (Other.p_status == S_NOTUSED
660 		/* record is unused */
661 		    || (Other.p_specialtype == SC_VALAR && Other.p_status == S_CLOAKED)) {
662 			/* cloaked valar */
663 			if (!Wizard)
664 				/* wizard can see everything on file */
665 				continue;
666 		}
667 
668 		++numusers;
669 
670 		if (ingameflag &&
671 		/* must be playing for the rest of these conditions */
672 		    (Player.p_specialtype >= SC_KING
673 		/* kings and higher can see others */
674 			|| Other.p_specialtype >= SC_KING
675 		/* kings and higher can be seen by others */
676 			|| Circle >= CIRCLE(Other.p_x, Other.p_y)
677 		/* those nearer the origin can be seen */
678 			|| Player.p_palantir)
679 		/* palantir enables one to see others */
680 		    && (Other.p_status != S_CLOAKED
681 			|| (Player.p_specialtype == SC_VALAR && Player.p_palantir))
682 		/* not cloaked; valar can see through cloak with a palantir */
683 		    && Other.p_specialtype != SC_VALAR)
684 			/* not a valar */
685 			/* coordinates should be printed */
686 			printw("%-20s  %8.0f  %8.0f ",
687 			    Other.p_name, Other.p_x, Other.p_y);
688 		else
689 			/* cannot see player's coordinates */
690 			printw("%-20s %19.19s ",
691 			    Other.p_name, descrlocation(&Other, TRUE));
692 
693 		printw("%6.0f %s  %-9.9s%s\n", Other.p_level, descrtype(&Other, TRUE),
694 		    Other.p_login, descrstatus(&Other));
695 
696 		if ((numusers % (LINES - 10)) == 0) {
697 			more(LINES - 1);
698 			move(9, 0);
699 			clrtobot();
700 		}
701 	}
702 
703 	printw("Total players on file = %d\n", numusers);
704 	refresh();
705 }
706 
707 /*
708  * FUNCTION: king stuff upon entering throne
709  *
710  * GLOBAL INPUTS: *Energyvoidfp, Other, Player, *stdscr,
711  *	Enrgyvoid, *Playersfp
712  *
713  * GLOBAL OUTPUTS: Other, Player, Changed
714  *
715  * DESCRIPTION:
716  *	If player is not already king, make him/her so if the old king
717  *	is not playing.
718  *	Clear energy voids with new king.
719  *	Print 'decree' prompt.
720  */
721 
722 void
723 throneroom(void)
724 {
725 	FILE *fp;		/* to clear energy voids */
726 	long loc = 0L;		/* location of old king in player file */
727 
728 	if (Player.p_specialtype < SC_KING) {
729 		/* not already king -- assumes crown */
730 		fseek(Playersfp, 0L, SEEK_SET);
731 		while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1)
732 			if (Other.p_specialtype == SC_KING && Other.p_status != S_NOTUSED) {
733 				/* found old king */
734 				if (Other.p_status != S_OFF) {
735 					/* old king is playing */
736 					mvaddstr(4, 0, "The king is playing, so you cannot steal his throne\n");
737 					altercoordinates(0.0, 0.0, A_NEAR);
738 					move(6, 0);
739 					return;
740 				} else {
741 					/* old king is not playing - remove him/her */
742 					Other.p_specialtype = SC_NONE;
743 					if (Other.p_crowns)
744 						--Other.p_crowns;
745 					writerecord(&Other, loc);
746 					break;
747 				}
748 			} else
749 				loc += SZ_PLAYERSTRUCT;
750 
751 		/* make player new king */
752 		Changed = TRUE;
753 		Player.p_specialtype = SC_KING;
754 		mvaddstr(4, 0, "You have become king!\n");
755 
756 		/* let everyone else know */
757 		fp = fopen(_PATH_MESS, "w");
758 		fprintf(fp, "All hail the new king!");
759 		fclose(fp);
760 
761 		/* clear all energy voids; retain location of holy grail */
762 		fseek(Energyvoidfp, 0L, SEEK_SET);
763 		fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp);
764 		fp = fopen(_PATH_VOID, "w");
765 		fwrite((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, fp);
766 		fclose(fp);
767 	}
768 
769 	mvaddstr(6, 0, "0:Decree  ");
770 }
771 
772 /*
773  * FUNCTION: king and valar special options
774  *
775  * GLOBAL INPUTS: *Energyvoidfp, Other, Illcmd[], Wizard, Player, *stdscr,
776  *	Databuf[], Enrgyvoid
777  *
778  * GLOBAL OUTPUTS: Other, Player, Enrgyvoid
779  *
780  * DESCRIPTION:
781  *	Tamper with other players.  Handle king/valar specific options.
782  */
783 
784 void
785 dotampered(void)
786 {
787 	short tamper;		/* value for tampering with other players */
788 	const char *option;	/* pointer to option description */
789 	double temp1 = 0.0, temp2 = 0.0;	/* other tampering values */
790 	int ch;			/* input */
791 	long loc;		/* location in energy void file */
792 	FILE *fp;		/* for opening gold file */
793 
794 	move(6, 0);
795 	clrtoeol();
796 	if (Player.p_specialtype < SC_COUNCIL && !Wizard) {
797 		/* king options */
798 		addstr("1:Transport  2:Curse  3:Energy Void  4:Bestow  5:Collect Taxes  ");
799 
800 		ch = getanswer(" ", TRUE);
801 		move(6, 0);
802 		clrtoeol();
803 		move(4, 0);
804 		switch (ch) {
805 		case '1':	/* transport someone */
806 			tamper = T_TRANSPORT;
807 			option = "transport";
808 			break;
809 
810 		case '2':	/* curse another */
811 			tamper = T_CURSED;
812 			option = "curse";
813 			break;
814 
815 		case '3':	/* create energy void */
816 			if ((loc = allocvoid()) > 20L * (long)SZ_VOIDSTRUCT)
817 				/* can only have 20 void active at once */
818 				mvaddstr(5, 0, "Sorry, void creation limit reached.\n");
819 			else {
820 				addstr("Enter the X Y coordinates of void ? ");
821 				getstring(Databuf, SZ_DATABUF);
822 				sscanf(Databuf, "%lf %lf", &temp1, &temp2);
823 				Enrgyvoid.ev_x = floor(temp1);
824 				Enrgyvoid.ev_y = floor(temp2);
825 				Enrgyvoid.ev_active = TRUE;
826 				writevoid(&Enrgyvoid, loc);
827 				mvaddstr(5, 0, "It is done.\n");
828 			}
829 			return;
830 
831 		case '4':	/* bestow gold to subject */
832 			tamper = T_BESTOW;
833 			addstr("How much gold to bestow ? ");
834 			temp1 = infloat();
835 			if (temp1 > Player.p_gold || temp1 < 0) {
836 				mvaddstr(5, 0, "You don't have that !\n");
837 				return;
838 			}
839 
840 			/* adjust gold after we are sure it will be given to someone */
841 			option = "give gold to";
842 			break;
843 
844 		case '5':	/* collect accumulated taxes */
845 			if ((fp = fopen(_PATH_GOLD, "r+")) != NULL) {
846 				/* collect taxes */
847 				fread((char *)&temp1, sizeof(double), 1, fp);
848 				fseek(fp, 0L, SEEK_SET);
849 				/* clear out value */
850 				temp2 = 0.0;
851 				fwrite((char *)&temp2, sizeof(double), 1, fp);
852 				fclose(fp);
853 			}
854 
855 			mvprintw(4, 0, "You have collected %.0f in gold.\n", temp1);
856 			Player.p_gold += floor(temp1);
857 			return;
858 
859 		default:
860 			return;
861 		}
862 		/* end of king options */
863 	} else {
864 		/* council of wise, valar, wizard options */
865 		addstr("1:Heal  ");
866 		if (Player.p_palantir || Wizard)
867 			addstr("2:Seek Grail  ");
868 		if (Player.p_specialtype == SC_VALAR || Wizard)
869 			addstr("3:Throw Monster  4:Relocate  5:Bless  ");
870 		if (Wizard)
871 			addstr("6:Vaporize  ");
872 
873 		ch = getanswer(" ", TRUE);
874 		if (!Wizard) {
875 			if (ch > '2' && Player.p_specialtype != SC_VALAR) {
876 				ILLCMD();
877 				return;
878 			}
879 
880 			if (Player.p_mana < MM_INTERVENE) {
881 				mvaddstr(5, 0, "No mana left.\n");
882 				return;
883 			} else
884 				Player.p_mana -= MM_INTERVENE;
885 		}
886 
887 		switch (ch) {
888 		case '1':	/* heal another */
889 			tamper = T_HEAL;
890 			option = "heal";
891 			break;
892 
893 		case '2':	/* seek grail */
894 			if (Player.p_palantir) {
895 				/* need a palantir to seek */
896 				fseek(Energyvoidfp, 0L, SEEK_SET);
897 				fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp);
898 				temp1 = distance(Player.p_x, Enrgyvoid.ev_x, Player.p_y, Enrgyvoid.ev_y);
899 				temp1 += ROLL(-temp1 / 10.0, temp1 / 5.0);	/* add some error */
900 				mvprintw(5, 0, "The palantir says the Grail is about %.0f away.\n", temp1);
901 			} else
902 				/* no palantir */
903 				mvaddstr(5, 0, "You need a palantir to seek the Grail.\n");
904 			return;
905 
906 		case '3':	/* lob monster at someone */
907 			mvaddstr(4, 0, "Which monster [0-99] ? ");
908 			temp1 = infloat();
909 			temp1 = MAX(0.0, MIN(99.0, temp1));
910 			tamper = T_MONSTER;
911 			option = "throw a monster at";
912 			break;
913 
914 		case '4':	/* move another player */
915 			mvaddstr(4, 0, "New X Y coordinates ? ");
916 			getstring(Databuf, SZ_DATABUF);
917 			sscanf(Databuf, "%lf %lf", &temp1, &temp2);
918 			tamper = T_RELOCATE;
919 			option = "relocate";
920 			break;
921 
922 		case '5':	/* bless a player */
923 			tamper = T_BLESSED;
924 			option = "bless";
925 			break;
926 
927 		case '6':	/* kill off a player */
928 			if (Wizard) {
929 				tamper = T_VAPORIZED;
930 				option = "vaporize";
931 				break;
932 			} else
933 				return;
934 
935 		default:
936 			return;
937 		}
938 
939 		/* adjust age after we are sure intervention will be done */
940 		/* end of valar, etc. options */
941 	}
942 
943 	for (;;) {
944 		/* prompt for player to affect */
945 		mvprintw(4, 0, "Who do you want to %s ? ", option);
946 		getstring(Databuf, SZ_DATABUF);
947 		truncstring(Databuf);
948 
949 		if (Databuf[0] == '\0')
950 			userlist(TRUE);
951 		else
952 			break;
953 	}
954 
955 	if (strcmp(Player.p_name, Databuf) != 0) {
956 		/* name other than self */
957 		if ((loc = findname(Databuf, &Other)) >= 0L) {
958 			if (Other.p_tampered != T_OFF) {
959 				mvaddstr(5, 0, "That person has something pending already.\n");
960 				return;
961 			} else {
962 				if (tamper == T_RELOCATE
963 				    && CIRCLE(temp1, temp2) < CIRCLE(Other.p_x, Other.p_y)
964 				    && !Wizard)
965 					mvaddstr(5, 0, "Cannot move someone closer to the Lord's Chamber.\n");
966 				else {
967 					if (tamper == T_BESTOW)
968 						Player.p_gold -= floor(temp1);
969 					if (!Wizard && (tamper == T_HEAL || tamper == T_MONSTER ||
970 						tamper == T_RELOCATE || tamper == T_BLESSED))
971 						Player.p_age += N_AGE;	/* age penalty */
972 					Other.p_tampered = tamper;
973 					Other.p_1scratch = floor(temp1);
974 					Other.p_2scratch = floor(temp2);
975 					writerecord(&Other, loc);
976 					mvaddstr(5, 0, "It is done.\n");
977 				}
978 				return;
979 			}
980 		} else
981 			/* player not found */
982 			mvaddstr(5, 0, "There is no one by that name.\n");
983 	} else
984 		/* self */
985 		mvaddstr(5, 0, "You may not do it to yourself!\n");
986 }
987 
988 /*
989  * FUNCTION: update energy void entry in energy void file
990  *
991  * ARGUMENTS:
992  *	struct energyvoid *vp - pointer to structure to write to file
993  *	long loc - location in file to update
994  *
995  * GLOBAL INPUTS: *Energyvoidfp
996  *
997  * DESCRIPTION:
998  *	Write out energy void structure at specified location.
999  */
1000 
1001 void
1002 writevoid(struct energyvoid *vp, long loc)
1003 {
1004 	fseek(Energyvoidfp, loc, SEEK_SET);
1005 	fwrite((char *)vp, SZ_VOIDSTRUCT, 1, Energyvoidfp);
1006 	fflush(Energyvoidfp);
1007 	fseek(Energyvoidfp, 0L, SEEK_SET);
1008 }
1009 
1010 /*
1011  * FUNCTION: allocate space for a new energy void
1012  *
1013  * RETURN VALUE: location of new energy void space
1014  *
1015  * GLOBAL INPUTS: *Energyvoidfp, Enrgyvoid
1016  *
1017  * DESCRIPTION:
1018  *	Search energy void file for an inactive entry and return its
1019  *	location.
1020  *	If no inactive ones are found, return one more than last location.
1021  */
1022 
1023 size_t
1024 allocvoid(void)
1025 {
1026 	size_t loc = 0;		/* location of new energy void */
1027 
1028 	fseek(Energyvoidfp, 0L, SEEK_SET);
1029 	while (fread((char *)&Enrgyvoid, SZ_VOIDSTRUCT, 1, Energyvoidfp) == 1)
1030 		if (Enrgyvoid.ev_active)
1031 			loc += SZ_VOIDSTRUCT;
1032 		else
1033 			break;
1034 
1035 	return (loc);
1036 }
1037