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