&li.auto_exit_sokoban, FALSE
},
+ {
+ -1, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(10),
+ &li.auto_count_gems, FALSE
+ },
+
{
-1, -1,
-1, -1,
&li.num_ball_contents, 4, MAX_ELEMENT_CONTENTS
},
+ {
+ EL_MM_MCDUFFIN, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &li.mm_laser_red, FALSE
+ },
+ {
+ EL_MM_MCDUFFIN, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
+ &li.mm_laser_green, FALSE
+ },
+ {
+ EL_MM_MCDUFFIN, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
+ &li.mm_laser_blue, TRUE
+ },
+
+ {
+ EL_DF_LASER, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(1),
+ &li.df_laser_red, TRUE
+ },
+ {
+ EL_DF_LASER, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(2),
+ &li.df_laser_green, TRUE
+ },
+ {
+ EL_DF_LASER, -1,
+ TYPE_BOOLEAN, CONF_VALUE_8_BIT(3),
+ &li.df_laser_blue, FALSE
+ },
+
+ {
+ EL_MM_FUSE, -1,
+ TYPE_INTEGER, CONF_VALUE_16_BIT(1),
+ &li.mm_time_fuse, 0
+ },
+
/* ---------- unused values ----------------------------------------------- */
{
{ LEVEL_FILE_TYPE_DX, "DX" },
{ LEVEL_FILE_TYPE_SB, "SB" },
{ LEVEL_FILE_TYPE_DC, "DC" },
+ { LEVEL_FILE_TYPE_MM, "MM" },
+ { LEVEL_FILE_TYPE_MM, "DF" },
{ -1, NULL },
};
setLevelInfoToDefaults_EM();
setLevelInfoToDefaults_SP();
+ setLevelInfoToDefaults_MM();
level->native_em_level = &native_em_level;
level->native_sp_level = &native_sp_level;
+ level->native_mm_level = &native_mm_level;
level->file_version = FILE_VERSION_ACTUAL;
level->game_version = GAME_VERSION_ACTUAL;
BorderElement = EL_STEELWALL;
+ /* detect custom elements when loading them */
+ level->file_has_custom_elements = FALSE;
+
/* set all bug compatibility flags to "false" => do not emulate this bug */
level->use_action_after_change_bug = FALSE;
return LEVEL_FILE_TYPE_UNKNOWN;
}
+static int getFileTypeFromMagicBytes(char *filename, int type)
+{
+ File *file;
+
+ if ((file = openFile(filename, MODE_READ)))
+ {
+ char chunk_name[CHUNK_ID_LEN + 1];
+
+ getFileChunkBE(file, chunk_name, NULL);
+
+ if (strEqual(chunk_name, "MMII") ||
+ strEqual(chunk_name, "MIRR"))
+ type = LEVEL_FILE_TYPE_MM;
+
+ closeFile(file);
+ }
+
+ return type;
+}
+
static boolean checkForPackageFromBasename(char *basename)
{
/* !!! WON'T WORK ANYMORE IF getFileTypeFromBasename() ALSO DETECTS !!!
if (fileExists(lfi->filename))
return;
}
+ else if (leveldir_current->level_filetype != NULL)
+ {
+ int filetype = getFiletypeFromID(leveldir_current->level_filetype);
+
+ /* check for specified native level file with standard file name */
+ setLevelFileInfo_FormatLevelFilename(lfi, filetype,
+ "%03d.%s", nr, LEVELFILE_EXTENSION);
+ if (fileExists(lfi->filename))
+ return;
+ }
/* check for native Rocks'n'Diamonds level file */
setLevelFileInfo_FormatLevelFilename(lfi, LEVEL_FILE_TYPE_RND,
{
if (lfi->type == LEVEL_FILE_TYPE_UNKNOWN)
lfi->type = getFileTypeFromBasename(lfi->basename);
+
+ if (lfi->type == LEVEL_FILE_TYPE_RND)
+ lfi->type = getFileTypeFromMagicBytes(lfi->filename, lfi->type);
}
static void setLevelFileInfo(struct LevelFileInfo *level_file_info, int nr)
element_info[element].push_delay_random = 8;
}
+ level->file_has_custom_elements = TRUE;
+
return chunk_size;
}
Error(ERR_WARN, "invalid custom element number %d", element);
}
+ level->file_has_custom_elements = TRUE;
+
return chunk_size;
}
ei->modified_settings = TRUE;
}
+ level->file_has_custom_elements = TRUE;
+
return chunk_size;
}
/* mark this custom element as modified */
ei->modified_settings = TRUE;
+ level->file_has_custom_elements = TRUE;
+
return chunk_size;
}
/* mark this group element as modified */
element_info[element].modified_settings = TRUE;
+ level->file_has_custom_elements = TRUE;
+
return chunk_size;
}
break;
}
+ level->file_has_custom_elements = TRUE;
+
return real_chunk_size;
}
*ei = xx_ei;
*group = xx_group;
+ level->file_has_custom_elements = TRUE;
+
return real_chunk_size;
}
{
struct LevelInfo_SP *level_sp = level->native_sp_level;
LevelInfoType *header = &level_sp->header;
- int i, x, y;
+ boolean num_invalid_elements = 0;
+ int i, j, x, y;
level->fieldx = level_sp->width;
level->fieldy = level_sp->height;
int element_new = getMappedElement(map_element_SP_to_RND(element_old));
if (element_new == EL_UNKNOWN)
- Error(ERR_WARN, "invalid element %d at position %d, %d",
+ {
+ num_invalid_elements++;
+
+ Error(ERR_DEBUG, "invalid element %d at position %d, %d",
element_old, x, y);
+ }
level->field[x][y] = element_new;
}
}
+ if (num_invalid_elements > 0)
+ Error(ERR_WARN, "found %d invalid elements%s", num_invalid_elements,
+ (!options.debug ? " (use '--debug' for more details)" : ""));
+
for (i = 0; i < MAX_PLAYERS; i++)
level->initial_player_gravity[i] =
(header->InitialGravity == 1 ? TRUE : FALSE);
+ /* skip leading spaces */
for (i = 0; i < SP_LEVEL_NAME_LEN; i++)
- level->name[i] = header->LevelTitle[i];
- level->name[SP_LEVEL_NAME_LEN] = '\0';
+ if (header->LevelTitle[i] != ' ')
+ break;
+
+ /* copy level title */
+ for (j = 0; i < SP_LEVEL_NAME_LEN; i++, j++)
+ level->name[j] = header->LevelTitle[i];
+ level->name[j] = '\0';
+
+ /* cut trailing spaces */
+ for (; j > 0; j--)
+ if (level->name[j - 1] == ' ' && level->name[j] == '\0')
+ level->name[j - 1] = '\0';
level->gems_needed = header->InfotronsNeeded;
level_sp->header.DemoRandomSeed = tape.random_seed;
demo->length = 0;
+
for (i = 0; i < tape.length; i++)
{
int demo_action = map_key_RND_to_SP(tape.pos[i].action[0]);
int demo_repeat = tape.pos[i].delay;
+ int demo_entries = (demo_repeat + 15) / 16;
+
+ if (demo->length + demo_entries >= SP_MAX_TAPE_LEN)
+ {
+ Error(ERR_WARN, "tape truncated: size exceeds maximum SP demo size %d",
+ SP_MAX_TAPE_LEN);
+
+ break;
+ }
for (j = 0; j < demo_repeat / 16; j++)
demo->data[demo->length++] = 0xf0 | demo_action;
demo->data[demo->length++] = ((demo_repeat % 16 - 1) << 4) | demo_action;
}
- demo->data[demo->length++] = 0xff;
-
demo->is_available = TRUE;
}
return;
tape.level_nr = demo->level_nr; /* (currently not used) */
- tape.length = demo->length - 1; /* without "end of demo" byte */
tape.random_seed = level_sp->header.DemoRandomSeed;
TapeSetDateFromEpochSeconds(getFileTimestampEpochSeconds(filename));
- for (i = 0; i < demo->length - 1; i++)
+ tape.counter = 0;
+ tape.pos[tape.counter].delay = 0;
+
+ for (i = 0; i < demo->length; i++)
{
int demo_action = demo->data[i] & 0x0f;
int demo_repeat = (demo->data[i] & 0xf0) >> 4;
+ int tape_action = map_key_SP_to_RND(demo_action);
+ int tape_repeat = demo_repeat + 1;
+ byte action[MAX_PLAYERS] = { tape_action, 0, 0, 0 };
+ boolean success = 0;
+ int j;
- tape.pos[i].action[0] = map_key_SP_to_RND(demo_action);
- tape.pos[i].delay = demo_repeat + 1;
+ for (j = 0; j < tape_repeat; j++)
+ success = TapeAddAction(action);
+
+ if (!success)
+ {
+ Error(ERR_WARN, "SP demo truncated: size exceeds maximum tape size %d",
+ MAX_TAPE_LEN);
+
+ break;
+ }
}
- tape.length_frames = GetTapeLengthFrames();
- tape.length_seconds = GetTapeLengthSeconds();
+ TapeHaltRecording();
+}
+
+
+/* ------------------------------------------------------------------------- */
+/* functions for loading MM level */
+/* ------------------------------------------------------------------------- */
+
+void CopyNativeLevel_RND_to_MM(struct LevelInfo *level)
+{
+ struct LevelInfo_MM *level_mm = level->native_mm_level;
+ int x, y;
+
+ level_mm->file_version = level->file_version;
+ level_mm->game_version = level->game_version;
+ level_mm->encoding_16bit_field = level->encoding_16bit_field;
+
+ level_mm->fieldx = MIN(level->fieldx, MM_MAX_PLAYFIELD_WIDTH);
+ level_mm->fieldy = MIN(level->fieldy, MM_MAX_PLAYFIELD_HEIGHT);
+
+ level_mm->time = level->time;
+ level_mm->kettles_needed = level->gems_needed;
+ level_mm->auto_count_kettles = level->auto_count_gems;
+
+ level_mm->laser_red = level->mm_laser_red;
+ level_mm->laser_green = level->mm_laser_green;
+ level_mm->laser_blue = level->mm_laser_blue;
+
+ strcpy(level_mm->name, level->name);
+ strcpy(level_mm->author, level->author);
+
+ level_mm->score[SC_PACMAN] = level->score[SC_PACMAN];
+ level_mm->score[SC_KEY] = level->score[SC_PACMAN];
+ level_mm->score[SC_TIME_BONUS] = level->score[SC_TIME_BONUS];
+
+ level_mm->amoeba_speed = level->amoeba_speed;
+ level_mm->time_fuse = level->mm_time_fuse;
+
+ for (x = 0; x < level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ Ur[x][y] =
+ level_mm->field[x][y] = map_element_RND_to_MM(level->field[x][y]);
+}
+
+void CopyNativeLevel_MM_to_RND(struct LevelInfo *level)
+{
+ struct LevelInfo_MM *level_mm = level->native_mm_level;
+ int x, y;
+
+ level->file_version = level_mm->file_version;
+ level->game_version = level_mm->game_version;
+ level->encoding_16bit_field = level_mm->encoding_16bit_field;
+
+ level->fieldx = MIN(level_mm->fieldx, MAX_LEV_FIELDX);
+ level->fieldy = MIN(level_mm->fieldy, MAX_LEV_FIELDY);
+
+ level->time = level_mm->time;
+ level->gems_needed = level_mm->kettles_needed;
+ level->auto_count_gems = level_mm->auto_count_kettles;
+
+ level->mm_laser_red = level_mm->laser_red;
+ level->mm_laser_green = level_mm->laser_green;
+ level->mm_laser_blue = level_mm->laser_blue;
+
+ strcpy(level->name, level_mm->name);
+
+ /* only overwrite author from 'levelinfo.conf' if author defined in level */
+ if (!strEqual(level_mm->author, ANONYMOUS_NAME))
+ strcpy(level->author, level_mm->author);
+
+ level->score[SC_PACMAN] = level_mm->score[SC_PACMAN];
+ level->score[SC_KEY] = level_mm->score[SC_PACMAN];
+ level->score[SC_TIME_BONUS] = level_mm->score[SC_TIME_BONUS];
+
+ level->amoeba_speed = level_mm->amoeba_speed;
+ level->mm_time_fuse = level_mm->time_fuse;
+
+ for (x = 0; x < level->fieldx; x++)
+ for (y = 0; y < level->fieldy; y++)
+ level->field[x][y] = map_element_MM_to_RND(level_mm->field[x][y]);
}
level->no_valid_file = TRUE;
}
+static void LoadLevelFromFileInfo_MM(struct LevelInfo *level,
+ struct LevelFileInfo *level_file_info,
+ boolean level_info_only)
+{
+ if (!LoadNativeLevel_MM(level_file_info->filename, level_info_only))
+ level->no_valid_file = TRUE;
+}
+
void CopyNativeLevel_RND_to_Native(struct LevelInfo *level)
{
if (level->game_engine_type == GAME_ENGINE_TYPE_EM)
CopyNativeLevel_RND_to_EM(level);
else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
CopyNativeLevel_RND_to_SP(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
+ CopyNativeLevel_RND_to_MM(level);
}
void CopyNativeLevel_Native_to_RND(struct LevelInfo *level)
CopyNativeLevel_EM_to_RND(level);
else if (level->game_engine_type == GAME_ENGINE_TYPE_SP)
CopyNativeLevel_SP_to_RND(level);
+ else if (level->game_engine_type == GAME_ENGINE_TYPE_MM)
+ CopyNativeLevel_MM_to_RND(level);
}
void SaveNativeLevel(struct LevelInfo *level)
level->game_engine_type = GAME_ENGINE_TYPE_SP;
break;
+ case LEVEL_FILE_TYPE_MM:
+ LoadLevelFromFileInfo_MM(level, level_file_info, level_info_only);
+ level->game_engine_type = GAME_ENGINE_TYPE_MM;
+ break;
+
case LEVEL_FILE_TYPE_DC:
LoadLevelFromFileInfo_DC(level, level_file_info, level_info_only);
break;
level->em_explodes_by_fire = TRUE;
}
-static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
+static void LoadLevel_InitStandardElements(struct LevelInfo *level)
{
- int i, j, x, y;
+ int i, x, y;
+
+ /* 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].e[x][y] =
+ getMappedElementByVersion(level->yamyam_content[i].e[x][y],
+ level->game_version);
+
+}
+
+static void LoadLevel_InitCustomElements(struct LevelInfo *level)
+{
+ int i, j;
/* map custom element change events that have changed in newer versions
(these following values were accidentally changed in version 3.0.1)
}
}
- /* 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].e[x][y] =
- getMappedElementByVersion(level->yamyam_content[i].e[x][y],
- level->game_version);
+ /* set some other uninitialized values of custom elements in older levels */
+ if (level->game_version < VERSION_IDENT(3,1,0,0))
+ {
+ for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
+ {
+ int element = EL_CUSTOM_START + i;
+
+ element_info[element].access_direction = MV_ALL_DIRECTIONS;
+
+ element_info[element].explosion_delay = 17;
+ element_info[element].ignition_delay = 8;
+ }
+ }
+}
+
+static void LoadLevel_InitElements(struct LevelInfo *level, char *filename)
+{
+ LoadLevel_InitStandardElements(level);
+
+ if (level->file_has_custom_elements)
+ LoadLevel_InitCustomElements(level);
/* initialize element properties for level editor etc. */
InitElementPropertiesEngine(level->game_version);
- InitElementPropertiesAfterLoading(level->game_version);
InitElementPropertiesGfxElement();
}
for (i = 0; i < tape->length; i++)
{
if (i >= MAX_TAPE_LEN)
+ {
+ Error(ERR_WARN, "tape truncated -- size exceeds maximum tape size %d",
+ MAX_TAPE_LEN);
+
+ // tape too large; read and ignore remaining tape data from this chunk
+ for (;i < tape->length; i++)
+ ReadUnusedBytesFromFile(file, tape->num_participating_players + 1);
+
break;
+ }
for (j = 0; j < MAX_PLAYERS; j++)
{
{ TYPE_SWITCH, &seci.el_sp, "editor.cascade.el_sp" },
{ TYPE_SWITCH, &seci.el_dc, "editor.cascade.el_dc" },
{ TYPE_SWITCH, &seci.el_dx, "editor.cascade.el_dx" },
+ { TYPE_SWITCH, &seci.el_mm, "editor.cascade.el_mm" },
+ { TYPE_SWITCH, &seci.el_df, "editor.cascade.el_df" },
{ TYPE_SWITCH, &seci.el_chars, "editor.cascade.el_chars" },
{ TYPE_SWITCH, &seci.el_steel_chars, "editor.cascade.el_steel_chars" },
{ TYPE_SWITCH, &seci.el_ce, "editor.cascade.el_ce" },
si->editor.el_supaplex = TRUE;
si->editor.el_diamond_caves = TRUE;
si->editor.el_dx_boulderdash = TRUE;
+
+ si->editor.el_mirror_magic = TRUE;
+ si->editor.el_deflektor = TRUE;
+
si->editor.el_chars = TRUE;
si->editor.el_steel_chars = TRUE;
si->editor_cascade.el_dc = TRUE;
si->editor_cascade.el_dx = TRUE;
+ si->editor_cascade.el_mm = TRUE;
+ si->editor_cascade.el_df = TRUE;
+
si->editor_cascade.el_chars = FALSE;
si->editor_cascade.el_steel_chars = FALSE;
si->editor_cascade.el_ce = FALSE;