Merge branch 'vendor/GDTOA'
[dragonfly.git] / games / phantasia / interplayer.c
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 }