added optional button to restart game (door, panel and touch variants)
[rocksndiamonds.git] / src / files.c
index bd631a46fb31f38fb395d7d83246f33da60932c0..097deead25dda3078dc12860702c540e87cfd2ff 100644 (file)
                                         CONF_CONTENT_NUM_BYTES : 1)
 
 #define CONF_ELEMENT_BYTE_POS(i)       ((i) * CONF_ELEMENT_NUM_BYTES)
-#define CONF_ELEMENTS_ELEMENT(b,i)     ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) |  \
+#define CONF_ELEMENTS_ELEMENT(b, i)    ((b[CONF_ELEMENT_BYTE_POS(i)] << 8) | \
                                        (b[CONF_ELEMENT_BYTE_POS(i) + 1]))
 
 #define CONF_CONTENT_ELEMENT_POS(c,x,y)        ((c) * CONF_CONTENT_NUM_ELEMENTS +    \
@@ -923,9 +923,14 @@ static struct LevelFileConfigInfo chunk_config_ELEM[] =
   },
   {
     EL_MM_GRAY_BALL,                   -1,
-    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(1),
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(3),
     &li.rotate_mm_ball_content,                TRUE
   },
+  {
+    EL_MM_GRAY_BALL,                   -1,
+    TYPE_BOOLEAN,                      CONF_VALUE_8_BIT(2),
+    &li.explode_mm_ball,               FALSE
+  },
 
   {
     EL_MM_STEEL_BLOCK,                 -1,
@@ -1898,8 +1903,7 @@ static void setLevelInfoToDefaults_Elements(struct LevelInfo *level)
     setElementChangeInfoToDefaults(ei->change);
 
     if (IS_CUSTOM_ELEMENT(element) ||
-       IS_GROUP_ELEMENT(element) ||
-       IS_INTERNAL_ELEMENT(element))
+       IS_GROUP_ELEMENT(element))
     {
       setElementDescriptionToDefault(ei);
 
@@ -3708,6 +3712,7 @@ static void CopyNativeLevel_RND_to_EM(struct LevelInfo *level)
   cav->lenses_time             = level->lenses_time;
   cav->magnify_time            = level->magnify_time;
 
+  cav->wind_time = 9999;
   cav->wind_direction =
     map_direction_RND_to_EM(level->wind_direction_initial);
 
@@ -4192,6 +4197,7 @@ static void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
   level_mm->num_ball_contents = level->num_mm_ball_contents;
   level_mm->ball_choice_mode = level->mm_ball_choice_mode;
   level_mm->rotate_ball_content = level->rotate_mm_ball_content;
+  level_mm->explode_ball = level->explode_mm_ball;
 
   for (i = 0; i < level->num_mm_ball_contents; i++)
     level_mm->ball_content[i] =
@@ -4244,6 +4250,7 @@ static void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
   level->num_mm_ball_contents = level_mm->num_ball_contents;
   level->mm_ball_choice_mode = level_mm->ball_choice_mode;
   level->rotate_mm_ball_content = level_mm->rotate_ball_content;
+  level->explode_mm_ball = level_mm->explode_ball;
 
   for (i = 0; i < level->num_mm_ball_contents; i++)
     level->mm_ball_content[i] =
@@ -9482,6 +9489,10 @@ static struct TokenInfo global_setup_tokens[] =
     TYPE_SWITCH,
     &setup.toons,                              "toons"
   },
+  {
+    TYPE_SWITCH,
+    &setup.global_animations,                  "global_animations"
+  },
   {
     TYPE_SWITCH,
     &setup.scroll_delay,                       "scroll_delay"
@@ -10401,6 +10412,7 @@ static void setSetupInfoToDefaults(struct SetupInfo *si)
   si->sound_music = TRUE;
   si->sound_simple = TRUE;
   si->toons = TRUE;
+  si->global_animations = TRUE;
   si->scroll_delay = TRUE;
   si->forced_scroll_delay = FALSE;
   si->scroll_delay_value = STD_SCROLL_DELAY;
@@ -11519,8 +11531,9 @@ static boolean string_has_parameter(char *s, char *s_contained)
     char next_char = s[strlen(s_contained)];
 
     // check if next character is delimiter or whitespace
-    return (next_char == ',' || next_char == '\0' ||
-           next_char == ' ' || next_char == '\t' ? TRUE : FALSE);
+    if (next_char == ',' || next_char == '\0' ||
+       next_char == ' ' || next_char == '\t')
+      return TRUE;
   }
 
   // check if string contains another parameter string after a comma
