added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / game_mm / mm_files.c
index 0b88735ec920e8bcac421222359fdc7bf4882ec6..4a85360925f4b409d969d114a3fca4718a6195ce 100644 (file)
@@ -4,7 +4,7 @@
 // (c) 1994-2017 by Artsoft Entertainment
 //                         Holger Schemel
 //                 info@artsoft.org
-//                 http://www.artsoft.org/
+//                 https://www.artsoft.org/
 // ----------------------------------------------------------------------------
 // mm_files.c
 // ============================================================================
 
 #include "mm_main.h"
 
-#define CHUNK_ID_LEN           4       /* IFF style chunk id length */
-#define CHUNK_SIZE_UNDEFINED   0       /* undefined chunk size == 0  */
-#define CHUNK_SIZE_NONE                -1      /* do not write chunk size    */
-#define FILE_VERS_CHUNK_SIZE   8       /* size of file version chunk */
-#define LEVEL_HEADER_SIZE      80      /* size of level file header */
-#define LEVEL_HEADER_UNUSED    19      /* unused level header bytes */
+#define CHUNK_ID_LEN           4       // IFF style chunk id length
+#define CHUNK_SIZE_UNDEFINED   0       // undefined chunk size == 0
+#define CHUNK_SIZE_NONE                -1      // do not write chunk size
+#define FILE_VERS_CHUNK_SIZE   8       // size of file version chunk
+#define LEVEL_HEADER_SIZE      80      // size of level file header
+#define LEVEL_HEADER_UNUSED    19      // unused level header bytes
 
-/* file identifier strings */
+// file identifier strings
 #define LEVEL_COOKIE_TMPL      "MIRRORMAGIC_LEVEL_FILE_VERSION_x.x"
 #define SCORE_COOKIE           "MIRRORMAGIC_SCORE_FILE_VERSION_1.4"
 
@@ -40,9 +40,9 @@ int default_score[LEVEL_SCORE_ELEMENTS] =
 };
 
 
