1 /*        $NetBSD: misc.c,v 1.24 2022/05/24 06:27:59 andvar Exp $     */
2 
3 /*
4  * misc.c  Phantasia miscellaneous support routines
5  */
6 
7 #include <errno.h>
8 #include <math.h>
9 #include <setjmp.h>
10 #include <signal.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include "macros.h"
17 #include "phantdefs.h"
18 #include "phantstruct.h"
19 #include "phantglobs.h"
20 #include "pathnames.h"
21 
22 #undef bool
23 #include <curses.h>
24 
25 
26 static double explevel(double);
27 
28 static void
movelevel(void)29 movelevel(void)
30 {
31           const struct charstats *statptr; /* for pointing into Stattable */
32           double  new;                  /* new level */
33           double  inc;                  /* increment between new and old levels */
34 
35           Changed = TRUE;
36 
37           if (Player.p_type == C_EXPER)
38                     /* roll a type to use for increment */
39                     statptr = &Stattable[(int) ROLL(C_MAGIC, C_HALFLING - C_MAGIC + 1)];
40           else
41                     statptr = Statptr;
42 
43           new = explevel(Player.p_experience);
44           inc = new - Player.p_level;
45           Player.p_level = new;
46 
47           /* add increments to statistics */
48           Player.p_strength += statptr->c_strength.increase * inc;
49           Player.p_mana += statptr->c_mana.increase * inc;
50           Player.p_brains += statptr->c_brains.increase * inc;
51           Player.p_magiclvl += statptr->c_magiclvl.increase * inc;
52           Player.p_maxenergy += statptr->c_energy.increase * inc;
53 
54           /* rest to maximum upon reaching new level */
55           Player.p_energy = Player.p_maxenergy + Player.p_shield;
56 
57           if (Player.p_crowns > 0 && Player.p_level >= 1000.0)
58                     /* no longer able to be king -- turn crowns into cash */
59           {
60                     Player.p_gold += ((double) Player.p_crowns) * 5000.0;
61                     Player.p_crowns = 0;
62           }
63           if (Player.p_level >= 3000.0 && Player.p_specialtype < SC_COUNCIL)
64                     /* make a member of the council */
65           {
66                     mvaddstr(6, 0, "You have made it to the Council of the Wise.\n");
67                     addstr("Good Luck on your search for the Holy Grail.\n");
68 
69                     Player.p_specialtype = SC_COUNCIL;
70 
71                     /* no rings for council and above */
72                     Player.p_ring.ring_type = R_NONE;
73                     Player.p_ring.ring_duration = 0;
74 
75                     Player.p_lives = 3; /* three extra lives */
76           }
77           if (Player.p_level > 9999.0 && Player.p_specialtype != SC_VALAR)
78                     death("Old age");
79 }
80 
81 const char   *
descrlocation(struct player * playerp,phbool shortflag)82 descrlocation(struct player *playerp, phbool shortflag)
83 {
84           double  circle;               /* corresponding circle for coordinates */
85           int     quadrant;   /* quandrant of grid */
86           const char   *label;          /* pointer to place name */
87           static const char *const nametable[4][4] =        /* names of places */
88           {
89                     {"Anorien", "Ithilien", "Rohan", "Lorien"},
90                     {"Gondor", "Mordor", "Dunland", "Rovanion"},
91                     {"South Gondor", "Khand", "Eriador", "The Iron Hills"},
92                     {"Far Harad", "Near Harad", "The Northern Waste", "Rhun"}
93           };
94 
95           if (playerp->p_specialtype == SC_VALAR)
96                     return (" is in Valhala");
97           else
98                     if ((circle = CIRCLE(playerp->p_x, playerp->p_y)) >= 1000.0) {
99                               if (MAX(fabs(playerp->p_x), fabs(playerp->p_y)) > D_BEYOND)
100                                         label = "The Point of No Return";
101                               else
102                                         label = "The Ashen Mountains";
103                     } else
104                               if (circle >= 55)
105                                         label = "Morannon";
106                               else
107                                         if (circle >= 35)
108                                                   label = "Kennaquahair";
109                                         else
110                                                   if (circle >= 20)
111                                                             label = "The Dead Marshes";
112                                                   else
113                                                             if (circle >= 9)
114                                                                       label = "The Outer Waste";
115                                                             else
116                                                                       if (circle >= 5)
117                                                                                 label = "The Moors Adventurous";
118                                                                       else {
119                                                                                 if (playerp->p_x == 0.0 && playerp->p_y == 0.0)
120                                                                                           label = "The Lord's Chamber";
121                                                                                 else {
122                                                                                           /* this
123                                                                                            *
124                                                                                            * expr
125                                                                                            * essi
126                                                                                            * on
127                                                                                            * is
128                                                                                            * spli
129                                                                                            * t
130                                                                                            * to
131                                                                                            * prev
132                                                                                            * ent
133                                                                                            * comp
134                                                                                            * iler
135                                                                                            *
136                                                                                            * loop
137                                                                                            *
138                                                                                            * with
139                                                                                            *
140                                                                                            * some
141                                                                                            *
142                                                                                            * comp
143                                                                                            * iler
144                                                                                            * s */
145                                                                                           quadrant = ((playerp->p_x > 0.0) ? 1 : 0);
146                                                                                           quadrant += ((playerp->p_y >= 0.0) ? 2 : 0);
147                                                                                           label = nametable[((int) circle) - 1][quadrant];
148                                                                                 }
149                                                                       }
150 
151           if (shortflag)
152                     snprintf(Databuf, SZ_DATABUF, "%.29s", label);
153           else
154                     snprintf(Databuf, SZ_DATABUF,
155                               " is in %s  (%.0f,%.0f)",
156                               label, playerp->p_x, playerp->p_y);
157 
158           return (Databuf);
159 }
160 
161 void
tradingpost(void)162 tradingpost(void)
163 {
164           double  numitems;   /* number of items to purchase */
165           double  cost;                 /* cost of purchase */
166           double  blessingcost;         /* cost of blessing */
167           int     ch;                   /* input */
168           int     size;                 /* size of the trading post */
169           int     loop;                 /* loop counter */
170           int     cheat = 0;  /* number of times player has tried to cheat */
171           bool    dishonest = FALSE;    /* set when merchant is dishonest */
172 
173           Player.p_status = S_TRADING;
174           writerecord(&Player, Fileloc);
175 
176           clear();
177           addstr("You are at a trading post. All purchases must be made with gold.");
178 
179           size = sqrt(fabs(Player.p_x / 100)) + 1;
180           size = MIN(7, size);
181 
182           /* set up cost of blessing */
183           blessingcost = 1000.0 * (Player.p_level + 5.0);
184 
185           /* print Menu */
186           move(7, 0);
187           for (loop = 0; loop < size; ++loop)
188                     /* print Menu */
189           {
190                     if (loop == 6)
191                               cost = blessingcost;
192                     else
193                               cost = Menu[loop].cost;
194                     printw("(%d) %-12s: %6.0f\n", loop + 1, Menu[loop].item, cost);
195           }
196 
197           mvprintw(5, 0, "L:Leave  P:Purchase  S:Sell Gems ? ");
198 
199           for (;;) {
200                     adjuststats();      /* truncate any bad values */
201 
202                     /* print some important statistics */
203                     mvprintw(1, 0, "Gold:   %9.0f  Gems:  %9.0f  Level:   %6.0f  Charms: %6d\n",
204                         Player.p_gold, Player.p_gems, Player.p_level, Player.p_charms);
205                     printw("Shield: %9.0f  Sword: %9.0f  Quicksilver:%3.0f  Blessed: %s\n",
206                         Player.p_shield, Player.p_sword, Player.p_quksilver,
207                         (Player.p_blessing ? " True" : "False"));
208                     printw("Brains: %9.0f  Mana:  %9.0f", Player.p_brains, Player.p_mana);
209 
210                     move(5, 36);
211                     ch = getanswer("LPS", FALSE);
212                     move(15, 0);
213                     clrtobot();
214                     switch (ch) {
215                     case 'L': /* leave */
216                     case '\n':
217                               altercoordinates(0.0, 0.0, A_NEAR);
218                               return;
219 
220                     case 'P': /* make purchase */
221                               mvaddstr(15, 0, "What what would you like to buy ? ");
222                               ch = getanswer(" 1234567", FALSE);
223                               move(15, 0);
224                               clrtoeol();
225 
226                               if (ch - '0' > size)
227                                         addstr("Sorry, this merchant doesn't have that.");
228                               else
229                                         switch (ch) {
230                                         case '1':
231                                                   printw("Mana is one per %.0f gold piece.  How many do you want (%.0f max) ? ",
232                                                       Menu[0].cost, floor(Player.p_gold / Menu[0].cost));
233                                                   cost = (numitems = floor(infloat())) * Menu[0].cost;
234 
235                                                   if (cost > Player.p_gold || numitems < 0)
236                                                             ++cheat;
237                                                   else {
238                                                             cheat = 0;
239                                                             Player.p_gold -= cost;
240                                                             if (drandom() < 0.02)
241                                                                       dishonest = TRUE;
242                                                             else
243                                                                       Player.p_mana += numitems;
244                                                   }
245                                                   break;
246 
247                                         case '2':
248                                                   printw("Shields are %.0f per +1.  How many do you want (%.0f max) ? ",
249                                                       Menu[1].cost, floor(Player.p_gold / Menu[1].cost));
250                                                   cost = (numitems = floor(infloat())) * Menu[1].cost;
251 
252                                                   if (numitems == 0.0)
253                                                             break;
254                                                   else
255                                                             if (cost > Player.p_gold || numitems < 0)
256                                                                       ++cheat;
257                                                             else
258                                                                       if (numitems < Player.p_shield)
259                                                                                 NOBETTER();
260                                                                       else {
261                                                                                 cheat = 0;
262                                                                                 Player.p_gold -= cost;
263                                                                                 if (drandom() < 0.02)
264                                                                                           dishonest = TRUE;
265                                                                                 else
266                                                                                           Player.p_shield = numitems;
267                                                                       }
268                                                   break;
269 
270                                         case '3':
271                                                   printw("A book costs %.0f gp.  How many do you want (%.0f max) ? ",
272                                                       Menu[2].cost, floor(Player.p_gold / Menu[2].cost));
273                                                   cost = (numitems = floor(infloat())) * Menu[2].cost;
274 
275                                                   if (cost > Player.p_gold || numitems < 0)
276                                                             ++cheat;
277                                                   else {
278                                                             cheat = 0;
279                                                             Player.p_gold -= cost;
280                                                             if (drandom() < 0.02)
281                                                                       dishonest = TRUE;
282                                                             else
283                                                                       if (drandom() * numitems > Player.p_level / 10.0
284                                                                           && numitems != 1) {
285                                                                                 printw("\nYou blew your mind!\n");
286                                                                                 Player.p_brains /= 5;
287                                                                       } else {
288                                                                                 Player.p_brains += floor(numitems) * ROLL(20, 8);
289                                                                       }
290                                                   }
291                                                   break;
292 
293                                         case '4':
294                                                   printw("Swords are %.0f gp per +1.  How many + do you want (%.0f max) ? ",
295                                                       Menu[3].cost, floor(Player.p_gold / Menu[3].cost));
296                                                   cost = (numitems = floor(infloat())) * Menu[3].cost;
297 
298                                                   if (numitems == 0.0)
299                                                             break;
300                                                   else
301                                                             if (cost > Player.p_gold || numitems < 0)
302                                                                       ++cheat;
303                                                             else
304                                                                       if (numitems < Player.p_sword)
305                                                                                 NOBETTER();
306                                                                       else {
307                                                                                 cheat = 0;
308                                                                                 Player.p_gold -= cost;
309                                                                                 if (drandom() < 0.02)
310                                                                                           dishonest = TRUE;
311                                                                                 else
312                                                                                           Player.p_sword = numitems;
313                                                                       }
314                                                   break;
315 
316                                         case '5':
317                                                   printw("A charm costs %.0f gp.  How many do you want (%.0f max) ? ",
318                                                       Menu[4].cost, floor(Player.p_gold / Menu[4].cost));
319                                                   cost = (numitems = floor(infloat())) * Menu[4].cost;
320 
321                                                   if (cost > Player.p_gold || numitems < 0)
322                                                             ++cheat;
323                                                   else {
324                                                             cheat = 0;
325                                                             Player.p_gold -= cost;
326                                                             if (drandom() < 0.02)
327                                                                       dishonest = TRUE;
328                                                             else
329                                                                       Player.p_charms += numitems;
330                                                   }
331                                                   break;
332 
333                                         case '6':
334                                                   printw("Quicksilver is %.0f gp per +1.  How many + do you want (%.0f max) ? ",
335                                                       Menu[5].cost, floor(Player.p_gold / Menu[5].cost));
336                                                   cost = (numitems = floor(infloat())) * Menu[5].cost;
337 
338                                                   if (numitems == 0.0)
339                                                             break;
340                                                   else
341                                                             if (cost > Player.p_gold || numitems < 0)
342                                                                       ++cheat;
343                                                             else
344                                                                       if (numitems < Player.p_quksilver)
345                                                                                 NOBETTER();
346                                                                       else {
347                                                                                 cheat = 0;
348                                                                                 Player.p_gold -= cost;
349                                                                                 if (drandom() < 0.02)
350                                                                                           dishonest = TRUE;
351                                                                                 else
352                                                                                           Player.p_quksilver = numitems;
353                                                                       }
354                                                   break;
355 
356                                         case '7':
357                                                   if (Player.p_blessing) {
358                                                             addstr("You already have a blessing.");
359                                                             break;
360                                                   }
361                                                   printw("A blessing requires a %.0f gp donation.  Still want one ? ", blessingcost);
362                                                   ch = getanswer("NY", FALSE);
363 
364                                                   if (ch == 'Y') {
365                                                             if (Player.p_gold < blessingcost)
366                                                                       ++cheat;
367                                                             else {
368                                                                       cheat = 0;
369                                                                       Player.p_gold -= blessingcost;
370                                                                       if (drandom() < 0.02)
371                                                                                 dishonest = TRUE;
372                                                                       else
373                                                                                 Player.p_blessing = TRUE;
374                                                             }
375                                                   }
376                                                   break;
377                                         }
378                               break;
379 
380                     case 'S': /* sell gems */
381                               mvprintw(15, 0, "A gem is worth %.0f gp.  How many do you want to sell (%.0f max) ? ",
382                                   (double) N_GEMVALUE, Player.p_gems);
383                               numitems = floor(infloat());
384 
385                               if (numitems > Player.p_gems || numitems < 0)
386                                         ++cheat;
387                               else {
388                                         cheat = 0;
389                                         Player.p_gems -= numitems;
390                                         Player.p_gold += numitems * N_GEMVALUE;
391                               }
392                     }
393 
394                     if (cheat == 1)
395                               mvaddstr(17, 0, "Come on, merchants aren't stupid.  Stop cheating.\n");
396                     else
397                               if (cheat == 2) {
398                                         mvaddstr(17, 0, "You had your chance.  This merchant happens to be\n");
399                                         printw("a %.0f level magic user, and you made %s mad!\n",
400                                             ROLL(Circle * 20.0, 40.0), (drandom() < 0.5) ? "him" : "her");
401                                         altercoordinates(0.0, 0.0, A_FAR);
402                                         Player.p_energy /= 2.0;
403                                         ++Player.p_sin;
404                                         more(23);
405                                         return;
406                               } else
407                                         if (dishonest) {
408                                                   mvaddstr(17, 0, "The merchant stole your money!");
409                                                   refresh();
410                                                   altercoordinates(Player.p_x - Player.p_x / 10.0,
411                                                       Player.p_y - Player.p_y / 10.0, A_SPECIFIC);
412                                                   sleep(2);
413                                                   return;
414                                         }
415           }
416 }
417 
418 void
displaystats(void)419 displaystats(void)
420 {
421           mvprintw(0, 0, "%s%s\n", Player.p_name, descrlocation(&Player, FALSE));
422           mvprintw(1, 0, "Level :%7.0f   Energy  :%9.0f(%9.0f)  Mana :%9.0f  Users:%3d\n",
423               Player.p_level, Player.p_energy, Player.p_maxenergy + Player.p_shield,
424               Player.p_mana, Users);
425           mvprintw(2, 0, "Quick :%3.0f(%3.0f)  Strength:%9.0f(%9.0f)  Gold :%9.0f  %s\n",
426               Player.p_speed, Player.p_quickness + Player.p_quksilver, Player.p_might,
427               Player.p_strength + Player.p_sword, Player.p_gold, descrstatus(&Player));
428 }
429 
430 void
allstatslist(void)431 allstatslist(void)
432 {
433           static const char *const flags[] = /* to print value of some bools */
434           {
435                     "False",
436                     " True"
437           };
438 
439           mvprintw(8, 0, "Type: %s\n", descrtype(&Player, FALSE));
440 
441           mvprintw(10, 0, "Experience: %9.0f", Player.p_experience);
442           mvprintw(11, 0, "Brains    : %9.0f", Player.p_brains);
443           mvprintw(12, 0, "Magic Lvl : %9.0f", Player.p_magiclvl);
444           mvprintw(13, 0, "Sin       : %9.5f", Player.p_sin);
445           mvprintw(14, 0, "Poison    : %9.5f", Player.p_poison);
446           mvprintw(15, 0, "Gems      : %9.0f", Player.p_gems);
447           mvprintw(16, 0, "Age       : %9ld", Player.p_age);
448           mvprintw(10, 40, "Holy Water: %9d", Player.p_holywater);
449           mvprintw(11, 40, "Amulets   : %9d", Player.p_amulets);
450           mvprintw(12, 40, "Charms    : %9d", Player.p_charms);
451           mvprintw(13, 40, "Crowns    : %9d", Player.p_crowns);
452           mvprintw(14, 40, "Shield    : %9.0f", Player.p_shield);
453           mvprintw(15, 40, "Sword     : %9.0f", Player.p_sword);
454           mvprintw(16, 40, "Quickslver: %9.0f", Player.p_quksilver);
455 
456           mvprintw(18, 0, "Blessing: %s   Ring: %s   Virgin: %s   Palantir: %s",
457               flags[(int)Player.p_blessing],
458               flags[Player.p_ring.ring_type != R_NONE],
459               flags[(int)Player.p_virgin],
460               flags[(int)Player.p_palantir]);
461 }
462 
463 const char   *
descrtype(struct player * playerp,phbool shortflag)464 descrtype(struct player *playerp, phbool shortflag)
465 {
466           int     type;                 /* for calculating result subscript */
467           static const char *const results[] =/* description table */
468           {
469                     " Magic User", " MU",
470                     " Fighter", " F ",
471                     " Elf", " E ",
472                     " Dwarf", " D ",
473                     " Halfling", " H ",
474                     " Experimento", " EX",
475                     " Super", " S ",
476                     " King", " K ",
477                     " Council of Wise", " CW",
478                     " Ex-Valar", " EV",
479                     " Valar", " V ",
480                     " ? ", " ? "
481           };
482 
483           type = playerp->p_type;
484 
485           switch (playerp->p_specialtype) {
486           case SC_NONE:
487                     type = playerp->p_type;
488                     break;
489 
490           case SC_KING:
491                     type = 7;
492                     break;
493 
494           case SC_COUNCIL:
495                     type = 8;
496                     break;
497 
498           case SC_EXVALAR:
499                     type = 9;
500                     break;
501 
502           case SC_VALAR:
503                     type = 10;
504                     break;
505           }
506 
507           type *= 2;                    /* calculate offset */
508 
509           if (type > 20)
510                     /* error */
511                     type = 22;
512 
513           if (shortflag)
514                     /* use short descriptions */
515                     ++type;
516 
517           if (playerp->p_crowns > 0) {
518                     strcpy(Databuf, results[type]);
519                     Databuf[0] = '*';
520                     return (Databuf);
521           } else
522                     return (results[type]);
523 }
524 
525 long
findname(const char * name,struct player * playerp)526 findname(const char *name, struct player *playerp)
527 {
528           long    loc = 0;    /* location in the file */
529 
530           fseek(Playersfp, 0L, SEEK_SET);
531           while (fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
532                     if (strcmp(playerp->p_name, name) == 0) {
533                               if (playerp->p_status != S_NOTUSED || Wizard)
534                                         /* found it */
535                                         return (loc);
536                     }
537                     loc += SZ_PLAYERSTRUCT;
538           }
539 
540           return (-1);
541 }
542 
543 long
allocrecord(void)544 allocrecord(void)
545 {
546           long    loc = 0L;   /* location in file */
547 
548           fseek(Playersfp, 0L, SEEK_SET);
549           while (fread((char *) &Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) {
550                     if (Other.p_status == S_NOTUSED)
551                               /* found an empty record */
552                               return (loc);
553                     else
554                               loc += SZ_PLAYERSTRUCT;
555           }
556 
557           /* make a new record */
558           initplayer(&Other);
559           Player.p_status = S_OFF;
560           writerecord(&Other, loc);
561 
562           return (loc);
563 }
564 
565 void
freerecord(struct player * playerp,long loc)566 freerecord(struct player *playerp, long loc)
567 {
568           playerp->p_name[0] = CH_MARKDELETE;
569           playerp->p_status = S_NOTUSED;
570           writerecord(playerp, loc);
571 }
572 
573 void
leavegame(void)574 leavegame(void)
575 {
576 
577           if (Player.p_level < 1.0)
578                     /* delete character */
579                     freerecord(&Player, Fileloc);
580           else {
581                     Player.p_status = S_OFF;
582                     writerecord(&Player, Fileloc);
583           }
584 
585           cleanup(TRUE);
586           /* NOTREACHED */
587 }
588 
589 void
death(const char * how)590 death(const char *how)
591 {
592           FILE   *fp;                   /* for updating various files */
593           int     ch;                   /* input */
594           static const char *const deathmesg[] =
595           /* add more messages here, if desired */
596           {
597                     "You have been wounded beyond repair.  ",
598                     "You have been disemboweled.  ",
599                     "You've been mashed, mauled, and spit upon.  (You're dead.)\n",
600                     "You died!  ",
601                     "You're a complete failure -- you've died!!\n",
602                     "You have been dealt a fatal blow!  "
603           };
604 
605           clear();
606 
607           if (strcmp(how, "Stupidity") != 0) {
608                     if (Player.p_level > 9999.0)
609                               /* old age */
610                               addstr("Characters must be retired upon reaching level 10000.  Sorry.");
611                     else
612                               if (Player.p_lives > 0)
613                                         /* extra lives */
614                               {
615                                         addstr("You should be more cautious.  You've been killed.\n");
616                                         printw("You only have %d more chance(s).\n", --Player.p_lives);
617                                         more(3);
618                                         Player.p_energy = Player.p_maxenergy;
619                                         return;
620                               } else
621                                         if (Player.p_specialtype == SC_VALAR) {
622                                                   addstr("You had your chances, but Valar aren't totally\n");
623                                                   addstr("immortal.  You are now left to wither and die . . .\n");
624                                                   more(3);
625                                                   Player.p_brains = Player.p_level / 25.0;
626                                                   Player.p_energy = Player.p_maxenergy /= 5.0;
627                                                   Player.p_quksilver = Player.p_sword = 0.0;
628                                                   Player.p_specialtype = SC_COUNCIL;
629                                                   return;
630                                         } else
631                                                   if (Player.p_ring.ring_inuse &&
632                                                       (Player.p_ring.ring_type == R_DLREG || Player.p_ring.ring_type == R_NAZREG))
633                                                             /* good ring in use - saved
634                                                              * from death */
635                                                   {
636                                                             mvaddstr(4, 0, "Your ring saved you from death!\n");
637                                                             refresh();
638                                                             Player.p_ring.ring_type = R_NONE;
639                                                             Player.p_energy = Player.p_maxenergy / 12.0 + 1.0;
640                                                             if (Player.p_crowns > 0)
641                                                                       --Player.p_crowns;
642                                                             return;
643                                                   } else
644                                                             if (Player.p_ring.ring_type == R_BAD
645                                                                 || Player.p_ring.ring_type == R_SPOILED)
646                                                                       /* bad ring in
647                                                                        * possession; name
648                                                                        * idiot after player */
649                                                             {
650                                                                       mvaddstr(4, 0,
651                                                                           "Your ring has taken control of you and turned you into a monster!\n");
652                                                                       fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
653                                                                       fread((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
654                                                                       strcpy(Curmonster.m_name, Player.p_name);
655                                                                       fseek(Monstfp, 13L * SZ_MONSTERSTRUCT, SEEK_SET);
656                                                                       fwrite((char *) &Curmonster, SZ_MONSTERSTRUCT, 1, Monstfp);
657                                                                       fflush(Monstfp);
658                                                             }
659           }
660           enterscore();                 /* update score board */
661 
662           /* put info in last dead file */
663           fp = fopen(_PATH_LASTDEAD, "w");
664           fprintf(fp, "%s (%s, run by %s, level %.0f, killed by %s)",
665               Player.p_name, descrtype(&Player, TRUE),
666               Player.p_login, Player.p_level, how);
667           fclose(fp);
668 
669           /* let other players know */
670           fp = fopen(_PATH_MESS, "w");
671           fprintf(fp, "%s was killed by %s.", Player.p_name, how);
672           fclose(fp);
673 
674           freerecord(&Player, Fileloc);
675 
676           clear();
677           move(10, 0);
678           addstr(deathmesg[(int) ROLL(0.0, (double) sizeof(deathmesg) / sizeof(char *))]);
679           addstr("Care to give it another try ? ");
680           ch = getanswer("NY", FALSE);
681 
682           if (ch == 'Y') {
683                     cleanup(FALSE);
684                     execl(_PATH_GAMEPROG, "phantasia", "-s",
685                         (Wizard ? "-S" : (char *) NULL), (char *) NULL);
686                     exit(0);
687                     /* NOTREACHED */
688           }
689           cleanup(TRUE);
690           /* NOTREACHED */
691 }
692 
693 void
writerecord(struct player * playerp,long place)694 writerecord(struct player *playerp, long place)
695 {
696           fseek(Playersfp, place, SEEK_SET);
697           fwrite((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
698           fflush(Playersfp);
699 }
700 
701 static double
explevel(double experience)702 explevel(double experience)
703 {
704           if (experience < 1.1e7)
705                     return (floor(pow((experience / 1000.0), 0.4875)));
706           else
707                     return (floor(pow((experience / 1250.0), 0.4865)));
708 }
709 
710 void
truncstring(char * string)711 truncstring(char *string)
712 {
713           int     length;               /* length of string */
714 
715           length = strlen(string);
716           while (string[--length] == ' ')
717                     string[length] = '\0';
718 }
719 
720 void
altercoordinates(double xnew,double ynew,int operation)721 altercoordinates(double xnew, double ynew, int operation)
722 {
723           switch (operation) {
724           case A_FORCED:                /* move with no checks */
725                     break;
726 
727           case A_NEAR:                  /* pick random coordinates near */
728                     xnew = Player.p_x + ROLL(1.0, 5.0);
729                     ynew = Player.p_y - ROLL(1.0, 5.0);
730                     /* fall through for check */
731 
732                     /* FALLTHROUGH */
733           case A_SPECIFIC:    /* just move player */
734                     if (Beyond && fabs(xnew) < D_BEYOND && fabs(ynew) < D_BEYOND)
735                               /*
736                                * cannot move back from point of no return
737                                * pick the largest coordinate to remain unchanged
738                                */
739                     {
740                               if (fabs(xnew) > fabs(ynew))
741                                         xnew = SGN(Player.p_x) * MAX(fabs(Player.p_x), D_BEYOND);
742                               else
743                                         ynew = SGN(Player.p_y) * MAX(fabs(Player.p_y), D_BEYOND);
744                     }
745                     break;
746 
747           case A_FAR:                   /* pick random coordinates far */
748                     xnew = Player.p_x + SGN(Player.p_x) * ROLL(50 * Circle, 250 * Circle);
749                     ynew = Player.p_y + SGN(Player.p_y) * ROLL(50 * Circle, 250 * Circle);
750                     break;
751           }
752 
753           /* now set location flags and adjust coordinates */
754           Circle = CIRCLE(Player.p_x = floor(xnew), Player.p_y = floor(ynew));
755 
756           /* set up flags based upon location */
757           Throne = Marsh = Beyond = FALSE;
758 
759           if (Player.p_x == 0.0 && Player.p_y == 0.0)
760                     Throne = TRUE;
761           else
762                     if (Circle < 35 && Circle >= 20)
763                               Marsh = TRUE;
764                     else
765                               if (MAX(fabs(Player.p_x), fabs(Player.p_y)) >= D_BEYOND)
766                                         Beyond = TRUE;
767 
768           Changed = TRUE;
769 }
770 
771 void
readrecord(struct player * playerp,long loc)772 readrecord(struct player *playerp, long loc)
773 {
774           fseek(Playersfp, loc, SEEK_SET);
775           fread((char *) playerp, SZ_PLAYERSTRUCT, 1, Playersfp);
776 }
777 
778 void
adjuststats(void)779 adjuststats(void)
780 {
781           double  dtemp;                /* for temporary calculations */
782 
783           if (explevel(Player.p_experience) > Player.p_level)
784                     /* move one or more levels */
785           {
786                     movelevel();
787                     if (Player.p_level > 5.0)
788                               Timeout = TRUE;
789           }
790           if (Player.p_specialtype == SC_VALAR)
791                     /* valar */
792                     Circle = Player.p_level / 5.0;
793 
794           /* calculate effective quickness */
795           dtemp = ((Player.p_gold + Player.p_gems / 2.0) - 1000.0) / Statptr->c_goldtote
796               - Player.p_level;
797           dtemp = MAX(0.0, dtemp);/* gold slows player down */
798           Player.p_speed = Player.p_quickness + Player.p_quksilver - dtemp;
799 
800           /* calculate effective strength */
801           if (Player.p_poison > 0.0)
802                     /* poison makes player weaker */
803           {
804                     dtemp = 1.0 - Player.p_poison * Statptr->c_weakness / 800.0;
805                     dtemp = MAX(0.1, dtemp);
806           } else
807                     dtemp = 1.0;
808           Player.p_might = dtemp * Player.p_strength + Player.p_sword;
809 
810           /* insure that important things are within limits */
811           Player.p_quksilver = MIN(99.0, Player.p_quksilver);
812           Player.p_mana = MIN(Player.p_mana,
813               Player.p_level * Statptr->c_maxmana + 1000.0);
814           Player.p_brains = MIN(Player.p_brains,
815               Player.p_level * Statptr->c_maxbrains + 200.0);
816           Player.p_charms = MIN(Player.p_charms, Player.p_level + 10.0);
817 
818           /*
819          * some implementations have problems with floating point compare
820          * we work around it with this stuff
821          */
822           Player.p_gold = floor(Player.p_gold) + 0.1;
823           Player.p_gems = floor(Player.p_gems) + 0.1;
824           Player.p_mana = floor(Player.p_mana) + 0.1;
825 
826           if (Player.p_ring.ring_type != R_NONE)
827                     /* do ring things */
828           {
829                     /* rest to max */
830                     Player.p_energy = Player.p_maxenergy + Player.p_shield;
831 
832                     if (Player.p_ring.ring_duration <= 0)
833                               /* clean up expired rings */
834                               switch (Player.p_ring.ring_type) {
835                               case R_BAD:         /* ring drives player crazy */
836                                         Player.p_ring.ring_type = R_SPOILED;
837                                         Player.p_ring.ring_duration = (short) ROLL(10.0, 25.0);
838                                         break;
839 
840                               case R_NAZREG:      /* ring disappears */
841                                         Player.p_ring.ring_type = R_NONE;
842                                         break;
843 
844                               case R_SPOILED:     /* ring kills player */
845                                         death("A cursed ring");
846                                         break;
847 
848                               case R_DLREG:       /* this ring doesn't expire */
849                                         Player.p_ring.ring_duration = 0;
850                                         break;
851                               }
852           }
853           if (Player.p_age / N_AGE > Player.p_degenerated)
854                     /* age player slightly */
855           {
856                     ++Player.p_degenerated;
857                     if (Player.p_quickness > 23.0)
858                               Player.p_quickness *= 0.99;
859                     Player.p_strength *= 0.97;
860                     Player.p_brains *= 0.95;
861                     Player.p_magiclvl *= 0.97;
862                     Player.p_maxenergy *= 0.95;
863                     Player.p_quksilver *= 0.95;
864                     Player.p_sword *= 0.93;
865                     Player.p_shield *= 0.93;
866           }
867 }
868 
869 void
initplayer(struct player * playerp)870 initplayer(struct player *playerp)
871 {
872           playerp->p_experience =
873               playerp->p_level =
874               playerp->p_strength =
875               playerp->p_sword =
876               playerp->p_might =
877               playerp->p_energy =
878               playerp->p_maxenergy =
879               playerp->p_shield =
880               playerp->p_quickness =
881               playerp->p_quksilver =
882               playerp->p_speed =
883               playerp->p_magiclvl =
884               playerp->p_mana =
885               playerp->p_brains =
886               playerp->p_poison =
887               playerp->p_gems =
888               playerp->p_sin =
889               playerp->p_1scratch =
890               playerp->p_2scratch = 0.0;
891 
892           playerp->p_gold = ROLL(50.0, 75.0) + 0.1;         /* give some gold */
893 
894           playerp->p_x = ROLL(-125.0, 251.0);
895           playerp->p_y = ROLL(-125.0, 251.0);     /* give random x, y */
896 
897           /* clear ring */
898           playerp->p_ring.ring_type = R_NONE;
899           playerp->p_ring.ring_duration = 0;
900           playerp->p_ring.ring_inuse = FALSE;
901 
902           playerp->p_age = 0L;
903 
904           playerp->p_degenerated = 1;   /* don't degenerate initially */
905 
906           playerp->p_type = C_FIGHTER;  /* default */
907           playerp->p_specialtype = SC_NONE;
908           playerp->p_lives =
909               playerp->p_crowns =
910               playerp->p_charms =
911               playerp->p_amulets =
912               playerp->p_holywater =
913               playerp->p_lastused = 0;
914           playerp->p_status = S_NOTUSED;
915           playerp->p_tampered = T_OFF;
916           playerp->p_istat = I_OFF;
917 
918           playerp->p_palantir =
919               playerp->p_blessing =
920               playerp->p_virgin =
921               playerp->p_blindness = FALSE;
922 
923           playerp->p_name[0] =
924               playerp->p_password[0] =
925               playerp->p_login[0] = '\0';
926 }
927 
928 void
readmessage(void)929 readmessage(void)
930 {
931           move(3, 0);
932           clrtoeol();
933           fseek(Messagefp, 0L, SEEK_SET);
934           if (fgets(Databuf, SZ_DATABUF, Messagefp) != NULL)
935                     addstr(Databuf);
936 }
937 
938 void
error(const char * whichfile)939 error(const char *whichfile)
940 {
941           int     (*funcp)(const char *,...);
942 
943           if (Windows) {
944                     funcp = printw;
945                     clear();
946           } else
947                     funcp = printf;
948 
949           (*funcp) ("An unrecoverable error has occurred reading %s.  (%s)\n", whichfile, strerror(errno));
950           (*funcp) ("Please run 'setup' to determine the problem.\n");
951           cleanup(TRUE);
952           /* NOTREACHED */
953 }
954 
955 double
distance(double x_1,double x_2,double y_1,double y_2)956 distance(double x_1, double x_2, double y_1, double y_2)
957 {
958           double  deltax, deltay;
959 
960           deltax = x_1 - x_2;
961           deltay = y_1 - y_2;
962           return (sqrt(deltax * deltax + deltay * deltay));
963 }
964 
965 void
ill_sig(int whichsig)966 ill_sig(int whichsig)
967 {
968           clear();
969           if (!(whichsig == SIGINT || whichsig == SIGQUIT))
970                     printw("Error: caught signal # %d.\n", whichsig);
971           cleanup(TRUE);
972           /* NOTREACHED */
973 }
974 
975 const char *
descrstatus(struct player * playerp)976 descrstatus(struct player *playerp)
977 {
978           switch (playerp->p_status) {
979           case S_PLAYING:
980                     if (playerp->p_energy < 0.2 * (playerp->p_maxenergy + playerp->p_shield))
981                               return ("Low Energy");
982                     else
983                               if (playerp->p_blindness)
984                                         return ("Blind");
985                               else
986                                         return ("In game");
987 
988           case S_CLOAKED:
989                     return ("Cloaked");
990 
991           case S_INBATTLE:
992                     return ("In Battle");
993 
994           case S_MONSTER:
995                     return ("Encounter");
996 
997           case S_TRADING:
998                     return ("Trading");
999 
1000           case S_OFF:
1001                     return ("Off");
1002 
1003           case S_HUNGUP:
1004                     return ("Hung up");
1005 
1006           default:
1007                     return ("");
1008           }
1009 }
1010 
1011 double
drandom(void)1012 drandom(void)
1013 {
1014           if (sizeof(int) != 2)
1015                     /* use only low bits */
1016                     return ((double) (random() & 0x7fff) / 32768.0);
1017           else
1018                     return ((double) random() / 32768.0);
1019 }
1020 
1021 void
collecttaxes(double gold,double gems)1022 collecttaxes(double gold, double gems)
1023 {
1024           FILE   *fp;                   /* to update Goldfile */
1025           double  dtemp;                /* for temporary calculations */
1026           double  taxes;                /* tax liability */
1027 
1028           /* add to cache */
1029           Player.p_gold += gold;
1030           Player.p_gems += gems;
1031 
1032           /* calculate tax liability */
1033           taxes = N_TAXAMOUNT / 100.0 * (N_GEMVALUE * gems + gold);
1034 
1035           if (Player.p_gold < taxes)
1036                     /* not enough gold to pay taxes, must convert some gems to
1037                      * gold */
1038           {
1039                     dtemp = floor(taxes / N_GEMVALUE + 1.0);          /* number of gems to
1040                                                                                  * convert */
1041 
1042                     if (Player.p_gems >= dtemp)
1043                               /* player has enough to convert */
1044                     {
1045                               Player.p_gems -= dtemp;
1046                               Player.p_gold += dtemp * N_GEMVALUE;
1047                     } else
1048                               /* take everything; this should never happen */
1049                     {
1050                               Player.p_gold += Player.p_gems * N_GEMVALUE;
1051                               Player.p_gems = 0.0;
1052                               taxes = Player.p_gold;
1053                     }
1054           }
1055           Player.p_gold -= taxes;
1056 
1057           if ((fp = fopen(_PATH_GOLD, "r+")) != NULL)
1058                     /* update taxes */
1059           {
1060                     dtemp = 0.0;
1061                     fread((char *) &dtemp, sizeof(double), 1, fp);
1062                     dtemp += floor(taxes);
1063                     fseek(fp, 0L, SEEK_SET);
1064                     fwrite((char *) &dtemp, sizeof(double), 1, fp);
1065                     fclose(fp);
1066           }
1067 }
1068