@@ -11538,6 +11551,85 @@ static boolean string_has_parameter(char *s, char *s_contained)
   return string_has_parameter(substring, s_contained);
 }
 
+static int get_anim_parameter_value_ce(char *s)
+{
+  char *s_ptr = s;
+  char *pattern_1 = "ce_change:custom_";
+  char *pattern_2 = ".page_";
+  int pattern_1_len = strlen(pattern_1);
+  char *matching_char = strstr(s_ptr, pattern_1);
+  int result = ANIM_EVENT_NONE;
+
+  if (matching_char == NULL)
+    return ANIM_EVENT_NONE;
+
+  result = ANIM_EVENT_CE_CHANGE;
+
+  s_ptr = matching_char + pattern_1_len;
+
+  // check for custom element number ("custom_X", "custom_XX" or "custom_XXX")
+  if (*s_ptr >= '0' && *s_ptr <= '9')
+  {
+    int gic_ce_nr = (*s_ptr++ - '0');
+
+    if (*s_ptr >= '0' && *s_ptr <= '9')
+    {
+      gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
+
+      if (*s_ptr >= '0' && *s_ptr <= '9')
+       gic_ce_nr = 10 * gic_ce_nr + (*s_ptr++ - '0');
+    }
+
+    if (gic_ce_nr < 1 || gic_ce_nr > NUM_CUSTOM_ELEMENTS)
+      return ANIM_EVENT_NONE;
+
+    // custom element stored as 0 to 255
+    gic_ce_nr--;
+
+    result |= gic_ce_nr << ANIM_EVENT_CE_BIT;
+  }
+  else
+  {
+    // invalid custom element number specified
+
+    return ANIM_EVENT_NONE;
+  }
+
+  // check for change page number ("page_X" or "page_XX") (optional)
+  if (strPrefix(s_ptr, pattern_2))
+  {
+    s_ptr += strlen(pattern_2);
+
+    if (*s_ptr >= '0' && *s_ptr <= '9')
+    {
+      int gic_page_nr = (*s_ptr++ - '0');
+
+      if (*s_ptr >= '0' && *s_ptr <= '9')
+       gic_page_nr = 10 * gic_page_nr + (*s_ptr++ - '0');
+
+      if (gic_page_nr < 1 || gic_page_nr > MAX_CHANGE_PAGES)
+       return ANIM_EVENT_NONE;
+
+      // change page stored as 1 to 32 (0 means "all change pages")
+
+      result |= gic_page_nr << ANIM_EVENT_PAGE_BIT;
+    }
+    else
+    {
+      // invalid animation part number specified
+
+      return ANIM_EVENT_NONE;
+    }
+  }
+
+  // discard result if next character is neither delimiter nor whitespace
+  if (!(*s_ptr == ',' || *s_ptr == '\0' ||
+       *s_ptr == ' ' || *s_ptr == '\t'))
+    return ANIM_EVENT_NONE;
+
+  return result;
+}
+
 static int get_anim_parameter_value(char *s)
 {
   int event_value[] =
@@ -11563,6 +11655,11 @@ static int get_anim_parameter_value(char *s)
   int result = ANIM_EVENT_NONE;
   int i;
 
+  result = get_anim_parameter_value_ce(s);
+
+  if (result != ANIM_EVENT_NONE)
+    return result;
+
   for (i = 0; i < ARRAY_SIZE(event_value); i++)
   {
     matching_char = strstr(s_ptr, pattern_1[i]);
@@ -11732,6 +11829,8 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
              strEqual(value, "lower")  ? POS_LOWER :
              strEqual(value, "bottom") ? POS_BOTTOM :
              strEqual(value, "any")    ? POS_ANY :
+             strEqual(value, "ce")     ? POS_CE :
+             strEqual(value, "ce_trigger") ? POS_CE_TRIGGER :
              strEqual(value, "last")   ? POS_LAST : POS_UNDEFINED);
   }
   else if (strEqual(suffix, ".align"))
