#include <ctype.h>
#include <sys/stat.h>
+#include <dirent.h>
+#include <math.h>
#include "libgame/libgame.h"
#define LEVEL_CHUNK_CNT3_UNUSED 10 /* unused CNT3 chunk bytes */
#define LEVEL_CPART_CUS3_SIZE 134 /* size of CUS3 chunk part */
#define LEVEL_CPART_CUS3_UNUSED 15 /* unused CUS3 bytes / part */
-#define LEVEL_CPART_CUS4_SIZE ??? /* size of CUS4 chunk part */
-#define LEVEL_CPART_CUS4_UNUSED ??? /* unused CUS4 bytes / part */
#define TAPE_HEADER_SIZE 20 /* size of tape file header */
#define TAPE_HEADER_UNUSED 3 /* unused tape header bytes */
change->delay_fixed = 0;
change->delay_random = 0;
- change->delay_frames = -1; /* later set to reliable default value */
+ change->delay_frames = 1;
change->trigger_element = EL_EMPTY_SPACE;
change->use_content = FALSE;
change->only_complete = FALSE;
change->use_random_change = FALSE;
- change->random = 0;
+ change->random = 100;
change->power = CP_NON_DESTRUCTIVE;
- for(x=0; x<3; x++)
- for(y=0; y<3; y++)
+ for (x = 0; x < 3; x++)
+ for (y = 0; y < 3; y++)
change->content[x][y] = EL_EMPTY_SPACE;
change->direct_action = 0;
level->fieldx = STD_LEV_FIELDX;
level->fieldy = STD_LEV_FIELDY;
- for(x=0; x<MAX_LEV_FIELDX; x++)
- for(y=0; y<MAX_LEV_FIELDY; y++)
+ for (x = 0; x < MAX_LEV_FIELDX; x++)
+ for (y = 0; y < MAX_LEV_FIELDY; y++)
level->field[x][y] = EL_SAND;
level->time = 100;
level->time_timegate = 10;
level->amoeba_content = EL_DIAMOND;
level->double_speed = FALSE;
- level->gravity = FALSE;
+ level->initial_gravity = FALSE;
level->em_slippery_gems = FALSE;
level->use_custom_template = FALSE;
- for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+ for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
level->name[i] = '\0';
- for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+ for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
level->author[i] = '\0';
strcpy(level->name, NAMELESS_LEVEL_NAME);
strcpy(level->author, ANONYMOUS_NAME);
- for (i=0; i<4; i++)
+ for (i = 0; i < 4; i++)
{
level->envelope_text[i][0] = '\0';
level->envelope_xsize[i] = MAX_ENVELOPE_XSIZE;
level->envelope_ysize[i] = MAX_ENVELOPE_YSIZE;
}
- for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+ for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
level->score[i] = 10;
level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
- for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
- for(x=0; x<3; x++)
- for(y=0; y<3; y++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ for (x = 0; x < 3; x++)
+ for (y = 0; y < 3; y++)
level->yamyam_content[i][x][y] =
(i < STD_ELEMENT_CONTENTS ? EL_ROCK : EL_EMPTY);
level->field[0][0] = EL_PLAYER_1;
level->field[STD_LEV_FIELDX - 1][STD_LEV_FIELDY - 1] = EL_EXIT_CLOSED;
- for (i=0; i < MAX_NUM_ELEMENTS; i++)
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
{
setElementChangePages(&element_info[i], 1);
setElementChangeInfoToDefaults(element_info[i].change);
}
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
- for(j=0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
+ for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
element_info[element].description[j] = '\0';
if (element_info[element].custom_description != NULL)
strncpy(element_info[element].description,
element_info[element].collect_score = 10; /* special default */
element_info[element].collect_count = 1; /* special default */
- element_info[element].push_delay_fixed = 2; /* special default */
- element_info[element].push_delay_random = 8; /* special default */
+ element_info[element].push_delay_fixed = -1; /* initialize later */
+ element_info[element].push_delay_random = -1; /* initialize later */
element_info[element].move_delay_fixed = 0;
element_info[element].move_delay_random = 0;
element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
- for(x=0; x<3; x++)
- for(y=0; y<3; y++)
+ for (x = 0; x < 3; x++)
+ for (y = 0; y < 3; y++)
element_info[element].content[x][y] = EL_EMPTY_SPACE;
element_info[element].access_type = 0;
element_info[element].current_change_page = 0;
/* start with no properties at all */
- for (j=0; j < NUM_EP_BITFIELDS; j++)
+ for (j = 0; j < NUM_EP_BITFIELDS; j++)
Properties[element][j] = EP_BITMASK_DEFAULT;
element_info[element].modified_settings = FALSE;
strcpy(level->author, PROGRAM_AUTHOR_STRING);
break;
- case LEVELCLASS_CONTRIBUTION:
- strncpy(level->author, leveldir_current->name,MAX_LEVEL_AUTHOR_LEN);
+ case LEVELCLASS_CONTRIB:
+ strncpy(level->author, leveldir_current->name, MAX_LEVEL_AUTHOR_LEN);
level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
break;
- case LEVELCLASS_USER:
+ case LEVELCLASS_PRIVATE:
strncpy(level->author, getRealName(), MAX_LEVEL_AUTHOR_LEN);
level->author[MAX_LEVEL_AUTHOR_LEN] = '\0';
break;
level->time = getFile16BitBE(file);
level->gems_needed = getFile16BitBE(file);
- for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+ for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
level->name[i] = getFile8Bit(file);
level->name[MAX_LEVEL_NAME_LEN] = 0;
- for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+ for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
level->score[i] = getFile8Bit(file);
level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
- for(i=0; i<STD_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
level->yamyam_content[i][x][y] = checkLevelElement(getFile8Bit(file));
level->amoeba_speed = getFile8Bit(file);
level->time_wheel = getFile8Bit(file);
level->amoeba_content = checkLevelElement(getFile8Bit(file));
level->double_speed = (getFile8Bit(file) == 1 ? TRUE : FALSE);
- level->gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+ level->initial_gravity = (getFile8Bit(file) == 1 ? TRUE : FALSE);
level->encoding_16bit_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
level->em_slippery_gems = (getFile8Bit(file) == 1 ? TRUE : FALSE);
{
int i;
- for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+ for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
level->author[i] = getFile8Bit(file);
level->author[MAX_LEVEL_NAME_LEN] = 0;
return chunk_size_expected;
}
- for(y=0; y<level->fieldy; y++)
- for(x=0; x<level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ for (x = 0; x < level->fieldx; x++)
level->field[x][y] =
checkLevelElement(level->encoding_16bit_field ? getFile16BitBE(file) :
getFile8Bit(file));
level->num_yamyam_contents > MAX_ELEMENT_CONTENTS)
level->num_yamyam_contents = STD_ELEMENT_CONTENTS;
- for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
level->yamyam_content[i][x][y] =
checkLevelElement(level->encoding_16bit_field ?
getFile16BitBE(file) : getFile8Bit(file));
ReadUnusedBytesFromFile(file, LEVEL_CHUNK_CNT2_UNUSED);
- for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
content_array[i][x][y] = checkLevelElement(getFile16BitBE(file));
/* correct invalid number of content fields -- should never happen */
{
level->num_yamyam_contents = num_contents;
- for(i=0; i<num_contents; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < num_contents; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
level->yamyam_content[i][x][y] = content_array[i][x][y];
}
else if (element == EL_BD_AMOEBA)
return chunk_size_expected;
}
- for(i=0; i < envelope_len; i++)
+ for (i = 0; i < envelope_len; i++)
level->envelope_text[envelope_nr][i] = getFile8Bit(file);
return chunk_size;
return chunk_size_expected;
}
- for (i=0; i < num_changed_custom_elements; i++)
+ for (i = 0; i < num_changed_custom_elements; i++)
{
int element = getFile16BitBE(file);
int properties = getFile32BitBE(file);
return chunk_size_expected;
}
- for (i=0; i < num_changed_custom_elements; i++)
+ for (i = 0; i < num_changed_custom_elements; i++)
{
int element = getFile16BitBE(file);
int custom_target_element = getFile16BitBE(file);
return chunk_size_expected;
}
- for (i=0; i < num_changed_custom_elements; i++)
+ for (i = 0; i < num_changed_custom_elements; i++)
{
int element = getFile16BitBE(file);
{
Error(ERR_WARN, "invalid custom element number %d", element);
- element = EL_DEFAULT; /* dummy element used for artwork config */
+ element = EL_DUMMY;
}
- for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
+ for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
element_info[element].description[j] = getFile8Bit(file);
element_info[element].description[MAX_ELEMENT_NAME_LEN] = 0;
element_info[element].move_direction_initial = getFile8Bit(file);
element_info[element].move_stepsize = getFile8Bit(file);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
element_info[element].content[x][y] =
checkLevelElement(getFile16BitBE(file));
element_info[element].change->random = getFile8Bit(file);
element_info[element].change->power = getFile8Bit(file);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
element_info[element].change->content[x][y] =
checkLevelElement(getFile16BitBE(file));
{
Error(ERR_WARN, "invalid custom element number %d", element);
- element = EL_DEFAULT; /* dummy element used for artwork config */
+ element = EL_DUMMY;
}
ei = &element_info[element];
- for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+ for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
ei->description[i] = getFile8Bit(file);
ei->description[MAX_ELEMENT_NAME_LEN] = 0;
ei->slippery_type = getFile8Bit(file);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
ei->content[x][y] = checkLevelElement(getFile16BitBE(file));
/* some free bytes for future custom property values and padding */
setElementChangePages(ei, ei->num_change_pages);
- for (i=0; i < ei->num_change_pages; i++)
+ for (i = 0; i < ei->num_change_pages; i++)
{
struct ElementChangeInfo *change = &ei->change_page[i];
change->random = getFile8Bit(file);
change->power = getFile8Bit(file);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
change->content[x][y] = checkLevelElement(getFile16BitBE(file));
change->can_change = getFile8Bit(file);
+ change->sides = getFile8Bit(file);
+
+ if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
+ change->sides = CH_SIDE_ANY;
+
/* some free bytes for future change property values and padding */
- ReadUnusedBytesFromFile(file, 9);
+ ReadUnusedBytesFromFile(file, 8);
}
/* mark this custom element as modified */
fclose(file);
}
-#if 1
-
static void LoadLevel_InitVersion(struct LevelInfo *level, char *filename)
{
if (leveldir_current == NULL) /* only when dumping level */
return;
+#if 0
+ printf("::: sort_priority: %d\n", leveldir_current->sort_priority);
+#endif
+
/* determine correct game engine version of current level */
- if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
- IS_LEVELCLASS_USER(leveldir_current))
+#if 1
+ if (!leveldir_current->latest_engine)
+#else
+ if (IS_LEVELCLASS_CONTRIB(leveldir_current) ||
+ IS_LEVELCLASS_PRIVATE(leveldir_current) ||
+ IS_LEVELCLASS_UNDEFINED(leveldir_current))
+#endif
{
#if 0
printf("\n::: This level is private or contributed: '%s'\n", filename);
#endif
- /* For user contributed and private levels, use the version of
- the game engine the levels were created for.
+#if 0
+ printf("\n::: Use the stored game engine version for this level\n");
+#endif
+
+ /* For all levels which are not forced to use the latest game engine
+ version (normally user contributed, private and undefined levels),
+ use the version of the game engine the levels were created for.
+
Since 2.0.1, the game engine version is now directly stored
in the level file (chunk "VERS"), so there is no need anymore
to set the game version from the file version (except for old,
/* do some special adjustments to support older level versions */
if (level->file_version == FILE_VERSION_1_0)
{
- Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
+ Error(ERR_WARN, "level file '%s' has version number 1.0", filename);
Error(ERR_WARN, "using high speed movement for player");
/* player was faster than monsters in (pre-)1.0 levels */
}
/* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
- if (level->game_version == VERSION_IDENT(2,0,1))
+ if (level->game_version == VERSION_IDENT(2,0,1,0))
level->em_slippery_gems = TRUE;
}
else
leveldir_current->sort_priority, filename);
#endif
- /* Always use the latest version of the game engine for all but
- user contributed and private levels; this allows for actual
- corrections in the game engine to take effect for existing,
- converted levels (from "classic" or other existing games) to
- make the game emulation more accurate, while (hopefully) not
- breaking existing levels created from other players. */
+#if 0
+ printf("\n::: Use latest game engine version for this level.\n");
+#endif
+
+ /* For all levels which are forced to use the latest game engine version
+ (normally all but user contributed, private and undefined levels), set
+ the game engine version to the actual version; this allows for actual
+ corrections in the game engine to take effect for existing, converted
+ levels (from "classic" or other existing games) to make the emulation
+ of the corresponding game more accurate, while (hopefully) not breaking
+ existing levels created from other players. */
+
+#if 0
+ printf("::: changing engine from %d to %d\n",
+ level->game_version, GAME_VERSION_ACTUAL);
+#endif
level->game_version = GAME_VERSION_ACTUAL;
if (level->file_version < FILE_VERSION_2_0)
level->em_slippery_gems = TRUE;
}
+
+#if 0
+ printf("::: => %d\n", level->game_version);
+#endif
}
static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
/* map custom element change events that have changed in newer versions
(these following values were accidentally changed in version 3.0.1) */
- if (level->game_version <= VERSION_IDENT(3,0,0))
+ if (level->game_version <= VERSION_IDENT(3,0,0,0))
{
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
/* order of checking and copying events to be mapped is important */
- for (j=CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
+ for (j = CE_BY_OTHER_ACTION; j >= CE_BY_PLAYER; j--)
{
if (HAS_CHANGE_EVENT(element, j - 2))
{
}
/* order of checking and copying events to be mapped is important */
- for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
+ for (j = CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
{
if (HAS_CHANGE_EVENT(element, j - 1))
{
}
/* some custom element change events get mapped since version 3.0.3 */
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
}
/* initialize "can_change" field for old levels with only one change page */
- if (level->game_version <= VERSION_IDENT(3,0,2))
+ if (level->game_version <= VERSION_IDENT(3,0,2,0))
{
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
}
}
- /* initialize element properties for level editor etc. */
- InitElementPropertiesEngine(level->game_version);
-}
-
-static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
-{
- int x, y;
-
- /* map elements that have changed in newer versions */
- for(y=0; y<level->fieldy; y++)
+#if 0
+ /* set default push delay values (corrected since version 3.0.7-1) */
+ if (level->game_version < VERSION_IDENT(3,0,7,1))
{
- for(x=0; x<level->fieldx; x++)
- {
- int element = level->field[x][y];
-
- if (level->game_version <= VERSION_IDENT(2,2,0))
- {
- /* map game font elements */
- element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
- element == EL_CHAR('\\') ? EL_CHAR_OUMLAUT :
- element == EL_CHAR(']') ? EL_CHAR_UUMLAUT :
- element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
- }
-
- if (level->game_version < VERSION_IDENT(3,0,0))
- {
- /* map Supaplex gravity tube elements */
- element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
- element == EL_SP_GRAVITY_PORT_RIGHT ? EL_SP_PORT_RIGHT :
- element == EL_SP_GRAVITY_PORT_UP ? EL_SP_PORT_UP :
- element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
- element);
- }
-
- level->field[x][y] = element;
- }
+ game.default_push_delay_fixed = 2;
+ game.default_push_delay_random = 8;
}
-
- /* copy elements to runtime playfield array */
- for(x=0; x<MAX_LEV_FIELDX; x++)
- for(y=0; y<MAX_LEV_FIELDY; y++)
- Feld[x][y] = level->field[x][y];
-
- /* initialize level size variables for faster access */
- lev_fieldx = level->fieldx;
- lev_fieldy = level->fieldy;
-
- /* determine border element for this level */
- SetBorderElement();
-}
-
-#else
-
-static void LoadLevel_InitLevel(struct LevelInfo *level, char *filename)
-{
- int i, j, x, y;
-
- if (leveldir_current == NULL) /* only when dumping level */
- return;
-
- /* determine correct game engine version of current level */
- if (IS_LEVELCLASS_CONTRIBUTION(leveldir_current) ||
- IS_LEVELCLASS_USER(leveldir_current))
+ else
{
-#if 0
- printf("\n::: This level is private or contributed: '%s'\n", filename);
-#endif
-
- /* For user contributed and private levels, use the version of
- the game engine the levels were created for.
- Since 2.0.1, the game engine version is now directly stored
- in the level file (chunk "VERS"), so there is no need anymore
- to set the game version from the file version (except for old,
- pre-2.0 levels, where the game version is still taken from the
- file format version used to store the level -- see above). */
-
- /* do some special adjustments to support older level versions */
- if (level->file_version == FILE_VERSION_1_0)
- {
- Error(ERR_WARN, "level file '%s'has version number 1.0", filename);
- Error(ERR_WARN, "using high speed movement for player");
+ game.default_push_delay_fixed = 8;
+ game.default_push_delay_random = 8;
+ }
- /* player was faster than monsters in (pre-)1.0 levels */
- level->double_speed = TRUE;
- }
+ /* set uninitialized push delay values of custom elements in older levels */
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
- /* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
- if (level->game_version == VERSION_IDENT(2,0,1))
- level->em_slippery_gems = TRUE;
+ if (element_info[element].push_delay_fixed == -1)
+ element_info[element].push_delay_fixed = game.default_push_delay_fixed;
+ if (element_info[element].push_delay_random == -1)
+ element_info[element].push_delay_random = game.default_push_delay_random;
}
- else
- {
-#if 0
- printf("\n::: ALWAYS USE LATEST ENGINE FOR THIS LEVEL: [%d] '%s'\n",
- leveldir_current->sort_priority, filename);
#endif
- /* Always use the latest version of the game engine for all but
- user contributed and private levels; this allows for actual
- corrections in the game engine to take effect for existing,
- converted levels (from "classic" or other existing games) to
- make the game emulation more accurate, while (hopefully) not
- breaking existing levels created from other players. */
-
- level->game_version = GAME_VERSION_ACTUAL;
-
- /* Set special EM style gems behaviour: EM style gems slip down from
- normal, steel and growing wall. As this is a more fundamental change,
- it seems better to set the default behaviour to "off" (as it is more
- natural) and make it configurable in the level editor (as a property
- of gem style elements). Already existing converted levels (neither
- private nor contributed levels) are changed to the new behaviour. */
+ /* initialize element properties for level editor etc. */
+ InitElementPropertiesEngine(level->game_version);
+}
- if (level->file_version < FILE_VERSION_2_0)
- level->em_slippery_gems = TRUE;
- }
+static void LoadLevel_InitPlayfield(struct LevelInfo *level, char *filename)
+{
+ int x, y;
/* map elements that have changed in newer versions */
- for(y=0; y<level->fieldy; y++)
+ for (y = 0; y < level->fieldy; y++)
{
- for(x=0; x<level->fieldx; x++)
+ for (x = 0; x < level->fieldx; x++)
{
int element = level->field[x][y];
- if (level->game_version <= VERSION_IDENT(2,2,0))
+ if (level->game_version <= VERSION_IDENT(2,2,0,0))
{
/* map game font elements */
element = (element == EL_CHAR('[') ? EL_CHAR_AUMLAUT :
element == EL_CHAR('^') ? EL_CHAR_COPYRIGHT : element);
}
- if (level->game_version < VERSION_IDENT(3,0,0))
+ if (level->game_version < VERSION_IDENT(3,0,0,0))
{
/* map Supaplex gravity tube elements */
element = (element == EL_SP_GRAVITY_PORT_LEFT ? EL_SP_PORT_LEFT :
}
}
- /* map custom element change events that have changed in newer versions
- (these following values have accidentally changed in version 3.0.1) */
- if (level->game_version <= VERSION_IDENT(3,0,0))
- {
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
-
- /* order of checking events to be mapped is important */
- for (j=CE_BY_OTHER; j >= CE_BY_PLAYER; j--)
- {
- if (HAS_CHANGE_EVENT(element, j - 2))
- {
- SET_CHANGE_EVENT(element, j - 2, FALSE);
- SET_CHANGE_EVENT(element, j, TRUE);
- }
- }
-
- /* order of checking events to be mapped is important */
- for (j=CE_OTHER_GETS_COLLECTED; j >= CE_COLLISION; j--)
- {
- if (HAS_CHANGE_EVENT(element, j - 1))
- {
- SET_CHANGE_EVENT(element, j - 1, FALSE);
- SET_CHANGE_EVENT(element, j, TRUE);
- }
- }
- }
- }
-
- /* initialize "can_change" field for old levels with only one change page */
- if (level->game_version <= VERSION_IDENT(3,0,2))
- {
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
- {
- int element = EL_CUSTOM_START + i;
-
- if (CAN_CHANGE(element))
- element_info[element].change->can_change = TRUE;
- }
- }
-
/* copy elements to runtime playfield array */
- for(x=0; x<MAX_LEV_FIELDX; x++)
- for(y=0; y<MAX_LEV_FIELDY; y++)
+ for (x = 0; x < MAX_LEV_FIELDX; x++)
+ for (y = 0; y < MAX_LEV_FIELDY; y++)
Feld[x][y] = level->field[x][y];
/* initialize level size variables for faster access */
/* determine border element for this level */
SetBorderElement();
-
- /* initialize element properties for level editor etc. */
- InitElementPropertiesEngine(level->game_version);
}
-#endif
-
void LoadLevelTemplate(int level_nr)
{
char *filename = getLevelFilename(level_nr);
putFile16BitBE(file, level->time);
putFile16BitBE(file, level->gems_needed);
- for(i=0; i<MAX_LEVEL_NAME_LEN; i++)
+ for (i = 0; i < MAX_LEVEL_NAME_LEN; i++)
putFile8Bit(file, level->name[i]);
- for(i=0; i<LEVEL_SCORE_ELEMENTS; i++)
+ for (i = 0; i < LEVEL_SCORE_ELEMENTS; i++)
putFile8Bit(file, level->score[i]);
- for(i=0; i<STD_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < STD_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
putFile8Bit(file, (level->encoding_16bit_yamyam ? EL_EMPTY :
level->yamyam_content[i][x][y]));
putFile8Bit(file, level->amoeba_speed);
putFile8Bit(file, (level->encoding_16bit_amoeba ? EL_EMPTY :
level->amoeba_content));
putFile8Bit(file, (level->double_speed ? 1 : 0));
- putFile8Bit(file, (level->gravity ? 1 : 0));
+ putFile8Bit(file, (level->initial_gravity ? 1 : 0));
putFile8Bit(file, (level->encoding_16bit_field ? 1 : 0));
putFile8Bit(file, (level->em_slippery_gems ? 1 : 0));
{
int i;
- for(i=0; i<MAX_LEVEL_AUTHOR_LEN; i++)
+ for (i = 0; i < MAX_LEVEL_AUTHOR_LEN; i++)
putFile8Bit(file, level->author[i]);
}
{
int x, y;
- for(y=0; y<level->fieldy; y++)
- for(x=0; x<level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ for (x = 0; x < level->fieldx; x++)
if (level->encoding_16bit_field)
putFile16BitBE(file, level->field[x][y]);
else
putFile8Bit(file, 0);
putFile8Bit(file, 0);
- for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
if (level->encoding_16bit_field)
putFile16BitBE(file, level->yamyam_content[i][x][y]);
else
content_xsize = 3;
content_ysize = 3;
- for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
content_array[i][x][y] = level->yamyam_content[i][x][y];
}
else if (element == EL_BD_AMOEBA)
content_xsize = 1;
content_ysize = 1;
- for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
content_array[i][x][y] = EL_EMPTY;
content_array[0][0][0] = level->amoeba_content;
}
WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT2_UNUSED);
- for(i=0; i<MAX_ELEMENT_CONTENTS; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < MAX_ELEMENT_CONTENTS; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
putFile16BitBE(file, content_array[i][x][y]);
}
WriteUnusedBytesToFile(file, LEVEL_CHUNK_CNT3_UNUSED);
- for(i=0; i < envelope_len; i++)
+ for (i = 0; i < envelope_len; i++)
putFile8Bit(file, level->envelope_text[envelope_nr][i]);
}
putFile16BitBE(file, num_changed_custom_elements);
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
putFile16BitBE(file, num_changed_custom_elements);
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
putFile16BitBE(file, num_changed_custom_elements);
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
{
putFile16BitBE(file, element);
- for(j=0; j<MAX_ELEMENT_NAME_LEN; j++)
+ for (j = 0; j < MAX_ELEMENT_NAME_LEN; j++)
putFile8Bit(file, element_info[element].description[j]);
putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
putFile8Bit(file, element_info[element].move_direction_initial);
putFile8Bit(file, element_info[element].move_stepsize);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
putFile16BitBE(file, element_info[element].content[x][y]);
putFile32BitBE(file, element_info[element].change->events);
putFile8Bit(file, element_info[element].change->random);
putFile8Bit(file, element_info[element].change->power);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
putFile16BitBE(file, element_info[element].change->content[x][y]);
putFile8Bit(file, element_info[element].slippery_type);
putFile16BitBE(file, element);
- for(i=0; i < MAX_ELEMENT_NAME_LEN; i++)
+ for (i = 0; i < MAX_ELEMENT_NAME_LEN; i++)
putFile8Bit(file, ei->description[i]);
putFile32BitBE(file, Properties[element][EP_BITFIELD_BASE]);
putFile8Bit(file, ei->slippery_type);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
putFile16BitBE(file, ei->content[x][y]);
/* some free bytes for future custom property values and padding */
/* write change property values */
- for (i=0; i < ei->num_change_pages; i++)
+ for (i = 0; i < ei->num_change_pages; i++)
{
struct ElementChangeInfo *change = &ei->change_page[i];
putFile8Bit(file, change->random);
putFile8Bit(file, change->power);
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
putFile16BitBE(file, change->content[x][y]);
putFile8Bit(file, change->can_change);
+ putFile8Bit(file, change->sides);
+
/* some free bytes for future change property values and padding */
- WriteUnusedBytesToFile(file, 9);
+ WriteUnusedBytesToFile(file, 8);
}
}
/* check level field for 16-bit elements */
level->encoding_16bit_field = FALSE;
- for(y=0; y<level->fieldy; y++)
- for(x=0; x<level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ for (x = 0; x < level->fieldx; x++)
if (level->field[x][y] > 255)
level->encoding_16bit_field = TRUE;
/* check yamyam content for 16-bit elements */
level->encoding_16bit_yamyam = FALSE;
- for(i=0; i<level->num_yamyam_contents; i++)
- for(y=0; y<3; y++)
- for(x=0; x<3; x++)
+ for (i = 0; i < level->num_yamyam_contents; i++)
+ for (y = 0; y < 3; y++)
+ for (x = 0; x < 3; x++)
if (level->yamyam_content[i][x][y] > 255)
level->encoding_16bit_yamyam = TRUE;
}
/* check for envelope content */
- for (i=0; i<4; i++)
+ for (i = 0; i < 4; i++)
{
if (strlen(level->envelope_text[i]) > 0)
{
/* check for non-default custom elements (unless using template level) */
if (!level->use_custom_template)
{
- for (i=0; i < NUM_CUSTOM_ELEMENTS; i++)
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
{
int element = EL_CUSTOM_START + i;
printf("\n");
printf("Amoeba Speed: %d\n", level->amoeba_speed);
printf("\n");
- printf("Gravity: %s\n", (level->gravity ? "yes" : "no"));
+ printf("Gravity: %s\n", (level->initial_gravity ? "yes" : "no"));
printf("Double Speed Movement: %s\n", (level->double_speed ? "yes" : "no"));
printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
/* default values (also for pre-1.2 tapes) with only the first player */
tape.player_participates[0] = TRUE;
- for(i=1; i<MAX_PLAYERS; i++)
+ for (i = 1; i < MAX_PLAYERS; i++)
tape.player_participates[i] = FALSE;
/* at least one (default: the first) player participates in every tape */
/* since version 1.2, tapes store which players participate in the tape */
tape->num_participating_players = 0;
- for(i=0; i<MAX_PLAYERS; i++)
+ for (i = 0; i < MAX_PLAYERS; i++)
{
tape->player_participates[i] = FALSE;
tape->level_identifier =
checked_realloc(tape->level_identifier, level_identifier_size);
- for(i=0; i < level_identifier_size; i++)
+ for (i = 0; i < level_identifier_size; i++)
tape->level_identifier[i] = getFile8Bit(file);
tape->level_nr = getFile16BitBE(file);
return chunk_size_expected;
}
- for(i=0; i<tape->length; i++)
+ for (i = 0; i < tape->length; i++)
{
if (i >= MAX_TAPELEN)
break;
- for(j=0; j<MAX_PLAYERS; j++)
+ for (j = 0; j < MAX_PLAYERS; j++)
{
tape->pos[i].action[j] = MV_NO_MOVING;
byte action = tape->pos[i].action[0];
int k, num_moves = 0;
- for (k=0; k<4; k++)
+ for (k = 0; k<4; k++)
{
if (action & joy_dir[k])
{
tape->pos[i + 1].delay = 1;
/* delay part */
- for(j=0; j<MAX_PLAYERS; j++)
+ for (j = 0; j < MAX_PLAYERS; j++)
tape->pos[i].action[j] = MV_NO_MOVING;
tape->pos[i].delay--;
tape.length_seconds = GetTapeLength();
#if 0
- printf("tape game version: %d\n", tape.game_version);
- printf("tape engine version: %d\n", tape.engine_version);
+ printf("::: tape game version: %d\n", tape.game_version);
+ printf("::: tape engine version: %d\n", tape.engine_version);
#endif
}
byte store_participating_players = 0;
/* set bits for participating players for compact storage */
- for(i=0; i<MAX_PLAYERS; i++)
+ for (i = 0; i < MAX_PLAYERS; i++)
if (tape->player_participates[i])
store_participating_players |= (1 << i);
putFile16BitBE(file, level_identifier_size);
- for(i=0; i < level_identifier_size; i++)
+ for (i = 0; i < level_identifier_size; i++)
putFile8Bit(file, tape->level_identifier[i]);
putFile16BitBE(file, tape->level_nr);
{
int i, j;
- for(i=0; i<tape->length; i++)
+ for (i = 0; i < tape->length; i++)
{
- for(j=0; j<MAX_PLAYERS; j++)
+ for (j = 0; j < MAX_PLAYERS; j++)
if (tape->player_participates[j])
putFile8Bit(file, tape->pos[i].action[j]);
tape.game_version = GAME_VERSION_ACTUAL;
/* count number of participating players */
- for(i=0; i<MAX_PLAYERS; i++)
+ for (i = 0; i < MAX_PLAYERS; i++)
if (tape.player_participates[i])
num_participating_players++;
printf("Level series identifier: '%s'\n", tape->level_identifier);
printf_line("-", 79);
- for(i=0; i<tape->length; i++)
+ for (i = 0; i < tape->length; i++)
{
if (i >= MAX_TAPELEN)
break;
printf("%03d: ", i);
- for(j=0; j<MAX_PLAYERS; j++)
+ for (j = 0; j < MAX_PLAYERS; j++)
{
if (tape->player_participates[j])
{
FILE *file;
/* always start with reliable default values */
- for(i=0; i<MAX_SCORE_ENTRIES; i++)
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
{
strcpy(highscore[i].Name, EMPTY_PLAYER_NAME);
highscore[i].Score = 0;
return;
}
- for(i=0; i<MAX_SCORE_ENTRIES; i++)
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
{
fscanf(file, "%d", &highscore[i].Score);
fgets(line, MAX_LINE_LEN, file);
fprintf(file, "%s\n\n", SCORE_COOKIE);
- for(i=0; i<MAX_SCORE_ENTRIES; i++)
+ for (i = 0; i < MAX_SCORE_ENTRIES; i++)
fprintf(file, "%d %s\n", highscore[i].Score, highscore[i].Name);
fclose(file);
#define SETUP_TOKEN_EDITOR_EL_CUSTOM 8
#define SETUP_TOKEN_EDITOR_EL_CUSTOM_MORE 9
#define SETUP_TOKEN_EDITOR_EL_HEADLINES 10
+#define SETUP_TOKEN_EDITOR_EL_USER_DEFINED 11
-#define NUM_EDITOR_SETUP_TOKENS 11
+#define NUM_EDITOR_SETUP_TOKENS 12
/* shortcut setup */
#define SETUP_TOKEN_SHORTCUT_SAVE_GAME 0
{ TYPE_SWITCH, &sei.el_custom, "editor.el_custom" },
{ TYPE_SWITCH, &sei.el_custom_more, "editor.el_custom_more" },
{ TYPE_SWITCH, &sei.el_headlines, "editor.el_headlines" },
+ { TYPE_SWITCH, &sei.el_user_defined, "editor.el_user_defined" },
};
static struct TokenInfo shortcut_setup_tokens[] =
si->editor.el_custom_more = FALSE;
si->editor.el_headlines = TRUE;
+ si->editor.el_user_defined = FALSE;
si->shortcut.save_game = DEFAULT_KEY_SAVE_GAME;
si->shortcut.load_game = DEFAULT_KEY_LOAD_GAME;
si->shortcut.toggle_pause = DEFAULT_KEY_TOGGLE_PAUSE;
- for (i=0; i<MAX_PLAYERS; i++)
+ for (i = 0; i < MAX_PLAYERS; i++)
{
si->input[i].use_joystick = FALSE;
si->input[i].joy.device_name=getStringCopy(getDeviceNameFromJoystickNr(i));
/* global setup */
si = setup;
- for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
setSetupInfo(global_setup_tokens, i,
getHashEntry(setup_file_hash, global_setup_tokens[i].text));
setup = si;
/* editor setup */
sei = setup.editor;
- for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
setSetupInfo(editor_setup_tokens, i,
getHashEntry(setup_file_hash,editor_setup_tokens[i].text));
setup.editor = sei;
/* shortcut setup */
ssi = setup.shortcut;
- for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
setSetupInfo(shortcut_setup_tokens, i,
getHashEntry(setup_file_hash,shortcut_setup_tokens[i].text));
setup.shortcut = ssi;
/* player setup */
- for (pnr=0; pnr<MAX_PLAYERS; pnr++)
+ for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
{
char prefix[30];
sprintf(prefix, "%s%d", TOKEN_STR_PLAYER_PREFIX, pnr + 1);
sii = setup.input[pnr];
- for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
{
char full_token[100];
/* system setup */
syi = setup.system;
- for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
setSetupInfo(system_setup_tokens, i,
getHashEntry(setup_file_hash, system_setup_tokens[i].text));
setup.system = syi;
/* options setup */
soi = setup.options;
- for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
setSetupInfo(options_setup_tokens, i,
getHashEntry(setup_file_hash, options_setup_tokens[i].text));
setup.options = soi;
/* global setup */
si = setup;
- for (i=0; i<NUM_GLOBAL_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_GLOBAL_SETUP_TOKENS; i++)
{
/* just to make things nicer :) */
if (i == SETUP_TOKEN_PLAYER_NAME + 1 ||
/* editor setup */
sei = setup.editor;
fprintf(file, "\n");
- for (i=0; i<NUM_EDITOR_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_EDITOR_SETUP_TOKENS; i++)
fprintf(file, "%s\n", getSetupLine(editor_setup_tokens, "", i));
/* shortcut setup */
ssi = setup.shortcut;
fprintf(file, "\n");
- for (i=0; i<NUM_SHORTCUT_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_SHORTCUT_SETUP_TOKENS; i++)
fprintf(file, "%s\n", getSetupLine(shortcut_setup_tokens, "", i));
/* player setup */
- for (pnr=0; pnr<MAX_PLAYERS; pnr++)
+ for (pnr = 0; pnr < MAX_PLAYERS; pnr++)
{
char prefix[30];
fprintf(file, "\n");
sii = setup.input[pnr];
- for (i=0; i<NUM_PLAYER_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_PLAYER_SETUP_TOKENS; i++)
fprintf(file, "%s\n", getSetupLine(player_setup_tokens, prefix, i));
}
/* system setup */
syi = setup.system;
fprintf(file, "\n");
- for (i=0; i<NUM_SYSTEM_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_SYSTEM_SETUP_TOKENS; i++)
fprintf(file, "%s\n", getSetupLine(system_setup_tokens, "", i));
/* options setup */
soi = setup.options;
fprintf(file, "\n");
- for (i=0; i<NUM_OPTIONS_SETUP_TOKENS; i++)
+ for (i = 0; i < NUM_OPTIONS_SETUP_TOKENS; i++)
fprintf(file, "%s\n", getSetupLine(options_setup_tokens, "", i));
fclose(file);
SetupFileHash *setup_file_hash;
int i;
- for (i=0; i<NUM_FILE_ELEMENTS; i++)
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
{
if (element_info[i].custom_description != NULL)
{
if ((setup_file_hash = loadSetupFileHash(filename)) == NULL)
return;
- for (i=0; i<NUM_FILE_ELEMENTS; i++)
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
{
char *token = getStringCat2(element_info[i].token_name, ".name");
char *value = getHashEntry(setup_file_hash, token);
int i, j;
/* always start with reliable default values from default config */
- for (i=0; image_config_vars[i].token != NULL; i++)
- for (j=0; image_config[j].token != NULL; j++)
+ for (i = 0; image_config_vars[i].token != NULL; i++)
+ for (j = 0; image_config[j].token != NULL; j++)
if (strcmp(image_config_vars[i].token, image_config[j].token) == 0)
*image_config_vars[i].value =
get_auto_parameter_value(image_config_vars[i].token,
return;
/* special case: initialize with default values that may be overwritten */
- for (i=0; i < NUM_SPECIAL_GFX_ARGS; i++)
+ for (i = 0; i < NUM_SPECIAL_GFX_ARGS; i++)
{
char *value_x = getHashEntry(setup_file_hash, "menu.draw_xoffset");
char *value_y = getHashEntry(setup_file_hash, "menu.draw_yoffset");
}
/* read (and overwrite with) values that may be specified in config file */
- for (i=0; image_config_vars[i].token != NULL; i++)
+ for (i = 0; image_config_vars[i].token != NULL; i++)
{
char *value = getHashEntry(setup_file_hash, image_config_vars[i].token);
freeSetupFileHash(setup_file_hash);
}
+
+void LoadUserDefinedEditorElementList(int **elements, int *num_elements)
+{
+ char *filename = getEditorSetupFilename();
+ SetupFileList *setup_file_list, *list;
+ SetupFileHash *element_hash;
+ int num_unknown_tokens = 0;
+ int i;
+
+ if ((setup_file_list = loadSetupFileList(filename)) == NULL)
+ return;
+
+ element_hash = newSetupFileHash();
+
+ for (i = 0; i < NUM_FILE_ELEMENTS; i++)
+ setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
+
+ /* determined size may be larger than needed (due to unknown elements) */
+ *num_elements = 0;
+ for (list = setup_file_list; list != NULL; list = list->next)
+ (*num_elements)++;
+
+ /* add space for up to 3 more elements for padding that may be needed */
+ *num_elements += 3;
+
+ *elements = checked_malloc(*num_elements * sizeof(int));
+
+ *num_elements = 0;
+ for (list = setup_file_list; list != NULL; list = list->next)
+ {
+ char *value = getHashEntry(element_hash, list->token);
+
+ if (value)
+ {
+ (*elements)[(*num_elements)++] = atoi(value);
+ }
+ else
+ {
+ if (num_unknown_tokens == 0)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'", filename);
+
+ num_unknown_tokens++;
+ }
+
+ Error(ERR_RETURN, "- token: '%s'", list->token);
+ }
+ }
+
+ if (num_unknown_tokens > 0)
+ Error(ERR_RETURN_LINE, "-");
+
+ while (*num_elements % 4) /* pad with empty elements, if needed */
+ (*elements)[(*num_elements)++] = EL_EMPTY;
+
+ freeSetupFileList(setup_file_list);
+ freeSetupFileHash(element_hash);
+
+#if 0
+ /* TEST-ONLY */
+ for (i = 0; i < *num_elements; i++)
+ printf("editor: element '%s' [%d]\n",
+ element_info[(*elements)[i]].token_name, (*elements)[i]);
+#endif
+}
+
+static struct MusicFileInfo *get_music_file_info(char *basename, int music)
+{
+ SetupFileHash *setup_file_hash = NULL;
+ struct MusicFileInfo tmp_music_file_info, *new_music_file_info;
+ char *filename_music = getCustomMusicFilename(basename);
+ char *filename_prefix, *filename_info;
+ struct
+ {
+ char *token;
+ char **value_ptr;
+ }
+ token_to_value_ptr[] =
+ {
+ { "context", &tmp_music_file_info.context },
+ { "title", &tmp_music_file_info.title },
+ { "artist", &tmp_music_file_info.artist },
+ { "album", &tmp_music_file_info.album },
+ { "year", &tmp_music_file_info.year },
+ { NULL, NULL },
+ };
+ int i;
+
+ if (filename_music == NULL)
+ return NULL;
+
+ /* ---------- try to replace file extension ---------- */
+
+ filename_prefix = getStringCopy(filename_music);
+ if (strrchr(filename_prefix, '.') != NULL)
+ *strrchr(filename_prefix, '.') = '\0';
+ filename_info = getStringCat2(filename_prefix, ".txt");
+
+#if 0
+ printf("trying to load file '%s'...\n", filename_info);
+#endif
+
+ if (fileExists(filename_info))
+ setup_file_hash = loadSetupFileHash(filename_info);
+
+ free(filename_prefix);
+ free(filename_info);
+
+ if (setup_file_hash == NULL)
+ {
+ /* ---------- try to add file extension ---------- */
+
+ filename_prefix = getStringCopy(filename_music);
+ filename_info = getStringCat2(filename_prefix, ".txt");
+
+#if 0
+ printf("trying to load file '%s'...\n", filename_info);
+#endif
+
+ if (fileExists(filename_info))
+ setup_file_hash = loadSetupFileHash(filename_info);
+
+ free(filename_prefix);
+ free(filename_info);
+ }
+
+ if (setup_file_hash == NULL)
+ return NULL;
+
+ /* ---------- music file info found ---------- */
+
+ memset(&tmp_music_file_info, 0, sizeof(struct MusicFileInfo));
+
+ for (i = 0; token_to_value_ptr[i].token != NULL; i++)
+ {
+ char *value = getHashEntry(setup_file_hash, token_to_value_ptr[i].token);
+
+ *token_to_value_ptr[i].value_ptr =
+ getStringCopy(value != NULL ? value : UNKNOWN_NAME);
+ }
+
+ tmp_music_file_info.music = music;
+
+ new_music_file_info = checked_malloc(sizeof(struct MusicFileInfo));
+ *new_music_file_info = tmp_music_file_info;
+
+ return new_music_file_info;
+}
+
+void LoadMusicInfo()
+{
+ char *music_directory = getCustomMusicDirectory();
+ int num_music = getMusicListSize();
+ int num_music_noconf = 0;
+ DIR *dir;
+ struct dirent *dir_entry;
+ struct FileInfo *music;
+ struct MusicFileInfo *next, **new;
+ int i;
+
+ while (music_file_info != NULL)
+ {
+ next = music_file_info->next;
+
+ if (music_file_info->context)
+ free(music_file_info->context);
+ if (music_file_info->title)
+ free(music_file_info->title);
+ if (music_file_info->artist)
+ free(music_file_info->artist);
+ if (music_file_info->album)
+ free(music_file_info->album);
+ if (music_file_info->year)
+ free(music_file_info->year);
+
+ free(music_file_info);
+
+ music_file_info = next;
+ }
+
+ new = &music_file_info;
+
+ for (i = 0; i < num_music; i++)
+ {
+ music = getMusicListEntry(i);
+
+ if (strcmp(music->filename, UNDEFINED_FILENAME) == 0)
+ continue;
+
+ /* a configured file may be not recognized as music */
+ if (!FileIsMusic(music->filename))
+ continue;
+
+#if 0
+ printf("::: -> '%s' (configured)\n", music->filename);
+#endif
+
+ *new = get_music_file_info(music->filename, i);
+ if (*new != NULL)
+ new = &(*new)->next;
+ }
+
+ if ((dir = opendir(music_directory)) == NULL)
+ {
+ Error(ERR_WARN, "cannot read music directory '%s'", music_directory);
+ return;
+ }
+
+ while ((dir_entry = readdir(dir)) != NULL) /* loop until last dir entry */
+ {
+ char *basename = dir_entry->d_name;
+ boolean music_already_used = FALSE;
+ int i;
+
+ for (i = 0; i < num_music; i++)
+ {
+ music = getMusicListEntry(i);
+
+ if (strcmp(basename, music->filename) == 0)
+ {
+ music_already_used = TRUE;
+ break;
+ }
+ }
+
+ if (music_already_used)
+ continue;
+
+ if (!FileIsMusic(basename))
+ continue;
+
+#if 0
+ printf("::: -> '%s' (found in directory)\n", basename);
+#endif
+
+ *new = get_music_file_info(basename, MAP_NOCONF_MUSIC(num_music_noconf));
+ if (*new != NULL)
+ new = &(*new)->next;
+
+ num_music_noconf++;
+ }
+
+ closedir(dir);
+
+#if 0
+ /* TEST-ONLY */
+ for (next = music_file_info; next != NULL; next = next->next)
+ printf("::: title == '%s'\n", next->title);
+#endif
+}
+
+void add_helpanim_entry(int element, int action, int direction, int delay,
+ int *num_list_entries)
+{
+ struct HelpAnimInfo *new_list_entry;
+ (*num_list_entries)++;
+
+ helpanim_info =
+ checked_realloc(helpanim_info,
+ *num_list_entries * sizeof(struct HelpAnimInfo));
+ new_list_entry = &helpanim_info[*num_list_entries - 1];
+
+ new_list_entry->element = element;
+ new_list_entry->action = action;
+ new_list_entry->direction = direction;
+ new_list_entry->delay = delay;
+}
+
+void print_unknown_token(char *filename, char *token, int token_nr)
+{
+ if (token_nr == 0)
+ {
+ Error(ERR_RETURN_LINE, "-");
+ Error(ERR_RETURN, "warning: unknown token(s) found in config file:");
+ Error(ERR_RETURN, "- config file: '%s'", filename);
+ }
+
+ Error(ERR_RETURN, "- token: '%s'", token);
+}
+
+void print_unknown_token_end(int token_nr)
+{
+ if (token_nr > 0)
+ Error(ERR_RETURN_LINE, "-");
+}
+
+void LoadHelpAnimInfo()
+{
+ char *filename = getHelpAnimFilename();
+ SetupFileList *setup_file_list = NULL, *list;
+ SetupFileHash *element_hash, *action_hash, *direction_hash;
+ int num_list_entries = 0;
+ int num_unknown_tokens = 0;
+ int i;
+
+ if (fileExists(filename))
+ setup_file_list = loadSetupFileList(filename);
+
+ if (setup_file_list == NULL)
+ {
+ /* use reliable default values from static configuration */
+ SetupFileList *insert_ptr;
+
+ insert_ptr = setup_file_list =
+ newSetupFileList(helpanim_config[0].token,
+ helpanim_config[0].value);
+
+ for (i = 1; helpanim_config[i].token; i++)
+ insert_ptr = addListEntry(insert_ptr,
+ helpanim_config[i].token,
+ helpanim_config[i].value);
+ }
+
+ element_hash = newSetupFileHash();
+ action_hash = newSetupFileHash();
+ direction_hash = newSetupFileHash();
+
+ for (i = 0; i < MAX_NUM_ELEMENTS; i++)
+ setHashEntry(element_hash, element_info[i].token_name, i_to_a(i));
+
+ for (i = 0; i < NUM_ACTIONS; i++)
+ setHashEntry(action_hash, element_action_info[i].suffix,
+ i_to_a(element_action_info[i].value));
+
+ /* do not store direction index (bit) here, but direction value! */
+ for (i = 0; i < NUM_DIRECTIONS; i++)
+ setHashEntry(direction_hash, element_direction_info[i].suffix,
+ i_to_a(1 << element_direction_info[i].value));
+
+ for (list = setup_file_list; list != NULL; list = list->next)
+ {
+ char *element_token, *action_token, *direction_token;
+ char *element_value, *action_value, *direction_value;
+ int delay = atoi(list->value);
+
+ if (strcmp(list->token, "end") == 0)
+ {
+ add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
+
+ continue;
+ }
+
+ /* first try to break element into element/action/direction parts;
+ if this does not work, also accept combined "element[.act][.dir]"
+ elements (like "dynamite.active"), which are unique elements */
+
+ if (strchr(list->token, '.') == NULL) /* token contains no '.' */
+ {
+ element_value = getHashEntry(element_hash, list->token);
+ if (element_value != NULL) /* element found */
+ add_helpanim_entry(atoi(element_value), -1, -1, delay,
+ &num_list_entries);
+ else
+ {
+ /* no further suffixes found -- this is not an element */
+ print_unknown_token(filename, list->token, num_unknown_tokens++);
+ }
+
+ continue;
+ }
+
+ /* token has format "<prefix>.<something>" */
+
+ action_token = strchr(list->token, '.'); /* suffix may be action ... */
+ direction_token = action_token; /* ... or direction */
+
+ element_token = getStringCopy(list->token);
+ *strchr(element_token, '.') = '\0';
+
+ element_value = getHashEntry(element_hash, element_token);
+
+ if (element_value == NULL) /* this is no element */
+ {
+ element_value = getHashEntry(element_hash, list->token);
+ if (element_value != NULL) /* combined element found */
+ add_helpanim_entry(atoi(element_value), -1, -1, delay,
+ &num_list_entries);
+ else
+ print_unknown_token(filename, list->token, num_unknown_tokens++);
+
+ free(element_token);
+
+ continue;
+ }
+
+ action_value = getHashEntry(action_hash, action_token);
+
+ if (action_value != NULL) /* action found */
+ {
+ add_helpanim_entry(atoi(element_value), atoi(action_value), -1, delay,
+ &num_list_entries);
+
+ free(element_token);
+
+ continue;
+ }
+
+ direction_value = getHashEntry(direction_hash, direction_token);
+
+ if (direction_value != NULL) /* direction found */
+ {
+ add_helpanim_entry(atoi(element_value), -1, atoi(direction_value), delay,
+ &num_list_entries);
+
+ free(element_token);
+
+ continue;
+ }
+
+ if (strchr(action_token + 1, '.') == NULL)
+ {
+ /* no further suffixes found -- this is not an action nor direction */
+
+ element_value = getHashEntry(element_hash, list->token);
+ if (element_value != NULL) /* combined element found */
+ add_helpanim_entry(atoi(element_value), -1, -1, delay,
+ &num_list_entries);
+ else
+ print_unknown_token(filename, list->token, num_unknown_tokens++);
+
+ free(element_token);
+
+ continue;
+ }
+
+ /* token has format "<prefix>.<suffix>.<something>" */
+
+ direction_token = strchr(action_token + 1, '.');
+
+ action_token = getStringCopy(action_token);
+ *strchr(action_token + 1, '.') = '\0';
+
+ action_value = getHashEntry(action_hash, action_token);
+
+ if (action_value == NULL) /* this is no action */
+ {
+ element_value = getHashEntry(element_hash, list->token);
+ if (element_value != NULL) /* combined element found */
+ add_helpanim_entry(atoi(element_value), -1, -1, delay,
+ &num_list_entries);
+ else
+ print_unknown_token(filename, list->token, num_unknown_tokens++);
+
+ free(element_token);
+ free(action_token);
+
+ continue;
+ }
+
+ direction_value = getHashEntry(direction_hash, direction_token);
+
+ if (direction_value != NULL) /* direction found */
+ {
+ add_helpanim_entry(atoi(element_value), atoi(action_value),
+ atoi(direction_value), delay, &num_list_entries);
+
+ free(element_token);
+ free(action_token);
+
+ continue;
+ }
+
+ /* this is no direction */
+
+ element_value = getHashEntry(element_hash, list->token);
+ if (element_value != NULL) /* combined element found */
+ add_helpanim_entry(atoi(element_value), -1, -1, delay,
+ &num_list_entries);
+ else
+ print_unknown_token(filename, list->token, num_unknown_tokens++);
+
+ free(element_token);
+ free(action_token);
+ }
+
+ print_unknown_token_end(num_unknown_tokens);
+
+ add_helpanim_entry(HELPANIM_LIST_NEXT, -1, -1, -1, &num_list_entries);
+ add_helpanim_entry(HELPANIM_LIST_END, -1, -1, -1, &num_list_entries);
+
+ freeSetupFileList(setup_file_list);
+ freeSetupFileHash(element_hash);
+ freeSetupFileHash(action_hash);
+ freeSetupFileHash(direction_hash);
+
+#if 0
+ /* TEST ONLY */
+ for (i = 0; i < num_list_entries; i++)
+ printf("::: %d, %d, %d => %d\n",
+ helpanim_info[i].element,
+ helpanim_info[i].action,
+ helpanim_info[i].direction,
+ helpanim_info[i].delay);
+#endif
+}
+
+void LoadHelpTextInfo()
+{
+ char *filename = getHelpTextFilename();
+ int i;
+
+ if (helptext_info != NULL)
+ {
+ freeSetupFileHash(helptext_info);
+ helptext_info = NULL;
+ }
+
+ if (fileExists(filename))
+ helptext_info = loadSetupFileHash(filename);
+
+ if (helptext_info == NULL)
+ {
+ /* use reliable default values from static configuration */
+ helptext_info = newSetupFileHash();
+
+ for (i = 0; helptext_config[i].token; i++)
+ setHashEntry(helptext_info,
+ helptext_config[i].token,
+ helptext_config[i].value);
+ }
+
+#if 0
+ /* TEST ONLY */
+ BEGIN_HASH_ITERATION(helptext_info, itr)
+ {
+ printf("::: '%s' => '%s'\n",
+ HASH_ITERATION_TOKEN(itr), HASH_ITERATION_VALUE(itr));
+ }
+ END_HASH_ITERATION(hash, itr)
+#endif
+}