#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 11 /* unused level header bytes */
+#define LEVEL_HEADER_UNUSED 3 /* unused level header bytes */
#define LEVEL_CHUNK_CNT2_SIZE 160 /* size of level CNT2 chunk */
#define LEVEL_CHUNK_CNT2_UNUSED 11 /* unused CNT2 chunk bytes */
#define LEVEL_CHUNK_CNT3_HEADER 16 /* size of level CNT3 header */
change->can_change = FALSE;
change->events = CE_BITMASK_DEFAULT;
- change->sides = CH_SIDE_ANY;
+
+ change->trigger_player = CH_PLAYER_ANY;
+ change->trigger_side = CH_SIDE_ANY;
+ change->trigger_page = CH_PAGE_ANY;
change->target_element = EL_EMPTY_SPACE;
change->trigger_element = EL_EMPTY_SPACE;
change->explode = FALSE;
- change->use_content = FALSE;
- change->only_complete = FALSE;
- change->use_random_change = FALSE;
- change->random = 100;
- change->power = CP_NON_DESTRUCTIVE;
+ change->use_target_content = FALSE;
+ change->only_if_complete = FALSE;
+ change->use_random_replace = FALSE;
+ change->random_percentage = 100;
+ change->replace_when = CP_WHEN_EMPTY;
for (x = 0; x < 3; x++)
for (y = 0; y < 3; y++)
- change->content[x][y] = EL_EMPTY_SPACE;
+ change->target_content[x][y] = EL_EMPTY_SPACE;
change->direct_action = 0;
change->other_action = 0;
static void setLevelInfoToDefaults(struct LevelInfo *level)
{
+ static boolean clipboard_elements_initialized = FALSE;
+
int i, j, x, y;
level->file_version = FILE_VERSION_ACTUAL;
level->time = 100;
level->gems_needed = 0;
+
level->amoeba_speed = 10;
+
level->time_magic_wall = 10;
level->time_wheel = 10;
level->time_light = 10;
level->time_timegate = 10;
+
level->amoeba_content = EL_DIAMOND;
+
level->double_speed = FALSE;
level->initial_gravity = FALSE;
level->em_slippery_gems = FALSE;
level->block_last_field = FALSE;
level->sp_block_last_field = TRUE;
+ level->instant_relocation = FALSE;
+
+ level->can_move_into_acid_bits = ~0; /* everything can move into acid */
+ level->dont_collide_with_bits = ~0; /* always deadly when colliding */
+
+ level->use_spring_bug = FALSE;
+ level->use_step_counter = FALSE;
level->use_custom_template = FALSE;
{
int element = i;
+ /* never initialize clipboard elements after the very first time */
+ if (IS_CLIPBOARD_ELEMENT(element) && clipboard_elements_initialized)
+ continue;
+
setElementChangePages(&element_info[element], 1);
setElementChangeInfoToDefaults(element_info[element].change);
- if (IS_CUSTOM_ELEMENT(element) || IS_GROUP_ELEMENT(element))
+ if (IS_CUSTOM_ELEMENT(element) ||
+ IS_GROUP_ELEMENT(element) ||
+ IS_INTERNAL_ELEMENT(element))
{
for (j = 0; j < MAX_ELEMENT_NAME_LEN + 1; j++)
element_info[element].description[j] = '\0';
element_info[element].gfx_element = EL_EMPTY_SPACE;
}
- if (IS_CUSTOM_ELEMENT(element))
+ if (IS_CUSTOM_ELEMENT(element) ||
+ IS_INTERNAL_ELEMENT(element))
{
+ element_info[element].access_direction = MV_ALL_DIRECTIONS;
+
element_info[element].collect_score = 10; /* special default */
element_info[element].collect_count = 1; /* special default */
element_info[element].push_delay_fixed = -1; /* initialize later */
element_info[element].push_delay_random = -1; /* initialize later */
+ element_info[element].drop_delay_fixed = 0;
+ element_info[element].drop_delay_random = 0;
element_info[element].move_delay_fixed = 0;
element_info[element].move_delay_random = 0;
element_info[element].slippery_type = SLIPPERY_ANY_RANDOM;
- element_info[element].explosion_delay = 0;
- element_info[element].ignition_delay = 0;
+ element_info[element].explosion_delay = 16;
+ element_info[element].ignition_delay = 8;
for (x = 0; x < 3; x++)
for (y = 0; y < 3; y++)
for (j = 0; j < NUM_EP_BITFIELDS; j++)
Properties[element][j] = EP_BITMASK_DEFAULT;
+ /* now set default properties */
+ SET_PROPERTY(element, EP_CAN_MOVE_INTO_ACID, TRUE);
+
element_info[element].modified_settings = FALSE;
}
- else if (IS_GROUP_ELEMENT(element) || element == EL_INTERNAL_EDITOR)
+
+ if (IS_GROUP_ELEMENT(element) ||
+ IS_INTERNAL_ELEMENT(element))
{
/* initialize memory for list of elements in group */
if (element_info[element].group == NULL)
}
}
+ clipboard_elements_initialized = TRUE;
+
BorderElement = EL_STEELWALL;
- level->no_level_file = FALSE;
+ level->no_valid_file = FALSE;
if (leveldir_current == NULL) /* only when dumping level */
return;
int getMappedElement(int element)
{
- /* map some (historic, now obsolete) elements */
+ /* remap some (historic, now obsolete) elements */
#if 1
switch (element)
return element;
}
+int getMappedElementByVersion(int element, int game_version)
+{
+ /* remap some elements due to certain game version */
+
+ if (game_version <= VERSION_IDENT(2,2,0,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 (game_version < VERSION_IDENT(3,0,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);
+ }
+
+ return element;
+}
+
static int LoadLevel_VERS(FILE *file, int chunk_size, struct LevelInfo *level)
{
level->file_version = getFileVersion(file);
level->block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
level->sp_block_last_field = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+ level->can_move_into_acid_bits = getFile32BitBE(file);
+ level->dont_collide_with_bits = getFile8Bit(file);
+
+ level->use_spring_bug = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+ level->use_step_counter = (getFile8Bit(file) == 1 ? TRUE : FALSE);
+
+ level->instant_relocation = (getFile8Bit(file) == 1 ? TRUE : FALSE);
ReadUnusedBytesFromFile(file, LEVEL_HEADER_UNUSED);
getMappedElement(getFile16BitBE(file));
element_info[element].change->explode = getFile8Bit(file);
- element_info[element].change->use_content = getFile8Bit(file);
- element_info[element].change->only_complete = getFile8Bit(file);
- element_info[element].change->use_random_change = getFile8Bit(file);
+ element_info[element].change->use_target_content = getFile8Bit(file);
+ element_info[element].change->only_if_complete = getFile8Bit(file);
+ element_info[element].change->use_random_replace = getFile8Bit(file);
- element_info[element].change->random = getFile8Bit(file);
- element_info[element].change->power = getFile8Bit(file);
+ element_info[element].change->random_percentage = getFile8Bit(file);
+ element_info[element].change->replace_when = getFile8Bit(file);
for (y = 0; y < 3; y++)
for (x = 0; x < 3; x++)
- element_info[element].change->content[x][y] =
+ element_info[element].change->target_content[x][y] =
getMappedElement(getFile16BitBE(file));
element_info[element].slippery_type = getFile8Bit(file);
ei->collect_score = getFile8Bit(file);
ei->collect_count = getFile8Bit(file);
- ei->push_delay_fixed = getFile16BitBE(file);
- ei->push_delay_random = getFile16BitBE(file);
+ ei->drop_delay_fixed = getFile8Bit(file);
+ ei->push_delay_fixed = getFile8Bit(file);
+ ei->drop_delay_random = getFile8Bit(file);
+ ei->push_delay_random = getFile8Bit(file);
ei->move_delay_fixed = getFile16BitBE(file);
ei->move_delay_random = getFile16BitBE(file);
/* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
ei->move_pattern |= (getFile16BitBE(file) << 16);
+ ei->access_direction = getFile8Bit(file);
+
+ ei->explosion_delay = getFile8Bit(file);
+ ei->ignition_delay = getFile8Bit(file);
+
/* some free bytes for future custom property values and padding */
- ReadUnusedBytesFromFile(file, 5);
+ ReadUnusedBytesFromFile(file, 2);
/* read change property values */
change->trigger_element = getMappedElement(getFile16BitBE(file));
change->explode = getFile8Bit(file);
- change->use_content = getFile8Bit(file);
- change->only_complete = getFile8Bit(file);
- change->use_random_change = getFile8Bit(file);
+ change->use_target_content = getFile8Bit(file);
+ change->only_if_complete = getFile8Bit(file);
+ change->use_random_replace = getFile8Bit(file);
- change->random = getFile8Bit(file);
- change->power = getFile8Bit(file);
+ change->random_percentage = getFile8Bit(file);
+ change->replace_when = getFile8Bit(file);
for (y = 0; y < 3; y++)
for (x = 0; x < 3; x++)
- change->content[x][y] = getMappedElement(getFile16BitBE(file));
+ change->target_content[x][y] = getMappedElement(getFile16BitBE(file));
change->can_change = getFile8Bit(file);
- change->sides = getFile8Bit(file);
+ change->trigger_side = getFile8Bit(file);
+
+#if 1
+ change->trigger_player = getFile8Bit(file);
+ change->trigger_page = getFile8Bit(file);
+
+ change->trigger_page = (change->trigger_page == CH_PAGE_ANY_FILE ?
+ CH_PAGE_ANY : (1 << change->trigger_page));
+
+ /* some free bytes for future change property values and padding */
+ ReadUnusedBytesFromFile(file, 6);
- if (change->sides == CH_SIDE_NONE) /* correct empty sides field */
- change->sides = CH_SIDE_ANY;
+#else
/* some free bytes for future change property values and padding */
ReadUnusedBytesFromFile(file, 8);
+#endif
}
/* mark this custom element as modified */
if (!(file = fopen(filename, MODE_READ)))
{
- level->no_level_file = TRUE;
+ level->no_valid_file = TRUE;
if (level != &level_template)
Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
getFileChunkBE(file, chunk_name, NULL);
if (strcmp(chunk_name, "CAVE") != 0)
{
+ level->no_valid_file = TRUE;
+
Error(ERR_WARN, "unknown format of level file '%s'", filename);
fclose(file);
return;
if (!checkCookieString(cookie, LEVEL_COOKIE_TMPL))
{
+ level->no_valid_file = TRUE;
+
Error(ERR_WARN, "unknown format of level file '%s'", filename);
fclose(file);
return;
if ((level->file_version = getFileVersionFromCookieString(cookie)) == -1)
{
+ level->no_valid_file = TRUE;
+
Error(ERR_WARN, "unsupported version of level file '%s'", filename);
fclose(file);
return;
if (!(file = fopen(filename, MODE_READ)))
{
- level->no_level_file = TRUE;
+ level->no_valid_file = TRUE;
Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
if (!(file = fopen(filename, MODE_READ)))
{
- level->no_level_file = TRUE;
+ level->no_valid_file = TRUE;
Error(ERR_WARN, "cannot read level '%s' -- using empty level", filename);
/* position file stream to the requested level inside the level package */
if (fseek(file, nr * SP_LEVEL_SIZE, SEEK_SET) != 0)
{
- level->no_level_file = TRUE;
+ level->no_valid_file = TRUE;
Error(ERR_WARN, "cannot fseek level '%s' -- using empty level", filename);
/* Default behaviour for EM style gems was "slippery" only in 2.0.1 */
if (level->game_version == VERSION_IDENT(2,0,1,0))
level->em_slippery_gems = TRUE;
+
+ if (level->game_version < VERSION_IDENT(2,2,0,0))
+ level->use_spring_bug = TRUE;
+
+ if (level->game_version < VERSION_IDENT(3,1,0,0))
+ {
+ int i, j;
+
+ level->can_move_into_acid_bits = 0; /* nothing can move into acid */
+ level->dont_collide_with_bits = 0; /* nothing is deadly when colliding */
+
+ setMoveIntoAcidProperty(level, EL_ROBOT, TRUE);
+ setMoveIntoAcidProperty(level, EL_SATELLITE, TRUE);
+ setMoveIntoAcidProperty(level, EL_PENGUIN, TRUE);
+ setMoveIntoAcidProperty(level, EL_BALLOON, TRUE);
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ SET_PROPERTY(EL_CUSTOM_START + i, EP_CAN_MOVE_INTO_ACID, TRUE);
+
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+ struct ElementInfo *ei = &element_info[element];
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ change->trigger_player = CH_PLAYER_ANY;
+ change->trigger_page = CH_PAGE_ANY;
+ }
+ }
+ }
}
else
{
static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
{
- int i, j;
+ int i, j, x, y;
/* map custom element change events that have changed in newer versions
(these following values were accidentally changed in version 3.0.1) */
}
}
+ /* correct custom element values (for old levels without these options) */
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+ struct ElementInfo *ei = &element_info[element];
+
+ if (ei->access_direction == MV_NO_MOVING)
+ ei->access_direction = MV_ALL_DIRECTIONS;
+
+ for (j = 0; j < ei->num_change_pages; j++)
+ {
+ struct ElementChangeInfo *change = &ei->change_page[j];
+
+ if (change->trigger_side == CH_SIDE_NONE)
+ change->trigger_side = CH_SIDE_ANY;
+ }
+ }
+
#if 0
/* set default push delay values (corrected since version 3.0.7-1) */
if (level->game_version < VERSION_IDENT(3,0,7,1))
}
#endif
+ /* map elements that have changed in newer versions */
+ level->amoeba_content = getMappedElementByVersion(level->amoeba_content,
+ level->game_version);
+ 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] =
+ getMappedElementByVersion(level->yamyam_content[i][x][y],
+ level->game_version);
+
/* initialize element properties for level editor etc. */
InitElementPropertiesEngine(level->game_version);
}
{
int element = level->field[x][y];
+#if 1
+ element = getMappedElementByVersion(element, level->game_version);
+#else
if (level->game_version <= VERSION_IDENT(2,2,0,0))
{
/* map game font elements */
element == EL_SP_GRAVITY_PORT_DOWN ? EL_SP_PORT_DOWN :
element);
}
+#endif
level->field[x][y] = element;
}
putFile8Bit(file, (level->block_last_field ? 1 : 0));
putFile8Bit(file, (level->sp_block_last_field ? 1 : 0));
+ putFile32BitBE(file, level->can_move_into_acid_bits);
+ putFile8Bit(file, level->dont_collide_with_bits);
+
+ putFile8Bit(file, (level->use_spring_bug ? 1 : 0));
+ putFile8Bit(file, (level->use_step_counter ? 1 : 0));
+
+ putFile8Bit(file, (level->instant_relocation ? 1 : 0));
WriteUnusedBytesToFile(file, LEVEL_HEADER_UNUSED);
}
putFile16BitBE(file, element_info[element].change->trigger_element);
putFile8Bit(file, element_info[element].change->explode);
- putFile8Bit(file, element_info[element].change->use_content);
- putFile8Bit(file, element_info[element].change->only_complete);
- putFile8Bit(file, element_info[element].change->use_random_change);
+ putFile8Bit(file, element_info[element].change->use_target_content);
+ putFile8Bit(file, element_info[element].change->only_if_complete);
+ putFile8Bit(file, element_info[element].change->use_random_replace);
- putFile8Bit(file, element_info[element].change->random);
- putFile8Bit(file, element_info[element].change->power);
+ putFile8Bit(file, element_info[element].change->random_percentage);
+ putFile8Bit(file, element_info[element].change->replace_when);
for (y = 0; y < 3; y++)
for (x = 0; x < 3; x++)
putFile8Bit(file, ei->collect_score);
putFile8Bit(file, ei->collect_count);
- putFile16BitBE(file, ei->push_delay_fixed);
- putFile16BitBE(file, ei->push_delay_random);
+ putFile8Bit(file, ei->drop_delay_fixed);
+ putFile8Bit(file, ei->push_delay_fixed);
+ putFile8Bit(file, ei->drop_delay_random);
+ putFile8Bit(file, ei->push_delay_random);
putFile16BitBE(file, ei->move_delay_fixed);
putFile16BitBE(file, ei->move_delay_random);
/* ... bits 16 - 31 of "move_pattern" (not nice, but downward compatible) */
putFile16BitBE(file, (ei->move_pattern >> 16) & 0xffff);
+ putFile8Bit(file, ei->access_direction);
+
+ putFile8Bit(file, ei->explosion_delay);
+ putFile8Bit(file, ei->ignition_delay);
+
/* some free bytes for future custom property values and padding */
- WriteUnusedBytesToFile(file, 5);
+ WriteUnusedBytesToFile(file, 2);
/* write change property values */
putFile16BitBE(file, change->trigger_element);
putFile8Bit(file, change->explode);
- putFile8Bit(file, change->use_content);
- putFile8Bit(file, change->only_complete);
- putFile8Bit(file, change->use_random_change);
+ putFile8Bit(file, change->use_target_content);
+ putFile8Bit(file, change->only_if_complete);
+ putFile8Bit(file, change->use_random_replace);
- putFile8Bit(file, change->random);
- putFile8Bit(file, change->power);
+ putFile8Bit(file, change->random_percentage);
+ putFile8Bit(file, change->replace_when);
for (y = 0; y < 3; y++)
for (x = 0; x < 3; x++)
- putFile16BitBE(file, change->content[x][y]);
+ putFile16BitBE(file, change->target_content[x][y]);
putFile8Bit(file, change->can_change);
- putFile8Bit(file, change->sides);
+ putFile8Bit(file, change->trigger_side);
+
+#if 1
+ putFile8Bit(file, change->trigger_player);
+ putFile8Bit(file, (change->trigger_page == CH_PAGE_ANY ? CH_PAGE_ANY_FILE :
+ log_2(change->trigger_page)));
+
+ /* some free bytes for future change property values and padding */
+ WriteUnusedBytesToFile(file, 6);
+
+#else
/* some free bytes for future change property values and padding */
WriteUnusedBytesToFile(file, 8);
+#endif
}
}
void DumpLevel(struct LevelInfo *level)
{
+ if (level->no_valid_file)
+ {
+ Error(ERR_WARN, "cannot dump -- no valid level file found");
+
+ return;
+ }
+
printf_line("-", 79);
printf("Level xxx (file version %08d, game version %08d)\n",
level->file_version, level->game_version);
printf("EM style slippery gems: %s\n", (level->em_slippery_gems ? "yes" : "no"));
printf("Player blocks last field: %s\n", (level->block_last_field ? "yes" : "no"));
printf("SP player blocks last field: %s\n", (level->sp_block_last_field ? "yes" : "no"));
+ printf("use spring bug: %s\n", (level->use_spring_bug ? "yes" : "no"));
+ printf("use step counter: %s\n", (level->use_step_counter ? "yes" : "no"));
printf_line("-", 79);
}
tape.recording = FALSE;
tape.playing = FALSE;
tape.pausing = FALSE;
+
+ tape.no_valid_file = FALSE;
}
static int LoadTape_VERS(FILE *file, int chunk_size, struct TapeInfo *tape)
setTapeInfoToDefaults();
if (!(file = fopen(filename, MODE_READ)))
+ {
+ tape.no_valid_file = TRUE;
+
+#if 0
+ Error(ERR_WARN, "cannot read tape '%s' -- using empty tape", filename);
+#endif
+
return;
+ }
getFileChunkBE(file, chunk_name, NULL);
if (strcmp(chunk_name, "RND1") == 0)
getFileChunkBE(file, chunk_name, NULL);
if (strcmp(chunk_name, "TAPE") != 0)
{
+ tape.no_valid_file = TRUE;
+
Error(ERR_WARN, "unknown format of tape file '%s'", filename);
fclose(file);
return;
if (!checkCookieString(cookie, TAPE_COOKIE_TMPL))
{
+ tape.no_valid_file = TRUE;
+
Error(ERR_WARN, "unknown format of tape file '%s'", filename);
fclose(file);
return;
if ((tape.file_version = getFileVersionFromCookieString(cookie)) == -1)
{
+ tape.no_valid_file = TRUE;
+
Error(ERR_WARN, "unsupported version of tape file '%s'", filename);
fclose(file);
return;
{
int i, j;
+#if 1
+ if (tape->no_valid_file)
+ {
+ Error(ERR_WARN, "cannot dump -- no valid tape file found");
+
+ return;
+ }
+#else
if (TAPE_IS_EMPTY(*tape))
{
Error(ERR_WARN, "no tape available for level %d", tape->level_nr);
+
return;
}
+#endif
printf_line("-", 79);
printf("Tape of Level %03d (file version %08d, game version %08d)\n",