@@ -11765,6 +11864,7 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
              string_has_parameter(value, "centered")   ? ANIM_CENTERED :
              string_has_parameter(value, "all")        ? ANIM_ALL :
              string_has_parameter(value, "tiled")      ? ANIM_TILED :
+             string_has_parameter(value, "level_nr")   ? ANIM_LEVEL_NR :
              ANIM_DEFAULT);
 
     if (string_has_parameter(value, "once"))
@@ -11821,11 +11921,16 @@ int get_parameter_value(char *value_raw, char *suffix, int type)
 
     if (string_has_parameter(value, "multiple_actions"))
       result |= STYLE_MULTIPLE_ACTIONS;
+
+    if (string_has_parameter(value, "consume_ce_event"))
+      result |= STYLE_CONSUME_CE_EVENT;
   }
   else if (strEqual(suffix, ".fade_mode"))
   {
     result = (string_has_parameter(value, "none")      ? FADE_MODE_NONE :
              string_has_parameter(value, "fade")       ? FADE_MODE_FADE :
+             string_has_parameter(value, "fade_in")    ? FADE_MODE_FADE_IN :
+             string_has_parameter(value, "fade_out")   ? FADE_MODE_FADE_OUT :
              string_has_parameter(value, "crossfade")  ? FADE_MODE_CROSSFADE :
              string_has_parameter(value, "melt")       ? FADE_MODE_MELT :
              string_has_parameter(value, "curtain")    ? FADE_MODE_CURTAIN :
@@ -12102,7 +12207,7 @@ static void InitMenuDesignSettings_SpecialPostProcessing(void)
       vp_playfield->width = MIN(vp_playfield->width, vp_playfield->max_width);
 
     if (vp_playfield->max_height != -1)
-      vp_playfield->height = MIN(vp_playfield->height,vp_playfield->max_height);
+      vp_playfield->height = MIN(vp_playfield->height, vp_playfield->max_height);
 
     // adjust playfield position according to specified alignment
 
@@ -12337,6 +12442,45 @@ static void InitMenuDesignSettings_SpecialPostProcessing_AfterGraphics(void)
   }
 }
 
+static void InitMenuDesignSettings_PreviewPlayers_Ext(SetupFileHash *hash,
+                                                      boolean initialize)
+{
+  // special case: check if network and preview player positions are redefined,
+  // to compare this later against the main menu level preview being redefined
+  struct TokenIntPtrInfo menu_config_players[] =
+  {
+    { "main.network_players.x",        &menu.main.network_players.redefined    },
+    { "main.network_players.y",        &menu.main.network_players.redefined    },
+    { "main.preview_players.x",        &menu.main.preview_players.redefined    },
+    { "main.preview_players.y",        &menu.main.preview_players.redefined    },
+    { "preview.x",             &preview.redefined                      },
+    { "preview.y",             &preview.redefined                      }
+  };
+  int i;
+
+  if (initialize)
+  {
+    for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
+      *menu_config_players[i].value = FALSE;
+  }
+  else
+  {
+    for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
+      if (getHashEntry(hash, menu_config_players[i].token) != NULL)
+        *menu_config_players[i].value = TRUE;
+  }
+}
+
+static void InitMenuDesignSettings_PreviewPlayers(void)
+{
+  InitMenuDesignSettings_PreviewPlayers_Ext(NULL, TRUE);
+}
+
+static void InitMenuDesignSettings_PreviewPlayers_FromHash(SetupFileHash *hash)
+{
+  InitMenuDesignSettings_PreviewPlayers_Ext(hash, FALSE);
+}
+
 static void LoadMenuDesignSettingsFromFilename(char *filename)
 {
   static struct TitleFadingInfo tfi;
@@ -12471,7 +12615,9 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     {
       { "menu.draw_xoffset.INFO",      &menu.draw_xoffset_info[i]      },
       { "menu.draw_yoffset.INFO",      &menu.draw_yoffset_info[i]      },
-      { "menu.list_size.INFO",         &menu.list_size_info[i]         }
+      { "menu.list_size.INFO",         &menu.list_size_info[i]         },
+      { "menu.list_entry_size.INFO",   &menu.list_entry_size_info[i]   },
+      { "menu.tile_size.INFO",         &menu.tile_size_info[i]         }
     };
 
     for (j = 0; j < ARRAY_SIZE(menu_config); j++)
@@ -12511,6 +12657,7 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     struct TokenIntPtrInfo menu_config[] =
     {
       { "menu.left_spacing.INFO",      &menu.left_spacing_info[i]      },
+      { "menu.middle_spacing.INFO",    &menu.middle_spacing_info[i]    },
       { "menu.right_spacing.INFO",     &menu.right_spacing_info[i]     },
       { "menu.top_spacing.INFO",       &menu.top_spacing_info[i]       },
       { "menu.bottom_spacing.INFO",    &menu.bottom_spacing_info[i]    },
@@ -12675,28 +12822,12 @@ static void LoadMenuDesignSettingsFromFilename(char *filename)
     }
   }
 
