home *** CD-ROM | disk | FTP | other *** search
/ DP Tool Club 19 / CD_ASCQ_19_010295.iso / vrac / odoors50.zip / EX_SKI.C < prev    next >
C/C++ Source or Header  |  1994-09-24  |  19KB  |  579 lines

  1. /* EX_SKI.C - EX_SKI is a simple but addictive door game that is written     */
  2. /*            using OpenDoors. In this action game, the player must control  */
  3. /*            a skier through a downhill slalom course. The user may turn    */
  4. /*            the skier left or right, and the game ends as soon as the      */
  5. /*            player skis outside the marked course. The game begins at      */
  6. /*            an easy level, but quickly becomes more and more difficult     */
  7. /*            as the course to be navigated becomes more and more narrow.    */
  8. /*            The game maintains a list of players with high scores, and     */
  9. /*            this list may be viewed from the main menu.                    */
  10.  
  11. #include "opendoor.h"      /* Header file for OpenDoors functions */
  12.  
  13. #include <string.h>        /* Other required C header files */
  14. #include <stdio.h>
  15. #include <time.h>
  16. #include <errno.h>
  17. #include <stdlib.h>
  18.  
  19.  
  20. /* Hard-coded configurable constants - change these values to alter game */
  21. #define HIGH_SCORES           15       /* Number of high scores in list */
  22. #define TICK_DELAY            1        /* Delay between each game tick */
  23. #define INITIAL_COURSE_WIDTH  30       /* Initial width of ski course */
  24. #define MINIMUM_COURSE_WIDTH  4        /* Minimum width of course */
  25. #define DECREASE_WIDTH_AFTER  100      /* # of ticks before course narrows */
  26. #define CHANGE_DIRECTION      10       /* % of ticks course changes direction */
  27. #define MAX_NAME_SIZE         35       /* Maximum characters in player name */
  28. #define WAIT_FOR_FILE         10       /* Time to wait for access to file */
  29. #define SCORE_FILENAME   "SKIGAME.DAT" /* Name of high score file */
  30.  
  31.  
  32. /* High-score file format structure */
  33. typedef struct
  34. {
  35.    char szPlayerName[MAX_NAME_SIZE + 1];
  36.    long lnHighScore;
  37.    time_t lnPlayDate;
  38. } tHighScoreRecord;
  39.  
  40. typedef struct
  41. {
  42.    tHighScoreRecord aRecord[HIGH_SCORES];
  43. } tHighScoreFile;
  44.  
  45.  
  46. /* Prototypes for functions defined and used in this file */
  47. FILE *OpenAndReadHighScores(tHighScoreFile *pFileContents);
  48. void CloseHighScores(FILE *pfHighScoreFile);
  49. void WriteHighScores(FILE *pfHighScoreFile, tHighScoreFile *pFileContents);
  50. FILE *OpenExclusiveFile(char *pszFileName, char *pszAccess, time_t Wait);
  51. int FileExists(char *pszFileName);
  52. void ShowHighScores(void);
  53. void PlayGame(void);
  54. void SpaceRight(int nColumns);
  55. void MoveLeft(int nColumns);
  56. int AddHighScore(tHighScoreFile *pHighScores, tHighScoreRecord *pScoreRecord);
  57. long StartTimer(void);
  58. void WaitForTimerToElapse(long lnStartValue, long lnDuration);
  59.  
  60.  
  61. /* main() - Program's execution begins here */
  62. int main()
  63. {
  64.    char chMenuChoice;
  65.  
  66.    /* Loop until the user chooses to exit the door */
  67.    do
  68.    {
  69.       /* Clear the screen */
  70.       od_clr_scr();
  71.  
  72.       /* Display program title */
  73.       od_printf("`bright white`                █▀▀ █▀▄ █▀█ █▄ █ █▀▄   █▀▀ █   █▀█ █   █▀█ █▀█▀█\n\r");
  74.       od_printf("`bright red`────────────────`bright white`█▀█`bright red`─`bright white`█▀▄`bright red`─`bright white`█▀█`bright red`─`bright white`█`bright red`─`bright white`▀█`bright red`─`bright white`█");
  75.       od_printf("`bright red`─`bright white`█`bright red`───`bright white`▀▀█`bright red`─`bright white`█`bright red`───`bright white`█▀█`bright red`─`bright white`█`bright red`───`bright white`█");
  76.       od_printf("`bright red`─`bright white`█`bright red`─`bright white`█`bright red`─`bright white`█`bright red`─`bright white`█`bright red`───────────────\n\r");
  77.       od_printf("`bright white`                ▀▀▀ ▀ ▀ ▀ ▀ ▀  ▀ ▀▀    ▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀ ▀▀▀ ▀ ▀ ▀\n\r\n\r");
  78.  
  79.       /* Display instructions */
  80.       od_printf("`dark green`Prepare yourself for the challenge of Grand Slalom downhill skiing!\n\r\n\r");
  81.       od_printf("When `flashing dark green`playing`dark green` the game, press:\n\r");
  82.       od_printf("`dark green`          [`bright green`Q`dark green`] key to ski left\n\r");
  83.       od_printf("          [`bright green`W`dark green`] key to ski right\n\r\n\r");
  84.       od_printf("All that you have to do is ski within the slalom course.\n\r");
  85.       od_printf("It may sound easy - but be warned - it gets harder as you go!\n\r\n\r");
  86.  
  87.       /* Get menu choice from user. */
  88.       od_printf("`bright white`Now, press [ENTER] to begin game, [H] to view High Scores, [E] to Exit: ");
  89.       chMenuChoice = od_get_answer("HE\n\r");
  90.  
  91.       /* Perform appropriate action based on user's choice */
  92.       switch(chMenuChoice)
  93.       {
  94.          case '\n':
  95.          case '\r':
  96.             /* If user chooses to play the game */
  97.             PlayGame();
  98.             break;
  99.  
  100.          case 'H':
  101.             /* If user chose to view high scores */
  102.             ShowHighScores();
  103.             break;
  104.  
  105.          case 'E':
  106.             /* If user chose to return to BBS */
  107.             od_printf("\n\rBye from SKIGAME!\n\r");
  108.             break;
  109.       }
  110.    } while(chMenuChoice != 'E');
  111.  
  112.    /* Exit door at errorlevel 10, and do not hang up */
  113.    od_exit(10, FALSE);
  114.  
  115.    return(1);
  116. }
  117.  
  118.  
  119. /* OpenAndReadHighScores() - Opens high score file and reads contents. If */
  120. /*                           file does not exist, it is created. File is  */
  121. /*                           locked to serialize access by other nodes on */
  122. /*                           this system.                                 */
  123. FILE *OpenAndReadHighScores(tHighScoreFile *pFileContents)
  124. {
  125.    FILE *pfFile;
  126.    int iHighScore;
  127.  
  128.    /* If high score file does not exist */
  129.    if(!FileExists(SCORE_FILENAME))
  130.    {
  131.       /* Open and create it */
  132.       pfFile = OpenExclusiveFile(SCORE_FILENAME, "wb", WAIT_FOR_FILE);
  133.  
  134.       /* If open was successful */
  135.       if(pfFile != NULL)
  136.       {
  137.          /* Initialize new high score list */
  138.          for(iHighScore = 0; iHighScore < HIGH_SCORES; ++iHighScore)
  139.          {
  140.             pFileContents->aRecord[iHighScore].lnHighScore = 0L;
  141.          }
  142.  
  143.          /* Write high score list to the file */
  144.          WriteHighScores(pfFile, pFileContents);
  145.       }
  146.    }
  147.  
  148.    /* If high score file does exit */
  149.    else
  150.    {
  151.       /* Open the existing file */
  152.       pfFile = OpenExclusiveFile(SCORE_FILENAME, "r+b",
  153.                                 WAIT_FOR_FILE);
  154.  
  155.       /* Read the contents of the file */
  156.       if(fread(pFileContents, sizeof(tHighScoreFile), 1, pfFile) != 1)
  157.       {
  158.          /* If unable to read file, then return with an error */
  159.          fclose(pfFile);
  160.          pfFile = NULL;
  161.       }
  162.    }
  163.  
  164.    /* Return pointer to high score file, if avilable */
  165.    return(pfFile);
  166. }
  167.  
  168.  
  169. /* FileExists() - Returns TRUE if file exists, otherwise returns FALSE */
  170. int FileExists(char *pszFileName)
  171. {
  172.    /* Attempt to open the specified file for reading. */
  173.    FILE *pfFile = OpenExclusiveFile(pszFileName, "rb", WAIT_FOR_FILE);
  174.  
  175.    if(pfFile != NULL)
  176.    {
  177.       /* If we are able to open the file, then close it and return */
  178.       /* indicating that it exists.                                */
  179.       fclose(pfFile);
  180.       return(TRUE);
  181.    }
  182.    else
  183.    {
  184.       /* If we are unable to open the file, we proceed as if the file        */
  185.       /* doesn't exist (note that this may not always be a valid assumption) */
  186.       return(FALSE);
  187.    }
  188. }
  189.  
  190.  
  191. /* OpenExclusiveFile() - Opens a file for exclusive access, waiting if the */
  192. /*                       file is not currently available.                  */
  193. FILE *OpenExclusiveFile(char *pszFileName, char *pszAccess, time_t Wait)
  194. {
  195.    FILE *pfFile;
  196.    time_t StartTime = time(NULL);
  197.  
  198.    for(;;)
  199.    {
  200.       /* Attempt to open file */
  201.       pfFile = fopen(pszFileName, pszAccess);
  202.  
  203.       /* If file was opened successfuly, then exit */
  204.       if(pfFile != NULL) break;
  205.  
  206.       /* If open failed, but not due to access failure, then exit */
  207.       if(errno != EACCES) break;
  208.  
  209.       /* If maximum time has elapsed, then exit */
  210.       if(StartTime + Wait < time(NULL)) break;
  211.  
  212.       /* Give the OpenDoors kernel a chance to execute before trying again */
  213.       od_kernel();
  214.    }
  215.  
  216.    /* Return pointer to file, if opened */
  217.    return(pfFile);
  218. }
  219.  
  220.  
  221. /* CloseHighScores() - Closes the high score file, allowing other nodes on */
  222. /*                     system to access it.                                */
  223. void CloseHighScores(FILE *pfHighScoreFile)
  224. {
  225.    if(pfHighScoreFile != NULL)
  226.    {
  227.       fclose(pfHighScoreFile);
  228.    }
  229. }
  230.  
  231.  
  232. /* WriteHighScores() - Writes the information from pFileContents to the */
  233. /*                     high score file.                                 */
  234. void WriteHighScores(FILE *pfHighScoreFile, tHighScoreFile *pFileContents)
  235. {
  236.    if(pfHighScoreFile != NULL)
  237.    {
  238.       fseek(pfHighScoreFile, 0L, SEEK_SET);
  239.       fwrite(pFileContents, sizeof(tHighScoreFile), 1, pfHighScoreFile);
  240.    }
  241. }
  242.  
  243.  
  244. /* ShowHighScores() - Called From DoDoor() to display list of high scores */
  245. void ShowHighScores(void)
  246. {
  247.    FILE *pfFile;
  248.    tHighScoreFile HighScores;
  249.    int iHighScore;
  250.    struct tm *pTimeBlock;
  251.    char szTimeString[34];
  252.  
  253.    /* Clear the screen */
  254.    od_clr_scr();
  255.  
  256.    /* Attempt to read high scores from file */
  257.    pfFile = OpenAndReadHighScores(&HighScores);
  258.    CloseHighScores(pfFile);
  259.  
  260.    if(pfFile == NULL)
  261.    {
  262.       /* If unable to open high score file, display an error message */
  263.       od_printf("`bright red`Unable to access high score file!\n\r");
  264.    }
  265.    else
  266.    {
  267.       /* Display header line */
  268.       od_printf("`bright green`Player                            Score     "
  269.                 "Record Date`dark green`\n\r");
  270.       od_printf("───────────────────────────────────────────────────────────────────────────────\n\r");
  271.  
  272.       /* Display high scores */
  273.       for(iHighScore = 0; iHighScore < HIGH_SCORES; ++iHighScore)
  274.       {
  275.          /* Exit loop when we have reached the end of the high scores */
  276.          if(HighScores.aRecord[iHighScore].lnHighScore == 0L) break;
  277.  
  278.          /* Get local time when player set the high score */
  279.          pTimeBlock = localtime(&HighScores.aRecord[iHighScore].lnPlayDate);
  280.          strftime(szTimeString, sizeof(szTimeString),
  281.             "%B %d, %Y at %I:%M%p", pTimeBlock);
  282.  
  283.          /* Display next high score */
  284.          od_printf("%-32.32s  %-8ld  %s\n\r",
  285.                    HighScores.aRecord[iHighScore].szPlayerName,
  286.                    HighScores.aRecord[iHighScore].lnHighScore,
  287.                    szTimeString);
  288.       }
  289.    }
  290.  
  291.    /* Display footer line */
  292.    od_printf("───────────────────────────────────────────────────────────────────────────────\n\r\n\r");
  293.  
  294.    /* Wait for user to press a key */
  295.    od_printf("`bright white`Press [ENTER]/[RETURN] to continue: ");
  296.    od_get_answer("\n\r");
  297. }
  298.  
  299.  
  300. /* PlayGame() - Called from DoDoor() when user chooses to play a game. */
  301. void PlayGame(void)
  302. {
  303.    int nLeftEdge = 1;
  304.    int nRightEdge = nLeftEdge + 1 + INITIAL_COURSE_WIDTH;
  305.    int nPlayerPos = nLeftEdge + 1 + (INITIAL_COURSE_WIDTH / 2);
  306.    long lnScore = 0;
  307.    int nDistanceSinceShrink = 0;
  308.    int bMovingRight = TRUE;
  309.    char cKeyPress;
  310.    tHighScoreRecord ScoreRecord;
  311.    FILE *pfFile;
  312.    tHighScoreFile HighScores;
  313.    long lnTimerStart;
  314.    int nBackup;
  315.  
  316.    /* Clear the Screen */
  317.    od_set_color(L_WHITE, B_BLACK);
  318.    od_clr_scr();
  319.  
  320.    /* Set current display colour to white */
  321.    od_set_attrib(L_WHITE);
  322.  
  323.    /* Re-seed random number generator */
  324.    srand((unsigned int)time(NULL));
  325.  
  326.    /* Loop until game is over */
  327.    for(;;)
  328.    {
  329.       /* Start timer */
  330.       lnTimerStart = StartTimer();
  331.  
  332.       /* Display current line */
  333.       if(od_control.user_ansi || od_control.user_avatar)
  334.       {
  335.          SpaceRight(nLeftEdge - 1);
  336.          od_set_color(L_WHITE, D_RED);
  337.          od_putch(223);
  338.          od_repeat((unsigned char)219, 
  339.             (unsigned char)(nPlayerPos - nLeftEdge - 1));
  340.          od_putch(254);
  341.          od_repeat((unsigned char)219,
  342.             (unsigned char)(nRightEdge - nPlayerPos - 1));
  343.          od_putch(223);
  344.          nBackup = nRightEdge - nPlayerPos + 1;
  345.       }
  346.       else
  347.       {
  348.          /* If neither ANSI nor AVATAR modes are active, then display */
  349.          /* course using plain-ASCII.                                 */
  350.          SpaceRight(nLeftEdge - 1);
  351.          od_putch(bMovingRight ? '\\' : '/');
  352.          SpaceRight(nPlayerPos - nLeftEdge - 1);
  353.          od_putch('o');
  354.          SpaceRight(nRightEdge - nPlayerPos - 1);
  355.          od_putch(bMovingRight ? '\\' : '/');
  356.       }
  357.  
  358.       /* Loop for each key pressed by user */
  359.       while((cKeyPress = (char)od_get_key(FALSE)) != '\0')
  360.       {
  361.          if(cKeyPress == 'q' || cKeyPress == 'Q')
  362.          {
  363.             /* Move left */
  364.             --nPlayerPos;
  365.          }
  366.          else if(cKeyPress == 'w' || cKeyPress == 'W')
  367.          {
  368.             /* Move right */
  369.             ++nPlayerPos;
  370.          }
  371.       }
  372.  
  373.       /* Check whether course should turn */
  374.       if((rand() % 100) < CHANGE_DIRECTION)
  375.       {
  376.          bMovingRight = !bMovingRight;
  377.       }
  378.       else
  379.       {
  380.          /* If no change in direction, then position moves */
  381.          /* Adjust course position appropriately */
  382.          if(bMovingRight)
  383.          {
  384.             ++nLeftEdge;
  385.             ++nRightEdge;
  386.          }
  387.          else
  388.          {
  389.             --nLeftEdge;
  390.             --nRightEdge;
  391.          }
  392.       }
  393.  
  394.       /* Check whether course size should shink */
  395.       if(++nDistanceSinceShrink >= DECREASE_WIDTH_AFTER)
  396.       {
  397.          /* Reset distance */
  398.          nDistanceSinceShrink = 0;
  399.  
  400.          /* Randomly choose a side to shrink */
  401.          if((rand() % 100) < 50)
  402.          {
  403.             ++nLeftEdge;
  404.          }
  405.          else
  406.          {
  407.             --nRightEdge;
  408.          }
  409.       }
  410.  
  411.       /* Change course direction if it collides with edge of screen */
  412.       if(nLeftEdge < 1)
  413.       {
  414.          bMovingRight = TRUE;
  415.          ++nLeftEdge;
  416.          ++nRightEdge;
  417.       }
  418.       else if(nRightEdge > 79)
  419.       {
  420.          bMovingRight = FALSE;
  421.          --nLeftEdge;
  422.          --nRightEdge;
  423.       }
  424.  
  425.       /* Check that player is still within the course */
  426.       if(nPlayerPos <= nLeftEdge || nPlayerPos >= nRightEdge)
  427.       {
  428.          /* Player has left course - game over! */
  429.          od_set_color(D_GREY, B_BLACK);
  430.          od_clr_scr();
  431.          od_printf("`flashing bright red`       !!! Game Over !!!\n\r\n\r");
  432.          od_printf("`dark green`You have veered off the course!\n\r\n\r");
  433.          od_printf("Your Score is: %ld\n\r", lnScore);
  434.  
  435.          /* Create a score record */
  436.          ScoreRecord.lnHighScore = lnScore;
  437.          strncpy(ScoreRecord.szPlayerName, od_control.user_name, MAX_NAME_SIZE);
  438.          ScoreRecord.szPlayerName[MAX_NAME_SIZE] = '\0';
  439.          ScoreRecord.lnPlayDate = time(NULL);
  440.  
  441.          /* Attempt to read high scores from file */
  442.          pfFile = OpenAndReadHighScores(&HighScores);
  443.  
  444.          if(pfFile == NULL)
  445.          {
  446.             /* If unable to open high score file, display an error message */
  447.             od_printf("`bright red`Unable to access high score file!\n\r");
  448.          }
  449.          else
  450.          {
  451.             /* Check whether user made it to high score list */
  452.             if(AddHighScore(&HighScores, &ScoreRecord))
  453.             {
  454.                od_printf("Congratulations! You have made it to the high score list!\n\r");
  455.                /* If so, write the new high score list */
  456.                WriteHighScores(pfFile, &HighScores);
  457.             }
  458.  
  459.             /* Close and unlock file */
  460.             CloseHighScores(pfFile);
  461.          }
  462.  
  463.          /* Wait for user to press enter */
  464.          od_printf("`bright white`\n\rPress [ENTER]/[RETURN] to return to menu: ");
  465.          od_get_answer("\n\r");
  466.  
  467.          return;
  468.       }
  469.  
  470.       /* Wait for tick time to elapse */
  471.       WaitForTimerToElapse(lnTimerStart, TICK_DELAY);
  472.  
  473.       /* Increase score */
  474.       ++lnScore;
  475.  
  476.       /* Replace skiier character with track character */
  477.       if(od_control.user_ansi)
  478.       {
  479.          MoveLeft(nBackup);
  480.          od_set_color(L_WHITE, D_GREY);
  481.          od_putch(178);
  482.          od_set_color(L_WHITE, B_BLACK);
  483.       }
  484.  
  485.       /* Move to next line */
  486.       od_printf("\r\n");
  487.    }
  488. }
  489.  
  490.  
  491. /* SpaceRight() - Moves right the specified number of columns. In ANSI mode, */
  492. /*                uses the move cursor right control sequence. Otherwise,    */
  493. /*                uses od_repeat(), which is optimized for ASCII and AVATAR  */
  494. /*                modes.                                                     */
  495. void SpaceRight(int nColumns)
  496. {
  497.    char szSequence[6];
  498.  
  499.    /* If we don't have a positive column count, then return immediately */
  500.    if(nColumns <= 0) return;
  501.  
  502.    /* If operating in ANSI mode */
  503.    if(od_control.user_ansi)
  504.    {
  505.       /* Move cursor right using ESC[nC control sequence */
  506.       sprintf(szSequence, "\x1b[%02.2dC", nColumns);
  507.       od_disp_emu(szSequence, TRUE);
  508.    }
  509.  
  510.    /* If not operating in ANSI mode */
  511.    else
  512.    {
  513.       od_repeat(' ', (unsigned char)nColumns);
  514.    }
  515. }
  516.  
  517.  
  518. /* MoveLeft() - Moves the cursor right the specified number of columns. */
  519. /*              Intended for use in ANSI mode only.                     */
  520. void MoveLeft(int nColumns)
  521. {
  522.    /* Move cursor left using ESC[nD control sequence */
  523.    char szSequence[6];
  524.    sprintf(szSequence, "\x1b[%02.2dD", nColumns);
  525.    od_disp_emu(szSequence, TRUE);
  526. }
  527.  
  528.  
  529. /* AddHighScore() - Adds a new score to the high score list, if it is high   */
  530. /*                  enough. Returns TRUE if score is added, FALSE otherwise. */
  531. int AddHighScore(tHighScoreFile *pHighScores, tHighScoreRecord *pScoreRecord)
  532. {
  533.    int iHighScore;
  534.    int iExistingScore;
  535.  
  536.    /* Loop through each existing high score */
  537.    for(iHighScore = 0; iHighScore < HIGH_SCORES; ++iHighScore)
  538.    {
  539.       /* If new score is greater than or equal to this one, then its */
  540.       /* position has been found.                                    */
  541.       if(pHighScores->aRecord[iHighScore].lnHighScore <=
  542.          pScoreRecord->lnHighScore)
  543.       {
  544.          /* Move remaining scores down one in list */
  545.          for(iExistingScore = HIGH_SCORES - 1; iExistingScore >= iHighScore + 1;
  546.           --iExistingScore)
  547.          {
  548.             pHighScores->aRecord[iExistingScore] =
  549.                pHighScores->aRecord[iExistingScore - 1];
  550.          }
  551.  
  552.          /* Add new score to list */
  553.          pHighScores->aRecord[iHighScore] = *pScoreRecord;
  554.  
  555.          /* Return with success */
  556.          return(TRUE);
  557.       }
  558.    }
  559.  
  560.    /* Score did not make it to list */
  561.    return(FALSE);
  562. }
  563.  
  564.  
  565. long StartTimer(void)
  566. {
  567.    return(*(long far *)0x46cL);
  568. }
  569.  
  570.  
  571. void WaitForTimerToElapse(long lnStartValue, long lnDuration)
  572. {
  573.    while(*(long far *)0x46cL >= lnStartValue &&
  574.          lnStartValue + lnDuration >= *(long far *)0x46cL)
  575.    {
  576.       od_kernal();
  577.    }
  578. }
  579.