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