Subversion Repositories Games.Rick Dangerous

Rev

Rev 9 | Rev 11 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. /*
  2.  * src/game.c
  3.  *
  4.  * Copyright (C) 1998-2002 BigOrno (bigorno@bigorno.net). All rights reserved.
  5.  *
  6.  * The use and distribution terms for this software are contained in the file
  7.  * named README, which can be found in the root of this distribution. By
  8.  * using this software in any fashion, you are agreeing to be bound by the
  9.  * terms of this license.
  10.  *
  11.  * You must not remove this notice, or any other, from this software.
  12.  */
  13.  
  14. #include <stdio.h>
  15. #include <stdlib.h>
  16.  
  17. #include "system.h"
  18. #include "game.h"
  19.  
  20. #include "draw.h"
  21. #include "maps.h"
  22. #include "ents.h"
  23. #include "e_rick.h"
  24. #include "e_sbonus.h"
  25. #include "e_them.h"
  26. #include "screens.h"
  27. #include "rects.h"
  28. #include "scroller.h"
  29. #include "control.h"
  30.  
  31.  
  32. /*
  33.  * local typedefs
  34.  */
  35. typedef enum
  36. {
  37.    INIT_GAME, INIT_BUFFER,
  38.    INTRO_MAIN, INTRO_MAP,
  39.    PAUSE_PRESSED1, PAUSE_PRESSED1B, PAUSED, PAUSE_PRESSED2,
  40.    PLAY0, PLAY1, PLAY2, PLAY3,
  41.    CHAIN_SUBMAP, CHAIN_MAP, CHAIN_END,
  42.    SCROLL_UP, SCROLL_DOWN,
  43.    RESTART, GAMEOVER, GETNAME, EXIT
  44. } game_state_t;
  45.  
  46.  
  47. /*
  48.  * global vars
  49.  */
  50. U8 game_period = 0;
  51. U8 game_waitevt = FALSE;
  52. rect_t *game_rects = NULL;
  53.  
  54. U8 game_lives = 0;
  55. U8 game_bombs = 0;
  56. U8 game_bullets = 0;
  57. U32 game_score = 0;
  58.  
  59. U16 game_map = 0;
  60. U16 game_submap = 0;
  61.  
  62. U8 game_dir = 0;
  63. U8 game_chsm = FALSE;
  64.  
  65. U8 want_infinitelives = FALSE;
  66. U8 enable_endkey = TRUE;
  67.  
  68.  
  69. hscore_t game_hscores[8] =
  70. {
  71.    { 8000, "DANGERSTU@" },
  72.    { 7000, "SIMES@@@@@" },
  73.    { 6000, "KEN@T@ZEN@" },
  74.    { 5000, "BOBBLE@@@@" },
  75.    { 4000, "GREG@LAA@@" },
  76.    { 3000, "TELLY@@@@@" },
  77.    { 2000, "CHIGLET@@@" },
  78.    { 1000, "ANDYSPLEEN" }
  79. };
  80.  
  81. sound_t *WAV_GAMEOVER;
  82. sound_t *WAV_SBONUS2;
  83. sound_t *WAV_BULLET;
  84. sound_t *WAV_BOMBSHHT;
  85. sound_t *WAV_EXPLODE;
  86. sound_t *WAV_STICK;
  87. sound_t *WAV_WALK;
  88. sound_t *WAV_CRAWL;
  89. sound_t *WAV_JUMP;
  90. sound_t *WAV_PAD;
  91. sound_t *WAV_BOX;
  92. sound_t *WAV_BONUS;
  93. sound_t *WAV_SBONUS1;
  94. sound_t *WAV_DIE;
  95. sound_t *WAV_ENTITY[10];
  96.  
  97.  
  98. /*
  99.  * local vars
  100.  */
  101. static U8 isave_frow;
  102. static game_state_t game_state;
  103. static sound_t *music_snd;
  104.  
  105.  
  106. /*
  107.  * prototypes
  108.  */
  109. static void frame (void);
  110. static void init (void);
  111. static void play0 (void);
  112. static void play3 (void);
  113. static void restart (void);
  114. static void isave (void);
  115. static void irestore (void);
  116. static void loaddata (void);
  117. static void freedata (void);
  118.  
  119.  
  120. /*
  121.  * Music
  122.  */
  123. void game_setmusic (char *name, U8 loop)
  124. {
  125.    U8 channel;
  126.  
  127.    if (music_snd)
  128.       game_stopmusic ();
  129.  
  130.    music_snd = syssnd_load (name);
  131.  
  132.    if (music_snd)
  133.    {
  134.       music_snd->dispose = TRUE; /* music is always "fire and forget" */
  135.       channel = syssnd_play(music_snd, loop);
  136.    }
  137. }
  138.  
  139.  
  140. void game_stopmusic (void)
  141. {
  142.    syssnd_stopsound (music_snd);
  143.    music_snd = NULL;
  144. }
  145.  
  146.  
  147. /*
  148.  * Main loop
  149.  */
  150. void game_run (void)
  151. {
  152.    U32 tm, tmx;
  153.  
  154.    loaddata (); /* load cached data */
  155.  
  156.    tm = sys_gettime ();
  157.    game_state = INIT_GAME;
  158.  
  159.    /* main loop */
  160.    while (game_state != EXIT)
  161.    {
  162.       // Pierre-Marie Baty -- intro runs faster
  163.       if (game_state != INTRO_MAP)
  164.          game_period = (sysarg_args_period ? sysarg_args_period : GAME_PERIOD);
  165.       else
  166.          game_period = (sysarg_args_period ? sysarg_args_period : GAME_PERIOD) / 2;
  167.  
  168.       /* timer */
  169.       tmx = tm; tm = sys_gettime (); tmx = tm - tmx;
  170.       if (tmx < game_period)
  171.          sys_sleep (game_period - tmx);
  172.  
  173.       /* video */
  174.       /*DEBUG*//*game_rects=&draw_SCREENRECT;*//*DEBUG*/
  175.       sysvid_paint (game_rects);
  176.       draw_STATUSRECT.next = NULL; /* FIXME freerects should handle this */
  177.  
  178.       /* sound */
  179.       /*snd_mix ();*/
  180.  
  181.       /* events */
  182.       if (game_waitevt)
  183.          sysevt_wait (); /* wait for an event */
  184.       else
  185.          sysevt_poll (); /* process events (non-blocking) */
  186.  
  187.       /* frame */
  188.       frame ();
  189.    }
  190.  
  191.    freedata (); /* free cached data */
  192. }
  193.  
  194.  
  195. // Pierre-Marie Baty -- addition: sort high scores
  196. int sort_hscores (const void *q1, const void *q2)
  197. {
  198.    return (((hscore_t *) q1)->score < ((hscore_t *) q2)->score);
  199. }
  200.  
  201.  
  202. /*
  203.  * Prepare frame
  204.  *
  205.  * This function loops forever: use 'return' when a frame is ready.
  206.  * When returning, game_rects must contain every parts of the buffer
  207.  * that have been modified.
  208.  */
  209. static void frame (void)
  210. {
  211.    while (1)
  212.    {
  213.  
  214.       switch (game_state)
  215.       {
  216.  
  217.  
  218.       case INIT_GAME:
  219.          init ();
  220.          game_state = INTRO_MAIN;
  221.          break;
  222.  
  223.  
  224.  
  225.       case INTRO_MAIN:
  226.          switch (screen_introMain ())
  227.          {
  228.          case SCREEN_RUNNING:
  229.             return;
  230.          case SCREEN_DONE:
  231.             game_state = INTRO_MAP;
  232.             break;
  233.          case SCREEN_EXIT:
  234.             game_state = EXIT;
  235.             return;
  236.          }
  237.       break;
  238.  
  239.  
  240.  
  241.       case INTRO_MAP:
  242.          switch (screen_introMap ())
  243.          {
  244.          case SCREEN_RUNNING:
  245.             return;
  246.          case SCREEN_DONE:
  247.             game_waitevt = FALSE;
  248.             game_state = INIT_BUFFER;
  249.             break;
  250.          case SCREEN_EXIT:
  251.             game_state = EXIT;
  252.             return;
  253.          }
  254.       break;
  255.  
  256.  
  257.  
  258.       case INIT_BUFFER:
  259.          syssnd_pause (FALSE, TRUE); // reset sounds
  260.          sysvid_clear (); /* clear buffer */
  261.          draw_map (); /* draw the map onto the buffer */
  262.          draw_drawStatus (); /* draw the status bar onto the buffer */
  263.          game_rects = &draw_SCREENRECT; /* request full buffer refresh */
  264.          game_state = PLAY0;
  265.          return;
  266.  
  267.  
  268.  
  269.       case PAUSE_PRESSED1:
  270.          screen_pause (TRUE);
  271.          game_state = PAUSE_PRESSED1B;
  272.          break;
  273.  
  274.  
  275.  
  276.       case PAUSE_PRESSED1B:
  277.          if (control_status & CONTROL_PAUSE)
  278.             return;
  279.          game_state = PAUSED;
  280.          break;
  281.  
  282.  
  283.  
  284.       case PAUSED:
  285.          if (control_status & CONTROL_PAUSE)
  286.             game_state = PAUSE_PRESSED2;
  287.          if (control_status & CONTROL_EXIT)
  288.             game_state = EXIT;
  289.          return;
  290.  
  291.  
  292.  
  293.       case PAUSE_PRESSED2:
  294.          if (!(control_status & CONTROL_PAUSE))
  295.          {
  296.             game_waitevt = FALSE;
  297.             screen_pause (FALSE);
  298.             syssnd_pause (FALSE, FALSE);
  299.             game_state = PLAY2;
  300.          }
  301.       return;
  302.  
  303.  
  304.  
  305.       case PLAY0:
  306.          play0 ();
  307.          break;
  308.  
  309.  
  310.  
  311.       case PLAY1:
  312.          if (control_status & CONTROL_PAUSE)
  313.          {
  314.             syssnd_pause (TRUE, FALSE);
  315.             game_waitevt = TRUE;
  316.             game_state = PAUSE_PRESSED1;
  317.          }
  318.          else
  319.             game_state = PLAY2;
  320.          break;
  321.  
  322.  
  323.  
  324.       case PLAY2:
  325.          if (E_RICK_STTST (E_RICK_STDEAD))
  326.          {
  327.             /* rick is dead */
  328.             if (--game_lives)
  329.             {
  330.                game_state = RESTART;
  331.                if (want_infinitelives)
  332.                   game_lives = 6;
  333.             }
  334.             else
  335.                game_state = GAMEOVER;
  336.          }
  337.          else if (game_chsm)
  338.             game_state = CHAIN_SUBMAP; // request to chain to next submap
  339.          else
  340.             game_state = PLAY3;
  341.          break;
  342.  
  343.  
  344.  
  345.       case PLAY3:
  346.          play3 ();
  347.          return;
  348.  
  349.  
  350.  
  351.       case CHAIN_SUBMAP:
  352.          if (map_chain ())
  353.             game_state = CHAIN_END;
  354.          else
  355.          {
  356.             game_bullets = 0x06;
  357.             game_bombs = 0x06;
  358.             game_map++;
  359.  
  360.             if (game_map == 0x04)
  361.             {
  362.                /* reached end of game */
  363.                /* FIXME @292?*/
  364.             }
  365.  
  366.             game_state = CHAIN_MAP;
  367.          }
  368.          break;
  369.  
  370.  
  371.  
  372.       case CHAIN_MAP:/* CHAIN MAP */
  373.          switch (screen_introMap ())
  374.          {
  375.          case SCREEN_RUNNING:
  376.             return;
  377.          case SCREEN_DONE:
  378.             if (game_map >= 0x04)
  379.             {
  380.                /* reached end of game */
  381.                sysarg_args_map = 0;
  382.                sysarg_args_submap = 0;
  383.                sysarg_args_score = 0;
  384.                game_state = GAMEOVER;
  385.             }
  386.             else
  387.             {
  388.                /* initialize game */
  389.                ent_ents[1].x = map_maps[game_map].x;
  390.                ent_ents[1].y = map_maps[game_map].y;
  391.                map_frow = (U8) map_maps[game_map].row;
  392.                game_submap = map_maps[game_map].submap;
  393.                game_state = CHAIN_END;
  394.             }
  395.             break;
  396.                case SCREEN_EXIT:
  397.             game_state = EXIT;
  398.             return;
  399.          }
  400.          break;
  401.  
  402.  
  403.  
  404.       case CHAIN_END:
  405.          syssnd_pause (FALSE, TRUE); // reset sounds
  406.          map_init (); /* initialize the map */
  407.          isave (); /* save data in case of a restart */
  408.          ent_clprev (); /* cleanup entities */
  409.          draw_map (); /* draw the map onto the buffer */
  410.          draw_drawStatus (); /* draw the status bar onto the buffer */
  411.          draw_STATUSRECT.next = NULL;
  412.          game_state = PLAY3;
  413.          frame (); // Pierre-Marie Baty -- patch by Jason Andersen: skip refreshing the screen for 2 pumps
  414.          frame (); // to allow the sprites to settle into the appropriate frame
  415.          game_rects = &draw_SCREENRECT;  /* request full screen refresh */
  416.          return;
  417.  
  418.  
  419.  
  420.       case SCROLL_UP:
  421.          switch (scroll_up ())
  422.          {
  423.          case SCROLL_RUNNING:
  424.             return;
  425.          case SCROLL_DONE:
  426.             game_state = PLAY0;
  427.             break;
  428.          }
  429.          break;
  430.  
  431.  
  432.  
  433.       case SCROLL_DOWN:
  434.          switch (scroll_down ())
  435.          {
  436.          case SCROLL_RUNNING:
  437.             return;
  438.          case SCROLL_DONE:
  439.             game_state = PLAY0;
  440.             break;
  441.          }
  442.          break;
  443.  
  444.  
  445.  
  446.       case RESTART:
  447.          restart ();
  448.          game_state = PLAY0;
  449.          return;
  450.  
  451.  
  452.  
  453.       case GAMEOVER:
  454.          switch (screen_gameover ())
  455.          {
  456.          case SCREEN_RUNNING:
  457.             return;
  458.          case SCREEN_DONE:
  459.             game_state = GETNAME;
  460.             break;
  461.          case SCREEN_EXIT:
  462.             game_state = EXIT;
  463.             break;
  464.          }
  465.          break;
  466.  
  467.  
  468.  
  469.       case GETNAME:
  470.          switch (screen_getname ()) {
  471.          case SCREEN_RUNNING:
  472.             return;
  473.          case SCREEN_DONE:
  474.             game_state = INIT_GAME;
  475.             return;
  476.          case SCREEN_EXIT:
  477.             game_state = EXIT;
  478.             break;
  479.          }
  480.          break;
  481.  
  482.  
  483.  
  484.       case EXIT:
  485.          return;
  486.  
  487.       }
  488.    }
  489. }
  490.  
  491.  
  492. /*
  493.  * Initialize the game
  494.  */
  495. static void init (void)
  496. {
  497.    U8 i;
  498.  
  499.    E_RICK_STRST (0xff);
  500.  
  501.    game_lives = 6;
  502.    game_bombs = 6;
  503.    game_bullets = 6;
  504.    game_score = 0;
  505.  
  506.    game_map = sysarg_args_map;
  507.  
  508.    if (sysarg_args_submap == 0)
  509.    {
  510.       game_submap = map_maps[game_map].submap;
  511.       map_frow = (U8)map_maps[game_map].row;
  512.    }
  513.    else
  514.    {
  515.       /* dirty hack to determine frow */
  516.       game_submap = sysarg_args_submap;
  517.       i = 0;
  518.       while (i < MAP_NBR_CONNECT && (map_connect[i].submap != game_submap || map_connect[i].dir != RIGHT))
  519.          i++;
  520.       map_frow = map_connect[i].rowin - 0x10;
  521.       ent_ents[1].y = 0x10 << 3;
  522.    }
  523.  
  524.    // Pierre-Marie Baty -- addition: start score
  525.    if (sysarg_args_score != 0)
  526.       game_score = sysarg_args_score + 1;
  527.  
  528.    ent_ents[1].x = map_maps[game_map].x;
  529.    ent_ents[1].y = map_maps[game_map].y;
  530.    ent_ents[1].w = 0x18;
  531.    ent_ents[1].h = 0x15;
  532.    ent_ents[1].n = 0x01;
  533.    ent_ents[1].sprite = 0x01;
  534.    ent_ents[1].front = FALSE;
  535.    ent_ents[ENT_ENTSNUM].n = 0xFF;
  536.  
  537.    map_resetMarks ();
  538.    map_init ();
  539.    isave ();
  540. }
  541.  
  542.  
  543. /*
  544.  * play0
  545.  *
  546.  */
  547. static void play0 (void)
  548. {
  549.    if (control_status & CONTROL_END)
  550.    {
  551.       /* request to end the game */
  552.       game_state = GAMEOVER;
  553.       return;
  554.    }
  555.  
  556.    if (control_last == CONTROL_EXIT)
  557.    {
  558.       /* request to exit the game */
  559.       game_state = EXIT;
  560.       return;
  561.    }
  562.  
  563.    ent_action (); /* run entities */
  564.    e_them_rndseed++; /* (0270) */
  565.  
  566.    game_state = PLAY1;
  567. }
  568.  
  569.  
  570. /*
  571.  * play3
  572.  *
  573.  */
  574. static void play3 (void)
  575. {
  576.    static rect_t *r;
  577.  
  578.    ent_draw (); /* draw all entities onto the buffer */
  579.  
  580.    /* sound */
  581.    draw_drawStatus (); /* draw the status bar onto the buffer*/
  582.  
  583.    r = &draw_STATUSRECT;
  584.    r->next = ent_rects; /* refresh status bar too */
  585.    game_rects = r; /* take care to cleanup draw_STATUSRECT->next later! */
  586.  
  587.    if (!E_RICK_STTST (E_RICK_STZOMBIE))
  588.    {
  589.       /* need to scroll ? */
  590.       if (ent_ents[1].y >= 200 + 4)
  591.       {
  592.          game_state = SCROLL_UP;
  593.          return;
  594.       }
  595.       if (ent_ents[1].y <= 100 - 4)
  596.       {
  597.          game_state = SCROLL_DOWN;
  598.          return;
  599.       }
  600.    }
  601.  
  602.    game_state = PLAY0;
  603. }
  604.  
  605.  
  606. /*
  607.  * restart
  608.  *
  609.  */
  610. static void restart (void)
  611. {
  612.    E_RICK_STRST (/*E_RICK_STDEAD | E_RICK_STZOMBIE*/ 0xff); // Pierre-Marie Baty -- correct sprite position on restart
  613.  
  614.    game_bullets = 6;
  615.    game_bombs = 6;
  616.  
  617.    ent_ents[1].n = 1;
  618.  
  619.    irestore ();
  620.    map_init ();
  621.    isave ();
  622.    ent_clprev ();
  623.    draw_map ();
  624.    draw_drawStatus ();
  625.    game_rects = &draw_SCREENRECT;
  626. }
  627.  
  628.  
  629. /*
  630.  * isave (0bbb)
  631.  *
  632.  */
  633. static void isave (void)
  634. {
  635.    e_rick_save ();
  636.    isave_frow = map_frow;
  637. }
  638.  
  639.  
  640. /*
  641.  * irestore (0bdc)
  642.  *
  643.  */
  644. static void irestore (void)
  645. {
  646.    e_rick_restore ();
  647.    map_frow = isave_frow;
  648. }
  649.  
  650.  
  651. /*
  652.  *
  653.  */
  654. static void loaddata (void)
  655. {
  656.    /*
  657.     * Cache sounds
  658.     *
  659.     * tune[0-5].wav not cached
  660.     */
  661.    WAV_GAMEOVER = syssnd_load ("sounds/gameover.wav");
  662.    WAV_SBONUS2 = syssnd_load ("sounds/sbonus2.wav");
  663.    WAV_BULLET = syssnd_load ("sounds/bullet.wav");
  664.    WAV_BOMBSHHT = syssnd_load ("sounds/bombshht.wav");
  665.    WAV_EXPLODE = syssnd_load ("sounds/explode.wav");
  666.    WAV_STICK = syssnd_load ("sounds/stick.wav");
  667.    WAV_WALK = syssnd_load ("sounds/walk.wav");
  668.    WAV_CRAWL = syssnd_load ("sounds/crawl.wav");
  669.    WAV_JUMP = syssnd_load ("sounds/jump.wav");
  670.    WAV_PAD = syssnd_load ("sounds/pad.wav");
  671.    WAV_BOX = syssnd_load ("sounds/box.wav");
  672.    WAV_BONUS = syssnd_load ("sounds/bonus.wav");
  673.    WAV_SBONUS1 = syssnd_load ("sounds/sbonus1.wav");
  674.    WAV_DIE = syssnd_load ("sounds/die.wav");
  675.    WAV_ENTITY[0] = syssnd_load ("sounds/ent0.wav");
  676.    WAV_ENTITY[1] = syssnd_load ("sounds/ent1.wav");
  677.    WAV_ENTITY[2] = syssnd_load ("sounds/ent2.wav");
  678.    WAV_ENTITY[3] = syssnd_load ("sounds/ent3.wav");
  679.    WAV_ENTITY[4] = syssnd_load ("sounds/ent4.wav");
  680.    WAV_ENTITY[5] = syssnd_load ("sounds/ent5.wav");
  681.    WAV_ENTITY[6] = syssnd_load ("sounds/ent6.wav");
  682.    WAV_ENTITY[7] = syssnd_load ("sounds/ent7.wav");
  683.    WAV_ENTITY[8] = syssnd_load ("sounds/ent8.wav");
  684.  
  685.    // Pierre-Marie Baty -- addition: load high scores
  686.    {
  687.       char hiscorefile_fullpathname[1024];
  688.       char linebuf[256];
  689.       hscore_t *hscore;
  690.       U32 score;
  691.       char *ptr;
  692.       size_t i, j;
  693.       FILE *fp;
  694.       sprintf_s (hiscorefile_fullpathname, 1024, "%s/%s", sys_getbasepath (), "hiscores.txt");
  695.       fopen_s (&fp, hiscorefile_fullpathname, "rb");
  696.       if (fp != NULL)
  697.       {
  698.          for (i = 0; (fgets (linebuf, sizeof (linebuf), fp) != NULL) && (i < 8);)
  699.          {
  700.             score = strtol (linebuf, &ptr, 10);
  701.             if (ptr <= linebuf)
  702.                continue;
  703.             while ((*ptr == '\t') || (*ptr == ' '))
  704.                ptr++;
  705.             hscore = &game_hscores[i++];
  706.             hscore->score = score;
  707.             for (j = 0; j < sizeof (hscore->name); j++)
  708.                hscore->name[j] = '@';
  709.             for (j = 0; j < sizeof (hscore->name); j++, ptr++)
  710.                hscore->name[j] = (((*ptr >= 'A') && (*ptr <= 'Z')) || (*ptr == '.') ? *ptr : '@');
  711.          }
  712.          qsort (game_hscores, 8, sizeof (hscore_t), sort_hscores);
  713.          fclose (fp);
  714.       }
  715.    }
  716. }
  717.  
  718.  
  719. /*
  720.  *
  721.  */
  722. static void freedata (void)
  723. {
  724.    syssnd_stopall ();
  725.    syssnd_free (WAV_GAMEOVER);
  726.    syssnd_free (WAV_SBONUS2);
  727.    syssnd_free (WAV_BULLET);
  728.    syssnd_free (WAV_BOMBSHHT);
  729.    syssnd_free (WAV_EXPLODE);
  730.    syssnd_free (WAV_STICK);
  731.    syssnd_free (WAV_WALK);
  732.    syssnd_free (WAV_CRAWL);
  733.    syssnd_free (WAV_JUMP);
  734.    syssnd_free (WAV_PAD);
  735.    syssnd_free (WAV_BOX);
  736.    syssnd_free (WAV_BONUS);
  737.    syssnd_free (WAV_SBONUS1);
  738.    syssnd_free (WAV_DIE);
  739.    syssnd_free (WAV_ENTITY[0]);
  740.    syssnd_free (WAV_ENTITY[1]);
  741.    syssnd_free (WAV_ENTITY[2]);
  742.    syssnd_free (WAV_ENTITY[3]);
  743.    syssnd_free (WAV_ENTITY[4]);
  744.    syssnd_free (WAV_ENTITY[5]);
  745.    syssnd_free (WAV_ENTITY[6]);
  746.    syssnd_free (WAV_ENTITY[7]);
  747.    syssnd_free (WAV_ENTITY[8]);
  748.  
  749.    // Pierre-Marie Baty -- addition: save high scores
  750.    {
  751.       char hiscorefile_fullpathname[1024];
  752.       size_t i, j;
  753.       FILE *fp;
  754.       sprintf_s (hiscorefile_fullpathname, 1024, "%s/%s", sys_getbasepath (), "hiscores.txt");
  755.       fopen_s (&fp, hiscorefile_fullpathname, "wb");
  756.       if (fp != NULL)
  757.       {
  758.          for (i = 0; i < sizeof (game_hscores) / sizeof (game_hscores[0]); i++)
  759.          {
  760.             fprintf (fp, "%d\t", game_hscores[i].score);
  761.             for (j = 0; j < sizeof (game_hscores[i].name); j++)
  762.                fputc ((game_hscores[i].name[j] != '@' ? game_hscores[i].name[j] : ' '), fp);
  763.             fputc ('\n', fp);
  764.          }
  765.          fclose (fp);
  766.       }
  767.    }
  768. }
  769.