(linenum→info "unix/slp.c:2238")

bsd-games/2.17/phantasia/main.c

    1: /*      $NetBSD: main.c,v 1.15 2004/12/09 05:15:59 jmc 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/stat.h>
   31: #include <sys/types.h>
   32: #include <pwd.h>
   33: #include <termios.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:  * Put one line of text into the file 'motd' for announcements, etc.
   46:  */
   47: 
   48: /*
   49:  * The scoreboard file is updated when someone dies, and keeps track
   50:  * of the highest character to date for that login.
   51:  * Being purged from the character file does not cause the scoreboard
   52:  * to be updated.
   53:  */
   54: 
   55: 
   56: /*
   57:  * main.c       Main routines for Phantasia
   58:  */
   59: 
   60: #include "include.h"
   61: #undef bool
   62: #include <curses.h>
   63: 
   64: int     main(int, char **);
   65: 
   66: int
   67: main(argc, argv)
   68:         int     argc;
   69:         char  **argv;
   70: {
   71:         bool    noheader = FALSE;      /* set if don't want header */
   72:         bool    headeronly = FALSE;    /* set if only want header */
   73:         bool    examine = FALSE;       /* set if examine a character */
   74:         time_t  seconds;               /* for time of day */
   75:         double  dtemp;                 /* for temporary calculations */
   76: 
   77:         initialstate();                        /* init globals */
   78: 
   79:         /* process arguments */
   80:         while (--argc && (*++argv)[0] == '-')
   81:                 switch ((*argv)[1]) {
   82:                 case 's':     /* short */
   83:                         noheader = TRUE;
   84:                         break;
   85: 
   86:                 case 'H':     /* Header */
   87:                         headeronly = TRUE;
   88:                         break;
   89: 
   90:                 case 'a':     /* all users */
   91:                         activelist();
   92:                         cleanup(TRUE);
   93:                         /* NOTREACHED */
   94: 
   95:                 case 'p':     /* purge old players */
   96:                         purgeoldplayers();
   97:                         cleanup(TRUE);
   98:                         /* NOTREACHED */
   99: 
  100:                 case 'S':     /* set 'Wizard' */
  101:                         Wizard = !getuid();
  102:                         break;
  103: 
  104:                 case 'x':     /* examine */
  105:                         examine = TRUE;
  106:                         break;
  107: 
  108:                 case 'm':     /* monsters */
  109:                         monstlist();
  110:                         cleanup(TRUE);
  111:                         /* NOTREACHED */
  112: 
  113:                 case 'b':     /* scoreboard */
  114:                         scorelist();
  115:                         cleanup(TRUE);
  116:                         /* NOTREACHED */
  117:                 }
  118: 
  119:         if (!isatty(0))                /* don't let non-tty's play */
  120:                 cleanup(TRUE);
  121:         /* NOTREACHED */
  122: 
  123:         playinit();            /* set up to catch signals, init curses */
  124: 
  125:         if (examine) {
  126:                 changestats(FALSE);
  127:                 cleanup(TRUE);
  128:                 /* NOTREACHED */
  129:         }
  130:         if (!noheader) {
  131:                 titlelist();
  132:                 purgeoldplayers();    /* clean up old characters */
  133:         }
  134:         if (headeronly)
  135:                 cleanup(TRUE);
  136:         /* NOTREACHED */
  137: 
  138:         do
  139:                 /* get the player structure filled */
  140:         {
  141:                 Fileloc = -1L;
  142: 
  143:                 mvaddstr(22, 17, "Do you have a character to run [Q = Quit] ? ");
  144: 
  145:                 switch (getanswer("NYQ", FALSE)) {
  146:                 case 'Y':
  147:                         Fileloc = recallplayer();
  148:                         break;
  149: 
  150:                 case 'Q':
  151:                         cleanup(TRUE);
  152:                         /* NOTREACHED */
  153: 
  154:                 default:
  155:                         Fileloc = rollnewplayer();
  156:                         break;
  157:                 }
  158:                 clear();
  159:         }
  160:         while (Fileloc < 0L);
  161: 
  162:         if (Player.p_level > 5.0)
  163:                 /* low level players have long timeout */
  164:                 Timeout = TRUE;
  165: 
  166:         /* update some important player statistics */
  167:         strcpy(Player.p_login, Login);
  168:         time(&seconds);
  169:         Player.p_lastused = localtime(&seconds)->tm_yday;
  170:         Player.p_status = S_PLAYING;
  171:         writerecord(&Player, Fileloc);
  172: 
  173:         Statptr = &Stattable[Player.p_type];   /* initialize pointer */
  174: 
  175:         /* catch interrupts */
  176: #ifdef  BSD41
  177:         sigset(SIGINT, interrupt);
  178: #endif
  179: #ifdef  BSD42
  180:         signal(SIGINT, interrupt);
  181: #endif
  182: #ifdef  SYS3
  183:         signal(SIGINT, interrupt);
  184: #endif
  185: #ifdef  SYS5
  186:         signal(SIGINT, interrupt);
  187: #endif
  188: 
  189:         altercoordinates(Player.p_x, Player.p_y, A_FORCED);    /* set some flags */
  190: 
  191:         clear();
  192: 
  193:         for (;;)
  194:                 /* loop forever, processing input */
  195:         {
  196: 
  197:                 adjuststats();        /* cleanup stats */
  198: 
  199:                 if (Throne && Player.p_crowns == 0 && Player.p_specialtype != SC_KING)
  200:                         /* not allowed on throne -- move */
  201:                 {
  202:                         mvaddstr(5, 0, "You're not allowed in the Lord's Chamber without a crown.\n");
  203:                         altercoordinates(0.0, 0.0, A_NEAR);
  204:                 }
  205:                 checktampered();/* check for energy voids, etc. */
  206: 
  207:                 if (Player.p_status != S_CLOAKED
  208:                 /* not cloaked */
  209:                     && (dtemp = fabs(Player.p_x)) == fabs(Player.p_y)
  210:                 /* |x| = |y| */
  211:                     && !Throne)
  212:                         /* not on throne */
  213:                 {
  214:                         dtemp = sqrt(dtemp / 100.0);
  215:                         if (floor(dtemp) == dtemp)
  216:                                 /* |x| / 100 == n*n; at a trading post */
  217:                         {
  218:                                 tradingpost();
  219:                                 clear();
  220:                         }
  221:                 }
  222:                 checkbattle();        /* check for player to player battle */
  223:                 neatstuff();  /* gurus, medics, etc. */
  224: 
  225:                 if (Player.p_status == S_CLOAKED) {
  226:                         /* costs 3 mana per turn to be cloaked */
  227:                         if (Player.p_mana > 3.0)
  228:                                 Player.p_mana -= 3.0;
  229:                         else
  230:                                 /* ran out of mana, uncloak */
  231:                         {
  232:                                 Player.p_status = S_PLAYING;
  233:                                 Changed = TRUE;
  234:                         }
  235:                 }
  236: 
  237:                 if (Player.p_status != S_PLAYING && Player.p_status != S_CLOAKED)
  238:                         /* change status back to S_PLAYING */
  239:                 {
  240:                         Player.p_status = S_PLAYING;
  241:                         Changed = TRUE;
  242:                 }
  243:                 if (Changed)
  244:                         /* update file only if important stuff has changed */
  245:                 {
  246:                         writerecord(&Player, Fileloc);
  247:                         Changed = FALSE;
  248:                         continue;
  249:                 }
  250:                 readmessage();        /* read message, if any */
  251: 
  252:                 displaystats();       /* print statistics */
  253: 
  254:                 move(6, 0);
  255: 
  256:                 if (Throne)
  257:                         /* maybe make king, print prompt, etc. */
  258:                         throneroom();
  259: 
  260:                 /* print status line */
  261:                 addstr("1:Move  2:Players  3:Talk  4:Stats  5:Quit  ");
  262:                 if (Player.p_level >= MEL_CLOAK && Player.p_magiclvl >= ML_CLOAK)
  263:                         addstr("6:Cloak  ");
  264:                 if (Player.p_level >= MEL_TELEPORT && Player.p_magiclvl >= ML_TELEPORT)
  265:                         addstr("7:Teleport  ");
  266:                 if (Player.p_specialtype >= SC_COUNCIL || Wizard)
  267:                         addstr("8:Intervene  ");
  268: 
  269:                 procmain();   /* process input */
  270:         }
  271: }
  272: 
  273: void
  274: initialstate()
  275: {
  276:         struct stat sb;
  277: 
  278:         Beyond = FALSE;
  279:         Marsh = FALSE;
  280:         Throne = FALSE;
  281:         Changed = FALSE;
  282:         Wizard = FALSE;
  283:         Timeout = FALSE;
  284:         Users = 0;
  285:         Windows = FALSE;
  286:         Echo = TRUE;
  287: 
  288:         /* setup login name */
  289:         if ((Login = getlogin()) == NULL)
  290:                 Login = getpwuid(getuid())->pw_name;
  291: 
  292:         /* open some files */
  293:         if ((Playersfp = fopen(_PATH_PEOPLE, "r+")) == NULL)
  294:                 error(_PATH_PEOPLE);
  295:         /* NOTREACHED */
  296:         if (fileno(Playersfp) < 3)
  297:                 exit(1);
  298: 
  299:         if ((Monstfp = fopen(_PATH_MONST, "r+")) == NULL)
  300:                 error(_PATH_MONST);
  301:         /* NOTREACHED */
  302: 
  303:         if ((Messagefp = fopen(_PATH_MESS, "r")) == NULL)
  304:                 error(_PATH_MESS);
  305:         /* NOTREACHED */
  306: 
  307:         if ((Energyvoidfp = fopen(_PATH_VOID, "r+")) == NULL)
  308:                 error(_PATH_VOID);
  309:         if (fstat(fileno(Energyvoidfp), &sb) == -1)
  310:                 error("stat");
  311:         if (sb.st_size == 0) {
  312:                 /* initialize grail to new location */
  313:                 Enrgyvoid.ev_active = TRUE;
  314:                 Enrgyvoid.ev_x = ROLL(-1.0e6, 2.0e6);
  315:                 Enrgyvoid.ev_y = ROLL(-1.0e6, 2.0e6);
  316:                 writevoid(&Enrgyvoid, 0L);
  317:         }
  318: 
  319:         /* NOTREACHED */
  320: 
  321:         srandom((unsigned) time(NULL));        /* prime random numbers */
  322: }
  323: 
  324: long
  325: rollnewplayer()
  326: {
  327:         int     chartype;      /* character type */
  328:         int     ch;            /* input */
  329: 
  330:         initplayer(&Player);   /* initialize player structure */
  331: 
  332:         clear();
  333:         mvaddstr(4, 21, "Which type of character do you want:");
  334:         mvaddstr(8, 4,
  335: "1:Magic User  2:Fighter  3:Elf  4:Dwarf  5:Halfling  6:Experimento  ");
  336:         if (Wizard) {
  337:                 addstr("7:Super  ? ");
  338:                 chartype = getanswer("1234567", FALSE);
  339:         } else {
  340:                 addstr("?  ");
  341:                 chartype = getanswer("123456", FALSE);
  342:         }
  343: 
  344:         do {
  345:                 genchar(chartype);    /* roll up a character */
  346: 
  347:                 /* print out results */
  348:                 mvprintw(12, 14,
  349:                     "Strength    :  %2.0f  Quickness:  %2.0f  Mana       :  %2.0f\n",
  350:                     Player.p_strength, Player.p_quickness, Player.p_mana);
  351:                 mvprintw(13, 14,
  352:                     "Energy Level:  %2.0f  Brains   :  %2.0f  Magic Level:  %2.0f\n",
  353:                     Player.p_energy, Player.p_brains, Player.p_magiclvl);
  354: 
  355:                 if (Player.p_type == C_EXPER || Player.p_type == C_SUPER)
  356:                         break;
  357: 
  358:                 mvaddstr(14, 14, "Type '1' to keep >");
  359:                 ch = getanswer(" ", TRUE);
  360:         }
  361:         while (ch != '1');
  362: 
  363:         if (Player.p_type == C_EXPER || Player.p_type == C_SUPER)
  364:                 /* get coordinates for experimento */
  365:                 for (;;) {
  366:                         mvaddstr(16, 0, "Enter the X Y coordinates of your experimento ? ");
  367:                         getstring(Databuf, SZ_DATABUF);
  368:                         sscanf(Databuf, "%lf %lf", &Player.p_x, &Player.p_y);
  369: 
  370:                         if (fabs(Player.p_x) > D_EXPER || fabs(Player.p_y) > D_EXPER)
  371:                                 mvaddstr(17, 0, "Invalid coordinates.  Try again.\n");
  372:                         else
  373:                                 break;
  374:                 }
  375: 
  376:         for (;;)
  377:                 /* name the new character */
  378:         {
  379:                 mvprintw(18, 0,
  380:                     "Give your character a name [up to %d characters] ?  ", SZ_NAME - 1);
  381:                 getstring(Player.p_name, SZ_NAME);
  382:                 truncstring(Player.p_name);   /* remove trailing blanks */
  383: 
  384:                 if (Player.p_name[0] == '\0')
  385:                         /* no null names */
  386:                         mvaddstr(19, 0, "Invalid name.");
  387:                 else
  388:                         if (findname(Player.p_name, &Other) >= 0L)
  389:                                 /* cannot have duplicate names */
  390:                                 mvaddstr(19, 0, "Name already in use.");
  391:                         else
  392:                                 /* name is acceptable */
  393:                                 break;
  394: 
  395:                 addstr("  Pick another.\n");
  396:         }
  397: 
  398:         /* get a password for character */
  399:         Echo = FALSE;
  400: 
  401:         do {
  402:                 mvaddstr(20, 0, "Give your character a password [up to 8 characters] ? ");
  403:                 getstring(Player.p_password, SZ_PASSWORD);
  404:                 mvaddstr(21, 0, "Enter again to verify: ");
  405:                 getstring(Databuf, SZ_PASSWORD);
  406:         }
  407:         while (strcmp(Player.p_password, Databuf) != 0);
  408: 
  409:         Echo = TRUE;
  410: 
  411:         return (allocrecord());
  412: }
  413: 
  414: void
  415: procmain()
  416: {
  417:         int     ch;            /* input */
  418:         double  x;             /* desired new x coordinate */
  419:         double  y;             /* desired new y coordinate */
  420:         double  temp;          /* for temporary calculations */
  421:         FILE   *fp;            /* for opening files */
  422:         int     loop;          /* a loop counter */
  423:         bool    hasmoved = FALSE;      /* set if player has moved */
  424: 
  425:         ch = inputoption();
  426:         mvaddstr(4, 0, "\n\n");        /* clear status area */
  427: 
  428:         move(7, 0);
  429:         clrtobot();            /* clear data on bottom area of screen */
  430: 
  431:         if (Player.p_specialtype == SC_VALAR && (ch == '1' || ch == '7'))
  432:                 /* valar cannot move */
  433:                 ch = ' ';
  434: 
  435:         switch (ch) {
  436:         case 'K':              /* move up/north */
  437:         case 'N':
  438:                 x = Player.p_x;
  439:                 y = Player.p_y + MAXMOVE();
  440:                 hasmoved = TRUE;
  441:                 break;
  442: 
  443:         case 'J':              /* move down/south */
  444:         case 'S':
  445:                 x = Player.p_x;
  446:                 y = Player.p_y - MAXMOVE();
  447:                 hasmoved = TRUE;
  448:                 break;
  449: 
  450:         case 'L':              /* move right/east */
  451:         case 'E':
  452:                 x = Player.p_x + MAXMOVE();
  453:                 y = Player.p_y;
  454:                 hasmoved = TRUE;
  455:                 break;
  456: 
  457:         case 'H':              /* move left/west */
  458:         case 'W':
  459:                 x = Player.p_x - MAXMOVE();
  460:                 y = Player.p_y;
  461:                 hasmoved = TRUE;
  462:                 break;
  463: 
  464:         default:               /* rest */
  465:                 Player.p_energy += (Player.p_maxenergy + Player.p_shield) / 15.0
  466:                     + Player.p_level / 3.0 + 2.0;
  467:                 Player.p_energy =
  468:                     MIN(Player.p_energy, Player.p_maxenergy + Player.p_shield);
  469: 
  470:                 if (Player.p_status != S_CLOAKED)
  471:                         /* cannot find mana if cloaked */
  472:                 {
  473:                         Player.p_mana += (Circle + Player.p_level) / 4.0;
  474: 
  475:                         if (drandom() < 0.2 && Player.p_status == S_PLAYING && !Throne)
  476:                                 /* wandering monster */
  477:                                 encounter(-1);
  478:                 }
  479:                 break;
  480: 
  481:         case 'X':              /* change/examine a character */
  482:                 changestats(TRUE);
  483:                 break;
  484: 
  485:         case '1':              /* move */
  486:                 for (loop = 3; loop; --loop) {
  487:                         mvaddstr(4, 0, "X Y Coordinates ? ");
  488:                         getstring(Databuf, SZ_DATABUF);
  489: 
  490:                         if (sscanf(Databuf, "%lf %lf", &x, &y) != 2)
  491:                                 mvaddstr(5, 0, "Try again\n");
  492:                         else
  493:                                 if (distance(Player.p_x, x, Player.p_y, y) > MAXMOVE())
  494:                                         ILLMOVE();
  495:                                 else {
  496:                                         hasmoved = TRUE;
  497:                                         break;
  498:                                 }
  499:                 }
  500:                 break;
  501: 
  502:         case '2':              /* players */
  503:                 userlist(TRUE);
  504:                 break;
  505: 
  506:         case '3':              /* message */
  507:                 mvaddstr(4, 0, "Message ? ");
  508:                 getstring(Databuf, SZ_DATABUF);
  509:                 /* we open the file for writing to erase any data which is
  510:                  * already there */
  511:                 fp = fopen(_PATH_MESS, "w");
  512:                 if (Databuf[0] != '\0')
  513:                         fprintf(fp, "%s: %s", Player.p_name, Databuf);
  514:                 fclose(fp);
  515:                 break;
  516: 
  517:         case '4':              /* stats */
  518:                 allstatslist();
  519:                 break;
  520: 
  521:         case '5':              /* good-bye */
  522:                 leavegame();
  523:                 /* NOTREACHED */
  524: 
  525:         case '6':              /* cloak */
  526:                 if (Player.p_level < MEL_CLOAK || Player.p_magiclvl < ML_CLOAK)
  527:                         ILLCMD();
  528:                 else
  529:                         if (Player.p_status == S_CLOAKED)
  530:                                 Player.p_status = S_PLAYING;
  531:                         else
  532:                                 if (Player.p_mana < MM_CLOAK)
  533:                                         mvaddstr(5, 0, "No mana left.\n");
  534:                                 else {
  535:                                         Changed = TRUE;
  536:                                         Player.p_mana -= MM_CLOAK;
  537:                                         Player.p_status = S_CLOAKED;
  538:                                 }
  539:                 break;
  540: 
  541:         case '7':              /* teleport */
  542:                 /*
  543:                  * conditions for teleport
  544:                  *     - 20 per (level plus magic level)
  545:                  *     - OR council of the wise or valar or ex-valar
  546:                  *     - OR transport from throne
  547:                  * transports from throne cost no mana
  548:                  */
  549:                 if (Player.p_level < MEL_TELEPORT || Player.p_magiclvl < ML_TELEPORT)
  550:                         ILLCMD();
  551:                 else
  552:                         for (loop = 3; loop; --loop) {
  553:                                 mvaddstr(4, 0, "X Y Coordinates ? ");
  554:                                 getstring(Databuf, SZ_DATABUF);
  555: 
  556:                                 if (sscanf(Databuf, "%lf %lf", &x, &y) == 2) {
  557:                                         temp = distance(Player.p_x, x, Player.p_y, y);
  558:                                         if (!Throne
  559:                                         /* can transport anywhere from throne */
  560:                                             && Player.p_specialtype <= SC_COUNCIL
  561:                                         /* council, valar can transport
  562:                                          * anywhere */
  563:                                             && temp > (Player.p_level + Player.p_magiclvl) * 20.0)
  564:                                                 /* can only move 20 per exp.
  565:                                                  * level + mag. level */
  566:                                                 ILLMOVE();
  567:                                         else {
  568:                                                 temp = (temp / 75.0 + 1.0) * 20.0;        /* mana used */
  569: 
  570:                                                 if (!Throne && temp > Player.p_mana)
  571:                                                         mvaddstr(5, 0, "Not enough power for that distance.\n");