-  // special case: check if network and preview player positions are redefined,
-  // to compare this later against the main menu level preview being redefined
-  struct TokenIntPtrInfo menu_config_players[] =
-  {
-    { "main.network_players.x",        &menu.main.network_players.redefined    },
-    { "main.network_players.y",        &menu.main.network_players.redefined    },
-    { "main.preview_players.x",        &menu.main.preview_players.redefined    },
-    { "main.preview_players.y",        &menu.main.preview_players.redefined    },
-    { "preview.x",             &preview.redefined                      },
-    { "preview.y",             &preview.redefined                      }
-  };
-
-  for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
-    *menu_config_players[i].value = FALSE;
-
-  for (i = 0; i < ARRAY_SIZE(menu_config_players); i++)
-    if (getHashEntry(setup_file_hash, menu_config_players[i].token) != NULL)
-      *menu_config_players[i].value = TRUE;
-
   // read (and overwrite with) values that may be specified in config file
   InitMenuDesignSettings_FromHash(setup_file_hash, TRUE);
 
+  // special case: check if network and preview player positions are redefined
+  InitMenuDesignSettings_PreviewPlayers_FromHash(setup_file_hash);
+
   freeSetupFileHash(setup_file_hash);
 }
 
@@ -12706,6 +12837,7 @@ void LoadMenuDesignSettings(void)
 
   InitMenuDesignSettings_Static();
   InitMenuDesignSettings_SpecialPreProcessing();
