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