drm: Pass CRTC ID in userspace vblank events
[dragonfly.git] / games / phantasia / interplayer.c
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 }