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