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