1 /* 2 * Phantasia 3.3.2 -- Interterminal fantasy game 3 * 4 * Edward A. Estes 5 * AT&T, March 12, 1986 6 * 7 * $FreeBSD: src/games/phantasia/main.c,v 1.8 1999/11/16 02:57:34 billf Exp $ 8 * $DragonFly: src/games/phantasia/main.c,v 1.3 2005/05/31 00:06:26 swildner Exp $ 9 */ 10 11 /* DISCLAIMER: 12 * 13 * This game is distributed for free as is. It is not guaranteed to work 14 * in every conceivable environment. It is not even guaranteed to work 15 * in ANY environment. 16 * 17 * This game is distributed without notice of copyright, therefore it 18 * may be used in any manner the recipient sees fit. However, the 19 * author assumes no responsibility for maintaining or revising this 20 * game, in its original form, or any derivitives thereof. 21 * 22 * The author shall not be responsible for any loss, cost, or damage, 23 * including consequential damage, caused by reliance on this material. 24 * 25 * The author makes no warranties, express or implied, including warranties 26 * of merchantability or fitness for a particular purpose or use. 27 * 28 * AT&T is in no way connected with this game. 29 */ 30 31 #include <sys/types.h> 32 #include <pwd.h> 33 #include <string.h> 34 35 /* 36 * The program allocates as much file space as it needs to store characters, 37 * so the possibility exists for the character file to grow without bound. 38 * The file is purged upon normal entry to try to avoid that problem. 39 * A similar problem exists for energy voids. To alleviate the problem here, 40 * the void file is cleared with every new king, and a limit is placed 41 * on the size of the energy void file. 42 */ 43 44 /* 45 * The scoreboard file is updated when someone dies, and keeps track 46 * of the highest character to date for that login. 47 * Being purged from the character file does not cause the scoreboard 48 * to be updated. 49 */ 50 51 52 #include "include.h" 53 54 /* functions which we need to know about */ 55 /* fight.c */ 56 extern void encounter(int); 57 /* gamesupport.c */ 58 extern void activelist(void); 59 extern void changestats(bool); 60 extern void monstlist(void); 61 extern void purgeoldplayers(void); 62 extern void scorelist(void); 63 /* interplayer.c */ 64 extern void checkbattle(void); 65 extern void checktampered(void); 66 extern void dotampered(void); 67 extern void throneroom(void); 68 extern void userlist(bool); 69 /* io.c */ 70 extern int getanswer(const char *, bool); 71 extern void getstring(char *, int); 72 extern double infloat(void); 73 extern int inputoption(void); 74 extern void more(int); 75 /* misc.c */ 76 extern void adjuststats(void); 77 extern long allocrecord(void); 78 extern void allstatslist(void); 79 extern void altercoordinates(double, double, int); 80 extern void collecttaxes(double, double); 81 extern void death(const char *); 82 extern void displaystats(void); 83 extern double distance(double, double, double, double); 84 extern void error(const char *); 85 extern long findname(char *, struct player *); 86 extern void initplayer(struct player *); 87 extern void leavegame(void); 88 extern void readmessage(void); 89 extern void tradingpost(void); 90 extern void truncstring(char *); 91 extern void writerecord(struct player *, long); 92 /* phantglobs.c */ 93 extern double drandom(void); 94 95 void cleanup(bool); 96 void genchar(int); 97 void initialstate(void); 98 void neatstuff(void); 99 void playinit(void); 100 void procmain(void); 101 long recallplayer(void); 102 long rollnewplayer(void); 103 void titlelist(void); 104 105 /* 106 * FUNCTION: initialize state, and call main process 107 * 108 * GLOBAL INPUTS: *Login, Throne, Wizard, Player, *stdscr, Changed, Databuf[], 109 * Fileloc, Stattable[] 110 * 111 * GLOBAL OUTPUTS: Wizard, Player, Changed, Fileloc, Timeout, *Statptr 112 * 113 * DESCRIPTION: 114 * Process arguments, initialize program, and loop forever processing 115 * player input. 116 */ 117 118 int 119 main(int argc, char **argv) 120 { 121 bool noheader = FALSE; /* set if don't want header */ 122 bool headeronly = FALSE; /* set if only want header */ 123 bool examine = FALSE; /* set if examine a character */ 124 time_t seconds; /* for time of day */ 125 double dtemp; /* for temporary calculations */ 126 127 initialstate(); /* init globals */ 128 129 /* process arguments */ 130 while (--argc && (*++argv)[0] == '-') 131 switch ((*argv)[1]) { 132 case 's': /* short */ 133 noheader = TRUE; 134 break; 135 136 case 'H': /* Header */ 137 headeronly = TRUE; 138 break; 139 140 case 'a': /* all users */ 141 activelist(); 142 cleanup(TRUE); 143 /* NOTREACHED */ 144 145 case 'p': /* purge old players */ 146 purgeoldplayers(); 147 cleanup(TRUE); 148 /* NOTREACHED */ 149 150 case 'S': /* set 'Wizard' */ 151 Wizard = !getuid(); 152 break; 153 154 case 'x': /* examine */ 155 examine = TRUE; 156 break; 157 158 case 'm': /* monsters */ 159 monstlist(); 160 cleanup(TRUE); 161 /* NOTREACHED */ 162 163 case 'b': /* scoreboard */ 164 scorelist(); 165 cleanup(TRUE); 166 /* NOTREACHED */ 167 } 168 169 if (!isatty(0)) /* don't let non-tty's play */ 170 cleanup(TRUE); 171 /* NOTREACHED */ 172 173 playinit(); /* set up to catch signals, init curses */ 174 175 if (examine) { 176 changestats(FALSE); 177 cleanup(TRUE); 178 /* NOTREACHED */ 179 } 180 181 if (!noheader) { 182 titlelist(); 183 purgeoldplayers(); /* clean up old characters */ 184 } 185 186 if (headeronly) 187 cleanup(TRUE); 188 /* NOTREACHED */ 189 190 do { 191 /* get the player structure filled */ 192 Fileloc = -1L; 193 194 mvaddstr(22, 17, "Do you have a character to run [Q = Quit] ? "); 195 196 switch (getanswer("NYQ", FALSE)) { 197 case 'Y': 198 Fileloc = recallplayer(); 199 break; 200 201 case 'Q': 202 cleanup(TRUE); 203 /* NOTREACHED */ 204 205 default: 206 Fileloc = rollnewplayer(); 207 break; 208 } 209 clear(); 210 } while (Fileloc < 0L); 211 212 if (Player.p_level > 5.0) 213 /* low level players have long timeout */ 214 Timeout = TRUE; 215 216 /* update some important player statistics */ 217 strcpy(Player.p_login, Login); 218 time(&seconds); 219 Player.p_lastused = localtime(&seconds)->tm_yday; 220 Player.p_status = S_PLAYING; 221 writerecord(&Player, Fileloc); 222 223 Statptr = &Stattable[Player.p_type]; /* initialize pointer */ 224 225 /* catch interrupts */ 226 #ifdef BSD41 227 sigset(SIGINT, interrupt); 228 #endif 229 #ifdef BSD42 230 signal(SIGINT, interrupt); 231 #endif 232 #ifdef SYS3 233 signal(SIGINT, interrupt); 234 #endif 235 #ifdef SYS5 236 signal(SIGINT, interrupt); 237 #endif 238 239 altercoordinates(Player.p_x, Player.p_y, A_FORCED); /* set some flags */ 240 241 clear(); 242 243 for (;;) { 244 /* loop forever, processing input */ 245 246 adjuststats(); /* cleanup stats */ 247 248 if (Throne && Player.p_crowns == 0 && Player.p_specialtype != SC_KING) { 249 /* not allowed on throne -- move */ 250 mvaddstr(5, 0, "You're not allowed in the Lord's Chamber without a crown.\n"); 251 altercoordinates(0.0, 0.0, A_NEAR); 252 } 253 254 checktampered(); /* check for energy voids, etc. */ 255 256 if (Player.p_status != S_CLOAKED 257 /* not cloaked */ 258 && (dtemp = fabs(Player.p_x)) == fabs(Player.p_y) 259 /* |x| = |y| */ 260 && !Throne) { 261 /* not on throne */ 262 dtemp = sqrt(dtemp / 100.0); 263 if (floor(dtemp) == dtemp) { 264 /* |x| / 100 == n*n; at a trading post */ 265 tradingpost(); 266 clear(); 267 } 268 } 269 270 checkbattle(); /* check for player to player battle */ 271 neatstuff(); /* gurus, medics, etc. */ 272 273 if (Player.p_status == S_CLOAKED) { 274 /* costs 3 mana per turn to be cloaked */ 275 if (Player.p_mana > 3.0) 276 Player.p_mana -= 3.0; 277 else { 278 /* ran out of mana, uncloak */ 279 Player.p_status = S_PLAYING; 280 Changed = TRUE; 281 } 282 } 283 284 if (Player.p_status != S_PLAYING && Player.p_status != S_CLOAKED) { 285 /* change status back to S_PLAYING */ 286 Player.p_status = S_PLAYING; 287 Changed = TRUE; 288 } 289 290 if (Changed) { 291 /* update file only if important stuff has changed */ 292 writerecord(&Player, Fileloc); 293 Changed = FALSE; 294 continue; 295 } 296 297 readmessage(); /* read message, if any */ 298 299 displaystats(); /* print statistics */ 300 301 move(6, 0); 302 303 if (Throne) 304 /* maybe make king, print prompt, etc. */ 305 throneroom(); 306 307 /* print status line */ 308 addstr("1:Move 2:Players 3:Talk 4:Stats 5:Quit "); 309 if (Player.p_level >= MEL_CLOAK && Player.p_magiclvl >= ML_CLOAK) 310 addstr("6:Cloak "); 311 if (Player.p_level >= MEL_TELEPORT && Player.p_magiclvl >= ML_TELEPORT) 312 addstr("7:Teleport "); 313 if (Player.p_specialtype >= SC_COUNCIL || Wizard) 314 addstr("8:Intervene "); 315 316 procmain(); /* process input */ 317 } 318 } 319 320 /* 321 * FUNCTION: initialize some important global variable 322 * 323 * GLOBAL OUTPUTS: *Energyvoidfp, Echo, Marsh, *Login, Users, Beyond, 324 * Throne, Wizard, Changed, Okcount, Timeout, Windows, *Monstfp, *Messagefp, 325 * *Playersfp 326 * 327 * DESCRIPTION: 328 * Set global flags, and open files which remain open. 329 */ 330 331 void 332 initialstate(void) 333 { 334 Beyond = FALSE; 335 Marsh = FALSE; 336 Throne = FALSE; 337 Changed = FALSE; 338 Wizard = FALSE; 339 Timeout = FALSE; 340 Users = 0; 341 Windows = FALSE; 342 Echo = TRUE; 343 344 /* setup login name */ 345 if ((Login = getlogin()) == NULL) 346 Login = getpwuid(getuid())->pw_name; 347 348 /* open some files */ 349 if ((Playersfp = fopen(_PATH_PEOPLE, "r+")) == NULL) 350 error(_PATH_PEOPLE); 351 /* NOTREACHED */ 352 353 if ((Monstfp = fopen(_PATH_MONST, "r+")) == NULL) 354 error(_PATH_MONST); 355 /* NOTREACHED */ 356 357 if ((Messagefp = fopen(_PATH_MESS, "r")) == NULL) 358 error(_PATH_MESS); 359 /* NOTREACHED */ 360 361 if ((Energyvoidfp = fopen(_PATH_VOID, "r+")) == NULL) 362 error(_PATH_VOID); 363 /* NOTREACHED */ 364 365 srandomdev(); 366 } 367 368 /* 369 * FUNCTION: roll up a new character 370 * 371 * GLOBAL INPUTS: Other, Wizard, Player, *stdscr, Databuf[] 372 * 373 * GLOBAL OUTPUTS: Echo 374 * 375 * DESCRIPTION: 376 * Prompt player, and roll up new character. 377 */ 378 379 long 380 rollnewplayer(void) 381 { 382 int chartype; /* character type */ 383 int ch; /* input */ 384 385 initplayer(&Player); /* initialize player structure */ 386 387 clear(); 388 mvaddstr(4, 21, "Which type of character do you want:"); 389 mvaddstr(8, 4, "1:Magic User 2:Fighter 3:Elf 4:Dwarf 5:Halfling 6:Experimento "); 390 if (Wizard) { 391 addstr("7:Super ? "); 392 chartype = getanswer("1234567", FALSE); 393 } else { 394 addstr("? "); 395 chartype = getanswer("123456", FALSE); 396 } 397 398 do { 399 genchar(chartype); /* roll up a character */ 400 401 /* print out results */ 402 mvprintw(12, 14, 403 "Strength : %2.0f Quickness: %2.0f Mana : %2.0f\n", 404 Player.p_strength, Player.p_quickness, Player.p_mana); 405 mvprintw(13, 14, 406 "Energy Level: %2.0f Brains : %2.0f Magic Level: %2.0f\n", 407 Player.p_energy, Player.p_brains, Player.p_magiclvl); 408 409 if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) 410 break; 411 412 mvaddstr(14, 14, "Type '1' to keep >"); 413 ch = getanswer(" ", TRUE); 414 } while (ch != '1'); 415 416 if (Player.p_type == C_EXPER || Player.p_type == C_SUPER) 417 /* get coordinates for experimento */ 418 for (;;) { 419 mvaddstr(16, 0, "Enter the X Y coordinates of your experimento ? "); 420 getstring(Databuf, SZ_DATABUF); 421 sscanf(Databuf, "%lf %lf", &Player.p_x, &Player.p_y); 422 423 if (fabs(Player.p_x) > D_EXPER || fabs(Player.p_y) > D_EXPER) 424 mvaddstr(17, 0, "Invalid coordinates. Try again.\n"); 425 else 426 break; 427 } 428 429 for (;;) { 430 /* name the new character */ 431 mvprintw(18, 0, 432 "Give your character a name [up to %d characters] ? ", SZ_NAME - 1); 433 getstring(Player.p_name, SZ_NAME); 434 truncstring(Player.p_name); /* remove trailing blanks */ 435 436 if (Player.p_name[0] == '\0') 437 /* no null names */ 438 mvaddstr(19, 0, "Invalid name."); 439 else if (findname(Player.p_name, &Other) >= 0L) 440 /* cannot have duplicate names */ 441 mvaddstr(19, 0, "Name already in use."); 442 else 443 /* name is acceptable */ 444 break; 445 446 addstr(" Pick another.\n"); 447 } 448 449 /* get a password for character */ 450 Echo = FALSE; 451 452 do { 453 mvaddstr(20, 0, "Give your character a password [up to 8 characters] ? "); 454 getstring(Player.p_password, SZ_PASSWORD); 455 mvaddstr(21, 0, "One more time to verify ? "); 456 getstring(Databuf, SZ_PASSWORD); 457 } while (strcmp(Player.p_password, Databuf) != 0); 458 459 Echo = TRUE; 460 461 return (allocrecord()); 462 } 463 464 /* 465 * FUNCTION: process input from player 466 * 467 * GLOBAL INPUTS: Circle, Illcmd[], Throne, Wizard, Player, *stdscr, 468 * Databuf[], Illmove[] 469 * 470 * GLOBAL OUTPUTS: Player, Changed 471 * 472 * DESCRIPTION: 473 * Process main menu options. 474 */ 475 476 void 477 procmain(void) 478 { 479 int ch; /* input */ 480 double x; /* desired new x coordinate */ 481 double y; /* desired new y coordinate */ 482 double temp; /* for temporary calculations */ 483 FILE *fp; /* for opening files */ 484 int loop; /* a loop counter */ 485 bool hasmoved = FALSE; /* set if player has moved */ 486 487 ch = inputoption(); 488 mvaddstr(4, 0, "\n\n"); /* clear status area */ 489 490 move(7, 0); 491 clrtobot(); /* clear data on bottom area of screen */ 492 493 if (Player.p_specialtype == SC_VALAR && (ch == '1' || ch == '7')) 494 /* valar cannot move */ 495 ch = ' '; 496 497 switch (ch) { 498 case 'K': /* move up/north */ 499 case 'N': 500 x = Player.p_x; 501 y = Player.p_y + MAXMOVE(); 502 hasmoved = TRUE; 503 break; 504 505 case 'J': /* move down/south */ 506 case 'S': 507 x = Player.p_x; 508 y = Player.p_y - MAXMOVE(); 509 hasmoved = TRUE; 510 break; 511 512 case 'L': /* move right/east */ 513 case 'E': 514 x = Player.p_x + MAXMOVE(); 515 y = Player.p_y; 516 hasmoved = TRUE; 517 break; 518 519 case 'H': /* move left/west */ 520 case 'W': 521 x = Player.p_x - MAXMOVE(); 522 y = Player.p_y; 523 hasmoved = TRUE; 524 break; 525 526 default: /* rest */ 527 Player.p_energy += (Player.p_maxenergy + Player.p_shield) / 15.0 + 528 Player.p_level / 3.0 + 2.0; 529 Player.p_energy = 530 MIN(Player.p_energy, Player.p_maxenergy + Player.p_shield); 531 532 if (Player.p_status != S_CLOAKED) { 533 /* cannot find mana if cloaked */ 534 Player.p_mana += (Circle + Player.p_level) / 4.0; 535 536 if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) 537 /* wandering monster */ 538 encounter(-1); 539 } 540 break; 541 542 case 'X': /* change/examine a character */ 543 changestats(TRUE); 544 break; 545 546 case '1': /* move */ 547 for (loop = 3; loop; --loop) { 548 mvaddstr(4, 0, "X Y Coordinates ? "); 549 getstring(Databuf, SZ_DATABUF); 550 551 if (sscanf(Databuf, "%lf %lf", &x, &y) != 2) 552 mvaddstr(5, 0, "Try again\n"); 553 else if (distance(Player.p_x, x, Player.p_y, y) > MAXMOVE()) 554 ILLMOVE(); 555 else { 556 hasmoved = TRUE; 557 break; 558 } 559 } 560 break; 561 562 case '2': /* players */ 563 userlist(TRUE); 564 break; 565 566 case '3': /* message */ 567 mvaddstr(4, 0, "Message ? "); 568 getstring(Databuf, SZ_DATABUF); 569 /* we open the file for writing to erase any data which is already there */ 570 fp = fopen(_PATH_MESS, "w"); 571 if (Databuf[0] != '\0') 572 fprintf(fp, "%s: %s", Player.p_name, Databuf); 573 fclose(fp); 574 break; 575 576 case '4': /* stats */ 577 allstatslist(); 578 break; 579 580 case '5': /* good-bye */ 581 leavegame(); 582 /* NOTREACHED */ 583 584 case '6': /* cloak */ 585 if (Player.p_level < MEL_CLOAK || Player.p_magiclvl < ML_CLOAK) 586 ILLCMD(); 587 else if (Player.p_status == S_CLOAKED) 588 Player.p_status = S_PLAYING; 589 else if (Player.p_mana < MM_CLOAK) 590 mvaddstr(5, 0, "No mana left.\n"); 591 else { 592 Changed = TRUE; 593 Player.p_mana -= MM_CLOAK; 594 Player.p_status = S_CLOAKED; 595 } 596 break; 597 598 case '7': /* teleport */ 599 600 /* 601 * conditions for teleport 602 * - 20 per (level plus magic level) 603 * - OR council of the wise or valar or ex-valar 604 * - OR transport from throne 605 * transports from throne cost no mana 606 */ 607 if (Player.p_level < MEL_TELEPORT || Player.p_magiclvl < ML_TELEPORT) 608 ILLCMD(); 609 else 610 for (loop = 3; loop; --loop) { 611 mvaddstr(4, 0, "X Y Coordinates ? "); 612 getstring(Databuf, SZ_DATABUF); 613 614 if (sscanf(Databuf, "%lf %lf", &x, &y) == 2) { 615 temp = distance(Player.p_x, x, Player.p_y, y); 616 if (!Throne 617 /* can transport anywhere from throne */ 618 && Player.p_specialtype <= SC_COUNCIL 619 /* council, valar can transport anywhere */ 620 && temp > (Player.p_level + Player.p_magiclvl) * 20.0) 621 /* can only move 20 per exp. level + mag. level */ 622 ILLMOVE(); 623 else { 624 temp = (temp / 75.0 + 1.0) * 20.0; /* mana used */ 625 626 if (!Throne && temp > Player.p_mana) 627 mvaddstr(5, 0, "Not enough power for that distance.\n"); 628 else { 629 if (!Throne) 630 Player.p_mana -= temp; 631 hasmoved = TRUE; 632 break; 633 } 634 } 635 } 636 } 637 break; 638 639 case 'C': 640 case '9': /* monster */ 641 if (Throne) 642 /* no monsters while on throne */ 643 mvaddstr(5, 0, "No monsters in the chamber!\n"); 644 else if (Player.p_specialtype != SC_VALAR) { 645 /* the valar cannot call monsters */ 646 Player.p_sin += 1e-6; 647 encounter(-1); 648 } 649 break; 650 651 case '0': /* decree */ 652 if (Wizard || (Player.p_specialtype == SC_KING && Throne)) 653 /* kings must be on throne to decree */ 654 dotampered(); 655 else 656 ILLCMD(); 657 break; 658 659 case '8': /* intervention */ 660 if (Wizard || Player.p_specialtype >= SC_COUNCIL) 661 dotampered(); 662 else 663 ILLCMD(); 664 break; 665 } 666 667 if (hasmoved) { 668 /* player has moved -- alter coordinates, and do random monster */ 669 altercoordinates(x, y, A_SPECIFIC); 670 671 if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne) 672 encounter(-1); 673 } 674 } 675 676 /* 677 * FUNCTION: print title page 678 * 679 * GLOBAL INPUTS: Lines, Other, *stdscr, Databuf[], *Playersfp 680 * 681 * GLOBAL OUTPUTS: Lines 682 * 683 * DESCRIPTION: 684 * Print important information about game, players, etc. 685 */ 686 687 void 688 titlelist(void) 689 { 690 FILE *fp; /* used for opening various files */ 691 bool councilfound = FALSE; /* set if we find a member of the council */ 692 bool kingfound = FALSE; /* set if we find a king */ 693 double hiexp, nxtexp; /* used for finding the two highest players */ 694 double hilvl, nxtlvl; /* used for finding the two highest players */ 695 char hiname[21], nxtname[21]; /* used for finding the two highest players */ 696 697 nxtexp = 0; 698 mvaddstr(0, 14, "W e l c o m e t o P h a n t a s i a (vers. 3.3.2)!"); 699 700 /* print message of the day */ 701 if ((fp = fopen(_PATH_MOTD, "r")) != NULL 702 && fgets(Databuf, SZ_DATABUF, fp) != NULL) { 703 mvaddstr(2, 40 - strlen(Databuf) / 2, Databuf); 704 fclose(fp); 705 } 706 707 /* search for king */ 708 fseek(Playersfp, 0L, SEEK_SET); 709 while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 710 if (Other.p_specialtype == SC_KING && 711 Other.p_status != S_NOTUSED) { 712 /* found the king */ 713 sprintf(Databuf, "The present ruler is %s Level:%.0f", 714 Other.p_name, Other.p_level); 715 mvaddstr(4, 40 - strlen(Databuf) / 2, Databuf); 716 kingfound = TRUE; 717 break; 718 } 719 720 if (!kingfound) 721 mvaddstr(4, 24, "There is no ruler at this time."); 722 723 /* search for valar */ 724 fseek(Playersfp, 0L, SEEK_SET); 725 while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 726 if (Other.p_specialtype == SC_VALAR && Other.p_status != S_NOTUSED) { 727 /* found the valar */ 728 sprintf(Databuf, "The Valar is %s Login: %s", 729 Other.p_name, Other.p_login); 730 mvaddstr(6, 40 - strlen(Databuf) / 2, Databuf); 731 break; 732 } 733 734 /* search for council of the wise */ 735 fseek(Playersfp, 0L, SEEK_SET); 736 Lines = 10; 737 while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 738 if (Other.p_specialtype == SC_COUNCIL && Other.p_status != S_NOTUSED) { 739 /* found a member of the council */ 740 if (!councilfound) { 741 mvaddstr(8, 30, "Council of the Wise:"); 742 councilfound = TRUE; 743 } 744 745 /* This assumes a finite (<=5) number of C.O.W.: */ 746 sprintf(Databuf, "%s Login: %s", Other.p_name, Other.p_login); 747 mvaddstr(Lines++, 40 - strlen(Databuf) / 2, Databuf); 748 } 749 750 /* search for the two highest players */ 751 nxtname[0] = hiname[0] = '\0'; 752 hiexp = 0.0; 753 nxtlvl = hilvl = 0; 754 755 fseek(Playersfp, 0L, SEEK_SET); 756 while (fread((char *)&Other, SZ_PLAYERSTRUCT, 1, Playersfp) == 1) 757 if (Other.p_experience > hiexp && Other.p_specialtype <= SC_KING && Other.p_status != S_NOTUSED) { 758 /* highest found so far */ 759 nxtexp = hiexp; 760 hiexp = Other.p_experience; 761 nxtlvl = hilvl; 762 hilvl = Other.p_level; 763 strcpy(nxtname, hiname); 764 strcpy(hiname, Other.p_name); 765 } else if (Other.p_experience > nxtexp && 766 Other.p_specialtype <= SC_KING && 767 Other.p_status != S_NOTUSED) { 768 /* next highest found so far */ 769 nxtexp = Other.p_experience; 770 nxtlvl = Other.p_level; 771 strcpy(nxtname, Other.p_name); 772 } 773 774 mvaddstr(15, 28, "Highest characters are:"); 775 sprintf(Databuf, "%s Level:%.0f and %s Level:%.0f", 776 hiname, hilvl, nxtname, nxtlvl); 777 mvaddstr(17, 40 - strlen(Databuf) / 2, Databuf); 778 779 /* print last to die */ 780 if ((fp = fopen(_PATH_LASTDEAD, "r")) != NULL 781 && fgets(Databuf, SZ_DATABUF, fp) != NULL) { 782 mvaddstr(19, 25, "The last character to die was:"); 783 mvaddstr(20, 40 - strlen(Databuf) / 2, Databuf); 784 fclose(fp); 785 } 786 787 refresh(); 788 } 789 790 /* 791 * FUNCTION: find a character on file 792 * 793 * GLOBAL INPUTS: Player, *stdscr, Databuf[] 794 * 795 * GLOBAL OUTPUTS: Echo, Player 796 * 797 * DESCRIPTION: 798 * Search for a character of a certain name, and check password. 799 */ 800 801 long 802 recallplayer(void) 803 { 804 long loc = 0L; /* location in player file */ 805 int loop; /* loop counter */ 806 int ch; /* input */ 807 808 clear(); 809 mvprintw(10, 0, "What was your character's name ? "); 810 getstring(Databuf, SZ_NAME); 811 truncstring(Databuf); 812 813 if ((loc = findname(Databuf, &Player)) >= 0L) { 814 /* found character */ 815 Echo = FALSE; 816 817 for (loop = 0; loop < 2; ++loop) { 818 /* prompt for password */ 819 mvaddstr(11, 0, "Password ? "); 820 getstring(Databuf, SZ_PASSWORD); 821 if (strcmp(Databuf, Player.p_password) == 0) { 822 /* password good */ 823 Echo = TRUE; 824 825 if (Player.p_status != S_OFF) { 826 /* player did not exit normally last time */ 827 clear(); 828 addstr("Your character did not exit normally last time.\n"); 829 addstr("If you think you have good cause to have your character saved,\n"); 830 printw("you may quit and mail your reason to 'root'.\n"); 831 addstr("Otherwise, continuing spells certain death.\n"); 832 addstr("Do you want to quit ? "); 833 ch = getanswer("YN", FALSE); 834 if (ch == 'Y') { 835 Player.p_status = S_HUNGUP; 836 writerecord(&Player, loc); 837 cleanup(TRUE); 838 /* NOTREACHED */ 839 } 840 death("Stupidity"); 841 /* NOTREACHED */ 842 } 843 return (loc); 844 } else 845 mvaddstr(12, 0, "No good.\n"); 846 } 847 848 Echo = TRUE; 849 } else 850 mvaddstr(11, 0, "Not found.\n"); 851 852 more(13); 853 return (-1L); 854 } 855 856 /* 857 * FUNCTION: do random stuff 858 * 859 * GLOBAL INPUTS: Player, *stdscr, *Statptr 860 * 861 * GLOBAL OUTPUTS: Player 862 * 863 * DESCRIPTION: 864 * Handle gurus, medics, etc. 865 */ 866 867 void 868 neatstuff(void) 869 { 870 double temp; /* for temporary calculations */ 871 int ch; /* input */ 872 873 switch ((int)ROLL(0.0, 100.0)) { 874 case 1: 875 case 2: 876 if (Player.p_poison > 0.0) { 877 mvaddstr(4, 0, "You've found a medic! How much will you offer to be cured ? "); 878 temp = floor(infloat()); 879 if (temp < 0.0 || temp > Player.p_gold) { 880 /* negative gold, or more than available */ 881 mvaddstr(6, 0, "He was not amused, and made you worse.\n"); 882 Player.p_poison += 1.0; 883 } else if (drandom() / 2.0 > (temp + 1.0) / MAX(Player.p_gold, 1)) 884 /* medic wants 1/2 of available gold */ 885 mvaddstr(5, 0, "Sorry, he wasn't interested.\n"); 886 else { 887 mvaddstr(5, 0, "He accepted."); 888 Player.p_poison = MAX(0.0, Player.p_poison - 1.0); 889 Player.p_gold -= temp; 890 } 891 } 892 break; 893 894 case 3: 895 mvaddstr(4, 0, "You've been caught raping and pillaging!\n"); 896 Player.p_experience += 4000.0; 897 Player.p_sin += 0.5; 898 break; 899 900 case 4: 901 temp = ROLL(10.0, 75.0); 902 mvprintw(4, 0, "You've found %.0f gold pieces, want them ? ", temp); 903 ch = getanswer("NY", FALSE); 904 905 if (ch == 'Y') 906 collecttaxes(temp, 0.0); 907 break; 908 909 case 5: 910 if (Player.p_sin > 1.0) { 911 mvaddstr(4, 0, "You've found a Holy Orb!\n"); 912 Player.p_sin -= 0.25; 913 } 914 break; 915 916 case 6: 917 if (Player.p_poison < 1.0) { 918 mvaddstr(4, 0, "You've been hit with a plague!\n"); 919 Player.p_poison += 1.0; 920 } 921 break; 922 923 case 7: 924 mvaddstr(4, 0, "You've found some holy water.\n"); 925 ++Player.p_holywater; 926 break; 927 928 case 8: 929 mvaddstr(4, 0, "You've met a Guru. . ."); 930 if (drandom() * Player.p_sin > 1.0) 931 addstr("You disgusted him with your sins!\n"); 932 else if (Player.p_poison > 0.0) { 933 addstr("He looked kindly upon you, and cured you.\n"); 934 Player.p_poison = 0.0; 935 } else { 936 addstr("He rewarded you for your virtue.\n"); 937 Player.p_mana += 50.0; 938 Player.p_shield += 2.0; 939 } 940 break; 941 942 case 9: 943 mvaddstr(4, 0, "You've found an amulet.\n"); 944 ++Player.p_amulets; 945 break; 946 947 case 10: 948 if (Player.p_blindness) { 949 mvaddstr(4, 0, "You've regained your sight!\n"); 950 Player.p_blindness = FALSE; 951 } 952 break; 953 954 default: /* deal with poison */ 955 if (Player.p_poison > 0.0) { 956 temp = Player.p_poison * Statptr->c_weakness 957 * Player.p_maxenergy / 600.0; 958 if (Player.p_energy > Player.p_maxenergy / 10.0 959 && temp + 5.0 < Player.p_energy) 960 Player.p_energy -= temp; 961 } 962 break; 963 } 964 } 965 966 /* 967 * FUNCTION: generate a random character 968 * 969 * ARGUMENTS: 970 * int type - ASCII value of character type to generate 971 * 972 * GLOBAL INPUTS: Wizard, Player, Stattable[] 973 * 974 * GLOBAL OUTPUTS: Player 975 * 976 * DESCRIPTION: 977 * Use the lookup table for rolling stats. 978 */ 979 980 void 981 genchar(int type) 982 { 983 int subscript; /* used for subscripting into Stattable */ 984 struct charstats *statptr; /* for pointing into Stattable */ 985 986 subscript = type - '1'; 987 988 if (subscript < C_MAGIC || subscript > C_EXPER) 989 if (subscript != C_SUPER || !Wizard) 990 /* fighter is default */ 991 subscript = C_FIGHTER; 992 993 statptr = &Stattable[subscript]; 994 995 Player.p_quickness = 996 ROLL(statptr->c_quickness.base, statptr->c_quickness.interval); 997 Player.p_strength = 998 ROLL(statptr->c_strength.base, statptr->c_strength.interval); 999 Player.p_mana = 1000 ROLL(statptr->c_mana.base, statptr->c_mana.interval); 1001 Player.p_maxenergy = 1002 Player.p_energy = 1003 ROLL(statptr->c_energy.base, statptr->c_energy.interval); 1004 Player.p_brains = 1005 ROLL(statptr->c_brains.base, statptr->c_brains.interval); 1006 Player.p_magiclvl = 1007 ROLL(statptr->c_magiclvl.base, statptr->c_magiclvl.interval); 1008 1009 Player.p_type = subscript; 1010 1011 if (Player.p_type == C_HALFLING) 1012 /* give halfling some experience */ 1013 Player.p_experience = ROLL(600.0, 200.0); 1014 } 1015 1016 /* 1017 * FUNCTION: initialize for playing game 1018 * 1019 * GLOBAL INPUTS: *stdscr, ill_sig() 1020 * 1021 * GLOBAL OUTPUTS: Windows 1022 * 1023 * DESCRIPTION: 1024 * Catch a bunch of signals, and turn on curses stuff. 1025 */ 1026 1027 void 1028 playinit(void) 1029 { 1030 /* catch/ingnore signals */ 1031 1032 #ifdef BSD41 1033 sigignore(SIGQUIT); 1034 sigignore(SIGALRM); 1035 sigignore(SIGTERM); 1036 sigignore(SIGTSTP); 1037 sigignore(SIGTTIN); 1038 sigignore(SIGTTOU); 1039 sighold(SIGINT); 1040 sigset(SIGHUP, ill_sig); 1041 sigset(SIGTRAP, ill_sig); 1042 sigset(SIGIOT, ill_sig); 1043 sigset(SIGEMT, ill_sig); 1044 sigset(SIGFPE, ill_sig); 1045 sigset(SIGBUS, ill_sig); 1046 sigset(SIGSEGV, ill_sig); 1047 sigset(SIGSYS, ill_sig); 1048 sigset(SIGPIPE, ill_sig); 1049 #endif 1050 #ifdef BSD42 1051 signal(SIGQUIT, ill_sig); 1052 signal(SIGALRM, SIG_IGN); 1053 signal(SIGTERM, SIG_IGN); 1054 signal(SIGTSTP, SIG_IGN); 1055 signal(SIGTTIN, SIG_IGN); 1056 signal(SIGTTOU, SIG_IGN); 1057 signal(SIGINT, ill_sig); 1058 signal(SIGHUP, SIG_DFL); 1059 signal(SIGTRAP, ill_sig); 1060 signal(SIGIOT, ill_sig); 1061 signal(SIGEMT, ill_sig); 1062 signal(SIGFPE, ill_sig); 1063 signal(SIGBUS, ill_sig); 1064 signal(SIGSEGV, ill_sig); 1065 signal(SIGSYS, ill_sig); 1066 signal(SIGPIPE, ill_sig); 1067 #endif 1068 #ifdef SYS3 1069 signal(SIGINT, SIG_IGN); 1070 signal(SIGQUIT, SIG_IGN); 1071 signal(SIGTERM, SIG_IGN); 1072 signal(SIGALRM, SIG_IGN); 1073 signal(SIGHUP, ill_sig); 1074 signal(SIGTRAP, ill_sig); 1075 signal(SIGIOT, ill_sig); 1076 signal(SIGEMT, ill_sig); 1077 signal(SIGFPE, ill_sig); 1078 signal(SIGBUS, ill_sig); 1079 signal(SIGSEGV, ill_sig); 1080 signal(SIGSYS, ill_sig); 1081 signal(SIGPIPE, ill_sig); 1082 #endif 1083 #ifdef SYS5 1084 signal(SIGINT, SIG_IGN); 1085 signal(SIGQUIT, SIG_IGN); 1086 signal(SIGTERM, SIG_IGN); 1087 signal(SIGALRM, SIG_IGN); 1088 signal(SIGHUP, ill_sig); 1089 signal(SIGTRAP, ill_sig); 1090 signal(SIGIOT, ill_sig); 1091 signal(SIGEMT, ill_sig); 1092 signal(SIGFPE, ill_sig); 1093 signal(SIGBUS, ill_sig); 1094 signal(SIGSEGV, ill_sig); 1095 signal(SIGSYS, ill_sig); 1096 signal(SIGPIPE, ill_sig); 1097 #endif 1098 1099 initscr(); /* turn on curses */ 1100 noecho(); /* do not echo input */ 1101 cbreak(); /* do not process erase, kill */ 1102 clear(); 1103 refresh(); 1104 Windows = TRUE; /* mark the state */ 1105 } 1106 1107 1108 /* 1109 * FUNCTION: close some files, and maybe exit 1110 * 1111 * ARGUMENTS: 1112 * bool doexit - exit flag 1113 * 1114 * GLOBAL INPUTS: *Energyvoidfp, LINES, *stdscr, Windows, *Monstfp, 1115 * *Messagefp, *Playersfp 1116 * 1117 * DESCRIPTION: 1118 * Close all open files. If we are "in curses" terminate curses. 1119 * If 'doexit' is set, exit, otherwise return. 1120 */ 1121 1122 void 1123 cleanup(bool doexit) 1124 { 1125 if (Windows) { 1126 move(LINES - 2, 0); 1127 refresh(); 1128 nocbreak(); 1129 endwin(); 1130 } 1131 if (Playersfp) { 1132 fclose(Playersfp); 1133 Playersfp = NULL; 1134 } 1135 if (Monstfp) { 1136 fclose(Monstfp); 1137 Monstfp = NULL; 1138 } 1139 if (Messagefp) { 1140 fclose(Messagefp); 1141 Messagefp = NULL; 1142 } 1143 if (Energyvoidfp) { 1144 fclose(Energyvoidfp); 1145 Energyvoidfp = NULL; 1146 } 1147 1148 if (doexit) 1149 exit(0); 1150 /* NOTREACHED */ 1151 } 1152