+  InitMenuDesignSettings_PreviewPlayers();
 
   if (!GFX_OVERRIDE_ARTWORK(ARTWORK_TYPE_GRAPHICS))
   {
@@ -12828,11 +12960,13 @@ static struct MusicFileInfo *get_music_file_info_ext(char *basename, int music,
     { "artist_header", &tmp_music_file_info.artist_header      },
     { "album_header",  &tmp_music_file_info.album_header       },
     { "year_header",   &tmp_music_file_info.year_header        },
+    { "played_header", &tmp_music_file_info.played_header      },
 
     { "title",         &tmp_music_file_info.title              },
     { "artist",                &tmp_music_file_info.artist             },
     { "album",         &tmp_music_file_info.album              },
     { "year",          &tmp_music_file_info.year               },
+    { "played",                &tmp_music_file_info.played             },
 
     { NULL,            NULL                                    },
   };
@@ -12928,14 +13062,12 @@ static boolean sound_info_listed(struct MusicFileInfo *list, char *basename)
 
 void LoadMusicInfo(void)
 {
-  char *music_directory = getCustomMusicDirectory_NoConf();
+  int num_music_noconf = getMusicListSize_NoConf();
   int num_music = getMusicListSize();
-  int num_music_noconf = 0;
   int num_sounds = getSoundListSize();
-  Directory *dir;
-  DirectoryEntry *dir_entry;
   struct FileInfo *music, *sound;
   struct MusicFileInfo *next, **new;
+
   int i;
 
   while (music_file_info != NULL)
@@ -12948,11 +13080,13 @@ void LoadMusicInfo(void)
     checked_free(music_file_info->artist_header);
     checked_free(music_file_info->album_header);
     checked_free(music_file_info->year_header);
+    checked_free(music_file_info->played_header);
 
     checked_free(music_file_info->title);
     checked_free(music_file_info->artist);
     checked_free(music_file_info->album);
     checked_free(music_file_info->year);
+    checked_free(music_file_info->played);
 
     free(music_file_info);
 
@@ -12961,76 +13095,68 @@ void LoadMusicInfo(void)
 
   new = &music_file_info;
 
-  for (i = 0; i < num_music; i++)
+  // get (configured or unconfigured) music file info for all levels
+  for (i = leveldir_current->first_level;
+       i <= leveldir_current->last_level; i++)
   {
-    music = getMusicListEntry(i);
+    int music_nr;
 
-    if (music->filename == NULL)
-      continue;
+    if (levelset.music[i] != MUS_UNDEFINED)
+    {
+      // get music file info for configured level music
+      music_nr = levelset.music[i];
+    }
+    else if (num_music_noconf > 0)
+    {
+      // get music file info for unconfigured level music
+      int level_pos = i - leveldir_current->first_level;
 
-    if (strEqual(music->filename, UNDEFINED_FILENAME))
+      music_nr = MAP_NOCONF_MUSIC(level_pos % num_music_noconf);
+    }
+    else
+    {
       continue;
+    }
 
-    // a configured file may be not recognized as music
-    if (!FileIsMusic(music->filename))
+    char *basename = getMusicInfoEntryFilename(music_nr);
+
+    if (basename == NULL)
       continue;
 
-    if (!music_info_listed(music_file_info, music->filename))
+    if (!music_info_listed(music_file_info, basename))
     {
-      *new = get_music_file_info(music->filename, i);
+      *new = get_music_file_info(basename, music_nr);
 
       if (*new != NULL)
        new = &(*new)->next;
     }
   }
 
-  if ((dir = openDirectory(music_directory)) == NULL)
-  {
-    Warn("cannot read music directory '%s'", music_directory);
-
-    return;
-  }
-
-  while ((dir_entry = readDirectory(dir)) != NULL)     // loop all entries
+  // get music file info for all remaining configured music files
+  for (i = 0; i < num_music; i++)
   {
-    char *basename = dir_entry->basename;
-    boolean music_already_used = FALSE;
-    int i;
-
-    // skip all music files that are configured in music config file
-    for (i = 0; i < num_music; i++)
-    {
-      music = getMusicListEntry(i);
-
-      if (music->filename == NULL)
-       continue;
+    music = getMusicListEntry(i);
 
-      if (strEqual(basename, music->filename))
-      {
-       music_already_used = TRUE;
-       break;
-      }
-    }
+    if (music->filename == NULL)
+      continue;
 
-    if (music_already_used)
+    if (strEqual(music->filename, UNDEFINED_FILENAME))
       continue;
 
-    if (!FileIsMusic(dir_entry->filename))
+    // a configured file may be not recognized as music
+    if (!FileIsMusic(music->filename))
       continue;
 
-    if (!music_info_listed(music_file_info, basename))
+    if (!music_info_listed(music_file_info, music->filename))
     {
-      *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
+      *new = get_music_file_info(music->filename, i);
 
       if (*new != NULL)
        new = &(*new)->next;
     }
-
-    num_music_noconf++;
   }
 
-  closeDirectory(dir);
-
+  // get sound file info for all configured sound files
   for (i = 0; i < num_sounds; i++)
   {
     sound = getSoundListEntry(i);