-/* ========================================================================= */
-/* level file functions                                                      */
-/* ========================================================================= */
+// ============================================================================
+// level file functions
+// ============================================================================
 
 static void ReadChunk_MM_VERS(File *file, int *file_version, int *game_version)
 {
@@ -52,12 +52,12 @@ static void ReadChunk_MM_VERS(File *file, int *file_version, int *game_version)
   file_version_major = getFile8Bit(file);
   file_version_minor = getFile8Bit(file);
   file_version_patch = getFile8Bit(file);
-  getFile8Bit(file);           /* not used */
+  getFile8Bit(file);           // not used
 
   game_version_major = getFile8Bit(file);
   game_version_minor = getFile8Bit(file);
   game_version_patch = getFile8Bit(file);
-  getFile8Bit(file);           /* not used */
+  getFile8Bit(file);           // not used
 
   *file_version = MM_VERSION_IDENT(file_version_major,
                                   file_version_minor,
@@ -80,38 +80,44 @@ static void WriteChunk_MM_VERS(FILE *file, int file_version, int game_version)
   fputc(file_version_major, file);
   fputc(file_version_minor, file);
   fputc(file_version_patch, file);
-  fputc(0, file);      /* not used */
+  fputc(0, file);      // not used
 
   fputc(game_version_major, file);
   fputc(game_version_minor, file);
   fputc(game_version_patch, file);
-  fputc(0, file);      /* not used */
+  fputc(0, file);      // not used
 }
 
-void setLevelInfoToDefaults_MM()
+void setLevelInfoToDefaults_MM(void)
 {
   int i, x, y;
 
   native_mm_level.file_version = MM_FILE_VERSION_ACTUAL;
   native_mm_level.game_version = MM_GAME_VERSION_ACTUAL;
 
-  native_mm_level.encoding_16bit_field = FALSE;        /* default: only 8-bit elements */
+  native_mm_level.encoding_16bit_field = FALSE;        // default: only 8-bit elements
 
-  lev_fieldx = native_mm_level.fieldx = STD_LEV_FIELDX;
-  lev_fieldy = native_mm_level.fieldy = STD_LEV_FIELDY;
+  native_mm_level.fieldx = STD_LEV_FIELDX;
+  native_mm_level.fieldy = STD_LEV_FIELDY;
 
   for (x = 0; x < MAX_LEV_FIELDX; x++)
     for (y = 0; y < MAX_LEV_FIELDY; y++)
-      native_mm_level.field[x][y] = Feld[x][y] = Ur[x][y] = EL_EMPTY;
+      native_mm_level.field[x][y] = Ur[x][y] = EL_EMPTY;
 
   native_mm_level.time = 100;
   native_mm_level.kettles_needed = 0;
   native_mm_level.auto_count_kettles = TRUE;
   native_mm_level.amoeba_speed = 0;
-  native_mm_level.time_fuse = 0;
-  native_mm_level.laser_red = FALSE;
-  native_mm_level.laser_green = FALSE;
-  native_mm_level.laser_blue = TRUE;
+  native_mm_level.time_fuse = 25;
+  native_mm_level.time_bomb = 75;
+  native_mm_level.time_ball = 75;
+  native_mm_level.time_block = 75;
+  native_mm_level.mm_laser_red = FALSE;
+  native_mm_level.mm_laser_green = FALSE;
+  native_mm_level.mm_laser_blue = TRUE;
+  native_mm_level.df_laser_red = TRUE;
+  native_mm_level.df_laser_green = TRUE;
+  native_mm_level.df_laser_blue = FALSE;
 
   for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
     native_mm_level.name[i] = '\0';
@@ -124,9 +130,27 @@ void setLevelInfoToDefaults_MM()
   for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
     native_mm_level.score[i] = 10;
 
-  native_mm_level.field[0][0] = Feld[0][0] = Ur[0][0] = EL_MCDUFFIN_RIGHT;
+  int ball_content[] =
+  {
+    EL_MIRROR_START,
+    EL_MIRROR_FIXED_START,
+    EL_POLAR_START,
+    EL_POLAR_CROSS_START,
+    EL_PACMAN_START,
+    EL_KETTLE,
+    EL_BOMB,
+    EL_PRISM
+  };
+  int num_ball_contents = sizeof(ball_content) / sizeof(int);
+
+  native_mm_level.num_ball_contents = num_ball_contents;
+  native_mm_level.ball_choice_mode = ANIM_RANDOM;
+
+  for (i = 0; i < num_ball_contents; i++)
+    native_mm_level.ball_content[i] = ball_content[i];
+
+  native_mm_level.field[0][0] = Ur[0][0] = EL_MCDUFFIN_RIGHT;
   native_mm_level.field[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
-    Feld[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] =
     Ur[STD_LEV_FIELDX-1][STD_LEV_FIELDY-1] = EL_EXIT_CLOSED;
 }
 
@@ -134,7 +158,8 @@ static int checkLevelElement(int element)
 {
   if (element >= EL_FIRST_RUNTIME_EL)
   {
-    Error(ERR_WARN, "invalid level element %d", element);
+    Warn("invalid level element %d", element);
+
     element = EL_CHAR_FRAGE;
   }
 
@@ -155,8 +180,8 @@ static int LoadLevel_MM_HEAD(File *file, int chunk_size,
   int i;
   int laser_color;
 
-  lev_fieldx = level->fieldx = getFile8Bit(file);
-  lev_fieldy = level->fieldy = getFile8Bit(file);
+  level->fieldx = getFile8Bit(file);
+  level->fieldy = getFile8Bit(file);
 
   level->time           = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
   level->kettles_needed = getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN);
@@ -182,10 +207,14 @@ static int LoadLevel_MM_HEAD(File *file, int chunk_size,
   level->amoeba_speed          = getFile8Bit(file);
   level->time_fuse             = getFile8Bit(file);
 
+  // fuse time was 0 and hardcoded in game engine in level files up to 2.0.x
+  if (level->file_version <= MM_FILE_VERSION_2_0)
+    level->time_fuse = 25;
+
   laser_color                  = getFile8Bit(file);
-  level->laser_red             = (laser_color >> 2) & 0x01;
-  level->laser_green           = (laser_color >> 1) & 0x01;
-  level->laser_blue            = (laser_color >> 0) & 0x01;
+  level->mm_laser_red          = (laser_color >> 2) & 0x01;
+  level->mm_laser_green                = (laser_color >> 1) & 0x01;
+  level->mm_laser_blue         = (laser_color >> 0) & 0x01;
 
   level->encoding_16bit_field  = (getFile8Bit(file) == 1 ? TRUE : FALSE);
 
@@ -229,7 +258,7 @@ static int LoadLevel_MM_BODY(File *file, int chunk_size,
 
   for (y = 0; y < level->fieldy; y++)
     for (x = 0; x < level->fieldx; x++)
-      native_mm_level.field[x][y] = Feld[x][y] = Ur[x][y] =
+      native_mm_level.field[x][y] = Ur[x][y] =
        checkLevelElement(level->encoding_16bit_field ?
                          getFile16BitInteger(file, BYTE_ORDER_BIG_ENDIAN) :
                          getFile8Bit(file));
@@ -258,13 +287,13 @@ boolean LoadNativeLevel_MM(char *filename, boolean level_info_only)
     {  NULL,  0,                       NULL }
   };
 
-  /* always start with reliable default values */
+  // always start with reliable default values
   setLevelInfoToDefaults_MM();
 
   if (!(file = openFile(filename, MODE_READ)))
   {
     if (!level_info_only)
-      Error(ERR_WARN, "cannot read level '%s' - creating new level", filename);
+      Warn("cannot read level '%s' - creating new level", filename);
 
     return FALSE;
   }
@@ -272,19 +301,19 @@ boolean LoadNativeLevel_MM(char *filename, boolean level_info_only)
   getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
   if (strcmp(chunk_name, "MMII") == 0)
   {
-    getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);  /* not used */
+    getFile32BitInteger(file, BYTE_ORDER_BIG_ENDIAN);  // not used
 
     getFileChunk(file, chunk_name, NULL, BYTE_ORDER_BIG_ENDIAN);
     if (strcmp(chunk_name, "CAVE") != 0)
     {
-      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      Warn("unknown format of level file '%s'", filename);
 
       closeFile(file);
 
       return FALSE;
     }
   }
-  else /* check for pre-2.0 file format with cookie string */
+  else // check for pre-2.0 file format with cookie string
   {
     strcpy(cookie, chunk_name);
     getStringFromFile(file, &cookie[4], MAX_LINE_LEN - 4);
@@ -293,7 +322,7 @@ boolean LoadNativeLevel_MM(char *filename, boolean level_info_only)
 
     if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
     {
-      Error(ERR_WARN, "unknown format of level file '%s'", filename);
+      Warn("unknown format of level file '%s'", filename);
 
       closeFile(file);
 
@@ -303,7 +332,7 @@ boolean LoadNativeLevel_MM(char *filename, boolean level_info_only)
     if ((native_mm_level.file_version = getFileVersionFromCookieString(cookie))
        == -1)
     {
-      Error(ERR_WARN, "unsupported version of level file '%s'", filename);
+      Warn("unsupported version of level file '%s'", filename);
 
       closeFile(file);
 
@@ -321,7 +350,7 @@ boolean LoadNativeLevel_MM(char *filename, boolean level_info_only)
 
     if (chunk_info[i].name == NULL)
     {
-      Error(ERR_WARN, "unknown chunk '%s' in level file '%s'",
+      Warn("unknown chunk '%s' in level file '%s'",
            chunk_name, filename);
 
       ReadUnusedBytesFromFile(file, chunk_size);
@@ -329,22 +358,22 @@ boolean LoadNativeLevel_MM(char *filename, boolean level_info_only)
     else if (chunk_info[i].size != -1 &&
             chunk_info[i].size != chunk_size)
     {
-      Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
+      Warn("wrong size (%d) of chunk '%s' in level file '%s'",
            chunk_size, chunk_name, filename);
 
       ReadUnusedBytesFromFile(file, chunk_size);
     }
     else
     {
-      /* call function to load this level chunk */
+      // call function to load this level chunk
       int chunk_size_expected =
        (chunk_info[i].loader)(file, chunk_size, &native_mm_level);
 
-      /* the size of some chunks cannot be checked before reading other
-        chunks first (like "HEAD" and "BODY") that contain some header
-        information, so check them here */
+      // the size of some chunks cannot be checked before reading other
+      // chunks first (like "HEAD" and "BODY") that contain some header
+      // information, so check them here
       if (chunk_size_expected != chunk_size)
-       Error(ERR_WARN, "wrong size (%d) of chunk '%s' in level file '%s'",
+       Warn("wrong size (%d) of chunk '%s' in level file '%s'",
              chunk_size, chunk_name, filename);
     }
   }
@@ -375,9 +404,9 @@ static void SaveLevel_MM_HEAD(FILE *file, struct LevelInfo_MM *level)
   fputc(level->amoeba_speed, file);
   fputc(level->time_fuse, file);
 
-  laser_color = ((level->laser_red   << 2) |
-                (level->laser_green << 1) |
-                (level->laser_blue  << 0));
+  laser_color = ((level->mm_laser_red   << 2) |
+                (level->mm_laser_green << 1) |
+                (level->mm_laser_blue  << 0));
   fputc(laser_color, file);
 
   fputc((level->encoding_16bit_field ? 1 : 0), file);
@@ -413,12 +442,12 @@ void SaveNativeLevel_MM(char *filename)
 
   if (!(file = fopen(filename, MODE_WRITE)))
   {
-    Error(ERR_WARN, "cannot save level file '%s'", filename);
+    Warn("cannot save level file '%s'", filename);
 
     return;
   }
 
-  /* check level field for 16-bit elements */
+  // check level field for 16-bit elements
   native_mm_level.encoding_16bit_field = FALSE;
 
   for (y = 0; y < native_mm_level.fieldy; y++)